swarph-cli 0.72.7__tar.gz → 0.72.9__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.
- {swarph_cli-0.72.7/src/swarph_cli.egg-info → swarph_cli-0.72.9}/PKG-INFO +1 -1
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/pyproject.toml +1 -1
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/__init__.py +1 -1
- swarph_cli-0.72.9/src/swarph_cli/commands/roster.py +237 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/watchdog.py +113 -9
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/guide/GUIDE.md +1 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/main.py +1 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/rowclear.py +65 -4
- {swarph_cli-0.72.7 → swarph_cli-0.72.9/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
- swarph_cli-0.72.9/tests/test_1059_watchdog_unread_env.py +179 -0
- swarph_cli-0.72.9/tests/test_1061_roster_cli.py +137 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_432_rowclear.py +165 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar.py +40 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog.py +2 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/LICENSE +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/README.md +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/setup.cfg +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_main.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_security.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.7 → swarph_cli-0.72.9}/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.72.
|
|
3
|
+
Version: 0.72.9
|
|
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.72.
|
|
7
|
+
version = "0.72.9"
|
|
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" }
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""``swarph roster`` — read/write the gateway's operator-maintained capacity table.
|
|
2
|
+
|
|
3
|
+
card #1061 (ROSTER AS DATA): the roster replaces tribal knowledge about who
|
|
4
|
+
builds, who reviews, on which model/effort, under which quota, and who is out
|
|
5
|
+
until when. Reads hit GET /roster (any authenticated caller); writes hit
|
|
6
|
+
PUT /roster/{peer} (operators only — the gateway 403s the rest).
|
|
7
|
+
|
|
8
|
+
swarph roster [--peer PEER] [--json] list the table (or one row)
|
|
9
|
+
swarph roster set PEER [--model M] [--effort E] [--roles r1,r2]
|
|
10
|
+
[--quota-pool P] [--quota-used PCT --quota-as-of TS] [--resets DATE]
|
|
11
|
+
[--until TS | --clear-until] [--note TEXT]
|
|
12
|
+
|
|
13
|
+
A quota VALUE without its as_of coordinate is a rumour: --quota-used /
|
|
14
|
+
--resets without --quota-as-of are refused client-side (exit 2) before the
|
|
15
|
+
gateway's own 400. The pool NAME needs no coordinate (identifier, not a
|
|
16
|
+
measurement). --clear-until is the grok-returns flow (explicit null clears).
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import sys
|
|
23
|
+
import urllib.error
|
|
24
|
+
import urllib.request
|
|
25
|
+
|
|
26
|
+
# card #1061 role vocabulary — mirrors the gateway's _ROSTER_ROLES. A role
|
|
27
|
+
# the gateway does not know is refused here (exit 2) before its 400; when the
|
|
28
|
+
# vocabulary grows, BOTH pins change together (gateway tests + test_1061 below).
|
|
29
|
+
ROSTER_ROLES = ("build", "validate", "plan-review", "orchestrate")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _http_get_json(url, token, *, timeout=10.0):
|
|
33
|
+
from swarph_cli.commands.mesh import _http_get_json as _get
|
|
34
|
+
return _get(url, token, timeout=timeout)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _http_put_json(url, body, token, *, timeout=10.0):
|
|
38
|
+
from swarph_cli.commands.mesh import _require_absolute_gateway_url
|
|
39
|
+
_require_absolute_gateway_url(url)
|
|
40
|
+
data = json.dumps(body).encode("utf-8")
|
|
41
|
+
req = urllib.request.Request(
|
|
42
|
+
url, data=data, method="PUT",
|
|
43
|
+
headers={"Authorization": f"Bearer {token}",
|
|
44
|
+
"Content-Type": "application/json"})
|
|
45
|
+
try:
|
|
46
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
47
|
+
return resp.status, json.loads(resp.read().decode("utf-8") or "{}")
|
|
48
|
+
except urllib.error.HTTPError as exc:
|
|
49
|
+
try:
|
|
50
|
+
err_body = json.loads(exc.read().decode("utf-8") or "{}")
|
|
51
|
+
except Exception:
|
|
52
|
+
err_body = {"detail": str(exc)}
|
|
53
|
+
return exc.code, err_body
|
|
54
|
+
except urllib.error.URLError as exc:
|
|
55
|
+
return 0, {"detail": str(exc)}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _resolve(args):
|
|
59
|
+
from swarph_cli.tokens import resolve_token
|
|
60
|
+
resolution = resolve_token(args.self_name, args.token_file,
|
|
61
|
+
identity_is_explicit=bool(args.self_name))
|
|
62
|
+
if resolution is None:
|
|
63
|
+
print("swarph roster: no token (set MESH_GATEWAY_TOKEN, pass "
|
|
64
|
+
"--token-file, or --as a peer with a stored token)",
|
|
65
|
+
file=sys.stderr)
|
|
66
|
+
return None
|
|
67
|
+
return resolution.token
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _quota_compact(r):
|
|
71
|
+
if r.get("quota_used_pct") is None and not r.get("quota_pool"):
|
|
72
|
+
return "-"
|
|
73
|
+
parts = []
|
|
74
|
+
if r.get("quota_pool"):
|
|
75
|
+
parts.append(str(r["quota_pool"]))
|
|
76
|
+
if r.get("quota_used_pct") is not None:
|
|
77
|
+
parts.append(f"{r['quota_used_pct']}%")
|
|
78
|
+
if r.get("quota_as_of"):
|
|
79
|
+
parts.append(f"@{r['quota_as_of']}")
|
|
80
|
+
if r.get("quota_resets_at"):
|
|
81
|
+
parts.append(f"(resets {r['quota_resets_at']})")
|
|
82
|
+
return " ".join(parts)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _line(r):
|
|
86
|
+
roles = ",".join(r["roles"]) if r.get("roles") else "-"
|
|
87
|
+
bits = [r["peer"],
|
|
88
|
+
f"model={r.get('model') or '-'}",
|
|
89
|
+
f"effort={r.get('effort') or '-'}",
|
|
90
|
+
f"roles={roles}",
|
|
91
|
+
f"quota={_quota_compact(r)}"]
|
|
92
|
+
if r.get("unavailable_until"):
|
|
93
|
+
bits.append(f"out-until={r['unavailable_until']}")
|
|
94
|
+
bits.append(f"updated_by={r.get('updated_by') or '?'}")
|
|
95
|
+
return " ".join(bits)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _run_list(args):
|
|
99
|
+
token = _resolve(args)
|
|
100
|
+
if token is None:
|
|
101
|
+
return 2
|
|
102
|
+
gateway = args.gateway.rstrip("/")
|
|
103
|
+
status, payload = _http_get_json(f"{gateway}/roster", token)
|
|
104
|
+
if status < 200 or status >= 300:
|
|
105
|
+
detail = payload.get("detail", "<gateway error>") if isinstance(payload, dict) else "<gateway error>"
|
|
106
|
+
print(f"swarph roster: gateway {status}: {detail}", file=sys.stderr)
|
|
107
|
+
return 1
|
|
108
|
+
rows = payload.get("roster") if isinstance(payload, dict) else None
|
|
109
|
+
if not isinstance(rows, list):
|
|
110
|
+
print("swarph roster: unexpected /roster shape (no roster list)",
|
|
111
|
+
file=sys.stderr)
|
|
112
|
+
return 1
|
|
113
|
+
if args.peer:
|
|
114
|
+
rows = [r for r in rows if isinstance(r, dict) and r.get("peer") == args.peer]
|
|
115
|
+
if not rows:
|
|
116
|
+
print(f"swarph roster: no row for {args.peer!r}", file=sys.stderr)
|
|
117
|
+
return 1
|
|
118
|
+
if args.json:
|
|
119
|
+
print(json.dumps(rows, indent=2))
|
|
120
|
+
else:
|
|
121
|
+
for r in rows:
|
|
122
|
+
print(_line(r) if isinstance(r, dict) else r)
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _run_set(args):
|
|
127
|
+
token = _resolve(args)
|
|
128
|
+
if token is None:
|
|
129
|
+
return 2
|
|
130
|
+
if ((args.quota_used is not None or args.resets is not None)
|
|
131
|
+
and args.quota_as_of is None):
|
|
132
|
+
print("swarph roster set: --quota-used / --resets need --quota-as-of "
|
|
133
|
+
"(a quota value without its coordinate is a rumour)",
|
|
134
|
+
file=sys.stderr)
|
|
135
|
+
return 2
|
|
136
|
+
body = {}
|
|
137
|
+
if args.model is not None:
|
|
138
|
+
body["model"] = args.model
|
|
139
|
+
if args.effort is not None:
|
|
140
|
+
body["effort"] = args.effort
|
|
141
|
+
if args.roles is not None:
|
|
142
|
+
roles = [x.strip() for x in args.roles.split(",") if x.strip()]
|
|
143
|
+
bad = [x for x in roles if x not in ROSTER_ROLES]
|
|
144
|
+
if bad:
|
|
145
|
+
print(f"swarph roster set: unknown role(s) {bad} "
|
|
146
|
+
f"(vocabulary: {', '.join(ROSTER_ROLES)})", file=sys.stderr)
|
|
147
|
+
return 2
|
|
148
|
+
body["roles"] = roles
|
|
149
|
+
if args.quota_pool is not None:
|
|
150
|
+
body["quota_pool"] = args.quota_pool
|
|
151
|
+
if args.quota_used is not None:
|
|
152
|
+
body["quota_used_pct"] = args.quota_used
|
|
153
|
+
if args.resets is not None:
|
|
154
|
+
body["quota_resets_at"] = args.resets
|
|
155
|
+
if args.quota_as_of is not None:
|
|
156
|
+
body["quota_as_of"] = args.quota_as_of
|
|
157
|
+
if args.until is not None:
|
|
158
|
+
body["unavailable_until"] = args.until
|
|
159
|
+
if args.clear_until:
|
|
160
|
+
body["unavailable_until"] = None
|
|
161
|
+
if args.note is not None:
|
|
162
|
+
body["note"] = args.note
|
|
163
|
+
if not body:
|
|
164
|
+
print("swarph roster set: nothing to set (pass a field flag)",
|
|
165
|
+
file=sys.stderr)
|
|
166
|
+
return 2
|
|
167
|
+
gateway = args.gateway.rstrip("/")
|
|
168
|
+
status, payload = _http_put_json(
|
|
169
|
+
f"{gateway}/roster/{args.peer}", body, token)
|
|
170
|
+
if status < 200 or status >= 300:
|
|
171
|
+
detail = payload.get("detail", "<gateway error>") if isinstance(payload, dict) else "<gateway error>"
|
|
172
|
+
print(f"swarph roster set: gateway {status}: {detail}", file=sys.stderr)
|
|
173
|
+
return 1
|
|
174
|
+
if args.json:
|
|
175
|
+
print(json.dumps(payload, indent=2))
|
|
176
|
+
else:
|
|
177
|
+
print(f"updated {args.peer} (updated_by={payload.get('updated_by', '?')})")
|
|
178
|
+
return 0
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _add_global(p):
|
|
182
|
+
p.add_argument("--as", dest="self_name", default=None,
|
|
183
|
+
help="peer identity for token resolution")
|
|
184
|
+
from swarph_cli.gateway_default import env_gateway
|
|
185
|
+
p.add_argument("--gateway", default=env_gateway(),
|
|
186
|
+
help="mesh-gateway base URL")
|
|
187
|
+
p.add_argument("--token-file", default=None,
|
|
188
|
+
help="explicit Bearer [REDACTED] file")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _build_parser():
|
|
192
|
+
p = argparse.ArgumentParser(
|
|
193
|
+
prog="swarph roster",
|
|
194
|
+
description="Read/write the gateway capacity roster (card #1061).")
|
|
195
|
+
_add_global(p)
|
|
196
|
+
p.add_argument("--peer", default=None,
|
|
197
|
+
help="show only this peer (list mode)")
|
|
198
|
+
p.add_argument("--json", action="store_true",
|
|
199
|
+
help="print the raw gateway payload")
|
|
200
|
+
sub = p.add_subparsers(dest="command")
|
|
201
|
+
s = sub.add_parser("set", help="upsert one roster row (operators only)")
|
|
202
|
+
_add_global(s)
|
|
203
|
+
s.add_argument("peer", help="peer the row belongs to")
|
|
204
|
+
s.add_argument("--json", action="store_true",
|
|
205
|
+
help="print the raw gateway payload")
|
|
206
|
+
s.add_argument("--model", default=None)
|
|
207
|
+
s.add_argument("--effort", default=None)
|
|
208
|
+
s.add_argument("--roles", default=None,
|
|
209
|
+
help="comma-separated subset of " + ",".join(ROSTER_ROLES))
|
|
210
|
+
s.add_argument("--quota-pool", default=None)
|
|
211
|
+
s.add_argument("--quota-used", type=float, default=None,
|
|
212
|
+
help="quota_used_pct (needs --quota-as-of)")
|
|
213
|
+
s.add_argument("--quota-as-of", default=None)
|
|
214
|
+
s.add_argument("--resets", default=None,
|
|
215
|
+
help="quota_resets_at date (needs --quota-as-of)")
|
|
216
|
+
group = s.add_mutually_exclusive_group()
|
|
217
|
+
group.add_argument("--until", default=None,
|
|
218
|
+
help="unavailable_until timestamp")
|
|
219
|
+
group.add_argument("--clear-until", action="store_true",
|
|
220
|
+
help="clear unavailable_until (peer is back)")
|
|
221
|
+
s.add_argument("--note", default=None)
|
|
222
|
+
return p
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def run_roster(argv):
|
|
226
|
+
parser = _build_parser()
|
|
227
|
+
args = parser.parse_args(argv)
|
|
228
|
+
try:
|
|
229
|
+
if args.command == "set":
|
|
230
|
+
return _run_set(args)
|
|
231
|
+
if args.command is None:
|
|
232
|
+
return _run_list(args)
|
|
233
|
+
parser.error(f"unknown command: {args.command}")
|
|
234
|
+
except RuntimeError as exc:
|
|
235
|
+
print(f"swarph roster: {exc}", file=sys.stderr)
|
|
236
|
+
return 2
|
|
237
|
+
return 2
|
|
@@ -468,6 +468,14 @@ def _gateway_configured(gateway: str) -> bool:
|
|
|
468
468
|
"""
|
|
469
469
|
return bool((gateway or "").strip())
|
|
470
470
|
|
|
471
|
+
def _resolve_unread_token(token: Optional[str]) -> Optional[str]:
|
|
472
|
+
"""Token precedence for the unread check: explicit arg (cron setups
|
|
473
|
+
pass env straight through), else the watchdog identity's token file
|
|
474
|
+
(card #1059 — template units have no env). The VALUE is never logged.
|
|
475
|
+
"""
|
|
476
|
+
return token or _read_service_token()
|
|
477
|
+
|
|
478
|
+
|
|
471
479
|
def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Optional[int]:
|
|
472
480
|
"""Query gateway for unread DM count addressed to peer.
|
|
473
481
|
|
|
@@ -483,15 +491,15 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
|
|
|
483
491
|
the code it was documenting — found 2026-08-12 when the contradiction
|
|
484
492
|
caused a real reader to conclude a gateway/token outage makes A1 MORE
|
|
485
493
|
eager, when it makes A1 permanently inert for that cell instead.
|
|
494
|
+
|
|
495
|
+
Prefers the count-only endpoint (card #1059: no bodies cross the wire);
|
|
496
|
+
a 404 (gateway predates the endpoint) falls back to the list query so a
|
|
497
|
+
mixed fleet never goes blind from the client side alone.
|
|
486
498
|
"""
|
|
487
499
|
if not _gateway_configured(gateway):
|
|
488
500
|
return None
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
req = urllib.request.Request(url)
|
|
492
|
-
if token:
|
|
493
|
-
req.add_header("Authorization", f"Bearer {token}")
|
|
494
|
-
else:
|
|
501
|
+
token = _resolve_unread_token(token)
|
|
502
|
+
if not token:
|
|
495
503
|
# A missing token and a network outage both surface as None to the
|
|
496
504
|
# caller (correctly — the decision matrix treats them the same way),
|
|
497
505
|
# but they are different FACTS an operator would want to know apart.
|
|
@@ -503,10 +511,50 @@ def _gateway_unread_count(gateway: str, peer: str, token: Optional[str]) -> Opti
|
|
|
503
511
|
f"unread-count check may 401 if the gateway requires auth; "
|
|
504
512
|
f"A1 stays inert until one is set",
|
|
505
513
|
file=sys.stderr)
|
|
514
|
+
count = _gateway_unread_via_count(gateway, peer, token)
|
|
515
|
+
if count is not None:
|
|
516
|
+
return count
|
|
517
|
+
return _gateway_unread_via_list(gateway, peer, token)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _gateway_request(gateway: str, path: str, query: dict,
|
|
521
|
+
token: Optional[str]):
|
|
522
|
+
"""GET path+query with an optional Bearer token. Returns the decoded
|
|
523
|
+
JSON body, or (None, status) on transport failure — the status lets
|
|
524
|
+
callers tell a missing route (404: fall back) from a dead end."""
|
|
525
|
+
url = f"{gateway.rstrip('/')}{path}?{urllib.parse.urlencode(query)}"
|
|
526
|
+
req = urllib.request.Request(url)
|
|
527
|
+
if token:
|
|
528
|
+
req.add_header("Authorization", f"Bearer {token}")
|
|
506
529
|
try:
|
|
507
530
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
508
|
-
|
|
509
|
-
except
|
|
531
|
+
return json.loads(resp.read().decode("utf-8")), resp.status
|
|
532
|
+
except urllib.error.HTTPError as exc:
|
|
533
|
+
return None, exc.code
|
|
534
|
+
except (urllib.error.URLError, OSError, json.JSONDecodeError):
|
|
535
|
+
return None, None
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _gateway_unread_via_count(gateway: str, peer: str,
|
|
539
|
+
token: Optional[str]) -> Optional[int]:
|
|
540
|
+
"""Count-only read: {"to_node", "n"}. No bodies cross the wire, so a
|
|
541
|
+
least-privilege watchdog identity suffices (card #1059)."""
|
|
542
|
+
data, _status = _gateway_request(
|
|
543
|
+
gateway, "/messages/unread-count", {"to_node": peer}, token)
|
|
544
|
+
if isinstance(data, dict) and isinstance(data.get("n"), int):
|
|
545
|
+
return data["n"]
|
|
546
|
+
return None
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _gateway_unread_via_list(gateway: str, peer: str,
|
|
550
|
+
token: Optional[str]) -> Optional[int]:
|
|
551
|
+
"""Pre-count-endpoint fallback: parse the list query. Kept so a mixed
|
|
552
|
+
fleet (new client, old gateway) degrades to body reads, never to
|
|
553
|
+
blindness — the 404 is the only trigger, never a 403/401."""
|
|
554
|
+
data, status = _gateway_request(
|
|
555
|
+
gateway, "/messages",
|
|
556
|
+
{"to_node": peer, "unread_only": "true", "limit": 1}, token)
|
|
557
|
+
if status == 404:
|
|
510
558
|
return None
|
|
511
559
|
# Gateway shape varies — handle both list and {"messages": [...]} forms
|
|
512
560
|
if isinstance(data, list):
|
|
@@ -1874,6 +1922,37 @@ def _execstart_flags(args: argparse.Namespace) -> str:
|
|
|
1874
1922
|
return (" " + " ".join(parts)) if parts else ""
|
|
1875
1923
|
|
|
1876
1924
|
|
|
1925
|
+
def _watchdog_service_token_path() -> Path:
|
|
1926
|
+
"""Where the watchdog identity's token lives. Overridable for tests
|
|
1927
|
+
and unusual homes; never a unit file, never argv (card #1059)."""
|
|
1928
|
+
return Path(os.environ.get(
|
|
1929
|
+
"SWARPH_WATCHDOG_TOKEN_FILE",
|
|
1930
|
+
"~/.config/swarph/watchdog-service.token")).expanduser()
|
|
1931
|
+
|
|
1932
|
+
|
|
1933
|
+
def _read_service_token() -> Optional[str]:
|
|
1934
|
+
"""The watchdog identity's token from its file, or None (absent /
|
|
1935
|
+
unreadable / blank). The VALUE never hits logs — callers log only
|
|
1936
|
+
whether one was found."""
|
|
1937
|
+
try:
|
|
1938
|
+
tok = _watchdog_service_token_path().read_text(
|
|
1939
|
+
encoding="utf-8").strip()
|
|
1940
|
+
except OSError:
|
|
1941
|
+
return None
|
|
1942
|
+
return tok or None
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
def _write_service_token_file(path: Path, token: str) -> None:
|
|
1946
|
+
"""Write the token file 0600 (parents created). Refuses multiline
|
|
1947
|
+
values — an env file line cannot hold them, and silently writing
|
|
1948
|
+
half a credential is worse than failing."""
|
|
1949
|
+
if "\n" in token:
|
|
1950
|
+
raise ValueError("token contains a newline")
|
|
1951
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1952
|
+
path.write_text(token.strip() + "\n", encoding="utf-8")
|
|
1953
|
+
os.chmod(path, 0o600)
|
|
1954
|
+
|
|
1955
|
+
|
|
1877
1956
|
def run_install_service(args: argparse.Namespace) -> int:
|
|
1878
1957
|
"""Install systemd timer + service for periodic watchdog --check.
|
|
1879
1958
|
|
|
@@ -1950,6 +2029,25 @@ def run_install_service(args: argparse.Namespace) -> int:
|
|
|
1950
2029
|
1,
|
|
1951
2030
|
)
|
|
1952
2031
|
|
|
2032
|
+
# Card #1059/ruling_1332: template units run without the installer's
|
|
2033
|
+
# env, so the unread check reads None and A1 stays inert (exit 7,
|
|
2034
|
+
# opencode + cursor-lin measured live). The watchdog identity's
|
|
2035
|
+
# token lives in a 0600 token FILE (path via SWARPH_WATCHDOG_TOKEN_FILE,
|
|
2036
|
+
# default ~/.config/swarph/watchdog-service.token) — never in a unit,
|
|
2037
|
+
# never in argv, never in the EnvironmentFile default. The installer
|
|
2038
|
+
# writes it from its own env; rotation is re-running the install.
|
|
2039
|
+
install_token = os.environ.get("MESH_GATEWAY_TOKEN")
|
|
2040
|
+
token_path = _watchdog_service_token_path()
|
|
2041
|
+
if install_token is not None and "\n" in install_token:
|
|
2042
|
+
print("ERROR: MESH_GATEWAY_TOKEN contains a newline — refusing "
|
|
2043
|
+
"to write the token file.", file=sys.stderr)
|
|
2044
|
+
return 4
|
|
2045
|
+
if not install_token:
|
|
2046
|
+
print("WARNING: MESH_GATEWAY_TOKEN not in installer env — "
|
|
2047
|
+
"template runs will exit 7 (noop_unread_unknown) until the "
|
|
2048
|
+
f"token file exists at {token_path}; re-run --install-service "
|
|
2049
|
+
"with it set.", file=sys.stderr)
|
|
2050
|
+
|
|
1953
2051
|
targets = [
|
|
1954
2052
|
(_SYSTEMD_UNIT_DIR / service_name, service_content),
|
|
1955
2053
|
(_SYSTEMD_UNIT_DIR / timer_name, timer_content),
|
|
@@ -1970,6 +2068,9 @@ def run_install_service(args: argparse.Namespace) -> int:
|
|
|
1970
2068
|
for path, content in targets:
|
|
1971
2069
|
print(f"\n# would write {path}:", file=sys.stderr)
|
|
1972
2070
|
print(content, file=sys.stderr)
|
|
2071
|
+
if install_token:
|
|
2072
|
+
print(f"\n# would write {token_path} (0600, value not shown):",
|
|
2073
|
+
file=sys.stderr)
|
|
1973
2074
|
if legacy_note:
|
|
1974
2075
|
print(f"\n{legacy_note}", file=sys.stderr)
|
|
1975
2076
|
print(
|
|
@@ -1992,7 +2093,10 @@ def run_install_service(args: argparse.Namespace) -> int:
|
|
|
1992
2093
|
for path, content in targets:
|
|
1993
2094
|
path.write_text(content, encoding="utf-8")
|
|
1994
2095
|
print(f"wrote {path}", file=sys.stderr)
|
|
1995
|
-
|
|
2096
|
+
if install_token:
|
|
2097
|
+
_write_service_token_file(token_path, install_token)
|
|
2098
|
+
print(f"wrote {token_path} (0600)", file=sys.stderr)
|
|
2099
|
+
except (OSError, PermissionError, ValueError) as exc:
|
|
1996
2100
|
print(f"ERROR: failed to write unit files: {exc}", file=sys.stderr)
|
|
1997
2101
|
return 5
|
|
1998
2102
|
|
|
@@ -543,6 +543,7 @@ answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not comman
|
|
|
543
543
|
- `swarph protocol-handler` -- register `swarph://` as an OS URL-scheme handler.
|
|
544
544
|
- `swarph ratify` -- witness flip that admits an onboarded peer; takes the peer name and `--reason`.
|
|
545
545
|
- `swarph rights` -- RBAC rights over the gateway (companion of `swarph group`).
|
|
546
|
+
- `swarph roster` -- the gateway capacity table (card #1061): who builds, who reviews, quota, who is out until when. `swarph roster --peer <peer>`, `swarph roster set <peer> --roles build` (operators only).
|
|
546
547
|
- `swarph rowclear-hook` -- SessionStart hook (card #432): when `source` is `clear`, write the new session id into the role's `<role>.session-id` pin so the next spawn resumes the cleared session.
|
|
547
548
|
- `swarph scan` -- statically scan an artifact for dangerous patterns before publish.
|
|
548
549
|
- `swarph schedule` -- the gateway's scheduled events: create, list, get, enable, disable, delete, fire-now.
|
|
@@ -110,6 +110,7 @@ _VERB_HANDLERS: dict[str, str] = {
|
|
|
110
110
|
"board": "swarph_cli.commands.board.run_board",
|
|
111
111
|
"group": "swarph_cli.commands.group.run_group",
|
|
112
112
|
"rights": "swarph_cli.commands.group.run_rights",
|
|
113
|
+
"roster": "swarph_cli.commands.roster.run_roster",
|
|
113
114
|
"mcp-server": "swarph_cli.commands.mcp_server.run_mcp_server",
|
|
114
115
|
"compress": "swarph_cli.commands.compress.run_compress",
|
|
115
116
|
"cell": "swarph_cli.commands.cell.run_cell",
|
|
@@ -6,6 +6,10 @@ when the cell holds no live row, the pane reads idle, and no DM is owed.
|
|
|
6
6
|
type nothing), or ``live``. v1 opt-in is drop-on-meta-edge only: a cell
|
|
7
7
|
that owns a project keeps its context (design_1007 / optin_owners).
|
|
8
8
|
|
|
9
|
+
v1.1 (#1362): a taken row whose step needs are UNMET is waiting, not
|
|
10
|
+
working — it does not count as live. Needs state comes from the card's
|
|
11
|
+
step graph; anything unreadable about it fails closed (no clear).
|
|
12
|
+
|
|
9
13
|
A ``/clear`` mints a new Claude session id. The SessionStart hook with
|
|
10
14
|
``source=clear`` writes that id into the role's ``<role>.session-id``
|
|
11
15
|
pin, so the next spawn resumes the cleared session instead of the long
|
|
@@ -95,11 +99,62 @@ def _dm_owed(state) -> bool:
|
|
|
95
99
|
return False
|
|
96
100
|
|
|
97
101
|
|
|
102
|
+
def _step_needs_met(state, row: dict, http_get: Callable,
|
|
103
|
+
graphs: dict) -> Optional[bool]:
|
|
104
|
+
"""Is this open row's work actionable (True), waiting (False), or
|
|
105
|
+
unevaluable (None)?
|
|
106
|
+
|
|
107
|
+
A row with no step has no needs to unmeet (ruling_1236: still live).
|
|
108
|
+
Otherwise the card's step graph (GET /board/cards/{id}/graph) names the
|
|
109
|
+
step's needs with a satisfied flag — every need satisfied means the
|
|
110
|
+
holder can act; one unsatisfied need means the cell is between rows.
|
|
111
|
+
Anything unreadable — no card id, a failed graph fetch, a missing step
|
|
112
|
+
entry, a needs entry without a boolean satisfied flag — is None: an
|
|
113
|
+
unevaluable row must not look like an empty cell (fail closed, #1362).
|
|
114
|
+
One graph fetch per card per poll; the same http_get the board read uses.
|
|
115
|
+
"""
|
|
116
|
+
step = row.get("step")
|
|
117
|
+
if not step:
|
|
118
|
+
return True
|
|
119
|
+
card_id = row.get("card_id")
|
|
120
|
+
if not isinstance(card_id, int) or isinstance(card_id, bool):
|
|
121
|
+
return None
|
|
122
|
+
if card_id not in graphs:
|
|
123
|
+
url = f"{state.gateway}/board/cards/{card_id}/graph"
|
|
124
|
+
try:
|
|
125
|
+
status, body = http_get(url, state.token)
|
|
126
|
+
except Exception:
|
|
127
|
+
return None
|
|
128
|
+
if status != 200 or not isinstance(body, dict):
|
|
129
|
+
return None
|
|
130
|
+
steps = body.get("steps")
|
|
131
|
+
if not isinstance(steps, list):
|
|
132
|
+
return None
|
|
133
|
+
graphs[card_id] = steps
|
|
134
|
+
for entry in graphs[card_id]:
|
|
135
|
+
if isinstance(entry, dict) and entry.get("step") == step:
|
|
136
|
+
needs = entry.get("needs")
|
|
137
|
+
if not isinstance(needs, list):
|
|
138
|
+
return None
|
|
139
|
+
for need in needs:
|
|
140
|
+
if not isinstance(need, dict) or not isinstance(
|
|
141
|
+
need.get("satisfied"), bool):
|
|
142
|
+
return None
|
|
143
|
+
if not need["satisfied"]:
|
|
144
|
+
return False
|
|
145
|
+
return True
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
|
|
98
149
|
def _live_row_count(state, http_get: Callable) -> Optional[int]:
|
|
99
|
-
"""How many
|
|
150
|
+
"""How many ACTIONABLE rows this cell holds, or None if unread.
|
|
100
151
|
|
|
101
152
|
None fails closed: an unreadable board must not look like an empty
|
|
102
153
|
one, or a live row we failed to see gets a ``/clear``.
|
|
154
|
+
|
|
155
|
+
v1.1 (#1362): an open row whose step needs are unmet is waiting, not
|
|
156
|
+
working — it does not count. An open row with no step, or whose step
|
|
157
|
+
needs nothing, is still live (ruling_1236 unchanged).
|
|
103
158
|
"""
|
|
104
159
|
url = f"{state.gateway}/board/obligations?holder={state.self_name}"
|
|
105
160
|
try:
|
|
@@ -111,15 +166,21 @@ def _live_row_count(state, http_get: Callable) -> Optional[int]:
|
|
|
111
166
|
rows = body.get("obligations")
|
|
112
167
|
if not isinstance(rows, list):
|
|
113
168
|
return None
|
|
169
|
+
graphs: dict = {}
|
|
114
170
|
live = 0
|
|
115
171
|
for row in rows:
|
|
116
172
|
if not isinstance(row, dict):
|
|
117
173
|
return None
|
|
118
174
|
status = str(row.get("status") or "")
|
|
119
|
-
if status
|
|
120
|
-
|
|
121
|
-
|
|
175
|
+
if status in _FINISHED:
|
|
176
|
+
continue
|
|
177
|
+
if status != _LIVE:
|
|
122
178
|
return None
|
|
179
|
+
met = _step_needs_met(state, row, http_get, graphs)
|
|
180
|
+
if met is None:
|
|
181
|
+
return None
|
|
182
|
+
if met:
|
|
183
|
+
live += 1
|
|
123
184
|
return live
|
|
124
185
|
|
|
125
186
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.9
|
|
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
|
|
@@ -110,6 +110,7 @@ src/swarph_cli/commands/peer_reply_drain.py
|
|
|
110
110
|
src/swarph_cli/commands/postcompact_hook_output.py
|
|
111
111
|
src/swarph_cli/commands/protocol_handler.py
|
|
112
112
|
src/swarph_cli/commands/ratify.py
|
|
113
|
+
src/swarph_cli/commands/roster.py
|
|
113
114
|
src/swarph_cli/commands/rowclear_hook.py
|
|
114
115
|
src/swarph_cli/commands/schedule.py
|
|
115
116
|
src/swarph_cli/commands/security.py
|
|
@@ -195,6 +196,8 @@ tests/test_1013_spawn_channel.py
|
|
|
195
196
|
tests/test_1017_muse_tui.py
|
|
196
197
|
tests/test_1050_surrogate_wake.py
|
|
197
198
|
tests/test_1052_opencode_deaf.py
|
|
199
|
+
tests/test_1059_watchdog_unread_env.py
|
|
200
|
+
tests/test_1061_roster_cli.py
|
|
198
201
|
tests/test_124_register_merge.py
|
|
199
202
|
tests/test_126_pending_from_complete.py
|
|
200
203
|
tests/test_132_ensure_monitor_state_dir.py
|