cloudsmith-cli 1.20.2__tar.gz → 1.22.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 (143) hide show
  1. {cloudsmith_cli-1.20.2/cloudsmith_cli.egg-info → cloudsmith_cli-1.22.0}/PKG-INFO +2 -2
  2. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/command.py +8 -6
  3. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/__init__.py +1 -0
  4. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/auth.py +1 -1
  5. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/check.py +14 -11
  6. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/copy.py +9 -12
  7. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +2 -0
  8. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +3 -1
  9. cloudsmith_cli-1.22.0/cloudsmith_cli/cli/commands/credential_helper/generic.py +62 -0
  10. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +2 -17
  11. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/delete.py +7 -5
  12. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/dependencies.py +9 -12
  13. cloudsmith_cli-1.22.0/cloudsmith_cli/cli/commands/domains.py +301 -0
  14. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/download.py +34 -28
  15. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/entitlements.py +91 -89
  16. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/list_.py +28 -26
  17. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/login.py +21 -18
  18. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/main.py +7 -14
  19. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/mcp.py +19 -19
  20. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/metadata.py +70 -66
  21. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +14 -12
  22. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/metrics/packages.py +9 -7
  23. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/move.py +10 -8
  24. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/policy/deny.py +37 -32
  25. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/policy/license.py +30 -27
  26. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +29 -30
  27. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/push.py +73 -70
  28. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/quarantine.py +12 -16
  29. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/quota/history.py +5 -3
  30. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/quota/quota.py +5 -3
  31. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/repos.py +34 -31
  32. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/resync.py +7 -6
  33. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/status.py +8 -12
  34. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/tags.py +63 -56
  35. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/tokens.py +31 -24
  36. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/upstream.py +42 -45
  37. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +13 -11
  38. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/whoami.py +5 -3
  39. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/config.py +43 -16
  40. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/decorators.py +9 -7
  41. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/exceptions.py +15 -21
  42. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/saml.py +1 -5
  43. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/utils.py +8 -28
  44. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/validators.py +9 -10
  45. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/webserver.py +2 -2
  46. cloudsmith_cli-1.22.0/cloudsmith_cli/conftest.py +16 -0
  47. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/exceptions.py +11 -0
  48. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/init.py +3 -0
  49. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/packages.py +2 -2
  50. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/quota.py +12 -14
  51. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/upstreams.py +4 -4
  52. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/vulnerabilities.py +2 -2
  53. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/config.py +11 -10
  54. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/models.py +1 -1
  55. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +1 -2
  56. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +1 -1
  57. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/download.py +9 -6
  58. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/keyring.py +5 -3
  59. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/mcp/server.py +14 -13
  60. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/pagination.py +2 -2
  61. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/ratelimits.py +6 -10
  62. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/rest.py +1 -1
  63. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/backends.py +1 -0
  64. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/common.py +11 -18
  65. cloudsmith_cli-1.22.0/cloudsmith_cli/credential_helpers/custom_domains.py +377 -0
  66. cloudsmith_cli-1.22.0/cloudsmith_cli/credential_helpers/default_domains.py +305 -0
  67. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/docker/installer.py +11 -11
  68. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +17 -9
  69. cloudsmith_cli-1.22.0/cloudsmith_cli/credential_helpers/generic.py +63 -0
  70. cloudsmith_cli-1.22.0/cloudsmith_cli/data/VERSION +1 -0
  71. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0/cloudsmith_cli.egg-info}/PKG-INFO +2 -2
  72. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli.egg-info/SOURCES.txt +5 -0
  73. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli.egg-info/requires.txt +1 -1
  74. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/pyproject.toml +7 -1
  75. cloudsmith_cli-1.20.2/cloudsmith_cli/credential_helpers/custom_domains.py +0 -281
  76. cloudsmith_cli-1.20.2/cloudsmith_cli/data/VERSION +0 -1
  77. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/LICENSE +0 -0
  78. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/MANIFEST.in +0 -0
  79. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/README.md +0 -0
  80. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/__init__.py +0 -0
  81. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/__main__.py +0 -0
  82. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/__init__.py +0 -0
  83. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
  84. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
  85. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/logout.py +0 -0
  86. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
  87. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
  88. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
  89. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
  90. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
  91. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
  92. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
  93. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/table.py +0 -0
  94. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/cli/types.py +0 -0
  95. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/__init__.py +0 -0
  96. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/__init__.py +0 -0
  97. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/distros.py +0 -0
  98. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
  99. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/files.py +0 -0
  100. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/metadata.py +0 -0
  101. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/metrics.py +0 -0
  102. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/orgs.py +0 -0
  103. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/rates.py +0 -0
  104. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/repos.py +0 -0
  105. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/status.py +0 -0
  106. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/user.py +0 -0
  107. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/api/version.py +0 -0
  108. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/cache_utils.py +0 -0
  109. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
  110. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
  111. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
  112. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
  113. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
  114. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +0 -0
  115. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
  116. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
  117. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
  118. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
  119. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
  120. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
  121. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
  122. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
  123. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
  124. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
  125. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
  126. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
  127. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -0
  128. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
  129. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/mcp/data.py +0 -0
  130. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/utils.py +0 -0
  131. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/core/version.py +0 -0
  132. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
  133. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
  134. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/credential_helpers/launchers.py +0 -0
  135. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/data/config.ini +0 -0
  136. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/data/credentials.ini +0 -0
  137. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/templates/__init__.py +0 -0
  138. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/templates/auth_error.html +0 -0
  139. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli/templates/auth_success.html +0 -0
  140. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
  141. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
  142. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
  143. {cloudsmith_cli-1.20.2 → cloudsmith_cli-1.22.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsmith-cli
3
- Version: 1.20.2
3
+ Version: 1.22.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
@@ -30,7 +30,7 @@ Requires-Dist: click-spinner>=0.1.7
30
30
  Requires-Dist: json5>=0.9.0
31
31
  Requires-Dist: cloudsmith-api<3.0,>=2.0.29
32
32
  Requires-Dist: keyring>=25.4.1
33
- Requires-Dist: mcp==1.27.2
33
+ Requires-Dist: mcp==1.28.1
34
34
  Requires-Dist: PyJWT[crypto]>=2.0.0
35
35
  Requires-Dist: python-toon==0.1.2
36
36
  Requires-Dist: requests>=2.18.4
@@ -24,9 +24,12 @@ def _is_json_output_requested(exception):
24
24
  return True
25
25
 
26
26
  for idx, arg in enumerate(argv):
27
- if arg in ("-F", "--output-format") and idx + 1 < len(argv):
28
- if argv[idx + 1] in ("json", "pretty_json"):
29
- return True
27
+ if (
28
+ arg in ("-F", "--output-format")
29
+ and idx + 1 < len(argv)
30
+ and argv[idx + 1] in ("json", "pretty_json")
31
+ ):
32
+ return True
30
33
 
31
34
  return False
32
35
 
@@ -88,9 +91,8 @@ class AliasGroup(DYMGroup):
88
91
  return commands
89
92
 
90
93
  def get_command(self, ctx, cmd_name):
91
- if getattr(ctx, "showing_help", False):
92
- if "|" in cmd_name:
93
- cmd_name = cmd_name.split("|")[0]
94
+ if getattr(ctx, "showing_help", False) and "|" in cmd_name:
95
+ cmd_name = cmd_name.split("|")[0]
94
96
 
95
97
  try:
96
98
  cmd_name = self.inverse[cmd_name]
@@ -8,6 +8,7 @@ from . import (
8
8
  delete,
9
9
  dependencies,
10
10
  docs,
11
+ domains,
11
12
  download,
12
13
  entitlements,
13
14
  help_,
@@ -133,7 +133,7 @@ def authenticate(
133
133
  err=True,
134
134
  )
135
135
 
136
- owner = owner[0].strip("'[]'")
136
+ owner = owner[0].strip("[]'")
137
137
 
138
138
  click.echo(
139
139
  f"Beginning authentication for the {click.style(owner, bold=True)} org ... ",
@@ -33,9 +33,11 @@ def rates(ctx, opts):
33
33
  click.echo("Retrieving rate limits ... ", nl=False, err=use_stderr)
34
34
 
35
35
  context_msg = "Failed to retrieve status!"
36
- with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
37
- with maybe_spinner(opts):
38
- resources_limits = get_rate_limits()
36
+ with (
37
+ handle_api_exceptions(ctx, opts=opts, context_msg=context_msg),
38
+ maybe_spinner(opts),
39
+ ):
40
+ resources_limits = get_rate_limits()
39
41
 
40
42
  click.secho("OK", fg="green", err=use_stderr)
41
43
 
@@ -53,11 +55,10 @@ def rates(ctx, opts):
53
55
  "Yes" if limits.throttled else "No",
54
56
  fg="red" if limits.throttled else "green",
55
57
  ),
56
- "%(remaining)s/%(limit)s"
57
- % {
58
- "remaining": click.style(str(limits.remaining), fg="yellow"),
59
- "limit": click.style(str(limits.limit), fg="yellow"),
60
- },
58
+ "{remaining}/{limit}".format(
59
+ remaining=click.style(str(limits.remaining), fg="yellow"),
60
+ limit=click.style(str(limits.limit), fg="yellow"),
61
+ ),
61
62
  click.style(str(limits.interval), fg="blue"),
62
63
  click.style(str(limits.reset), fg="magenta"),
63
64
  ]
@@ -85,9 +86,11 @@ def service(ctx, opts):
85
86
  click.echo("Retrieving service status ... ", nl=False, err=use_stderr)
86
87
 
87
88
  context_msg = "Failed to retrieve status!"
88
- with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
89
- with maybe_spinner(opts):
90
- status, version = get_status(with_version=True)
89
+ with (
90
+ handle_api_exceptions(ctx, opts=opts, context_msg=context_msg),
91
+ maybe_spinner(opts),
92
+ ):
93
+ status, version = get_status(with_version=True)
91
94
 
92
95
  click.secho("OK", fg="green", err=use_stderr)
93
96
 
@@ -59,24 +59,21 @@ def copy(
59
59
  use_stderr = utils.should_use_stderr(opts)
60
60
 
61
61
  click.echo(
62
- "Copying %(slug)s package from %(source)s to %(dest)s ... "
63
- % {
64
- "slug": click.style(slug, bold=True),
65
- "source": click.style(source, bold=True),
66
- "dest": click.style(destination, bold=True),
67
- },
62
+ f"Copying {click.style(slug, bold=True)} package from {click.style(source, bold=True)} to {click.style(destination, bold=True)} ... ",
68
63
  nl=False,
69
64
  err=use_stderr,
70
65
  )
71
66
 
72
67
  context_msg = "Failed to copy package!"
73
- with handle_api_exceptions(
74
- ctx, opts=opts, context_msg=context_msg, reraise_on_error=skip_errors
68
+ with (
69
+ handle_api_exceptions(
70
+ ctx, opts=opts, context_msg=context_msg, reraise_on_error=skip_errors
71
+ ),
72
+ maybe_spinner(opts),
75
73
  ):
76
- with maybe_spinner(opts):
77
- _, new_slug = copy_package(
78
- owner=owner, repo=source, identifier=slug, destination=destination
79
- )
74
+ _, new_slug = copy_package(
75
+ owner=owner, repo=source, identifier=slug, destination=destination
76
+ )
80
77
 
81
78
  click.secho("OK", fg="green", err=use_stderr)
82
79
 
@@ -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")
@@ -29,7 +29,8 @@ def docker(opts, operation):
29
29
 
30
30
  Provides credentials for all Cloudsmith Docker registries: ``*.cloudsmith.io``,
31
31
  ``*.cloudsmith.com``, and any custom domains configured for the organisation
32
- (requires CLOUDSMITH_ORG and a valid API key/token).
32
+ (requires an organisation - ``--org``, CLOUDSMITH_ORG or ``org`` in
33
+ ``config.ini`` - and a valid API key/token).
33
34
 
34
35
  Input (stdin):
35
36
  Server URL as plain text (e.g. "docker.cloudsmith.io")
@@ -57,6 +58,7 @@ def docker(opts, operation):
57
58
  sys.stdin,
58
59
  credential=opts.credential,
59
60
  api_host=opts.api_host,
61
+ org=opts.org,
60
62
  )
61
63
 
62
64
  if stdout is not None:
@@ -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)
@@ -8,7 +8,6 @@ Provides ``credential-helper install``, ``credential-helper uninstall``, and
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- import os
12
11
  import sys
13
12
 
14
13
  import click
@@ -94,11 +93,6 @@ def _get_installer(name: str):
94
93
  default=False,
95
94
  help="Bypass the custom-domain cache and fetch fresh data from the API.",
96
95
  )
97
- @click.option(
98
- "--org",
99
- default=None,
100
- help="Cloudsmith organisation slug for custom-domain discovery.",
101
- )
102
96
  @common_cli_config_options
103
97
  @common_cli_output_options
104
98
  @common_api_auth_options
@@ -113,7 +107,6 @@ def install_cmd(
113
107
  dry_run: bool,
114
108
  no_discover: bool,
115
109
  refresh: bool,
116
- org: str | None,
117
110
  ) -> None:
118
111
  """Install a credential helper launcher and configure the package manager.
119
112
 
@@ -138,13 +131,6 @@ def install_cmd(
138
131
  $ cloudsmith credential-helper install docker --no-discover
139
132
  """
140
133
  installer = _get_installer(helper)
141
- org = org or os.environ.get("CLOUDSMITH_ORG", "").strip() or None
142
- api_key = opts.credential.api_key if opts.credential else None
143
- auth_type = (
144
- getattr(opts.credential, "auth_type", "api_key")
145
- if opts.credential
146
- else "api_key"
147
- )
148
134
  try:
149
135
  actions = installer.install(
150
136
  bin_dir=bin_dir,
@@ -152,9 +138,8 @@ def install_cmd(
152
138
  dry_run=dry_run,
153
139
  discover=not no_discover,
154
140
  refresh=refresh,
155
- org=org,
156
- api_key=api_key,
157
- auth_type=auth_type,
141
+ org=opts.org,
142
+ credential=opts.credential,
158
143
  api_host=opts.api_host,
159
144
  )
160
145
  except OSError as exc:
@@ -47,20 +47,22 @@ def delete(ctx, opts, owner_repo_package, yes):
47
47
 
48
48
  use_stderr = utils.should_use_stderr(opts)
49
49
 
50
- prompt = "delete the %(package)s from %(owner)s/%(repo)s" % delete_args
50
+ prompt = "delete the {package} from {owner}/{repo}".format(**delete_args)
51
51
  if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
52
52
  return
53
53
 
54
54
  click.echo(
55
- "Deleting %(package)s from %(owner)s/%(repo)s ... " % delete_args,
55
+ "Deleting {package} from {owner}/{repo} ... ".format(**delete_args),
56
56
  nl=False,
57
57
  err=use_stderr,
58
58
  )
59
59
 
60
60
  context_msg = "Failed to delete the package!"
61
- with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
62
- with maybe_spinner(opts):
63
- delete_package(owner=owner, repo=repo, identifier=slug)
61
+ with (
62
+ handle_api_exceptions(ctx, opts=opts, context_msg=context_msg),
63
+ maybe_spinner(opts),
64
+ ):
65
+ delete_package(owner=owner, repo=repo, identifier=slug)
64
66
 
65
67
  click.secho("OK", fg="green", err=use_stderr)
66
68
 
@@ -53,23 +53,20 @@ def list_dependencies(ctx, opts, owner_repo_package):
53
53
  use_stderr = utils.should_use_stderr(opts)
54
54
 
55
55
  click.echo(
56
- "Getting direct (non-transitive) dependencies of %(package)s in "
57
- "%(owner)s/%(repo)s ... "
58
- % {
59
- "owner": click.style(owner, bold=True),
60
- "repo": click.style(repo, bold=True),
61
- "package": click.style(identifier, bold=True),
62
- },
56
+ f"Getting direct (non-transitive) dependencies of {click.style(identifier, bold=True)} in "
57
+ f"{click.style(owner, bold=True)}/{click.style(repo, bold=True)} ... ",
63
58
  nl=False,
64
59
  err=use_stderr,
65
60
  )
66
61
 
67
62
  context_msg = "Failed to get dependencies of package!"
68
- with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
69
- with maybe_spinner(opts):
70
- deps, page_info = get_package_dependencies(
71
- owner=owner, repo=repo, identifier=identifier
72
- )
63
+ with (
64
+ handle_api_exceptions(ctx, opts=opts, context_msg=context_msg),
65
+ maybe_spinner(opts),
66
+ ):
67
+ deps, page_info = get_package_dependencies(
68
+ owner=owner, repo=repo, identifier=identifier
69
+ )
73
70
 
74
71
  click.secho("OK", fg="green", err=use_stderr)
75
72
 
@@ -0,0 +1,301 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """List the hosts Cloudsmith can authenticate: built-in and custom domains.
3
+
4
+ The built-in ``*.cloudsmith.io`` service hosts ship with the CLI so that every
5
+ consumer shares one authoritative table; custom domains cannot be inferred, so
6
+ discovering them requires an API lookup.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+
14
+ import click
15
+ from click.core import ParameterSource
16
+
17
+ from ...core.api.exceptions import ApiException
18
+ from ...core.pagination import PageInfo
19
+ from ...credential_helpers.custom_domains import (
20
+ get_custom_domains,
21
+ order_by_precedence,
22
+ read_all_cached_domains,
23
+ )
24
+ from ...credential_helpers.default_domains import (
25
+ DomainType,
26
+ format_for_backend_kind,
27
+ load_default_domains,
28
+ untrusted_config_declares_domains,
29
+ )
30
+ from .. import command
31
+ from ..config import get_default_config_path
32
+ from ..decorators import (
33
+ common_api_auth_options,
34
+ common_cli_config_options,
35
+ common_cli_list_options,
36
+ initialise_api,
37
+ )
38
+ from .main import main
39
+
40
+ PROTOCOL_VERSION = 1
41
+
42
+
43
+ def _label(value: str | None) -> str | None:
44
+ """Normalise a filter value for comparison, or None if it is empty."""
45
+ return (value or "").strip().lower() or None
46
+
47
+
48
+ def _serves_format(entry_format: str | None, wanted: str | None) -> bool:
49
+ """Whether an entry's format satisfies a ``--format`` filter."""
50
+ return wanted is None or entry_format is None or entry_format == wanted
51
+
52
+
53
+ def _is_domain_type(domain_type: DomainType, wanted: str | None) -> bool:
54
+ """Whether a host's purpose satisfies a ``--domain-type`` filter."""
55
+ return wanted is None or domain_type.value == wanted
56
+
57
+
58
+ def _custom_entries(records) -> list[dict]:
59
+ """Render custom-domain records as document entries, in the order given."""
60
+ return [
61
+ {
62
+ "host": record.host,
63
+ "format": format_for_backend_kind(record.backend_kind),
64
+ "type": "custom",
65
+ "domain_type": record.domain_type.value,
66
+ "org": record.org,
67
+ "repository": record.repository,
68
+ "primary": record.primary,
69
+ "created_at": record.created_at,
70
+ }
71
+ for record in records
72
+ ]
73
+
74
+
75
+ @main.group(name="domains", cls=command.AliasGroup, aliases=["domain"])
76
+ def domains_():
77
+ """
78
+ Inspect the Cloudsmith domains the CLI can authenticate.
79
+
80
+ See the help for subcommands for more information on each.
81
+ """
82
+
83
+
84
+ @domains_.command(name="list", aliases=["ls"])
85
+ @click.option(
86
+ "--refresh",
87
+ is_flag=True,
88
+ default=False,
89
+ help="Bypass the custom-domain cache and fetch fresh data from the API.",
90
+ )
91
+ @click.option(
92
+ "--format",
93
+ "format_",
94
+ default=None,
95
+ help="Only list hosts usable for this package format. Hosts that serve no "
96
+ "single format (the download CDN, the generic upload endpoint) serve every "
97
+ "format and are always listed.",
98
+ )
99
+ @click.option(
100
+ "--domain-type",
101
+ "domain_type",
102
+ default=None,
103
+ help="Only list hosts with this purpose: download, upload, or native_api. "
104
+ "Every host has exactly one, so this is an exact match.",
105
+ )
106
+ @click.option(
107
+ "--repo",
108
+ "--repository",
109
+ "repo",
110
+ default=None,
111
+ help="Only list hosts usable for this repository, most-preferred first. "
112
+ "Drops custom domains bound to a different repository.",
113
+ )
114
+ @common_cli_config_options
115
+ @common_cli_list_options
116
+ @common_api_auth_options
117
+ @initialise_api
118
+ @click.pass_context
119
+ def list_domains( # pylint: disable=too-many-arguments
120
+ ctx,
121
+ opts,
122
+ refresh: bool,
123
+ format_: str | None,
124
+ domain_type: str | None,
125
+ repo: str | None,
126
+ page: int,
127
+ page_size: int,
128
+ page_all: bool,
129
+ ) -> None:
130
+ """Emit the Cloudsmith hosts and their package formats as JSON.
131
+
132
+ Machine-readable output for programmatic consumers. It lists the built-in
133
+ ``*.cloudsmith.io`` service hosts alongside custom domains, with ``type``
134
+ distinguishing ``default`` from ``custom`` and ``domain_type`` saying what
135
+ each host is for: ``download``, ``upload``, or ``native_api`` for a host
136
+ speaking one package format's own protocol. An organisation's own custom
137
+ domains are listed ahead of the built-in hosts, which serve as the
138
+ fallback. Only usable hosts are listed: a custom domain that is disabled or
139
+ not yet validated serves nothing, so it is left out rather than offered as
140
+ somewhere to publish to. Check it in the Cloudsmith UI if one you expect is
141
+ missing here.
142
+
143
+ Built-in hosts are always listed and need no organisation or authentication.
144
+ An organisation from ``--org``, CLOUDSMITH_ORG or ``org`` in ``config.ini``
145
+ adds its custom domains, and a failed lookup exits non-zero rather than
146
+ rendering as "no domains". With no organisation the command lists whatever
147
+ earlier runs cached and makes no API call; ``--refresh`` bypasses that cache
148
+ for a configured organisation.
149
+
150
+ Where two custom domains could serve the same request Cloudsmith picks the
151
+ one bound to the repository in hand, then ``primary`` over secondary, then
152
+ the oldest — so ``created_at`` is reported to make that order reproducible.
153
+ The custom domains are always listed in that order, so the first is the one
154
+ Cloudsmith would bind; ``--repo`` is what lets the repository-bound hosts
155
+ take part in it.
156
+
157
+ Results support the same --page/--page-size (-p/-l) options as other
158
+ `list` commands, selecting a page of the combined, filtered list. Unlike
159
+ those commands, this one defaults to --page-all (--show-all): the
160
+ combined list is small and built from a local cache rather than a live
161
+ paginated endpoint, so there is no benefit to hiding entries by default.
162
+ Passing --page or --page-size switches to paged output.
163
+
164
+ Output (stdout):
165
+ JSON: {"version": 1, "domains": [{"host": ..., "format": ...,
166
+ "type": ..., "domain_type": ..., "org": ..., "repository": ...,
167
+ "primary": ..., "created_at": ...}], "meta": {"pagination": {...}}}
168
+
169
+ "meta" is only present when the result is paginated (i.e. not
170
+ --page-all), and mirrors the pagination metadata other `list`
171
+ commands emit for `-F json`.
172
+
173
+ Examples:
174
+
175
+ \b
176
+ # List built-in hosts plus any custom domains already cached
177
+ $ cloudsmith domains list
178
+
179
+ \b
180
+ # List built-in hosts plus an organisation's custom domains
181
+ $ cloudsmith domains list --org my-org
182
+
183
+ \b
184
+ # The hosts usable for one repository, most-preferred first
185
+ $ cloudsmith domains list --org my-org --repo my-repo --format maven
186
+
187
+ \b
188
+ # Where to upload to
189
+ $ cloudsmith domains list --org my-org --domain-type upload
190
+ """
191
+ if not page_all:
192
+ explicit_sources = {
193
+ src
194
+ for src in (
195
+ ParameterSource.COMMANDLINE,
196
+ ParameterSource.ENVIRONMENT,
197
+ getattr(ParameterSource, "PROMPT", None),
198
+ )
199
+ if src is not None
200
+ }
201
+ page_explicit = ctx.get_parameter_source("page") in explicit_sources
202
+ page_size_explicit = ctx.get_parameter_source("page_size") in explicit_sources
203
+ if not page_explicit and not page_size_explicit:
204
+ # Unlike other `list` commands, domains list defaults to showing
205
+ # everything: the combined list is small and built from a local
206
+ # cache, so paging by default would hide entries for no benefit.
207
+ page_all = True
208
+
209
+ explicit_config = ctx.meta.get("config_file")
210
+ if explicit_config and os.path.isdir(explicit_config):
211
+ explicit_config = os.path.join(explicit_config, "config.ini")
212
+
213
+ if explicit_config is None and untrusted_config_declares_domains():
214
+ click.secho(
215
+ "Warning: ignoring [domains] section in ./config.ini -- config.ini "
216
+ "in the current directory is untrusted. Put it in "
217
+ f"{get_default_config_path()} or ~/.cloudsmith instead.",
218
+ fg="yellow",
219
+ err=True,
220
+ )
221
+
222
+ wanted_format = _label(format_)
223
+ wanted_type = _label(domain_type)
224
+ repository = _label(repo)
225
+
226
+ default_entries = [
227
+ {
228
+ "host": domain.host,
229
+ "format": domain.format_label,
230
+ "type": "default",
231
+ "domain_type": domain.domain_type.value,
232
+ "org": None,
233
+ "repository": None,
234
+ "primary": True,
235
+ "created_at": None,
236
+ }
237
+ for domain in load_default_domains(config_path=explicit_config)
238
+ if _serves_format(domain.format_label, wanted_format)
239
+ and _is_domain_type(domain.domain_type, wanted_type)
240
+ ]
241
+
242
+ org = opts.org
243
+ if org:
244
+ try:
245
+ records = get_custom_domains(
246
+ org,
247
+ credential=opts.credential,
248
+ api_host=opts.api_host,
249
+ refresh=refresh,
250
+ strict=True,
251
+ configure_api=False,
252
+ )
253
+ except ApiException as exc:
254
+ raise click.ClickException(
255
+ f"Failed to fetch custom domains for {org!r}: {exc}"
256
+ ) from exc
257
+ else:
258
+ if refresh:
259
+ click.secho(
260
+ "Warning: --refresh needs an organisation to fetch from, so the "
261
+ "cached custom domains below are unchanged. Set --org, "
262
+ "CLOUDSMITH_ORG or org in config.ini.",
263
+ fg="yellow",
264
+ err=True,
265
+ )
266
+ records = read_all_cached_domains()
267
+
268
+ records = [
269
+ record
270
+ for record in records
271
+ if _serves_format(format_for_backend_kind(record.backend_kind), wanted_format)
272
+ and _is_domain_type(record.domain_type, wanted_type)
273
+ and record.is_active
274
+ and (repository is None or record.serves_repository(repository))
275
+ ]
276
+ records = sorted(
277
+ order_by_precedence(records, repository), key=lambda record: record.org
278
+ )
279
+
280
+ data = _custom_entries(records) + default_entries
281
+
282
+ document = {"version": PROTOCOL_VERSION, "domains": data}
283
+
284
+ if not page_all:
285
+ if page < 1 or page_size < 1:
286
+ raise click.UsageError("--page and --page-size must be positive.")
287
+
288
+ total = len(data)
289
+ start = (page - 1) * page_size
290
+ page_info = PageInfo()
291
+ page_info.count = total
292
+ page_info.page = page
293
+ page_info.page_size = page_size
294
+ page_info.page_total = -(-total // page_size) or 1
295
+
296
+ document["domains"] = data[start : start + page_size]
297
+ document["meta"] = {
298
+ "pagination": page_info.as_dict(num_results=len(document["domains"]))
299
+ }
300
+
301
+ click.echo(json.dumps(document))