mixdog 0.7.1
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.
- package/.claude-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.gitattributes +34 -0
- package/.mcp.json +14 -0
- package/ARCHITECTURE.md +77 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +45 -0
- package/DATA-FLOW.md +79 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/SECURITY.md +138 -0
- package/UNINSTALL.md +112 -0
- package/agents/maintenance.md +5 -0
- package/agents/memory-classification.md +30 -0
- package/agents/scheduler-task.md +18 -0
- package/agents/webhook-handler.md +27 -0
- package/agents/worker.md +24 -0
- package/bin/bridge +133 -0
- package/bin/statusline-launcher.mjs +78 -0
- package/bin/statusline-lib.mjs +550 -0
- package/bin/statusline.mjs +607 -0
- package/bun.lock +802 -0
- package/commands/config.md +16 -0
- package/commands/doctor.md +13 -0
- package/commands/setup.md +17 -0
- package/defaults/cycle3-review-prompt.md +90 -0
- package/defaults/hidden-roles.json +65 -0
- package/defaults/memory-chunk-prompt.md +63 -0
- package/defaults/memory-promote-prompt.md +135 -0
- package/defaults/mixdog-config.template.json +27 -0
- package/defaults/user-workflow.json +8 -0
- package/defaults/user-workflow.md +12 -0
- package/hooks/hooks.json +73 -0
- package/hooks/lib/active-instance.cjs +77 -0
- package/hooks/lib/permission-evaluator.cjs +411 -0
- package/hooks/lib/permission-route.cjs +63 -0
- package/hooks/lib/permission-rules.cjs +170 -0
- package/hooks/lib/settings-loader.cjs +116 -0
- package/hooks/post-tool-use.cjs +84 -0
- package/hooks/pre-mcp-sandbox.cjs +158 -0
- package/hooks/pre-tool-subagent.cjs +253 -0
- package/hooks/session-start.cjs +1372 -0
- package/hooks/turn-timer.cjs +82 -0
- package/lib/claude-md-writer.cjs +386 -0
- package/lib/config-cjs.cjs +61 -0
- package/lib/hook-pipe-path.cjs +10 -0
- package/lib/keychain-cjs.cjs +263 -0
- package/lib/plugin-paths.cjs +61 -0
- package/lib/rules-builder.cjs +241 -0
- package/lib/text-utils.cjs +61 -0
- package/native/README.md +117 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +107 -0
- package/prompts/code-review.txt +16 -0
- package/prompts/security-audit.txt +17 -0
- package/rules/bridge/00-common.md +39 -0
- package/rules/bridge/20-skip-protocol.md +18 -0
- package/rules/bridge/30-explorer.md +33 -0
- package/rules/bridge/40-cycle1-agent.md +52 -0
- package/rules/bridge/41-cycle2-agent.md +62 -0
- package/rules/bridge/42-cycle3-agent.md +44 -0
- package/rules/lead/00-tool-lead.md +61 -0
- package/rules/lead/01-general.md +23 -0
- package/rules/lead/02-channels.md +49 -0
- package/rules/lead/03-team.md +27 -0
- package/rules/lead/04-workflow.md +20 -0
- package/rules/shared/00-language.md +14 -0
- package/rules/shared/01-tool.md +138 -0
- package/scripts/bootstrap.mjs +184 -0
- package/scripts/bridge-unify-smoke.mjs +308 -0
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/builtin-utils-smoke.mjs +392 -0
- package/scripts/check-json.mjs +45 -0
- package/scripts/check-syntax-changed.mjs +102 -0
- package/scripts/check-syntax.mjs +58 -0
- package/scripts/code-graph-batch.test.mjs +33 -0
- package/scripts/config-preserve-smoke.mjs +180 -0
- package/scripts/doctor.mjs +484 -0
- package/scripts/edit-normalize-fuzz.mjs +130 -0
- package/scripts/edit-normalize-smoke.mjs +401 -0
- package/scripts/edit-operation-smoke.mjs +369 -0
- package/scripts/edit2-smoke.mjs +63 -0
- package/scripts/fuzzy-e2e.mjs +28 -0
- package/scripts/fuzzy-smoke.mjs +26 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/guard-smoke.mjs +66 -0
- package/scripts/hidden-role-schema-smoke.mjs +162 -0
- package/scripts/hook-routing-smoke.mjs +29 -0
- package/scripts/inject-input.ps1 +204 -0
- package/scripts/io-complex-smoke.mjs +667 -0
- package/scripts/io-explore-bench.mjs +424 -0
- package/scripts/io-guardrails-smoke.mjs +205 -0
- package/scripts/io-mini-bench-baseline.json +11 -0
- package/scripts/io-mini-bench.mjs +216 -0
- package/scripts/io-route-harness.mjs +933 -0
- package/scripts/io-telemetry-report.mjs +691 -0
- package/scripts/mutation-bench.mjs +564 -0
- package/scripts/mutation-io-smoke.mjs +1081 -0
- package/scripts/native-patch-bridge-smoke.mjs +288 -0
- package/scripts/native-patch-smoke.mjs +304 -0
- package/scripts/patch-interior-context-smoke.mjs +49 -0
- package/scripts/patch-newline-utf8-smoke.mjs +157 -0
- package/scripts/perf-hook-smoke.mjs +71 -0
- package/scripts/permission-eval-smoke.mjs +426 -0
- package/scripts/prep-patch.mjs +53 -0
- package/scripts/prep-shim.mjs +96 -0
- package/scripts/provider-cache-smoke.mjs +687 -0
- package/scripts/report-runtime-health.mjs +132 -0
- package/scripts/run-mcp.mjs +1547 -0
- package/scripts/salvage-v4a-shatter.test.mjs +58 -0
- package/scripts/scoped-cache-io-smoke.mjs +103 -0
- package/scripts/shell-policy-round3-smoke.mjs +46 -0
- package/scripts/smoke-runtime-negative.ps1 +100 -0
- package/scripts/smoke-runtime-negative.sh +95 -0
- package/scripts/stall-policy-smoke.mjs +50 -0
- package/scripts/start-memory-worker.mjs +23 -0
- package/scripts/statusline-launcher-smoke.mjs +82 -0
- package/scripts/stress-atomic-write.mjs +1028 -0
- package/scripts/test-config-rmw-restore.mjs +122 -0
- package/scripts/test-fault-inject.mjs +164 -0
- package/scripts/test-large-file.mjs +174 -0
- package/scripts/tool-edge-smoke.mjs +209 -0
- package/scripts/uninstall.mjs +201 -0
- package/scripts/webhook-selfheal-smoke.mjs +29 -0
- package/scripts/write-overwrite-guard-smoke.mjs +56 -0
- package/server-main.mjs +3055 -0
- package/server.mjs +468 -0
- package/setup/config-merge.mjs +254 -0
- package/setup/install.mjs +120 -0
- package/setup/launch-core.mjs +507 -0
- package/setup/launch.mjs +101 -0
- package/setup/setup-server.mjs +3206 -0
- package/setup/setup.html +3693 -0
- package/skills/retro-skill-proposer/SKILL.md +92 -0
- package/skills/schedule-add/SKILL.md +77 -0
- package/skills/setup/SKILL.md +346 -0
- package/skills/webhook-add/SKILL.md +81 -0
- package/src/agent/bridge-stall-watchdog.mjs +337 -0
- package/src/agent/index.mjs +2138 -0
- package/src/agent/orchestrator/activity-bus.mjs +38 -0
- package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
- package/src/agent/orchestrator/bridge-retry.mjs +220 -0
- package/src/agent/orchestrator/bridge-trace.mjs +583 -0
- package/src/agent/orchestrator/cache-mtime.mjs +58 -0
- package/src/agent/orchestrator/config.mjs +358 -0
- package/src/agent/orchestrator/context/collect.mjs +651 -0
- package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
- package/src/agent/orchestrator/drain-registry.mjs +50 -0
- package/src/agent/orchestrator/explore-validator.mjs +8 -0
- package/src/agent/orchestrator/internal-roles.mjs +118 -0
- package/src/agent/orchestrator/internal-tools.mjs +88 -0
- package/src/agent/orchestrator/jobs.mjs +116 -0
- package/src/agent/orchestrator/mcp/client.mjs +364 -0
- package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
- package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
- package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
- package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
- package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
- package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
- package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
- package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
- package/src/agent/orchestrator/providers/registry.mjs +192 -0
- package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
- package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
- package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
- package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
- package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
- package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
- package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
- package/src/agent/orchestrator/session/cache/util.mjs +49 -0
- package/src/agent/orchestrator/session/loop.mjs +1478 -0
- package/src/agent/orchestrator/session/manager.mjs +1975 -0
- package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
- package/src/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
- package/src/agent/orchestrator/session/store.mjs +624 -0
- package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
- package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
- package/src/agent/orchestrator/session/trim.mjs +491 -0
- package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
- package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
- package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
- package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
- package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
- package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
- package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
- package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
- package/src/agent/orchestrator/stall-policy.mjs +195 -0
- package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
- package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
- package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
- package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
- package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
- package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
- package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
- package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
- package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
- package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
- package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
- package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
- package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
- package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
- package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
- package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
- package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
- package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
- package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
- package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
- package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
- package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
- package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
- package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
- package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
- package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
- package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
- package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
- package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
- package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
- package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
- package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
- package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
- package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
- package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
- package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
- package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
- package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
- package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
- package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
- package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
- package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
- package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
- package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
- package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
- package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
- package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
- package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
- package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
- package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
- package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
- package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
- package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
- package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
- package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
- package/src/agent/orchestrator/tools/builtin.mjs +500 -0
- package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
- package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
- package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
- package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
- package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
- package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
- package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
- package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
- package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
- package/src/agent/orchestrator/tools/host-input.mjs +204 -0
- package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
- package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
- package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
- package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
- package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
- package/src/agent/orchestrator/tools/patch.mjs +2754 -0
- package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
- package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
- package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
- package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
- package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
- package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
- package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
- package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
- package/src/agent/orchestrator/workflow-store.mjs +93 -0
- package/src/agent/tool-defs.mjs +103 -0
- package/src/channels/backends/discord.mjs +784 -0
- package/src/channels/data/voice-runtime-manifest.json +138 -0
- package/src/channels/index.mjs +3229 -0
- package/src/channels/lib/cli-worker-host.mjs +12 -0
- package/src/channels/lib/config-lock.mjs +13 -0
- package/src/channels/lib/config.mjs +292 -0
- package/src/channels/lib/drop-trace.mjs +71 -0
- package/src/channels/lib/event-pipeline.mjs +81 -0
- package/src/channels/lib/event-queue.mjs +345 -0
- package/src/channels/lib/executor.mjs +168 -0
- package/src/channels/lib/format.mjs +188 -0
- package/src/channels/lib/holidays.mjs +138 -0
- package/src/channels/lib/hook-pipe-server.mjs +802 -0
- package/src/channels/lib/interaction-workflows.mjs +184 -0
- package/src/channels/lib/memory-client.mjs +149 -0
- package/src/channels/lib/output-forwarder.mjs +765 -0
- package/src/channels/lib/runtime-paths.mjs +479 -0
- package/src/channels/lib/scheduler.mjs +723 -0
- package/src/channels/lib/session-control.mjs +36 -0
- package/src/channels/lib/session-discovery.mjs +103 -0
- package/src/channels/lib/settings.mjs +11 -0
- package/src/channels/lib/state-file.mjs +68 -0
- package/src/channels/lib/status-snapshot.mjs +219 -0
- package/src/channels/lib/tool-format.mjs +140 -0
- package/src/channels/lib/transcript-discovery.mjs +195 -0
- package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
- package/src/channels/lib/webhook.mjs +1179 -0
- package/src/channels/lib/whisper-server.mjs +477 -0
- package/src/channels/tool-defs.mjs +170 -0
- package/src/daemon/host.mjs +118 -0
- package/src/daemon/mcp-transport.mjs +47 -0
- package/src/daemon/session.mjs +100 -0
- package/src/daemon/thin-client.mjs +71 -0
- package/src/daemon/transport.mjs +163 -0
- package/src/memory/data/runtime-manifest.json +40 -0
- package/src/memory/index.mjs +3305 -0
- package/src/memory/lib/agent-ipc.mjs +93 -0
- package/src/memory/lib/bridge-trace-queries.mjs +120 -0
- package/src/memory/lib/core-memory-store.mjs +330 -0
- package/src/memory/lib/embedding-provider.mjs +269 -0
- package/src/memory/lib/embedding-worker.mjs +323 -0
- package/src/memory/lib/llm-worker-host.mjs +17 -0
- package/src/memory/lib/memory-cycle.mjs +11 -0
- package/src/memory/lib/memory-cycle1.mjs +641 -0
- package/src/memory/lib/memory-cycle2.mjs +1284 -0
- package/src/memory/lib/memory-cycle3.mjs +540 -0
- package/src/memory/lib/memory-embed.mjs +299 -0
- package/src/memory/lib/memory-extraction.mjs +5 -0
- package/src/memory/lib/memory-maintenance-store.mjs +32 -0
- package/src/memory/lib/memory-ops-policy.mjs +190 -0
- package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
- package/src/memory/lib/memory-recall-read-query.mjs +7 -0
- package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
- package/src/memory/lib/memory-recall-store.mjs +621 -0
- package/src/memory/lib/memory-retrievers.mjs +112 -0
- package/src/memory/lib/memory-score.mjs +71 -0
- package/src/memory/lib/memory-text-utils.mjs +58 -0
- package/src/memory/lib/memory.mjs +412 -0
- package/src/memory/lib/model-profile.mjs +85 -0
- package/src/memory/lib/pg/adapter.mjs +308 -0
- package/src/memory/lib/pg/process.mjs +360 -0
- package/src/memory/lib/pg/supervisor.mjs +396 -0
- package/src/memory/lib/project-id-resolver.mjs +86 -0
- package/src/memory/lib/runtime-fetcher.mjs +442 -0
- package/src/memory/lib/trace-store.mjs +728 -0
- package/src/memory/tool-defs.mjs +79 -0
- package/src/search/index.mjs +1173 -0
- package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
- package/src/search/lib/backends/exa.mjs +50 -0
- package/src/search/lib/backends/firecrawl.mjs +61 -0
- package/src/search/lib/backends/gemini-api.mjs +83 -0
- package/src/search/lib/backends/grok-oauth.mjs +86 -0
- package/src/search/lib/backends/index.mjs +150 -0
- package/src/search/lib/backends/openai-api.mjs +144 -0
- package/src/search/lib/backends/openai-oauth.mjs +98 -0
- package/src/search/lib/backends/openai-web-search.mjs +76 -0
- package/src/search/lib/backends/tavily.mjs +55 -0
- package/src/search/lib/backends/xai-api.mjs +113 -0
- package/src/search/lib/cache.mjs +131 -0
- package/src/search/lib/config.mjs +192 -0
- package/src/search/lib/formatter.mjs +115 -0
- package/src/search/lib/provider-usage.mjs +67 -0
- package/src/search/lib/providers.mjs +47 -0
- package/src/search/lib/search-intent.mjs +109 -0
- package/src/search/lib/setup-handler.mjs +261 -0
- package/src/search/lib/state.mjs +201 -0
- package/src/search/lib/web-tools.mjs +1207 -0
- package/src/search/tool-defs.mjs +83 -0
- package/src/setup/defender-exclusion.mjs +183 -0
- package/src/shared/abort-controller.mjs +15 -0
- package/src/shared/atomic-file.mjs +420 -0
- package/src/shared/config.mjs +350 -0
- package/src/shared/daemon-recycle.mjs +108 -0
- package/src/shared/disable-claude-builtins.mjs +88 -0
- package/src/shared/err-text.mjs +12 -0
- package/src/shared/llm/cost.mjs +66 -0
- package/src/shared/llm/http-agent.mjs +123 -0
- package/src/shared/llm/index.mjs +41 -0
- package/src/shared/llm/pid-cleanup.mjs +27 -0
- package/src/shared/llm/usage-log.mjs +47 -0
- package/src/shared/plugin-paths.mjs +58 -0
- package/src/shared/schedules-store.mjs +70 -0
- package/src/shared/seed.mjs +119 -0
- package/src/shared/user-cwd.mjs +213 -0
- package/src/shared/user-data-guard.mjs +238 -0
- package/src/status/aggregator.mjs +584 -0
- package/src/status/server.mjs +413 -0
- package/tools.json +1653 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
2
|
+
import { execFileSync } from "child_process";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join, resolve } from "path";
|
|
5
|
+
import { ensureDir, readJsonFile, removeFileIfExists, writeJsonFile } from "./state-file.mjs";
|
|
6
|
+
const RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT
|
|
7
|
+
? resolve(process.env.MIXDOG_RUNTIME_ROOT)
|
|
8
|
+
: join(tmpdir(), "mixdog");
|
|
9
|
+
const OWNER_DIR = join(RUNTIME_ROOT, "owners");
|
|
10
|
+
const ACTIVE_INSTANCE_FILE = join(RUNTIME_ROOT, "active-instance.json");
|
|
11
|
+
const RUNTIME_STALE_TTL = 24 * 60 * 60 * 1e3;
|
|
12
|
+
const STATUS_FILE_TTL = 6 * 60 * 60 * 1e3;
|
|
13
|
+
const TMP_FILE_TTL = 60 * 60 * 1e3;
|
|
14
|
+
function sanitize(value) {
|
|
15
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
16
|
+
}
|
|
17
|
+
function forEachFile(dirPath, visit) {
|
|
18
|
+
try {
|
|
19
|
+
for (const fileName of readdirSync(dirPath)) {
|
|
20
|
+
visit(join(dirPath, fileName), fileName);
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function ensureRuntimeDirs() {
|
|
26
|
+
ensureDir(RUNTIME_ROOT);
|
|
27
|
+
ensureDir(OWNER_DIR);
|
|
28
|
+
}
|
|
29
|
+
function makeInstanceId(pid = process.pid) {
|
|
30
|
+
return String(pid);
|
|
31
|
+
}
|
|
32
|
+
function parsePositivePid(value) {
|
|
33
|
+
const pid = Number(value);
|
|
34
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
35
|
+
}
|
|
36
|
+
function getTerminalLeadPid() {
|
|
37
|
+
return parsePositivePid(process.env.MIXDOG_SUPERVISOR_PID) ?? process.pid;
|
|
38
|
+
}
|
|
39
|
+
// Pinned-owner default: ON. The first session to claim ownership auto-pins
|
|
40
|
+
// itself and keeps the seat until its PID dies. Opt out per session with
|
|
41
|
+
// MIXDOG_PIN_OWNER=0 (or false/no/off) when you want the legacy 10 s stale-
|
|
42
|
+
// window takeover behavior — e.g. throwaway shells that should never become
|
|
43
|
+
// the sticky main.
|
|
44
|
+
function isOwnerPinEnabled() {
|
|
45
|
+
const v = process.env.MIXDOG_PIN_OWNER;
|
|
46
|
+
if (v === undefined || v === null || v === "") return true;
|
|
47
|
+
const low = String(v).toLowerCase();
|
|
48
|
+
return !(low === "0" || low === "false" || low === "no" || low === "off");
|
|
49
|
+
}
|
|
50
|
+
function getServerPid() {
|
|
51
|
+
return parsePositivePid(process.env.MIXDOG_SERVER_PID) ?? (process.env.MIXDOG_WORKER_MODE === "1" ? null : process.pid);
|
|
52
|
+
}
|
|
53
|
+
function getActiveOwnerPid(state) {
|
|
54
|
+
return parsePositivePid(state?.ownerLeadPid)
|
|
55
|
+
?? parsePositivePid(state?.terminalLeadPid)
|
|
56
|
+
?? parsePositivePid(state?.supervisor_pid)
|
|
57
|
+
?? parsePositivePid(state?.instanceId);
|
|
58
|
+
}
|
|
59
|
+
function isPidAlive(pid) {
|
|
60
|
+
const n = parsePositivePid(pid);
|
|
61
|
+
if (!n) return false;
|
|
62
|
+
try {
|
|
63
|
+
process.kill(n, 0);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return e?.code === "EPERM";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function activeInstanceStaleReason(state) {
|
|
70
|
+
const ownerPid = getActiveOwnerPid(state);
|
|
71
|
+
if (!isPidAlive(ownerPid)) return `owner PID ${ownerPid ?? "unknown"} is dead`;
|
|
72
|
+
const channelsPid = parsePositivePid(state?.channels_pid);
|
|
73
|
+
if (channelsPid && !isPidAlive(channelsPid)) return `channels PID ${channelsPid} is dead`;
|
|
74
|
+
const workerPid = parsePositivePid(state?.worker_pid);
|
|
75
|
+
if (workerPid && !isPidAlive(workerPid)) return `worker PID ${workerPid} is dead`;
|
|
76
|
+
const serverPid = parsePositivePid(state?.server_pid);
|
|
77
|
+
if (serverPid && !isPidAlive(serverPid)) return `server PID ${serverPid} is dead`;
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
function buildRuntimeIdentity() {
|
|
81
|
+
const terminalLeadPid = getTerminalLeadPid();
|
|
82
|
+
const serverPid = getServerPid();
|
|
83
|
+
return {
|
|
84
|
+
ownerLeadPid: terminalLeadPid,
|
|
85
|
+
terminalLeadPid,
|
|
86
|
+
supervisor_pid: terminalLeadPid,
|
|
87
|
+
server_pid: serverPid,
|
|
88
|
+
worker_pid: process.pid,
|
|
89
|
+
...process.env.MIXDOG_WORKER_MODE === "1" ? { channels_pid: process.pid } : {},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function getTurnEndPath(instanceId) {
|
|
93
|
+
return join(RUNTIME_ROOT, `turn-end-${sanitize(instanceId)}`);
|
|
94
|
+
}
|
|
95
|
+
function getStatusPath(instanceId) {
|
|
96
|
+
return join(RUNTIME_ROOT, `status-${sanitize(instanceId)}.json`);
|
|
97
|
+
}
|
|
98
|
+
function getControlPath(instanceId) {
|
|
99
|
+
return join(RUNTIME_ROOT, `control-${sanitize(instanceId)}.json`);
|
|
100
|
+
}
|
|
101
|
+
function getControlResponsePath(instanceId) {
|
|
102
|
+
return join(RUNTIME_ROOT, `control-${sanitize(instanceId)}.response.json`);
|
|
103
|
+
}
|
|
104
|
+
function getPermissionResultPath(instanceId, uuid) {
|
|
105
|
+
return join(RUNTIME_ROOT, `perm-${sanitize(instanceId)}-${sanitize(uuid)}.result`);
|
|
106
|
+
}
|
|
107
|
+
function getStopFlagPath(instanceId) {
|
|
108
|
+
return join(RUNTIME_ROOT, `stop-${sanitize(instanceId)}.flag`);
|
|
109
|
+
}
|
|
110
|
+
function getChannelOwnerPath(channelId) {
|
|
111
|
+
return join(OWNER_DIR, `${sanitize(channelId)}.json`);
|
|
112
|
+
}
|
|
113
|
+
function readActiveInstance() {
|
|
114
|
+
let state = readJsonFile(ACTIVE_INSTANCE_FILE, null);
|
|
115
|
+
if (!state) {
|
|
116
|
+
// Transient read during an atomic rename may yield empty/partial content.
|
|
117
|
+
// Retry once after 50 ms before giving up.
|
|
118
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
|
|
119
|
+
state = readJsonFile(ACTIVE_INSTANCE_FILE, null);
|
|
120
|
+
if (!state) return null;
|
|
121
|
+
}
|
|
122
|
+
const staleReason = activeInstanceStaleReason(state);
|
|
123
|
+
if (staleReason) {
|
|
124
|
+
// Owner is dead — but the file may carry process-independent runtime
|
|
125
|
+
// state (memory_port, pg_*) that next callers want to preserve. Wiping
|
|
126
|
+
// the whole file forced the next refresh to rebuild from {} and drop
|
|
127
|
+
// memory_port, which broke the scheduler and MCP memory lookup
|
|
128
|
+
// until the memory worker re-advertised. Instead, clear only the
|
|
129
|
+
// owner-identity fields and keep the rest as a stale-but-useful prev
|
|
130
|
+
// for the next refresh. refreshActiveInstance still treats the
|
|
131
|
+
// returned state as "no live owner" via getActiveOwnerPid downstream.
|
|
132
|
+
const {
|
|
133
|
+
pinned: _stalePinned,
|
|
134
|
+
instanceId: _staleId,
|
|
135
|
+
ownerLeadPid: _staleOwner,
|
|
136
|
+
terminalLeadPid: _staleTerm,
|
|
137
|
+
supervisor_pid: _staleSup,
|
|
138
|
+
server_pid: _staleServer,
|
|
139
|
+
worker_pid: _staleWorker,
|
|
140
|
+
channels_pid: _staleChannels,
|
|
141
|
+
supervisor_started_at: _staleStart,
|
|
142
|
+
server_started_at: _staleServerStart,
|
|
143
|
+
httpPort: _staleHttpPort,
|
|
144
|
+
backendReady: _staleBackendReady,
|
|
145
|
+
turnEndFile: _staleTurnEnd,
|
|
146
|
+
statusFile: _staleStatus,
|
|
147
|
+
...stableRest
|
|
148
|
+
} = state ?? {};
|
|
149
|
+
const ownerFieldsAlreadyEmpty = _staleId === undefined
|
|
150
|
+
&& _staleOwner === undefined
|
|
151
|
+
&& _staleTerm === undefined
|
|
152
|
+
&& _staleSup === undefined;
|
|
153
|
+
if (!ownerFieldsAlreadyEmpty) {
|
|
154
|
+
process.stderr.write(`mixdog: stale active-instance.json (${staleReason}), clearing owner fields\n`);
|
|
155
|
+
try { writeActiveInstance({ ...stableRest, updatedAt: Date.now() }); } catch {}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
return state;
|
|
160
|
+
}
|
|
161
|
+
function writeActiveInstance(state) {
|
|
162
|
+
ensureRuntimeDirs();
|
|
163
|
+
writeJsonFile(ACTIVE_INSTANCE_FILE, state);
|
|
164
|
+
}
|
|
165
|
+
function buildActiveInstanceState(instanceId, meta) {
|
|
166
|
+
return {
|
|
167
|
+
instanceId,
|
|
168
|
+
...buildRuntimeIdentity(),
|
|
169
|
+
supervisor_started_at: Date.now(),
|
|
170
|
+
server_started_at: Date.now(),
|
|
171
|
+
updatedAt: Date.now(),
|
|
172
|
+
turnEndFile: getTurnEndPath(instanceId),
|
|
173
|
+
statusFile: getStatusPath(instanceId),
|
|
174
|
+
...meta?.channelId ? { channelId: meta.channelId } : {},
|
|
175
|
+
...meta?.transcriptPath ? { transcriptPath: meta.transcriptPath } : {},
|
|
176
|
+
...meta?.httpPort ? { httpPort: meta.httpPort } : {},
|
|
177
|
+
...meta?.memory_port ? { memory_port: meta.memory_port } : {},
|
|
178
|
+
...typeof meta?.backendReady === "boolean" ? { backendReady: meta.backendReady } : {}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function refreshActiveInstance(instanceId, meta) {
|
|
182
|
+
const prev = readActiveInstance();
|
|
183
|
+
// readActiveInstance returns null when the recorded owner PID is dead,
|
|
184
|
+
// even after the dead-owner fix preserves stable fields on disk. For
|
|
185
|
+
// the preservedExtra spread below we still need those fields, so read
|
|
186
|
+
// the raw file directly as a fallback when prev is null.
|
|
187
|
+
const prevForPreserve = prev ?? readJsonFile(ACTIVE_INSTANCE_FILE, null);
|
|
188
|
+
// Drop stale fields (pid/startedAt) written by older server versions.
|
|
189
|
+
const { pid: _legacyPid, startedAt: _legacyStartedAt, ...prevRest } = prev ?? {};
|
|
190
|
+
const identity = buildRuntimeIdentity();
|
|
191
|
+
// server_started_at tracks the CURRENT server_pid's start time so the
|
|
192
|
+
// dev-sync barrier can verify the CHILD's freshness (the supervisor's
|
|
193
|
+
// supervisor_started_at is stable across child respawns and cannot).
|
|
194
|
+
// Preserve across refreshes when server_pid is unchanged; stamp fresh
|
|
195
|
+
// when server_pid is new/changed or there is no prev advert.
|
|
196
|
+
const prevServerPid = parsePositivePid(prevForPreserve?.server_pid);
|
|
197
|
+
const prevServerStartedAt = Number(prevForPreserve?.server_started_at);
|
|
198
|
+
const serverStartedAt = (
|
|
199
|
+
prevServerPid !== null
|
|
200
|
+
&& identity.server_pid !== null
|
|
201
|
+
&& prevServerPid === identity.server_pid
|
|
202
|
+
&& Number.isFinite(prevServerStartedAt)
|
|
203
|
+
) ? prevServerStartedAt : Date.now();
|
|
204
|
+
const next = {
|
|
205
|
+
...(prev?.instanceId === instanceId ? prevRest : buildActiveInstanceState(instanceId)),
|
|
206
|
+
...identity,
|
|
207
|
+
server_started_at: serverStartedAt,
|
|
208
|
+
updatedAt: Date.now(),
|
|
209
|
+
...meta?.channelId ? { channelId: meta.channelId } : {},
|
|
210
|
+
...meta?.transcriptPath ? { transcriptPath: meta.transcriptPath } : {},
|
|
211
|
+
...meta?.httpPort ? { httpPort: meta.httpPort } : {},
|
|
212
|
+
...meta?.memory_port ? { memory_port: meta.memory_port } : {},
|
|
213
|
+
...typeof meta?.backendReady === "boolean" ? { backendReady: meta.backendReady } : {},
|
|
214
|
+
};
|
|
215
|
+
// Pinned ownership (default ON): each refresh reasserts the pinned flag
|
|
216
|
+
// from the current process's env. Other processes refreshing carry their
|
|
217
|
+
// own env, so the flag never outlives the pinned process. Set
|
|
218
|
+
// MIXDOG_PIN_OWNER=0 to opt out and revert to stale-window takeover.
|
|
219
|
+
if (isOwnerPinEnabled()) next.pinned = true;
|
|
220
|
+
else delete next.pinned;
|
|
221
|
+
// I1: pg_* spreads FIRST so newFields above win on conflict.
|
|
222
|
+
// prev.pg_port='A', meta.httpPort-adjacent pg_port='B' → result.pg_port='B'.
|
|
223
|
+
// memory_port: preserve when the advertising memory worker is still alive
|
|
224
|
+
// (sync process.kill(pid,0); ESRCH=dead). Same server_pid restart still
|
|
225
|
+
// preserves; a live owner from another session must not be dropped on handoff.
|
|
226
|
+
const preservedExtra = Object.fromEntries(
|
|
227
|
+
Object.entries(prevForPreserve ?? {}).filter(([k]) => k.startsWith('pg_'))
|
|
228
|
+
);
|
|
229
|
+
const prevMemoryServerPid = parsePositivePid(prevForPreserve?.memory_server_pid);
|
|
230
|
+
const prevMemoryOwnerAlive = (() => {
|
|
231
|
+
if (prevMemoryServerPid === null) return false;
|
|
232
|
+
try {
|
|
233
|
+
process.kill(prevMemoryServerPid, 0);
|
|
234
|
+
return true;
|
|
235
|
+
} catch (e) {
|
|
236
|
+
if (e && e.code === "ESRCH") return false;
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
})();
|
|
240
|
+
const sameMemoryAdvertiser =
|
|
241
|
+
prevMemoryServerPid !== null &&
|
|
242
|
+
identity.server_pid !== null &&
|
|
243
|
+
prevMemoryServerPid === identity.server_pid;
|
|
244
|
+
if (sameMemoryAdvertiser || prevMemoryOwnerAlive) {
|
|
245
|
+
if (prevForPreserve && Object.prototype.hasOwnProperty.call(prevForPreserve, 'memory_port')) {
|
|
246
|
+
preservedExtra.memory_port = prevForPreserve.memory_port;
|
|
247
|
+
preservedExtra.memory_server_pid = prevMemoryServerPid;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const nextWithExtra = { ...preservedExtra, ...next };
|
|
251
|
+
writeActiveInstance(nextWithExtra);
|
|
252
|
+
return nextWithExtra;
|
|
253
|
+
}
|
|
254
|
+
const SERVER_PID_FILE = join(
|
|
255
|
+
RUNTIME_ROOT,
|
|
256
|
+
`server-${sanitize(process.env.CLAUDE_PLUGIN_DATA ?? "default")}.pid`
|
|
257
|
+
);
|
|
258
|
+
function looksLikeTribChannelsServer(pid) {
|
|
259
|
+
const pidStr = String(pid);
|
|
260
|
+
if (process.platform === "win32") {
|
|
261
|
+
try {
|
|
262
|
+
const out = execFileSync("tasklist", ["/FI", `PID eq ${pidStr}`, "/FO", "CSV", "/NH"], { encoding: "utf8", windowsHide: true }).trim();
|
|
263
|
+
if (!out || out.includes("No tasks")) return false;
|
|
264
|
+
const lower = out.toLowerCase();
|
|
265
|
+
return lower.includes("server.ts") && (lower.includes("node") || lower.includes("tsx") || lower.includes("mixdog"));
|
|
266
|
+
} catch {
|
|
267
|
+
// Transient probe failure: treat as unknown, not server (default-deny).
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
const cmd = execFileSync("ps", ["-o", "command=", "-p", pidStr], { encoding: "utf8", windowsHide: true }).trim();
|
|
273
|
+
if (!cmd) return false;
|
|
274
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT ?? "";
|
|
275
|
+
if (!cmd.includes("server.ts")) return false;
|
|
276
|
+
return cmd.includes("mixdog") || pluginRoot && cmd.includes(pluginRoot) || cmd.includes("tsx server.ts") || cmd.includes("node") && cmd.includes("server");
|
|
277
|
+
} catch {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function waitForExit(pid, timeoutMs) {
|
|
282
|
+
const start = Date.now();
|
|
283
|
+
while (Date.now() - start < timeoutMs) {
|
|
284
|
+
try {
|
|
285
|
+
process.kill(pid, 0);
|
|
286
|
+
} catch {
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
// Use a short synchronous pause via a tiny Atomics spin on a shared buffer
|
|
290
|
+
// so the event loop is not fully starved. Kept synchronous because callers
|
|
291
|
+
// (killSinglePid) are sync; 100 ms sleep via Atomics.wait on a 1-element buffer.
|
|
292
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100);
|
|
293
|
+
}
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
function killSinglePid(pid) {
|
|
297
|
+
if (process.platform === "win32") {
|
|
298
|
+
// Capture creation time before kill to guard against PID reuse.
|
|
299
|
+
let _pidCreation = null;
|
|
300
|
+
try {
|
|
301
|
+
const _wmic = execFileSync("wmic", ["process", "where", `ProcessId=${pid}`, "get", "CreationDate", "/VALUE"], { encoding: "utf8", timeout: 3e3, windowsHide: true }).trim();
|
|
302
|
+
const _m = _wmic.match(/CreationDate=(\S+)/);
|
|
303
|
+
if (_m) _pidCreation = _m[1];
|
|
304
|
+
} catch { /* wmic unavailable — skip start_time guard */ }
|
|
305
|
+
if (_pidCreation !== null) {
|
|
306
|
+
// Re-verify: if a new process has already taken the PID, abort.
|
|
307
|
+
try {
|
|
308
|
+
const _check = execFileSync("wmic", ["process", "where", `ProcessId=${pid}`, "get", "CreationDate", "/VALUE"], { encoding: "utf8", timeout: 3e3, windowsHide: true }).trim();
|
|
309
|
+
const _cm = _check.match(/CreationDate=(\S+)/);
|
|
310
|
+
if (!_cm || _cm[1] !== _pidCreation) {
|
|
311
|
+
console.warn(`[singleton] PID ${pid} creation time changed — aborting kill (PID reuse detected)`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
} catch { /* process gone — nothing to kill */ return; }
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
execFileSync("taskkill", ["/F", "/T", "/PID", String(pid)], { encoding: "utf8", timeout: 5e3, windowsHide: true });
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.warn(`[singleton] taskkill failed for PID ${pid}:`, err.message);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
// Capture start time before kill to guard against PID reuse.
|
|
323
|
+
let _pidStart = null;
|
|
324
|
+
try {
|
|
325
|
+
_pidStart = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", windowsHide: true }).trim();
|
|
326
|
+
} catch { /* ps unavailable — skip start_time guard */ }
|
|
327
|
+
if (_pidStart) {
|
|
328
|
+
try {
|
|
329
|
+
const _cs = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", windowsHide: true }).trim();
|
|
330
|
+
if (_cs !== _pidStart) {
|
|
331
|
+
console.warn(`[singleton] PID ${pid} start time changed — aborting kill (PID reuse detected)`);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
} catch { return; }
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
process.kill(pid, "SIGTERM");
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
if (!waitForExit(pid, 2e3)) {
|
|
341
|
+
try {
|
|
342
|
+
process.kill(pid, "SIGKILL");
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
if (!waitForExit(pid, 1e3)) {
|
|
346
|
+
console.warn(`[singleton] failed to kill previous server PID ${pid}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function killAllPreviousServers() {
|
|
352
|
+
try {
|
|
353
|
+
const oldPid = parseInt(readFileSync(SERVER_PID_FILE, "utf8").trim(), 10);
|
|
354
|
+
if (oldPid && oldPid !== process.pid && oldPid !== process.ppid) {
|
|
355
|
+
try {
|
|
356
|
+
process.kill(oldPid, 0);
|
|
357
|
+
} catch {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (looksLikeTribChannelsServer(oldPid) === true) {
|
|
361
|
+
killSinglePid(oldPid);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function writeServerPid() {
|
|
368
|
+
ensureRuntimeDirs();
|
|
369
|
+
writeFileSync(SERVER_PID_FILE, String(process.pid));
|
|
370
|
+
}
|
|
371
|
+
function clearServerPid() {
|
|
372
|
+
try {
|
|
373
|
+
const current = readFileSync(SERVER_PID_FILE, "utf8").trim();
|
|
374
|
+
if (current === String(process.pid)) removeFileIfExists(SERVER_PID_FILE);
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function cleanupStaleRuntimeFiles(now = Date.now()) {
|
|
379
|
+
ensureRuntimeDirs();
|
|
380
|
+
forEachFile(RUNTIME_ROOT, (fullPath, file) => {
|
|
381
|
+
if (file === "owners" || file === "active-instance.json") return;
|
|
382
|
+
try {
|
|
383
|
+
const heartbeat = /^supervisor-heartbeat\.(\d+)\.json$/.exec(file);
|
|
384
|
+
if (heartbeat) {
|
|
385
|
+
const pid = Number(heartbeat[1]);
|
|
386
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
387
|
+
try { process.kill(pid, 0); }
|
|
388
|
+
catch { removeFileIfExists(fullPath); return; }
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (/^server-.*\.pid$/.test(file)) {
|
|
392
|
+
let pid = NaN;
|
|
393
|
+
try { pid = Number(readFileSync(fullPath, "utf8").trim()); } catch {}
|
|
394
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
395
|
+
try { process.kill(pid, 0); }
|
|
396
|
+
catch { removeFileIfExists(fullPath); return; }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const age = now - statSync(fullPath).mtimeMs;
|
|
400
|
+
// status snapshots and atomic-write .tmp leftovers churn quickly;
|
|
401
|
+
// tighter TTLs keep dead-process residue from accumulating.
|
|
402
|
+
let ttl = RUNTIME_STALE_TTL;
|
|
403
|
+
if (/^status-.*\.json$/.test(file)) ttl = STATUS_FILE_TTL;
|
|
404
|
+
else if (/\.tmp$/.test(file)) ttl = TMP_FILE_TTL;
|
|
405
|
+
if (age > ttl) removeFileIfExists(fullPath);
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
forEachFile(OWNER_DIR, (fullPath) => {
|
|
410
|
+
try {
|
|
411
|
+
// Owner liveness check beats mtime — a record pointing at a dead
|
|
412
|
+
// instanceId is stale immediately regardless of when it was written.
|
|
413
|
+
const owner = readJsonFile(fullPath, null);
|
|
414
|
+
const ownerPid = Number(owner?.pid ?? owner?.instanceId);
|
|
415
|
+
if (Number.isFinite(ownerPid) && ownerPid > 0) {
|
|
416
|
+
try { process.kill(ownerPid, 0); }
|
|
417
|
+
catch { removeFileIfExists(fullPath); return; }
|
|
418
|
+
}
|
|
419
|
+
if (now - statSync(fullPath).mtimeMs > RUNTIME_STALE_TTL) removeFileIfExists(fullPath);
|
|
420
|
+
} catch {
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function cleanupInstanceRuntimeFiles(instanceId) {
|
|
425
|
+
const targets = [
|
|
426
|
+
getTurnEndPath(instanceId),
|
|
427
|
+
getStatusPath(instanceId),
|
|
428
|
+
getControlPath(instanceId),
|
|
429
|
+
getControlResponsePath(instanceId),
|
|
430
|
+
getStopFlagPath(instanceId)
|
|
431
|
+
];
|
|
432
|
+
for (const target of targets) {
|
|
433
|
+
removeFileIfExists(target);
|
|
434
|
+
}
|
|
435
|
+
forEachFile(RUNTIME_ROOT, (fullPath, file) => {
|
|
436
|
+
if (file.startsWith(`perm-${sanitize(instanceId)}-`)) {
|
|
437
|
+
removeFileIfExists(fullPath);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function releaseOwnedChannelLocks(instanceId) {
|
|
442
|
+
forEachFile(OWNER_DIR, (fullPath) => {
|
|
443
|
+
const owner = readJsonFile(fullPath, null);
|
|
444
|
+
if (owner?.instanceId === instanceId) removeFileIfExists(fullPath);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function clearActiveInstance(instanceId) {
|
|
448
|
+
const active = readActiveInstance();
|
|
449
|
+
if (active?.instanceId !== instanceId) return;
|
|
450
|
+
removeFileIfExists(ACTIVE_INSTANCE_FILE);
|
|
451
|
+
}
|
|
452
|
+
export {
|
|
453
|
+
ACTIVE_INSTANCE_FILE,
|
|
454
|
+
OWNER_DIR,
|
|
455
|
+
RUNTIME_ROOT,
|
|
456
|
+
RUNTIME_STALE_TTL,
|
|
457
|
+
buildActiveInstanceState,
|
|
458
|
+
cleanupInstanceRuntimeFiles,
|
|
459
|
+
cleanupStaleRuntimeFiles,
|
|
460
|
+
clearActiveInstance,
|
|
461
|
+
clearServerPid,
|
|
462
|
+
ensureRuntimeDirs,
|
|
463
|
+
getActiveOwnerPid,
|
|
464
|
+
getChannelOwnerPath,
|
|
465
|
+
getControlPath,
|
|
466
|
+
getControlResponsePath,
|
|
467
|
+
getPermissionResultPath,
|
|
468
|
+
getTerminalLeadPid,
|
|
469
|
+
getStatusPath,
|
|
470
|
+
getStopFlagPath,
|
|
471
|
+
getTurnEndPath,
|
|
472
|
+
killAllPreviousServers,
|
|
473
|
+
makeInstanceId,
|
|
474
|
+
readActiveInstance,
|
|
475
|
+
refreshActiveInstance,
|
|
476
|
+
releaseOwnedChannelLocks,
|
|
477
|
+
writeActiveInstance,
|
|
478
|
+
writeServerPid
|
|
479
|
+
};
|