vmware-aiops 2.3.2__tar.gz → 2.4.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 (198) hide show
  1. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.claude-plugin/plugin.json +1 -1
  2. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.mcp.json +1 -1
  3. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/PKG-INFO +2 -1
  4. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/README-CN.md +1 -0
  5. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/README.md +1 -0
  6. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/RELEASE_NOTES.md +9 -0
  7. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/openclaw.plugin.json +1 -1
  8. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/package.json +1 -1
  9. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/pyproject.toml +1 -1
  10. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/server.json +2 -2
  11. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/SKILL.md +2 -2
  12. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/references/setup-guide.md +6 -5
  13. vmware_aiops-2.4.0/tests/test_policy_roles.py +117 -0
  14. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/uv.lock +5 -5
  15. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/__init__.py +1 -1
  16. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/policy_environment.py +3 -0
  17. vmware_aiops-2.4.0/vmware_aiops/policy_roles.py +123 -0
  18. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.clawhubignore +0 -0
  19. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.env.example +0 -0
  20. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  21. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  22. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  23. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  24. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.github/dependabot.yml +0 -0
  25. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/.gitignore +0 -0
  26. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/Dockerfile +0 -0
  27. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/LICENSE +0 -0
  28. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/SECURITY.md +0 -0
  29. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/config.example.yaml +0 -0
  30. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docker-compose.yml +0 -0
  31. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/continue.md +0 -0
  32. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/cursor.md +0 -0
  33. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/goose.md +0 -0
  34. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/localcowork.md +0 -0
  35. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/mcp-agent.md +0 -0
  36. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/integrations/vscode-copilot.md +0 -0
  37. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/docs/plans/2026-03-09-plan-apply-design.md +0 -0
  38. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/deploy.yaml +0 -0
  39. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/README.md +0 -0
  40. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/claude-code.json +0 -0
  41. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/continue.yaml +0 -0
  42. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/cursor.json +0 -0
  43. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/goose.json +0 -0
  44. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/localcowork.json +0 -0
  45. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/mcp-agent.yaml +0 -0
  46. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/mcp-configs/vscode-copilot.json +0 -0
  47. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/examples/ollama-local-setup.md +0 -0
  48. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/evals/evals.json +0 -0
  49. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/references/agent-guardrails.md +0 -0
  50. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/references/capabilities.md +0 -0
  51. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/references/cli-reference.md +0 -0
  52. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/skills/vmware-aiops/references/investigation-protocol.md +0 -0
  53. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/smithery.yaml +0 -0
  54. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/__init__.py +0 -0
  55. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/conftest.py +0 -0
  56. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/__init__.py +0 -0
  57. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/__init__.py +0 -0
  58. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/_family.py +0 -0
  59. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/_scores.json +0 -0
  60. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/_scoring.py +0 -0
  61. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/_skill.py +0 -0
  62. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/conftest.py +0 -0
  63. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/test_entity_reachability.py +0 -0
  64. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/test_error_actionability.py +0 -0
  65. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
  66. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  67. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/__init__.py +0 -0
  68. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/_diag_fakes.py +0 -0
  69. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/_pc_fakes.py +0 -0
  70. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_aiops_specific.py +0 -0
  71. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_alarm_health_scale_n1.py +0 -0
  72. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_atexit_handlers_do_not_accumulate.py +0 -0
  73. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_capability_grader.py +0 -0
  74. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_cli_destructive_guards.py +0 -0
  75. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_cli_writes_guarded.py +0 -0
  76. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_cluster_health_summary_delegation.py +0 -0
  77. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_code_review_fixes.py +0 -0
  78. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_config_path_resolution_is_shared.py +0 -0
  79. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_daemon_calls_are_audited.py +0 -0
  80. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_daemon_host_log_cycles.py +0 -0
  81. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_deploy_correctness.py +0 -0
  82. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_doctor_authenticates_every_target.py +0 -0
  83. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_documented_gates_match_the_registry.py +0 -0
  84. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_enum_params_match_implementation.py +0 -0
  85. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_env_secret_obfuscation.py +0 -0
  86. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_event_sets_match_real_class_names.py +0 -0
  87. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_events_are_read_newest_first.py +0 -0
  88. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_guest_download_writes_the_local_disk.py +0 -0
  89. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_guest_username_is_explicit.py +0 -0
  90. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_host_log_scan_reads_real_logs.py +0 -0
  91. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_host_paths_protected.py +0 -0
  92. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_instructions_name_targets.py +0 -0
  93. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_inventory_scale_issue31.py +0 -0
  94. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_list_envelope_conformance.py +0 -0
  95. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_list_plans_is_non_destructive.py +0 -0
  96. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_login_without_vsphere_role.py +0 -0
  97. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_mcp_frame_marks_failures.py +0 -0
  98. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_ova_network_mapping.py +0 -0
  99. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_release_blockers.py +0 -0
  100. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_result_envelope.py +0 -0
  101. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_returned_failure_is_audited.py +0 -0
  102. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_session_probe_eviction.py +0 -0
  103. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_set_ttl_safety.py +0 -0
  104. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_snapshot_async.py +0 -0
  105. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_text_io_declares_encoding.py +0 -0
  106. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_tool_annotations_match_behaviour.py +0 -0
  107. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_unreachable_host_vmks.py +0 -0
  108. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_v1_5_25_blockers.py +0 -0
  109. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/eval/regression/test_vim_attribute_conformance.py +0 -0
  110. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_access_info.py +0 -0
  111. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_cluster_mgmt_sanitize.py +0 -0
  112. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_cluster_ttl_plan_gate.py +0 -0
  113. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_concurrent_local_state.py +0 -0
  114. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_config.py +0 -0
  115. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_drs_rules.py +0 -0
  116. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_gate_review_fixes.py +0 -0
  117. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_guest_tools_gate.py +0 -0
  118. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_host_network_mgmt.py +0 -0
  119. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_init_and_onboarding.py +0 -0
  120. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_network_mgmt.py +0 -0
  121. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_no_destructive_ops.py +0 -0
  122. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_plan_gate_narrow_fixes.py +0 -0
  123. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_plan_rollback_final_fixes.py +0 -0
  124. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_reset_alarm_gate.py +0 -0
  125. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_results_name_their_target.py +0 -0
  126. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_safe_error_passthrough.py +0 -0
  127. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_session_pool.py +0 -0
  128. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_stop_signal_runs_atexit.py +0 -0
  129. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_vm_delete_blast_radius.py +0 -0
  130. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/tests/test_vm_tools_gate.py +0 -0
  131. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/_fsutil.py +0 -0
  132. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/access_requirements.py +0 -0
  133. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/__init__.py +0 -0
  134. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/_common.py +0 -0
  135. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/_root.py +0 -0
  136. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/access.py +0 -0
  137. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/alarm.py +0 -0
  138. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/cluster.py +0 -0
  139. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/deploy.py +0 -0
  140. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/doctor.py +0 -0
  141. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/hub.py +0 -0
  142. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/investigate.py +0 -0
  143. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/mcp_config.py +0 -0
  144. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/plan.py +0 -0
  145. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/scan.py +0 -0
  146. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/summary.py +0 -0
  147. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/cli/vm.py +0 -0
  148. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/config.py +0 -0
  149. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/connection.py +0 -0
  150. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/doctor.py +0 -0
  151. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/init_wizard.py +0 -0
  152. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/__init__.py +0 -0
  153. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/__main__.py +0 -0
  154. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/_shared.py +0 -0
  155. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/server.py +0 -0
  156. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/__init__.py +0 -0
  157. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/access.py +0 -0
  158. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/alarm.py +0 -0
  159. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/cluster.py +0 -0
  160. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/datastore.py +0 -0
  161. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/deploy.py +0 -0
  162. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/guest.py +0 -0
  163. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/network.py +0 -0
  164. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/plan.py +0 -0
  165. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/summary.py +0 -0
  166. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/ttl.py +0 -0
  167. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/mcp_server/tools/vm.py +0 -0
  168. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/notify/__init__.py +0 -0
  169. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/notify/audit.py +0 -0
  170. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/notify/logger.py +0 -0
  171. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/notify/webhook.py +0 -0
  172. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/__init__.py +0 -0
  173. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/_paging.py +0 -0
  174. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/alarm_mgmt.py +0 -0
  175. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/cluster_gate.py +0 -0
  176. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/cluster_mgmt.py +0 -0
  177. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/datastore_browser.py +0 -0
  178. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/gate.py +0 -0
  179. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/guest_gate.py +0 -0
  180. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/guest_ops.py +0 -0
  181. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/health.py +0 -0
  182. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/host_network_mgmt.py +0 -0
  183. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/inventory.py +0 -0
  184. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/iscsi_config.py +0 -0
  185. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/network_mgmt.py +0 -0
  186. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/ova_deploy.py +0 -0
  187. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/plan_executor.py +0 -0
  188. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/plan_gate.py +0 -0
  189. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/planner.py +0 -0
  190. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/ttl.py +0 -0
  191. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/vm_delete_gate.py +0 -0
  192. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/vm_deploy.py +0 -0
  193. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/vm_gate.py +0 -0
  194. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/ops/vm_lifecycle.py +0 -0
  195. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/scanner/__init__.py +0 -0
  196. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/scanner/alarm_scanner.py +0 -0
  197. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/scanner/log_scanner.py +0 -0
  198. {vmware_aiops-2.3.2 → vmware_aiops-2.4.0}/vmware_aiops/scanner/scheduler.py +0 -0
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "vmware-aiops",
4
4
  "displayName": "VMware AIops",
5
- "version": "2.3.2",
5
+ "version": "2.4.0",
6
6
  "description": "AI-powered VMware vCenter/ESXi VM lifecycle and deployment with 61 MCP tools.",
7
7
  "author": {
8
8
  "name": "Wei Zhou",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-aiops==2.3.2",
8
+ "vmware-aiops==2.4.0",
9
9
  "vmware-aiops-mcp"
10
10
  ]
11
11
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-aiops
3
- Version: 2.3.2
3
+ Version: 2.4.0
4
4
  Summary: VMware vCenter/ESXi AI-powered VM lifecycle and deployment tool
5
5
  Project-URL: Homepage, https://github.com/vmware-skills/VMware-AIops
6
6
  Project-URL: Repository, https://github.com/vmware-skills/VMware-AIops
@@ -1204,6 +1204,7 @@ claude mcp add --transport http vmware-aiops https://mcp.example.com:8443/mcp \
1204
1204
  (default 2) sessions per target; each session is a login on the target, and a call waits at most 300 s for one.
1205
1205
  - **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
1206
1206
  nothing on the platform. Run one endpoint per privilege level.
1207
+ - **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
1207
1208
 
1208
1209
  **Offer only read tools, plus the writes you choose:**
1209
1210
 
@@ -843,6 +843,7 @@ claude mcp add --transport http vmware-aiops https://mcp.example.com:8443/mcp \
843
843
  个会话;每个会话都是在该 target 上的一次登录,一个调用最多等待 300 秒拿到会话。
844
844
  - **能连到这个端点的人,都以该 server 配置的账号操作。** token 只用来识别调用者,不在平台上授予任何权限。
845
845
  每个权限级别单独起一个端点。
846
+ - **一个端点给多人或多种角色用:** 先运行一次 `vmware-audit roles enable`,再给每个 token 指定角色(`vmware-audit token create 名字 --role reader`,或在 `roles.yaml` 里定义可用的工具和 target;需 vmware-policy ≥ 1.24.0)。角色只能限定这个 token 在本端点上能调用什么,不能超出账号本身的权限。详见 vmware-policy README 的「网络调用者的角色」一节。
846
847
 
847
848
  **只提供读工具,加上你选定的写工具:**
848
849
 
@@ -1180,6 +1180,7 @@ claude mcp add --transport http vmware-aiops https://mcp.example.com:8443/mcp \
1180
1180
  (default 2) sessions per target; each session is a login on the target, and a call waits at most 300 s for one.
1181
1181
  - **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
1182
1182
  nothing on the platform. Run one endpoint per privilege level.
1183
+ - **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
1183
1184
 
1184
1185
  **Offer only read tools, plus the writes you choose:**
1185
1186
 
@@ -1,3 +1,12 @@
1
+ ## v2.4.0 — roles for network callers (HLD §18)
2
+
3
+ * Declares the role metadata vmware-policy ≥ 1.24.0 needs to apply **roles** to this server over HTTPS
4
+ (HLD §18): how a missing target resolves, the guest and host-file tools that escape a role, and what each composite tool can run — `apply_vm_plan` and `rollback_vm_plan` reach every tool a plan step maps to plus `delete_vm` (rollback), `set_vm_ttl` reaches `delete_vm`, the batch tools reach the deploy and clone tools and the follow-up steps their arguments ask for (`reconfigure_vm`, `create_vm_snapshot`, `power_on_vm`, and `attach_iso_to_vm` for a deployment spec); a test derives these from what the batch code calls. A role may allow a composite only if it allows all of them. A test fails when a new plan action is not mapped to a tool. With an older vmware-policy nothing changes. Once an operator runs
5
+ `vmware-audit roles enable`, a token's role decides which tools and targets it may use here; a refused call is
6
+ audited as `denied`. See vmware-policy's README, "Roles for network callers".
7
+ * A target-limited role is checked against the same config the connection uses: the copy the server's connection manager loaded on its first connection. Edit `config.yaml` and restart for both to see the change.
8
+ * README and setup guide: one endpoint for several people or roles (run `vmware-audit roles enable` first).
9
+
1
10
  ## v2.3.2 — tools may not touch this server's own control files
2
11
 
3
12
  * **Security fix (HLD §18 step 0).** guest file download/upload, OVA and deployment-spec paths could name this server's own files, so over HTTPS a token holder could
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-aiops==2.3.2",
14
+ "vmware-aiops==2.4.0",
15
15
  "vmware-aiops-mcp"
16
16
  ]
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zw008/vmware-aiops",
3
- "version": "2.3.2",
3
+ "version": "2.4.0",
4
4
  "description": "AI-powered VMware vCenter/ESXi VM lifecycle and deployment with 61 MCP tools.",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-aiops"
7
- version = "2.3.2"
7
+ version = "2.4.0"
8
8
  description = "VMware vCenter/ESXi AI-powered VM lifecycle and deployment tool"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/vmware-skills/VMware-AIops",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.3.2",
10
+ "version": "2.4.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-aiops",
15
- "version": "2.3.2",
15
+ "version": "2.4.0",
16
16
  "runtimeHint": "uvx",
17
17
  "packageArguments": [
18
18
  {
@@ -63,7 +63,7 @@ Read before connecting an agent. Per-tool inventory: `references/capabilities.md
63
63
  ## Quick Install
64
64
 
65
65
  ```bash
66
- uv tool install vmware-aiops==2.3.2
66
+ uv tool install vmware-aiops==2.4.0
67
67
  vmware-aiops doctor
68
68
  vmware-aiops hub status # see which family members are installed
69
69
  ```
@@ -303,7 +303,7 @@ The account has no vSphere role. Assign one (Read-Only or higher) on the vCenter
303
303
  ## Setup
304
304
 
305
305
  ```bash
306
- uv tool install vmware-aiops==2.3.2
306
+ uv tool install vmware-aiops==2.4.0
307
307
  mkdir -p ~/.vmware-aiops
308
308
  vmware-aiops init # generates config.yaml and .env templates
309
309
  chmod 600 ~/.vmware-aiops/.env
@@ -6,13 +6,13 @@ All install methods fetch from the same source: [github.com/vmware-skills/VMware
6
6
 
7
7
  ```bash
8
8
  # Via PyPI (recommended for version pinning)
9
- uv tool install vmware-aiops==2.3.2
9
+ uv tool install vmware-aiops==2.4.0
10
10
 
11
11
  # Via Skills.sh (fetches from GitHub)
12
- npx skills add vmware-skills/VMware-AIops#v2.3.2
12
+ npx skills add vmware-skills/VMware-AIops#v2.4.0
13
13
 
14
14
  # Via ClawHub (fetches from ClawHub registry snapshot of GitHub)
15
- clawhub install @zw008/vmware-aiops --version 2.3.2
15
+ clawhub install @zw008/vmware-aiops --version 2.4.0
16
16
  ```
17
17
 
18
18
  ### Claude Code
@@ -35,7 +35,7 @@ claude mcp add vmware-aiops -- vmware-aiops mcp
35
35
 
36
36
  ```bash
37
37
  # 1. Install from PyPI (source: github.com/vmware-skills/VMware-AIops)
38
- uv tool install vmware-aiops==2.3.2
38
+ uv tool install vmware-aiops==2.4.0
39
39
 
40
40
  # 2. Verify installation source
41
41
  vmware-aiops --version # confirms installed version
@@ -73,7 +73,7 @@ The `vmware-aiops` package installs a Python CLI binary and its dependencies (py
73
73
  ## Development Install
74
74
 
75
75
  ```bash
76
- git clone --branch v2.3.2 https://github.com/vmware-skills/VMware-AIops.git
76
+ git clone --branch v2.4.0 https://github.com/vmware-skills/VMware-AIops.git
77
77
  cd VMware-AIops
78
78
  uv venv && source .venv/bin/activate
79
79
  # --no-sources: pyproject's [tool.uv.sources] points vmware-monitor at a sibling
@@ -245,4 +245,5 @@ vmware-aiops mcp --transport https --host 0.0.0.0 --port 8443 \
245
245
  - **Everyone who can reach the endpoint acts with this server's account.** Tokens identify callers; they grant
246
246
  nothing on the platform. Run one endpoint per privilege level (for example a read-only account behind
247
247
  `--expose read`, and a separate endpoint for operators).
248
+ - **Several people or roles on one endpoint:** run `vmware-audit roles enable` once, then give each token a role (`vmware-audit token create NAME --role reader`, or a role in `roles.yaml` naming tools and targets; vmware-policy ≥ 1.24.0). A role limits what the token may call on this endpoint; it cannot grant more than the account has. See vmware-policy's README, "Roles for network callers".
248
249
 
@@ -0,0 +1,117 @@
1
+ """HLD §18 D-22: the role metadata this skill declares matches its real code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+
7
+ import pytest
8
+
9
+ from vmware_aiops import policy_roles
10
+ from vmware_aiops.ops.plan_executor import dispatch_actions
11
+ from vmware_aiops.ops.plan_gate import REFUSED_ACTIONS
12
+
13
+
14
+ def _registry() -> set[str]:
15
+ from vmware_aiops.mcp_server.server import mcp
16
+
17
+ return {t.name for t in asyncio.run(mcp.list_tools())}
18
+
19
+
20
+ @pytest.mark.unit
21
+ def test_every_plan_action_is_mapped_or_refused():
22
+ """A new plan action must name the tool it amounts to, or a role could reach it unlisted."""
23
+ missing = dispatch_actions() - set(policy_roles.ACTION_TOOLS) - set(REFUSED_ACTIONS)
24
+ assert not missing, f"map these plan actions in policy_roles.ACTION_TOOLS: {sorted(missing)}"
25
+
26
+
27
+ @pytest.mark.unit
28
+ def test_every_declared_name_is_a_real_tool():
29
+ tools = _registry()
30
+ named = set(policy_roles.ESCAPES) | set(policy_roles.REACHES)
31
+ for reached in policy_roles.REACHES.values():
32
+ named |= reached
33
+ named |= set(policy_roles.ACTION_TOOLS.values())
34
+ assert named - tools == set(), sorted(named - tools)
35
+
36
+
37
+ @pytest.mark.unit
38
+ def test_plans_reach_delete_through_rollback():
39
+ assert "delete_vm" in policy_roles.REACHES["apply_vm_plan"]
40
+ assert "delete_vm" in policy_roles.REACHES["set_vm_ttl"]
41
+
42
+
43
+ @pytest.mark.unit
44
+ def test_metadata_is_declared_when_policy_has_roles():
45
+ roles = pytest.importorskip("vmware_policy.roles")
46
+ meta = roles.metadata("aiops")
47
+ assert meta is not None and meta.version == 1
48
+ assert meta.reaches["apply_vm_plan"] == policy_roles.REACHES["apply_vm_plan"]
49
+ assert "run_vm_guest_command" in meta.escapes
50
+
51
+
52
+ #: ops function a batch calls → the tool that does the same thing alone.
53
+ _OPS_TOOL = {
54
+ "clone_vm": "clone_vm",
55
+ "reconfigure_vm": "reconfigure_vm",
56
+ "create_snapshot": "create_vm_snapshot",
57
+ "power_on_vm": "power_on_vm",
58
+ "attach_iso": "attach_iso_to_vm",
59
+ "linked_clone": "deploy_linked_clone",
60
+ "deploy_from_template": "deploy_vm_from_template",
61
+ "deploy_ova": "deploy_vm_from_ova",
62
+ "create_vm": "create_vm",
63
+ }
64
+
65
+ #: batch tool → its ops function in vmware_aiops/ops/vm_deploy.py
66
+ _BATCH_OPS = {
67
+ "batch_clone_vms": "batch_clone",
68
+ "batch_linked_clone_vms": "batch_linked_clone",
69
+ "batch_deploy_from_spec": "batch_deploy",
70
+ }
71
+
72
+
73
+ def _calls_in(func_name: str) -> set[str]:
74
+ import ast
75
+ import inspect
76
+
77
+ from vmware_aiops.ops import vm_deploy
78
+
79
+ tree = ast.parse(inspect.getsource(getattr(vm_deploy, func_name)))
80
+ return {
81
+ n.func.id if isinstance(n.func, ast.Name) else n.func.attr
82
+ for n in ast.walk(tree)
83
+ if isinstance(n, ast.Call) and isinstance(n.func, (ast.Name, ast.Attribute))
84
+ }
85
+
86
+
87
+ @pytest.mark.unit
88
+ @pytest.mark.parametrize("tool", sorted(_BATCH_OPS))
89
+ def test_batch_reach_covers_every_step_it_runs(tool):
90
+ """Review M1: a batch that also reconfigures/snapshots/powers on reaches those tools."""
91
+ called = _calls_in(_BATCH_OPS[tool])
92
+ reached = {_OPS_TOOL[c] for c in called if c in _OPS_TOOL}
93
+ assert reached, f"{_BATCH_OPS[tool]} calls none of the known ops; update _OPS_TOOL"
94
+ assert reached <= policy_roles.REACHES[tool], sorted(reached - policy_roles.REACHES[tool])
95
+
96
+
97
+ @pytest.mark.unit
98
+ def test_the_resolver_uses_the_config_the_connection_manager_holds(monkeypatch):
99
+ """Review H1/H-1: once the server's connection manager exists, a config edit must
100
+ not move the role check off the config it connects with; before, read the file."""
101
+ import importlib
102
+ import types
103
+
104
+ from vmware_aiops import policy_roles as pr
105
+
106
+ server = importlib.import_module("vmware_aiops.mcp_server._shared")
107
+ assert hasattr(server, "_conn_mgr"), "policy_roles looks up a global the server does not have"
108
+ edited, loads = object(), []
109
+ monkeypatch.setattr(pr, "load_config", lambda: loads.append(1) or edited)
110
+
111
+ monkeypatch.setattr(server, "_conn_mgr", None)
112
+ assert pr._connection_config() is edited and pr._connection_config() is edited
113
+ assert len(loads) == 2 # no manager yet: the file as it is now, every time
114
+
115
+ held = object()
116
+ monkeypatch.setattr(server, "_conn_mgr", types.SimpleNamespace(_config=held))
117
+ assert pr._connection_config() is held # the manager's copy, not the edited file
@@ -1195,7 +1195,7 @@ wheels = [
1195
1195
 
1196
1196
  [[package]]
1197
1197
  name = "vmware-aiops"
1198
- version = "2.3.2"
1198
+ version = "2.4.0"
1199
1199
  source = { editable = "." }
1200
1200
  dependencies = [
1201
1201
  { name = "apscheduler" },
@@ -1240,7 +1240,7 @@ dev = [
1240
1240
 
1241
1241
  [[package]]
1242
1242
  name = "vmware-monitor"
1243
- version = "2.3.1"
1243
+ version = "2.4.0"
1244
1244
  source = { editable = "../VMware-Monitor" }
1245
1245
  dependencies = [
1246
1246
  { name = "apscheduler" },
@@ -1276,14 +1276,14 @@ dev = [
1276
1276
 
1277
1277
  [[package]]
1278
1278
  name = "vmware-policy"
1279
- version = "1.23.1"
1279
+ version = "1.24.0"
1280
1280
  source = { registry = "https://pypi.org/simple" }
1281
1281
  dependencies = [
1282
1282
  { name = "pyyaml" },
1283
1283
  { name = "rich" },
1284
1284
  { name = "typer" },
1285
1285
  ]
1286
- sdist = { url = "https://files.pythonhosted.org/packages/ec/2c/03d2327478838ed3d407c7fc30ebbd6a2cf3b3d2c58285ba6653697224b0/vmware_policy-1.23.1.tar.gz", hash = "sha256:6fe55a99fc71f9591bedd3c5dcc00042559953f02a7bf7da9d2b9b4638877c08", size = 332522, upload-time = "2026-10-16T02:55:48.081Z" }
1286
+ sdist = { url = "https://files.pythonhosted.org/packages/4a/62/3a3cb83af77e3dd3c8fc92cb3b7968bfda04a2f71bf0d6f9b72ee3198441/vmware_policy-1.24.0.tar.gz", hash = "sha256:fba12732ccbd4371232c15d9fd014f9f5d697497210aedff2515d1d205ed3577", size = 361302, upload-time = "2026-10-16T07:18:28.317Z" }
1287
1287
  wheels = [
1288
- { url = "https://files.pythonhosted.org/packages/b7/22/94fc30b3e273fd0e9739fb11ac0bfa8232352cefcaf95ee25229bb277733/vmware_policy-1.23.1-py3-none-any.whl", hash = "sha256:4afe95894e8a05556dfeb9e2c6a37186cf8c4bf3017131dedfb8c23478514cec", size = 114484, upload-time = "2026-10-16T02:55:46.591Z" },
1288
+ { url = "https://files.pythonhosted.org/packages/3d/f2/fe3d9d384712023916e2cacddc385f14b095509c5e00aea417d1a9550e98/vmware_policy-1.24.0-py3-none-any.whl", hash = "sha256:cf615d0256134f01fb84c39b083649281a66d2c064c2eaa28786a0fa95ec6e58", size = 131460, upload-time = "2026-10-16T07:18:26.907Z" },
1289
1289
  ]
@@ -1,3 +1,3 @@
1
1
  """VMware AIops - AI-powered vCenter/ESXi monitoring and operations."""
2
2
 
3
- __version__ = "2.3.2"
3
+ __version__ = "2.4.0"
@@ -38,3 +38,6 @@ def _environment_for(target: str | None) -> str:
38
38
  # to ALLOW. Keyed, a resolver only ever answers for its own skill, so
39
39
  # registering at import time is safe again.
40
40
  set_environment_resolver(_environment_for, skill=skill_name(__name__))
41
+
42
+ # Role metadata (HLD §18) is declared beside the environment resolver, on every surface.
43
+ import vmware_aiops.policy_roles # noqa: E402,F401
@@ -0,0 +1,123 @@
1
+ """What vmware-policy needs to apply roles to this skill (HLD §18 D-21/D-22).
2
+
3
+ Policy cannot see which tools run other tools, which tools escape a role, or how a
4
+ missing ``target`` resolves; this module declares them. Imported by
5
+ ``policy_environment``, so the MCP server and the CLI both register it. With a
6
+ vmware-policy that has no roles module (< 1.24) nothing is declared.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+
13
+ from vmware_policy import skill_name
14
+
15
+ from vmware_aiops.config import load_config
16
+
17
+ #: Each plan action → the tool that does the same thing. ``reset``/``suspend`` have no
18
+ #: tool of their own; they change power state like ``power_off_vm``. The storage
19
+ #: actions are refused by ``plan_gate.REFUSED_ACTIONS`` and are not listed.
20
+ #: ``tests/test_policy_roles.py`` fails if a dispatch action is missing here.
21
+ ACTION_TOOLS: dict[str, str] = {
22
+ "attach_iso": "attach_iso_to_vm",
23
+ "clone": "clone_vm",
24
+ "cluster_add_host": "add_cluster_host",
25
+ "cluster_remove_host": "remove_cluster_host",
26
+ "configure_cluster": "configure_cluster",
27
+ "convert_to_template": "convert_vm_to_template",
28
+ "create_cluster": "create_cluster",
29
+ "create_snapshot": "create_vm_snapshot",
30
+ "create_vm": "create_vm",
31
+ "delete_cluster": "delete_cluster",
32
+ "delete_snapshot": "delete_vm_snapshot",
33
+ "delete_vm": "delete_vm",
34
+ "deploy_ova": "deploy_vm_from_ova",
35
+ "deploy_template": "deploy_vm_from_template",
36
+ "guest_download": "download_vm_guest_file",
37
+ "guest_exec": "run_vm_guest_command",
38
+ "guest_upload": "upload_vm_guest_file",
39
+ "linked_clone": "deploy_linked_clone",
40
+ "migrate": "migrate_vm",
41
+ "power_off": "power_off_vm",
42
+ "power_on": "power_on_vm",
43
+ "reconfigure": "reconfigure_vm",
44
+ "reset": "power_off_vm",
45
+ "revert_snapshot": "revert_vm_snapshot",
46
+ "suspend": "power_off_vm",
47
+ }
48
+
49
+ #: A plan's rollback deletes VMs the plan created, so applying or rolling back a plan
50
+ #: reaches ``delete_vm`` even when no step names it.
51
+ _PLAN_REACHES = frozenset(ACTION_TOOLS.values()) | {"delete_vm"}
52
+
53
+ REACHES: dict[str, frozenset[str]] = {
54
+ "apply_vm_plan": _PLAN_REACHES,
55
+ "rollback_vm_plan": _PLAN_REACHES,
56
+ "set_vm_ttl": frozenset({"delete_vm"}), # the scheduler deletes later, with no caller
57
+ # The batch tools run the follow-up steps their arguments ask for (review M1).
58
+ "batch_clone_vms": frozenset(
59
+ {"clone_vm", "reconfigure_vm", "create_vm_snapshot", "power_on_vm"}
60
+ ),
61
+ "batch_linked_clone_vms": frozenset({"deploy_linked_clone"}),
62
+ "batch_deploy_from_spec": frozenset(
63
+ {
64
+ "deploy_vm_from_template",
65
+ "clone_vm",
66
+ "deploy_vm_from_ova",
67
+ "deploy_linked_clone",
68
+ "create_vm",
69
+ "reconfigure_vm",
70
+ "attach_iso_to_vm",
71
+ "create_vm_snapshot",
72
+ "power_on_vm",
73
+ }
74
+ ),
75
+ "restore_vm_clean_slate": frozenset({"power_off_vm", "revert_vm_snapshot"}),
76
+ "provision_vm_guest": frozenset({"run_vm_guest_command", "upload_vm_guest_file"}),
77
+ }
78
+
79
+ #: Tools that give their holder more than a role: code in a guest, files on the
80
+ #: server host. (Host-path tools are added by policy while a file root is set.)
81
+ ESCAPES = frozenset(
82
+ {
83
+ "run_vm_guest_command",
84
+ "run_vm_guest_command_with_output",
85
+ "upload_vm_guest_file",
86
+ "download_vm_guest_file",
87
+ "provision_vm_guest",
88
+ }
89
+ )
90
+
91
+
92
+ def _connection_config():
93
+ """The config the connection layer uses for this call (review H1, then H-1).
94
+
95
+ The server's connection manager loads the config once, on its first connection,
96
+ and keeps it. Once it exists, the role is checked against that same object, so
97
+ an edit to config.yaml cannot move the check off the target the call connects
98
+ to. Before it exists, the file as it is now is what the manager is about to load.
99
+ Looked up in ``sys.modules`` so the CLI never imports the MCP server.
100
+ """
101
+ server = sys.modules.get("vmware_aiops.mcp_server._shared")
102
+ manager = getattr(server, "_conn_mgr", None) if server is not None else None
103
+ return manager._config if manager is not None else load_config()
104
+
105
+
106
+ def _target_name(target: str | None) -> str:
107
+ """The target the connection layer would use for ``target`` (or the default)."""
108
+ config = _connection_config()
109
+ return (config.get_target(target) if target else config.default_target).name
110
+
111
+
112
+ try:
113
+ from vmware_policy.roles import declare_role_metadata
114
+ except ImportError: # vmware-policy without roles: nothing to declare
115
+ declare_role_metadata = None
116
+
117
+ if declare_role_metadata is not None:
118
+ declare_role_metadata(
119
+ skill_name(__name__),
120
+ escapes=ESCAPES,
121
+ reaches=REACHES,
122
+ resolver=_target_name,
123
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes