swarph-cli 0.60.0__tar.gz → 0.62.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.60.0/src/swarph_cli.egg-info → swarph_cli-0.62.0}/PKG-INFO +1 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/pyproject.toml +2 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/cell.py +1 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/init.py +1 -0
- swarph_cli-0.62.0/src/swarph_cli/commands/install_opencode_plugin.py +173 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory_emit_hook.py +29 -7
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/monitor.py +95 -2
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/spawn.py +288 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/wake_hook_output.py +1 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/enrich.py +28 -6
- swarph_cli-0.62.0/src/swarph_cli/dreaming/slm.py +120 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/main.py +1 -0
- swarph_cli-0.62.0/src/swarph_cli/payloads/opencode/plugin.js +82 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +1 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +26 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/SOURCES.txt +7 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_654_reexec.py +87 -0
- swarph_cli-0.62.0/tests/test_656_owned_slm.py +184 -0
- swarph_cli-0.62.0/tests/test_memory_emit_snippet.py +43 -0
- swarph_cli-0.62.0/tests/test_opencode_membrane.py +510 -0
- swarph_cli-0.62.0/tests/test_opencode_plugin.py +144 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/LICENSE +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/README.md +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/setup.cfg +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_group_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_import_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_init_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_main.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_security.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_app.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_version_command.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.60.0 → swarph_cli-0.62.0}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.62.0
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.62.0"
|
|
8
8
|
description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -146,6 +146,7 @@ swarph_cli = ["systemd/*.service", "systemd/*.timer", "systemd/*.default",
|
|
|
146
146
|
"systemd/*.path",
|
|
147
147
|
"scripts/*.sh", "scripts/*.ps1", "guide/*.md",
|
|
148
148
|
"payloads/postcompact/*.sh", "payloads/postcompact/*.cmd",
|
|
149
|
+
"payloads/opencode/*.js",
|
|
149
150
|
"skills/*/SKILL.md", "skills/*/LICENSE"]
|
|
150
151
|
# v0.15: ship the bundled mesh-gateway schema so `swarph gateway serve`
|
|
151
152
|
# can apply it at startup via MESH_SCHEMA_PATH (defaults next to server.py).
|
|
@@ -66,7 +66,7 @@ _validate_uuid = validate_uuid_str # historical helper name
|
|
|
66
66
|
# ONE swarph-cli release, and entry into swarph_shared.VALID_PROVIDERS only in a
|
|
67
67
|
# later one. spawn.py's guard is `VALID_PROVIDERS ⊆ MEMBRANES`, so this direction
|
|
68
68
|
# is inert while the reverse raises at import for every fresh install (#247).
|
|
69
|
-
CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor"}
|
|
69
|
+
CLI_ENABLED_PROVIDERS = VALID_PROVIDERS | {"muse", "cursor", "opencode"}
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def _config_root() -> Path:
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""``swarph install-opencode-plugin`` — the OpenCode plugin that wires swarph hooks.
|
|
2
|
+
|
|
3
|
+
Part 2 of the opencode membrane. opencode has NO command-hook surface — its only
|
|
4
|
+
hook surface is a JavaScript plugin loaded from the config dir's ``plugins/``
|
|
5
|
+
tree (auto-loaded, no ``opencode.json`` ``plugin`` entry required; measured:
|
|
6
|
+
``~/.config/opencode/plugin/opensling.js`` loads against a config containing only
|
|
7
|
+
``$schema``). This verb renders the package payload (baking the pinned Python
|
|
8
|
+
interpreter, the same reason ``install_codex_hooks`` pins it: PATH presence is
|
|
9
|
+
not a liveness proof) and writes it as ``swarph-opencode.js``:
|
|
10
|
+
|
|
11
|
+
* ``--scope user`` → ``~/.config/opencode/plugins/swarph-opencode.js`` —
|
|
12
|
+
the OPERATOR's bare ``opencode`` sessions (outside a swarph cell).
|
|
13
|
+
* ``--scope project`` → ``<cwd>/.opencode/plugins/swarph-opencode.js`` — a
|
|
14
|
+
per-project wiring.
|
|
15
|
+
|
|
16
|
+
The swarph CELL does NOT read either of those locations: its membrane relocates
|
|
17
|
+
``XDG_CONFIG_HOME`` to ``<cwd>/.opencode-cell/config``, so its plugin lives at
|
|
18
|
+
``<cwd>/.opencode-cell/config/opencode/plugins/swarph-opencode.js`` and is
|
|
19
|
+
written by the membrane's ``_opencode_env`` via ``ensure_cell_plugin`` — a
|
|
20
|
+
spawned cell is wired without a separate onboarding step.
|
|
21
|
+
|
|
22
|
+
Identity (SWARPH_SELF / SWARPH_MEMORY_DIR) is read by the plugin from env AT
|
|
23
|
+
RUNTIME, not baked — the membrane already stamps SWARPH_SELF into the cell env,
|
|
24
|
+
and a bare-session install inherits the operator's env at hook time.
|
|
25
|
+
|
|
26
|
+
Idempotent, ``--uninstall``, ``--dry-run``, write-landed assert — the same
|
|
27
|
+
operator contract as every other hook installer (#527 task 3).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import json
|
|
34
|
+
import sys
|
|
35
|
+
from importlib import resources
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
from swarph_cli.cell import _atomic_write_text
|
|
39
|
+
|
|
40
|
+
_PAYLOAD = "swarph_cli.payloads.opencode"
|
|
41
|
+
_PLUGIN_NAME = "swarph-opencode.js"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _payload_text() -> str:
|
|
45
|
+
"""Read the plugin FROM THE PACKAGE — never a source-tree path, so tests
|
|
46
|
+
exercise the same importlib.resources lookup an installed wheel depends on
|
|
47
|
+
(the 0.39.3 undeclared-package-data lesson)."""
|
|
48
|
+
return resources.files(_PAYLOAD).joinpath("plugin.js").read_text(encoding="utf-8")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _render_plugin(interpreter: str) -> str:
|
|
52
|
+
r"""Render the plugin with @PYTHON@ replaced by ``interpreter`` AS A JS STRING.
|
|
53
|
+
|
|
54
|
+
>>> shlex.quote is WRONG here, and it was the shipped defect (#423 review).
|
|
55
|
+
The placeholder sits on the RHS of ``const PY = @PYTHON@;`` — a JavaScript
|
|
56
|
+
string literal position, NOT shell text. ``shlex.quote`` wraps in single
|
|
57
|
+
quotes and leaves backslashes raw, so on Windows
|
|
58
|
+
``C:\Program Files\Python\python.exe`` became
|
|
59
|
+
``const PY = "'C:\Program Files\Python\python.exe'";`` — invalid escapes, and
|
|
60
|
+
the single quotes land INSIDE the double quotes, so the value is a wrong path
|
|
61
|
+
even where it parses. json.dumps produces a valid JS string literal (double
|
|
62
|
+
quoted, backslashes escaped as ``\\``), which a JS parser accepts and decodes
|
|
63
|
+
back to the exact path. The template leaves the quotes to the substitution
|
|
64
|
+
(``const PY = @PYTHON@;``) so they are never doubled. <<<
|
|
65
|
+
"""
|
|
66
|
+
return _payload_text().replace("@PYTHON@", json.dumps(interpreter))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _render() -> str:
|
|
70
|
+
"""The rendered plugin, with @PYTHON@ pinned to THIS interpreter."""
|
|
71
|
+
return _render_plugin(str(Path(sys.executable).resolve()))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _target(scope: str) -> Path:
|
|
75
|
+
if scope == "project":
|
|
76
|
+
return Path.cwd() / ".opencode" / "plugins" / _PLUGIN_NAME
|
|
77
|
+
return Path.home() / ".config" / "opencode" / "plugins" / _PLUGIN_NAME
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def cell_plugin_path(cwd: Path) -> Path:
|
|
81
|
+
"""Where a cell's plugin lives inside its isolated config dir.
|
|
82
|
+
|
|
83
|
+
Mirrors ``_OPENCODE_CELL_SUBDIR`` + the XDG_CONFIG_HOME layout the membrane
|
|
84
|
+
sets in ``_opencode_env`` — kept here (not imported from spawn) so spawn can
|
|
85
|
+
call back into it without a cycle.
|
|
86
|
+
"""
|
|
87
|
+
return cwd / ".opencode-cell" / "config" / "opencode" / "plugins" / _PLUGIN_NAME
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ensure_cell_plugin(cwd: Path) -> None:
|
|
91
|
+
"""Write the cell's plugin if absent or changed. Idempotent + best-effort:
|
|
92
|
+
a failure must not block the spawn (worst case the cell runs unhooked)."""
|
|
93
|
+
try:
|
|
94
|
+
rendered = _render()
|
|
95
|
+
target = cell_plugin_path(cwd)
|
|
96
|
+
if target.exists() and target.read_text(encoding="utf-8") == rendered:
|
|
97
|
+
return
|
|
98
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
_atomic_write_text(target, rendered)
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _read_existing(target: Path) -> str:
|
|
105
|
+
return target.read_text(encoding="utf-8") if target.exists() else ""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def run_install_opencode_plugin(argv: list[str] | None = None) -> int:
|
|
109
|
+
if argv is None:
|
|
110
|
+
argv = sys.argv[2:]
|
|
111
|
+
|
|
112
|
+
p = argparse.ArgumentParser(prog="swarph install-opencode-plugin")
|
|
113
|
+
p.add_argument("--scope", choices=("user", "project"), default="user")
|
|
114
|
+
p.add_argument("--uninstall", action="store_true")
|
|
115
|
+
p.add_argument("--dry-run", action="store_true")
|
|
116
|
+
args = p.parse_args(argv)
|
|
117
|
+
|
|
118
|
+
target = _target(args.scope)
|
|
119
|
+
|
|
120
|
+
if args.dry_run:
|
|
121
|
+
print(f"# swarph install-opencode-plugin --dry-run", file=sys.stderr)
|
|
122
|
+
print(f"# scope: {args.scope}", file=sys.stderr)
|
|
123
|
+
print(f"# target: {target}", file=sys.stderr)
|
|
124
|
+
print(f"# action: {'uninstall' if args.uninstall else 'install'}", file=sys.stderr)
|
|
125
|
+
if not args.uninstall:
|
|
126
|
+
print(_render())
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
if args.uninstall:
|
|
130
|
+
existed = target.exists()
|
|
131
|
+
if existed:
|
|
132
|
+
target.unlink()
|
|
133
|
+
print(
|
|
134
|
+
f"swarph install-opencode-plugin: uninstalled {_PLUGIN_NAME} from "
|
|
135
|
+
f"{target} ({'removed' if existed else 'was absent'}).",
|
|
136
|
+
file=sys.stderr,
|
|
137
|
+
)
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
rendered = _render()
|
|
141
|
+
if _read_existing(target) == rendered:
|
|
142
|
+
print(
|
|
143
|
+
f"swarph install-opencode-plugin: no change needed at {target} "
|
|
144
|
+
"(plugin already in desired state).",
|
|
145
|
+
file=sys.stderr,
|
|
146
|
+
)
|
|
147
|
+
return 0
|
|
148
|
+
|
|
149
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
150
|
+
_atomic_write_text(target, rendered)
|
|
151
|
+
# The #527 task-3 assert: "installed" is a claim about the filesystem.
|
|
152
|
+
if target.read_text(encoding="utf-8") != rendered:
|
|
153
|
+
print(
|
|
154
|
+
f"swarph install-opencode-plugin: WRITE DID NOT LAND — {target} "
|
|
155
|
+
"re-reads as different content than was written. The install is NOT "
|
|
156
|
+
"in effect.",
|
|
157
|
+
file=sys.stderr,
|
|
158
|
+
)
|
|
159
|
+
return 2
|
|
160
|
+
print(
|
|
161
|
+
f"swarph install-opencode-plugin: installed {_PLUGIN_NAME} at {target}.",
|
|
162
|
+
file=sys.stderr,
|
|
163
|
+
)
|
|
164
|
+
print(
|
|
165
|
+
"NOTE: opencode loads the plugin table at session start. Already-open "
|
|
166
|
+
"sessions keep the pre-edit table — open a FRESH session before testing.",
|
|
167
|
+
file=sys.stderr,
|
|
168
|
+
)
|
|
169
|
+
return 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
raise SystemExit(run_install_opencode_plugin(sys.argv[1:]))
|
|
@@ -67,15 +67,37 @@ def _memory_slug(file_path: str) -> Optional[str]:
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
def _snippet(path: Path) -> str:
|
|
70
|
-
"""
|
|
71
|
-
empty snippet (the pointer still emits — the write happened
|
|
70
|
+
"""One-line summary of a memory: its frontmatter `description`, else the first body
|
|
71
|
+
line. Unreadable file → empty snippet (the pointer still emits — the write happened).
|
|
72
|
+
|
|
73
|
+
Every memory file opens with YAML frontmatter, so "first non-empty line" returned the
|
|
74
|
+
literal `---` delimiter. That is a NON-EMPTY STRING, so the caller's `if snippet` guard
|
|
75
|
+
passed and the timeline took `memory cached: ---`. MEASURED 2026-09-15: 21 such entries
|
|
76
|
+
on the shared timeline between 2026-08-25 and 2026-09-15, every one of them useless.
|
|
77
|
+
An emptiness guard cannot catch a delimiter; the fix has to be here, not at the guard.
|
|
78
|
+
"""
|
|
72
79
|
try:
|
|
73
|
-
|
|
74
|
-
s = line.strip().lstrip("#").strip()
|
|
75
|
-
if s:
|
|
76
|
-
return " ".join(s.split())[:_SNIPPET_CHARS]
|
|
80
|
+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
77
81
|
except Exception:
|
|
78
|
-
|
|
82
|
+
return ""
|
|
83
|
+
body = lines
|
|
84
|
+
if lines and lines[0].strip() == "---":
|
|
85
|
+
for i, line in enumerate(lines[1:], start=1):
|
|
86
|
+
if line.strip() == "---":
|
|
87
|
+
# `description:` exists precisely to be a one-line summary — prefer it.
|
|
88
|
+
for fm in lines[1:i]:
|
|
89
|
+
if fm.strip().lower().startswith("description:"):
|
|
90
|
+
d = fm.split(":", 1)[1].strip().strip('"').strip("'")
|
|
91
|
+
if d:
|
|
92
|
+
return " ".join(d.split())[:_SNIPPET_CHARS]
|
|
93
|
+
body = lines[i + 1:]
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
body = lines[1:] # unterminated frontmatter: skip the opening delimiter
|
|
97
|
+
for line in body:
|
|
98
|
+
s = line.strip().lstrip("#").strip()
|
|
99
|
+
if s and s != "---": # a stray delimiter is never a summary
|
|
100
|
+
return " ".join(s.split())[:_SNIPPET_CHARS]
|
|
79
101
|
return ""
|
|
80
102
|
|
|
81
103
|
|
|
@@ -1703,7 +1703,7 @@ def _cmd_clear_reexec_hold(args: argparse.Namespace) -> int:
|
|
|
1703
1703
|
return 0
|
|
1704
1704
|
|
|
1705
1705
|
|
|
1706
|
-
def _resolve_consumed_tree(swarph_bin: str) -> tuple:
|
|
1706
|
+
def _resolve_consumed_tree(swarph_bin: str, pythonpath: str = None) -> tuple:
|
|
1707
1707
|
"""#807: (interpreter, init_path, how) for the tree the swarph BINARY loads.
|
|
1708
1708
|
Reads the shim's shebang, asks that interpreter where swarph_cli lives. The
|
|
1709
1709
|
installer's own interpreter is the wrong witness (a pipx-run installer watched
|
|
@@ -1722,6 +1722,8 @@ def _resolve_consumed_tree(swarph_bin: str) -> tuple:
|
|
|
1722
1722
|
# whoever runs the installer (a dev shell's PYTHONPATH=src would otherwise be
|
|
1723
1723
|
# reported as "the tree the monitors load" — the exact misreading this fixes).
|
|
1724
1724
|
env = {k: v for k, v in os.environ.items() if k not in ("PYTHONPATH", "PYTHONHOME", "PYTHONSAFEPATH")}
|
|
1725
|
+
if pythonpath:
|
|
1726
|
+
env["PYTHONPATH"] = pythonpath
|
|
1725
1727
|
try:
|
|
1726
1728
|
r = subprocess.run([interp, "-c", "import swarph_cli, sys; print(swarph_cli.__file__)"],
|
|
1727
1729
|
capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
@@ -1733,6 +1735,55 @@ def _resolve_consumed_tree(swarph_bin: str) -> tuple:
|
|
|
1733
1735
|
return interp, r.stdout.strip().replace("\\", "/"), f"resolved from {swarph_bin} -> {interp}"
|
|
1734
1736
|
|
|
1735
1737
|
|
|
1738
|
+
_USER_SITE_RE = re.compile(r"^(.*/\.local/lib/python[0-9]+\.[0-9]+/site-packages)/swarph_cli/__init__\.py$")
|
|
1739
|
+
|
|
1740
|
+
|
|
1741
|
+
def _user_site_of(init_path: str):
|
|
1742
|
+
"""The user-site dir a swarph_cli __init__ lives in, or None (pipx venv, system)."""
|
|
1743
|
+
m = _USER_SITE_RE.match((init_path or "").replace("\\", "/"))
|
|
1744
|
+
return m.group(1) if m else None
|
|
1745
|
+
|
|
1746
|
+
|
|
1747
|
+
def _owner_user_site(swarph_bin: str, interp: str):
|
|
1748
|
+
"""#807 (root cannot see the user site): the pip --user tree of the SHIM'S OWNER for
|
|
1749
|
+
the shim's interpreter — ~owner/.local/lib/pythonX.Y/site-packages — if swarph_cli
|
|
1750
|
+
is there. The unit runs as root and root's interpreter never searches another
|
|
1751
|
+
user's site, so the installer must find it by construction, not by import."""
|
|
1752
|
+
import pwd, subprocess
|
|
1753
|
+
try:
|
|
1754
|
+
home = pwd.getpwuid(Path(swarph_bin).stat().st_uid).pw_dir
|
|
1755
|
+
r = subprocess.run([interp, "-c", "import sys; print('%d.%d' % sys.version_info[:2])"],
|
|
1756
|
+
capture_output=True, text=True, encoding="utf-8", errors="replace",
|
|
1757
|
+
timeout=20)
|
|
1758
|
+
except (OSError, KeyError, subprocess.SubprocessError):
|
|
1759
|
+
return None
|
|
1760
|
+
ver = r.stdout.strip()
|
|
1761
|
+
if r.returncode != 0 or not re.fullmatch(r"[0-9]+\.[0-9]+", ver):
|
|
1762
|
+
return None
|
|
1763
|
+
cand = f"{home}/.local/lib/python{ver}/site-packages"
|
|
1764
|
+
return cand if Path(cand, "swarph_cli", "__init__.py").exists() else None
|
|
1765
|
+
|
|
1766
|
+
|
|
1767
|
+
def _condition_probe(interpreter: str, pythonpath: str = None) -> tuple:
|
|
1768
|
+
"""Run the unit's ExecCondition NOW, as the user running this verb, with the env the
|
|
1769
|
+
unit will have. (ok, detail). The 2026-09-15 lesson: a condition that fails on every
|
|
1770
|
+
fire is a SKIP (Result=success) — OnFailure never fires, and the unit is quieter than
|
|
1771
|
+
the failed state it replaced. So the writer checks it before it writes."""
|
|
1772
|
+
import subprocess
|
|
1773
|
+
env = {k: v for k, v in os.environ.items() if k not in ("PYTHONPATH", "PYTHONHOME", "PYTHONSAFEPATH")}
|
|
1774
|
+
if pythonpath:
|
|
1775
|
+
env["PYTHONPATH"] = pythonpath
|
|
1776
|
+
try:
|
|
1777
|
+
r = subprocess.run([interpreter, "-c", "import swarph_cli"], capture_output=True, text=True,
|
|
1778
|
+
encoding="utf-8", errors="replace", timeout=20, env=env)
|
|
1779
|
+
except (OSError, subprocess.SubprocessError) as e:
|
|
1780
|
+
return False, f"{interpreter} could not be run ({type(e).__name__})"
|
|
1781
|
+
if r.returncode == 0:
|
|
1782
|
+
return True, "OK"
|
|
1783
|
+
last = (r.stderr.strip().splitlines() or ["(no stderr)"])[-1]
|
|
1784
|
+
return False, f"rc={r.returncode}: {last}"
|
|
1785
|
+
|
|
1786
|
+
|
|
1736
1787
|
def _live_tree():
|
|
1737
1788
|
"""The tree the INSTALLER's own interpreter loads (a pipx-run installer reports the
|
|
1738
1789
|
pipx venv), asked through the same scrubbed child as the consumed tree, so a dev
|
|
@@ -1772,12 +1823,39 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1772
1823
|
# interpreter, not the interpreter running this verb.
|
|
1773
1824
|
interpreter, init_path, how = _resolve_consumed_tree(swarph_bin)
|
|
1774
1825
|
live_init = _live_tree()
|
|
1826
|
+
pythonpath = None
|
|
1827
|
+
if init_path is None and interpreter:
|
|
1828
|
+
# #807 (2026-09-15): run as root, the shim's interpreter cannot import a pip
|
|
1829
|
+
# --user install at all — the same ModuleNotFoundError as a mid-install
|
|
1830
|
+
# window, on EVERY fire. Find the owner's user site by construction.
|
|
1831
|
+
us = _owner_user_site(swarph_bin, interpreter)
|
|
1832
|
+
if us:
|
|
1833
|
+
_i, init2, how2 = _resolve_consumed_tree(swarph_bin, pythonpath=us)
|
|
1834
|
+
if init2:
|
|
1835
|
+
init_path, how = init2, (f"{how2} via the shim owner's user site — {interpreter} "
|
|
1836
|
+
f"alone cannot import it (this user is not the owner, e.g. root)")
|
|
1775
1837
|
if init_path is None:
|
|
1776
1838
|
if live_init is None:
|
|
1777
1839
|
raise RuntimeError("cannot resolve the installed swarph_cli package "
|
|
1778
1840
|
"path — is this a broken install?")
|
|
1779
1841
|
init_path, how = live_init, f"{how}; fell back to the LIVE interpreter"
|
|
1780
1842
|
extra_trees = [t for t in {live_init} if t and t != init_path]
|
|
1843
|
+
# A user-site tree is invisible to root's interpreter: the unit runs as root, so
|
|
1844
|
+
# the site the monitors load is handed to it explicitly. A pipx/venv tree needs
|
|
1845
|
+
# nothing (its interpreter owns its site) and gets no Environment= line.
|
|
1846
|
+
pythonpath = _user_site_of(init_path)
|
|
1847
|
+
env_line = f"Environment=PYTHONPATH={pythonpath}\n" if pythonpath else ""
|
|
1848
|
+
# Provenance (2026-09-15): a unit on disk could not say what produced it, so a
|
|
1849
|
+
# render from an unreleased checkout was indistinguishable from a release.
|
|
1850
|
+
# The PACKAGE PATH is the field that tells a release apart from a checkout: an
|
|
1851
|
+
# unreleased tree reports the last release's __version__ until the bump, and
|
|
1852
|
+
# the interpreter is the same either way (lab-ovh, #420 review, measured).
|
|
1853
|
+
import swarph_cli as _pkg
|
|
1854
|
+
from swarph_cli import __version__ as _ver
|
|
1855
|
+
from datetime import datetime, timezone
|
|
1856
|
+
rendered_by = (f"swarph-cli {_ver} ({str(_pkg.__file__).replace(chr(92), '/')}) "
|
|
1857
|
+
f"install-reexec, {sys.executable}, "
|
|
1858
|
+
f"{datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}")
|
|
1781
1859
|
rendered = {}
|
|
1782
1860
|
for name, rel in _REEXEC_TEMPLATES.items():
|
|
1783
1861
|
text = _read_packaged(rel)
|
|
@@ -1785,7 +1863,9 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1785
1863
|
.replace("<SITE_PACKAGES_INIT>", init_path)
|
|
1786
1864
|
.replace("<INTERPRETER>", interpreter or sys.executable)
|
|
1787
1865
|
.replace("<SWARPH_BIN>", swarph_bin)
|
|
1788
|
-
.replace("<STATE_ROOT>", state_root)
|
|
1866
|
+
.replace("<STATE_ROOT>", state_root)
|
|
1867
|
+
.replace("<ENV_PYTHONPATH>\n", env_line)
|
|
1868
|
+
.replace("<RENDERED_BY>", rendered_by))
|
|
1789
1869
|
if name.endswith(".path") and extra_trees:
|
|
1790
1870
|
out = out.replace(f"PathChanged={init_path}\n",
|
|
1791
1871
|
f"PathChanged={init_path}\n"
|
|
@@ -1803,6 +1883,9 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1803
1883
|
dropins[f"{name}.d/10-onfailure.conf"] = (
|
|
1804
1884
|
f"# #807: written by `swarph monitor install-reexec --on-failure`; box-local.\n"
|
|
1805
1885
|
f"[Unit]\nOnFailure={args.on_failure}\n")
|
|
1886
|
+
import getpass
|
|
1887
|
+
cond_ok, cond_detail = _condition_probe(interpreter or sys.executable, pythonpath)
|
|
1888
|
+
who = getpass.getuser()
|
|
1806
1889
|
if not args.write:
|
|
1807
1890
|
for name, text in rendered.items():
|
|
1808
1891
|
print(f"# ── {name} ──")
|
|
@@ -1811,6 +1894,8 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1811
1894
|
print(f"# ── {name} ──")
|
|
1812
1895
|
print(text, end="")
|
|
1813
1896
|
print(f"# watching: {init_path} ({how})")
|
|
1897
|
+
print(f"# condition check as {who}: {'OK' if cond_ok else 'FAILS — ' + cond_detail}"
|
|
1898
|
+
+ ("" if cond_ok else " (the unit would be SKIPPED on every fire; --write will refuse)"))
|
|
1814
1899
|
for t in extra_trees:
|
|
1815
1900
|
print(f"# also watching: {t} (a second install tree; the monitors do not load it)")
|
|
1816
1901
|
print(f"# import check: {interpreter or sys.executable} -c 'import swarph_cli'")
|
|
@@ -1822,6 +1907,13 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1822
1907
|
print("swarph monitor install-reexec --write: Linux-only.",
|
|
1823
1908
|
file=sys.stderr)
|
|
1824
1909
|
return 2
|
|
1910
|
+
if not cond_ok:
|
|
1911
|
+
print(f"swarph monitor install-reexec --write: REFUSED — the ExecCondition this unit would "
|
|
1912
|
+
f"carry fails right now for {who}: {interpreter or sys.executable} -c 'import swarph_cli' "
|
|
1913
|
+
f"→ {cond_detail}. Installed, it would be SKIPPED on every fire (Result=success, no "
|
|
1914
|
+
f"OnFailure) — a control quieter than the failed state it replaces. Nothing written.",
|
|
1915
|
+
file=sys.stderr)
|
|
1916
|
+
return 2
|
|
1825
1917
|
target_dir = Path(args.dir)
|
|
1826
1918
|
if not target_dir.is_dir() or not os.access(target_dir, os.W_OK):
|
|
1827
1919
|
print(f"swarph monitor install-reexec: {target_dir} is not writable — "
|
|
@@ -1836,6 +1928,7 @@ def _cmd_install_reexec(args: argparse.Namespace) -> int:
|
|
|
1836
1928
|
(target_dir / name).write_text(text, encoding="utf-8")
|
|
1837
1929
|
print(f"wrote {target_dir / name}")
|
|
1838
1930
|
print(f"watching: {init_path} ({how})")
|
|
1931
|
+
print(f"condition check as {who}: OK" + (f" (PYTHONPATH={pythonpath})" if pythonpath else ""))
|
|
1839
1932
|
for t in extra_trees:
|
|
1840
1933
|
print(f"also watching: {t}")
|
|
1841
1934
|
print("next: systemctl daemon-reload && systemctl reset-failed swarph-monitor-reexec.path "
|