swarph-cli 0.72.44__tar.gz → 0.72.45__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.72.44/src/swarph_cli.egg-info → swarph_cli-0.72.45}/PKG-INFO +1 -1
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/pyproject.toml +1 -1
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/event.py +51 -4
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/mcp_server.py +9 -3
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/mesh.py +8 -1
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/redact.py +6 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scrub.py +7 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
- swarph_cli-0.72.45/tests/test_1472_redact_boundaries.py +218 -0
- swarph_cli-0.72.45/tests/test_event_emit.py +166 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mcp_server.py +39 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_scrub.py +6 -0
- swarph_cli-0.72.44/tests/test_event_emit.py +0 -36
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/LICENSE +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/README.md +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/setup.cfg +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_cost.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/board_usage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/commander.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/committee.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/dev_extract.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/roster.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/skunkworks.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/commit_trailers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/duplicate_session.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/launchd_render.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/quota_sync.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/the_ten.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/backend.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1034_copy_mode.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1068_structured_accept_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1071_quota_sync.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1083_openai_decisions.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1144_cell_yaml_github.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1197_commander_taps.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1203_swarph_me_refusal.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1206_offer_question.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1214_egress.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1219_egress_probe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1221_quota_windows.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1222_maybe_start_cap.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1228_quota_reach.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1243_the_ten.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1248_ship_price_table.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1250_price_cache.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1256_commander_wake.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1261_mcp_install.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1262_wake_followups.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1265_wake_note.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1269_triage_label.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1277_close_usage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1278_dreaming_reach.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1286_dm_unread_cursor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1297_commander.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1300_authorization_check.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1301_restart_skew.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1320_commit_trailers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1321_layered_redaction.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1340_router_rollout.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1346_shell_string_gh.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1347_shell_depth_cap.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1353_obligation_release_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1385_duplicate_session_argv.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1396_dev_extract.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_basic.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1405_skunkworks_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1427_membrane_workflow.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1436_dev_extract_freshness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework1_engine.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework2_engine.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_rework3_engine.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1452_standing_wake_expiry.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1470_gh_router_fail_closed.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_1478_triage_dm.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_3130_sender.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_3150_identity_router_install.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_513_duplicate_session.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_931_board_search.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_935_deadline_clock.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_committee_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_copilot_pane_reader.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_launchd_units.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_main.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_security.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.44 → swarph_cli-0.72.45}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.45
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.72.
|
|
7
|
+
version = "0.72.45"
|
|
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" }
|
|
@@ -20,7 +20,7 @@ from ._gateway_client import (
|
|
|
20
20
|
resolve_self_name,
|
|
21
21
|
resolve_token,
|
|
22
22
|
)
|
|
23
|
-
from swarph_cli.redact import redact_text
|
|
23
|
+
from swarph_cli.redact import encode_outbound_json, redact_text
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -47,6 +47,34 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
47
47
|
return p
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
class ChainTokenAttachError(RuntimeError):
|
|
51
|
+
"""The redacted event bytes are not JSON, so the chain token is not sent."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _attach_chain_token(data: bytes, chain_token: str) -> bytes:
|
|
55
|
+
"""Add the CLI chain token onto an already-redacted event body.
|
|
56
|
+
|
|
57
|
+
Same shape as ``mcp_server._search``: redact first, then set the one
|
|
58
|
+
field that must reach the wire. A pasted chain_token line in the payload
|
|
59
|
+
was already scrubbed and is not this value.
|
|
60
|
+
|
|
61
|
+
Redaction can rewrite a quote inside the content string and leave bytes
|
|
62
|
+
that are not JSON. That refusal is by name (``JSONDecodeError`` or
|
|
63
|
+
``UnicodeDecodeError``). The token is not copied into the error, and the
|
|
64
|
+
caller sends nothing.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
body_obj = json.loads(data.decode("utf-8"))
|
|
68
|
+
inner = json.loads(body_obj["content"])
|
|
69
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
70
|
+
raise ChainTokenAttachError(
|
|
71
|
+
f"_attach_chain_token refused: {type(exc).__name__}"
|
|
72
|
+
) from None
|
|
73
|
+
inner["swarph_event"]["chain_token"] = chain_token
|
|
74
|
+
body_obj["content"] = json.dumps(inner, separators=(",", ":"), sort_keys=True)
|
|
75
|
+
return json.dumps(body_obj).encode("utf-8")
|
|
76
|
+
|
|
77
|
+
|
|
50
78
|
def _post_channel(
|
|
51
79
|
*,
|
|
52
80
|
channel: str,
|
|
@@ -76,8 +104,6 @@ def _post_channel(
|
|
|
76
104
|
base = (gateway or env_gateway()).rstrip("/")
|
|
77
105
|
payload = redact_text(payload, caller="event.emit") or ""
|
|
78
106
|
ev: dict = {"event": event, "payload": payload}
|
|
79
|
-
if chain_token is not None: # carry an explicit "" through; only omit when unset
|
|
80
|
-
ev["chain_token"] = chain_token
|
|
81
107
|
# Sentinel-wrapped so P1's guard can UNAMBIGUOUSLY identify an event post: a
|
|
82
108
|
# normal channel message won't carry the top-level `swarph_event` key, so the
|
|
83
109
|
# guard keys off `content.swarph_event` — never a false-positive on a plain
|
|
@@ -91,7 +117,28 @@ def _post_channel(
|
|
|
91
117
|
"kind": "fyi",
|
|
92
118
|
"content": json.dumps(content, separators=(",", ":"), sort_keys=True),
|
|
93
119
|
}
|
|
94
|
-
|
|
120
|
+
# Redact with no chain token in the body. Attach the CLI value after, so
|
|
121
|
+
# only that value skips redaction. An explicit "" is carried; omit only
|
|
122
|
+
# when the flag was unset. Card #1472.
|
|
123
|
+
if chain_token is None:
|
|
124
|
+
status, resp = post_json(f"{base}/messages", body, token)
|
|
125
|
+
else:
|
|
126
|
+
# Attach after redaction. If those bytes are not JSON, name the
|
|
127
|
+
# refusal and return before post_json: nothing is sent, no traceback.
|
|
128
|
+
try:
|
|
129
|
+
encoded = _attach_chain_token(
|
|
130
|
+
encode_outbound_json(body, caller="event.emit"),
|
|
131
|
+
chain_token,
|
|
132
|
+
)
|
|
133
|
+
except ChainTokenAttachError as exc:
|
|
134
|
+
print(f"swarph event: {exc}", file=sys.stderr)
|
|
135
|
+
return 2
|
|
136
|
+
status, resp = post_json(
|
|
137
|
+
f"{base}/messages",
|
|
138
|
+
body,
|
|
139
|
+
token,
|
|
140
|
+
encoded=encoded,
|
|
141
|
+
)
|
|
95
142
|
if 200 <= status < 300:
|
|
96
143
|
print(f"emitted event={event} channel={channel} id={resp.get('id')}")
|
|
97
144
|
return 0
|
|
@@ -89,10 +89,16 @@ def _search(query: str, *, url: str, token: str | None = None) -> list[dict]:
|
|
|
89
89
|
transport exception.
|
|
90
90
|
"""
|
|
91
91
|
endpoint = url.rstrip("/") + "/api/ask"
|
|
92
|
-
|
|
92
|
+
# Redact the query only. The auth token is the credential this call
|
|
93
|
+
# presents to the search face. Putting it in the dict that
|
|
94
|
+
# encode_outbound_json walks replaces a credential-shaped value
|
|
95
|
+
# (secrets.token_urlsafe(32), the gateway mint) with
|
|
96
|
+
# [REDACTED:HIGH_ENTROPY] before the request is sent. Card #1472.
|
|
97
|
+
data = encode_outbound_json({"message": query}, caller="mcp_server._search")
|
|
93
98
|
if token:
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
body_obj = json.loads(data.decode("utf-8"))
|
|
100
|
+
body_obj["token"] = token
|
|
101
|
+
data = json.dumps(body_obj).encode("utf-8")
|
|
96
102
|
req = urllib.request.Request(
|
|
97
103
|
endpoint,
|
|
98
104
|
data=data,
|
|
@@ -444,9 +444,16 @@ def _post_json(
|
|
|
444
444
|
token: str,
|
|
445
445
|
*,
|
|
446
446
|
timeout: float = 10.0,
|
|
447
|
+
encoded: bytes | None = None,
|
|
447
448
|
) -> tuple[int, dict]:
|
|
448
449
|
_require_absolute_gateway_url(url)
|
|
449
|
-
|
|
450
|
+
# event.emit redacts with no chain token, then passes the bytes with the
|
|
451
|
+
# CLI --chain-token already attached. Encoding those bytes again would
|
|
452
|
+
# strip that one value. Every other caller leaves encoded unset.
|
|
453
|
+
if encoded is None:
|
|
454
|
+
data = encode_outbound_json(body, caller="mesh._post_json")
|
|
455
|
+
else:
|
|
456
|
+
data = encoded
|
|
450
457
|
req = urllib.request.Request(
|
|
451
458
|
url,
|
|
452
459
|
data=data,
|
|
@@ -132,6 +132,11 @@ _ANTHROPIC_RE = re.compile(r"\bsk-ant-[A-Za-z0-9\-_]{8,}\b")
|
|
|
132
132
|
_OPENAI_RE = re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b")
|
|
133
133
|
_XAI_RE = re.compile(r"\bxai-[A-Za-z0-9\-_]{16,}\b")
|
|
134
134
|
_GCP_RE = re.compile(r"\bAIza[0-9A-Za-z\-_]{35}\b")
|
|
135
|
+
_GCP_FINAL_RE = re.compile(
|
|
136
|
+
r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
|
|
137
|
+
r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
|
|
138
|
+
r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"
|
|
139
|
+
)
|
|
135
140
|
_SLACK_RE = re.compile(r"\bxox[baprs]-[A-Za-z0-9\-]{8,}\b")
|
|
136
141
|
_AWS_KEY_RE = re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b")
|
|
137
142
|
_AWS_SECRET_RE = re.compile(
|
|
@@ -332,6 +337,7 @@ def redact_text(
|
|
|
332
337
|
# Layer 6: Shannon Entropy Scoring
|
|
333
338
|
out = _SECRET_ASSIGN_RE.sub(_replace_secret_assignment, out)
|
|
334
339
|
out = _replace_standalone_tokens(out)
|
|
340
|
+
out = _GCP_FINAL_RE.sub("[REDACTED:GCP_API_KEY]", out)
|
|
335
341
|
|
|
336
342
|
return out
|
|
337
343
|
|
|
@@ -39,6 +39,13 @@ _SECRET_PATTERNS = [
|
|
|
39
39
|
# password/token/secret assignments.
|
|
40
40
|
(re.compile(r"(?i)\b(password|passwd|pwd|api[_-]?key|auth[_-]?token|secret)\b\s*[:=]\s*\S+"),
|
|
41
41
|
r"\1=[REDACTED]"),
|
|
42
|
+
(re.compile(r"\bsk-(?!ant-)(?:proj-|svcacct-)?[A-Za-z0-9\-_]{16,}\b"),
|
|
43
|
+
"[REDACTED-API-KEY]"),
|
|
44
|
+
(re.compile(
|
|
45
|
+
r"(?:\bAIza[0-9A-Za-z\-_]{34}(?:-|[0-9A-Za-z_](?![0-9A-Za-z_]))"
|
|
46
|
+
r"|(?<=[0-9A-Za-z_])AIza[0-9A-Za-z\-_]{34}[0-9A-Za-z_](?![0-9A-Za-z_])"
|
|
47
|
+
r"|(?<=\w)AIza[0-9A-Za-z\-_]{34}-)"),
|
|
48
|
+
"[REDACTED-API-KEY]"),
|
|
42
49
|
]
|
|
43
50
|
|
|
44
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.45
|
|
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
|
|
@@ -285,6 +285,7 @@ tests/test_1452_rework2_engine.py
|
|
|
285
285
|
tests/test_1452_rework3_engine.py
|
|
286
286
|
tests/test_1452_standing_wake_expiry.py
|
|
287
287
|
tests/test_1470_gh_router_fail_closed.py
|
|
288
|
+
tests/test_1472_redact_boundaries.py
|
|
288
289
|
tests/test_1478_triage_dm.py
|
|
289
290
|
tests/test_183_card_anchor.py
|
|
290
291
|
tests/test_184_notify_honesty.py
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Card #1472: pasted chain_token text stays scrubbed, and a trailing-dash
|
|
2
|
+
AIza key is scrubbed too.
|
|
3
|
+
|
|
4
|
+
Every secret is built at runtime. No realistic key literal.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from swarph_cli.redact import encode_outbound_json, redact_text
|
|
11
|
+
from swarph_cli.scrub import scrub_text
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _sk_proj() -> str:
|
|
15
|
+
return "sk-proj-" + ("n7" * 12)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _ghp() -> str:
|
|
19
|
+
return "ghp_" + ("Ab" * 16)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _aiza_trailing_dash() -> str:
|
|
23
|
+
# AIza + 35 chars, last character '-'. No digits, so the entropy
|
|
24
|
+
# layer does not also eat it: only the AIza rule can.
|
|
25
|
+
secret = "AI" + "za" + ("m" * 34) + "-"
|
|
26
|
+
assert len(secret) == 39
|
|
27
|
+
assert secret.endswith("-")
|
|
28
|
+
return secret
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_dm_body_pasted_chain_token_line_is_fully_redacted():
|
|
32
|
+
"""Drop's measured leak: keys sit inside a pasted "chain_token": "..." value.
|
|
33
|
+
|
|
34
|
+
encode_outbound_json is what mesh._post_json sends. Both keys must be
|
|
35
|
+
gone from those bytes. A shield that puts the quoted span back fails this.
|
|
36
|
+
"""
|
|
37
|
+
sk = _sk_proj()
|
|
38
|
+
ghp = _ghp()
|
|
39
|
+
content = (
|
|
40
|
+
'pasted log "chain_token": "\n'
|
|
41
|
+
f"export OPENAI_API_KEY={sk}\n"
|
|
42
|
+
f"GITHUB={ghp}\n"
|
|
43
|
+
'" end'
|
|
44
|
+
)
|
|
45
|
+
followed = f"chain_token: note\n{sk}\n{ghp}"
|
|
46
|
+
for text in (content, followed):
|
|
47
|
+
body = {
|
|
48
|
+
"from_node": "friendly-coder",
|
|
49
|
+
"to_node": "lab-ovh",
|
|
50
|
+
"kind": "answer",
|
|
51
|
+
"content": text,
|
|
52
|
+
}
|
|
53
|
+
raw = encode_outbound_json(body, caller="t").decode("utf-8")
|
|
54
|
+
assert sk not in raw
|
|
55
|
+
assert ghp not in raw
|
|
56
|
+
assert "REDACTED" in raw
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_redact_text_redacts_chain_token_json_holding_an_sk_proj_key():
|
|
60
|
+
sk = _sk_proj()
|
|
61
|
+
blob = '{"chain_token": "' + sk + '"}'
|
|
62
|
+
out = redact_text(blob, caller="t")
|
|
63
|
+
assert sk not in out
|
|
64
|
+
assert "REDACTED" in out
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_chain_placeholder_literal_is_left_alone():
|
|
68
|
+
literal = "SWARPH.CHAIN.0.END"
|
|
69
|
+
assert redact_text(f"see {literal} here", caller="t") == f"see {literal} here"
|
|
70
|
+
sk = _sk_proj()
|
|
71
|
+
text = literal + '\n{"chain_token": "' + sk + '"}'
|
|
72
|
+
out = redact_text(text, caller="t")
|
|
73
|
+
assert out.startswith(literal)
|
|
74
|
+
assert sk not in out
|
|
75
|
+
assert json.loads(out.split("\n", 1)[1])["chain_token"] != sk
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_aiza_key_then_hyphen_suffix_is_redacted():
|
|
79
|
+
"""A '-' after the 35-character body is a boundary, not a longer tail.
|
|
80
|
+
|
|
81
|
+
key-x and key-- must be scrubbed. Putting '-' back into the lookahead
|
|
82
|
+
(?![0-9A-Za-z\\-_]) hides both, because the next character is '-'.
|
|
83
|
+
"""
|
|
84
|
+
secret = "AI" + "za" + ("m" * 35)
|
|
85
|
+
assert len(secret) == 39
|
|
86
|
+
assert not secret.endswith("-")
|
|
87
|
+
for suffix in ("-x", "--"):
|
|
88
|
+
text = secret + suffix
|
|
89
|
+
scrubbed = scrub_text(text)
|
|
90
|
+
assert scrubbed is not None
|
|
91
|
+
assert secret not in scrubbed
|
|
92
|
+
assert "REDACTED" in scrubbed
|
|
93
|
+
redacted = redact_text(text, caller="t")
|
|
94
|
+
assert secret not in redacted
|
|
95
|
+
assert "REDACTED" in redacted
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _assert_scrub_and_redact(text: str, *secrets: str) -> None:
|
|
99
|
+
scrubbed = scrub_text(text)
|
|
100
|
+
assert scrubbed is not None
|
|
101
|
+
redacted = redact_text(text, caller="t")
|
|
102
|
+
for secret in secrets:
|
|
103
|
+
assert secret not in scrubbed
|
|
104
|
+
assert secret not in redacted
|
|
105
|
+
assert "REDACTED" in scrubbed
|
|
106
|
+
assert "REDACTED" in redacted
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_aiza_dash_key_followed_by_word_char_is_redacted():
|
|
110
|
+
"""A 35th character of '-' then x, _, or 'abc def' (drop #4429).
|
|
111
|
+
|
|
112
|
+
{35}(?![0-9A-Za-z_]) refuses that follower, so the key stays visible.
|
|
113
|
+
Both scrub_text and redact_text have to take the dash.
|
|
114
|
+
"""
|
|
115
|
+
secret = _aiza_trailing_dash()
|
|
116
|
+
for suffix in ("x", "_", "abc def"):
|
|
117
|
+
_assert_scrub_and_redact(secret + suffix, secret)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_dash_ending_aiza_then_api_key_hides_key_and_secret():
|
|
121
|
+
""":<dash-ending key>api_key=<secret> leaves neither (drop #4437).
|
|
122
|
+
|
|
123
|
+
The match stops on the key. A greedy tail would swallow api_key, and
|
|
124
|
+
the secret would stay where the keyword-value rule should have put
|
|
125
|
+
[REDACTED:DSN_CREDENTIAL].
|
|
126
|
+
"""
|
|
127
|
+
key = _aiza_trailing_dash()
|
|
128
|
+
secret = "plain-" + "secret-value"
|
|
129
|
+
text = f":{key}api_key={secret}"
|
|
130
|
+
scrubbed = scrub_text(text)
|
|
131
|
+
assert scrubbed is not None
|
|
132
|
+
assert key not in scrubbed
|
|
133
|
+
assert secret not in scrubbed
|
|
134
|
+
assert "api_key=[REDACTED]" in scrubbed
|
|
135
|
+
redacted = redact_text(text, caller="t")
|
|
136
|
+
assert key not in redacted
|
|
137
|
+
assert secret not in redacted
|
|
138
|
+
assert "api_key=[REDACTED:DSN_CREDENTIAL]" in redacted
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_dash_ending_aiza_glued_through_chain_token_redacts_both_keys():
|
|
142
|
+
"""token=<dash-ending key>--chain_token<full key> leaves neither key.
|
|
143
|
+
|
|
144
|
+
A match that stops on the first key leaves the second glued to the
|
|
145
|
+
word character at the end of chain_token. Values are built here, with
|
|
146
|
+
no digits, so the entropy layer cannot hide a miss.
|
|
147
|
+
"""
|
|
148
|
+
dash_key = _aiza_trailing_dash()
|
|
149
|
+
full_key = "AI" + "za" + ("n" * 35)
|
|
150
|
+
assert len(full_key) == 39
|
|
151
|
+
assert "-" not in full_key
|
|
152
|
+
text = f"token={dash_key}--chain_token{full_key}"
|
|
153
|
+
_assert_scrub_and_redact(text, dash_key, full_key)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_drop_4452_chain_and_proj_shapes_leave_no_key():
|
|
157
|
+
"""Drop #4452's two shapes, on scrub_text and redact_text.
|
|
158
|
+
|
|
159
|
+
chain_token<dash key>x<full key>{ leaves neither key. A proj key glued
|
|
160
|
+
to a dash-ending key and then api_key=<another dash key> leaves none of
|
|
161
|
+
the three. The keyword value below is a low-entropy secret: scrub_text
|
|
162
|
+
must hide it. redact_text's OpenAI rule is main's, and it still runs
|
|
163
|
+
before the keyword rule, so that low-entropy value can remain there.
|
|
164
|
+
"""
|
|
165
|
+
dash = _aiza_trailing_dash()
|
|
166
|
+
full = "AI" + "za" + ("n" * 35)
|
|
167
|
+
_assert_scrub_and_redact(f"chain_token{dash}x{full}{{", dash, full)
|
|
168
|
+
|
|
169
|
+
proj = "sk-proj-" + ("n7" * 12)
|
|
170
|
+
trail = "AI" + "za" + ("p" * 34) + "-"
|
|
171
|
+
assert len(trail) == 39 and trail.endswith("-")
|
|
172
|
+
_assert_scrub_and_redact(f"{proj}{dash}api_key={trail}", proj, dash, trail)
|
|
173
|
+
|
|
174
|
+
secret = "plain-" + "secret-value"
|
|
175
|
+
text = f"{proj}{dash}api_key={secret}"
|
|
176
|
+
scrubbed = scrub_text(text)
|
|
177
|
+
assert scrubbed is not None
|
|
178
|
+
assert proj not in scrubbed
|
|
179
|
+
assert dash not in scrubbed
|
|
180
|
+
assert secret not in scrubbed
|
|
181
|
+
redacted = redact_text(text, caller="t")
|
|
182
|
+
assert proj not in redacted
|
|
183
|
+
assert dash not in redacted
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_dash_ending_key_glued_after_word_char_is_redacted():
|
|
187
|
+
"""A dash-ending key glued after x, _, another key, or é is redacted.
|
|
188
|
+
|
|
189
|
+
No digits, so the entropy layer cannot hide a miss. Both scrub_text
|
|
190
|
+
and redact_text have to take the key.
|
|
191
|
+
"""
|
|
192
|
+
dash = _aiza_trailing_dash()
|
|
193
|
+
other = "AI" + "za" + ("q" * 35)
|
|
194
|
+
assert len(other) == 39
|
|
195
|
+
for prefix in ("x", "_", other, "é"):
|
|
196
|
+
_assert_scrub_and_redact(prefix + dash, dash)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_aiza_key_ending_in_dash_is_redacted_before_space_quote_or_punctuation():
|
|
200
|
+
secret = _aiza_trailing_dash()
|
|
201
|
+
samples = [
|
|
202
|
+
f"prefix {secret} suffix",
|
|
203
|
+
f'"{secret}"',
|
|
204
|
+
f"'{secret}'",
|
|
205
|
+
secret,
|
|
206
|
+
f"{secret}.",
|
|
207
|
+
f"{secret},",
|
|
208
|
+
f"({secret})",
|
|
209
|
+
f"{secret}\nnext",
|
|
210
|
+
]
|
|
211
|
+
for text in samples:
|
|
212
|
+
scrubbed = scrub_text(text)
|
|
213
|
+
assert scrubbed is not None
|
|
214
|
+
assert secret not in scrubbed
|
|
215
|
+
assert "REDACTED" in scrubbed
|
|
216
|
+
redacted = redact_text(text, caller="t")
|
|
217
|
+
assert secret not in redacted
|
|
218
|
+
assert "REDACTED" in redacted
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
def test_event_emit_posts_with_tag_and_token(monkeypatch):
|
|
2
|
+
from swarph_cli.commands import event as evmod
|
|
3
|
+
captured = {}
|
|
4
|
+
monkeypatch.setattr(evmod, "_post_channel", lambda **kw: captured.update(kw) or 0)
|
|
5
|
+
rc = evmod.run_event(["emit", "stage-done", "the output", "--chain-token", "TOK", "--channel", "events"])
|
|
6
|
+
assert rc == 0
|
|
7
|
+
assert captured["channel"] == "events"
|
|
8
|
+
assert captured["event"] == "stage-done"
|
|
9
|
+
assert captured["payload"] == "the output"
|
|
10
|
+
assert captured["chain_token"] == "TOK"
|
|
11
|
+
|
|
12
|
+
def test_event_emit_no_token(monkeypatch):
|
|
13
|
+
from swarph_cli.commands import event as evmod
|
|
14
|
+
captured = {}
|
|
15
|
+
monkeypatch.setattr(evmod, "_post_channel", lambda **kw: captured.update(kw) or 0)
|
|
16
|
+
evmod.run_event(["emit", "e", "p"])
|
|
17
|
+
assert captured.get("chain_token") is None # argparse default=None, not ""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_event_emit_real_envelope_is_sentinel_wrapped(monkeypatch):
|
|
21
|
+
# exercise the REAL _post_channel (not the seam) to prove the content envelope is
|
|
22
|
+
# sentinel-wrapped so P1's guard identifies events unambiguously (a plain channel
|
|
23
|
+
# message won't carry the top-level `swarph_event` key). The CLI chain token is
|
|
24
|
+
# attached on the bytes after redaction, not inside the pre-redact body.
|
|
25
|
+
import json
|
|
26
|
+
from swarph_cli.commands import event as evmod
|
|
27
|
+
monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "lab-ovh")
|
|
28
|
+
monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
|
|
29
|
+
captured = {}
|
|
30
|
+
|
|
31
|
+
def _capture(url, body, token, encoded=None, timeout=10.0):
|
|
32
|
+
captured.update(body=body, encoded=encoded)
|
|
33
|
+
return 200, {"id": 1}
|
|
34
|
+
|
|
35
|
+
monkeypatch.setattr(evmod, "post_json", _capture)
|
|
36
|
+
rc = evmod.run_event(["emit", "stage-done", "out", "--chain-token", "TOK"])
|
|
37
|
+
assert rc == 0
|
|
38
|
+
content = json.loads(captured["body"]["content"])
|
|
39
|
+
assert "swarph_event" in content
|
|
40
|
+
assert content["swarph_event"] == {"event": "stage-done", "payload": "out"}
|
|
41
|
+
assert captured["body"]["kind"] == "fyi" # VALID_KINDS-safe; P1 promotes to first-class
|
|
42
|
+
wire = json.loads(json.loads(captured["encoded"])["content"])
|
|
43
|
+
assert wire["swarph_event"] == {"event": "stage-done", "payload": "out", "chain_token": "TOK"}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_chain_token_reaches_the_wire_and_the_payload_is_still_scrubbed(monkeypatch):
|
|
47
|
+
"""The CLI --chain-token is attached after redaction, like _search.
|
|
48
|
+
|
|
49
|
+
A producer-signed token and a dotless credential-shaped token both reach
|
|
50
|
+
the wire. A classic key in the payload does not. A pasted chain_token
|
|
51
|
+
field is not this bypass.
|
|
52
|
+
"""
|
|
53
|
+
import json
|
|
54
|
+
import secrets
|
|
55
|
+
|
|
56
|
+
from swarph_cli.chain_token import sign_chain_token
|
|
57
|
+
from swarph_cli.commands import event as evmod
|
|
58
|
+
from swarph_cli.redact import redact_text
|
|
59
|
+
|
|
60
|
+
signed = sign_chain_token("producer-secret-xyz", "chain-1", 1, "jti-abc")
|
|
61
|
+
opaque = None
|
|
62
|
+
for _ in range(40):
|
|
63
|
+
candidate = secrets.token_urlsafe(32)
|
|
64
|
+
if redact_text(candidate, caller="t") != candidate:
|
|
65
|
+
opaque = candidate
|
|
66
|
+
break
|
|
67
|
+
assert opaque is not None
|
|
68
|
+
classic = "sk-" + ("cd" * 12)
|
|
69
|
+
|
|
70
|
+
class _Resp:
|
|
71
|
+
status = 200
|
|
72
|
+
|
|
73
|
+
def __enter__(self):
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def __exit__(self, *args):
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
def read(self):
|
|
80
|
+
return b'{"id": 7}'
|
|
81
|
+
|
|
82
|
+
def emit(chain):
|
|
83
|
+
captured = {}
|
|
84
|
+
|
|
85
|
+
def fake_urlopen(req, timeout=None):
|
|
86
|
+
captured["raw"] = req.data
|
|
87
|
+
return _Resp()
|
|
88
|
+
|
|
89
|
+
monkeypatch.setattr(
|
|
90
|
+
"swarph_cli.commands.mesh.urllib.request.urlopen", fake_urlopen
|
|
91
|
+
)
|
|
92
|
+
monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "friendly-coder")
|
|
93
|
+
monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
|
|
94
|
+
rc = evmod.run_event([
|
|
95
|
+
"emit", "stage-done", "note " + classic,
|
|
96
|
+
"--chain-token", chain,
|
|
97
|
+
"--gateway", "http://127.0.0.1:9",
|
|
98
|
+
])
|
|
99
|
+
assert rc == 0, rc
|
|
100
|
+
body = json.loads(captured["raw"].decode("utf-8"))
|
|
101
|
+
return json.loads(body["content"])["swarph_event"]
|
|
102
|
+
|
|
103
|
+
for chain in (signed, opaque):
|
|
104
|
+
ev = emit(chain)
|
|
105
|
+
assert ev["chain_token"] == chain
|
|
106
|
+
assert classic not in ev["payload"]
|
|
107
|
+
assert "REDACTED" in ev["payload"]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_chain_token_on_unparseable_body_refuses_and_sends_nothing(monkeypatch, capsys):
|
|
111
|
+
"""Redaction can break the content JSON (password="}, PASSWORD=", api_key=").
|
|
112
|
+
|
|
113
|
+
--chain-token must refuse by name, send nothing, and exit nonzero.
|
|
114
|
+
No traceback, and neither the payload nor the token reaches the stubbed wire.
|
|
115
|
+
"""
|
|
116
|
+
from swarph_cli.commands import event as evmod
|
|
117
|
+
|
|
118
|
+
token = "chain-token-must-not-leak"
|
|
119
|
+
payloads = (
|
|
120
|
+
'password="}',
|
|
121
|
+
'export PASSWORD=" x',
|
|
122
|
+
'api_key="',
|
|
123
|
+
)
|
|
124
|
+
calls = []
|
|
125
|
+
|
|
126
|
+
class _Resp:
|
|
127
|
+
status = 200
|
|
128
|
+
|
|
129
|
+
def __enter__(self):
|
|
130
|
+
return self
|
|
131
|
+
|
|
132
|
+
def __exit__(self, *args):
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def read(self):
|
|
136
|
+
return b'{"id": 1}'
|
|
137
|
+
|
|
138
|
+
def fake_urlopen(req, timeout=None):
|
|
139
|
+
calls.append(req.data or b"")
|
|
140
|
+
return _Resp()
|
|
141
|
+
|
|
142
|
+
monkeypatch.setattr(
|
|
143
|
+
"swarph_cli.commands.mesh.urllib.request.urlopen", fake_urlopen
|
|
144
|
+
)
|
|
145
|
+
monkeypatch.setattr(evmod, "resolve_self_name", lambda *a, **k: "friendly-coder")
|
|
146
|
+
monkeypatch.setattr(evmod, "resolve_token", lambda *a, **k: "tok")
|
|
147
|
+
|
|
148
|
+
for payload in payloads:
|
|
149
|
+
before = len(calls)
|
|
150
|
+
rc = evmod.run_event([
|
|
151
|
+
"emit", "stage-done", payload,
|
|
152
|
+
"--chain-token", token,
|
|
153
|
+
"--gateway", "http://127.0.0.1:9",
|
|
154
|
+
])
|
|
155
|
+
captured = capsys.readouterr()
|
|
156
|
+
assert rc != 0
|
|
157
|
+
assert "Traceback" not in captured.err
|
|
158
|
+
assert "Traceback" not in captured.out
|
|
159
|
+
assert "_attach_chain_token refused: JSONDecodeError" in captured.err
|
|
160
|
+
assert token not in captured.err
|
|
161
|
+
assert token not in captured.out
|
|
162
|
+
assert payload not in captured.err
|
|
163
|
+
assert payload not in captured.out
|
|
164
|
+
assert calls[before:] == []
|
|
165
|
+
|
|
166
|
+
assert calls == []
|
|
@@ -94,6 +94,45 @@ def test_search_passes_token_when_given():
|
|
|
94
94
|
assert captured["data"]["token"] == "secret"
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def test_search_keeps_a_credential_shaped_token_and_scrubs_the_message():
|
|
98
|
+
"""PR 648: the auth token used to ride inside the redacted body.
|
|
99
|
+
|
|
100
|
+
A gateway-mint shape (token_urlsafe(32)) is often replaced with
|
|
101
|
+
[REDACTED:HIGH_ENTROPY]. The message is still scrubbed.
|
|
102
|
+
"""
|
|
103
|
+
import secrets
|
|
104
|
+
|
|
105
|
+
from swarph_cli.redact import redact_text
|
|
106
|
+
|
|
107
|
+
tok = None
|
|
108
|
+
for _ in range(40):
|
|
109
|
+
candidate = secrets.token_urlsafe(32)
|
|
110
|
+
if redact_text(candidate, caller="t") != candidate:
|
|
111
|
+
tok = candidate
|
|
112
|
+
break
|
|
113
|
+
assert tok is not None, "need one credential-shaped token the redactor would eat"
|
|
114
|
+
classic = "sk-" + ("ab" * 12)
|
|
115
|
+
captured = {}
|
|
116
|
+
|
|
117
|
+
def _fake(req, timeout=None): # noqa: ANN001 - test double
|
|
118
|
+
captured["data"] = json.loads(req.data.decode("utf-8"))
|
|
119
|
+
resp = mock.MagicMock()
|
|
120
|
+
resp.read.return_value = json.dumps({"results": []}).encode("utf-8")
|
|
121
|
+
resp.__enter__ = lambda s: s
|
|
122
|
+
resp.__exit__ = lambda s, *a: False
|
|
123
|
+
return resp
|
|
124
|
+
|
|
125
|
+
with mock.patch(
|
|
126
|
+
"swarph_cli.commands.mcp_server.urllib.request.urlopen", _fake
|
|
127
|
+
):
|
|
128
|
+
mcp_server._search(
|
|
129
|
+
"look " + classic, url="https://metaedge.surf/", token=tok
|
|
130
|
+
)
|
|
131
|
+
assert captured["data"]["token"] == tok
|
|
132
|
+
assert classic not in captured["data"]["message"]
|
|
133
|
+
assert "REDACTED" in captured["data"]["message"]
|
|
134
|
+
|
|
135
|
+
|
|
97
136
|
# --------------------------------------------------------------------------- #
|
|
98
137
|
# _add — parse_uri gate + add.run_add dispatch
|
|
99
138
|
# --------------------------------------------------------------------------- #
|
|
@@ -14,9 +14,15 @@ from swarph_cli.scrub import scrub_text
|
|
|
14
14
|
@pytest.mark.parametrize("secret", [
|
|
15
15
|
"sk-ant-aaaa-bbbb-cccc-dddd",
|
|
16
16
|
"sk-abcdefghijklmnop123456",
|
|
17
|
+
# Hyphenated OpenAI shapes, built here so the source has no key literal.
|
|
18
|
+
# The old [A-Za-z0-9] class stopped at the hyphen (card #1472).
|
|
19
|
+
"sk-proj-" + ("n7" * 12),
|
|
20
|
+
"sk-svcacct-" + ("m8" * 12),
|
|
17
21
|
"xai-abcdefghijklmnop123456",
|
|
18
22
|
# Built at runtime: the literal full shape trips gitleaks gcp-api-key.
|
|
19
23
|
"AIza" + "SyDdI0T9mbB4y7KzQ3wX2vU5tS8rR1pP6oO",
|
|
24
|
+
# 35th character is '-'. A closing \b does not match before the space.
|
|
25
|
+
"AI" + "za" + ("m" * 34) + "-",
|
|
20
26
|
"ghp_abcdefghijklmnopqrstuvwx1234567890",
|
|
21
27
|
# Built at runtime: the literal full shape trips push protection.
|
|
22
28
|
"xoxb-" + "123456789012-abcdefghijklmnop",
|