vmware-storage 2.5.0__tar.gz → 2.6.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 (125) hide show
  1. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/.claude-plugin/plugin.json +1 -1
  2. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/.mcp.json +1 -1
  3. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/PKG-INFO +10 -3
  4. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/README-CN.md +5 -1
  5. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/README.md +8 -1
  6. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/RELEASE_NOTES.md +10 -1
  7. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/SECURITY.md +2 -2
  8. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/openclaw.plugin.json +1 -1
  9. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/package.json +1 -1
  10. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/pyproject.toml +2 -2
  11. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/server.json +2 -2
  12. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/SKILL.md +2 -2
  13. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/references/cli-reference.md +13 -0
  14. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/references/setup-guide.md +3 -3
  15. vmware_storage-2.6.0/tests/eval/regression/test_datastore_cli_json.py +62 -0
  16. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/uv.lock +3 -3
  17. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/__init__.py +1 -1
  18. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/cli.py +12 -6
  19. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/.clawhubignore +0 -0
  20. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/.env.example +0 -0
  21. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/.gitignore +0 -0
  22. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/Dockerfile +0 -0
  23. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/LICENSE +0 -0
  24. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/config.example.yaml +0 -0
  25. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/docker-compose.yml +0 -0
  26. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/README.md +0 -0
  27. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/claude-code.json +0 -0
  28. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/continue.yaml +0 -0
  29. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/cursor.json +0 -0
  30. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/goose.json +0 -0
  31. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/localcowork.json +0 -0
  32. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/mcp-agent.yaml +0 -0
  33. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/examples/mcp-configs/vscode-copilot.json +0 -0
  34. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/patterns/README.md +0 -0
  35. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/patterns/iscsi-target-stale-rescan.yaml +0 -0
  36. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/evals/evals.json +0 -0
  37. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/references/agent-guardrails.md +0 -0
  38. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/skills/vmware-storage/references/capabilities.md +0 -0
  39. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/smithery.yaml +0 -0
  40. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/__init__.py +0 -0
  41. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/conftest.py +0 -0
  42. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/__init__.py +0 -0
  43. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/__init__.py +0 -0
  44. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/_family.py +0 -0
  45. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/_scores.json +0 -0
  46. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/_scoring.py +0 -0
  47. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/_skill.py +0 -0
  48. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/conftest.py +0 -0
  49. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/test_entity_reachability.py +0 -0
  50. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/test_error_actionability.py +0 -0
  51. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
  52. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  53. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/__init__.py +0 -0
  54. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_atexit_handlers_do_not_accumulate.py +0 -0
  55. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_audit_isolation.py +0 -0
  56. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_browse_ds_path_join.py +0 -0
  57. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_browse_sees_every_file.py +0 -0
  58. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_cached_loader_agrees_with_the_resolver.py +0 -0
  59. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_capability_grader.py +0 -0
  60. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
  61. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_credential_env_override.py +0 -0
  62. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_datastore_list_renders.py +0 -0
  63. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_doctor_authenticates_every_target.py +0 -0
  64. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_doctor_uses_the_config_the_tools_use.py +0 -0
  65. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
  66. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_error_hints_fit_the_surface.py +0 -0
  67. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_fc_multipath.py +0 -0
  68. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_image_registry_cross_process.py +0 -0
  69. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
  70. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_inventory_scale_n1.py +0 -0
  71. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_iscsi_unreachable_host.py +0 -0
  72. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_list_cached_images_target.py +0 -0
  73. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_login_without_a_role.py +0 -0
  74. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_release_blockers.py +0 -0
  75. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_remedy_survives_a_long_home.py +0 -0
  76. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_result_envelope.py +0 -0
  77. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_returned_failure_is_audited.py +0 -0
  78. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_scan_populates_image_cache.py +0 -0
  79. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_session_probe_eviction.py +0 -0
  80. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_storage_hardening_fixes.py +0 -0
  81. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_storage_ops_behavior.py +0 -0
  82. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_tool_names_2_0.py +0 -0
  83. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_vim_attribute_conformance.py +0 -0
  84. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_vsan_efficiency.py +0 -0
  85. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_vsan_health_is_actually_queried.py +0 -0
  86. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/regression/test_vsan_reports_what_it_could_not_read.py +0 -0
  87. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/spec/__init__.py +0 -0
  88. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/spec/vsan_efficiency_endpoints.py +0 -0
  89. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/eval/spec/vsan_health_endpoints.py +0 -0
  90. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_access_info.py +0 -0
  91. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_datastore_vm_usage.py +0 -0
  92. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_filelock.py +0 -0
  93. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_init_and_onboarding.py +0 -0
  94. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_iscsi_gate.py +0 -0
  95. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_no_destructive_ops.py +0 -0
  96. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_no_destructive_vm_code.py +0 -0
  97. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_policy_roles.py +0 -0
  98. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_request_error_hints.py +0 -0
  99. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_safe_error_passthrough.py +0 -0
  100. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_session_pool.py +0 -0
  101. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/tests/test_stop_signal_runs_atexit.py +0 -0
  102. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/_filelock.py +0 -0
  103. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/access_requirements.py +0 -0
  104. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/config.py +0 -0
  105. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/connection.py +0 -0
  106. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/doctor.py +0 -0
  107. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/init_wizard.py +0 -0
  108. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/mcp_server/__init__.py +0 -0
  109. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/mcp_server/__main__.py +0 -0
  110. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/mcp_server/server.py +0 -0
  111. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/notify/__init__.py +0 -0
  112. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/notify/audit.py +0 -0
  113. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/__init__.py +0 -0
  114. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/datastore_browser.py +0 -0
  115. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/datastore_usage.py +0 -0
  116. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/inventory.py +0 -0
  117. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/iscsi_config.py +0 -0
  118. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/iscsi_gate.py +0 -0
  119. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/multipath.py +0 -0
  120. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/storage_paths.py +0 -0
  121. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/vsan.py +0 -0
  122. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/vsan_efficiency.py +0 -0
  123. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/ops/vsan_sdk.py +0 -0
  124. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/policy_environment.py +0 -0
  125. {vmware_storage-2.5.0 → vmware_storage-2.6.0}/vmware_storage/policy_roles.py +0 -0
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "vmware-storage",
4
4
  "displayName": "VMware Storage",
5
- "version": "2.5.0",
5
+ "version": "2.6.0",
6
6
  "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
7
7
  "author": {
8
8
  "name": "Wei Zhou",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-storage==2.5.0",
8
+ "vmware-storage==2.6.0",
9
9
  "vmware-storage-mcp"
10
10
  ]
11
11
  }
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-storage
3
- Version: 2.5.0
3
+ Version: 2.6.0
4
4
  Summary: VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill.
5
5
  Project-URL: Homepage, https://github.com/vmware-skills/VMware-Storage
6
6
  Project-URL: Repository, https://github.com/vmware-skills/VMware-Storage
7
7
  Project-URL: Issues, https://github.com/vmware-skills/VMware-Storage/issues
8
8
  Project-URL: Changelog, https://github.com/vmware-skills/VMware-Storage/blob/main/RELEASE_NOTES.md
9
- Author-email: Wei Zhou <wei-wz.zhou@broadcom.com>
9
+ Author-email: Wei Zhou <zhouwei008@gmail.com>
10
10
  License-Expression: MIT
11
11
  License-File: LICENSE
12
12
  Requires-Python: >=3.10
@@ -24,7 +24,7 @@ Description-Content-Type: text/markdown
24
24
 
25
25
  For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
26
26
 
27
- > **Author**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
27
+ > **Author**: Wei Zhou, VMware by Broadcom — zhouwei008@gmail.com
28
28
  > This is a community-driven project by a VMware engineer, not an official VMware product.
29
29
  > For official VMware developer tools see [developer.broadcom.com](https://developer.broadcom.com).
30
30
 
@@ -137,6 +137,13 @@ The `add-target` command automatically rescans storage. Use `--dry-run` to previ
137
137
 
138
138
  ### Find Deployable Images Across Datastores
139
139
 
140
+ For scripts, add `--json` to `datastore list`, `datastore browse`, or
141
+ `datastore scan-images`. Successful output is the complete result envelope,
142
+ including `items`, counts, truncation and any cache note. Without the flag,
143
+ list keeps its table and browse/scan-images keep their legacy JSON arrays.
144
+ Other read commands already output JSON by default. Failures still exit nonzero
145
+ with the existing CLI error message.
146
+
140
147
  1. List all datastores: `vmware-storage datastore list`
141
148
  2. Scan for images: `vmware-storage datastore scan-images datastore01`
142
149
  3. Browse with a pattern: `vmware-storage datastore browse datastore01 --pattern "*.iso"`
@@ -3,7 +3,7 @@
3
3
 
4
4
  要用自己的模型和 Agent 平台进行只读自测,请看 marketplace 根目录的[共享模型适配自测任务](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md)。
5
5
 
6
- > **作者**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
6
+ > **作者**: Wei Zhou, VMware by Broadcom — zhouwei008@gmail.com
7
7
  > 本项目由 VMware 工程师维护的社区项目,非 VMware 官方产品。
8
8
  > VMware 官方开发者工具请访问 [developer.broadcom.com](https://developer.broadcom.com)。
9
9
 
@@ -152,6 +152,10 @@ targets:
152
152
 
153
153
  ### 查找可部署镜像
154
154
 
155
+ 脚本可为 `datastore list`、`datastore browse`、`datastore scan-images` 添加 `--json`。
156
+ 成功时输出完整结果封装,保留 `items`、数量、截断状态及缓存提示。省略该选项时,list 仍为表格,
157
+ browse/scan-images 仍为原有 JSON 数组。其他只读命令已默认输出 JSON;失败仍以非零退出码和现有 CLI 错误消息报告。
158
+
155
159
  1. 列出所有数据存储:`vmware-storage datastore list`
156
160
  2. 扫描镜像:`vmware-storage datastore scan-images datastore01`
157
161
  3. 按模式浏览:`vmware-storage datastore browse datastore01 --pattern "*.iso"`
@@ -3,7 +3,7 @@
3
3
 
4
4
  For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
5
5
 
6
- > **Author**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
6
+ > **Author**: Wei Zhou, VMware by Broadcom — zhouwei008@gmail.com
7
7
  > This is a community-driven project by a VMware engineer, not an official VMware product.
8
8
  > For official VMware developer tools see [developer.broadcom.com](https://developer.broadcom.com).
9
9
 
@@ -116,6 +116,13 @@ The `add-target` command automatically rescans storage. Use `--dry-run` to previ
116
116
 
117
117
  ### Find Deployable Images Across Datastores
118
118
 
119
+ For scripts, add `--json` to `datastore list`, `datastore browse`, or
120
+ `datastore scan-images`. Successful output is the complete result envelope,
121
+ including `items`, counts, truncation and any cache note. Without the flag,
122
+ list keeps its table and browse/scan-images keep their legacy JSON arrays.
123
+ Other read commands already output JSON by default. Failures still exit nonzero
124
+ with the existing CLI error message.
125
+
119
126
  1. List all datastores: `vmware-storage datastore list`
120
127
  2. Scan for images: `vmware-storage datastore scan-images datastore01`
121
128
  3. Browse with a pattern: `vmware-storage datastore browse datastore01 --pattern "*.iso"`
@@ -1,3 +1,12 @@
1
+ ## v2.6.0 — complete datastore CLI JSON envelopes (2026-11-08)
2
+
3
+ * Update public author/project contact metadata to `zhouwei008@gmail.com`.
4
+
5
+ ### datastore CLI JSON envelopes
6
+ - Add `--json` to datastore list/browse/scan-images to preserve the complete
7
+ result envelope, including unknown totals, truncation and cache notes.
8
+ - Preserve the existing default table/JSON-array formats and error behavior.
9
+
1
10
  ## v2.5.0 — per-VM datastore usage and clearer storage refusals (2026-11-01)
2
11
 
3
12
  * New read-only `get_datastore_vm_usage(datastore_name, top_n=10)` reports per-VM committed, uncommitted and unshared space on one datastore, plus attributed/unattributed used space and VMs whose usage could not be measured. It uses one PropertyCollector pass, sorts largest committed usage first, and does not turn unknown into zero. The MCP surface now has 16 tools. Verified against a lab vCenter before this release branch.
@@ -1388,7 +1397,7 @@ This release tracks vmware-pilot v1.5.17 (new `investigate_alert` template + `re
1388
1397
  - **[READ]/[WRITE] tool prefixes**: All MCP tool descriptions now start with [READ] or [WRITE] to clearly indicate operation type
1389
1398
  - **Read/write split counts**: SKILL.md MCP Tools section header shows exact read vs write tool counts
1390
1399
  - **Negative routing**: Description frontmatter includes "Do NOT use when..." clause to prevent misrouting
1391
- - **Broadcom author attestation**: README.md, README-CN.md, and pyproject.toml include VMware by Broadcom author identity (wei-wz.zhou@broadcom.com) to resolve Snyk E005 brand warnings
1400
+ - **Broadcom author attestation**: README.md, README-CN.md, and pyproject.toml include VMware by Broadcom author identity (zhouwei008@gmail.com) to resolve Snyk E005 brand warnings
1392
1401
 
1393
1402
  ### Storage-specific
1394
1403
 
@@ -4,13 +4,13 @@
4
4
 
5
5
  This is a community-maintained open-source project and is **not affiliated with, endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and "vSphere" are trademarks of Broadcom Inc.
6
6
 
7
- **Author**: Wei Zhou, VMware by Broadcom — wei-wz.zhou@broadcom.com
7
+ **Author**: Wei Zhou, VMware by Broadcom — zhouwei008@gmail.com
8
8
 
9
9
  ## Reporting Vulnerabilities
10
10
 
11
11
  If you discover a security vulnerability, please report it privately:
12
12
 
13
- - **Email**: wei-wz.zhou@broadcom.com
13
+ - **Email**: zhouwei008@gmail.com
14
14
  - **GitHub**: Open a [private security advisory](https://github.com/vmware-skills/VMware-Storage/security/advisories/new)
15
15
 
16
16
  Do **not** open a public GitHub issue for security vulnerabilities.
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-storage==2.5.0",
14
+ "vmware-storage==2.6.0",
15
15
  "vmware-storage-mcp"
16
16
  ]
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zw008/vmware-storage",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "VMware vSphere storage with 16 MCP tools: datastores, iSCSI, vSAN, FC multipath.",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-storage"
7
- version = "2.5.0"
7
+ version = "2.6.0"
8
8
  description = "VMware vSphere storage management: datastores, iSCSI, vSAN. Domain-focused MCP skill."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.10"
12
- authors = [{ name = "Wei Zhou", email = "wei-wz.zhou@broadcom.com" }]
12
+ authors = [{ name = "Wei Zhou", email = "zhouwei008@gmail.com" }]
13
13
  dependencies = [
14
14
  "pyvmomi>=8.0.3.0,<10.0",
15
15
  "pyaml>=24.0,<27.0",
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/vmware-skills/VMware-Storage",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.5.0",
10
+ "version": "2.6.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-storage",
15
- "version": "2.5.0",
15
+ "version": "2.6.0",
16
16
  "runtimeHint": "uvx",
17
17
  "packageArguments": [
18
18
  {
@@ -42,7 +42,7 @@ VMware vSphere storage management — 16 MCP tools for datastores, iSCSI, vSAN,
42
42
  ## Quick Install
43
43
 
44
44
  ```bash
45
- uv tool install vmware-storage==2.5.0
45
+ uv tool install vmware-storage==2.6.0
46
46
  vmware-storage init # guided setup: writes config + .env (chmod 600, password grep-safe), then verifies
47
47
  vmware-storage doctor
48
48
  ```
@@ -274,7 +274,7 @@ Corporate TLS proxies inject certificates that uv's bundled CA store doesn't tru
274
274
  ## Setup
275
275
 
276
276
  ```bash
277
- uv tool install vmware-storage==2.5.0
277
+ uv tool install vmware-storage==2.6.0
278
278
  mkdir -p ~/.vmware-storage
279
279
  cp config.example.yaml ~/.vmware-storage/config.yaml
280
280
  # Edit config.yaml with your vCenter/ESXi targets
@@ -276,3 +276,16 @@ Privileges are checked on the root folder: `not_granted_at_root` may still be gr
276
276
  |----------|-------------|
277
277
  | `VMWARE_STORAGE_CONFIG` | Override config file path (used by MCP server) |
278
278
  | `VMWARE_<TARGET>_PASSWORD` | Password for a target (e.g., `VMWARE_MY_VCENTER_PASSWORD`) |
279
+
280
+ ### Datastore JSON envelopes
281
+
282
+ Add `--json` to `datastore list`, `datastore browse`, or `datastore scan-images`
283
+ for the full successful result envelope, matching the MCP operation's result:
284
+ `items`, `returned`, `limit`, `total`, `truncated`, `hint`, and any extra evidence
285
+ such as `cache_note`. Unknown/null values remain unknown, not zero or complete.
286
+ A successful image scan can carry a local-cache failure note; do not discard it.
287
+
288
+ Without this option list prints its table and browse/scan-images retain their
289
+ legacy JSON arrays. Other read commands already emit JSON by default. This flag
290
+ does not alter reads, local-cache writes, Policy or auditing. Errors retain the
291
+ existing text message and nonzero exit code; scripts must check the exit status.
@@ -13,19 +13,19 @@ Complete setup and security guide for `vmware-storage`.
13
13
  ### Via uv (recommended)
14
14
 
15
15
  ```bash
16
- uv tool install vmware-storage==2.5.0
16
+ uv tool install vmware-storage==2.6.0
17
17
  ```
18
18
 
19
19
  ### Via pip
20
20
 
21
21
  ```bash
22
- pip install vmware-storage==2.5.0
22
+ pip install vmware-storage==2.6.0
23
23
  ```
24
24
 
25
25
  ### From source
26
26
 
27
27
  ```bash
28
- git clone --branch v2.5.0 https://github.com/vmware-skills/VMware-Storage.git
28
+ git clone --branch v2.6.0 https://github.com/vmware-skills/VMware-Storage.git
29
29
  cd VMware-Storage
30
30
  pip install -e .
31
31
  ```
@@ -0,0 +1,62 @@
1
+ """Successful JSON output must preserve coverage and local-cache evidence."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from types import SimpleNamespace
6
+
7
+ import pytest
8
+ from typer.testing import CliRunner
9
+
10
+ from vmware_storage import cli
11
+ from vmware_storage.ops import datastore_browser, inventory
12
+
13
+
14
+ @pytest.fixture
15
+ def connection(monkeypatch):
16
+ si = object()
17
+ monkeypatch.setattr(cli, "_get_connection", lambda *a: si)
18
+ monkeypatch.setattr(cli, "load_config", lambda *a: object())
19
+ monkeypatch.setattr(cli, "ConnectionManager", lambda *a: SimpleNamespace(
20
+ resolve_target_name=lambda target: "lab", connect=lambda target: si))
21
+ return si
22
+
23
+
24
+ def _envelope(items):
25
+ return {"items": items, "returned": len(items), "limit": 2,
26
+ "total": None, "truncated": True, "hint": "incomplete evidence"}
27
+
28
+
29
+ @pytest.mark.parametrize("command,owner,operation", [
30
+ (["list"], inventory, "list_datastores"),
31
+ (["browse", "ds"], datastore_browser, "browse_datastore"),
32
+ (["scan-images", "ds"], datastore_browser, "scan_images"),
33
+ ])
34
+ @pytest.mark.parametrize("items", [[], [{"name": "[SSD] 数据", "usage_pct": None}]])
35
+ def test_json_preserves_envelope_even_when_empty_or_unknown(
36
+ monkeypatch, connection, command, owner, operation, items,
37
+ ):
38
+ envelope = _envelope(items)
39
+ envelope["cache_note"] = "Scan succeeded but the local cache was not updated"
40
+ calls = []
41
+
42
+ def read(si, *a, **kw):
43
+ assert si is connection
44
+ calls.append(kw)
45
+ return envelope
46
+
47
+ monkeypatch.setattr(owner, operation, read)
48
+ out = CliRunner().invoke(cli.app, ["datastore", *command, "--json"])
49
+ assert out.exit_code == 0, out.output
50
+ assert json.loads(out.stdout) == envelope
51
+ assert len(calls) == 1
52
+
53
+
54
+ @pytest.mark.parametrize("command,operation", [
55
+ (["browse", "ds"], "browse_datastore"), (["scan-images", "ds"], "scan_images"),
56
+ ])
57
+ def test_legacy_json_arrays_are_unchanged_without_flag(monkeypatch, connection, command, operation):
58
+ items = [{"name": "image.iso"}]
59
+ monkeypatch.setattr(datastore_browser, operation, lambda *a, **kw: _envelope(items))
60
+ out = CliRunner().invoke(cli.app, ["datastore", *command])
61
+ assert out.exit_code == 0, out.output
62
+ assert json.loads(out.stdout) == items
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 5
3
3
  requires-python = ">=3.10"
4
4
  resolution-markers = [
5
5
  "python_full_version >= '3.14' and sys_platform == 'win32'",
@@ -341,7 +341,7 @@ name = "exceptiongroup"
341
341
  version = "1.3.1"
342
342
  source = { registry = "https://pypi.org/simple" }
343
343
  dependencies = [
344
- { name = "typing-extensions", marker = "python_full_version < '3.13'" },
344
+ { name = "typing-extensions" },
345
345
  ]
346
346
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
347
347
  wheels = [
@@ -1176,7 +1176,7 @@ wheels = [
1176
1176
 
1177
1177
  [[package]]
1178
1178
  name = "vmware-storage"
1179
- version = "2.5.0"
1179
+ version = "2.6.0"
1180
1180
  source = { editable = "." }
1181
1181
  dependencies = [
1182
1182
  { name = "mcp", extra = ["cli"] },
@@ -1,3 +1,3 @@
1
1
  """VMware Storage — vSphere datastore, iSCSI, and vSAN management."""
2
2
 
3
- __version__ = "2.5.0"
3
+ __version__ = "2.6.0"
@@ -208,6 +208,7 @@ def _usage_cell(usage_pct: float | None) -> Text:
208
208
  @handle_cli_errors
209
209
  @audited("list_all_datastores")
210
210
  def ds_list(
211
+ as_json: bool = typer.Option(False, "--json", help="Return the complete result envelope"),
211
212
  target: str | None = typer.Option(None, help="Target name"),
212
213
  config: str | None = typer.Option(None, "--config", help="Config file path"),
213
214
  ) -> None:
@@ -215,8 +216,11 @@ def ds_list(
215
216
  from vmware_storage.ops.inventory import list_datastores
216
217
 
217
218
  si = _get_connection(target, config)
218
- result = list_datastores(si, include_vm_count=True)["items"]
219
+ result = list_datastores(si, include_vm_count=True)
219
220
  _audit.log_query(target=target or "default", resource="datastores", query_type="list")
221
+ if as_json:
222
+ _print_json(result)
223
+ return
220
224
  table = Table(title="Datastores")
221
225
  table.add_column("Name", style="bold")
222
226
  table.add_column("Type")
@@ -224,7 +228,7 @@ def ds_list(
224
228
  table.add_column("Free GB", justify="right")
225
229
  table.add_column("Usage %", justify="right")
226
230
  table.add_column("VMs", justify="right")
227
- for ds in result:
231
+ for ds in result["items"]:
228
232
  table.add_row(
229
233
  # Text(), not str: a datastore may legally be called "[SSD] prod",
230
234
  # and vCenter's name is data, not markup we wrote.
@@ -245,6 +249,7 @@ def ds_browse(
245
249
  ds_name: str = typer.Argument(help="Datastore name"),
246
250
  path: str = typer.Option("", help="Subdirectory path"),
247
251
  pattern: str = typer.Option("*", help="Glob pattern"),
252
+ as_json: bool = typer.Option(False, "--json", help="Return the complete result envelope"),
248
253
  target: str | None = typer.Option(None, help="Target name"),
249
254
  config: str | None = typer.Option(None, "--config", help="Config file path"),
250
255
  ) -> None:
@@ -252,9 +257,9 @@ def ds_browse(
252
257
  from vmware_storage.ops.datastore_browser import browse_datastore
253
258
 
254
259
  si = _get_connection(target, config)
255
- result = browse_datastore(si, ds_name, path=path, pattern=pattern)["items"]
260
+ result = browse_datastore(si, ds_name, path=path, pattern=pattern)
256
261
  _audit.log_query(target=target or "default", resource=ds_name, query_type="browse")
257
- _print_json(result)
262
+ _print_json(result if as_json else result["items"])
258
263
 
259
264
 
260
265
  @ds_app.command("scan-images")
@@ -262,6 +267,7 @@ def ds_browse(
262
267
  @audited("scan_datastore_images")
263
268
  def ds_scan_images(
264
269
  ds_name: str = typer.Argument(help="Datastore name"),
270
+ as_json: bool = typer.Option(False, "--json", help="Return the complete result envelope"),
265
271
  target: str | None = typer.Option(None, help="Target name"),
266
272
  config: str | None = typer.Option(None, "--config", help="Config file path"),
267
273
  ) -> None:
@@ -271,9 +277,9 @@ def ds_scan_images(
271
277
  conn_mgr = ConnectionManager(load_config(Path(config) if config else None))
272
278
  name = conn_mgr.resolve_target_name(target)
273
279
  si = conn_mgr.connect(name)
274
- result = scan_images(si, ds_name, target=name)["items"]
280
+ result = scan_images(si, ds_name, target=name)
275
281
  _audit.log_query(target=target or "default", resource=ds_name, query_type="scan_images")
276
- _print_json(result)
282
+ _print_json(result if as_json else result["items"])
277
283
 
278
284
 
279
285
  # ---------------------------------------------------------------------------
File without changes