swarph-cli 0.72.8__tar.gz → 0.72.10__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.8/src/swarph_cli.egg-info → swarph_cli-0.72.10}/PKG-INFO +1 -1
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/pyproject.toml +1 -1
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/__init__.py +1 -1
- swarph_cli-0.72.10/src/swarph_cli/commands/roster.py +237 -0
- swarph_cli-0.72.10/src/swarph_cli/commands/sweep.py +413 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/guide/GUIDE.md +2 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/main.py +2 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/rowclear.py +65 -4
- swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.service +15 -0
- swarph_cli-0.72.10/src/swarph_cli/systemd/swarph-stuck-sweep.timer +14 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/SOURCES.txt +7 -0
- swarph_cli-0.72.10/tests/test_1061_roster_cli.py +137 -0
- swarph_cli-0.72.10/tests/test_1064_stuck_sweep.py +332 -0
- swarph_cli-0.72.10/tests/test_1065_release.py +295 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_432_rowclear.py +165 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_sidecar.py +40 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/LICENSE +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/README.md +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/setup.cfg +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_main.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_security.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.8 → swarph_cli-0.72.10}/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.10
|
|
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.10"
|
|
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
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
"""``swarph sweep`` — the card-#1064 stuck-work sweep, one daily shot.
|
|
2
|
+
|
|
3
|
+
Finds (a) open/taken rows blocked >=48h on an unmoving dependency and (b)
|
|
4
|
+
approved-or-old open PRs with no obligation row naming them; DMs each
|
|
5
|
+
finding's BLOCKING holder once per 24h (never the waiter) and appends one
|
|
6
|
+
line per finding to the nightly watchtower report file. Never closes or
|
|
7
|
+
reassigns: the only board write is the DM itself (POST /messages).
|
|
8
|
+
|
|
9
|
+
swarph sweep [--dry-run] [--repos R1,R2] [--state-file P] [--report-file P]
|
|
10
|
+
[--as SELF] [--gateway URL] [--token-file F] [--json]
|
|
11
|
+
|
|
12
|
+
--dry-run reads the live board and GitHub, prints the findings, and writes
|
|
13
|
+
nothing (no DMs, no state, no report) — the rehearsal shape.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
|
+
|
|
25
|
+
_STUCK_H = 48
|
|
26
|
+
_RESEND_H = 24
|
|
27
|
+
|
|
28
|
+
_DEFAULT_REPOS = (
|
|
29
|
+
"BrainSurfing-tech/swarph-cli",
|
|
30
|
+
"BrainSurfing-tech/mesh-gateway",
|
|
31
|
+
"BrainSurfing-tech/swarph-codegraph-fed",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_PR_URL_RE = re.compile(
|
|
35
|
+
r"github\.com/([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)/pull/(\d+)",
|
|
36
|
+
re.IGNORECASE)
|
|
37
|
+
_PR_NUM_RE = re.compile(r"\bPR\s*#?(\d+)\b", re.IGNORECASE)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _parse_ts(v):
|
|
41
|
+
"""Tolerant ISO parse -> epoch seconds, or None. Never raises: an
|
|
42
|
+
unparseable timestamp is unevaluable, not zero."""
|
|
43
|
+
if not v:
|
|
44
|
+
return None
|
|
45
|
+
try:
|
|
46
|
+
dt = datetime.fromisoformat(str(v).replace("Z", "+00:00"))
|
|
47
|
+
except (TypeError, ValueError):
|
|
48
|
+
return None
|
|
49
|
+
try:
|
|
50
|
+
return dt.timestamp()
|
|
51
|
+
except (TypeError, ValueError, OverflowError, OSError):
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _age_days(now, ts):
|
|
56
|
+
return round((now - ts) / 86400)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _row_last_change(row):
|
|
60
|
+
for k in ("closed_at", "taken_at", "created_at"):
|
|
61
|
+
ts = _parse_ts(row.get(k))
|
|
62
|
+
if ts is not None:
|
|
63
|
+
return ts
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _named_prs(rows):
|
|
68
|
+
"""(repo-or-None, number) pairs obligation rows name: full URLs match
|
|
69
|
+
exactly; bare PR numbers match by number across tracked repos."""
|
|
70
|
+
named = set()
|
|
71
|
+
for row in rows:
|
|
72
|
+
if not isinstance(row, dict):
|
|
73
|
+
continue
|
|
74
|
+
blob = " ".join(
|
|
75
|
+
str(row.get(k) or "") for k in ("accept", "close_evidence"))
|
|
76
|
+
for m in _PR_URL_RE.finditer(blob):
|
|
77
|
+
try:
|
|
78
|
+
named.add((m.group(1).lower(), int(m.group(2))))
|
|
79
|
+
except ValueError:
|
|
80
|
+
continue
|
|
81
|
+
for m in _PR_NUM_RE.finditer(blob):
|
|
82
|
+
try:
|
|
83
|
+
named.add((None, int(m.group(1))))
|
|
84
|
+
except ValueError:
|
|
85
|
+
continue
|
|
86
|
+
return named
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _graph_steps(http_get, gateway, token, card_id, cache):
|
|
90
|
+
if card_id in cache:
|
|
91
|
+
return cache[card_id]
|
|
92
|
+
try:
|
|
93
|
+
status, body = http_get(
|
|
94
|
+
f"{gateway}/board/cards/{card_id}/graph", token)
|
|
95
|
+
except Exception:
|
|
96
|
+
return None
|
|
97
|
+
if status != 200 or not isinstance(body, dict):
|
|
98
|
+
return None
|
|
99
|
+
steps = body.get("steps")
|
|
100
|
+
if not isinstance(steps, list):
|
|
101
|
+
return None
|
|
102
|
+
cache[card_id] = steps
|
|
103
|
+
return steps
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _row_findings(rows, by_id, http_get, gateway, token, now):
|
|
107
|
+
"""Kind (a): one finding per (open taken waiter, unsatisfied old need)."""
|
|
108
|
+
out = []
|
|
109
|
+
cache = {}
|
|
110
|
+
for w in rows:
|
|
111
|
+
if w.get("status") != "open" or not w.get("taken_at"):
|
|
112
|
+
continue
|
|
113
|
+
step = w.get("step")
|
|
114
|
+
if not step:
|
|
115
|
+
continue
|
|
116
|
+
card_id = w.get("card_id")
|
|
117
|
+
if not isinstance(card_id, int) or isinstance(card_id, bool):
|
|
118
|
+
continue
|
|
119
|
+
steps = _graph_steps(http_get, gateway, token, card_id, cache)
|
|
120
|
+
if steps is None:
|
|
121
|
+
continue
|
|
122
|
+
entry = next((e for e in steps
|
|
123
|
+
if isinstance(e, dict) and e.get("step") == step), None)
|
|
124
|
+
if entry is None:
|
|
125
|
+
continue
|
|
126
|
+
needs = entry.get("needs")
|
|
127
|
+
if not isinstance(needs, list):
|
|
128
|
+
continue
|
|
129
|
+
for need in needs:
|
|
130
|
+
if not isinstance(need, dict):
|
|
131
|
+
continue
|
|
132
|
+
sat = need.get("satisfied")
|
|
133
|
+
if sat is True:
|
|
134
|
+
continue
|
|
135
|
+
if sat is not False:
|
|
136
|
+
continue # unevaluable need names no finding
|
|
137
|
+
rid = need.get("row_id")
|
|
138
|
+
dep = by_id.get(rid)
|
|
139
|
+
if not isinstance(dep, dict):
|
|
140
|
+
continue
|
|
141
|
+
if dep.get("status") == "closed":
|
|
142
|
+
continue # terminal: satisfied or a rework case, not unmoving
|
|
143
|
+
changed = _row_last_change(dep)
|
|
144
|
+
if changed is None or now - changed < _STUCK_H * 3600:
|
|
145
|
+
continue
|
|
146
|
+
holder = dep.get("holder")
|
|
147
|
+
blocker = holder if holder and dep.get("holder_known", True) else None
|
|
148
|
+
out.append({
|
|
149
|
+
"kind": "row",
|
|
150
|
+
"waiter_id": w.get("id"),
|
|
151
|
+
"waiter": w.get("holder"),
|
|
152
|
+
"waiter_step": step,
|
|
153
|
+
"card_id": card_id,
|
|
154
|
+
"need": need.get("step"),
|
|
155
|
+
"need_id": rid,
|
|
156
|
+
"blocker": blocker,
|
|
157
|
+
"age_days": _age_days(now, changed),
|
|
158
|
+
})
|
|
159
|
+
return out
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _pr_findings(named, gh_prs, repos, peers, now):
|
|
163
|
+
"""Kind (b): approved-or-old open PRs no row names. An author with no
|
|
164
|
+
mesh peer is report-only — the CLI must not invent a recipient."""
|
|
165
|
+
out = []
|
|
166
|
+
for repo in repos:
|
|
167
|
+
try:
|
|
168
|
+
prs = gh_prs(repo)
|
|
169
|
+
except Exception:
|
|
170
|
+
continue
|
|
171
|
+
if not isinstance(prs, list):
|
|
172
|
+
continue
|
|
173
|
+
for pr in prs:
|
|
174
|
+
if not isinstance(pr, dict):
|
|
175
|
+
continue
|
|
176
|
+
try:
|
|
177
|
+
number = int(pr.get("number"))
|
|
178
|
+
except (TypeError, ValueError):
|
|
179
|
+
continue
|
|
180
|
+
if (repo.lower(), number) in named or (None, number) in named:
|
|
181
|
+
continue
|
|
182
|
+
approved = pr.get("reviewDecision") == "APPROVED"
|
|
183
|
+
created = _parse_ts(pr.get("createdAt"))
|
|
184
|
+
old = created is not None and now - created >= _STUCK_H * 3600
|
|
185
|
+
if not approved and not old:
|
|
186
|
+
continue
|
|
187
|
+
author = pr.get("author") or {}
|
|
188
|
+
login = author.get("login") if isinstance(author, dict) else None
|
|
189
|
+
# blocker names the responsible party even when they have no
|
|
190
|
+
# mesh peer — the SEND step DMs only peers (report-only else).
|
|
191
|
+
out.append({
|
|
192
|
+
"kind": "pr",
|
|
193
|
+
"repo": repo,
|
|
194
|
+
"number": number,
|
|
195
|
+
"title": pr.get("title") or "",
|
|
196
|
+
"url": pr.get("url") or "",
|
|
197
|
+
"approved": approved,
|
|
198
|
+
"age_days": _age_days(now, created) if created else None,
|
|
199
|
+
"blocker": login,
|
|
200
|
+
})
|
|
201
|
+
return out
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _finding_key(f):
|
|
205
|
+
if f["kind"] == "row":
|
|
206
|
+
return f"row:{f['waiter_id']}:{f['need']}"
|
|
207
|
+
return f"pr:{f['repo']}#{f['number']}"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _report_line(now_iso, f):
|
|
211
|
+
if f["kind"] == "row":
|
|
212
|
+
return (f"{now_iso} STUCK row waiter=#{f['waiter_id']}({f['waiter']}) "
|
|
213
|
+
f"step={f['waiter_step']} card=#{f['card_id']} "
|
|
214
|
+
f"blocked-by=#{f['need_id']}({f['blocker']}) "
|
|
215
|
+
f"need={f['need']} need-age={f['age_days']}d")
|
|
216
|
+
how = "approved" if f["approved"] else f"age={f['age_days']}d"
|
|
217
|
+
return (f"{now_iso} STUCK pr {f['repo']}#{f['number']} "
|
|
218
|
+
f"'{f['title']}' ({how}, no review row) author={f['blocker']}")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _dm_content(f):
|
|
222
|
+
if f["kind"] == "row":
|
|
223
|
+
return (f"STUCK-WORK SWEEP: row #{f['waiter_id']} ({f['waiter']}, "
|
|
224
|
+
f"{f['waiter_step']}, card #{f['card_id']}) has waited on "
|
|
225
|
+
f"your row #{f['need_id']} ({f['need']}, unchanged "
|
|
226
|
+
f"{f['age_days']}d). Please move it, fail it, or say what "
|
|
227
|
+
f"it waits on.")
|
|
228
|
+
state = "approved" if f["approved"] else "open %sd" % (f['age_days'],)
|
|
229
|
+
return (f"STUCK-WORK SWEEP: your PR {f['repo']}#{f['number']} "
|
|
230
|
+
f"'{f['title']}' ({f['url']}) is {state} "
|
|
231
|
+
f"with no review row naming it. Please land it or ask for review.")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def run_sweep(*, gateway, token, sender, repos, state_file, report_file,
|
|
235
|
+
now, http_get, http_post, gh_prs, dry_run=False):
|
|
236
|
+
"""One sweep. Returns (rc, findings): rc 1 only when the board itself is
|
|
237
|
+
unreadable (nothing evaluable); findings are the SENT (or dry-run, would
|
|
238
|
+
send) findings. Only ever writes DMs + the two local files."""
|
|
239
|
+
gateway = gateway.rstrip("/")
|
|
240
|
+
try:
|
|
241
|
+
status, body = http_get(f"{gateway}/board/obligations", token)
|
|
242
|
+
except Exception:
|
|
243
|
+
status, body = None, None
|
|
244
|
+
if status != 200 or not isinstance(body, dict) or not isinstance(
|
|
245
|
+
body.get("obligations"), list):
|
|
246
|
+
print("swarph sweep: board unreadable — no findings, nothing sent",
|
|
247
|
+
file=sys.stderr)
|
|
248
|
+
return 1, []
|
|
249
|
+
rows = [r for r in body["obligations"] if isinstance(r, dict)]
|
|
250
|
+
by_id = {r.get("id"): r for r in rows
|
|
251
|
+
if isinstance(r.get("id"), int)}
|
|
252
|
+
try:
|
|
253
|
+
pstatus, pbody = http_get(f"{gateway}/peers", token)
|
|
254
|
+
peers = {p["name"] for p in pbody.get("peers", [])
|
|
255
|
+
if isinstance(p, dict) and p.get("name")}
|
|
256
|
+
if pstatus != 200:
|
|
257
|
+
peers = set()
|
|
258
|
+
except Exception:
|
|
259
|
+
peers = set()
|
|
260
|
+
|
|
261
|
+
findings = _row_findings(rows, by_id, http_get, gateway, token, now)
|
|
262
|
+
findings += _pr_findings(_named_prs(rows), gh_prs, repos, peers, now)
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
with open(state_file, encoding="utf-8") as fp:
|
|
266
|
+
sent = json.load(fp)
|
|
267
|
+
sent = sent if isinstance(sent, dict) else {}
|
|
268
|
+
except (OSError, ValueError):
|
|
269
|
+
sent = {}
|
|
270
|
+
now_iso = datetime.fromtimestamp(now, timezone.utc).isoformat()
|
|
271
|
+
fresh, updates = [], {}
|
|
272
|
+
for f in findings:
|
|
273
|
+
key = _finding_key(f)
|
|
274
|
+
try:
|
|
275
|
+
last = _parse_ts(sent.get(key))
|
|
276
|
+
except Exception:
|
|
277
|
+
last = None
|
|
278
|
+
if last is not None and now - last < _RESEND_H * 3600:
|
|
279
|
+
continue
|
|
280
|
+
fresh.append(f)
|
|
281
|
+
updates[key] = now_iso
|
|
282
|
+
|
|
283
|
+
if dry_run:
|
|
284
|
+
for f in fresh:
|
|
285
|
+
print("DRY " + _report_line(now_iso, f))
|
|
286
|
+
return 0, fresh
|
|
287
|
+
|
|
288
|
+
for f in fresh:
|
|
289
|
+
# DM only a blocker with a mesh peer: pinging the waiter instead is
|
|
290
|
+
# the FAIL, and inventing a recipient for a gh login is its cousin.
|
|
291
|
+
if f["blocker"] is not None and f["blocker"] in peers:
|
|
292
|
+
status, payload = http_post(
|
|
293
|
+
f"{gateway}/messages",
|
|
294
|
+
{"from_node": sender, "to_node": f["blocker"],
|
|
295
|
+
"kind": "fyi", "content": _dm_content(f)},
|
|
296
|
+
token)
|
|
297
|
+
if status < 200 or status >= 300:
|
|
298
|
+
detail = (payload.get("detail", "<gateway error>")
|
|
299
|
+
if isinstance(payload, dict) else "<gateway error>")
|
|
300
|
+
print(f"swarph sweep: DM to {f['blocker']} failed "
|
|
301
|
+
f"{status}: {detail}", file=sys.stderr)
|
|
302
|
+
continue
|
|
303
|
+
line = _report_line(now_iso, f)
|
|
304
|
+
print(line)
|
|
305
|
+
try:
|
|
306
|
+
with open(report_file, "a", encoding="utf-8") as fp:
|
|
307
|
+
fp.write(line + "\n")
|
|
308
|
+
except OSError as exc:
|
|
309
|
+
print(f"swarph sweep: report append failed: {exc}",
|
|
310
|
+
file=sys.stderr)
|
|
311
|
+
if updates:
|
|
312
|
+
sent.update(updates)
|
|
313
|
+
try:
|
|
314
|
+
with open(state_file, "w", encoding="utf-8") as fp:
|
|
315
|
+
json.dump(sent, fp)
|
|
316
|
+
except OSError as exc:
|
|
317
|
+
print(f"swarph sweep: state write failed: {exc}", file=sys.stderr)
|
|
318
|
+
return 0, fresh
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _gh_prs(repo):
|
|
322
|
+
"""Live gh transport: open PRs with the fields the matcher needs, or
|
|
323
|
+
None when gh is unusable (that repo's findings are skipped, not
|
|
324
|
+
invented)."""
|
|
325
|
+
try:
|
|
326
|
+
p = subprocess.run(
|
|
327
|
+
["gh", "pr", "list", "--repo", repo, "--state", "open",
|
|
328
|
+
"--limit", "100", "--json",
|
|
329
|
+
"number,title,createdAt,reviewDecision,url,author"],
|
|
330
|
+
capture_output=True, text=True, encoding="utf-8",
|
|
331
|
+
errors="replace", timeout=60)
|
|
332
|
+
except (OSError, subprocess.SubprocessError):
|
|
333
|
+
return None
|
|
334
|
+
if p.returncode != 0:
|
|
335
|
+
return None
|
|
336
|
+
try:
|
|
337
|
+
data = json.loads(p.stdout or "[]")
|
|
338
|
+
except ValueError:
|
|
339
|
+
return None
|
|
340
|
+
return data if isinstance(data, list) else None
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _default_state_file():
|
|
344
|
+
xdg = os.environ.get("XDG_STATE_HOME", "").strip()
|
|
345
|
+
root = xdg if xdg else os.path.join(os.path.expanduser("~"), ".local", "state")
|
|
346
|
+
return os.path.join(root, "swarph", "stuck-sweep.json")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _build_parser():
|
|
350
|
+
p = argparse.ArgumentParser(
|
|
351
|
+
prog="swarph sweep",
|
|
352
|
+
description="Daily stuck-work sweep: blocked rows + unreviewed PRs "
|
|
353
|
+
"(card #1064). DMs blockers, never closes/reassigns.")
|
|
354
|
+
p.add_argument("--dry-run", action="store_true",
|
|
355
|
+
help="print findings; send/record/write nothing")
|
|
356
|
+
p.add_argument("--repos", default=None,
|
|
357
|
+
help="comma-separated owner/repo list (default "
|
|
358
|
+
"$SWARPH_SWEEP_REPOS or the fleet three)")
|
|
359
|
+
p.add_argument("--state-file", default=None)
|
|
360
|
+
p.add_argument("--report-file", default=None,
|
|
361
|
+
help="nightly watchtower report path "
|
|
362
|
+
"(default $SWARPH_SWEEP_REPORT or state-adjacent log)")
|
|
363
|
+
p.add_argument("--json", action="store_true")
|
|
364
|
+
p.add_argument("--as", dest="self_name", default=None,
|
|
365
|
+
help="DM sender identity")
|
|
366
|
+
from swarph_cli.gateway_default import env_gateway
|
|
367
|
+
p.add_argument("--gateway", default=env_gateway())
|
|
368
|
+
p.add_argument("--token-file", default=None)
|
|
369
|
+
return p
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def run_sweep_cmd(argv):
|
|
373
|
+
import time as _time
|
|
374
|
+
from swarph_cli.commands import mesh as _mesh
|
|
375
|
+
from swarph_cli.tokens import resolve_token
|
|
376
|
+
args = _build_parser().parse_args(argv)
|
|
377
|
+
try:
|
|
378
|
+
# #872 doctrine: an automated DM must name its sender. Dry-run sends
|
|
379
|
+
# nothing and needs no identity.
|
|
380
|
+
if not args.self_name and not args.dry_run:
|
|
381
|
+
print("swarph sweep: --as a sender is required (automated DMs "
|
|
382
|
+
"must name their sender; --dry-run needs no identity)",
|
|
383
|
+
file=sys.stderr)
|
|
384
|
+
return 2
|
|
385
|
+
resolution = resolve_token(
|
|
386
|
+
args.self_name, args.token_file,
|
|
387
|
+
identity_is_explicit=bool(args.self_name))
|
|
388
|
+
if resolution is None:
|
|
389
|
+
print("swarph sweep: no token (set MESH_GATEWAY_TOKEN, pass "
|
|
390
|
+
"--token-file, or --as a sender)", file=sys.stderr)
|
|
391
|
+
return 2
|
|
392
|
+
sender = args.self_name or "(dry-run)"
|
|
393
|
+
repos = ([r.strip() for r in args.repos.split(",") if r.strip()]
|
|
394
|
+
if args.repos else
|
|
395
|
+
[r.strip() for r in os.environ.get(
|
|
396
|
+
"SWARPH_SWEEP_REPOS", ",".join(_DEFAULT_REPOS)
|
|
397
|
+
).split(",") if r.strip()])
|
|
398
|
+
state_file = args.state_file or os.environ.get(
|
|
399
|
+
"SWARPH_SWEEP_STATE", _default_state_file())
|
|
400
|
+
report_file = (args.report_file or os.environ.get("SWARPH_SWEEP_REPORT")
|
|
401
|
+
or os.path.splitext(state_file)[0] + ".log")
|
|
402
|
+
rc, findings = run_sweep(
|
|
403
|
+
gateway=args.gateway, token=resolution.token, sender=sender,
|
|
404
|
+
repos=repos, state_file=state_file, report_file=report_file,
|
|
405
|
+
now=_time.time(), http_get=_mesh._http_get_json,
|
|
406
|
+
http_post=_mesh._post_json, gh_prs=_gh_prs,
|
|
407
|
+
dry_run=args.dry_run)
|
|
408
|
+
if args.json:
|
|
409
|
+
print(json.dumps(findings, indent=2))
|
|
410
|
+
return rc
|
|
411
|
+
except RuntimeError as exc:
|
|
412
|
+
print(f"swarph sweep: {exc}", file=sys.stderr)
|
|
413
|
+
return 2
|