swarph-cli 0.46.0__tar.gz → 0.48.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 (339) hide show
  1. {swarph_cli-0.46.0/src/swarph_cli.egg-info → swarph_cli-0.48.0}/PKG-INFO +1 -1
  2. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/pyproject.toml +3 -2
  3. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/__init__.py +1 -1
  4. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/brain_ask.py +7 -3
  5. swarph_cli-0.48.0/src/swarph_cli/commands/gateway.py +278 -0
  6. swarph_cli-0.48.0/src/swarph_cli/commands/install_postcompact_hook.py +528 -0
  7. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/install_wake_hook.py +57 -6
  8. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/mesh.py +218 -25
  9. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/onboard.py +154 -5
  10. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/spawn.py +50 -4
  11. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/watchdog.py +8 -1
  12. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/schema.sql +3 -1
  13. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/main.py +1 -0
  14. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/_shim.cmd +7 -0
  15. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +12 -0
  16. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +5 -0
  17. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +19 -0
  18. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +9 -0
  19. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +19 -0
  20. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +19 -0
  21. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +22 -0
  22. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/precompact-flag.sh +10 -0
  23. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +20 -0
  24. swarph_cli-0.48.0/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +23 -0
  25. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/codex-waker.default +4 -1
  26. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/peer-reply-drain.default +4 -1
  27. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/tokens.py +22 -0
  28. {swarph_cli-0.46.0 → swarph_cli-0.48.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
  29. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli.egg-info/SOURCES.txt +15 -0
  30. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_467b_onboard_operator_credential.py +42 -0
  31. swarph_cli-0.48.0/tests/test_548_gateway_remainder.py +82 -0
  32. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_brain_ask_command.py +5 -2
  33. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cursor_membrane.py +43 -2
  34. swarph_cli-0.48.0/tests/test_gateway_command.py +226 -0
  35. swarph_cli-0.48.0/tests/test_install_postcompact_hook.py +532 -0
  36. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_install_wake_hook.py +56 -2
  37. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_sidecar.py +9 -2
  38. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_monitor_state_split.py +57 -0
  39. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_onboard_command.py +192 -0
  40. swarph_cli-0.48.0/tests/test_tmux_wake_submit_verify.py +272 -0
  41. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_model_rung.py +22 -0
  42. swarph_cli-0.46.0/src/swarph_cli/commands/gateway.py +0 -116
  43. swarph_cli-0.46.0/tests/test_gateway_command.py +0 -92
  44. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/LICENSE +0 -0
  45. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/README.md +0 -0
  46. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/setup.cfg +0 -0
  47. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/__main__.py +0 -0
  48. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/__init__.py +0 -0
  49. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/backends.py +0 -0
  50. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/pack.py +0 -0
  51. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/prices.py +0 -0
  52. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/quality.py +0 -0
  53. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
  54. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/runner.py +0 -0
  55. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/bench/validate.py +0 -0
  56. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/caller.py +0 -0
  57. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/__init__.py +0 -0
  58. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/harden.py +0 -0
  59. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/lineage.py +0 -0
  60. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/liveness.py +0 -0
  61. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/manifest.py +0 -0
  62. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/paths.py +0 -0
  63. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/capture/verify.py +0 -0
  64. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/cell.py +0 -0
  65. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/chain_token.py +0 -0
  66. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/__init__.py +0 -0
  67. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/_content.py +0 -0
  68. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/_display.py +0 -0
  69. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
  70. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/add.py +0 -0
  71. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/bench.py +0 -0
  72. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/board.py +0 -0
  73. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
  74. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/brain.py +0 -0
  75. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/cell.py +0 -0
  76. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
  77. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/channel.py +0 -0
  78. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/chat.py +0 -0
  79. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/codegraph.py +0 -0
  80. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
  81. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
  82. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/codex_waker.py +0 -0
  83. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/compress.py +0 -0
  84. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/daemon.py +0 -0
  85. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/event.py +0 -0
  86. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/gh_route.py +0 -0
  87. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/group.py +0 -0
  88. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/guide.py +0 -0
  89. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/highlight.py +0 -0
  90. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/hook_output.py +0 -0
  91. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/hooks.py +0 -0
  92. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/import_session.py +0 -0
  93. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/init.py +0 -0
  94. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
  95. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/install_hook.py +0 -0
  96. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
  97. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/lane.py +0 -0
  98. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/mcp_server.py +0 -0
  99. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/memory.py +0 -0
  100. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
  101. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/memory_sync.py +0 -0
  102. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/monitor.py +0 -0
  103. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/okf_links.py +0 -0
  104. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
  105. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
  106. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
  107. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/ratify.py +0 -0
  108. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/schedule.py +0 -0
  109. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/security.py +0 -0
  110. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/service.py +0 -0
  111. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/timeline.py +0 -0
  112. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
  113. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/compress/__init__.py +0 -0
  114. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/compress/levers.py +0 -0
  115. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/compress/marker.py +0 -0
  116. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/compress/verify.py +0 -0
  117. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/console_safe.py +0 -0
  118. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/delivery_queue.py +0 -0
  119. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/__init__.py +0 -0
  120. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
  121. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
  122. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/server.py +0 -0
  123. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gateway/services_control.py +0 -0
  124. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/gh_identity.py +0 -0
  125. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/guide/GUIDE.md +0 -0
  126. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/multiplexer.py +0 -0
  127. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/parsers/__init__.py +0 -0
  128. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/parsers/claude.py +0 -0
  129. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_executor.py +0 -0
  130. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_executor_windows.py +0 -0
  131. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_reconciliation.py +0 -0
  132. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
  133. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_service_host.py +0 -0
  134. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_service_payload.py +0 -0
  135. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_service_reply.py +0 -0
  136. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/peer_staging.py +0 -0
  137. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/scripts/__init__.py +0 -0
  138. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
  139. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
  140. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
  141. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/service/__init__.py +0 -0
  142. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/service/app.py +0 -0
  143. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/service/providers.py +0 -0
  144. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/session_bridge.py +0 -0
  145. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/stall_alert.py +0 -0
  146. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
  147. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
  148. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
  149. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
  150. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
  151. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
  152. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
  153. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
  154. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
  155. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
  156. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
  157. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli.egg-info/requires.txt +0 -0
  158. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
  159. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_124_register_merge.py +0 -0
  160. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_201_win32_bash_refusal.py +0 -0
  161. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_243_onboard_per_peer_token.py +0 -0
  162. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_307_task4_ask_reply.py +0 -0
  163. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_319_refusal_exit_codes.py +0 -0
  164. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_323_mistral_backend.py +0 -0
  165. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_397_gh_identity_router.py +0 -0
  166. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_397_router_preflight.py +0 -0
  167. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_523_guide.py +0 -0
  168. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_524_conftest_hermeticity.py +0 -0
  169. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_532_accept_flag.py +0 -0
  170. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_535_version_capability.py +0 -0
  171. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_546_default_gateway.py +0 -0
  172. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_552_codegraph_gateway.py +0 -0
  173. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_555_relay_row_shape.py +0 -0
  174. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_556_relay_freshness.py +0 -0
  175. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_84_cli_labels.py +0 -0
  176. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_add_security_gate.py +0 -0
  177. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_add.py +0 -0
  178. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_hash.py +0 -0
  179. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_lib.py +0 -0
  180. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_mcp.py +0 -0
  181. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_skill.py +0 -0
  182. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_tool.py +0 -0
  183. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_artifact_uri.py +0 -0
  184. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_backends.py +0 -0
  185. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_command.py +0 -0
  186. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_pack.py +0 -0
  187. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_prices.py +0 -0
  188. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_quality.py +0 -0
  189. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_runner.py +0 -0
  190. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_bench_validate.py +0 -0
  191. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_board_card_thread.py +0 -0
  192. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_board_command.py +0 -0
  193. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_board_merge_check.py +0 -0
  194. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_board_obligation_close.py +0 -0
  195. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
  196. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
  197. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_brain_command.py +0 -0
  198. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_caller_meta_guard.py +0 -0
  199. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_capture_lineage.py +0 -0
  200. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_capture_liveness.py +0 -0
  201. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_capture_manifest.py +0 -0
  202. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_capture_paths.py +0 -0
  203. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_capture_security.py +0 -0
  204. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cell_command_dispatch.py +0 -0
  205. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cell_harden.py +0 -0
  206. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cell_loader.py +0 -0
  207. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cell_selfcheck.py +0 -0
  208. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_cell_verify.py +0 -0
  209. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_chain_token.py +0 -0
  210. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_channel_command.py +0 -0
  211. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
  212. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_channel_post.py +0 -0
  213. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_channel_wake_policy_filter.py +0 -0
  214. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_chat_command.py +0 -0
  215. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_claude_parser.py +0 -0
  216. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_claude_tmux_template.py +0 -0
  217. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_codegraph_caller_scope.py +0 -0
  218. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_codegraph_command.py +0 -0
  219. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_codegraph_hook.py +0 -0
  220. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_codegraph_mcp.py +0 -0
  221. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_codex_waker.py +0 -0
  222. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_compress_caller_convention.py +0 -0
  223. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_compress_command.py +0 -0
  224. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_compress_levers.py +0 -0
  225. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_compress_marker.py +0 -0
  226. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_compress_verify.py +0 -0
  227. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_console_encoding_e2e.py +0 -0
  228. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_content_composition.py +0 -0
  229. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_daemon_command.py +0 -0
  230. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_daemon_console_encoding.py +0 -0
  231. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_daemon_delivery.py +0 -0
  232. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_delivery_queue.py +0 -0
  233. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_display_sanitize.py +0 -0
  234. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
  235. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_event_emit.py +0 -0
  236. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_feature_to_uri.py +0 -0
  237. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_gateway_brain.py +0 -0
  238. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_gateway_client.py +0 -0
  239. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_gateway_message_idempotency.py +0 -0
  240. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
  241. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_group_command.py +0 -0
  242. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_heartbeat_check.py +0 -0
  243. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_highlight_command.py +0 -0
  244. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hook_output.py +0 -0
  245. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_activity_marker.py +0 -0
  246. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_add.py +0 -0
  247. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_bundle.py +0 -0
  248. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_init.py +0 -0
  249. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
  250. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_lifecycle.py +0 -0
  251. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_merge.py +0 -0
  252. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_verify.py +0 -0
  253. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
  254. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_hooks_win32_command_path.py +0 -0
  255. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_import_command.py +0 -0
  256. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_inbox_identity_provenance.py +0 -0
  257. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_init_command.py +0 -0
  258. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_init_provider_menu.py +0 -0
  259. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_install_codex_hooks.py +0 -0
  260. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_install_hook.py +0 -0
  261. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_install_multiplexer_command.py +0 -0
  262. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_lane_command.py +0 -0
  263. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_main.py +0 -0
  264. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_main_unknown_verb_guard.py +0 -0
  265. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mcp_server.py +0 -0
  266. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_memory_command.py +0 -0
  267. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_memory_gateway.py +0 -0
  268. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_memory_sync.py +0 -0
  269. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_memory_sync_membrane.py +0 -0
  270. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_command.py +0 -0
  271. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_inbox_mark_read.py +0 -0
  272. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_inbox_sanitize.py +0 -0
  273. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_send_unknown_peer.py +0 -0
  274. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
  275. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
  276. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_meta_edge_identity.py +0 -0
  277. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_monitor_channel_poll.py +0 -0
  278. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_monitor_cli.py +0 -0
  279. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_monitor_daemon_collision.py +0 -0
  280. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_monitor_self_name_verification.py +0 -0
  281. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_multiplexer.py +0 -0
  282. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_muse_membrane.py +0 -0
  283. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_muse_own_cli.py +0 -0
  284. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_obligation_close_is_surfaced.py +0 -0
  285. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_okf_links.py +0 -0
  286. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_packaged_artifacts_exist.py +0 -0
  287. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_executor.py +0 -0
  288. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_executor_windows.py +0 -0
  289. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_reply_drain_command.py +0 -0
  290. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
  291. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_reply_outbox.py +0 -0
  292. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_reply_transport.py +0 -0
  293. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_service_e2e.py +0 -0
  294. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_service_host.py +0 -0
  295. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_service_payload.py +0 -0
  296. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_peer_service_reply.py +0 -0
  297. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_postcompact_recall.py +0 -0
  298. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_process_liveness.py +0 -0
  299. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_protocol_handler.py +0 -0
  300. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_providers_isolation.py +0 -0
  301. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_ratify_command.py +0 -0
  302. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_repl_caller_convention.py +0 -0
  303. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_schedule_command.py +0 -0
  304. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_schedule_create_contract.py +0 -0
  305. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_security.py +0 -0
  306. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_service_app.py +0 -0
  307. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_service_command.py +0 -0
  308. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_service_providers.py +0 -0
  309. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_session_bridge_inject.py +0 -0
  310. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_session_bridge_probe.py +0 -0
  311. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_session_bridge_resolve.py +0 -0
  312. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_session_path_role_gate.py +0 -0
  313. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_shipped_units_match_the_cli.py +0 -0
  314. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_smoke_chat.py +0 -0
  315. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_smoke_one_shot.py +0 -0
  316. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_smoke_phase_5_5.py +0 -0
  317. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_base_hoist.py +0 -0
  318. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_command.py +0 -0
  319. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_identity_stamp.py +0 -0
  320. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_live_pin.py +0 -0
  321. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_mitosis_lineage.py +0 -0
  322. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_tmux_session.py +0 -0
  323. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_spawn_windows_relaunch.py +0 -0
  324. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_stall_alert.py +0 -0
  325. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_subprocess_decode_encoding.py +0 -0
  326. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_timeline_command.py +0 -0
  327. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_token_file_one_parser.py +0 -0
  328. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_token_precedence_explicit_first.py +0 -0
  329. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_unified_token_resolver.py +0 -0
  330. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_version_consistency.py +0 -0
  331. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_vibe_membrane.py +0 -0
  332. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog.py +0 -0
  333. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_dm_wake.py +0 -0
  334. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
  335. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
  336. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_emit_health.py +0 -0
  337. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_install_flags.py +0 -0
  338. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_watchdog_stale_peers.py +0 -0
  339. {swarph_cli-0.46.0 → swarph_cli-0.48.0}/tests/test_windows_waker_installer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swarph-cli
3
- Version: 0.46.0
3
+ Version: 0.48.0
4
4
  Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
5
5
  Author: Pierre Samson, Claude Opus
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "swarph-cli"
7
- version = "0.46.0"
7
+ version = "0.48.0"
8
8
  description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -120,7 +120,8 @@ where = ["src"]
120
120
  # importlib.resources, so an undeclared GUIDE.md passes every test in the
121
121
  # tree and fails on every installed wheel -- the 0.39.3 bug above, again.
122
122
  swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
123
- "scripts/*.sh", "scripts/*.ps1", "guide/*.md"]
123
+ "scripts/*.sh", "scripts/*.ps1", "guide/*.md",
124
+ "payloads/postcompact/*.sh", "payloads/postcompact/*.cmd"]
124
125
  # v0.15: ship the bundled mesh-gateway schema so `swarph gateway serve`
125
126
  # can apply it at startup via MESH_SCHEMA_PATH (defaults next to server.py).
126
127
  "swarph_cli.gateway" = ["*.sql"]
@@ -16,6 +16,6 @@ The architecture splits CLI from substrate so:
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- __version__ = "0.46.0"
19
+ __version__ = "0.48.0"
20
20
 
21
21
  __all__ = ["__version__"]
@@ -10,7 +10,8 @@ verb so any cell can search the swarm's shared memory the same way. Two modes:
10
10
  Stdlib-only. Config from the environment, mirroring ``swarph mesh``'s token model:
11
11
 
12
12
  GBRAIN_MCP_URL gbrain MCP endpoint; falls back to SWARPH_BRAIN_MCP, else
13
- / SWARPH_BRAIN_MCP http://127.0.0.1:8792/mcp
13
+ / SWARPH_BRAIN_MCP http://100.107.222.72:8792/mcp (tailnet IP — gbrain
14
+ binds no loopback; measured 2026-08-23, card #548)
14
15
  GBRAIN_TOKEN read token; falls back to SWARPH_BRAIN_TOKEN, then to the
15
16
  / SWARPH_BRAIN_TOKEN mesh per-peer token (~/.config/swarph/<self>.peer_token).
16
17
  / peer-token file Once gbrain accepts mesh peer tokens, the peer token IS
@@ -34,12 +35,15 @@ from swarph_cli import tokens
34
35
  from pathlib import Path
35
36
  from typing import Optional
36
37
 
37
- _DEFAULT_GBRAIN = "http://127.0.0.1:8792/mcp"
38
+ # TAILNET IP, NOT loopback (card #548): measured 2026-08-23, gbrain binds
39
+ # 100.107.222.72:8792 ONLY — 127.0.0.1:8792 refuses on the gateway box itself.
40
+ # A loopback default is deaf everywhere, including on the box running gbrain.
41
+ _DEFAULT_GBRAIN = "http://100.107.222.72:8792/mcp"
38
42
  _DEFAULT_TOPK = 6
39
43
 
40
44
 
41
45
  def _resolve_endpoint() -> str:
42
- """Endpoint precedence: GBRAIN_MCP_URL > SWARPH_BRAIN_MCP > localhost default.
46
+ """Endpoint precedence: GBRAIN_MCP_URL > SWARPH_BRAIN_MCP > tailnet default.
43
47
 
44
48
  The SWARPH_BRAIN_MCP fallback keeps the verb config-compatible with the
45
49
  standalone ``swarph-brain-ask`` script (which reads SWARPH_BRAIN_*), so one
@@ -0,0 +1,278 @@
1
+ """``swarph gateway`` — run the bundled mesh-gateway server.
2
+
3
+ The mesh-gateway is the coordination/DM server behind the swarph mesh:
4
+ peer registry, DM inbox/outbox, feature aggregation + allowlist/caps,
5
+ and lane/service control. It used to live as a standalone deployment;
6
+ ``swarph gateway serve`` bundles it as a first-class verb so any host
7
+ can stand up a gateway the same way it runs the client verbs.
8
+
9
+ The FastAPI/uvicorn server stack is an OPTIONAL extra so the core
10
+ client paths (one-shot / chat / mesh / brain-ask) stay dependency-light::
11
+
12
+ pip install "swarph-cli[gateway]"
13
+
14
+ Token model (mirrors ``swarph mesh``'s ``MESH_GATEWAY_TOKEN`` bearer):
15
+
16
+ * ``--token`` sets ``MESH_GATEWAY_TOKEN`` for the served process.
17
+ * Otherwise an existing ``MESH_GATEWAY_TOKEN`` in the environment is used.
18
+ * If neither is present a fresh token is minted (``secrets.token_urlsafe``)
19
+ and printed once so the operator can hand it to the mesh cells.
20
+
21
+ ``bootstrap-ratify`` (#565-B) is the ratify ladder's first rung: on a fresh
22
+ gateway zero peers are ratified, so no witness exists and ``swarph ratify``
23
+ can never complete. This verb flips ONE peer ratified via a local DB write —
24
+ an explicit, audited act by the human commander on the gateway box, not a
25
+ migration accident. It self-destroys: it refuses when any ratified peer
26
+ already exists.
27
+
28
+ Bare ``swarph gateway`` prints help.
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import argparse
34
+ import getpass
35
+ import os
36
+ import secrets
37
+ import socket
38
+ import sqlite3
39
+ import sys
40
+ from datetime import datetime, timezone
41
+
42
+
43
+ def _build_parser() -> argparse.ArgumentParser:
44
+ p = argparse.ArgumentParser(
45
+ prog="swarph gateway",
46
+ description=(
47
+ "Run the bundled mesh-gateway server (peer registry + DM "
48
+ "inbox/outbox + feature aggregation + lane/service control)."
49
+ ),
50
+ )
51
+ sub = p.add_subparsers(dest="command")
52
+
53
+ serve = sub.add_parser("serve", help="run the mesh-gateway HTTP server")
54
+ serve.add_argument(
55
+ "--host",
56
+ default="127.0.0.1",
57
+ help="bind address (default 127.0.0.1; use a tailnet IP to expose to peers)",
58
+ )
59
+ serve.add_argument(
60
+ "--port",
61
+ type=int,
62
+ default=8788,
63
+ help="bind port (default 8788)",
64
+ )
65
+ serve.add_argument(
66
+ "--token",
67
+ default=None,
68
+ help="bearer token for the gateway (sets MESH_GATEWAY_TOKEN); "
69
+ "minted + printed once if omitted and not already in the env",
70
+ )
71
+ serve.add_argument(
72
+ "--db",
73
+ default=None,
74
+ help="path to the gateway SQLite DB (sets MESH_DB_PATH)",
75
+ )
76
+
77
+ boot = sub.add_parser(
78
+ "bootstrap-ratify",
79
+ help="ratify the FIRST peer on a fresh gateway (human commander, "
80
+ "local DB write, audited)",
81
+ )
82
+ boot.add_argument("peer", help="peer name to bootstrap-ratify")
83
+ boot.add_argument(
84
+ "--db",
85
+ default=None,
86
+ help="path to the gateway SQLite DB (default: $MESH_DB_PATH or "
87
+ "~/.swarph/mesh.db)",
88
+ )
89
+ boot.add_argument(
90
+ "--reason",
91
+ default=None,
92
+ help="extra audit text appended to the bootstrap reason",
93
+ )
94
+ return p
95
+
96
+
97
+ def _serve(args: argparse.Namespace) -> int:
98
+ # Dependency gate — the server stack is an optional extra. Probe the
99
+ # imports before touching uvicorn so a missing extra prints a clean
100
+ # install hint instead of a traceback.
101
+ try:
102
+ import fastapi # noqa: F401
103
+ import uvicorn # noqa: F401
104
+ except ImportError:
105
+ sys.stderr.write(
106
+ 'swarph gateway needs the optional server deps: '
107
+ 'pip install "swarph-cli[gateway]"\n'
108
+ )
109
+ return 2
110
+
111
+ if args.db:
112
+ os.environ["MESH_DB_PATH"] = args.db
113
+
114
+ token = args.token or os.environ.get("MESH_GATEWAY_TOKEN")
115
+ if not token:
116
+ token = secrets.token_urlsafe(48)
117
+ print(
118
+ "swarph gateway: minted a new MESH_GATEWAY_TOKEN "
119
+ "(give this to your mesh cells):\n"
120
+ f" {token}",
121
+ file=sys.stderr,
122
+ )
123
+ os.environ["MESH_GATEWAY_TOKEN"] = token
124
+
125
+ uvicorn.run(
126
+ "swarph_cli.gateway.server:app",
127
+ host=args.host,
128
+ port=args.port,
129
+ log_level="info",
130
+ )
131
+ return 0
132
+
133
+
134
+ def _bootstrap_ratify(args: argparse.Namespace) -> int:
135
+ """#565-B: the ratify ladder's first rung, as an audited local write.
136
+
137
+ On a fresh gateway, `swarph ratify` can never complete — the witness must
138
+ itself be ratified, and zero peers are. The live mesh only works because
139
+ a one-time Phase 5.5 migration grandfathered its cohort; every deployment
140
+ since hangs off that accident. This verb makes the bootstrap explicit:
141
+
142
+ * LOCAL DB write — operator-with-filesystem-access is already god; this
143
+ makes the act explicit and audited instead of a SQL escape hatch.
144
+ * HUMAN-only — an interactive tty and a typed 'yes'. One prompt, no
145
+ ceremony: the gate must not cut into the onboarding flow it serves.
146
+ * SELF-DESTROYING — refuses when ANY ratified peer exists, so it can
147
+ never become a second ratification path. Recovery stays possible:
148
+ whenever zero ratified peers exist, the gate passes again.
149
+ * HAND-IN-HAND with onboarding — the target must already be registered
150
+ (onboard first), and the output names the next step.
151
+ """
152
+ db_path = (
153
+ args.db
154
+ or os.environ.get("MESH_DB_PATH")
155
+ or os.path.expanduser("~/.swarph/mesh.db")
156
+ )
157
+ if not os.path.exists(db_path):
158
+ sys.stderr.write(
159
+ f"swarph gateway bootstrap-ratify: no gateway DB at {db_path}\n"
160
+ " Run this on the gateway box, or pass --db / set MESH_DB_PATH.\n"
161
+ )
162
+ return 2
163
+
164
+ # Match server.py _conn(): autocommit with explicit transaction control
165
+ # (we BEGIN IMMEDIATE ourselves) and FK enforcement ON — the audit insert
166
+ # into peer_ratifications references claude_peers, and without the pragma
167
+ # a dangling reference would write silently.
168
+ conn = sqlite3.connect(db_path, isolation_level=None)
169
+ conn.execute("PRAGMA foreign_keys = ON")
170
+ conn.row_factory = sqlite3.Row
171
+ try:
172
+ row = conn.execute(
173
+ "SELECT ratified FROM claude_peers WHERE name = ?", (args.peer,)
174
+ ).fetchone()
175
+ if row is None:
176
+ sys.stderr.write(
177
+ f"swarph gateway bootstrap-ratify: {args.peer!r} is not "
178
+ "registered —\n onboard it first:\n"
179
+ f" swarph onboard {args.peer}\n"
180
+ )
181
+ return 2
182
+ if row["ratified"]:
183
+ print(f"{args.peer!r} is already ratified — nothing to do.")
184
+ return 0
185
+ existing = conn.execute(
186
+ "SELECT name FROM claude_peers WHERE ratified = 1 LIMIT 1"
187
+ ).fetchone()
188
+ if existing:
189
+ sys.stderr.write(
190
+ f"swarph gateway bootstrap-ratify: refusing — "
191
+ f"{existing['name']!r} is already ratified.\n"
192
+ " The ladder has its first rung; bootstrap exists ONLY for a "
193
+ "fresh gateway.\n"
194
+ f" Ratify {args.peer!r} through the normal witness path "
195
+ f"(a ratified witness runs:\n"
196
+ f" swarph ratify {args.peer} --reason \"<short text>\")\n"
197
+ )
198
+ return 2
199
+
200
+ if not sys.stdin.isatty():
201
+ sys.stderr.write(
202
+ "swarph gateway bootstrap-ratify: refusing — this is the "
203
+ "human commander's act.\n"
204
+ " Run it interactively on the gateway box (no pipes, no "
205
+ "scripts, no cells).\n"
206
+ )
207
+ return 2
208
+
209
+ operator = f"{getpass.getuser()}@{socket.gethostname()}"
210
+ print(f"Bootstrap-ratify {args.peer!r} on {db_path}:")
211
+ print(" - zero ratified peers exist (fresh gateway)")
212
+ print(" - flips ratified=1 and writes an audited peer_ratifications "
213
+ "row (binding_regime='bootstrap')")
214
+ print(f" - performed by {operator}")
215
+ if input("Type 'yes' to confirm: ").strip().lower() != "yes":
216
+ print("aborted — nothing written.")
217
+ return 1
218
+
219
+ now = datetime.now(timezone.utc).isoformat()
220
+ reason = f"bootstrap by {operator} (#565)"
221
+ if args.reason:
222
+ reason += f": {args.reason}"
223
+ conn.execute("BEGIN IMMEDIATE")
224
+ try:
225
+ row = conn.execute(
226
+ "SELECT ratified FROM claude_peers WHERE name = ?", (args.peer,)
227
+ ).fetchone()
228
+ existing = conn.execute(
229
+ "SELECT name FROM claude_peers WHERE ratified = 1 LIMIT 1"
230
+ ).fetchone()
231
+ if row is None or row["ratified"] or existing:
232
+ conn.execute("ROLLBACK")
233
+ sys.stderr.write(
234
+ "swarph gateway bootstrap-ratify: gateway state changed while "
235
+ "you confirmed; refusing to create a second first rung.\n"
236
+ )
237
+ return 2
238
+ conn.execute(
239
+ "UPDATE claude_peers SET ratified=1, ratified_at=?, "
240
+ "ratified_by=?, ratification_reason=? WHERE name=?",
241
+ (now, "bootstrap", reason, args.peer),
242
+ )
243
+ conn.execute(
244
+ "INSERT INTO peer_ratifications "
245
+ "(peer, ratified_by, ratified_at, reason, witness_dm_id, "
246
+ "binding_regime) VALUES (?, ?, ?, ?, NULL, 'bootstrap')",
247
+ (args.peer, "bootstrap", now, reason),
248
+ )
249
+ conn.execute("COMMIT")
250
+ except Exception:
251
+ conn.execute("ROLLBACK")
252
+ raise
253
+ finally:
254
+ conn.close()
255
+
256
+ print(f"{args.peer!r} ratified (bootstrap). The ladder has its first "
257
+ "rung — hand in hand with onboarding:")
258
+ print(f" - {args.peer} can now WITNESS ratifications for the cells "
259
+ "that follow")
260
+ print(f" - next cell: `swarph onboard <cell>` on its box, complete the "
261
+ f"handshake, then a witness runs:")
262
+ print(f" swarph ratify <cell> --reason \"<short text>\"")
263
+ return 0
264
+
265
+
266
+ def run_gateway(argv: list) -> int:
267
+ """Entry point invoked by ``swarph_cli.main`` verb dispatch."""
268
+ parser = _build_parser()
269
+ args = parser.parse_args(argv)
270
+
271
+ if args.command == "serve":
272
+ return _serve(args)
273
+ if args.command == "bootstrap-ratify":
274
+ return _bootstrap_ratify(args)
275
+
276
+ # bare `swarph gateway` (no subcommand) — print help.
277
+ parser.print_help()
278
+ return 0