cloudsmith-cli 1.12.1__tar.gz → 1.14.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 (135) hide show
  1. {cloudsmith_cli-1.12.1/cloudsmith_cli.egg-info → cloudsmith_cli-1.14.0}/PKG-INFO +45 -2
  2. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/README.md +42 -0
  3. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/__init__.py +2 -0
  4. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/auth.py +70 -13
  5. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/download.py +12 -1
  6. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/logout.py +151 -0
  7. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/tokens.py +71 -0
  8. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/vulnerabilities.py +141 -0
  9. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/whoami.py +193 -0
  10. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/config.py +38 -0
  11. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/conftest.py +27 -0
  12. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_auth.py +283 -0
  13. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_download.py +53 -1
  14. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_logout.py +147 -0
  15. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_tokens.py +75 -8
  16. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_vulnerabilities.py +112 -0
  17. cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/test_webserver.py +155 -0
  18. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/utils.py +24 -0
  19. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/webserver.py +23 -10
  20. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/init.py +34 -16
  21. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/user.py +10 -0
  22. cloudsmith_cli-1.14.0/cloudsmith_cli/core/api/vulnerabilities.py +230 -0
  23. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/config.py +22 -0
  24. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/download.py +29 -1
  25. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/keyring.py +51 -0
  26. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/server.py +4 -10
  27. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_download.py +90 -0
  28. cloudsmith_cli-1.14.0/cloudsmith_cli/core/tests/test_init.py +371 -0
  29. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_keyring.py +99 -5
  30. cloudsmith_cli-1.14.0/cloudsmith_cli/data/VERSION +1 -0
  31. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0/cloudsmith_cli.egg-info}/PKG-INFO +45 -2
  32. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/SOURCES.txt +8 -0
  33. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/requires.txt +2 -1
  34. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/setup.py +2 -1
  35. cloudsmith_cli-1.12.1/cloudsmith_cli/cli/commands/whoami.py +0 -62
  36. cloudsmith_cli-1.12.1/cloudsmith_cli/core/tests/test_init.py +0 -186
  37. cloudsmith_cli-1.12.1/cloudsmith_cli/data/VERSION +0 -1
  38. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/LICENSE +0 -0
  39. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/MANIFEST.in +0 -0
  40. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/__init__.py +0 -0
  41. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/__main__.py +0 -0
  42. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/__init__.py +0 -0
  43. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/command.py +0 -0
  44. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/check.py +0 -0
  45. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
  46. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
  47. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
  48. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
  49. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/entitlements.py +0 -0
  50. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
  51. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/list_.py +0 -0
  52. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/login.py +0 -0
  53. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/main.py +0 -0
  54. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/mcp.py +0 -0
  55. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
  56. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
  57. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
  58. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
  59. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/move.py +0 -0
  60. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
  61. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
  62. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
  63. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
  64. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
  65. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/push.py +0 -0
  66. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
  67. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
  68. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
  69. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
  70. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
  71. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
  72. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
  73. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/status.py +0 -0
  74. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
  75. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
  76. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/decorators.py +0 -0
  77. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/exceptions.py +0 -0
  78. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/saml.py +0 -0
  79. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/table.py +0 -0
  80. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/__init__.py +0 -0
  81. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/__init__.py +0 -0
  82. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/__init__.py +0 -0
  83. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_deny.py +0 -0
  84. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_licence.py +0 -0
  85. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_vulnerability.py +0 -0
  86. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_check.py +0 -0
  87. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_entitlements.py +0 -0
  88. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_login.py +0 -0
  89. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_main.py +0 -0
  90. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_mcp.py +0 -0
  91. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_package_commands.py +0 -0
  92. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_repos.py +0 -0
  93. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_upstream.py +0 -0
  94. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/conftest.py +0 -0
  95. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_push.py +0 -0
  96. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_saml.py +0 -0
  97. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_utils.py +0 -0
  98. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/utils.py +0 -0
  99. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/types.py +0 -0
  100. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/validators.py +0 -0
  101. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/__init__.py +0 -0
  102. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/__init__.py +0 -0
  103. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/distros.py +0 -0
  104. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
  105. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
  106. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/files.py +0 -0
  107. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/metrics.py +0 -0
  108. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/orgs.py +0 -0
  109. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/packages.py +0 -0
  110. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/quota.py +0 -0
  111. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/rates.py +0 -0
  112. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/repos.py +0 -0
  113. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/status.py +0 -0
  114. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
  115. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/version.py +0 -0
  116. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
  117. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/data.py +0 -0
  118. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/pagination.py +0 -0
  119. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/ratelimits.py +0 -0
  120. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/rest.py +0 -0
  121. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/__init__.py +0 -0
  122. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_rest.py +0 -0
  123. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_version.py +0 -0
  124. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/utils.py +0 -0
  125. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/version.py +0 -0
  126. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/data/config.ini +0 -0
  127. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/data/credentials.ini +0 -0
  128. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/__init__.py +0 -0
  129. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/auth_error.html +0 -0
  130. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/auth_success.html +0 -0
  131. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
  132. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
  133. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/not-zip-safe +0 -0
  134. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
  135. {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsmith-cli
3
- Version: 1.12.1
3
+ Version: 1.14.0
4
4
  Summary: Cloudsmith Command-Line Interface (CLI)
5
5
  Home-page: https://github.com/cloudsmith-io/cloudsmith-cli
6
6
  Author: Cloudsmith Ltd
@@ -34,9 +34,10 @@ Requires-Dist: json5>=0.9.0
34
34
  Requires-Dist: cloudsmith-api<3.0,>=2.0.24
35
35
  Requires-Dist: keyring>=25.4.1
36
36
  Requires-Dist: mcp==1.9.1
37
- Requires-Dist: toon-python==0.1.2
37
+ Requires-Dist: python-toon==0.1.2
38
38
  Requires-Dist: requests>=2.18.4
39
39
  Requires-Dist: requests_toolbelt>=1.0.0
40
+ Requires-Dist: rich>=13.0.0
40
41
  Requires-Dist: semver>=2.7.9
41
42
  Requires-Dist: urllib3>=2.5
42
43
  Dynamic: author
@@ -87,6 +88,7 @@ The CLI currently supports the following commands (and sub-commands):
87
88
  - `delete`|`rm`: Delete a package from a repository.
88
89
  - `dependencies`|`deps`: List direct (non-transitive) dependencies for a package.
89
90
  - `docs`: Launch the help website in your browser.
91
+ - `download`: Download a package from a repository.
90
92
  - `entitlements`|`ents`: Manage the entitlements for a repository.
91
93
  - `create`|`new`: Create a new entitlement in a repository.
92
94
  - `delete`|`rm`: Delete an entitlement from a repository.
@@ -102,6 +104,7 @@ The CLI currently supports the following commands (and sub-commands):
102
104
  - `packages`: List packages for a repository. (Aliases `repos list`)
103
105
  - `repos`: List repositories for a namespace (owner).
104
106
  - `login`|`token`: Retrieve your API authentication token/key via login.
107
+ - `logout`: Clear stored authentication credentials and SSO tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
105
108
  - `metrics`: Metrics and statistics for a repository.
106
109
  - `tokens`: Retrieve bandwidth usage for entitlement tokens.
107
110
  - `packages`: Retrieve package usage for repository.
@@ -167,6 +170,7 @@ The CLI currently supports the following commands (and sub-commands):
167
170
  - `rpm`: Manage rpm upstreams for a repository.
168
171
  - `ruby`: Manage ruby upstreams for a repository.
169
172
  - `swift`: Manage swift upstreams for a repository.
173
+ - `vulnerabilities`: Retrieve vulnerability results for a package.
170
174
  - `whoami`: Retrieve your current authentication status.
171
175
 
172
176
  ## Installation
@@ -304,6 +308,45 @@ cloudsmith push rpm --help
304
308
  ```
305
309
 
306
310
 
311
+ ## Downloading Packages
312
+
313
+ You can download packages from repositories using the `cloudsmith download` command. The CLI supports various filtering options to help you find and download the exact package you need.
314
+
315
+ For example, to download a specific package:
316
+
317
+ ```
318
+ cloudsmith download your-account/your-repo package-name
319
+ ```
320
+
321
+ You can filter by various attributes like version, format, architecture, operating system, and tags:
322
+
323
+ ```
324
+ # Download a specific version
325
+ cloudsmith download your-account/your-repo package-name --version 1.2.3
326
+
327
+ # Filter by format and architecture
328
+ cloudsmith download your-account/your-repo package-name --format deb --arch amd64
329
+
330
+ # Filter by package tag (e.g., latest, stable, beta)
331
+ cloudsmith download your-account/your-repo package-name --tag latest
332
+
333
+ # Combine tag with metadata filters
334
+ cloudsmith download your-account/your-repo package-name --tag stable --format deb --arch arm64
335
+
336
+ # Download all associated files (POM, sources, javadoc, etc.)
337
+ cloudsmith download your-account/your-repo package-name --all-files
338
+
339
+ # Preview what would be downloaded without actually downloading
340
+ cloudsmith download your-account/your-repo package-name --dry-run
341
+ ```
342
+
343
+ For more advanced usage and all available options:
344
+
345
+ ```
346
+ cloudsmith download --help
347
+ ```
348
+
349
+
307
350
  ## Contributing
308
351
 
309
352
  Yes! Please do contribute, this is why we love open source. Please see [CONTRIBUTING](https://github.com/cloudsmith-io/cloudsmith-cli/blob/master/CONTRIBUTING.md) for contribution guidelines when making code changes or raising issues for bug reports, ideas, discussions and/or questions (i.e. help required).
@@ -32,6 +32,7 @@ The CLI currently supports the following commands (and sub-commands):
32
32
  - `delete`|`rm`: Delete a package from a repository.
33
33
  - `dependencies`|`deps`: List direct (non-transitive) dependencies for a package.
34
34
  - `docs`: Launch the help website in your browser.
35
+ - `download`: Download a package from a repository.
35
36
  - `entitlements`|`ents`: Manage the entitlements for a repository.
36
37
  - `create`|`new`: Create a new entitlement in a repository.
37
38
  - `delete`|`rm`: Delete an entitlement from a repository.
@@ -47,6 +48,7 @@ The CLI currently supports the following commands (and sub-commands):
47
48
  - `packages`: List packages for a repository. (Aliases `repos list`)
48
49
  - `repos`: List repositories for a namespace (owner).
49
50
  - `login`|`token`: Retrieve your API authentication token/key via login.
51
+ - `logout`: Clear stored authentication credentials and SSO tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
50
52
  - `metrics`: Metrics and statistics for a repository.
51
53
  - `tokens`: Retrieve bandwidth usage for entitlement tokens.
52
54
  - `packages`: Retrieve package usage for repository.
@@ -112,6 +114,7 @@ The CLI currently supports the following commands (and sub-commands):
112
114
  - `rpm`: Manage rpm upstreams for a repository.
113
115
  - `ruby`: Manage ruby upstreams for a repository.
114
116
  - `swift`: Manage swift upstreams for a repository.
117
+ - `vulnerabilities`: Retrieve vulnerability results for a package.
115
118
  - `whoami`: Retrieve your current authentication status.
116
119
 
117
120
  ## Installation
@@ -249,6 +252,45 @@ cloudsmith push rpm --help
249
252
  ```
250
253
 
251
254
 
255
+ ## Downloading Packages
256
+
257
+ You can download packages from repositories using the `cloudsmith download` command. The CLI supports various filtering options to help you find and download the exact package you need.
258
+
259
+ For example, to download a specific package:
260
+
261
+ ```
262
+ cloudsmith download your-account/your-repo package-name
263
+ ```
264
+
265
+ You can filter by various attributes like version, format, architecture, operating system, and tags:
266
+
267
+ ```
268
+ # Download a specific version
269
+ cloudsmith download your-account/your-repo package-name --version 1.2.3
270
+
271
+ # Filter by format and architecture
272
+ cloudsmith download your-account/your-repo package-name --format deb --arch amd64
273
+
274
+ # Filter by package tag (e.g., latest, stable, beta)
275
+ cloudsmith download your-account/your-repo package-name --tag latest
276
+
277
+ # Combine tag with metadata filters
278
+ cloudsmith download your-account/your-repo package-name --tag stable --format deb --arch arm64
279
+
280
+ # Download all associated files (POM, sources, javadoc, etc.)
281
+ cloudsmith download your-account/your-repo package-name --all-files
282
+
283
+ # Preview what would be downloaded without actually downloading
284
+ cloudsmith download your-account/your-repo package-name --dry-run
285
+ ```
286
+
287
+ For more advanced usage and all available options:
288
+
289
+ ```
290
+ cloudsmith download --help
291
+ ```
292
+
293
+
252
294
  ## Contributing
253
295
 
254
296
  Yes! Please do contribute, this is why we love open source. Please see [CONTRIBUTING](https://github.com/cloudsmith-io/cloudsmith-cli/blob/master/CONTRIBUTING.md) for contribution guidelines when making code changes or raising issues for bug reports, ideas, discussions and/or questions (i.e. help required).
@@ -12,6 +12,7 @@ from . import (
12
12
  help_,
13
13
  list_,
14
14
  login,
15
+ logout,
15
16
  mcp,
16
17
  metrics,
17
18
  move,
@@ -25,5 +26,6 @@ from . import (
25
26
  tags,
26
27
  tokens,
27
28
  upstream,
29
+ vulnerabilities,
28
30
  whoami,
29
31
  )
@@ -9,14 +9,16 @@ from ..exceptions import handle_api_exceptions
9
9
  from ..saml import create_configured_session, get_idp_url
10
10
  from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
11
11
  from .main import main
12
- from .tokens import create
12
+ from .tokens import create, request_api_key
13
13
 
14
14
  # Authentication server configuration
15
15
  AUTH_SERVER_HOST = "127.0.0.1"
16
16
  AUTH_SERVER_PORT = 12400
17
17
 
18
18
 
19
- def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=False):
19
+ def _perform_saml_authentication(
20
+ opts, owner, enable_token_creation=False, use_stderr=False
21
+ ):
20
22
  """Perform SAML authentication via web browser and local web server."""
21
23
  session = create_configured_session(opts)
22
24
  api_host = opts.api_config.host
@@ -25,12 +27,12 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
25
27
 
26
28
  click.echo(
27
29
  f"Opening your organization's SAML IDP URL in your browser: {click.style(idp_url, bold=True)}",
28
- err=json,
30
+ err=use_stderr,
29
31
  )
30
- click.echo(err=json)
32
+ click.echo(err=use_stderr)
31
33
  webbrowser.open(idp_url)
32
34
 
33
- click.echo("Starting webserver to begin authentication ... ", err=json)
35
+ click.echo("Starting webserver to begin authentication ... ", err=use_stderr)
34
36
 
35
37
  auth_server = AuthenticationWebServer(
36
38
  (AUTH_SERVER_HOST, AUTH_SERVER_PORT),
@@ -60,14 +62,14 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
60
62
  "--token",
61
63
  default=False,
62
64
  is_flag=True,
63
- help="Retrieve a user API token after successful authentication.",
65
+ help="[DEPRECATED: Use --request-api-key] Retrieve a user API token after successful authentication.",
64
66
  )
65
67
  @click.option(
66
68
  "-f",
67
69
  "--force",
68
70
  default=False,
69
71
  is_flag=True,
70
- help="Force refresh of user API token without prompts.",
72
+ help="[DEPRECATED: Use --request-api-key] Force refresh of user API token without prompts.",
71
73
  )
72
74
  @click.option(
73
75
  "--save-config",
@@ -79,17 +81,49 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
79
81
  "--json",
80
82
  default=False,
81
83
  is_flag=True,
82
- help="Output token details in json format.",
84
+ help="[DEPRECATED: Use --output-format json] Output token details in JSON format.",
85
+ )
86
+ @click.option(
87
+ "--request-api-key",
88
+ "request_api_key_flag",
89
+ default=False,
90
+ is_flag=True,
91
+ help="Retrieve API token (auto-creates or auto-rotates, no prompts). "
92
+ "Warning: If token exists, this will rotate it and invalidate the old key.",
83
93
  )
84
94
  @decorators.common_cli_config_options
85
95
  @decorators.common_cli_output_options
86
96
  @decorators.initialise_api
87
97
  @click.pass_context
88
- def authenticate(ctx, opts, owner, token, force, save_config, json):
98
+ def authenticate(
99
+ ctx, opts, owner, token, force, save_config, json, request_api_key_flag
100
+ ):
89
101
  """Authenticate to Cloudsmith using the org's SAML setup."""
90
- json = json or utils.should_use_stderr(opts)
91
- # If using json output, we redirect info messages to stderr
92
- use_stderr = json
102
+ # Validate mutual exclusivity
103
+ if request_api_key_flag and (token or force):
104
+ raise click.UsageError(
105
+ "--request-api-key cannot be used with --token or --force. "
106
+ "Use --request-api-key alone for fully automated token retrieval."
107
+ )
108
+
109
+ # Determine if we should redirect info messages to stderr
110
+ use_stderr = request_api_key_flag or json or utils.should_use_stderr(opts)
111
+
112
+ if token:
113
+ click.secho(
114
+ "DEPRECATION WARNING: The `--token` flag is deprecated and will be removed in a future release. "
115
+ "Please use `--request-api-key` instead.",
116
+ fg="yellow",
117
+ err=True,
118
+ )
119
+
120
+ if force:
121
+ click.secho(
122
+ "DEPRECATION WARNING: The `--force` flag is deprecated and will be removed in a future release. "
123
+ "Please use `--request-api-key` instead (force is implied).",
124
+ fg="yellow",
125
+ err=True,
126
+ )
93
127
 
94
128
  if json and not utils.should_use_stderr(opts):
95
129
  click.secho(
@@ -106,11 +140,34 @@ def authenticate(ctx, opts, owner, token, force, save_config, json):
106
140
  err=use_stderr,
107
141
  )
108
142
 
143
+ # Determine if we need to refresh API after SSO (required for token operations)
144
+ enable_token_creation = token or request_api_key_flag
145
+
109
146
  context_message = "Failed to authenticate via SSO!"
110
147
  with handle_api_exceptions(ctx, opts=opts, context_msg=context_message):
111
148
  _perform_saml_authentication(
112
- opts, owner, enable_token_creation=token, json=json
149
+ opts,
150
+ owner,
151
+ enable_token_creation=enable_token_creation,
152
+ use_stderr=use_stderr,
113
153
  )
114
154
 
155
+ if request_api_key_flag:
156
+ # Non-interactive token retrieval
157
+ new_token = request_api_key(ctx, opts, save_config=save_config)
158
+
159
+ if not new_token:
160
+ raise click.ClickException(
161
+ "Failed to retrieve API token. No token was returned."
162
+ )
163
+
164
+ # Check if JSON output is requested
165
+ if utils.maybe_print_as_json(opts, new_token):
166
+ return
167
+
168
+ # Default: output only the raw token value to stdout
169
+ click.echo(new_token.key)
170
+ return
171
+
115
172
  if token:
116
173
  ctx.invoke(create, opts=opts, save_config=save_config, force=force, json=json)
@@ -42,6 +42,11 @@ from .main import main
42
42
  @click.option(
43
43
  "--arch", "arch_filter", help="Architecture filter (e.g., 'amd64', 'arm64')."
44
44
  )
45
+ @click.option(
46
+ "--tag",
47
+ "tag_filter",
48
+ help="Filter by package tag (e.g., 'latest', 'stable'). Use --format, --arch, --os for metadata filters.",
49
+ )
45
50
  @click.option(
46
51
  "--outfile",
47
52
  type=click.Path(),
@@ -78,6 +83,7 @@ def download( # noqa: C901
78
83
  format_filter,
79
84
  os_filter,
80
85
  arch_filter,
86
+ tag_filter,
81
87
  outfile,
82
88
  overwrite,
83
89
  all_files,
@@ -88,7 +94,7 @@ def download( # noqa: C901
88
94
  Download a package from a Cloudsmith repository.
89
95
 
90
96
  This command downloads a package binary from a Cloudsmith repository. You can
91
- filter packages by version, format, operating system, and architecture.
97
+ filter packages by version, format, operating system, architecture, and tags.
92
98
 
93
99
  Examples:
94
100
 
@@ -104,6 +110,10 @@ def download( # noqa: C901
104
110
  # Download with filters and custom output name
105
111
  cloudsmith download myorg/myrepo mypackage --format deb --arch amd64 --outfile my-package.deb
106
112
 
113
+ \b
114
+ # Download a package with a specific tag
115
+ cloudsmith download myorg/myrepo mypackage --tag latest
116
+
107
117
  \b
108
118
  # Download all associated files (POM, sources, javadoc, etc.) for a Maven/NuGet package
109
119
  cloudsmith download myorg/myrepo mypackage --all-files
@@ -150,6 +160,7 @@ def download( # noqa: C901
150
160
  format_filter=format_filter,
151
161
  os_filter=os_filter,
152
162
  arch_filter=arch_filter,
163
+ tag_filter=tag_filter,
153
164
  yes=yes,
154
165
  )
155
166
 
@@ -0,0 +1,151 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """CLI/Commands - Log out and clear authentication state."""
3
+
4
+ import os
5
+
6
+ import click
7
+ import cloudsmith_api
8
+
9
+ from ...core import keyring
10
+ from .. import decorators, utils
11
+ from ..config import CredentialsReader
12
+ from .main import main
13
+
14
+
15
+ def _clear_credentials(dry_run, use_stderr):
16
+ """Clear credential files. Returns result dict."""
17
+ creds_files = CredentialsReader.find_existing_files()
18
+ if not creds_files:
19
+ click.echo("No credentials file found.", err=use_stderr)
20
+ return {"action": "not_found", "files": []}
21
+
22
+ if not dry_run:
23
+ for path in creds_files:
24
+ CredentialsReader.clear_api_key(path)
25
+
26
+ verb = "Would remove" if dry_run else "Removed"
27
+ for path in creds_files:
28
+ click.echo(
29
+ f"{verb} credentials from: " + click.style(path, bold=True),
30
+ err=use_stderr,
31
+ )
32
+ action = "would_remove" if dry_run else "removed"
33
+ return {"action": action, "files": list(creds_files)}
34
+
35
+
36
+ def _clear_keyring(api_host, dry_run, use_stderr):
37
+ """Clear SSO tokens from keyring. Returns result dict."""
38
+ if not keyring.should_use_keyring():
39
+ click.secho(
40
+ "Keyring is disabled (CLOUDSMITH_NO_KEYRING is set).",
41
+ fg="yellow",
42
+ err=use_stderr,
43
+ )
44
+ return {"action": "disabled"}
45
+
46
+ if not keyring.has_sso_tokens(api_host):
47
+ click.echo("No SSO tokens found in system keyring.", err=use_stderr)
48
+ return {"action": "not_found"}
49
+
50
+ if dry_run:
51
+ click.echo("Would remove SSO tokens from system keyring.", err=use_stderr)
52
+ return {"action": "would_remove"}
53
+
54
+ deleted = keyring.delete_sso_tokens(api_host)
55
+ action = "removed" if deleted else "failed"
56
+ msg = f"{'Removed' if deleted else 'Failed to remove'} SSO tokens from system keyring."
57
+ click.secho(msg, fg=None if deleted else "red", err=use_stderr)
58
+ return {"action": action} if deleted else {"action": action, "message": msg}
59
+
60
+
61
+ def _env_api_key_status():
62
+ """Return structured status for the CLOUDSMITH_API_KEY env var."""
63
+ is_set = bool(os.environ.get("CLOUDSMITH_API_KEY"))
64
+ return {
65
+ "is_set": is_set,
66
+ "action": "unset CLOUDSMITH_API_KEY" if is_set else "none",
67
+ }
68
+
69
+
70
+ def _collect_warnings(keyring_only, config_only):
71
+ """Collect advisory warnings based on flags and environment."""
72
+ warnings = []
73
+ if config_only:
74
+ warnings.append("SSO tokens were not modified (--config-only).")
75
+ if keyring_only:
76
+ warnings.append("credentials.ini was not modified (--keyring-only).")
77
+ if os.environ.get("CLOUDSMITH_API_KEY"):
78
+ warnings.append(
79
+ "CLOUDSMITH_API_KEY is set in your environment. "
80
+ "Run: unset CLOUDSMITH_API_KEY"
81
+ )
82
+ return warnings
83
+
84
+
85
+ @main.command()
86
+ @click.option(
87
+ "--api-host",
88
+ envvar="CLOUDSMITH_API_HOST",
89
+ default=None,
90
+ help="The API host to clear keyring tokens for.",
91
+ )
92
+ @click.option(
93
+ "--keyring-only",
94
+ is_flag=True,
95
+ default=False,
96
+ help="Only clear SSO tokens from the system keyring.",
97
+ )
98
+ @click.option(
99
+ "--config-only",
100
+ is_flag=True,
101
+ default=False,
102
+ help="Only clear credentials from credentials.ini.",
103
+ )
104
+ @click.option(
105
+ "--dry-run",
106
+ is_flag=True,
107
+ default=False,
108
+ help="Show what would be removed without removing anything.",
109
+ )
110
+ @decorators.common_cli_config_options
111
+ @decorators.common_cli_output_options
112
+ @click.pass_context
113
+ def logout(ctx, opts, api_host, keyring_only, config_only, dry_run):
114
+ """Clear stored authentication credentials and SSO tokens."""
115
+ if keyring_only and config_only:
116
+ raise click.UsageError(
117
+ "--keyring-only and --config-only are mutually exclusive."
118
+ )
119
+
120
+ if api_host is None:
121
+ api_host = opts.api_host or cloudsmith_api.Configuration().host
122
+
123
+ use_stderr = utils.should_use_stderr(opts)
124
+
125
+ credential_file = (
126
+ _clear_credentials(dry_run, use_stderr)
127
+ if not keyring_only
128
+ else {"action": "skipped", "files": []}
129
+ )
130
+ keyring_result = (
131
+ _clear_keyring(api_host, dry_run, use_stderr)
132
+ if not config_only
133
+ else {"action": "skipped"}
134
+ )
135
+ warnings = _collect_warnings(keyring_only, config_only)
136
+
137
+ for warning in warnings:
138
+ click.secho(f"Note: {warning}", fg="yellow", err=use_stderr)
139
+
140
+ utils.maybe_print_as_json(
141
+ opts,
142
+ {
143
+ "api_host": api_host,
144
+ "dry_run": dry_run,
145
+ "sources": {
146
+ "credential_file": credential_file,
147
+ "keyring": keyring_result,
148
+ "environment_api_key": _env_api_key_status(),
149
+ },
150
+ },
151
+ )
@@ -31,6 +31,77 @@ def handle_duplicate_token_error(exc, ctx, opts, save_config, force, json):
31
31
  raise exc
32
32
 
33
33
 
34
+ def request_api_key(ctx, opts, save_config=False):
35
+ """
36
+ Request an API key non-interactively.
37
+
38
+ This function creates a new token or rotates an existing one without any prompts.
39
+ Used by the --request-api-key flag in the auth command.
40
+
41
+ Returns the token object on success.
42
+ Raises ApiException on failure.
43
+ """
44
+ context_msg = "Failed to retrieve API token!"
45
+
46
+ try:
47
+ # Don't use handle_api_exceptions here so we can catch and handle
48
+ # the "already has token" error ourselves
49
+ with utils.maybe_spinner(opts):
50
+ new_token = api.create_user_token_saml()
51
+
52
+ if save_config:
53
+ create, has_errors = create_config_files(
54
+ ctx, opts, api_key=new_token.key, force=True
55
+ )
56
+ new_config_messaging(has_errors, opts, create, api_key=new_token.key)
57
+
58
+ return new_token
59
+
60
+ except exceptions.ApiException as exc:
61
+ if exc.status == 401:
62
+ # Unauthorized - re-raise with handler
63
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
64
+ raise
65
+
66
+ if (
67
+ exc.status == 400
68
+ and exc.detail
69
+ and "User has already created an API key" in exc.detail
70
+ ):
71
+ # Token exists - rotate it automatically
72
+ click.echo(
73
+ "Warning: Rotating existing API token. Your old key will be invalidated.",
74
+ err=True,
75
+ )
76
+
77
+ # List tokens and select the first one
78
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
79
+ with utils.maybe_spinner(opts):
80
+ api_tokens = api.list_user_tokens()
81
+
82
+ if not api_tokens:
83
+ raise click.ClickException("No existing tokens found to rotate.")
84
+
85
+ token_slug = api_tokens[0].slug_perm
86
+
87
+ # Refresh the token
88
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
89
+ with utils.maybe_spinner(opts):
90
+ new_token = api.refresh_user_token(token_slug)
91
+
92
+ if save_config:
93
+ create, has_errors = create_config_files(
94
+ ctx, opts, api_key=new_token.key, force=True
95
+ )
96
+ new_config_messaging(has_errors, opts, create, api_key=new_token.key)
97
+
98
+ return new_token
99
+
100
+ # Other errors - use the handler
101
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
102
+ raise exc
103
+
104
+
34
105
  @main.group(cls=command.AliasGroup, name="tokens")
35
106
  @decorators.common_cli_config_options
36
107
  @decorators.common_cli_output_options