77from cloudsmith_cli .cli import validators
88
99from ..core .api .init import initialise_api as _initialise_api
10+ from ..core .credentials import CredentialContext , CredentialProviderChain
11+ from ..core .credentials .session import create_session as _create_session
1012from ..core .mcp import server
1113from . import config , utils
1214
@@ -20,6 +22,14 @@ def report_retry(seconds, context=None):
2022 )
2123
2224
25+ def _pop_boolean_flag (kwargs , name , invert = False ):
26+ """Pop a boolean flag from kwargs, optionally inverting it."""
27+ value = kwargs .pop (name )
28+ if value is not None and invert :
29+ value = not value
30+ return value
31+
32+
2333def common_package_action_options (f ):
2434 """Add common options for package actions."""
2535
@@ -214,15 +224,17 @@ def common_api_auth_options(f):
214224 def wrapper (ctx , * args , ** kwargs ):
215225 # pylint: disable=missing-docstring
216226 opts = config .get_or_create_options (ctx )
217- opts .api_key = kwargs .pop ("api_key" )
227+ api_key = kwargs .pop ("api_key" )
228+ if api_key :
229+ opts .api_key = api_key
218230 kwargs ["opts" ] = opts
219231 return ctx .invoke (f , * args , ** kwargs )
220232
221233 return wrapper
222234
223235
224- def initialise_api (f ):
225- """Initialise the Cloudsmith API for use ."""
236+ def initialise_session (f ):
237+ """Create a shared HTTP session with proxy/SSL/user-agent settings ."""
226238
227239 @click .option (
228240 "--api-host" , envvar = "CLOUDSMITH_API_HOST" , help = "The API host to connect to."
@@ -252,6 +264,78 @@ def initialise_api(f):
252264 envvar = "CLOUDSMITH_API_HEADERS" ,
253265 help = "A CSV list of extra headers (key=value) to send to the API." ,
254266 )
267+ @click .pass_context
268+ @functools .wraps (f )
269+ def wrapper (ctx , * args , ** kwargs ):
270+ # pylint: disable=missing-docstring
271+ opts = config .get_or_create_options (ctx )
272+ opts .api_host = kwargs .pop ("api_host" )
273+ opts .api_proxy = kwargs .pop ("api_proxy" )
274+ opts .api_ssl_verify = _pop_boolean_flag (
275+ kwargs , "without_api_ssl_verify" , invert = True
276+ )
277+ opts .api_user_agent = kwargs .pop ("api_user_agent" )
278+ opts .api_headers = kwargs .pop ("api_headers" )
279+
280+ opts .session = _create_session (
281+ proxy = opts .api_proxy ,
282+ ssl_verify = opts .api_ssl_verify ,
283+ user_agent = opts .api_user_agent ,
284+ headers = opts .api_headers ,
285+ )
286+
287+ kwargs ["opts" ] = opts
288+ return ctx .invoke (f , * args , ** kwargs )
289+
290+ return wrapper
291+
292+
293+ def resolve_credentials (f ):
294+ """Resolve credentials via the provider chain. Depends on initialise_session."""
295+
296+ @click .pass_context
297+ @functools .wraps (f )
298+ def wrapper (ctx , * args , ** kwargs ):
299+ # pylint: disable=missing-docstring
300+ opts = config .get_or_create_options (ctx )
301+
302+ context = CredentialContext (
303+ session = opts .session ,
304+ api_key = opts .api_key ,
305+ api_host = opts .api_host or "https://api.cloudsmith.io" ,
306+ creds_file_path = ctx .meta .get ("creds_file" ),
307+ profile = ctx .meta .get ("profile" ),
308+ debug = opts .debug ,
309+ )
310+
311+ chain = CredentialProviderChain ()
312+ credential = chain .resolve (context )
313+
314+ if context .keyring_refresh_failed :
315+ click .secho (
316+ "An error occurred when attempting to refresh your SSO access token. "
317+ "To refresh this session, run 'cloudsmith auth'" ,
318+ fg = "yellow" ,
319+ err = True ,
320+ )
321+ if credential :
322+ click .secho (
323+ "Falling back to API key authentication." ,
324+ fg = "yellow" ,
325+ err = True ,
326+ )
327+
328+ opts .credential = credential
329+
330+ kwargs ["opts" ] = opts
331+ return ctx .invoke (f , * args , ** kwargs )
332+
333+ return initialise_session (wrapper )
334+
335+
336+ def initialise_api (f ):
337+ """Initialise the Cloudsmith API for use. Depends on resolve_credentials."""
338+
255339 @click .option (
256340 "-R" ,
257341 "--without-rate-limit" ,
@@ -294,20 +378,8 @@ def initialise_api(f):
294378 @functools .wraps (f )
295379 def wrapper (ctx , * args , ** kwargs ):
296380 # pylint: disable=missing-docstring
297- def _set_boolean (name , invert = False ):
298- value = kwargs .pop (name )
299- value = value if value is not None else None
300- if value is not None and invert :
301- value = not value
302- return value
303-
304381 opts = config .get_or_create_options (ctx )
305- opts .api_host = kwargs .pop ("api_host" )
306- opts .api_proxy = kwargs .pop ("api_proxy" )
307- opts .api_ssl_verify = _set_boolean ("without_api_ssl_verify" , invert = True )
308- opts .api_user_agent = kwargs .pop ("api_user_agent" )
309- opts .api_headers = kwargs .pop ("api_headers" )
310- opts .rate_limit = _set_boolean ("without_rate_limit" , invert = True )
382+ opts .rate_limit = _pop_boolean_flag (kwargs , "without_rate_limit" , invert = True )
311383 opts .rate_limit_warning = kwargs .pop ("rate_limit_warning" )
312384 opts .error_retry_max = kwargs .pop ("error_retry_max" )
313385 opts .error_retry_backoff = kwargs .pop ("error_retry_backoff" )
@@ -320,7 +392,7 @@ def call_print_rate_limit_info_with_opts(rate_info):
320392 opts .api_config = _initialise_api (
321393 debug = opts .debug ,
322394 host = opts .api_host ,
323- key = opts .api_key ,
395+ credential = opts .credential ,
324396 proxy = opts .api_proxy ,
325397 ssl_verify = opts .api_ssl_verify ,
326398 user_agent = opts .api_user_agent ,
@@ -336,7 +408,7 @@ def call_print_rate_limit_info_with_opts(rate_info):
336408 kwargs ["opts" ] = opts
337409 return ctx .invoke (f , * args , ** kwargs )
338410
339- return wrapper
411+ return resolve_credentials ( wrapper )
340412
341413
342414def initialise_mcp (f ):
0 commit comments