cloudsmith-cli 1.20.1__tar.gz → 1.21.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. {cloudsmith_cli-1.20.1/cloudsmith_cli.egg-info → cloudsmith_cli-1.21.0}/PKG-INFO +1 -1
  2. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +2 -0
  3. cloudsmith_cli-1.21.0/cloudsmith_cli/cli/commands/credential_helper/generic.py +62 -0
  4. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/decorators.py +0 -3
  5. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/exceptions.py +6 -6
  6. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/webserver.py +4 -24
  7. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/files.py +2 -1
  8. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/config.py +2 -2
  9. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/models.py +12 -0
  10. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/download.py +7 -15
  11. cloudsmith_cli-1.21.0/cloudsmith_cli/credential_helpers/generic.py +63 -0
  12. cloudsmith_cli-1.21.0/cloudsmith_cli/data/VERSION +1 -0
  13. cloudsmith_cli-1.21.0/cloudsmith_cli/templates/__init__.py +27 -0
  14. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0/cloudsmith_cli.egg-info}/PKG-INFO +1 -1
  15. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli.egg-info/SOURCES.txt +2 -0
  16. cloudsmith_cli-1.20.1/cloudsmith_cli/data/VERSION +0 -1
  17. cloudsmith_cli-1.20.1/cloudsmith_cli/templates/__init__.py +0 -3
  18. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/LICENSE +0 -0
  19. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/MANIFEST.in +0 -0
  20. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/README.md +0 -0
  21. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/__init__.py +0 -0
  22. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/__main__.py +0 -0
  23. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/__init__.py +0 -0
  24. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/command.py +0 -0
  25. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/__init__.py +0 -0
  26. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/auth.py +0 -0
  27. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/check.py +0 -0
  28. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
  29. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +0 -0
  30. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +0 -0
  31. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
  32. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
  33. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
  34. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/download.py +0 -0
  35. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/entitlements.py +0 -0
  36. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
  37. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/list_.py +0 -0
  38. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/login.py +0 -0
  39. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/logout.py +0 -0
  40. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/main.py +0 -0
  41. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/mcp.py +0 -0
  42. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/metadata.py +0 -0
  43. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
  44. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
  45. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
  46. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
  47. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/move.py +0 -0
  48. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
  49. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
  50. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
  51. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
  52. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
  53. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/push.py +0 -0
  54. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
  55. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
  56. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
  57. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
  58. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
  59. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
  60. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
  61. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/status.py +0 -0
  62. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
  63. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
  64. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
  65. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
  66. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/commands/whoami.py +0 -0
  67. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/config.py +0 -0
  68. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
  69. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/saml.py +0 -0
  70. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/table.py +0 -0
  71. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/types.py +0 -0
  72. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/utils.py +0 -0
  73. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/cli/validators.py +0 -0
  74. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/__init__.py +0 -0
  75. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/__init__.py +0 -0
  76. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/distros.py +0 -0
  77. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
  78. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
  79. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/init.py +0 -0
  80. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/metadata.py +0 -0
  81. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/metrics.py +0 -0
  82. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/orgs.py +0 -0
  83. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/packages.py +0 -0
  84. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/quota.py +0 -0
  85. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/rates.py +0 -0
  86. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/repos.py +0 -0
  87. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/status.py +0 -0
  88. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
  89. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/user.py +0 -0
  90. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/version.py +0 -0
  91. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/api/vulnerabilities.py +0 -0
  92. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/cache_utils.py +0 -0
  93. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
  94. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
  95. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
  96. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
  97. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
  98. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +0 -0
  99. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
  100. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
  101. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
  102. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
  103. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
  104. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
  105. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
  106. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
  107. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
  108. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
  109. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
  110. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
  111. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
  112. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -0
  113. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +0 -0
  114. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/keyring.py +0 -0
  115. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
  116. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/mcp/data.py +0 -0
  117. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/mcp/server.py +0 -0
  118. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/pagination.py +0 -0
  119. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/ratelimits.py +0 -0
  120. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/rest.py +0 -0
  121. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/utils.py +0 -0
  122. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/core/version.py +0 -0
  123. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
  124. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
  125. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/common.py +0 -0
  126. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/custom_domains.py +0 -0
  127. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
  128. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/docker/installer.py +0 -0
  129. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
  130. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/credential_helpers/launchers.py +0 -0
  131. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/data/config.ini +0 -0
  132. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/data/credentials.ini +0 -0
  133. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/templates/auth_error.html +0 -0
  134. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli/templates/auth_success.html +0 -0
  135. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
  136. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
  137. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli.egg-info/requires.txt +0 -0
  138. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
  139. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/pyproject.toml +0 -0
  140. {cloudsmith_cli-1.20.1 → cloudsmith_cli-1.21.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsmith-cli
3
- Version: 1.20.1
3
+ Version: 1.21.0
4
4
  Summary: Cloudsmith Command-Line Interface (CLI)
5
5
  Author-email: Cloudsmith Ltd <support@cloudsmith.io>
6
6
  License-Expression: Apache-2.0
@@ -10,6 +10,7 @@ import click
10
10
 
11
11
  from ..main import main
12
12
  from .docker import docker as docker_cmd
13
+ from .generic import generic as generic_cmd
13
14
  from .manage import install_cmd, list_cmd, uninstall_cmd
14
15
 
15
16
 
@@ -32,6 +33,7 @@ def credential_helper():
32
33
 
33
34
 
34
35
  credential_helper.add_command(docker_cmd, name="docker")
36
+ credential_helper.add_command(generic_cmd, name="generic")
35
37
  credential_helper.add_command(install_cmd, name="install")
36
38
  credential_helper.add_command(uninstall_cmd, name="uninstall")
37
39
  credential_helper.add_command(list_cmd, name="list")
@@ -0,0 +1,62 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """
3
+ Generic credential helper command.
4
+
5
+ Emits a versioned JSON credential document.
6
+ """
7
+
8
+ import sys
9
+
10
+ import click
11
+
12
+ from ....credential_helpers.generic import execute
13
+ from ...decorators import (
14
+ common_api_auth_options,
15
+ common_cli_config_options,
16
+ resolve_credentials,
17
+ )
18
+
19
+
20
+ @click.command()
21
+ @common_cli_config_options
22
+ @common_api_auth_options
23
+ @resolve_credentials
24
+ def generic(opts):
25
+ """
26
+ Emit a Cloudsmith credential as JSON.
27
+
28
+ Resolves a credential through the full provider chain and writes a
29
+ versioned JSON document to stdout. Takes no arguments: a Cloudsmith token
30
+ is organisation-wide, so the host it will be used against does not change
31
+ which credential resolves.
32
+
33
+ \b
34
+ Output (stdout):
35
+ JSON: {"version": 1, "username": "token", "password": "<token>"}
36
+
37
+ \b
38
+ Exit codes:
39
+ 0: Success
40
+ 1: No credential could be resolved
41
+
42
+ Examples:
43
+
44
+ \b
45
+ # Resolve a credential
46
+ $ cloudsmith credential-helper generic
47
+
48
+ \b
49
+ # Extract just the token
50
+ $ cloudsmith credential-helper generic | jq -r .password
51
+
52
+ \b
53
+ Environment variables:
54
+ CLOUDSMITH_API_KEY: API key for authentication (optional)
55
+ """
56
+ exit_code, stdout, stderr = execute(credential=opts.credential)
57
+
58
+ if stdout is not None:
59
+ click.echo(stdout)
60
+ if stderr is not None:
61
+ click.echo(stderr, err=True)
62
+ sys.exit(exit_code)
@@ -246,9 +246,6 @@ def common_api_auth_options(f):
246
246
  opts.api_key_from_flag = None
247
247
  opts.api_key_from_env = None
248
248
 
249
- # Keep opts.api_key populated for any code that still reads it directly.
250
- if api_key:
251
- opts.api_key = api_key
252
249
  kwargs["opts"] = opts
253
250
  return ctx.invoke(f, *args, **kwargs)
254
251
 
@@ -7,7 +7,6 @@ import sys
7
7
  import click
8
8
 
9
9
  from ..core.api.exceptions import ApiException
10
- from ..core.keyring import get_access_token
11
10
 
12
11
 
13
12
  @contextlib.contextmanager
@@ -171,16 +170,17 @@ def get_error_hint(ctx, opts, exc):
171
170
  def get_401_error_hint(ctx, opts, exc):
172
171
  """Get the hint for a 401/Unauthorised error."""
173
172
  # pylint: disable=unused-argument
174
- if opts.api_key:
173
+ credential = getattr(opts, "credential", None)
174
+
175
+ if credential and credential.auth_type == "bearer":
176
+ return "Since you have an SSO access token set, this probably means that it has expired. Try getting a new token with 'cloudsmith auth', then try again."
177
+
178
+ if credential:
175
179
  return (
176
180
  "Since you have an API key set, this probably means "
177
181
  "you don't have the permission to perform this action."
178
182
  )
179
183
 
180
- access_token = get_access_token(opts.api_host)
181
- if access_token:
182
- return "Since you have an SSO access token set, this probably means that it has expired. Try getting a new token with 'cloudsmith auth', then try again."
183
-
184
184
  if ctx.info_name == "token":
185
185
  # This is already the token command
186
186
  return (
@@ -1,5 +1,4 @@
1
1
  import html
2
- import os
3
2
  import socket
4
3
  from functools import cached_property
5
4
  from http.server import BaseHTTPRequestHandler, HTTPServer
@@ -7,6 +6,7 @@ from urllib.parse import parse_qsl, unquote, urlparse
7
6
 
8
7
  import click
9
8
 
9
+ from .. import templates
10
10
  from ..core.api.exceptions import ApiException
11
11
  from ..core.api.init import initialise_api
12
12
  from ..core.credentials.models import CredentialResult
@@ -16,32 +16,12 @@ from .saml import exchange_2fa_token
16
16
 
17
17
  def get_template_path(template_name):
18
18
  """Get the absolute path to a template file."""
19
- base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20
- return os.path.join(base_dir, "templates", template_name)
19
+ return templates.template_path(template_name)
21
20
 
22
21
 
23
22
  def render_template(template_name, **context):
24
- """
25
- Render a template with the given context.
26
-
27
- Args:
28
- template_name: Name of the template file
29
- context: Dictionary of variables to replace in the template
30
-
31
- Returns:
32
- Rendered HTML content
33
- """
34
- template_path = get_template_path(template_name)
35
-
36
- with open(template_path, encoding="utf-8") as file:
37
- content = file.read()
38
-
39
- # Replace placeholders with context values
40
- for key, value in context.items():
41
- placeholder = f"<!-- {key.upper()}_PLACEHOLDER -->"
42
- content = content.replace(placeholder, value if value else "")
43
-
44
- return content
23
+ """Render a template with the given context (see :func:`cloudsmith_cli.templates.render`)."""
24
+ return templates.render(template_name, **context)
45
25
 
46
26
 
47
27
  class AuthenticationWebServer(HTTPServer):
@@ -97,10 +97,11 @@ def upload_file(upload_url, upload_fields, filepath, callback=None):
97
97
  def multi_part_upload_file(
98
98
  opts, upload_url, owner, repo, filepath, callback, upload_id
99
99
  ):
100
+ headers = opts.credential.auth_headers() if opts.credential else {}
101
+
100
102
  with open(filepath, "rb") as f:
101
103
  chunk_number = 1
102
104
  session = create_requests_session()
103
- headers = {"X-Api-Key": opts.api_key}
104
105
  while chunk := f.read(CHUNK_SIZE):
105
106
  resp = session.put(
106
107
  upload_url,
@@ -117,9 +117,9 @@ def new_config_messaging(has_errors, opts, create, api_key):
117
117
  click.secho("Oops, please fix the errors and try again!", fg="red")
118
118
  return
119
119
 
120
- if opts.api_key != api_key:
120
+ if opts.api_key_from_file != api_key:
121
121
  click.echo()
122
- if opts.api_key:
122
+ if opts.api_key_from_file:
123
123
  click.secho(
124
124
  "Note: The above API key doesn't match what you have in "
125
125
  "your default credentials config file.",
@@ -42,3 +42,15 @@ class CredentialResult:
42
42
  source_name: str
43
43
  source_detail: str | None = None
44
44
  auth_type: Literal["api_key", "bearer"] = "api_key"
45
+
46
+ def auth_headers(self) -> dict[str, str]:
47
+ """Return the headers that authenticate a raw request with this credential.
48
+
49
+ The two headers are not interchangeable: a raw JWT in ``X-Api-Key`` is
50
+ rejected outright, aborting the auth chain before the SSO
51
+ authenticators run.
52
+ """
53
+ if self.auth_type == "bearer":
54
+ return {"Authorization": f"Bearer {self.api_key}"}
55
+
56
+ return {"X-Api-Key": self.api_key}
@@ -5,12 +5,11 @@ import hashlib
5
5
  import os
6
6
 
7
7
  import click
8
- import cloudsmith_api
9
8
  import requests
10
9
  from rich.console import Console
11
10
  from rich.table import Table
12
11
 
13
- from . import keyring, ratelimits, utils
12
+ from . import ratelimits, utils
14
13
  from .api.exceptions import catch_raise_api_exception
15
14
  from .api.packages import get_packages_api, list_packages
16
15
  from .rest import create_requests_session
@@ -36,21 +35,14 @@ def resolve_auth(
36
35
  headers = {}
37
36
  auth_source = "none"
38
37
 
39
- # Follow the same authentication logic as the API initialization
40
- # Priority: explicit --api-key > SSO token > configured API key
38
+ credential = getattr(opts, "credential", None)
41
39
 
42
- # Only attempt keyring operations if keyring is enabled
43
- config = cloudsmith_api.Configuration()
44
- access_token = keyring.get_access_token(config.host)
45
- api_key = api_key_opt or getattr(opts, "api_key", None)
46
-
47
- if api_key:
48
- # Prioritize API key (from --api-key option or CLOUDSMITH_API_KEY env var) over SSO
49
- headers["X-Api-Key"] = api_key
40
+ if api_key_opt:
41
+ headers["X-Api-Key"] = api_key_opt
50
42
  auth_source = "api-key"
51
- elif access_token:
52
- headers["Authorization"] = f"Bearer {access_token}"
53
- auth_source = "sso"
43
+ elif credential:
44
+ headers = credential.auth_headers()
45
+ auth_source = "sso" if credential.auth_type == "bearer" else "api-key"
54
46
 
55
47
  return session, headers, auth_source
56
48
 
@@ -0,0 +1,63 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """
3
+ Generic credential helper runtime.
4
+
5
+ Emits a versioned JSON credential document.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ PROTOCOL_VERSION = 1
14
+
15
+ _REFUSAL_MESSAGE = (
16
+ "Error: Unable to retrieve credentials. "
17
+ "Provide credentials via the CLOUDSMITH_API_KEY environment variable, "
18
+ "credentials.ini, the system keyring, or an OIDC service. "
19
+ "Verify current authentication with `cloudsmith whoami --verbose`."
20
+ )
21
+
22
+
23
+ def build_response(credential):
24
+ """
25
+ Build the versioned credential document.
26
+
27
+ Args:
28
+ credential: Pre-resolved CredentialResult from the provider chain
29
+
30
+ Returns:
31
+ dict: The credential document, or None when no credential is available
32
+ """
33
+ if not credential or not credential.api_key:
34
+ return None
35
+
36
+ return {
37
+ "version": PROTOCOL_VERSION,
38
+ "username": "token",
39
+ "password": credential.api_key,
40
+ }
41
+
42
+
43
+ def execute(credential=None) -> tuple[int, str | None, str | None]:
44
+ """
45
+ Resolve a credential into a versioned JSON document.
46
+
47
+ Args:
48
+ credential: Pre-resolved CredentialResult from the provider chain
49
+
50
+ Returns:
51
+ A (exit_code, stdout_text, stderr_text) tuple. Either text value may
52
+ be None if there is nothing to write to that stream. The document is
53
+ serialised in one step, so a partial document can never be emitted.
54
+ """
55
+ try:
56
+ response = build_response(credential)
57
+ if response is None:
58
+ return (1, None, _REFUSAL_MESSAGE)
59
+
60
+ return (0, json.dumps(response), None)
61
+ except Exception as exc: # pylint: disable=broad-except
62
+ logger.debug("generic credential-helper failed: %s", exc, exc_info=True)
63
+ return (1, None, _REFUSAL_MESSAGE)
@@ -0,0 +1 @@
1
+ 1.21.0
@@ -0,0 +1,27 @@
1
+ """Templates for the Cloudsmith CLI (HTML pages, config-file templates).
2
+
3
+ Templates use ``<!-- KEY_PLACEHOLDER -->`` markers so the files stay valid
4
+ HTML/XML and get normal editor validation; :func:`render` substitutes them.
5
+ """
6
+
7
+ import os
8
+
9
+
10
+ def template_path(name):
11
+ """Return the absolute path to the template *name* in this package."""
12
+ return os.path.join(os.path.dirname(os.path.abspath(__file__)), name)
13
+
14
+
15
+ def render(name, **context):
16
+ """Render template *name*, replacing ``<!-- KEY_PLACEHOLDER -->`` markers.
17
+
18
+ Each ``context`` key ``foo`` replaces ``<!-- FOO_PLACEHOLDER -->`` with its
19
+ value (empty string when falsy). Callers are responsible for escaping
20
+ values for the target format (e.g. XML-escaping).
21
+ """
22
+ with open(template_path(name), encoding="utf-8") as handle:
23
+ content = handle.read()
24
+ for key, value in context.items():
25
+ placeholder = f"<!-- {key.upper()}_PLACEHOLDER -->"
26
+ content = content.replace(placeholder, value if value else "")
27
+ return content
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsmith-cli
3
- Version: 1.20.1
3
+ Version: 1.21.0
4
4
  Summary: Cloudsmith Command-Line Interface (CLI)
5
5
  Author-email: Cloudsmith Ltd <support@cloudsmith.io>
6
6
  License-Expression: Apache-2.0
@@ -51,6 +51,7 @@ cloudsmith_cli/cli/commands/vulnerabilities.py
51
51
  cloudsmith_cli/cli/commands/whoami.py
52
52
  cloudsmith_cli/cli/commands/credential_helper/__init__.py
53
53
  cloudsmith_cli/cli/commands/credential_helper/docker.py
54
+ cloudsmith_cli/cli/commands/credential_helper/generic.py
54
55
  cloudsmith_cli/cli/commands/credential_helper/manage.py
55
56
  cloudsmith_cli/cli/commands/metrics/__init__.py
56
57
  cloudsmith_cli/cli/commands/metrics/command.py
@@ -122,6 +123,7 @@ cloudsmith_cli/credential_helpers/__init__.py
122
123
  cloudsmith_cli/credential_helpers/backends.py
123
124
  cloudsmith_cli/credential_helpers/common.py
124
125
  cloudsmith_cli/credential_helpers/custom_domains.py
126
+ cloudsmith_cli/credential_helpers/generic.py
125
127
  cloudsmith_cli/credential_helpers/launchers.py
126
128
  cloudsmith_cli/credential_helpers/docker/__init__.py
127
129
  cloudsmith_cli/credential_helpers/docker/installer.py
@@ -1 +0,0 @@
1
- 1.20.1
@@ -1,3 +0,0 @@
1
- """
2
- HTML templates for Cloudsmith CLI interface.
3
- """
File without changes