zen-cli 0.1.1__tar.gz → 0.1.4__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 (199) hide show
  1. {zen_cli-0.1.1 → zen_cli-0.1.4}/.gitignore +3 -0
  2. zen_cli-0.1.4/PKG-INFO +98 -0
  3. zen_cli-0.1.4/README.md +66 -0
  4. zen_cli-0.1.4/pyproject.toml +75 -0
  5. zen_cli-0.1.4/src/zen_cli/__init__.py +5 -0
  6. zen_cli-0.1.4/src/zen_cli/__main__.py +4 -0
  7. zen_cli-0.1.4/src/zen_cli/cli.py +176 -0
  8. zen_cli-0.1.4/src/zen_cli/cmds/activity/__init__.py +1 -0
  9. zen_cli-0.1.4/src/zen_cli/cmds/activity/get.py +20 -0
  10. zen_cli-0.1.4/src/zen_cli/cmds/activity/list.py +157 -0
  11. zen_cli-0.1.4/src/zen_cli/cmds/api.py +145 -0
  12. zen_cli-0.1.4/src/zen_cli/cmds/auth/__init__.py +1 -0
  13. zen_cli-0.1.4/src/zen_cli/cmds/auth/details.py +19 -0
  14. zen_cli-0.1.4/src/zen_cli/cmds/auth/refresh.py +11 -0
  15. zen_cli-0.1.4/src/zen_cli/cmds/auth/status.py +17 -0
  16. zen_cli-0.1.4/src/zen_cli/cmds/auth/token.py +17 -0
  17. zen_cli-0.1.4/src/zen_cli/cmds/completion.py +29 -0
  18. zen_cli-0.1.4/src/zen_cli/cmds/credential/__init__.py +1 -0
  19. zen_cli-0.1.4/src/zen_cli/cmds/credential/create.py +13 -0
  20. zen_cli-0.1.4/src/zen_cli/cmds/credential/delete.py +27 -0
  21. zen_cli-0.1.4/src/zen_cli/cmds/credential/get.py +19 -0
  22. zen_cli-0.1.4/src/zen_cli/cmds/credential/list.py +18 -0
  23. zen_cli-0.1.4/src/zen_cli/cmds/deployment/__init__.py +1 -0
  24. zen_cli-0.1.4/src/zen_cli/cmds/deployment/create.py +15 -0
  25. zen_cli-0.1.4/src/zen_cli/cmds/deployment/delete.py +28 -0
  26. zen_cli-0.1.4/src/zen_cli/cmds/deployment/get.py +19 -0
  27. zen_cli-0.1.4/src/zen_cli/cmds/deployment/list.py +27 -0
  28. zen_cli-0.1.4/src/zen_cli/cmds/entitlement/__init__.py +1 -0
  29. zen_cli-0.1.4/src/zen_cli/cmds/entitlement/create.py +13 -0
  30. zen_cli-0.1.4/src/zen_cli/cmds/entitlement/delete.py +27 -0
  31. zen_cli-0.1.4/src/zen_cli/cmds/entitlement/get.py +19 -0
  32. zen_cli-0.1.4/src/zen_cli/cmds/entitlement/list.py +19 -0
  33. zen_cli-0.1.4/src/zen_cli/cmds/group/__init__.py +1 -0
  34. zen_cli-0.1.4/src/zen_cli/cmds/group/create.py +13 -0
  35. zen_cli-0.1.4/src/zen_cli/cmds/group/delete.py +33 -0
  36. zen_cli-0.1.4/src/zen_cli/cmds/group/get.py +19 -0
  37. zen_cli-0.1.4/src/zen_cli/cmds/group/list.py +26 -0
  38. zen_cli-0.1.4/src/zen_cli/cmds/login.py +375 -0
  39. zen_cli-0.1.4/src/zen_cli/cmds/logout.py +9 -0
  40. zen_cli-0.1.4/src/zen_cli/cmds/mcp.py +13 -0
  41. zen_cli-0.1.4/src/zen_cli/cmds/network/__init__.py +1 -0
  42. zen_cli-0.1.4/src/zen_cli/cmds/network/create.py +13 -0
  43. zen_cli-0.1.4/src/zen_cli/cmds/network/delete.py +34 -0
  44. zen_cli-0.1.4/src/zen_cli/cmds/network/get.py +19 -0
  45. zen_cli-0.1.4/src/zen_cli/cmds/network/list.py +27 -0
  46. zen_cli-0.1.4/src/zen_cli/cmds/notification/__init__.py +1 -0
  47. zen_cli-0.1.4/src/zen_cli/cmds/notification/get.py +19 -0
  48. zen_cli-0.1.4/src/zen_cli/cmds/notification/list.py +66 -0
  49. zen_cli-0.1.4/src/zen_cli/cmds/org/__init__.py +1 -0
  50. zen_cli-0.1.4/src/zen_cli/cmds/org/create.py +13 -0
  51. zen_cli-0.1.4/src/zen_cli/cmds/org/get.py +51 -0
  52. zen_cli-0.1.4/src/zen_cli/cmds/org/list.py +33 -0
  53. zen_cli-0.1.4/src/zen_cli/cmds/org/use.py +46 -0
  54. zen_cli-0.1.4/src/zen_cli/cmds/pool/__init__.py +1 -0
  55. zen_cli-0.1.4/src/zen_cli/cmds/pool/create.py +13 -0
  56. zen_cli-0.1.4/src/zen_cli/cmds/pool/delete.py +34 -0
  57. zen_cli-0.1.4/src/zen_cli/cmds/pool/get.py +19 -0
  58. zen_cli-0.1.4/src/zen_cli/cmds/pool/list.py +93 -0
  59. zen_cli-0.1.4/src/zen_cli/cmds/pool/use.py +35 -0
  60. zen_cli-0.1.4/src/zen_cli/cmds/profile/__init__.py +1 -0
  61. zen_cli-0.1.4/src/zen_cli/cmds/profile/init.py +10 -0
  62. zen_cli-0.1.4/src/zen_cli/cmds/provider/__init__.py +1 -0
  63. zen_cli-0.1.4/src/zen_cli/cmds/provider/create.py +13 -0
  64. zen_cli-0.1.4/src/zen_cli/cmds/provider/delete.py +27 -0
  65. zen_cli-0.1.4/src/zen_cli/cmds/provider/get.py +27 -0
  66. zen_cli-0.1.4/src/zen_cli/cmds/provider/list.py +43 -0
  67. zen_cli-0.1.4/src/zen_cli/cmds/sag/__init__.py +1 -0
  68. zen_cli-0.1.4/src/zen_cli/cmds/sag/create.py +13 -0
  69. zen_cli-0.1.4/src/zen_cli/cmds/sag/delete.py +34 -0
  70. zen_cli-0.1.4/src/zen_cli/cmds/sag/get.py +19 -0
  71. zen_cli-0.1.4/src/zen_cli/cmds/sag/list.py +20 -0
  72. zen_cli-0.1.4/src/zen_cli/cmds/task/__init__.py +1 -0
  73. zen_cli-0.1.4/src/zen_cli/cmds/task/cancel.py +27 -0
  74. zen_cli-0.1.4/src/zen_cli/cmds/task/delete.py +42 -0
  75. zen_cli-0.1.4/src/zen_cli/cmds/task/duplicate.py +75 -0
  76. zen_cli-0.1.4/src/zen_cli/cmds/task/get.py +20 -0
  77. zen_cli-0.1.4/src/zen_cli/cmds/task/list.py +132 -0
  78. zen_cli-0.1.4/src/zen_cli/cmds/task/log.py +60 -0
  79. zen_cli-0.1.4/src/zen_cli/cmds/task/namespaces.py +11 -0
  80. zen_cli-0.1.4/src/zen_cli/cmds/task/use.py +37 -0
  81. zen_cli-0.1.4/src/zen_cli/cmds/task/wait.py +28 -0
  82. zen_cli-0.1.4/src/zen_cli/cmds/upgrade.py +13 -0
  83. zen_cli-0.1.4/src/zen_cli/cmds/vm/__init__.py +1 -0
  84. zen_cli-0.1.4/src/zen_cli/cmds/vm/delete.py +39 -0
  85. zen_cli-0.1.4/src/zen_cli/cmds/vm/get.py +17 -0
  86. zen_cli-0.1.4/src/zen_cli/cmds/vm/list.py +184 -0
  87. zen_cli-0.1.4/src/zen_cli/cmds/vm/poweroff.py +29 -0
  88. zen_cli-0.1.4/src/zen_cli/cmds/vm/poweron.py +29 -0
  89. zen_cli-0.1.4/src/zen_cli/cmds/vm/restart.py +19 -0
  90. zen_cli-0.1.4/src/zen_cli/cmds/vm/shutdown.py +19 -0
  91. zen_cli-0.1.4/src/zen_cli/cmds/vm/use.py +48 -0
  92. zen_cli-0.1.4/src/zen_cli/cmds/vm/wait.py +48 -0
  93. zen_cli-0.1.4/src/zen_cli/config/default.yaml +31 -0
  94. zen_cli-0.1.4/src/zen_cli/config/int.yaml +21 -0
  95. zen_cli-0.1.4/src/zen_cli/config/prod.yaml +31 -0
  96. zen_cli-0.1.4/src/zen_cli/config/stg.yaml +31 -0
  97. zen_cli-0.1.4/src/zen_cli/ctxp/__init__.py +12 -0
  98. zen_cli-0.1.4/src/zen_cli/ctxp/_init.py +76 -0
  99. zen_cli-0.1.4/src/zen_cli/ctxp/built_in_cmds/__init__.py +0 -0
  100. zen_cli-0.1.4/src/zen_cli/ctxp/built_in_cmds/_ut.py +117 -0
  101. zen_cli-0.1.4/src/zen_cli/ctxp/built_in_cmds/context.py +92 -0
  102. zen_cli-0.1.4/src/zen_cli/ctxp/built_in_cmds/profile.py +156 -0
  103. zen_cli-0.1.4/src/zen_cli/ctxp/cli_options.py +135 -0
  104. zen_cli-0.1.4/src/zen_cli/ctxp/cli_processor.py +257 -0
  105. zen_cli-0.1.4/src/zen_cli/ctxp/cmd_util.py +88 -0
  106. zen_cli-0.1.4/src/zen_cli/ctxp/config.py +21 -0
  107. zen_cli-0.1.4/src/zen_cli/ctxp/context.py +116 -0
  108. zen_cli-0.1.4/src/zen_cli/ctxp/data_util.py +461 -0
  109. zen_cli-0.1.4/src/zen_cli/ctxp/dispatcher.py +84 -0
  110. zen_cli-0.1.4/src/zen_cli/ctxp/extension.py +32 -0
  111. zen_cli-0.1.4/src/zen_cli/ctxp/fn_util.py +61 -0
  112. zen_cli-0.1.4/src/zen_cli/ctxp/fstore.py +273 -0
  113. zen_cli-0.1.4/src/zen_cli/ctxp/jsondot.py +341 -0
  114. zen_cli-0.1.4/src/zen_cli/ctxp/logger.py +220 -0
  115. zen_cli-0.1.4/src/zen_cli/ctxp/profile.py +268 -0
  116. zen_cli-0.1.4/src/zen_cli/ctxp/profile_store.py +35 -0
  117. zen_cli-0.1.4/src/zen_cli/ctxp/recent.py +116 -0
  118. zen_cli-0.1.4/src/zen_cli/ctxp/state.py +42 -0
  119. zen_cli-0.1.4/src/zen_cli/ctxp/telemetry.py +150 -0
  120. zen_cli-0.1.4/src/zen_cli/ctxp/template_util.py +26 -0
  121. zen_cli-0.1.4/src/zen_cli/ctxp/timeutil.py +11 -0
  122. zen_cli-0.1.4/src/zen_cli/ctxp/util.py +482 -0
  123. zen_cli-0.1.4/src/zen_cli/ctxp/var_template.py +109 -0
  124. zen_cli-0.1.4/src/zen_cli/mcp/__init__.py +3 -0
  125. zen_cli-0.1.4/src/zen_cli/mcp/_util.py +41 -0
  126. zen_cli-0.1.4/src/zen_cli/mcp/resources/__init__.py +33 -0
  127. zen_cli-0.1.4/src/zen_cli/mcp/resources/activity.py +60 -0
  128. zen_cli-0.1.4/src/zen_cli/mcp/resources/api.py +53 -0
  129. zen_cli-0.1.4/src/zen_cli/mcp/resources/credential.py +18 -0
  130. zen_cli-0.1.4/src/zen_cli/mcp/resources/deployment.py +14 -0
  131. zen_cli-0.1.4/src/zen_cli/mcp/resources/entitlement.py +21 -0
  132. zen_cli-0.1.4/src/zen_cli/mcp/resources/group.py +24 -0
  133. zen_cli-0.1.4/src/zen_cli/mcp/resources/network.py +21 -0
  134. zen_cli-0.1.4/src/zen_cli/mcp/resources/notification.py +40 -0
  135. zen_cli-0.1.4/src/zen_cli/mcp/resources/org.py +21 -0
  136. zen_cli-0.1.4/src/zen_cli/mcp/resources/pool.py +30 -0
  137. zen_cli-0.1.4/src/zen_cli/mcp/resources/provider.py +29 -0
  138. zen_cli-0.1.4/src/zen_cli/mcp/resources/sag.py +19 -0
  139. zen_cli-0.1.4/src/zen_cli/mcp/resources/task.py +63 -0
  140. zen_cli-0.1.4/src/zen_cli/mcp/resources/vm.py +34 -0
  141. zen_cli-0.1.4/src/zen_cli/mcp/server.py +72 -0
  142. zen_cli-0.1.4/src/zen_cli/service/__init__.py +0 -0
  143. zen_cli-0.1.4/src/zen_cli/service/activity.py +52 -0
  144. zen_cli-0.1.4/src/zen_cli/service/api.py +198 -0
  145. zen_cli-0.1.4/src/zen_cli/service/broker/__init__.py +2 -0
  146. zen_cli-0.1.4/src/zen_cli/service/broker/entitlement.py +21 -0
  147. zen_cli-0.1.4/src/zen_cli/service/broker/pool_group.py +24 -0
  148. zen_cli-0.1.4/src/zen_cli/service/cockpit/__init__.py +3 -0
  149. zen_cli-0.1.4/src/zen_cli/service/cockpit/network.py +24 -0
  150. zen_cli-0.1.4/src/zen_cli/service/cockpit/pool.py +24 -0
  151. zen_cli-0.1.4/src/zen_cli/service/cockpit/provider.py +21 -0
  152. zen_cli-0.1.4/src/zen_cli/service/cockpit/sag.py +24 -0
  153. zen_cli-0.1.4/src/zen_cli/service/cockpit/vm.py +110 -0
  154. zen_cli-0.1.4/src/zen_cli/service/credential.py +21 -0
  155. zen_cli-0.1.4/src/zen_cli/service/deployment.py +45 -0
  156. zen_cli-0.1.4/src/zen_cli/service/notification.py +44 -0
  157. zen_cli-0.1.4/src/zen_cli/service/org.py +32 -0
  158. zen_cli-0.1.4/src/zen_cli/service/task.py +381 -0
  159. zen_cli-0.1.4/src/zen_cli/sglib/__init__.py +3 -0
  160. zen_cli-0.1.4/src/zen_cli/sglib/auth.py +346 -0
  161. zen_cli-0.1.4/src/zen_cli/sglib/cli_options.py +63 -0
  162. zen_cli-0.1.4/src/zen_cli/sglib/client_util.py +490 -0
  163. zen_cli-0.1.4/src/zen_cli/sglib/csp.py +199 -0
  164. zen_cli-0.1.4/src/zen_cli/sglib/ez_client.py +348 -0
  165. zen_cli-0.1.4/src/zen_cli/sglib/init.py +18 -0
  166. zen_cli-0.1.4/src/zen_cli/sglib/login_support.py +333 -0
  167. zen_cli-0.1.4/src/zen_cli/sglib/mdc.py +19 -0
  168. zen_cli-0.1.4/src/zen_cli/sglib/payload_util.py +29 -0
  169. zen_cli-0.1.4/src/zen_cli/sglib/requtil.py +40 -0
  170. zen_cli-0.1.4/src/zen_cli/sglib/utils.py +107 -0
  171. zen_cli-0.1.4/src/zen_cli/sglib/zds_client.py +8 -0
  172. zen_cli-0.1.4/src/zen_cli/support/auth.py +63 -0
  173. zen_cli-0.1.4/src/zen_cli/support/payload.py +123 -0
  174. zen_cli-0.1.4/src/zen_cli/support/profile.py +49 -0
  175. zen_cli-0.1.4/src/zen_cli/support/task.py +312 -0
  176. zen_cli-0.1.4/src/zen_cli/support/use_util.py +103 -0
  177. zen_cli-0.1.4/src/zen_cli/support/vm.py +8 -0
  178. zen_cli-0.1.4/src/zen_cli/util/exec_util.py +263 -0
  179. zen_cli-0.1.4/src/zen_cli/util/exit.py +17 -0
  180. zen_cli-0.1.4/src/zen_cli/util/param_util.py +39 -0
  181. zen_cli-0.1.4/src/zen_cli/util/patch_util.py +38 -0
  182. zen_cli-0.1.4/src/zen_cli/util/query_util.py +98 -0
  183. zen_cli-0.1.4/src/zen_cli/util/readonly.py +8 -0
  184. zen_cli-0.1.4/src/zen_cli/util/versions.py +65 -0
  185. zen_cli-0.1.4/tests/test_auth_status.py +26 -0
  186. zen_cli-0.1.4/tests/test_cli.py +151 -0
  187. zen_cli-0.1.4/tests/test_device_login.py +132 -0
  188. zen_cli-0.1.4/tests/test_mcp.py +78 -0
  189. zen_cli-0.1.4/tests/test_payload.py +57 -0
  190. zen_cli-0.1.1/PKG-INFO +0 -57
  191. zen_cli-0.1.1/README.md +0 -37
  192. zen_cli-0.1.1/pyproject.toml +0 -40
  193. zen_cli-0.1.1/src/zen_cli/__init__.py +0 -1
  194. zen_cli-0.1.1/src/zen_cli/__main__.py +0 -4
  195. zen_cli-0.1.1/src/zen_cli/cli.py +0 -12
  196. zen_cli-0.1.1/src/zen_cli/commands/__init__.py +0 -1
  197. zen_cli-0.1.1/src/zen_cli/commands/hello.py +0 -7
  198. zen_cli-0.1.1/tests/test_cli.py +0 -24
  199. {zen_cli-0.1.1 → zen_cli-0.1.4}/LICENSE +0 -0
@@ -8,5 +8,8 @@ build/
8
8
  __pycache__/
9
9
  *.py[cod]
10
10
  .pypirc
11
+ .env
11
12
  .coverage
12
13
  htmlcov/
14
+ temp/
15
+ cli-ref/
zen_cli-0.1.4/PKG-INFO ADDED
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.5
2
+ Name: zen-cli
3
+ Version: 0.1.4
4
+ Summary: Command-line toolbox for Zen Cloud PC. Operate orgs, pools, VMs, and tasks; call ZDS APIs; expose a stdio MCP server.
5
+ Project-URL: Homepage, https://pypi.org/project/zen-cli/
6
+ Author: zen-cli authors
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: authlib
11
+ Requires-Dist: click>=8.1
12
+ Requires-Dist: coloredlogs
13
+ Requires-Dist: httpx2
14
+ Requires-Dist: mcp>=2
15
+ Requires-Dist: portalocker[win32]
16
+ Requires-Dist: pydantic
17
+ Requires-Dist: pyjwt
18
+ Requires-Dist: python-dotenv
19
+ Requires-Dist: pyyaml
20
+ Requires-Dist: questionary
21
+ Requires-Dist: tabulate
22
+ Requires-Dist: yumako
23
+ Provides-Extra: dev
24
+ Requires-Dist: build; extra == 'dev'
25
+ Requires-Dist: mypy; extra == 'dev'
26
+ Requires-Dist: pre-commit; extra == 'dev'
27
+ Requires-Dist: ruff; extra == 'dev'
28
+ Provides-Extra: release
29
+ Requires-Dist: build; extra == 'release'
30
+ Requires-Dist: twine; extra == 'release'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # zen-cli
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/zen-cli?logo=pypi&logoColor=white)](https://pypi.org/project/zen-cli/) [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![PyPI downloads](https://img.shields.io/pypi/dm/zen-cli)](https://pypi.org/project/zen-cli/) [![Wheel](https://img.shields.io/pypi/wheel/zen-cli)](https://pypi.org/project/zen-cli/) [![OS](https://img.shields.io/badge/OS-macOS%20%7C%20Linux%20%7C%20Windows-informational)](https://pypi.org/project/zen-cli/) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Checked with mypy](https://img.shields.io/badge/mypy-checked-2A6DB2)](https://mypy-lang.org/) [![Click](https://img.shields.io/badge/cli-Click%208-4584b6)](https://click.palletsprojects.com/) [![Hatch](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5)](https://github.com/pypa/hatch)
36
+
37
+ Command-line toolbox for Zen Cloud PC, designed for AI and human.
38
+
39
+ ## Contents
40
+
41
+ - [Try it out](#try-it-out)
42
+ - [Prerequisites](#prerequisites)
43
+ - [Installation](#installation)
44
+ - [Mac & Linux](#mac--linux)
45
+ - [Windows](#windows)
46
+ - [Use the CLI](#use-the-cli)
47
+ - [Documentation](#documentation)
48
+ - [License](#license)
49
+
50
+ ## Try it out
51
+
52
+ ### Prerequisites
53
+
54
+ * Python 3.12 or newer
55
+ * Pip
56
+
57
+ ### Installation
58
+
59
+ #### Mac & Linux
60
+
61
+ ```text
62
+ pip install zen-cli
63
+ ```
64
+
65
+ #### Windows
66
+
67
+ ```text
68
+ pip install zen-cli
69
+ ```
70
+
71
+ If Python was installed without “Add python to PATH”, add Python and its `Scripts` directory to PATH.
72
+
73
+ ### Use the CLI
74
+
75
+ ```text
76
+ zen login
77
+ zen auth status
78
+ zen auth details
79
+ zen auth token
80
+ ```
81
+
82
+ `zen login` reuses a valid cached token or configured credentials (API token, client id/secret). If neither is available it starts device-code login. It does not print the access token. Machine login is `zen login --client-id` / `--client-secret`. `zen auth status` is JSON (`logged_in`, `method`, `ttl`). `zen auth details` is the JWT and org. `zen auth token` is the bearer (`--service` on details/token). `zen auth refresh` refreshes the cached token.
83
+
84
+ Run a command, for example, list pools:
85
+
86
+ ```text
87
+ zen pool list
88
+ ```
89
+
90
+ ## Documentation
91
+
92
+ * [User guide](docs/user-guide.md) — profiles, flags, payloads, and command conventions
93
+ * [Cheatsheet](docs/cheatsheet.md) — command examples
94
+ * [Developer guide](docs/dev.md) — build, test, and package layout
95
+
96
+ ## License
97
+
98
+ Licensed under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,66 @@
1
+ # zen-cli
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/zen-cli?logo=pypi&logoColor=white)](https://pypi.org/project/zen-cli/) [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![PyPI downloads](https://img.shields.io/pypi/dm/zen-cli)](https://pypi.org/project/zen-cli/) [![Wheel](https://img.shields.io/pypi/wheel/zen-cli)](https://pypi.org/project/zen-cli/) [![OS](https://img.shields.io/badge/OS-macOS%20%7C%20Linux%20%7C%20Windows-informational)](https://pypi.org/project/zen-cli/) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Checked with mypy](https://img.shields.io/badge/mypy-checked-2A6DB2)](https://mypy-lang.org/) [![Click](https://img.shields.io/badge/cli-Click%208-4584b6)](https://click.palletsprojects.com/) [![Hatch](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5)](https://github.com/pypa/hatch)
4
+
5
+ Command-line toolbox for Zen Cloud PC, designed for AI and human.
6
+
7
+ ## Contents
8
+
9
+ - [Try it out](#try-it-out)
10
+ - [Prerequisites](#prerequisites)
11
+ - [Installation](#installation)
12
+ - [Mac & Linux](#mac--linux)
13
+ - [Windows](#windows)
14
+ - [Use the CLI](#use-the-cli)
15
+ - [Documentation](#documentation)
16
+ - [License](#license)
17
+
18
+ ## Try it out
19
+
20
+ ### Prerequisites
21
+
22
+ * Python 3.12 or newer
23
+ * Pip
24
+
25
+ ### Installation
26
+
27
+ #### Mac & Linux
28
+
29
+ ```text
30
+ pip install zen-cli
31
+ ```
32
+
33
+ #### Windows
34
+
35
+ ```text
36
+ pip install zen-cli
37
+ ```
38
+
39
+ If Python was installed without “Add python to PATH”, add Python and its `Scripts` directory to PATH.
40
+
41
+ ### Use the CLI
42
+
43
+ ```text
44
+ zen login
45
+ zen auth status
46
+ zen auth details
47
+ zen auth token
48
+ ```
49
+
50
+ `zen login` reuses a valid cached token or configured credentials (API token, client id/secret). If neither is available it starts device-code login. It does not print the access token. Machine login is `zen login --client-id` / `--client-secret`. `zen auth status` is JSON (`logged_in`, `method`, `ttl`). `zen auth details` is the JWT and org. `zen auth token` is the bearer (`--service` on details/token). `zen auth refresh` refreshes the cached token.
51
+
52
+ Run a command, for example, list pools:
53
+
54
+ ```text
55
+ zen pool list
56
+ ```
57
+
58
+ ## Documentation
59
+
60
+ * [User guide](docs/user-guide.md) — profiles, flags, payloads, and command conventions
61
+ * [Cheatsheet](docs/cheatsheet.md) — command examples
62
+ * [Developer guide](docs/dev.md) — build, test, and package layout
63
+
64
+ ## License
65
+
66
+ Licensed under the MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.26"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "zen-cli"
7
+ version = "0.1.4"
8
+ description = "Command-line toolbox for Zen Cloud PC. Operate orgs, pools, VMs, and tasks; call ZDS APIs; expose a stdio MCP server."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.12"
13
+ authors = [{ name = "zen-cli authors" }]
14
+ dependencies = [
15
+ "authlib",
16
+ "click>=8.1",
17
+ "coloredlogs",
18
+ "httpx2",
19
+ "mcp>=2",
20
+ "python-dotenv",
21
+ "pydantic",
22
+ "portalocker[win32]",
23
+ "PyJWT",
24
+ "pyyaml",
25
+ "questionary",
26
+ "tabulate",
27
+ "yumako",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = ["build", "pre-commit", "ruff", "mypy"]
32
+ release = ["build", "twine"]
33
+
34
+ [project.scripts]
35
+ zen = "zen_cli.cli:main"
36
+
37
+ [project.urls]
38
+ Homepage = "https://pypi.org/project/zen-cli/"
39
+
40
+ [tool.hatch.build.targets.sdist]
41
+ include = ["src/zen_cli", "tests", "README.md", "LICENSE"]
42
+
43
+ [tool.ruff]
44
+ target-version = "py312"
45
+ line-length = 120
46
+ src = ["src"]
47
+ exclude = ["cli-ref"]
48
+
49
+ [tool.ruff.lint]
50
+ select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
51
+
52
+ [tool.mypy]
53
+ python_version = "3.12"
54
+ strict = true
55
+ files = ["src"]
56
+
57
+ [[tool.mypy.overrides]]
58
+ module = ["mcp", "mcp.*"]
59
+ ignore_missing_imports = true
60
+
61
+ [[tool.mypy.overrides]]
62
+ module = [
63
+ "yumako",
64
+ "yumako.*",
65
+ "authlib",
66
+ "authlib.*",
67
+ "yaml",
68
+ "httpx2",
69
+ "httpx2.*",
70
+ ]
71
+ ignore_missing_imports = true
72
+
73
+ [[tool.mypy.overrides]]
74
+ module = ["zen_cli.cmds", "zen_cli.cmds.*", "zen_cli.ctxp", "zen_cli.ctxp.*"]
75
+ disallow_untyped_decorators = false
@@ -0,0 +1,5 @@
1
+ """zen-cli package."""
2
+
3
+ import httpx2
4
+
5
+ httpx2.alias_httpx()
@@ -0,0 +1,4 @@
1
+ from zen_cli.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,176 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import click
8
+ from dotenv import load_dotenv
9
+ from yumako import env
10
+
11
+ import zen_cli.ctxp as ctxp
12
+ from zen_cli.ctxp.cli_processor import register_commands
13
+
14
+ _PACKAGE_DIR = Path(__file__).resolve().parent
15
+ _COMMANDS_DIR = _PACKAGE_DIR / "cmds"
16
+ _CONFIG_DIR = _PACKAGE_DIR / "config"
17
+ _commands_loaded = False
18
+
19
+
20
+ # -----------------------------------------------------------
21
+ import zen_cli.ctxp.logger as logger # noqa: E402
22
+
23
+ logger.setup()
24
+ logging.getLogger("charset_normalizer").setLevel(logging.WARN)
25
+ logging.getLogger("csp").setLevel(logging.INFO)
26
+ logging.getLogger("context").setLevel(logging.WARN)
27
+ logging.getLogger("init").setLevel(logging.WARN)
28
+ logging.getLogger("profile").setLevel(logging.WARN)
29
+ logging.getLogger("httpx").setLevel(logging.WARN)
30
+ logging.getLogger("httpx2").setLevel(logging.WARN)
31
+ logging.getLogger("urllib3").setLevel(logging.ERROR)
32
+ # logging.getLogger("zen_cli.sglib.login_support").setLevel(logging.DEBUG)
33
+ # logging.getLogger("login_support").setLevel(logging.DEBUG)
34
+ # -----------------------------------------------------------
35
+ import zen_cli.sglib.ez_client as ez_client # noqa: E402
36
+
37
+ ez_client._print_http_error = False
38
+
39
+ CONTEXT_SETTINGS = {
40
+ "help_option_names": ["-h", "--help"],
41
+ "max_content_width": 160,
42
+ }
43
+
44
+
45
+ def _apply_readonly() -> None:
46
+ data = ctxp.profile.current(exit_on_failure=False)
47
+ if not data:
48
+ return
49
+ is_readonly = data.get("readonly")
50
+ if isinstance(is_readonly, str):
51
+ is_readonly = is_readonly.strip().lower() in ("1", "true", "yes")
52
+ else:
53
+ is_readonly = bool(is_readonly)
54
+ if is_readonly:
55
+ from zen_cli.util import readonly as readonly_support
56
+
57
+ ez_client.set_readonly(True, readonly_support.ALLOW_PATHS)
58
+
59
+
60
+ def _enable_verbose() -> None:
61
+ root = logging.getLogger()
62
+ root.setLevel(logging.DEBUG)
63
+ for handler in root.handlers:
64
+ handler.setLevel(logging.DEBUG)
65
+ for name in (
66
+ "zen_cli",
67
+ "csp",
68
+ "profile",
69
+ "httpx",
70
+ "httpx2",
71
+ "httpcore",
72
+ "authlib",
73
+ ):
74
+ logging.getLogger(name).setLevel(logging.DEBUG)
75
+
76
+
77
+ def _check_upgrade() -> None:
78
+ check_flag_from_env = env.bool("HCS_CLI_CHECK_UPGRADE", True)
79
+ if not check_flag_from_env:
80
+ return
81
+ if os.environ.get("_ZEN_COMPLETE"):
82
+ return
83
+ if len(sys.argv) > 1 and sys.argv[1] in ("upgrade", "mcp", "completion"):
84
+ return
85
+ from zen_cli.util.versions import check_upgrade
86
+
87
+ check_upgrade()
88
+
89
+
90
+ @click.group(name="zen", context_settings=CONTEXT_SETTINGS)
91
+ @click.version_option(package_name="zen-cli", prog_name="zen")
92
+ @click.option(
93
+ "--profile",
94
+ "-p",
95
+ type=str,
96
+ required=False,
97
+ help="Specify the profile to use. Optional.",
98
+ )
99
+ @click.option(
100
+ "--mdc",
101
+ type=str,
102
+ required=False,
103
+ help=("MDC name from profile zds.mdcs (e.g. beijing, singapore). Default is the first zds.mdcs entry."),
104
+ )
105
+ @click.option(
106
+ "--check-upgrade/--no-check-upgrade",
107
+ default=None,
108
+ help="Check for a new zen-cli version. On by default. Or set HCS_CLI_CHECK_UPGRADE=false.",
109
+ )
110
+ @click.option(
111
+ "--telemetry/--no-telemetry",
112
+ default=None,
113
+ help="Send command telemetry. Off by default. Or set ZEN_CLI_TELEMETRY=true.",
114
+ )
115
+ @click.option(
116
+ "--verbose",
117
+ "-v",
118
+ is_flag=True,
119
+ default=False,
120
+ help="Print debug logs, including HTTP method and URL.",
121
+ )
122
+ def cli(
123
+ profile: str,
124
+ mdc: str,
125
+ check_upgrade: bool | None,
126
+ telemetry: bool | None,
127
+ verbose: bool,
128
+ **kwargs: Any,
129
+ ) -> None:
130
+ """zen command-line interface."""
131
+ if verbose:
132
+ _enable_verbose()
133
+
134
+ if check_upgrade is not False:
135
+ _check_upgrade()
136
+
137
+ if telemetry is True:
138
+ ctxp.telemetry.enable()
139
+ elif telemetry is False:
140
+ ctxp.telemetry.disable()
141
+
142
+ if profile:
143
+ ctxp.profile.activate(profile, persist=False)
144
+
145
+ from zen_cli.ctxp._init import _initialized_app_name
146
+
147
+ if not _initialized_app_name:
148
+ return
149
+ from zen_cli.support import profile as profile_support
150
+
151
+ profile_support.ensure_standard_profiles(interactive=False)
152
+
153
+ if mdc:
154
+ from zen_cli.sglib import mdc as mdc_session
155
+
156
+ mdc_session.set(mdc)
157
+
158
+ _apply_readonly()
159
+ load_commands()
160
+
161
+
162
+ def load_commands() -> None:
163
+ """Attach built-in and cmds/ commands without running Click."""
164
+ global _commands_loaded
165
+ if _commands_loaded:
166
+ return
167
+ register_commands(cli, str(_COMMANDS_DIR))
168
+ _commands_loaded = True
169
+
170
+
171
+ def main() -> None:
172
+ ctxp.init(app_name="zen", config_path=str(_CONFIG_DIR))
173
+ load_dotenv(".env")
174
+ if os.environ.get("_ZEN_COMPLETE"):
175
+ ctxp.telemetry.disable()
176
+ ctxp.init_cli(main_cli=cli, commands_dir=str(_COMMANDS_DIR))
@@ -0,0 +1 @@
1
+ help = "System activity commands."
@@ -0,0 +1,20 @@
1
+ from typing import Any
2
+
3
+ import click
4
+
5
+ import zen_cli.sglib.cli_options as cli
6
+ from zen_cli.ctxp import recent
7
+ from zen_cli.service import activity
8
+
9
+
10
+ @click.command()
11
+ @cli.org_id
12
+ @click.argument("id", type=str, required=False)
13
+ def get(org: str, id: str) -> Any:
14
+ """Get a system activity by ID. Without an argument, use the recent activity."""
15
+ id = recent.require("activity", id)
16
+ org_id = None if org == "all" else cli.get_org_id(org)
17
+ ret = activity.get(id, org_id=org_id)
18
+ if ret:
19
+ return ret
20
+ return "", 1
@@ -0,0 +1,157 @@
1
+ from typing import Any
2
+
3
+ import click
4
+
5
+ import zen_cli.sglib.cli_options as cli
6
+ from zen_cli.ctxp import recent, util
7
+ from zen_cli.service import activity
8
+
9
+ _RESOURCE_TYPES = [
10
+ "ORG",
11
+ "ORG_PREFERENCE",
12
+ "DATACENTER",
13
+ "POOL",
14
+ "ENTITLEMENT",
15
+ "VM",
16
+ "EDGE",
17
+ "SAG",
18
+ "TASK",
19
+ "PROVIDER_INSTANCE",
20
+ "NET_INFRA",
21
+ "EXPRESS_DEPLOYMENT",
22
+ "POOL_GROUP",
23
+ "IMAGE",
24
+ "SITE",
25
+ "SPEC",
26
+ "SESSION",
27
+ "VMAGENT",
28
+ "NOTIFICATION",
29
+ ]
30
+
31
+ _TABLE_MAPPING = {
32
+ "code": "Code",
33
+ "severity": "Severity",
34
+ "initiator": "Initiator",
35
+ "serviceName": "Service",
36
+ "resourceId": "Resource",
37
+ "traceId": "Trace",
38
+ }
39
+
40
+
41
+ def _format_activity_table(data: Any) -> Any:
42
+ for d in data:
43
+ if d.get("timestamp") and not d.get("createdAt"):
44
+ d["createdAt"] = d["timestamp"]
45
+ if not d.get("name"):
46
+ d["name"] = d.get("resourceName") or d.get("code") or ""
47
+ if not d.get("type"):
48
+ d["type"] = d.get("resourceType")
49
+ return util.default_table_formatter(data, _TABLE_MAPPING)
50
+
51
+
52
+ @click.command(name="list")
53
+ @cli.org_id
54
+ @click.option("--service-name", type=str, required=False, help="Filter by service name.")
55
+ @click.option("--code", type=str, required=False, help="Filter by activity code.")
56
+ @click.option("--trace-id", type=str, required=False, help="Filter by trace ID.")
57
+ @click.option("--span-id", type=str, required=False, help="Filter by span ID.")
58
+ @click.option("--resource-id", type=str, required=False, help="Filter by resource ID.")
59
+ @click.option("--resource-name", type=str, required=False, help="Filter by resource name.")
60
+ @click.option("--task-id", type=str, required=False, help="Filter by task ID.")
61
+ @click.option("--parent-id", type=str, required=False, help="Filter by parent activity ID.")
62
+ @click.option("--initiator", type=str, required=False, help="Filter by initiator.")
63
+ @click.option(
64
+ "--initiator-type",
65
+ type=click.Choice(["SYSTEM", "OPS", "CUSTOMER_ADMIN", "USER"], case_sensitive=False),
66
+ required=False,
67
+ help="Filter by initiator type.",
68
+ )
69
+ @click.option(
70
+ "--resource-type",
71
+ type=click.Choice(_RESOURCE_TYPES, case_sensitive=False),
72
+ required=False,
73
+ help="Filter by resource type.",
74
+ )
75
+ @click.option(
76
+ "--severity",
77
+ type=click.Choice(["CRITICAL", "WARNING", "INFO", "TRIVIAL"], case_sensitive=False),
78
+ required=False,
79
+ help="Filter by severity.",
80
+ )
81
+ @click.option(
82
+ "--status",
83
+ type=click.Choice(
84
+ ["QUEUED", "START", "PROGRESS", "SUCCESS", "ERROR", "CANCELLED"],
85
+ case_sensitive=False,
86
+ ),
87
+ required=False,
88
+ help="Filter by status.",
89
+ )
90
+ @click.option(
91
+ "--timestamp-from",
92
+ type=str,
93
+ required=False,
94
+ help="Inclusive lower bound (ISO-8601).",
95
+ )
96
+ @click.option(
97
+ "--timestamp-to",
98
+ type=str,
99
+ required=False,
100
+ help="Inclusive upper bound (ISO-8601).",
101
+ )
102
+ @cli.sort
103
+ @cli.limit
104
+ @cli.formatter(_format_activity_table)
105
+ def list_activities(
106
+ org: str,
107
+ service_name: str,
108
+ code: str,
109
+ trace_id: str,
110
+ span_id: str,
111
+ resource_id: str,
112
+ resource_name: str,
113
+ task_id: str,
114
+ parent_id: str,
115
+ initiator: str,
116
+ initiator_type: str,
117
+ resource_type: str,
118
+ severity: str,
119
+ status: str,
120
+ timestamp_from: str,
121
+ timestamp_to: str,
122
+ sort: str,
123
+ limit: int,
124
+ ) -> Any:
125
+ """List system activities."""
126
+ org_id = None if org == "all" else cli.get_org_id(org)
127
+ if initiator_type:
128
+ initiator_type = initiator_type.upper()
129
+ if resource_type:
130
+ resource_type = resource_type.upper()
131
+ if severity:
132
+ severity = severity.upper()
133
+ if status:
134
+ status = status.upper()
135
+
136
+ ret = activity.list(
137
+ org_id=org_id,
138
+ service_name=service_name,
139
+ code=code,
140
+ trace_id=trace_id,
141
+ span_id=span_id,
142
+ resource_id=resource_id,
143
+ resource_name=resource_name,
144
+ task_id=task_id,
145
+ parent_id=parent_id,
146
+ initiator=initiator,
147
+ initiator_type=initiator_type,
148
+ resource_type=resource_type,
149
+ severity=severity,
150
+ status=status,
151
+ timestamp_from=timestamp_from,
152
+ timestamp_to=timestamp_to,
153
+ sort=sort,
154
+ limit=limit,
155
+ )
156
+ recent.helper.default_list(ret, "activity")
157
+ return ret