swarph-cli 0.53.2__tar.gz → 0.53.3__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.53.2/src/swarph_cli.egg-info → swarph_cli-0.53.3}/PKG-INFO +1 -1
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/pyproject.toml +1 -1
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/board.py +68 -7
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/highlight.py +66 -13
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/monitor.py +92 -26
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/watchdog.py +11 -0
- swarph_cli-0.53.3/src/swarph_cli/orphan_daemons.py +448 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_654_reexec.py +32 -3
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_board_command.py +57 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_highlight_command.py +63 -2
- swarph_cli-0.53.3/tests/test_orphan_daemons.py +238 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/LICENSE +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/README.md +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/setup.cfg +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_group_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_import_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_init_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_main.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_security.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_service_app.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_service_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_version_command.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.53.2 → swarph_cli-0.53.3}/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.53.
|
|
3
|
+
Version: 0.53.3
|
|
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.53.
|
|
7
|
+
version = "0.53.3"
|
|
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" }
|
|
@@ -199,16 +199,77 @@ def _cards_of(data):
|
|
|
199
199
|
return data.get("cards", []) if isinstance(data, dict) else (data or [])
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
def _rel_due(days_until) -> str:
|
|
203
|
+
"""Spell the relative age — a bare date makes the reader compute (#145)."""
|
|
204
|
+
if days_until is None:
|
|
205
|
+
return "?"
|
|
206
|
+
n = int(days_until)
|
|
207
|
+
if n < 0:
|
|
208
|
+
d = -n
|
|
209
|
+
return "1 day ago" if d == 1 else f"{d} days ago"
|
|
210
|
+
if n == 0:
|
|
211
|
+
return "today"
|
|
212
|
+
return "in 1 day" if n == 1 else f"in {n} days"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _format_due_head(data) -> list[str]:
|
|
216
|
+
"""Render the list endpoint's `due` summary + dated cards at the head.
|
|
217
|
+
|
|
218
|
+
Built ONLY from the LIST response (#661: GET /board/cards/{id} returns
|
|
219
|
+
due_state=None even when due_at is set — a single-card readout would
|
|
220
|
+
report zero overdue forever with no error).
|
|
221
|
+
"""
|
|
222
|
+
if not isinstance(data, dict):
|
|
223
|
+
return []
|
|
224
|
+
due = data.get("due")
|
|
225
|
+
if not isinstance(due, dict):
|
|
226
|
+
return []
|
|
227
|
+
lines = [
|
|
228
|
+
f"due: overdue={due.get('overdue', 0)} today={due.get('today', 0)} "
|
|
229
|
+
f"upcoming={due.get('upcoming', 0)} undated={due.get('undated', 0)}"
|
|
230
|
+
]
|
|
231
|
+
# Prefer overdue, then today, then upcoming — sorted by days_until ascending
|
|
232
|
+
# so the most overdue sits first. Pull rows from the cards array (the due
|
|
233
|
+
# block itself is counts only).
|
|
234
|
+
dated = [c for c in _cards_of(data)
|
|
235
|
+
if c.get("due_state") in ("overdue", "today", "upcoming")]
|
|
236
|
+
order = {"overdue": 0, "today": 1, "upcoming": 2}
|
|
237
|
+
dated.sort(key=lambda c: (
|
|
238
|
+
order.get(c.get("due_state"), 9),
|
|
239
|
+
c.get("days_until") is None,
|
|
240
|
+
c.get("days_until") if c.get("days_until") is not None else 0,
|
|
241
|
+
))
|
|
242
|
+
for c in dated:
|
|
243
|
+
title = _s(c.get("title") or "")
|
|
244
|
+
if len(title) > 60:
|
|
245
|
+
title = title[:57] + "..."
|
|
246
|
+
lines.append(
|
|
247
|
+
f" #{c.get('id')} {c.get('due_state'):<9} "
|
|
248
|
+
f"{_rel_due(c.get('days_until')):<14} {title}"
|
|
249
|
+
)
|
|
250
|
+
if not dated and (due.get("overdue") or due.get("today") or due.get("upcoming")):
|
|
251
|
+
# Counts say something is dated but this page's cards don't carry them
|
|
252
|
+
# (filtered list / pagination). Say so — silent empty is the #145 lie.
|
|
253
|
+
lines.append(" (dated cards not in this page — widen the list filter)")
|
|
254
|
+
elif not dated:
|
|
255
|
+
lines.append(" (none dated in this list)")
|
|
256
|
+
return lines
|
|
257
|
+
|
|
258
|
+
|
|
202
259
|
def _format_cards(data) -> str:
|
|
260
|
+
head = _format_due_head(data)
|
|
203
261
|
rows = _cards_of(data)
|
|
204
262
|
if not rows:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
263
|
+
body = ["(no cards)"]
|
|
264
|
+
else:
|
|
265
|
+
body = [f"{'ID':>4} {'STAGE':<9} {'PRJ':>4} {'AI²':<3} TITLE"]
|
|
266
|
+
for c in rows:
|
|
267
|
+
ai2 = "AI²" if c.get("ai2") else ""
|
|
268
|
+
body.append(f"{c.get('id',''):>4} {c.get('stage',''):<9} "
|
|
269
|
+
f"{c.get('project_id',''):>4} {ai2:<3} {_s(c.get('title'))}")
|
|
270
|
+
if head:
|
|
271
|
+
return "\n".join(head + [""] + body)
|
|
272
|
+
return "\n".join(body)
|
|
212
273
|
|
|
213
274
|
|
|
214
275
|
def _format_card(card: dict) -> str:
|
|
@@ -12,7 +12,9 @@ Usage:
|
|
|
12
12
|
|
|
13
13
|
Timeline dir: ``--timeline-dir`` > ``SWARPH_TIMELINE_DIR`` > ``~/.swarph/timeline``
|
|
14
14
|
(auto-created + ``git init``'d + given a ``merge=union`` .gitattributes if absent).
|
|
15
|
-
Cell identity: ``--cell`` > ``
|
|
15
|
+
Cell identity (#657 / house order #332): ``--cell`` > ``SWARPH_SELF`` >
|
|
16
|
+
``SWARPH_CELL`` > git user.name > hostname. SELF outranks CELL — psmux leaks
|
|
17
|
+
``SWARPH_CELL`` (#538), so CELL-first posts under another cell's name.
|
|
16
18
|
Push: only if an ``origin`` remote exists and ``--no-push`` is not set; otherwise
|
|
17
19
|
the highlight is committed locally (solo/offline timelines work).
|
|
18
20
|
"""
|
|
@@ -44,8 +46,47 @@ def _resolve_gateway(arg: str | None) -> str:
|
|
|
44
46
|
or "").strip()
|
|
45
47
|
|
|
46
48
|
|
|
49
|
+
def _peer_token_near_match(name: str) -> str | None:
|
|
50
|
+
"""Case-insensitive near-match against on-disk peer tokens (#657 / #510 shape).
|
|
51
|
+
|
|
52
|
+
When the resolved cell name has no credential, suggesting `mesh register`
|
|
53
|
+
mints a DUPLICATE peer if the real name differs only in case (Lab-ovh vs
|
|
54
|
+
lab-ovh). Prefer "did you mean …" over that advice.
|
|
55
|
+
"""
|
|
56
|
+
root = Path.home() / ".config" / "swarph"
|
|
57
|
+
if not root.is_dir():
|
|
58
|
+
return None
|
|
59
|
+
want = name.casefold()
|
|
60
|
+
for path in sorted(root.glob("*.peer_token")):
|
|
61
|
+
peer = path.name[: -len(".peer_token")]
|
|
62
|
+
if peer.casefold() == want and peer != name:
|
|
63
|
+
return peer
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _credential_error(cell: str, source: str, exc: RuntimeError) -> str:
|
|
68
|
+
"""Name WHERE the cell identity came from; never push register on a near-match."""
|
|
69
|
+
near = _peer_token_near_match(cell)
|
|
70
|
+
header = (
|
|
71
|
+
f"swarph highlight: cannot resolve a mesh credential for {cell!r} "
|
|
72
|
+
f"(resolved from {source})"
|
|
73
|
+
)
|
|
74
|
+
if near is not None:
|
|
75
|
+
return (
|
|
76
|
+
f"{header}.\n"
|
|
77
|
+
f" did you mean {near!r}? A peer token exists at "
|
|
78
|
+
f"{Path.home() / '.config' / 'swarph' / (near + '.peer_token')}.\n"
|
|
79
|
+
f" Do NOT `swarph mesh register` under {cell!r} — that mints a "
|
|
80
|
+
f"second identity for a cell that already has one (#657)."
|
|
81
|
+
)
|
|
82
|
+
# No near-match: keep the underlying resolver text (includes register advice
|
|
83
|
+
# for a genuinely unknown name).
|
|
84
|
+
return f"swarph highlight: {exc}"
|
|
85
|
+
|
|
86
|
+
|
|
47
87
|
def _log_via_gateway(gateway: str, cell: str, highlight: str,
|
|
48
|
-
memory: str, when: str, token_file: str | None
|
|
88
|
+
memory: str, when: str, token_file: str | None,
|
|
89
|
+
*, cell_source: str = "unknown") -> int:
|
|
49
90
|
"""POST the highlight to the gateway `/highlights` — the gateway holds the git
|
|
50
91
|
push credential, so the cell needs only its mesh peer token (no GitHub PAT).
|
|
51
92
|
Fail-loud: a non-200 or connection error returns 1 (never a silent git double-write)."""
|
|
@@ -58,7 +99,7 @@ def _log_via_gateway(gateway: str, cell: str, highlight: str,
|
|
|
58
99
|
try:
|
|
59
100
|
token = _resolve_token(cell, token_file)
|
|
60
101
|
except RuntimeError as exc:
|
|
61
|
-
print(
|
|
102
|
+
print(_credential_error(cell, cell_source, exc), file=sys.stderr)
|
|
62
103
|
return 1
|
|
63
104
|
status, resp = _post_json(url, body, token)
|
|
64
105
|
if status == 200:
|
|
@@ -109,17 +150,27 @@ def _is_git_repo(repo: Path) -> bool:
|
|
|
109
150
|
return r.returncode == 0 and r.stdout.strip() == "true"
|
|
110
151
|
|
|
111
152
|
|
|
112
|
-
def _resolve_cell(arg, repo: Path) -> str:
|
|
153
|
+
def _resolve_cell(arg, repo: Path) -> tuple[str, str]:
|
|
154
|
+
"""Return ``(cell, source)``.
|
|
155
|
+
|
|
156
|
+
Order is the house rule (#332 / #538 / #657), matching
|
|
157
|
+
``memory_emit_hook._cell``: flag, then SWARPH_SELF, then SWARPH_CELL, then
|
|
158
|
+
git user.name, then hostname. NEVER case-fold — the FAIL condition on #657
|
|
159
|
+
is normalising case instead of fixing this order.
|
|
160
|
+
"""
|
|
113
161
|
if arg:
|
|
114
|
-
return _collapse(arg)
|
|
115
|
-
|
|
116
|
-
if
|
|
117
|
-
return _collapse(
|
|
162
|
+
return _collapse(arg), "--cell"
|
|
163
|
+
self_env = os.environ.get("SWARPH_SELF")
|
|
164
|
+
if self_env:
|
|
165
|
+
return _collapse(self_env), "$SWARPH_SELF"
|
|
166
|
+
cell_env = os.environ.get("SWARPH_CELL")
|
|
167
|
+
if cell_env:
|
|
168
|
+
return _collapse(cell_env), "$SWARPH_CELL"
|
|
118
169
|
if _is_git_repo(repo):
|
|
119
170
|
r = _git(repo, "config", "user.name")
|
|
120
171
|
if r.returncode == 0 and r.stdout.strip():
|
|
121
|
-
return _collapse(r.stdout.strip())
|
|
122
|
-
return socket.gethostname()
|
|
172
|
+
return _collapse(r.stdout.strip()), "git user.name"
|
|
173
|
+
return socket.gethostname(), "hostname"
|
|
123
174
|
|
|
124
175
|
|
|
125
176
|
def _ensure_timeline(repo: Path, cell: str) -> None:
|
|
@@ -158,7 +209,8 @@ def run_highlight(argv: list) -> int:
|
|
|
158
209
|
p.add_argument("memory", nargs="?", default="",
|
|
159
210
|
help="optional memory pointer, e.g. [[some-memory]]")
|
|
160
211
|
p.add_argument("--cell", default=None,
|
|
161
|
-
help="cell identity (else
|
|
212
|
+
help="cell identity (else SWARPH_SELF / SWARPH_CELL / "
|
|
213
|
+
"git user / hostname)")
|
|
162
214
|
p.add_argument("--timeline-dir", default=None,
|
|
163
215
|
help="timeline repo (else SWARPH_TIMELINE_DIR / ~/.swarph/timeline)")
|
|
164
216
|
p.add_argument("--when", default=None,
|
|
@@ -175,7 +227,7 @@ def run_highlight(argv: list) -> int:
|
|
|
175
227
|
args = p.parse_args(argv)
|
|
176
228
|
|
|
177
229
|
repo = _resolve_dir(args.timeline_dir)
|
|
178
|
-
cell = _resolve_cell(args.cell, repo)
|
|
230
|
+
cell, cell_source = _resolve_cell(args.cell, repo)
|
|
179
231
|
highlight = _collapse(args.highlight)
|
|
180
232
|
memory = _collapse(args.memory)
|
|
181
233
|
when = _collapse(args.when) if args.when else ""
|
|
@@ -186,7 +238,8 @@ def run_highlight(argv: list) -> int:
|
|
|
186
238
|
# path too, so existing solo/offline timelines are unaffected.
|
|
187
239
|
gateway = "" if args.local else _resolve_gateway(args.gateway)
|
|
188
240
|
if gateway:
|
|
189
|
-
return _log_via_gateway(gateway, cell, highlight, memory, when,
|
|
241
|
+
return _log_via_gateway(gateway, cell, highlight, memory, when,
|
|
242
|
+
args.token_file, cell_source=cell_source)
|
|
190
243
|
|
|
191
244
|
_ensure_timeline(repo, cell)
|
|
192
245
|
ts = when or _now_ts()
|
|
@@ -1500,15 +1500,39 @@ def _sleep(seconds: float) -> None:
|
|
|
1500
1500
|
time.sleep(seconds)
|
|
1501
1501
|
|
|
1502
1502
|
|
|
1503
|
-
def
|
|
1504
|
-
"""
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1503
|
+
def _peer_from_monitor_unit(unit: str) -> "str | None":
|
|
1504
|
+
"""Mesh peer for a running ``swarph-monitor*.service`` unit.
|
|
1505
|
+
|
|
1506
|
+
Template instances encode the peer in the unit name; bespoke units carry
|
|
1507
|
+
it in ``ExecStart --as`` (#665).
|
|
1508
|
+
"""
|
|
1509
|
+
if unit.startswith("swarph-monitor@") and unit.endswith(".service"):
|
|
1510
|
+
return unit[len("swarph-monitor@"):-len(".service")]
|
|
1511
|
+
try:
|
|
1512
|
+
proc = subprocess.run(
|
|
1513
|
+
["systemctl", "show", unit, "-p", "ExecStart", "--value"],
|
|
1514
|
+
capture_output=True, text=True, check=True,
|
|
1515
|
+
encoding="utf-8", errors="replace", timeout=10,
|
|
1516
|
+
)
|
|
1517
|
+
except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
|
1518
|
+
return None
|
|
1519
|
+
text = proc.stdout.strip()
|
|
1520
|
+
if "monitor start" not in text:
|
|
1521
|
+
return None
|
|
1522
|
+
m = re.search(r"--as\s+(\S+)", text)
|
|
1523
|
+
return m.group(1) if m else None
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
def _supervised_monitor_units() -> "list[tuple[str, str]]":
|
|
1527
|
+
"""Every running swarph-monitor unit → ``(peer, unit.service)``.
|
|
1528
|
+
|
|
1529
|
+
Lists ``swarph-monitor*`` from systemd — template AND bespoke names — and
|
|
1530
|
+
confirms each unit's ExecStart is ``monitor start``. The old
|
|
1531
|
+
``swarph-monitor@*`` glob alone missed bespoke-named units (#665).
|
|
1532
|
+
"""
|
|
1509
1533
|
try:
|
|
1510
1534
|
out = subprocess.run(
|
|
1511
|
-
["systemctl", "list-units", "swarph-monitor
|
|
1535
|
+
["systemctl", "list-units", "swarph-monitor*", "--state=running",
|
|
1512
1536
|
"--no-legend", "--no-pager", "--plain"],
|
|
1513
1537
|
capture_output=True, text=True, check=True,
|
|
1514
1538
|
encoding="utf-8", errors="replace").stdout
|
|
@@ -1516,12 +1540,40 @@ def _unit_owned_instances() -> "list[str]":
|
|
|
1516
1540
|
raise RuntimeError(
|
|
1517
1541
|
"systemctl cannot list running swarph-monitor instances"
|
|
1518
1542
|
) from exc
|
|
1519
|
-
|
|
1543
|
+
by_peer: dict[str, str] = {}
|
|
1520
1544
|
for line in out.splitlines():
|
|
1521
|
-
|
|
1522
|
-
if
|
|
1523
|
-
|
|
1524
|
-
|
|
1545
|
+
unit = line.split()[0] if line.split() else ""
|
|
1546
|
+
if not unit.endswith(".service"):
|
|
1547
|
+
continue
|
|
1548
|
+
if unit == "swarph-monitor-reexec.service":
|
|
1549
|
+
continue
|
|
1550
|
+
if not unit.startswith("swarph-monitor"):
|
|
1551
|
+
continue
|
|
1552
|
+
peer = _peer_from_monitor_unit(unit)
|
|
1553
|
+
if peer:
|
|
1554
|
+
by_peer[peer] = unit
|
|
1555
|
+
return sorted(by_peer.items(), key=lambda t: t[0])
|
|
1556
|
+
|
|
1557
|
+
|
|
1558
|
+
def _unit_owned_instances() -> "list[str]":
|
|
1559
|
+
"""Peer names whose monitor is running under a systemd unit RIGHT NOW."""
|
|
1560
|
+
return [peer for peer, _ in _supervised_monitor_units()]
|
|
1561
|
+
|
|
1562
|
+
|
|
1563
|
+
def _count_pidfile_state_dirs(state_root: Path) -> int:
|
|
1564
|
+
"""State dirs that have ever claimed a monitor pidfile (#665 denominator)."""
|
|
1565
|
+
if not state_root.is_dir():
|
|
1566
|
+
return 0
|
|
1567
|
+
n = 0
|
|
1568
|
+
for peer_dir in state_root.iterdir():
|
|
1569
|
+
if peer_dir.is_dir() and (peer_dir / "mesh-sidecar" / mesh._MONITOR_PIDFILE).exists():
|
|
1570
|
+
n += 1
|
|
1571
|
+
return n
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
def _is_swarph_monitor_unit_name(unit_leaf: str) -> bool:
|
|
1575
|
+
"""True when a cgroup-derived unit leaf names a swarph monitor service."""
|
|
1576
|
+
return unit_leaf.startswith("swarph-monitor") and unit_leaf.endswith(".service")
|
|
1525
1577
|
|
|
1526
1578
|
|
|
1527
1579
|
def _cmd_reexec_on_change(args: argparse.Namespace) -> int:
|
|
@@ -1540,49 +1592,61 @@ def _cmd_reexec_on_change(args: argparse.Namespace) -> int:
|
|
|
1540
1592
|
print(" note: a RUNNING process's build is unknowable from outside "
|
|
1541
1593
|
"(#649) — 'running since' is the honest old-side coordinate")
|
|
1542
1594
|
|
|
1543
|
-
|
|
1595
|
+
supervised = _supervised_monitor_units()
|
|
1596
|
+
supervised_peers = {peer for peer, _ in supervised}
|
|
1597
|
+
pidfile_dirs = _count_pidfile_state_dirs(state_root)
|
|
1544
1598
|
held, out_of_scope, not_running, failed = [], [], [], []
|
|
1545
|
-
plan = [] # (peer, since-string)
|
|
1546
|
-
for peer in
|
|
1599
|
+
plan = [] # (peer, unit.service, since-string)
|
|
1600
|
+
for peer, unit in supervised:
|
|
1547
1601
|
state_dir = state_root / peer / "mesh-sidecar"
|
|
1548
1602
|
hold = _read_reexec_hold(state_dir)
|
|
1549
1603
|
if hold:
|
|
1550
|
-
held.append(f"{peer} — {_fmt_reexec_hold(hold)}")
|
|
1604
|
+
held.append(f"{peer} ({unit}) — {_fmt_reexec_hold(hold)}")
|
|
1551
1605
|
continue
|
|
1552
1606
|
pstatus, rec = mesh.pidfile_status(state_dir / mesh._MONITOR_PIDFILE)
|
|
1553
1607
|
since = (_proc_start_iso(rec["pid"]) if pstatus == "live_ours"
|
|
1554
1608
|
else None) or f"unknown (pidfile {pstatus})"
|
|
1555
|
-
plan.append((peer, since))
|
|
1609
|
+
plan.append((peer, unit, since))
|
|
1556
1610
|
|
|
1557
1611
|
# R6: monitors with a LIVE pidfile that no unit owns — tmux-scoped or
|
|
1558
1612
|
# hand-started — are named, never omitted.
|
|
1559
1613
|
if state_root.is_dir():
|
|
1560
1614
|
for peer_dir in sorted(state_root.iterdir()):
|
|
1561
1615
|
peer = peer_dir.name
|
|
1562
|
-
if peer in
|
|
1616
|
+
if peer in supervised_peers or not peer_dir.is_dir():
|
|
1563
1617
|
continue
|
|
1564
1618
|
state_dir = peer_dir / "mesh-sidecar"
|
|
1565
1619
|
pstatus, rec = mesh.pidfile_status(state_dir / mesh._MONITOR_PIDFILE)
|
|
1566
1620
|
if pstatus == "live_ours":
|
|
1621
|
+
derived = _derive_systemd_unit(rec["pid"])
|
|
1567
1622
|
cgroup = _read_cgroup(rec["pid"]) or ""
|
|
1568
1623
|
scope = next((p for p in reversed(cgroup.replace("\n", " ").split("/"))
|
|
1569
1624
|
if p), "unreadable cgroup")
|
|
1570
|
-
|
|
1625
|
+
if derived and _is_swarph_monitor_unit_name(f"{derived}.service"):
|
|
1626
|
+
# Enumerator missed a unit-owned monitor — name it loudly.
|
|
1627
|
+
out_of_scope.append(
|
|
1628
|
+
f"{peer} (pid {rec['pid']} in {derived}.service — "
|
|
1629
|
+
f"unit-owned but not enumerated; fix the enumerator)"
|
|
1630
|
+
)
|
|
1631
|
+
else:
|
|
1632
|
+
out_of_scope.append(f"{peer} (pid {rec['pid']} in {scope})")
|
|
1571
1633
|
elif pstatus in ("stale", "foreign"):
|
|
1572
1634
|
not_running.append(f"{peer} (pidfile {pstatus} — revival is "
|
|
1573
1635
|
f"the watchdog's job, not this verb's)")
|
|
1574
1636
|
|
|
1575
|
-
|
|
1637
|
+
restarted_n = 0
|
|
1638
|
+
for i, (peer, unit, since) in enumerate(plan):
|
|
1576
1639
|
if not args.apply:
|
|
1577
|
-
print(f" would restart: {peer} (running since {since})")
|
|
1640
|
+
print(f" would restart: {peer} via {unit} (running since {since})")
|
|
1578
1641
|
continue
|
|
1579
|
-
rc = _systemctl_run(["restart",
|
|
1642
|
+
rc = _systemctl_run(["restart", unit])
|
|
1580
1643
|
if rc == 0:
|
|
1581
|
-
|
|
1644
|
+
restarted_n += 1
|
|
1645
|
+
print(f" reexec: {peer} restarted onto {version} via {unit} "
|
|
1582
1646
|
f"(was running since {since})")
|
|
1583
1647
|
else:
|
|
1584
|
-
failed.append(f"{peer} (systemctl restart rc={rc})")
|
|
1585
|
-
print(f" reexec FAILED: {peer} (systemctl rc={rc})")
|
|
1648
|
+
failed.append(f"{peer} ({unit}, systemctl restart rc={rc})")
|
|
1649
|
+
print(f" reexec FAILED: {peer} ({unit}, systemctl rc={rc})")
|
|
1586
1650
|
if i < len(plan) - 1:
|
|
1587
1651
|
_sleep(args.stagger_s) # R3: serial, never simultaneous
|
|
1588
1652
|
|
|
@@ -1593,8 +1657,10 @@ def _cmd_reexec_on_change(args: argparse.Namespace) -> int:
|
|
|
1593
1657
|
# R6: the report is where out-of-scope cells exist at all.
|
|
1594
1658
|
for line in out_of_scope:
|
|
1595
1659
|
print(f" OUT OF SCOPE (not unit-owned; cannot be re-exec'd here): {line}")
|
|
1596
|
-
|
|
1660
|
+
action = "restarted" if args.apply else "would restart"
|
|
1661
|
+
print(f"reexec-on-change: {restarted_n if args.apply else len(plan)} {action}"
|
|
1597
1662
|
f"{'' if args.apply else ' (DRY RUN — pass --apply to act)'}, "
|
|
1663
|
+
f"{len(supervised)} unit-supervised, {pidfile_dirs} state dirs with pidfile, "
|
|
1598
1664
|
f"{len(held)} held, {len(out_of_scope)} out of scope, "
|
|
1599
1665
|
f"{len(not_running)} not running, {len(failed)} failed")
|
|
1600
1666
|
return 1 if failed else 0
|
|
@@ -162,6 +162,7 @@ Usage:
|
|
|
162
162
|
[--peer-health-window-sec SEC]
|
|
163
163
|
[--peer-health-recovery-threshold SEC]
|
|
164
164
|
[--log PATH] [--verbose]
|
|
165
|
+
swarph watchdog --orphan-daemons
|
|
165
166
|
swarph watchdog --install-service [--cell ROLE] [--dry-run]
|
|
166
167
|
|
|
167
168
|
Detects stranded Claude sessions (API throttle / harness death) and attempts
|
|
@@ -1916,6 +1917,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1916
1917
|
"--check", action="store_true",
|
|
1917
1918
|
help="One-shot check (cron-callable; exits with status code).",
|
|
1918
1919
|
)
|
|
1920
|
+
p.add_argument(
|
|
1921
|
+
"--orphan-daemons", action="store_true",
|
|
1922
|
+
help="#666 T1: read-only scan for orphaned `claude daemon run` "
|
|
1923
|
+
"processes (spawner dead + tmux-spawn scope gone). Prints a "
|
|
1924
|
+
"report; never signals. UNKNOWN ≠ ORPHANED.",
|
|
1925
|
+
)
|
|
1919
1926
|
p.add_argument(
|
|
1920
1927
|
"--install-service", action="store_true",
|
|
1921
1928
|
help="Install systemd timer + service for periodic --check invocation. "
|
|
@@ -2093,6 +2100,10 @@ def run_watchdog(argv: Optional[list[str]] = None) -> int:
|
|
|
2093
2100
|
if args.install_service:
|
|
2094
2101
|
return run_install_service(args)
|
|
2095
2102
|
|
|
2103
|
+
if args.orphan_daemons:
|
|
2104
|
+
from swarph_cli.orphan_daemons import run_orphan_daemons_report
|
|
2105
|
+
return run_orphan_daemons_report()
|
|
2106
|
+
|
|
2096
2107
|
if not args.check:
|
|
2097
2108
|
print(_USAGE, file=sys.stderr)
|
|
2098
2109
|
return 4
|