cloudinary-cli 1.16.1__tar.gz → 1.18.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 (106) hide show
  1. {cloudinary_cli-1.16.1/cloudinary_cli.egg-info → cloudinary_cli-1.18.0}/PKG-INFO +108 -3
  2. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/README.md +106 -1
  3. cloudinary_cli-1.18.0/cloudinary_cli/__init__.py +9 -0
  4. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/cli_group.py +16 -1
  5. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/__init__.py +2 -0
  6. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/admin.py +4 -3
  7. cloudinary_cli-1.18.0/cloudinary_cli/core/agent.py +488 -0
  8. cloudinary_cli-1.18.0/cloudinary_cli/core/collections.py +29 -0
  9. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/config.py +15 -1
  10. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/provisioning.py +11 -2
  11. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/search.py +2 -2
  12. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/uploader.py +3 -2
  13. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/utils.py +10 -3
  14. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/defaults.py +14 -1
  15. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/migrate.py +1 -1
  16. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/regen_derived.py +4 -0
  17. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/sync.py +39 -11
  18. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/upload_dir.py +6 -5
  19. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/html/upload_widget +1 -1
  20. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/api_utils.py +21 -7
  21. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/config_listing.py +38 -2
  22. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/config_resolver.py +9 -1
  23. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/config_utils.py +58 -2
  24. cloudinary_cli-1.18.0/cloudinary_cli/utils/env_config.py +48 -0
  25. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/file_utils.py +4 -0
  26. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/utils.py +52 -2
  27. cloudinary_cli-1.18.0/cloudinary_cli/version.py +1 -0
  28. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0/cloudinary_cli.egg-info}/PKG-INFO +108 -3
  29. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/SOURCES.txt +4 -0
  30. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/requires.txt +1 -1
  31. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/requirements.txt +1 -1
  32. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/helper_test.py +51 -21
  33. cloudinary_cli-1.18.0/test/test_cli.py +135 -0
  34. cloudinary_cli-1.18.0/test/test_cli_agent_cloud.py +741 -0
  35. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_api.py +55 -1
  36. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_url.py +1 -0
  37. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_modules/test_cli_make.py +1 -0
  38. cloudinary_cli-1.18.0/test/test_modules/test_cli_regen_derived.py +31 -0
  39. cloudinary_cli-1.18.0/test/test_modules/test_cli_sync.py +420 -0
  40. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_modules/test_cli_upload_dir.py +0 -3
  41. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_utils.py +64 -1
  42. cloudinary_cli-1.16.1/cloudinary_cli/__init__.py +0 -5
  43. cloudinary_cli-1.16.1/cloudinary_cli/core/agent.py +0 -203
  44. cloudinary_cli-1.16.1/cloudinary_cli/version.py +0 -1
  45. cloudinary_cli-1.16.1/test/test_cli.py +0 -49
  46. cloudinary_cli-1.16.1/test/test_modules/test_cli_sync.py +0 -222
  47. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/LICENSE +0 -0
  48. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/MANIFEST.in +0 -0
  49. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/__init__.py +0 -0
  50. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/callback_page.py +0 -0
  51. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/flow.py +0 -0
  52. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/loopback_server.py +0 -0
  53. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/oauth_config.py +0 -0
  54. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/refresh.py +0 -0
  55. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/auth/session.py +0 -0
  56. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/cli.py +0 -0
  57. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/auth.py +0 -0
  58. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/core/overrides.py +0 -0
  59. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/__init__.py +0 -0
  60. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/clone.py +0 -0
  61. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/modules/make.py +0 -0
  62. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/samples/__init__.py +0 -0
  63. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/html/media_library_widget +0 -0
  64. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/html/product_gallery +0 -0
  65. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/html/video_player +0 -0
  66. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/node/upload +0 -0
  67. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/python/base +0 -0
  68. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/python/explicit +0 -0
  69. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/python/find_all_empty_folders +0 -0
  70. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/python/upload +0 -0
  71. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/templates/ruby/upload +0 -0
  72. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/__init__.py +0 -0
  73. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/json_utils.py +0 -0
  74. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/search_utils.py +0 -0
  75. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli/utils/url_utils.py +0 -0
  76. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/dependency_links.txt +0 -0
  77. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/entry_points.txt +0 -0
  78. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/not-zip-safe +0 -0
  79. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/cloudinary_cli.egg-info/top_level.txt +0 -0
  80. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/setup.cfg +0 -0
  81. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/setup.py +0 -0
  82. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/__init__.py +0 -0
  83. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/conftest.py +0 -0
  84. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/oauth_helpers.py +0 -0
  85. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_auth_flow.py +0 -0
  86. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_auth_loopback.py +0 -0
  87. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_auth_region.py +0 -0
  88. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_auth_session.py +0 -0
  89. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_agent.py +0 -0
  90. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_config.py +0 -0
  91. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_config_oauth.py +0 -0
  92. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_samples.py +0 -0
  93. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_search_api.py +0 -0
  94. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_cli_utils.py +0 -0
  95. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_config_cache.py +0 -0
  96. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_config_concurrency.py +0 -0
  97. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_config_permissions.py +0 -0
  98. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_default_config.py +0 -0
  99. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_file_utils.py +0 -0
  100. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_json_utils.py +0 -0
  101. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_modules/__init__.py +0 -0
  102. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_modules/test_cli_clone.py +0 -0
  103. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_oauth_multiprocess.py +0 -0
  104. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_oauth_retry.py +0 -0
  105. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_oauth_token_seam.py +0 -0
  106. {cloudinary_cli-1.16.1 → cloudinary_cli-1.18.0}/test/test_search_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudinary-cli
3
- Version: 1.16.1
3
+ Version: 1.18.0
4
4
  Summary: A command line interface for Cloudinary with full API support
5
5
  Home-page: https://github.com/cloudinary/cloudinary-cli
6
6
  Author: Cloudinary, Brian Luk
@@ -13,7 +13,7 @@ Classifier: Operating System :: OS Independent
13
13
  Requires-Python: >=3.8.0
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: cloudinary>=1.45.0
16
+ Requires-Dist: cloudinary>=1.47.0
17
17
  Requires-Dist: pygments
18
18
  Requires-Dist: jinja2
19
19
  Requires-Dist: click
@@ -35,11 +35,24 @@ The Cloudinary CLI (Command Line Interface) enables you to interact with Cloudin
35
35
 
36
36
  It is fully documented at [https://cloudinary.com/documentation/cloudinary_cli](https://cloudinary.com/documentation/cloudinary_cli).
37
37
 
38
+ **Built for AI agents too.** [`cld agent cloud create`](#agent-cloud) provisions a working Cloudinary cloud in a single command — no signup, no verification email, no credentials to ask a human for — so an agent can start uploading and transforming right away, then hand the human a claim URL to keep it.
39
+
38
40
  ## Requirements
39
- Your own Cloudinary account. If you don't already have one, sign up at [https://cloudinary.com/users/register/free](https://cloudinary.com/users/register/free).
40
41
 
41
42
  Python 3.8 or later. You can install Python from [https://www.python.org/](https://www.python.org/). Note that the Python Package Installer (pip) is installed with it.
42
43
 
44
+ A Cloudinary account. There are two ways to get one:
45
+
46
+ **Are you an AI agent?** Run [`cld agent cloud create`](#agent-cloud) — no signup, no verification email, no human in the loop. You get a working cloud name, API key and secret in one command, and can upload and transform immediately:
47
+
48
+ ```
49
+ cld agent cloud create
50
+ ```
51
+
52
+ The cloud is temporary: media delivery is limited to the IP that created it, and it expires in about 24 hours unless a human claims it. Hand them the claim URL from the output — or run [`cld agent cloud claim`](#agent-cloud) — and it becomes a permanent account, keeping the same credentials and everything already uploaded.
53
+
54
+ **Are you a human?** Sign up at [https://cloudinary.com/users/register/free](https://cloudinary.com/users/register/free), then see [Configuration](#configuration) below. If you'd rather an agent set it up for you, [`cld agent signup`](#agent-signup) creates a Free-plan account in your name and emails you to verify it.
55
+
43
56
  ## Installation
44
57
 
45
58
  The CLI is published on PyPI as [`cloudinary-cli`](https://pypi.org/project/cloudinary-cli/). The package name (`cloudinary-cli`) is what you install; the command it provides is **`cld`** (it also installs a `cloudinary` alias). Pick the method that fits your setup. If you just want a working `cld` command and aren't sure, use **pipx** or **uv** — they install the CLI in its own isolated environment, so it won't conflict with other Python packages and you don't need to manage a virtual environment yourself.
@@ -168,7 +181,9 @@ cld logout # Revokes and removes a saved OAuth login.
168
181
  cld search --help # Shows usage for the Search API.
169
182
  cld admin # Lists Admin API methods.
170
183
  cld uploader # Lists Upload API methods.
184
+ cld collections # Lists Collections API methods.
171
185
  cld agent signup # For AI agents: creates a Cloudinary account on behalf of a human.
186
+ cld agent cloud # For AI agents: creates a temporary cloud that works immediately.
172
187
  ```
173
188
 
174
189
  ## Docker Usage
@@ -248,6 +263,32 @@ cld admin transformation my_new_transformation
248
263
 
249
264
  _**Note:** you can omit 'admin' from the command when calling an Admin API method._
250
265
 
266
+ ## Collections API
267
+
268
+ Enables you to run any methods that can be called through the collections API, to organize assets into collections and share them through public pages.
269
+
270
+ You can find documentation for each of the Collections API methods at [https://cloudinary.com/documentation/collections_api](https://cloudinary.com/documentation/collections_api).
271
+
272
+ The basic syntax using the Collections API is as follows:
273
+
274
+ ```
275
+ cld [cli options] collections [command options] [method] [method parameters]
276
+ ```
277
+
278
+ Example: create a collection, add an asset to it and publish it as a public page:
279
+
280
+ ```
281
+ cld collections create_collection "Summer campaign" -o description "Campaign assets"
282
+ cld collections add_collection_assets <collection_id> '["<asset_id>"]'
283
+ cld collections create_collection_public_page <collection_id>
284
+ ```
285
+
286
+ Create a dynamic collection from a search expression:
287
+
288
+ ```
289
+ cld collections create_collection "Summer images" -o type dynamic -O configuration '{"expression": "tags=summer"}'
290
+ ```
291
+
251
292
  ## Search API
252
293
 
253
294
  Runs the admin API search method, allowing you to use a Lucene query string as the expression.
@@ -364,6 +405,70 @@ Options:
364
405
  * `--sdk-framework <name>` — the Cloudinary SDK framework the agent intends to use.
365
406
  * `--json` — output the full raw JSON response (the agent contract) instead of the human-readable summary.
366
407
 
408
+ ### `agent cloud`
409
+
410
+ **For AI agents acting on behalf of a human.** Creates and claims *Claimable Clouds*: temporary clouds whose credentials work immediately, with no signup and no verification email. No existing configuration is required.
411
+
412
+ Media delivery is restricted to an IP allow-list, and the cloud is **disabled after about 24 hours** — along with everything uploaded to it — unless a human claims it. Claiming makes it permanent, keeps the credentials and assets, and lifts the IP restriction.
413
+
414
+ #### `agent cloud create`
415
+
416
+ ```
417
+ cld agent cloud create [command options] [email]
418
+ ```
419
+
420
+ Example:
421
+
422
+ ```
423
+ cld agent cloud create you@example.com --claim
424
+ ```
425
+
426
+ The cloud is saved as a named configuration along with its claim URL, which **the server returns exactly once and cannot be looked up again**. A cloud saved without it — or created outside the CLI — cannot be claimed via `agent cloud claim`.
427
+
428
+ The optional `email` only pre-fills the claim page. It is never verified and no mail is sent to it at creation, but it must be a real unused address: the server rejects addresses already taken and disposable domains.
429
+
430
+ Options:
431
+
432
+ * `--ip <address>` — an additional IP permitted to deliver media, repeatable. Omit to let the server use the address the request comes from; the literal `requester_ip` means that same address.
433
+ * `--name <name>` — name for the saved configuration (default: the returned cloud name).
434
+ * `--set-default` — set the saved configuration as the default.
435
+ * `--no-save` — show the credentials but do not save them as a configuration. The claim URL is then only in the output, so store it yourself.
436
+ * `--claim` — open the claim page as soon as the cloud is created.
437
+ * `--agent-framework <name>`, `--agent-llm-model <name>`, `--agent-goal <text>`, `--sdk-framework <name>` — attribution for the agent creating the cloud.
438
+ * `--json` — output the full raw JSON response (the agent contract) instead of the human-readable summary.
439
+
440
+ Delivery IPs are sent to the server as given; it validates them and returns the allow-list it actually stored. Read `delivery_ips` back from the response rather than assuming the list you sent was kept — behind a proxy or VPN your own address may be dropped, and the server refuses to create a cloud with no publicly routable address in the list.
441
+
442
+ Because the allow-list covers media delivery only, the Upload and Admin APIs are unaffected. Uploads succeeding while a delivery URL returns `x-cld-error: ACL deny` is the expected symptom of the restriction, not a broken cloud or bad credentials. It is therefore not a confidentiality control.
443
+
444
+ #### `agent cloud claim`
445
+
446
+ ```
447
+ cld agent cloud claim [command options] [name]
448
+ ```
449
+
450
+ Example:
451
+
452
+ ```
453
+ cld agent cloud claim mycloud --print
454
+ ```
455
+
456
+ Opens the claim page for a saved Claimable Cloud. Run without a name to pick from the saved clouds that have not expired.
457
+
458
+ Claiming is a **human action completed in a browser**: they enter an email address there, then click the link sent to it. This command only opens or prints the page — it cannot claim anything itself, and nothing reports whether a claim succeeded.
459
+
460
+ Options:
461
+
462
+ * `--print` (or `--no-open`) — print the claim URL instead of opening a browser. Required for headless and agent use; also implied by `--json`. The CLI prints rather than opens whenever stdout is not a terminal.
463
+ * `--json` — output `{cloud_name, claim_url, expires_at}` as JSON.
464
+
465
+ Saved Claimable Clouds are flagged in `cld config -ls` and `cld config -s <name>` with an expiry countdown, so you can see what is still claimable:
466
+
467
+ ```
468
+ NAME CLOUD TYPE DEFAULT ACTIVE STATUS
469
+ mycloud abcd1234 api_key unclaimed, expires in 8h
470
+ ```
471
+
367
472
  ## Additional configurations
368
473
 
369
474
  A configuration is a reference to a specified Cloudinary account or cloud name via its environment variable. You set the default configuration during setup and installation. Using different configurations allows you to access different Cloudinary cloud names, such as sub-accounts of your main Cloudinary account, or any additional Cloudinary accounts you may have.
@@ -9,11 +9,24 @@ The Cloudinary CLI (Command Line Interface) enables you to interact with Cloudin
9
9
 
10
10
  It is fully documented at [https://cloudinary.com/documentation/cloudinary_cli](https://cloudinary.com/documentation/cloudinary_cli).
11
11
 
12
+ **Built for AI agents too.** [`cld agent cloud create`](#agent-cloud) provisions a working Cloudinary cloud in a single command — no signup, no verification email, no credentials to ask a human for — so an agent can start uploading and transforming right away, then hand the human a claim URL to keep it.
13
+
12
14
  ## Requirements
13
- Your own Cloudinary account. If you don't already have one, sign up at [https://cloudinary.com/users/register/free](https://cloudinary.com/users/register/free).
14
15
 
15
16
  Python 3.8 or later. You can install Python from [https://www.python.org/](https://www.python.org/). Note that the Python Package Installer (pip) is installed with it.
16
17
 
18
+ A Cloudinary account. There are two ways to get one:
19
+
20
+ **Are you an AI agent?** Run [`cld agent cloud create`](#agent-cloud) — no signup, no verification email, no human in the loop. You get a working cloud name, API key and secret in one command, and can upload and transform immediately:
21
+
22
+ ```
23
+ cld agent cloud create
24
+ ```
25
+
26
+ The cloud is temporary: media delivery is limited to the IP that created it, and it expires in about 24 hours unless a human claims it. Hand them the claim URL from the output — or run [`cld agent cloud claim`](#agent-cloud) — and it becomes a permanent account, keeping the same credentials and everything already uploaded.
27
+
28
+ **Are you a human?** Sign up at [https://cloudinary.com/users/register/free](https://cloudinary.com/users/register/free), then see [Configuration](#configuration) below. If you'd rather an agent set it up for you, [`cld agent signup`](#agent-signup) creates a Free-plan account in your name and emails you to verify it.
29
+
17
30
  ## Installation
18
31
 
19
32
  The CLI is published on PyPI as [`cloudinary-cli`](https://pypi.org/project/cloudinary-cli/). The package name (`cloudinary-cli`) is what you install; the command it provides is **`cld`** (it also installs a `cloudinary` alias). Pick the method that fits your setup. If you just want a working `cld` command and aren't sure, use **pipx** or **uv** — they install the CLI in its own isolated environment, so it won't conflict with other Python packages and you don't need to manage a virtual environment yourself.
@@ -142,7 +155,9 @@ cld logout # Revokes and removes a saved OAuth login.
142
155
  cld search --help # Shows usage for the Search API.
143
156
  cld admin # Lists Admin API methods.
144
157
  cld uploader # Lists Upload API methods.
158
+ cld collections # Lists Collections API methods.
145
159
  cld agent signup # For AI agents: creates a Cloudinary account on behalf of a human.
160
+ cld agent cloud # For AI agents: creates a temporary cloud that works immediately.
146
161
  ```
147
162
 
148
163
  ## Docker Usage
@@ -222,6 +237,32 @@ cld admin transformation my_new_transformation
222
237
 
223
238
  _**Note:** you can omit 'admin' from the command when calling an Admin API method._
224
239
 
240
+ ## Collections API
241
+
242
+ Enables you to run any methods that can be called through the collections API, to organize assets into collections and share them through public pages.
243
+
244
+ You can find documentation for each of the Collections API methods at [https://cloudinary.com/documentation/collections_api](https://cloudinary.com/documentation/collections_api).
245
+
246
+ The basic syntax using the Collections API is as follows:
247
+
248
+ ```
249
+ cld [cli options] collections [command options] [method] [method parameters]
250
+ ```
251
+
252
+ Example: create a collection, add an asset to it and publish it as a public page:
253
+
254
+ ```
255
+ cld collections create_collection "Summer campaign" -o description "Campaign assets"
256
+ cld collections add_collection_assets <collection_id> '["<asset_id>"]'
257
+ cld collections create_collection_public_page <collection_id>
258
+ ```
259
+
260
+ Create a dynamic collection from a search expression:
261
+
262
+ ```
263
+ cld collections create_collection "Summer images" -o type dynamic -O configuration '{"expression": "tags=summer"}'
264
+ ```
265
+
225
266
  ## Search API
226
267
 
227
268
  Runs the admin API search method, allowing you to use a Lucene query string as the expression.
@@ -338,6 +379,70 @@ Options:
338
379
  * `--sdk-framework <name>` — the Cloudinary SDK framework the agent intends to use.
339
380
  * `--json` — output the full raw JSON response (the agent contract) instead of the human-readable summary.
340
381
 
382
+ ### `agent cloud`
383
+
384
+ **For AI agents acting on behalf of a human.** Creates and claims *Claimable Clouds*: temporary clouds whose credentials work immediately, with no signup and no verification email. No existing configuration is required.
385
+
386
+ Media delivery is restricted to an IP allow-list, and the cloud is **disabled after about 24 hours** — along with everything uploaded to it — unless a human claims it. Claiming makes it permanent, keeps the credentials and assets, and lifts the IP restriction.
387
+
388
+ #### `agent cloud create`
389
+
390
+ ```
391
+ cld agent cloud create [command options] [email]
392
+ ```
393
+
394
+ Example:
395
+
396
+ ```
397
+ cld agent cloud create you@example.com --claim
398
+ ```
399
+
400
+ The cloud is saved as a named configuration along with its claim URL, which **the server returns exactly once and cannot be looked up again**. A cloud saved without it — or created outside the CLI — cannot be claimed via `agent cloud claim`.
401
+
402
+ The optional `email` only pre-fills the claim page. It is never verified and no mail is sent to it at creation, but it must be a real unused address: the server rejects addresses already taken and disposable domains.
403
+
404
+ Options:
405
+
406
+ * `--ip <address>` — an additional IP permitted to deliver media, repeatable. Omit to let the server use the address the request comes from; the literal `requester_ip` means that same address.
407
+ * `--name <name>` — name for the saved configuration (default: the returned cloud name).
408
+ * `--set-default` — set the saved configuration as the default.
409
+ * `--no-save` — show the credentials but do not save them as a configuration. The claim URL is then only in the output, so store it yourself.
410
+ * `--claim` — open the claim page as soon as the cloud is created.
411
+ * `--agent-framework <name>`, `--agent-llm-model <name>`, `--agent-goal <text>`, `--sdk-framework <name>` — attribution for the agent creating the cloud.
412
+ * `--json` — output the full raw JSON response (the agent contract) instead of the human-readable summary.
413
+
414
+ Delivery IPs are sent to the server as given; it validates them and returns the allow-list it actually stored. Read `delivery_ips` back from the response rather than assuming the list you sent was kept — behind a proxy or VPN your own address may be dropped, and the server refuses to create a cloud with no publicly routable address in the list.
415
+
416
+ Because the allow-list covers media delivery only, the Upload and Admin APIs are unaffected. Uploads succeeding while a delivery URL returns `x-cld-error: ACL deny` is the expected symptom of the restriction, not a broken cloud or bad credentials. It is therefore not a confidentiality control.
417
+
418
+ #### `agent cloud claim`
419
+
420
+ ```
421
+ cld agent cloud claim [command options] [name]
422
+ ```
423
+
424
+ Example:
425
+
426
+ ```
427
+ cld agent cloud claim mycloud --print
428
+ ```
429
+
430
+ Opens the claim page for a saved Claimable Cloud. Run without a name to pick from the saved clouds that have not expired.
431
+
432
+ Claiming is a **human action completed in a browser**: they enter an email address there, then click the link sent to it. This command only opens or prints the page — it cannot claim anything itself, and nothing reports whether a claim succeeded.
433
+
434
+ Options:
435
+
436
+ * `--print` (or `--no-open`) — print the claim URL instead of opening a browser. Required for headless and agent use; also implied by `--json`. The CLI prints rather than opens whenever stdout is not a terminal.
437
+ * `--json` — output `{cloud_name, claim_url, expires_at}` as JSON.
438
+
439
+ Saved Claimable Clouds are flagged in `cld config -ls` and `cld config -s <name>` with an expiry countdown, so you can see what is still claimable:
440
+
441
+ ```
442
+ NAME CLOUD TYPE DEFAULT ACTIVE STATUS
443
+ mycloud abcd1234 api_key unclaimed, expires in 8h
444
+ ```
445
+
341
446
  ## Additional configurations
342
447
 
343
448
  A configuration is a reference to a specified Cloudinary account or cloud name via its environment variable. You set the default configuration during setup and installation. Using different configurations allows you to access different Cloudinary cloud names, such as sub-accounts of your main Cloudinary account, or any additional Cloudinary accounts you may have.
@@ -0,0 +1,9 @@
1
+ from cloudinary_cli.version import __version__
2
+ from cloudinary_cli.utils.env_config import import_sdk
3
+
4
+ # Must run before any other `import cloudinary`, so that an invalid CLOUDINARY_URL does not stop the CLI.
5
+ import_sdk()
6
+
7
+ import cloudinary
8
+
9
+ cloudinary.USER_PLATFORM = f"CloudinaryCLI/{__version__}"
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python3
2
+ import difflib
2
3
  import platform
3
4
  import shutil
4
5
 
@@ -13,7 +14,21 @@ from cloudinary_cli.version import __version__ as cli_version
13
14
  CONTEXT_SETTINGS = dict(max_content_width=shutil.get_terminal_size()[0], terminal_width=shutil.get_terminal_size()[0])
14
15
 
15
16
 
16
- @click.group(context_settings=CONTEXT_SETTINGS, invoke_without_command=True)
17
+ class SuggestingGroup(click.Group):
18
+ """Suggests similar command names when the command name is not known."""
19
+
20
+ def resolve_command(self, ctx, args):
21
+ try:
22
+ return super().resolve_command(ctx, args)
23
+ except click.UsageError as e:
24
+ names = [name for name in self.list_commands(ctx) if not self.get_command(ctx, name).hidden]
25
+ matches = difflib.get_close_matches(args[0], names) if args else []
26
+ if matches:
27
+ e.message += f"\nDid you mean: {', '.join(matches)}?"
28
+ raise
29
+
30
+
31
+ @click.group(cls=SuggestingGroup, context_settings=CONTEXT_SETTINGS, invoke_without_command=True)
17
32
  @click.help_option()
18
33
  @click.version_option(cli_version, prog_name="Cloudinary CLI",
19
34
  message=f"%(prog)s, version %(version)s\n"
@@ -3,6 +3,7 @@ import click
3
3
  from cloudinary_cli.core.admin import admin
4
4
  from cloudinary_cli.core.agent import agent_group
5
5
  from cloudinary_cli.core.auth import login, logout
6
+ from cloudinary_cli.core.collections import collections
6
7
  from cloudinary_cli.core.config import config_command
7
8
  from cloudinary_cli.core.search import search, search_folders
8
9
  from cloudinary_cli.core.uploader import uploader
@@ -22,6 +23,7 @@ commands = [
22
23
  admin,
23
24
  uploader,
24
25
  provisioning,
26
+ collections,
25
27
  url,
26
28
  utils
27
29
  ]
@@ -16,11 +16,12 @@ Format: cld <cli options> admin <command options> <method> <method parameters>
16
16
  \t cld admin resources max_results=10 -o tags sample
17
17
  """)
18
18
  @argument("params", nargs=-1)
19
- @option("-o", "--optional_parameter", multiple=True, nargs=2, help="Pass optional parameters as raw strings.")
19
+ @option("-o", "--optional_parameter", multiple=True, nargs=2,
20
+ help="Pass an optional parameter as a string, with no parsing. e.g. -o tags a,b")
20
21
  @option("-O", "--optional_parameter_parsed", multiple=True, nargs=2,
21
- help="Pass optional parameters as interpreted strings.")
22
+ help="Pass an optional parameter and parse its value as JSON or a boolean. e.g. -O context '{\"alt\": \"cat\"}'")
22
23
  @option("-A", "--auto_paginate", is_flag=True, help="Will auto paginate Admin API calls.", default=False)
23
- @option("-ff", "--filter_fields", multiple=True, help="Filter fields to return when using auto pagination.")
24
+ @option("-ff", "--filter_fields", multiple=True, help="Filter fields to return. Requires -A/--auto_paginate.")
24
25
  @option("-F", "--force", is_flag=True,
25
26
  help="Skip confirmations for auto pagination and destructive bulk API methods.")
26
27
  @option("-ls", "--ls", is_flag=True, help="List all available methods in the Admin API.")