plugin-scanner 2.0.73__tar.gz → 2.0.75__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 (331) hide show
  1. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/PKG-INFO +2 -2
  2. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docker-requirements.txt +50 -50
  3. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/pyproject.toml +2 -2
  4. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/pyproject.toml.bak +2 -2
  5. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/commands.py +77 -4
  6. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/render.py +9 -0
  7. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/runtime/surface_server.py +7 -2
  8. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/version.py +1 -1
  9. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_cli.py +94 -0
  10. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_surface_server.py +43 -0
  11. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/uv.lock +52 -52
  12. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.clusterfuzzlite/Dockerfile +0 -0
  13. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.clusterfuzzlite/build.sh +0 -0
  14. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.clusterfuzzlite/project.yaml +0 -0
  15. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.clusterfuzzlite/requirements-atheris.txt +0 -0
  16. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.dockerignore +0 -0
  17. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/CODEOWNERS +0 -0
  18. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  19. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  20. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  21. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/dependabot.yml +0 -0
  22. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/ci.yml +0 -0
  23. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/codeql.yml +0 -0
  24. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/dependabot-uv-lock.yml +0 -0
  25. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/fuzz.yml +0 -0
  26. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/harness-smoke.yml +0 -0
  27. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/publish.yml +0 -0
  28. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.github/workflows/scorecard.yml +0 -0
  29. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.gitignore +0 -0
  30. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/.pre-commit-hooks.yaml +0 -0
  31. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/CONTRIBUTING.md +0 -0
  32. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/Dockerfile +0 -0
  33. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/LICENSE +0 -0
  34. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/README.md +0 -0
  35. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/SECURITY.md +0 -0
  36. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/index.html +0 -0
  37. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/package.json +0 -0
  38. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/pnpm-lock.yaml +0 -0
  39. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/apple-touch-icon.png +0 -0
  40. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/brand/Logo_Icon_Dark.png +0 -0
  41. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/brand/Logo_Whole.png +0 -0
  42. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/favicon-16x16.png +0 -0
  43. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/favicon-32x32.png +0 -0
  44. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/public/favicon.ico +0 -0
  45. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/app.tsx +0 -0
  46. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/approval-center-layout.tsx +0 -0
  47. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/approval-center-primitives.tsx +0 -0
  48. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/approval-center-utils.ts +0 -0
  49. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/fleet-workspace.tsx +0 -0
  50. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/guard-api.ts +0 -0
  51. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/guard-demo.ts +0 -0
  52. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/guard-types.ts +0 -0
  53. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/main.tsx +0 -0
  54. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/receipts-workspace.tsx +0 -0
  55. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/runtime-overview.tsx +0 -0
  56. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/settings-workspace.tsx +0 -0
  57. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/styles.css +0 -0
  58. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/src/vite-env.d.ts +0 -0
  59. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/tsconfig.json +0 -0
  60. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/dashboard/vite.config.ts +0 -0
  61. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/approval-audit.md +0 -0
  62. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/architecture.md +0 -0
  63. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/get-started.md +0 -0
  64. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/harness-support.md +0 -0
  65. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/local-vs-cloud.md +0 -0
  66. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/guard/testing-matrix.md +0 -0
  67. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/trust/mcp-trust-draft.md +0 -0
  68. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/trust/plugin-trust-draft.md +0 -0
  69. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/docs/trust/skill-trust-local.md +0 -0
  70. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/fuzzers/manifest_fuzzer.py +0 -0
  71. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/requirements.txt +0 -0
  72. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/schemas/plugin-quality.v1.json +0 -0
  73. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/schemas/scan-result.v1.json +0 -0
  74. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/schemas/verify-result.v1.json +0 -0
  75. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/__init__.py +0 -0
  76. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/action_runner.py +0 -0
  77. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/argparse_utils.py +0 -0
  78. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/__init__.py +0 -0
  79. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/best_practices.py +0 -0
  80. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/claude.py +0 -0
  81. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/code_quality.py +0 -0
  82. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/ecosystem_common.py +0 -0
  83. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/gemini.py +0 -0
  84. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/manifest.py +0 -0
  85. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/manifest_support.py +0 -0
  86. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/marketplace.py +0 -0
  87. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/mcp_security.py +0 -0
  88. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/opencode.py +0 -0
  89. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/operational_security.py +0 -0
  90. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/security.py +0 -0
  91. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/checks/skill_security.py +0 -0
  92. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/cli.py +0 -0
  93. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/cli_ui.py +0 -0
  94. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/config.py +0 -0
  95. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/__init__.py +0 -0
  96. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/base.py +0 -0
  97. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/claude.py +0 -0
  98. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/codex.py +0 -0
  99. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/detect.py +0 -0
  100. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/gemini.py +0 -0
  101. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/opencode.py +0 -0
  102. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/registry.py +0 -0
  103. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/ecosystems/types.py +0 -0
  104. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/github_reporting.py +0 -0
  105. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/__init__.py +0 -0
  106. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/__init__.py +0 -0
  107. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/antigravity.py +0 -0
  108. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/base.py +0 -0
  109. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/claude_code.py +0 -0
  110. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/codex.py +0 -0
  111. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/copilot.py +0 -0
  112. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/cursor.py +0 -0
  113. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/gemini.py +0 -0
  114. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/hermes.py +0 -0
  115. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/mcp_servers.py +0 -0
  116. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/opencode.py +0 -0
  117. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/adapters/opencode_artifacts.py +0 -0
  118. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/approvals.py +0 -0
  119. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/bridge/__init__.py +0 -0
  120. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/capabilities.py +0 -0
  121. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/__init__.py +0 -0
  122. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/approval_commands.py +0 -0
  123. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/bootstrap.py +0 -0
  124. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/connect_flow.py +0 -0
  125. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/install_commands.py +0 -0
  126. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/product.py +0 -0
  127. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/prompt.py +0 -0
  128. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/cli/update_commands.py +0 -0
  129. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/codex_config.py +0 -0
  130. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/config.py +0 -0
  131. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/consumer/__init__.py +0 -0
  132. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/consumer/service.py +0 -0
  133. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/__init__.py +0 -0
  134. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/client.py +0 -0
  135. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/manager.py +0 -0
  136. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/server.py +0 -0
  137. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/apple-touch-icon.png +0 -0
  138. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/assets/guard-dashboard.js +0 -0
  139. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/assets/index.css +0 -0
  140. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Icon_Dark.png +0 -0
  141. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/brand/Logo_Whole.png +0 -0
  142. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/favicon-16x16.png +0 -0
  143. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/favicon-32x32.png +0 -0
  144. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/favicon.ico +0 -0
  145. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/daemon/static/index.html +0 -0
  146. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/edge_events.py +0 -0
  147. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/incident.py +0 -0
  148. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/launcher.py +0 -0
  149. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/mcp_tool_calls.py +0 -0
  150. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/models.py +0 -0
  151. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/policy/__init__.py +0 -0
  152. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/policy/engine.py +0 -0
  153. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/protect.py +0 -0
  154. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/proxy/__init__.py +0 -0
  155. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/proxy/remote.py +0 -0
  156. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/proxy/runtime_mcp.py +0 -0
  157. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/proxy/stdio.py +0 -0
  158. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/receipts/__init__.py +0 -0
  159. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/receipts/manager.py +0 -0
  160. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/risk.py +0 -0
  161. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/runtime/__init__.py +0 -0
  162. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/runtime/runner.py +0 -0
  163. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/runtime/secret_file_requests.py +0 -0
  164. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/schemas/__init__.py +0 -0
  165. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/schemas/consumer_mode.py +0 -0
  166. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/schemas/guard_event_v1.py +0 -0
  167. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/schemas/surface_server.py +0 -0
  168. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/shims.py +0 -0
  169. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/store.py +0 -0
  170. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/store_approvals.py +0 -0
  171. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/store_connect.py +0 -0
  172. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/guard/types.py +0 -0
  173. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/integrations/__init__.py +0 -0
  174. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/integrations/cisco_mcp_scanner.py +0 -0
  175. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/integrations/cisco_skill_scanner.py +0 -0
  176. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/lint_fixes.py +0 -0
  177. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/marketplace_support.py +0 -0
  178. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/models.py +0 -0
  179. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/path_support.py +0 -0
  180. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/policy.py +0 -0
  181. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/quality_artifact.py +0 -0
  182. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/repo_detect.py +0 -0
  183. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/reporting.py +0 -0
  184. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/rules/__init__.py +0 -0
  185. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/rules/registry.py +0 -0
  186. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/rules/specs.py +0 -0
  187. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/scanner.py +0 -0
  188. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/submission.py +0 -0
  189. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/suppressions.py +0 -0
  190. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_domain_scoring.py +0 -0
  191. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_helpers.py +0 -0
  192. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_mcp_scoring.py +0 -0
  193. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_models.py +0 -0
  194. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_plugin_scoring.py +0 -0
  195. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_scoring.py +0 -0
  196. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_skill_scoring.py +0 -0
  197. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/trust_specs.py +0 -0
  198. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/src/codex_plugin_scanner/verification.py +0 -0
  199. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/__init__.py +0 -0
  200. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/conftest.py +0 -0
  201. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/__init__.py +0 -0
  202. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/bad-plugin/.codex-plugin/plugin.json +0 -0
  203. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/bad-plugin/.mcp.json +0 -0
  204. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/bad-plugin/secrets.js +0 -0
  205. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/.claude-plugin/plugin.json +0 -0
  206. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/LICENSE +0 -0
  207. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/README.md +0 -0
  208. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/SECURITY.md +0 -0
  209. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/hooks/hooks.json +0 -0
  210. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/claude-plugin-good/skills/example/SKILL.md +0 -0
  211. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/code-quality-bad/evil.js +0 -0
  212. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/code-quality-bad/inject.js +0 -0
  213. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/GEMINI.md +0 -0
  214. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/LICENSE +0 -0
  215. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/README.md +0 -0
  216. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/SECURITY.md +0 -0
  217. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/commands/hello.toml +0 -0
  218. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/gemini-extension-good/gemini-extension.json +0 -0
  219. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/.codex-plugin/plugin.json +0 -0
  220. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/.codexignore +0 -0
  221. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/LICENSE +0 -0
  222. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/README.md +0 -0
  223. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/SECURITY.md +0 -0
  224. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/assets/icon.svg +0 -0
  225. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/assets/logo.svg +0 -0
  226. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/assets/screenshot.svg +0 -0
  227. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/good-plugin/skills/example/SKILL.md +0 -0
  228. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/guard-codex-malicious-mcp/.codex/config.toml +0 -0
  229. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/config.yaml +0 -0
  230. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/mcp_servers.json +0 -0
  231. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/skills/security/malicious/SKILL.md +0 -0
  232. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/SKILL.md +0 -0
  233. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/references/api-setup.md +0 -0
  234. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/skills/stealth/sneaky/scripts/deploy.sh +0 -0
  235. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/hermes-plugin-evil/skills/utils/benign/SKILL.md +0 -0
  236. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malformed-json/.codex-plugin/plugin.json +0 -0
  237. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/.codex-plugin/plugin.json +0 -0
  238. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/.codexignore +0 -0
  239. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/LICENSE +0 -0
  240. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/README.md +0 -0
  241. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/SECURITY.md +0 -0
  242. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/malicious-skill-plugin/skills/leaky-skill/SKILL.md +0 -0
  243. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/mcp-canary-server.py +0 -0
  244. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/minimal-plugin/.codex-plugin/plugin.json +0 -0
  245. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/missing-fields/.codex-plugin/plugin.json +0 -0
  246. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/mit-license/LICENSE +0 -0
  247. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/codex-plugin/.codex-plugin/plugin.json +0 -0
  248. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/codex-plugin/LICENSE +0 -0
  249. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/codex-plugin/README.md +0 -0
  250. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/codex-plugin/SECURITY.md +0 -0
  251. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/gemini-ext/README.md +0 -0
  252. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-ecosystem-repo/gemini-ext/gemini-extension.json +0 -0
  253. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/.agents/plugins/marketplace.json +0 -0
  254. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codex-plugin/plugin.json +0 -0
  255. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/.codexignore +0 -0
  256. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/LICENSE +0 -0
  257. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/README.md +0 -0
  258. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/SECURITY.md +0 -0
  259. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/alpha-plugin/skills/example/SKILL.md +0 -0
  260. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/.codex-plugin/plugin.json +0 -0
  261. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/multi-plugin-repo/plugins/beta-plugin/skills/example/SKILL.md +0 -0
  262. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/no-version/.codex-plugin/plugin.json +0 -0
  263. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/.opencode/commands/hello.md +0 -0
  264. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/.opencode/plugins/example.ts +0 -0
  265. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/LICENSE +0 -0
  266. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/README.md +0 -0
  267. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/SECURITY.md +0 -0
  268. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/opencode-good/opencode.jsonc +0 -0
  269. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/skills-missing-dir/.codex-plugin/plugin.json +0 -0
  270. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/skills-no-frontmatter/.codex-plugin/plugin.json +0 -0
  271. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/skills-no-frontmatter/skills/bad-skill/SKILL.md +0 -0
  272. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/with-marketplace/.codex-plugin/plugin.json +0 -0
  273. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/with-marketplace/marketplace-broken.json +0 -0
  274. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/fixtures/with-marketplace/marketplace.json +0 -0
  275. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test-trust-scoring.py +0 -0
  276. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test-trust-specs.py +0 -0
  277. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_action_runner.py +0 -0
  278. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_best_practices.py +0 -0
  279. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_cisco_install_surfaces.py +0 -0
  280. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_cli.py +0 -0
  281. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_code_quality.py +0 -0
  282. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_config.py +0 -0
  283. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_coverage_remaining.py +0 -0
  284. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_ecosystems.py +0 -0
  285. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_edge_cases.py +0 -0
  286. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_final_coverage.py +0 -0
  287. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_approvals.py +0 -0
  288. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_bootstrap.py +0 -0
  289. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_capabilities.py +0 -0
  290. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_claude_adapter.py +0 -0
  291. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_codex_e2e.py +0 -0
  292. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_codex_install.py +0 -0
  293. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_codex_proxy.py +0 -0
  294. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_config_paths.py +0 -0
  295. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_connect_flow.py +0 -0
  296. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_consumer_mode.py +0 -0
  297. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_copilot_adapter.py +0 -0
  298. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_copilot_proxy.py +0 -0
  299. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_daemon_manager.py +0 -0
  300. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_event_schema_v1.py +0 -0
  301. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_events.py +0 -0
  302. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_launch_env.py +0 -0
  303. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_opencode_proxy.py +0 -0
  304. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_product_flow.py +0 -0
  305. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_protect.py +0 -0
  306. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_render.py +0 -0
  307. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_risk.py +0 -0
  308. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_runtime.py +0 -0
  309. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_store_migrations.py +0 -0
  310. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_guard_verdicts.py +0 -0
  311. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_hermes_adapter.py +0 -0
  312. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_integration.py +0 -0
  313. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_lint_fixes.py +0 -0
  314. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_live_cisco_smoke.py +0 -0
  315. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_manifest.py +0 -0
  316. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_marketplace.py +0 -0
  317. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_mcp_security.py +0 -0
  318. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_operational_security.py +0 -0
  319. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_policy.py +0 -0
  320. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_quality_artifact.py +0 -0
  321. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_rule_registry.py +0 -0
  322. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_scanner.py +0 -0
  323. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_schema_contracts.py +0 -0
  324. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_security.py +0 -0
  325. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_security_ops.py +0 -0
  326. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_skill_security.py +0 -0
  327. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_submission.py +0 -0
  328. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_trust_scoring.py +0 -0
  329. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_trust_specs.py +0 -0
  330. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_verification.py +0 -0
  331. {plugin_scanner-2.0.73 → plugin_scanner-2.0.75}/tests/test_versioning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plugin-scanner
3
- Version: 2.0.73
3
+ Version: 2.0.75
4
4
  Summary: Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows.
5
5
  Project-URL: Homepage, https://github.com/hashgraph-online/ai-plugin-scanner
6
6
  Project-URL: Repository, https://github.com/hashgraph-online/ai-plugin-scanner
@@ -22,7 +22,7 @@ Classifier: Topic :: Security
22
22
  Classifier: Topic :: Software Development :: Quality Assurance
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: cisco-ai-skill-scanner~=2.0.9
25
- Requires-Dist: cryptography>=46.0.0
25
+ Requires-Dist: cryptography>=47.0.0
26
26
  Requires-Dist: rich>=13.0
27
27
  Requires-Dist: tomli>=2.0; python_version < '3.11'
28
28
  Provides-Extra: cisco
@@ -366,56 +366,56 @@ confusable-homoglyphs==3.3.1 \
366
366
  --hash=sha256:84c92cb79dc7f55aa290d0762b2349abd8dee4c16fbe6f99eac978d394e2e6a1 \
367
367
  --hash=sha256:b995001c9b2e1b4cea0cf5f3840a7c79188a8cbbad053d693572bd8c1c1ec460
368
368
  # via cisco-ai-skill-scanner
369
- cryptography==46.0.6 \
370
- --hash=sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70 \
371
- --hash=sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d \
372
- --hash=sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a \
373
- --hash=sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0 \
374
- --hash=sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97 \
375
- --hash=sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30 \
376
- --hash=sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759 \
377
- --hash=sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c \
378
- --hash=sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead \
379
- --hash=sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275 \
380
- --hash=sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58 \
381
- --hash=sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f \
382
- --hash=sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361 \
383
- --hash=sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507 \
384
- --hash=sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa \
385
- --hash=sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b \
386
- --hash=sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b \
387
- --hash=sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8 \
388
- --hash=sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8 \
389
- --hash=sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72 \
390
- --hash=sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175 \
391
- --hash=sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e \
392
- --hash=sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124 \
393
- --hash=sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a \
394
- --hash=sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c \
395
- --hash=sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f \
396
- --hash=sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d \
397
- --hash=sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4 \
398
- --hash=sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c \
399
- --hash=sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290 \
400
- --hash=sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca \
401
- --hash=sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d \
402
- --hash=sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a \
403
- --hash=sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed \
404
- --hash=sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a \
405
- --hash=sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb \
406
- --hash=sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8 \
407
- --hash=sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707 \
408
- --hash=sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410 \
409
- --hash=sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736 \
410
- --hash=sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2 \
411
- --hash=sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4 \
412
- --hash=sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013 \
413
- --hash=sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19 \
414
- --hash=sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b \
415
- --hash=sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738 \
416
- --hash=sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463 \
417
- --hash=sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77 \
418
- --hash=sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4
369
+ cryptography==47.0.0 \
370
+ --hash=sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7 \
371
+ --hash=sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27 \
372
+ --hash=sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd \
373
+ --hash=sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7 \
374
+ --hash=sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001 \
375
+ --hash=sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4 \
376
+ --hash=sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca \
377
+ --hash=sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0 \
378
+ --hash=sha256:1a405c08857258c11016777e11c02bacbe7ef596faf259305d282272a3a05cbe \
379
+ --hash=sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93 \
380
+ --hash=sha256:20fdbe3e38fb67c385d233c89371fa27f9909f6ebca1cecc20c13518dae65475 \
381
+ --hash=sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe \
382
+ --hash=sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515 \
383
+ --hash=sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10 \
384
+ --hash=sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7 \
385
+ --hash=sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92 \
386
+ --hash=sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829 \
387
+ --hash=sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8 \
388
+ --hash=sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52 \
389
+ --hash=sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b \
390
+ --hash=sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc \
391
+ --hash=sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c \
392
+ --hash=sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63 \
393
+ --hash=sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac \
394
+ --hash=sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31 \
395
+ --hash=sha256:7f1207974a904e005f762869996cf620e9bf79ecb4622f148550bb48e0eb35a7 \
396
+ --hash=sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1 \
397
+ --hash=sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203 \
398
+ --hash=sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7 \
399
+ --hash=sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769 \
400
+ --hash=sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923 \
401
+ --hash=sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74 \
402
+ --hash=sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b \
403
+ --hash=sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb \
404
+ --hash=sha256:9fe6b7c64926c765f9dff301f9c1b867febcda5768868ca084e18589113732ab \
405
+ --hash=sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76 \
406
+ --hash=sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f \
407
+ --hash=sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7 \
408
+ --hash=sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973 \
409
+ --hash=sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0 \
410
+ --hash=sha256:cffbba3392df0fa8629bb7f43454ee2925059ee158e23c54620b9063912b86c8 \
411
+ --hash=sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310 \
412
+ --hash=sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b \
413
+ --hash=sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318 \
414
+ --hash=sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab \
415
+ --hash=sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8 \
416
+ --hash=sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa \
417
+ --hash=sha256:f7db373287273d8af1414cf95dc4118b13ffdc62be521997b0f2b270771fef50 \
418
+ --hash=sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736
419
419
  # via
420
420
  # hol-guard
421
421
  # msoffcrypto-tool
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plugin-scanner"
7
- version = "2.0.73"
7
+ version = "2.0.75"
8
8
  description = "Lint, verify, and gate plugin ecosystems for maintainers, CI, and publish workflows."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -28,7 +28,7 @@ classifiers = [
28
28
  ]
29
29
  dependencies = [
30
30
  "rich>=13.0",
31
- "cryptography>=46.0.0",
31
+ "cryptography>=47.0.0",
32
32
  "tomli>=2.0; python_version < '3.11'",
33
33
  "cisco-ai-skill-scanner~=2.0.9",
34
34
  ]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hol-guard"
7
- version = "2.0.73"
7
+ version = "2.0.75"
8
8
  description = "Protect local AI harnesses with HOL Guard and run scanner checks for Codex, Claude, Cursor, Gemini, and OpenCode."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -28,7 +28,7 @@ classifiers = [
28
28
  ]
29
29
  dependencies = [
30
30
  "rich>=13.0",
31
- "cryptography>=46.0.0",
31
+ "cryptography>=47.0.0",
32
32
  "tomli>=2.0; python_version < '3.11'",
33
33
  "cisco-ai-skill-scanner~=2.0.9",
34
34
  ]
@@ -112,6 +112,7 @@ _GUARD_HELP_GROUPS = (
112
112
  "Everyday protection:\n"
113
113
  " start First-run setup and the Guard operating loop\n"
114
114
  " status Current local protection state and next actions\n"
115
+ " dashboard Open the local Guard dashboard in your browser\n"
115
116
  " run Enforce Guard before a harness launch\n"
116
117
  " approvals Resolve the current request queue\n"
117
118
  " receipts Review recent local decisions\n"
@@ -204,7 +205,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
204
205
  required=True,
205
206
  parser_class=FriendlyArgumentParser,
206
207
  metavar=(
207
- "{start,status,bootstrap,detect,install,update,uninstall,run,protect,preflight,scan,diff,receipts,inventory,abom,"
208
+ "{start,status,dashboard,bootstrap,detect,install,update,uninstall,run,protect,preflight,scan,diff,receipts,inventory,abom,"
208
209
  "approvals,explain,allow,deny,policies,exceptions,advisories,events,doctor,connect,login,sync,device,bridge}"
209
210
  ),
210
211
  )
@@ -217,6 +218,17 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
217
218
  _add_guard_common_args(status_parser)
218
219
  status_parser.add_argument("--json", action="store_true")
219
220
 
221
+ dashboard_parser = guard_subparsers.add_parser(
222
+ "dashboard",
223
+ help="Open the local Guard dashboard in your browser",
224
+ )
225
+ _add_guard_common_args(dashboard_parser)
226
+ dashboard_parser.add_argument("--json", action="store_true")
227
+
228
+ admin_parser = guard_subparsers.add_parser("admin", help=argparse.SUPPRESS)
229
+ _add_guard_common_args(admin_parser)
230
+ admin_parser.add_argument("--json", action="store_true")
231
+
220
232
  bootstrap_parser = guard_subparsers.add_parser(
221
233
  "bootstrap",
222
234
  help="Detect a harness, start the approval center, and install Guard for the best local target",
@@ -513,6 +525,7 @@ def _configure_guard_parser(guard_parser: argparse.ArgumentParser) -> None:
513
525
  hermes_mcp_proxy_parser.add_argument("--server", required=True)
514
526
  hermes_mcp_proxy_parser.add_argument("--stdio", action="store_true")
515
527
  hidden_commands = {
528
+ "admin",
516
529
  "hook",
517
530
  "daemon",
518
531
  "codex-mcp-proxy",
@@ -660,6 +673,43 @@ def run_guard_command(
660
673
  _emit("status", payload, getattr(args, "json", False))
661
674
  return 0
662
675
 
676
+ if args.guard_command in {"dashboard", "admin"}:
677
+ try:
678
+ approval_center_url = ensure_guard_daemon(guard_home)
679
+ except RuntimeError as error:
680
+ if getattr(args, "json", False):
681
+ _emit(
682
+ "dashboard",
683
+ {
684
+ "generated_at": _now(),
685
+ "opened": False,
686
+ "error": str(error),
687
+ },
688
+ True,
689
+ )
690
+ else:
691
+ print(str(error), file=sys.stderr)
692
+ return 1
693
+ open_result = _open_approval_center(
694
+ approval_center_url,
695
+ store=store,
696
+ config=config,
697
+ open_key="dashboard",
698
+ force_open=True,
699
+ )
700
+ _emit(
701
+ "dashboard",
702
+ {
703
+ "generated_at": _now(),
704
+ "approval_center_url": approval_center_url,
705
+ "browser_url": open_result.get("browser_url"),
706
+ "opened": bool(open_result.get("opened")),
707
+ "reason": str(open_result.get("reason") or "unknown"),
708
+ },
709
+ getattr(args, "json", False),
710
+ )
711
+ return 0
712
+
663
713
  if args.guard_command == "bootstrap":
664
714
  try:
665
715
  payload = build_guard_bootstrap_payload(
@@ -3278,17 +3328,28 @@ def _headless_approval_resolver(
3278
3328
  return resolve
3279
3329
 
3280
3330
 
3281
- def _open_approval_center(approval_center_url: str, *, store: GuardStore, config, open_key: str | None = None) -> None:
3331
+ def _open_approval_center(
3332
+ approval_center_url: str,
3333
+ *,
3334
+ store: GuardStore,
3335
+ config: GuardConfig,
3336
+ open_key: str | None = None,
3337
+ force_open: bool = False,
3338
+ ) -> dict[str, object]:
3282
3339
  surface_runtime = GuardSurfaceRuntime(store)
3283
3340
  auth_token = load_guard_daemon_auth_token(store.guard_home)
3284
- surface_runtime.ensure_surface(
3341
+ browser_url = _approval_center_browser_url(approval_center_url, auth_token)
3342
+ open_result = surface_runtime.ensure_surface(
3285
3343
  surface="approval-center",
3286
3344
  approval_center_url=approval_center_url,
3287
- browser_url=_approval_center_browser_url(approval_center_url, auth_token),
3345
+ browser_url=browser_url,
3288
3346
  approval_surface_policy=config.approval_surface_policy,
3289
3347
  open_key=open_key or approval_center_url,
3348
+ force_open=force_open,
3290
3349
  opener=webbrowser.open,
3291
3350
  )
3351
+ open_result["browser_url"] = _public_approval_center_url(browser_url) or approval_center_url
3352
+ return open_result
3292
3353
 
3293
3354
 
3294
3355
  def _approval_center_browser_url(approval_center_url: str, auth_token: str | None) -> str | None:
@@ -3304,6 +3365,18 @@ def _approval_center_browser_url(approval_center_url: str, auth_token: str | Non
3304
3365
  return urllib.parse.urlunparse(parsed._replace(fragment=urllib.parse.urlencode(fragment_pairs)))
3305
3366
 
3306
3367
 
3368
+ def _public_approval_center_url(browser_url: str | None) -> str | None:
3369
+ if browser_url is None:
3370
+ return None
3371
+ parsed = urllib.parse.urlparse(browser_url)
3372
+ fragment_pairs = [
3373
+ (key, value)
3374
+ for key, value in urllib.parse.parse_qsl(parsed.fragment, keep_blank_values=True)
3375
+ if key != "guard-token"
3376
+ ]
3377
+ return urllib.parse.urlunparse(parsed._replace(fragment=urllib.parse.urlencode(fragment_pairs)))
3378
+
3379
+
3307
3380
  def _approval_surface_policy_for_flow(config_policy: str, approval_flow: dict[str, object]) -> str:
3308
3381
  if approval_flow.get("tier") != "approval-center":
3309
3382
  return "notify-only"
@@ -654,6 +654,14 @@ def _render_connect(console: Console, payload: dict[str, object]) -> None:
654
654
  console.print(_build_steps_panel(_coerce_dict_list(payload.get("next_steps"))))
655
655
 
656
656
 
657
+ def _render_dashboard(console: Console, payload: dict[str, object]) -> None:
658
+ body = Table.grid(padding=(0, 1))
659
+ body.add_row("Dashboard", str(payload.get("approval_center_url") or "unknown"))
660
+ if payload.get("opened") is not None:
661
+ body.add_row("Browser opened", _bool_label(bool(payload.get("opened"))))
662
+ console.print(Panel(body, title="HOL Guard dashboard", border_style="cyan"))
663
+
664
+
657
665
  def _render_sync(console: Console, payload: dict[str, object]) -> None:
658
666
  body = Table.grid(padding=(0, 1))
659
667
  body.add_row("Synced at", str(payload.get("synced_at") or "unknown"))
@@ -1615,6 +1623,7 @@ _RENDERERS: dict[str, Any] = {
1615
1623
  "approvals": _render_approvals,
1616
1624
  "start": _render_start,
1617
1625
  "status": _render_status,
1626
+ "dashboard": _render_dashboard,
1618
1627
  "connect": _render_connect,
1619
1628
  "bootstrap": _render_bootstrap,
1620
1629
  "detect": _render_detect,
@@ -332,10 +332,15 @@ class GuardSurfaceRuntime:
332
332
  approval_surface_policy: str,
333
333
  open_key: str,
334
334
  opener: Callable[[str], object],
335
+ force_open: bool = False,
335
336
  ) -> dict[str, object]:
336
- if approval_surface_policy in {"notify-only", "never-auto-open"}:
337
+ if approval_surface_policy in {"notify-only", "never-auto-open"} and not force_open:
337
338
  return {"surface": surface, "opened": False, "reason": "policy-disabled", "open_key": open_key}
338
- if approval_surface_policy == "auto-open-once" and self.has_surface_opened(surface, open_key):
339
+ if (
340
+ approval_surface_policy == "auto-open-once"
341
+ and not force_open
342
+ and self.has_surface_opened(surface, open_key)
343
+ ):
339
344
  return {"surface": surface, "opened": False, "reason": "already-opened", "open_key": open_key}
340
345
  if self.has_live_surface(surface):
341
346
  return {"surface": surface, "opened": False, "reason": "live-client", "open_key": open_key}
@@ -1,3 +1,3 @@
1
1
  """Single source of truth for tool version."""
2
2
 
3
- __version__ = "2.0.73"
3
+ __version__ = "2.0.75"
@@ -6048,6 +6048,100 @@ url = http://127.0.0.1:8787/guard-canary
6048
6048
  assert connect_output["reason"] == "Guard sync requires a Pro or Team plan."
6049
6049
  assert connect_output["sync_message"] == "Guard sync requires a Pro or Team plan."
6050
6050
 
6051
+ def test_guard_dashboard_opens_local_approval_center(self, tmp_path, capsys, monkeypatch):
6052
+ home_dir = tmp_path / "home"
6053
+ opened_urls: list[str] = []
6054
+ open_keys: list[str | None] = []
6055
+ force_open_flags: list[bool] = []
6056
+
6057
+ monkeypatch.setattr(
6058
+ guard_commands_module,
6059
+ "ensure_guard_daemon",
6060
+ lambda guard_home: "http://127.0.0.1:5474",
6061
+ )
6062
+ monkeypatch.setattr(
6063
+ guard_commands_module,
6064
+ "_open_approval_center",
6065
+ lambda approval_center_url, *, store, config, open_key=None, force_open=False: (
6066
+ opened_urls.append(approval_center_url),
6067
+ open_keys.append(open_key),
6068
+ force_open_flags.append(force_open),
6069
+ {"opened": True, "reason": "opened", "browser_url": approval_center_url},
6070
+ )[-1],
6071
+ )
6072
+
6073
+ rc = main(["guard", "dashboard", "--home", str(home_dir), "--json"])
6074
+ output = json.loads(capsys.readouterr().out)
6075
+
6076
+ assert rc == 0
6077
+ assert opened_urls == ["http://127.0.0.1:5474"]
6078
+ assert open_keys == ["dashboard"]
6079
+ assert force_open_flags == [True]
6080
+ assert output["approval_center_url"] == "http://127.0.0.1:5474"
6081
+ assert output["browser_url"] == "http://127.0.0.1:5474"
6082
+ assert output["opened"] is True
6083
+ assert output["reason"] == "opened"
6084
+
6085
+ def test_guard_admin_alias_opens_local_approval_center(self, tmp_path, capsys, monkeypatch):
6086
+ home_dir = tmp_path / "home"
6087
+ opened_urls: list[str] = []
6088
+ open_keys: list[str | None] = []
6089
+ force_open_flags: list[bool] = []
6090
+
6091
+ monkeypatch.setattr(
6092
+ guard_commands_module,
6093
+ "ensure_guard_daemon",
6094
+ lambda guard_home: "http://127.0.0.1:5474",
6095
+ )
6096
+ monkeypatch.setattr(
6097
+ guard_commands_module,
6098
+ "_open_approval_center",
6099
+ lambda approval_center_url, *, store, config, open_key=None, force_open=False: (
6100
+ opened_urls.append(approval_center_url),
6101
+ open_keys.append(open_key),
6102
+ force_open_flags.append(force_open),
6103
+ {"opened": False, "reason": "policy-disabled", "browser_url": approval_center_url},
6104
+ )[-1],
6105
+ )
6106
+
6107
+ rc = main(["guard", "admin", "--home", str(home_dir), "--json"])
6108
+ output = json.loads(capsys.readouterr().out)
6109
+
6110
+ assert rc == 0
6111
+ assert opened_urls == ["http://127.0.0.1:5474"]
6112
+ assert open_keys == ["dashboard"]
6113
+ assert force_open_flags == [True]
6114
+ assert output["approval_center_url"] == "http://127.0.0.1:5474"
6115
+ assert output["browser_url"] == "http://127.0.0.1:5474"
6116
+ assert output["opened"] is False
6117
+ assert output["reason"] == "policy-disabled"
6118
+
6119
+ def test_guard_dashboard_returns_error_when_daemon_start_fails(self, tmp_path, capsys, monkeypatch):
6120
+ home_dir = tmp_path / "home"
6121
+
6122
+ monkeypatch.setattr(
6123
+ guard_commands_module,
6124
+ "ensure_guard_daemon",
6125
+ lambda guard_home: (_ for _ in ()).throw(RuntimeError("dashboard_unavailable")),
6126
+ )
6127
+
6128
+ rc = main(["guard", "dashboard", "--home", str(home_dir), "--json"])
6129
+ output = json.loads(capsys.readouterr().out)
6130
+
6131
+ assert rc == 1
6132
+ assert output["opened"] is False
6133
+ assert output["error"] == "dashboard_unavailable"
6134
+
6135
+ def test_public_approval_center_url_strips_guard_token(self):
6136
+ browser_url = guard_commands_module._approval_center_browser_url(
6137
+ "http://127.0.0.1:5474#section=inbox",
6138
+ "secret-token",
6139
+ )
6140
+
6141
+ assert browser_url is not None
6142
+ assert "guard-token=secret-token" in browser_url
6143
+ assert "guard-token=" not in guard_commands_module._public_approval_center_url(browser_url)
6144
+
6051
6145
  def test_guard_connect_pending_output_uses_product_copy_for_sign_in_gap(self, capsys):
6052
6146
  emit_guard_payload(
6053
6147
  "connect",
@@ -1620,3 +1620,46 @@ class TestGuardSurfaceServer:
1620
1620
  assert operation["operation_type"] == "run"
1621
1621
  assert approval["request_ids"] == ["req-1", "req-2"]
1622
1622
  assert resumed["status"] == "completed"
1623
+
1624
+ def test_guard_surface_runtime_force_open_bypasses_auto_open_once(self, tmp_path) -> None:
1625
+ store = GuardStore(tmp_path / "guard-home")
1626
+ runtime = GuardSurfaceRuntime(store)
1627
+ opened_urls: list[str] = []
1628
+
1629
+ first_result = runtime.ensure_surface(
1630
+ surface="approval-center",
1631
+ approval_center_url="http://127.0.0.1:5474",
1632
+ approval_surface_policy="auto-open-once",
1633
+ open_key="dashboard",
1634
+ opener=lambda url: opened_urls.append(url) or True,
1635
+ )
1636
+ second_result = runtime.ensure_surface(
1637
+ surface="approval-center",
1638
+ approval_center_url="http://127.0.0.1:5474",
1639
+ approval_surface_policy="auto-open-once",
1640
+ open_key="dashboard",
1641
+ force_open=True,
1642
+ opener=lambda url: opened_urls.append(url) or True,
1643
+ )
1644
+
1645
+ assert first_result["opened"] is True
1646
+ assert second_result["opened"] is True
1647
+ assert opened_urls == ["http://127.0.0.1:5474", "http://127.0.0.1:5474"]
1648
+
1649
+ def test_guard_surface_runtime_force_open_overrides_disabled_policy(self, tmp_path) -> None:
1650
+ store = GuardStore(tmp_path / "guard-home")
1651
+ runtime = GuardSurfaceRuntime(store)
1652
+ opened_urls: list[str] = []
1653
+
1654
+ result = runtime.ensure_surface(
1655
+ surface="approval-center",
1656
+ approval_center_url="http://127.0.0.1:5474",
1657
+ approval_surface_policy="never-auto-open",
1658
+ open_key="dashboard",
1659
+ force_open=True,
1660
+ opener=lambda url: opened_urls.append(url) or True,
1661
+ )
1662
+
1663
+ assert result["opened"] is True
1664
+ assert result["reason"] == "opened"
1665
+ assert opened_urls == ["http://127.0.0.1:5474"]