agentnode-sdk 0.11.3__tar.gz → 0.11.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/CHANGELOG.md +18 -0
  2. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/PKG-INFO +1 -1
  3. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/__init__.py +1 -1
  4. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/main.py +2 -1
  5. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/publish.py +39 -0
  6. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/pyproject.toml +1 -1
  7. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_publish.py +90 -5
  8. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/.env.example +0 -0
  9. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/.gitignore +0 -0
  10. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/README.md +0 -0
  11. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/REGISTRY_SIGNING_ACTIVATION.md +0 -0
  12. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/REGISTRY_SIGNING_SPEC.md +0 -0
  13. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/THREAT_MODEL.md +0 -0
  14. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/TRUST_STACK.md +0 -0
  15. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode.lock +0 -0
  16. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/_fileutil.py +0 -0
  17. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/async_client.py +0 -0
  18. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/capability_graph.py +0 -0
  19. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/capability_taxonomy.py +0 -0
  20. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/__init__.py +0 -0
  21. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/__main__.py +0 -0
  22. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/audit.py +0 -0
  23. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/auth.py +0 -0
  24. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/cassette_audit.py +0 -0
  25. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/commands.py +0 -0
  26. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/complements.py +0 -0
  27. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/init.py +0 -0
  28. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/mcp_commands.py +0 -0
  29. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/mcp_status.py +0 -0
  30. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/mcp_submit.py +0 -0
  31. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/mcp_verify.py +0 -0
  32. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/output.py +0 -0
  33. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/record_cases.py +0 -0
  34. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/sandbox_commands.py +0 -0
  35. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/serve.py +0 -0
  36. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/setup_wizard.py +0 -0
  37. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/smart_run.py +0 -0
  38. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/templates.py +0 -0
  39. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/validate.py +0 -0
  40. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/cli/verify_local.py +0 -0
  41. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/client.py +0 -0
  42. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/compatibility.py +0 -0
  43. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/config.py +0 -0
  44. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/credential_handle.py +0 -0
  45. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/credential_resolver.py +0 -0
  46. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/credential_store.py +0 -0
  47. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/detect.py +0 -0
  48. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/exceptions.py +0 -0
  49. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/guard.py +0 -0
  50. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/input_guard.py +0 -0
  51. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/installer.py +0 -0
  52. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/key_status.py +0 -0
  53. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/lock_integrity.py +0 -0
  54. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/mcp_server.py +0 -0
  55. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/models.py +0 -0
  56. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/planner.py +0 -0
  57. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/policy.py +0 -0
  58. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/references.py +0 -0
  59. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/registry_trust.py +0 -0
  60. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/resolve.py +0 -0
  61. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/resource_provider.py +0 -0
  62. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/risk_profile.py +0 -0
  63. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/run_log.py +0 -0
  64. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runner.py +0 -0
  65. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtime.py +0 -0
  66. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtimes/__init__.py +0 -0
  67. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtimes/agent_runner.py +0 -0
  68. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtimes/mcp_runner.py +0 -0
  69. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtimes/python_runner.py +0 -0
  70. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/runtimes/remote_runner.py +0 -0
  71. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/sandbox/__init__.py +0 -0
  72. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/sandbox/backend.py +0 -0
  73. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/sandbox/container_backend.py +0 -0
  74. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/sandbox/policy.py +0 -0
  75. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/sandbox/types.py +0 -0
  76. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/signature.py +0 -0
  77. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/signing_key.py +0 -0
  78. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/agentnode_sdk/skill.py +0 -0
  79. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/sandbox-image/Dockerfile +0 -0
  80. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/sandbox-image/README.md +0 -0
  81. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/analyze_scores.py +0 -0
  82. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/batch_verify.py +0 -0
  83. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/ci_smoke_test.py +0 -0
  84. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/generate_compatibility_artifacts.py +0 -0
  85. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/verify_toolcalls.py +0 -0
  86. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/scripts/weekly_retest.sh +0 -0
  87. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/__init__.py +0 -0
  88. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/conftest.py +0 -0
  89. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_agent_runner.py +0 -0
  90. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_async_client.py +0 -0
  91. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_audit_ux.py +0 -0
  92. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_auto_upgrade_policy.py +0 -0
  93. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_cli.py +0 -0
  94. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_cli_lock.py +0 -0
  95. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_cli_run_resolution.py +0 -0
  96. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_client.py +0 -0
  97. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_client_json_guard.py +0 -0
  98. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_client_sprint_b.py +0 -0
  99. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_config.py +0 -0
  100. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_credential_handle.py +0 -0
  101. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_credential_integration.py +0 -0
  102. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_credential_resolver.py +0 -0
  103. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_credential_store.py +0 -0
  104. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_detect.py +0 -0
  105. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_detect_and_install.py +0 -0
  106. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_e2e_runtime.py +0 -0
  107. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_edge_cases.py +0 -0
  108. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard.py +0 -0
  109. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_check.py +0 -0
  110. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_config_cache.py +0 -0
  111. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_policy.py +0 -0
  112. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_preview.py +0 -0
  113. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_schema.py +0 -0
  114. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_set.py +0 -0
  115. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_status.py +0 -0
  116. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_tool_override.py +0 -0
  117. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_tool_override_audit.py +0 -0
  118. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_tool_override_cli.py +0 -0
  119. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_guard_ux.py +0 -0
  120. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_input_guard.py +0 -0
  121. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_input_guard_escalation.py +0 -0
  122. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_install_hardening.py +0 -0
  123. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_installer_sprint_b.py +0 -0
  124. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_intelligence.py +0 -0
  125. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_key_status.py +0 -0
  126. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_llm_binding.py +0 -0
  127. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_llm_call_runlog.py +0 -0
  128. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_lock_integrity.py +0 -0
  129. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_lock_runtime.py +0 -0
  130. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_mcp_audit.py +0 -0
  131. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_mcp_doctor.py +0 -0
  132. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_mcp_sandbox.py +0 -0
  133. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_mcp_server.py +0 -0
  134. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_observability.py +0 -0
  135. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_planner.py +0 -0
  136. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_policy.py +0 -0
  137. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_policy_integration.py +0 -0
  138. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_prompt_specs.py +0 -0
  139. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_provider_matrix.py +0 -0
  140. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_references.py +0 -0
  141. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_registry_trust.py +0 -0
  142. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_remote_hardening.py +0 -0
  143. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_remote_runner.py +0 -0
  144. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_resource_provider.py +0 -0
  145. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_resource_specs.py +0 -0
  146. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_risk_profile.py +0 -0
  147. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_run_log.py +0 -0
  148. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_runner.py +0 -0
  149. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_runtime.py +0 -0
  150. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_runtime_audit.py +0 -0
  151. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_sandbox_backend.py +0 -0
  152. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_sandbox_doctor.py +0 -0
  153. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_sandbox_e2e.py +0 -0
  154. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_sandbox_gate.py +0 -0
  155. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_security_hardening.py +0 -0
  156. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_signature.py +0 -0
  157. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_signing_key.py +0 -0
  158. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_skill.py +0 -0
  159. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_smart.py +0 -0
  160. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_stability.py +0 -0
  161. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_toolpack_sandbox.py +0 -0
  162. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_v02.py +0 -0
  163. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_validate.py +0 -0
  164. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/test_validate_skill.py +0 -0
  165. {agentnode_sdk-0.11.3 → agentnode_sdk-0.11.4}/tests/validation_lockfile.json +0 -0
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.4 — Publish confirm gate
4
+
5
+ ### Added
6
+
7
+ - **`agentnode publish` now asks for confirmation before publishing.** After the
8
+ preview, the command prompts `Publish <pkg>@<version> to <registry>? [y/N]`
9
+ (default No) and only uploads on explicit `y`. A new `--yes`/`-y` flag skips
10
+ the prompt for CI/automation. `--dry-run` is unchanged (never prompts, never
11
+ publishes). Prevents accidental publishes of the wrong package/version/folder.
12
+
13
+ ### BREAKING / Upgrade Notes
14
+
15
+ - **Non-interactive publish now requires `--yes`.** Previously `agentnode publish`
16
+ in a non-interactive context (CI, piped stdin, or `AGENTNODE_NON_INTERACTIVE=1`)
17
+ uploaded silently. It now **refuses** without `--yes` and exits non-zero.
18
+ Automation that publishes must add `--yes`. Interactive use is unaffected
19
+ beyond the new prompt.
20
+
3
21
  ## 0.11.3 — Test hygiene + multi-tool run guidance
4
22
 
5
23
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnode-sdk
3
- Version: 0.11.3
3
+ Version: 0.11.4
4
4
  Summary: Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents.
5
5
  Project-URL: Homepage, https://agentnode.net
6
6
  Project-URL: Repository, https://github.com/agentnode-ai/agentnode
@@ -38,7 +38,7 @@ from agentnode_sdk.runtime import AgentNodeRuntime
38
38
  Client = AgentNodeClient
39
39
  ToolError = AgentNodeToolError
40
40
 
41
- __version__ = "0.11.3"
41
+ __version__ = "0.11.4"
42
42
  __all__ = [
43
43
  "AgentNode",
44
44
  "AsyncAgentNode",
@@ -154,6 +154,7 @@ def main(argv: list[str] | None = None) -> int:
154
154
  publish_parser.add_argument("--dry-run", action="store_true", help="Validate and build without uploading")
155
155
  publish_parser.add_argument("--skip-validate", action="store_true", help="Continue despite validation errors")
156
156
  publish_parser.add_argument("--token", default=None, help="API key (default: AGENTNODE_API_KEY env)")
157
+ publish_parser.add_argument("--yes", "-y", action="store_true", help="Skip the confirmation prompt (required for non-interactive/CI publish)")
157
158
 
158
159
  # record-cases
159
160
  record_parser = sub.add_parser("record-cases", help="Record VCR cassettes for API verification cases")
@@ -338,7 +339,7 @@ def main(argv: list[str] | None = None) -> int:
338
339
  return commands.cmd_verify_local(args.path)
339
340
  if args.command == "publish":
340
341
  from agentnode_sdk.cli.publish import cmd_publish
341
- return cmd_publish(args.path, dry_run=args.dry_run, skip_validate=args.skip_validate, token=args.token)
342
+ return cmd_publish(args.path, dry_run=args.dry_run, skip_validate=args.skip_validate, token=args.token, yes=args.yes)
342
343
  if args.command == "record-cases":
343
344
  return commands.cmd_record_cases(args.path, strict=args.strict)
344
345
  if args.command == "inspect":
@@ -286,12 +286,38 @@ def _post_publish(
286
286
  }
287
287
 
288
288
 
289
+ def _confirm_publish(
290
+ pkg_id: str, version: str, registry: str, *, yes: bool, interactive: bool
291
+ ) -> bool:
292
+ """Final consent gate before an external publish. Returns True to proceed.
293
+
294
+ - ``yes``: explicit bypass for CI/automation → proceed.
295
+ - not ``interactive``: refuse — never publish non-interactively without --yes.
296
+ - interactive: prompt ``[y/N]``, default No.
297
+ """
298
+ if yes:
299
+ return True
300
+ if not interactive:
301
+ print(
302
+ " Refusing to publish non-interactively. "
303
+ "Re-run with --yes to confirm in CI/automation.",
304
+ file=sys.stderr,
305
+ )
306
+ return False
307
+ answer = input(f" Publish {pkg_id}@{version} to {registry}? [y/N] ").strip().lower()
308
+ if answer in ("y", "yes"):
309
+ return True
310
+ print(" Publish cancelled.")
311
+ return False
312
+
313
+
289
314
  def cmd_publish(
290
315
  path_str: str,
291
316
  *,
292
317
  dry_run: bool = False,
293
318
  skip_validate: bool = False,
294
319
  token: str | None = None,
320
+ yes: bool = False,
295
321
  ) -> int:
296
322
  """Publish a package to the AgentNode registry."""
297
323
  from agentnode_sdk.cli.validate import validate_package_dir
@@ -389,6 +415,19 @@ def cmd_publish(
389
415
  )
390
416
  return 1
391
417
 
418
+ # Publish confirm gate — publish is an external, hard-to-undo action.
419
+ # Detect interactivity robustly (CI/pipes have a non-TTY stdin even without
420
+ # AGENTNODE_NON_INTERACTIVE set); non-interactive requires explicit --yes.
421
+ registry = _resolve_api_base().replace("https://", "").replace("http://", "")
422
+ interactive = sys.stdin.isatty() and os.environ.get(
423
+ "AGENTNODE_NON_INTERACTIVE", ""
424
+ ).lower() not in ("true", "1")
425
+ if not _confirm_publish(pkg_id, version, registry, yes=yes, interactive=interactive):
426
+ # interactive decline = clean cancel (0); non-interactive without --yes
427
+ # = refusal (1). When yes=True the gate always proceeds, so we never land
428
+ # here in that case.
429
+ return 0 if interactive else 1
430
+
392
431
  try:
393
432
  sig_block = _sign_for_publish(pkg_id, manifest, artifact_bytes)
394
433
  manifest["_signatures"] = {"publisher": [sig_block]}
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentnode-sdk"
7
- version = "0.11.3"
7
+ version = "0.11.4"
8
8
  description = "Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,7 +4,9 @@ from __future__ import annotations
4
4
  import io
5
5
  import json
6
6
  import os
7
+ import sys
7
8
  import tarfile
9
+ import types
8
10
  from pathlib import Path
9
11
  from unittest.mock import patch, MagicMock
10
12
 
@@ -15,6 +17,7 @@ from agentnode_sdk.cli.publish import (
15
17
  PUBLISH_EXCLUDE_FILES,
16
18
  PUBLISH_EXCLUDE_SUFFIXES,
17
19
  _build_artifact,
20
+ _confirm_publish,
18
21
  _should_exclude,
19
22
  cmd_publish,
20
23
  )
@@ -259,7 +262,7 @@ class TestCmdPublish:
259
262
  "message": "Published test-pack@1.0.0",
260
263
  }
261
264
  with patch("agentnode_sdk.cli.publish._post_publish", return_value=mock_resp):
262
- rc = cmd_publish(str(tmp_path), token="test-key-123")
265
+ rc = cmd_publish(str(tmp_path), token="test-key-123", yes=True)
263
266
  assert rc == 0
264
267
  out = capsys.readouterr().out
265
268
  assert "Published test-pack@1.0.0" in out
@@ -276,7 +279,7 @@ class TestCmdPublish:
276
279
  "details": [],
277
280
  }
278
281
  with patch("agentnode_sdk.cli.publish._post_publish", return_value=mock_resp):
279
- rc = cmd_publish(str(tmp_path), token="test-key-123")
282
+ rc = cmd_publish(str(tmp_path), token="test-key-123", yes=True)
280
283
  assert rc == 1
281
284
  out = capsys.readouterr().out
282
285
  assert "already exists" in out
@@ -484,7 +487,7 @@ class TestCmdPublishSigning:
484
487
  return mock_resp
485
488
 
486
489
  with patch("agentnode_sdk.cli.publish._post_publish", side_effect=capture_post):
487
- rc = cmd_publish(str(tmp_path), token="test-key")
490
+ rc = cmd_publish(str(tmp_path), token="test-key", yes=True)
488
491
 
489
492
  assert rc == 0
490
493
  assert "_signatures" in captured_manifest
@@ -517,7 +520,7 @@ class TestCmdPublishSigning:
517
520
  return {"slug": "test-pack", "version": "1.0.0", "message": "ok"}
518
521
 
519
522
  with patch("agentnode_sdk.cli.publish._post_publish", side_effect=capture_post):
520
- rc = cmd_publish(str(tmp_path), token="test-key")
523
+ rc = cmd_publish(str(tmp_path), token="test-key", yes=True)
521
524
 
522
525
  assert rc == 0
523
526
 
@@ -544,7 +547,7 @@ class TestCmdPublishSigning:
544
547
 
545
548
  with patch("agentnode_sdk.cli.publish._sign_for_publish", side_effect=RuntimeError("key error")):
546
549
  with patch("agentnode_sdk.cli.publish._post_publish", return_value=mock_resp):
547
- rc = cmd_publish(str(tmp_path), token="test-key")
550
+ rc = cmd_publish(str(tmp_path), token="test-key", yes=True)
548
551
 
549
552
  assert rc == 0
550
553
  err = capsys.readouterr().err
@@ -591,3 +594,85 @@ class TestPublisherSlugRegressionGuards:
591
594
  from agentnode_sdk.cli.publish import manifest_to_entry
592
595
  entry = manifest_to_entry(MINIMAL_MANIFEST, "sha256:abc123")
593
596
  assert "publisher_slug" not in entry
597
+
598
+
599
+ # ---------------------------------------------------------------------------
600
+ # Publish confirm gate (0.11.4)
601
+ # ---------------------------------------------------------------------------
602
+
603
+ class TestConfirmPublishHelper:
604
+ """The decision logic in isolation — the security-critical part."""
605
+
606
+ def test_yes_bypasses(self):
607
+ assert _confirm_publish("p", "1.0", "reg", yes=True, interactive=False) is True
608
+
609
+ def test_non_interactive_without_yes_refuses(self, capsys):
610
+ assert _confirm_publish("p", "1.0", "reg", yes=False, interactive=False) is False
611
+ assert "non-interactively" in capsys.readouterr().err
612
+
613
+ def test_interactive_y_proceeds(self, monkeypatch):
614
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "y")
615
+ assert _confirm_publish("p", "1.0", "reg", yes=False, interactive=True) is True
616
+
617
+ def test_interactive_yes_word_proceeds(self, monkeypatch):
618
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "YES")
619
+ assert _confirm_publish("p", "1.0", "reg", yes=False, interactive=True) is True
620
+
621
+ def test_interactive_n_cancels(self, monkeypatch, capsys):
622
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "n")
623
+ assert _confirm_publish("p", "1.0", "reg", yes=False, interactive=True) is False
624
+ assert "cancelled" in capsys.readouterr().out
625
+
626
+ def test_interactive_enter_default_cancels(self, monkeypatch):
627
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "")
628
+ assert _confirm_publish("p", "1.0", "reg", yes=False, interactive=True) is False
629
+
630
+
631
+ class TestCmdPublishConfirmGate:
632
+ """End-to-end wiring: the gate must sit before _post_publish."""
633
+
634
+ def test_non_interactive_without_yes_refuses(self, tmp_path, capsys, monkeypatch):
635
+ _write_manifest(tmp_path)
636
+ (tmp_path / "tools.py").write_text("def run(): pass")
637
+ # force non-tty deterministically (independent of pytest -s)
638
+ monkeypatch.setattr(sys, "stdin", types.SimpleNamespace(isatty=lambda: False))
639
+ with patch("agentnode_sdk.cli.publish._post_publish") as mock_post:
640
+ rc = cmd_publish(str(tmp_path), token="test-key") # no --yes
641
+ assert rc == 1
642
+ mock_post.assert_not_called()
643
+ assert "non-interactively" in capsys.readouterr().err
644
+
645
+ def test_non_interactive_with_yes_publishes(self, tmp_path, monkeypatch):
646
+ monkeypatch.setenv("AGENTNODE_CONFIG", str(tmp_path / "config"))
647
+ _write_manifest(tmp_path)
648
+ (tmp_path / "tools.py").write_text("def run(): pass")
649
+ monkeypatch.setattr(sys, "stdin", types.SimpleNamespace(isatty=lambda: False))
650
+ resp = {"slug": "test-pack", "version": "1.0.0", "message": "Published"}
651
+ with patch("agentnode_sdk.cli.publish._post_publish", return_value=resp) as mock_post:
652
+ rc = cmd_publish(str(tmp_path), token="test-key", yes=True)
653
+ assert rc == 0
654
+ mock_post.assert_called_once()
655
+
656
+ def test_interactive_decline_does_not_publish(self, tmp_path, monkeypatch):
657
+ _write_manifest(tmp_path)
658
+ (tmp_path / "tools.py").write_text("def run(): pass")
659
+ monkeypatch.delenv("AGENTNODE_NON_INTERACTIVE", raising=False)
660
+ monkeypatch.setattr(sys, "stdin", types.SimpleNamespace(isatty=lambda: True))
661
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "n")
662
+ with patch("agentnode_sdk.cli.publish._post_publish") as mock_post:
663
+ rc = cmd_publish(str(tmp_path), token="test-key")
664
+ assert rc == 0
665
+ mock_post.assert_not_called()
666
+
667
+ def test_interactive_accept_publishes(self, tmp_path, monkeypatch):
668
+ monkeypatch.setenv("AGENTNODE_CONFIG", str(tmp_path / "config"))
669
+ _write_manifest(tmp_path)
670
+ (tmp_path / "tools.py").write_text("def run(): pass")
671
+ monkeypatch.delenv("AGENTNODE_NON_INTERACTIVE", raising=False)
672
+ monkeypatch.setattr(sys, "stdin", types.SimpleNamespace(isatty=lambda: True))
673
+ monkeypatch.setattr("builtins.input", lambda *a, **k: "y")
674
+ resp = {"slug": "test-pack", "version": "1.0.0", "message": "Published"}
675
+ with patch("agentnode_sdk.cli.publish._post_publish", return_value=resp) as mock_post:
676
+ rc = cmd_publish(str(tmp_path), token="test-key")
677
+ assert rc == 0
678
+ mock_post.assert_called_once()
File without changes