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,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* supervisor-pg — PG child process lifecycle wired into the mixdog supervisor.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* ensurePgInstance(dataDir) → Promise<{ host, port, runtimeDir, pgdataDir }>
|
|
6
|
+
* stopPgForShutdown() → Promise<void> — call from server-main.mjs shutdown()
|
|
7
|
+
*
|
|
8
|
+
* Depends on Track A's pg-process.mjs for startPg / stopPg / healthcheckPg.
|
|
9
|
+
* Lazy-imported so this module loads cleanly before Track A lands.
|
|
10
|
+
*
|
|
11
|
+
* active-instance.json additions:
|
|
12
|
+
* pg_port? number — TCP port PG is listening on
|
|
13
|
+
* pg_started_at? number — epoch ms when PG was last started
|
|
14
|
+
* pg_pgdata? string — absolute path to the pgdata directory
|
|
15
|
+
* pg_runtime_dir? string — absolute path to the pg runtime binaries dir
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createServer } from 'node:net';
|
|
19
|
+
import {
|
|
20
|
+
unlinkSync, readFileSync, writeFileSync,
|
|
21
|
+
renameSync, statSync, mkdirSync,
|
|
22
|
+
} from 'node:fs';
|
|
23
|
+
import { join, resolve } from 'node:path';
|
|
24
|
+
import { tmpdir } from 'node:os';
|
|
25
|
+
import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
26
|
+
|
|
27
|
+
// ── pg-process interface (Track A) ───────────────────────────────────────────
|
|
28
|
+
// Dynamic import so this module loads even before Track A's file exists.
|
|
29
|
+
let _pgProc = null;
|
|
30
|
+
async function _getPgProc() {
|
|
31
|
+
if (_pgProc) return _pgProc;
|
|
32
|
+
// import.meta.url is in src/memory/lib/pg/ — process.mjs lives alongside.
|
|
33
|
+
const mod = await import('./process.mjs');
|
|
34
|
+
_pgProc = {
|
|
35
|
+
startPg: mod.startPg,
|
|
36
|
+
stopPg: mod.stopPg,
|
|
37
|
+
healthcheckPg: mod.healthcheckPg,
|
|
38
|
+
reconcileConfV2: mod.reconcileConfV2,
|
|
39
|
+
};
|
|
40
|
+
return _pgProc;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── In-process state ─────────────────────────────────────────────────────────
|
|
44
|
+
/** @type {{ port: number, pgdata: string, runtimeDir: string, proc: unknown } | null} */
|
|
45
|
+
let _live = null;
|
|
46
|
+
/** Dedup: one ensure coroutine at a time. */
|
|
47
|
+
let _ensureInFlight = null;
|
|
48
|
+
/** Per-process flag — postgresql.conf reconcile attempted once per supervisor. */
|
|
49
|
+
let _v2ReconcileTried = false;
|
|
50
|
+
|
|
51
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
52
|
+
const PG_PORT_MIN = 55432;
|
|
53
|
+
const PG_PORT_MAX = 55632;
|
|
54
|
+
const PG_LOG_MAX_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
55
|
+
const SPAWN_LOCK_NAME = 'pg-spawn.lock';
|
|
56
|
+
const LOCK_WAIT_MS = 30_000;
|
|
57
|
+
const LOCK_POLL_MS = 100;
|
|
58
|
+
const LOCK_WAIT_CODES = new Set(['EEXIST', 'EPERM', 'EACCES', 'EBUSY']);
|
|
59
|
+
|
|
60
|
+
// ── File lock (O_EXCL pattern from dispatch-persist.mjs / run-mcp.mjs) ───────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Acquire `${dataDir}/pg-spawn.lock`.
|
|
64
|
+
* Returns the lock-file path on success, or null on timeout (caller proceeds
|
|
65
|
+
* best-effort — worst case: two supervisors race initdb on the same pgdata,
|
|
66
|
+
* but the second will fail pg_ctl start gracefully and healthcheck will
|
|
67
|
+
* converge on the first's port).
|
|
68
|
+
*/
|
|
69
|
+
async function acquireSpawnLock(dataDir) {
|
|
70
|
+
const lp = join(dataDir, SPAWN_LOCK_NAME);
|
|
71
|
+
const deadline = Date.now() + LOCK_WAIT_MS;
|
|
72
|
+
const body = JSON.stringify({ pid: process.pid, startedAt: Date.now() });
|
|
73
|
+
for (;;) {
|
|
74
|
+
try {
|
|
75
|
+
// 'wx' = O_CREAT | O_EXCL — atomic, fails with EEXIST if held.
|
|
76
|
+
writeFileSync(lp, body, { flag: 'wx' });
|
|
77
|
+
return lp;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
if (!LOCK_WAIT_CODES.has(err?.code)) {
|
|
80
|
+
process.stderr.write(`[supervisor-pg] spawn lock error: ${err?.code || err?.message}\n`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
// Dead-holder detection: same as run-mcp.mjs acquireLock().
|
|
84
|
+
try {
|
|
85
|
+
const holder = JSON.parse(readFileSync(lp, 'utf8'));
|
|
86
|
+
if (holder?.pid) {
|
|
87
|
+
try { process.kill(holder.pid, 0); }
|
|
88
|
+
catch (ke) {
|
|
89
|
+
if (ke.code === 'ESRCH') {
|
|
90
|
+
try { unlinkSync(lp); } catch {}
|
|
91
|
+
continue; // retry immediately after removing stale lock
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch { /* unreadable — fall through and wait */ }
|
|
96
|
+
if (Date.now() >= deadline) {
|
|
97
|
+
throw new Error(`[supervisor-pg] spawn-lock acquire timeout (concurrent supervisor risk)`);
|
|
98
|
+
}
|
|
99
|
+
await new Promise(r => setTimeout(r, LOCK_POLL_MS));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function releaseSpawnLock(lp) {
|
|
105
|
+
if (!lp) return;
|
|
106
|
+
try { unlinkSync(lp); } catch {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── Port allocation ──────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
function _probePort(port) {
|
|
112
|
+
return new Promise(resolve => {
|
|
113
|
+
const srv = createServer();
|
|
114
|
+
srv.once('error', () => resolve(false));
|
|
115
|
+
srv.once('listening', () => srv.close(() => resolve(true)));
|
|
116
|
+
srv.listen(port, '127.0.0.1');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function allocatePort() {
|
|
121
|
+
for (let p = PG_PORT_MIN; p <= PG_PORT_MAX; p++) {
|
|
122
|
+
if (await _probePort(p)) return p;
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`[supervisor-pg] no free TCP port in range ${PG_PORT_MIN}–${PG_PORT_MAX}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Log rotation ─────────────────────────────────────────────────────────────
|
|
128
|
+
// Keep at most 1 archive (pg.log.1). Rotate at PG_LOG_MAX_BYTES.
|
|
129
|
+
|
|
130
|
+
function rotateLogIfNeeded(logPath) {
|
|
131
|
+
try {
|
|
132
|
+
const st = statSync(logPath);
|
|
133
|
+
if (st.size > PG_LOG_MAX_BYTES) {
|
|
134
|
+
const archive = logPath + '.1';
|
|
135
|
+
try { unlinkSync(archive); } catch {}
|
|
136
|
+
renameSync(logPath, archive);
|
|
137
|
+
}
|
|
138
|
+
} catch { /* log does not exist yet — nothing to rotate */ }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── active-instance.json patch ───────────────────────────────────────────────
|
|
142
|
+
// Atomic read-modify-write using the same tmp+rename pattern as server.mjs.
|
|
143
|
+
// Lives in MIXDOG_RUNTIME_ROOT or os.tmpdir()/mixdog/
|
|
144
|
+
// (see src/channels/lib/runtime-paths.mjs).
|
|
145
|
+
|
|
146
|
+
const _RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT
|
|
147
|
+
? resolve(process.env.MIXDOG_RUNTIME_ROOT)
|
|
148
|
+
: join(tmpdir(), 'mixdog');
|
|
149
|
+
const _ACTIVE_FILE = join(_RUNTIME_ROOT, 'active-instance.json');
|
|
150
|
+
|
|
151
|
+
function patchActiveInstance(fields) {
|
|
152
|
+
try {
|
|
153
|
+
let curRaw = {};
|
|
154
|
+
try { curRaw = JSON.parse(readFileSync(_ACTIVE_FILE, 'utf8')); } catch {}
|
|
155
|
+
// Drop stale fields (pid/startedAt) written by older server versions.
|
|
156
|
+
const { pid: _legacyPid, startedAt: _legacyStartedAt, ...cur } = curRaw ?? {};
|
|
157
|
+
// Omit null-valued fields (clean removal when pg is stopped).
|
|
158
|
+
const merged = { ...cur, updatedAt: Date.now() };
|
|
159
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
160
|
+
if (v == null) delete merged[k];
|
|
161
|
+
else merged[k] = v;
|
|
162
|
+
}
|
|
163
|
+
writeJsonAtomicSync(_ACTIVE_FILE, merged, { compact: true, lock: true, fsyncDir: true });
|
|
164
|
+
} catch (e) {
|
|
165
|
+
process.stderr.write(`[supervisor-pg] patchActiveInstance failed: ${e?.message}\n`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── postmaster.pid helpers ───────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
function readPostmasterPid(pgdata) {
|
|
172
|
+
try {
|
|
173
|
+
const raw = readFileSync(join(pgdata, 'postmaster.pid'), 'utf8');
|
|
174
|
+
const pid = parseInt(raw.split('\n')[0], 10);
|
|
175
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
176
|
+
} catch { return null; }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isPidAlive(pid) {
|
|
180
|
+
try { process.kill(pid, 0); return true; }
|
|
181
|
+
catch (e) {
|
|
182
|
+
// EPERM: process exists but we lack permission → alive.
|
|
183
|
+
// ESRCH: no such process → dead.
|
|
184
|
+
return e.code === 'EPERM';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Best-effort process-name check: confirms the pid's comm name contains 'postgres'.
|
|
190
|
+
* Falls back to true (alive) when the name cannot be determined.
|
|
191
|
+
*/
|
|
192
|
+
async function isPostgresPid(pid) {
|
|
193
|
+
try {
|
|
194
|
+
if (process.platform === 'linux') {
|
|
195
|
+
const { readFileSync: rfs } = await import('node:fs');
|
|
196
|
+
const comm = rfs(`/proc/${pid}/comm`, 'utf8').trim();
|
|
197
|
+
return comm.includes('postgres');
|
|
198
|
+
}
|
|
199
|
+
if (process.platform === 'darwin') {
|
|
200
|
+
const { spawnSync } = await import('node:child_process');
|
|
201
|
+
const r = spawnSync('ps', ['-o', 'comm=', '-p', String(pid)], { encoding: 'utf8', windowsHide: true });
|
|
202
|
+
if (r.status === 0) return (r.stdout || '').trim().includes('postgres');
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
if (process.platform === 'win32') {
|
|
206
|
+
const { spawnSync } = await import('node:child_process');
|
|
207
|
+
const r = spawnSync('tasklist', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], { encoding: 'utf8', windowsHide: true });
|
|
208
|
+
if (r.status === 0) return (r.stdout || '').toLowerCase().includes('postgres');
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
} catch { /* cannot read — fall back to alive */ }
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function isPostmasterAlive(pid) {
|
|
216
|
+
if (!isPidAlive(pid)) return false;
|
|
217
|
+
return isPostgresPid(pid);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Internal: spawn a fresh PG instance ─────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
async function _startFresh(dataDir, pgdata, port, runtimeDir) {
|
|
223
|
+
const { startPg } = await _getPgProc();
|
|
224
|
+
const logPath = join(dataDir, 'pg.log');
|
|
225
|
+
rotateLogIfNeeded(logPath);
|
|
226
|
+
mkdirSync(pgdata, { recursive: true });
|
|
227
|
+
// Track A's startPg handles initdb (if needed) then pg_ctl start.
|
|
228
|
+
// stdout/stderr are directed to logPath by Track A's implementation.
|
|
229
|
+
const pgdataDir = pgdata;
|
|
230
|
+
const proc = await startPg({ runtimeDir, pgdataDir, port, logPath });
|
|
231
|
+
const actualPort = proc?.port ?? port;
|
|
232
|
+
_live = { port: actualPort, pgdata, runtimeDir, proc };
|
|
233
|
+
patchActiveInstance({
|
|
234
|
+
pg_port: actualPort, pg_started_at: Date.now(),
|
|
235
|
+
pg_pgdata: pgdata, pg_runtime_dir: runtimeDir,
|
|
236
|
+
});
|
|
237
|
+
process.stderr.write(`[supervisor-pg] ${proc?.attached ? 'attached to' : 'started'} PG port=${actualPort} pgdata=${pgdata}\n`);
|
|
238
|
+
return { host: '127.0.0.1', port: actualPort, runtimeDir, pgdataDir };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Internal: full ensure logic (runs exclusively via _ensureInFlight) ────────
|
|
242
|
+
|
|
243
|
+
async function _doEnsure(dataDir) {
|
|
244
|
+
const { healthcheckPg, stopPg } = await _getPgProc();
|
|
245
|
+
const pgdata = join(dataDir, 'pgdata');
|
|
246
|
+
|
|
247
|
+
// Resolve runtimeDir via runtime-fetcher (cache-hits immediately when already downloaded).
|
|
248
|
+
const { ensureRuntime } = await import('../runtime-fetcher.mjs');
|
|
249
|
+
const { runtimeDir } = await ensureRuntime(dataDir);
|
|
250
|
+
|
|
251
|
+
// One-shot v2 conf reconcile — idempotent. Covers attach paths (in-process
|
|
252
|
+
// fast path / cross-process active-instance reuse) where startPg is never
|
|
253
|
+
// re-invoked on already-running PG instances.
|
|
254
|
+
if (!_v2ReconcileTried) {
|
|
255
|
+
_v2ReconcileTried = true;
|
|
256
|
+
try {
|
|
257
|
+
const { reconcileConfV2 } = await _getPgProc();
|
|
258
|
+
reconcileConfV2(runtimeDir, pgdata);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
process.stderr.write(`[supervisor-pg] reconcileConfV2 error (non-fatal): ${e?.message}\n`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ── Fast path: already live in this process ──────────────────────────────
|
|
265
|
+
if (_live) {
|
|
266
|
+
try {
|
|
267
|
+
if (await healthcheckPg({ port: _live.port })) {
|
|
268
|
+
return { host: '127.0.0.1', port: _live.port, runtimeDir: _live.runtimeDir, pgdataDir: _live.pgdata };
|
|
269
|
+
}
|
|
270
|
+
} catch {}
|
|
271
|
+
// healthcheck failed — fall through to recovery under lock
|
|
272
|
+
process.stderr.write(`[supervisor-pg] in-process PG failed healthcheck — recovering\n`);
|
|
273
|
+
_live = null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ── Acquire spawn lock to serialize initdb races ─────────────────────────
|
|
277
|
+
const lp = await acquireSpawnLock(dataDir);
|
|
278
|
+
try {
|
|
279
|
+
// ── Reuse path: another supervisor already started PG ─────────────────
|
|
280
|
+
let existingPort = null;
|
|
281
|
+
let ai = null;
|
|
282
|
+
try {
|
|
283
|
+
ai = JSON.parse(readFileSync(_ACTIVE_FILE, 'utf8'));
|
|
284
|
+
// Only reuse a recorded instance when it was started for THIS pgdata.
|
|
285
|
+
// active-instance.json is process-global; without matching pg_pgdata a
|
|
286
|
+
// healthy PG serving a different data directory would be reused, binding
|
|
287
|
+
// this dataDir's memory to the wrong cluster. A missing/mismatched
|
|
288
|
+
// pg_pgdata falls through to a fresh start for the requested pgdata.
|
|
289
|
+
if (ai?.pg_port && ai?.pg_pgdata && resolve(ai.pg_pgdata) === resolve(pgdata)) {
|
|
290
|
+
existingPort = ai.pg_port;
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
|
|
294
|
+
if (existingPort) {
|
|
295
|
+
try {
|
|
296
|
+
if (await healthcheckPg({ port: existingPort })) {
|
|
297
|
+
process.stderr.write(`[supervisor-pg] reusing PG on port ${existingPort}\n`);
|
|
298
|
+
const existingRtDir = ai?.pg_runtime_dir ?? runtimeDir;
|
|
299
|
+
_live = { port: existingPort, pgdata, runtimeDir: existingRtDir, proc: null };
|
|
300
|
+
return { host: '127.0.0.1', port: existingPort, runtimeDir: existingRtDir, pgdataDir: pgdata };
|
|
301
|
+
}
|
|
302
|
+
} catch {}
|
|
303
|
+
|
|
304
|
+
// ── Stale detection: pg_port recorded but healthcheck failing ─────────
|
|
305
|
+
process.stderr.write(
|
|
306
|
+
`[supervisor-pg] pg_port=${existingPort} recorded but healthcheck failed — recovering\n`,
|
|
307
|
+
);
|
|
308
|
+
const pmPid = readPostmasterPid(pgdata);
|
|
309
|
+
if (pmPid && await isPostmasterAlive(pmPid)) {
|
|
310
|
+
// postmaster alive but unhealthy: attempt graceful stop first
|
|
311
|
+
process.stderr.write(`[supervisor-pg] postmaster PID ${pmPid} alive — attempting graceful stopPg\n`);
|
|
312
|
+
try { await stopPg({ runtimeDir, pgdataDir: pgdata }); } catch (e) {
|
|
313
|
+
process.stderr.write(`[supervisor-pg] graceful stopPg failed: ${e?.message} — continuing to fresh start\n`);
|
|
314
|
+
}
|
|
315
|
+
} else if (pmPid) {
|
|
316
|
+
// postmaster dead: remove stale postmaster.pid so initdb/start is not blocked
|
|
317
|
+
process.stderr.write(`[supervisor-pg] postmaster PID ${pmPid} dead — removing stale postmaster.pid\n`);
|
|
318
|
+
try { unlinkSync(join(pgdata, 'postmaster.pid')); } catch {}
|
|
319
|
+
}
|
|
320
|
+
// Clear stale pg fields before restart
|
|
321
|
+
patchActiveInstance({ pg_port: null, pg_started_at: null, pg_pgdata: null });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Allocate a fresh port and spawn ───────────────────────────────────
|
|
325
|
+
const port = await allocatePort();
|
|
326
|
+
return await _startFresh(dataDir, pgdata, port, runtimeDir);
|
|
327
|
+
} finally {
|
|
328
|
+
releaseSpawnLock(lp);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Ensure a live PG instance exists for `dataDir`.
|
|
336
|
+
* Lazy: does nothing until first call — sessions that never touch memory pay
|
|
337
|
+
* zero cost.
|
|
338
|
+
*
|
|
339
|
+
* Concurrent calls are deduplicated — exactly one spawn/recover sequence runs
|
|
340
|
+
* at a time; additional callers await the same promise.
|
|
341
|
+
*
|
|
342
|
+
* @param {string} dataDir Plugin data directory (CLAUDE_PLUGIN_DATA).
|
|
343
|
+
* @returns {Promise<{ host: string, port: number, runtimeDir: string, pgdataDir: string }>}
|
|
344
|
+
*/
|
|
345
|
+
export function ensurePgInstance(dataDir) {
|
|
346
|
+
if (!_ensureInFlight) {
|
|
347
|
+
_ensureInFlight = _doEnsure(dataDir).finally(() => { _ensureInFlight = null; });
|
|
348
|
+
}
|
|
349
|
+
return _ensureInFlight;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Graceful PG shutdown — call from server-main.mjs shutdown() after workers
|
|
354
|
+
* have stopped. Sends pg_ctl stop to the pgdata directory; clears pg_port
|
|
355
|
+
* from active-instance.json.
|
|
356
|
+
*
|
|
357
|
+
* On the supervisor detached-killer path (run-mcp.mjs killChild → SIGTERM
|
|
358
|
+
* with no time for graceful stop) this function is never called — that is
|
|
359
|
+
* intentional. The next ensurePgInstance call will detect the stale
|
|
360
|
+
* postmaster.pid via isPidAlive() and recover automatically.
|
|
361
|
+
*/
|
|
362
|
+
export async function stopPgForShutdown() {
|
|
363
|
+
if (!_live) {
|
|
364
|
+
// _live may be null if PG was started by another process or adapter call.
|
|
365
|
+
// Attempt graceful stop via active-instance.json.
|
|
366
|
+
let ai = null;
|
|
367
|
+
try { ai = JSON.parse(readFileSync(_ACTIVE_FILE, 'utf8')); } catch {}
|
|
368
|
+
if (!ai?.pg_port || !ai?.pg_pgdata) return;
|
|
369
|
+
const pgdataDir2 = ai.pg_pgdata;
|
|
370
|
+
const runtimeDir2 = ai.pg_runtime_dir;
|
|
371
|
+
if (!runtimeDir2) {
|
|
372
|
+
process.stderr.write(`[supervisor-pg] stopPgForShutdown: pg_runtime_dir missing from active-instance.json — skipping\n`);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const { stopPg } = await _getPgProc();
|
|
377
|
+
await stopPg({ runtimeDir: runtimeDir2, pgdataDir: pgdataDir2 });
|
|
378
|
+
process.stderr.write(`[supervisor-pg] PG stopped (via active-instance.json) on shutdown\n`);
|
|
379
|
+
} catch (e) {
|
|
380
|
+
process.stderr.write(`[supervisor-pg] stopPg error on shutdown (no _live): ${e?.message}\n`);
|
|
381
|
+
}
|
|
382
|
+
patchActiveInstance({ pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const snap = _live;
|
|
386
|
+
_live = null;
|
|
387
|
+
_ensureInFlight = null;
|
|
388
|
+
try {
|
|
389
|
+
const { stopPg } = await _getPgProc();
|
|
390
|
+
await stopPg({ runtimeDir: snap.runtimeDir, pgdataDir: snap.pgdata });
|
|
391
|
+
process.stderr.write(`[supervisor-pg] PG stopped gracefully on shutdown\n`);
|
|
392
|
+
} catch (e) {
|
|
393
|
+
process.stderr.write(`[supervisor-pg] stopPg error on shutdown: ${e?.message}\n`);
|
|
394
|
+
}
|
|
395
|
+
patchActiveInstance({ pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null });
|
|
396
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, resolve } from 'node:path';
|
|
3
|
+
import { explicitSessionCwd } from '../../shared/user-cwd.mjs';
|
|
4
|
+
|
|
5
|
+
/** @type {Map<string, string|null>} */
|
|
6
|
+
const cache = new Map();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Walk up from `start`, returning the first directory whose
|
|
10
|
+
* `.mixdog/project.id` file exists. Returns null if no ancestor has one.
|
|
11
|
+
* Skips intermediate `.mixdog` directories that lack `project.id` so an
|
|
12
|
+
* inner empty marker does not mask an outer valid one.
|
|
13
|
+
* @param {string} start - absolute directory path
|
|
14
|
+
* @returns {string|null} absolute path of the containing directory
|
|
15
|
+
*/
|
|
16
|
+
function findProjectIdRoot(start) {
|
|
17
|
+
let dir = resolve(start);
|
|
18
|
+
while (true) {
|
|
19
|
+
if (existsSync(join(dir, '.mixdog', 'project.id'))) return dir;
|
|
20
|
+
const parent = dirname(dir);
|
|
21
|
+
if (parent === dir) return null; // filesystem root
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a project_id for the given working directory.
|
|
28
|
+
*
|
|
29
|
+
* Single source: .mixdog/project.id file in cwd or any ancestor directory.
|
|
30
|
+
* Returns the file content (trimmed), or null if no file is found or the
|
|
31
|
+
* content is "common" (case-insensitive).
|
|
32
|
+
*
|
|
33
|
+
* Removed: git origin parsing, gh CLI permission check, owner whitelist
|
|
34
|
+
* branch, lazy .mixdog/project.id write. Those were multi-step heuristics
|
|
35
|
+
* with no objective signal — project membership must be declared explicitly
|
|
36
|
+
* via a .mixdog/project.id file.
|
|
37
|
+
*
|
|
38
|
+
* Result is memoized by the .mixdog root directory path.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} cwd - absolute or relative working directory
|
|
41
|
+
* @returns {string|null}
|
|
42
|
+
*/
|
|
43
|
+
export function resolveProjectId(cwd) {
|
|
44
|
+
const absCwd = resolve(cwd);
|
|
45
|
+
|
|
46
|
+
const mixdogRoot = findProjectIdRoot(absCwd);
|
|
47
|
+
if (!mixdogRoot) return null;
|
|
48
|
+
|
|
49
|
+
if (cache.has(mixdogRoot)) return cache.get(mixdogRoot);
|
|
50
|
+
|
|
51
|
+
const idFile = join(mixdogRoot, '.mixdog', 'project.id');
|
|
52
|
+
let content;
|
|
53
|
+
try {
|
|
54
|
+
content = readFileSync(idFile, 'utf8').trim();
|
|
55
|
+
} catch {
|
|
56
|
+
// TOCTOU: the file vanished between findProjectIdRoot's existsSync and
|
|
57
|
+
// this read. Treat as no marker; do not cache so a transient miss can
|
|
58
|
+
// recover on a later call.
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
// "common" (case-insensitive) → forced COMMON
|
|
62
|
+
if (content.toLowerCase() === 'common' || !content) {
|
|
63
|
+
cache.set(mixdogRoot, null);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
cache.set(mixdogRoot, content);
|
|
68
|
+
return content;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a project_id for PROJECT CLASSIFICATION.
|
|
73
|
+
*
|
|
74
|
+
* Uses the explicitly-supplied cwd when provided; otherwise the explicit
|
|
75
|
+
* session cwd (MIXDOG_SESSION_CWD / user-cwd.txt) via explicitSessionCwd().
|
|
76
|
+
* Returns null (-> COMMON scope) when no explicit cwd exists. Never consults
|
|
77
|
+
* process.cwd(): the server's launch dir is not a project signal and would
|
|
78
|
+
* misclassify rows stored under the service/plugin cwd.
|
|
79
|
+
*
|
|
80
|
+
* @param {string|null|undefined} explicitCwd - caller-supplied cwd, if any
|
|
81
|
+
* @returns {string|null}
|
|
82
|
+
*/
|
|
83
|
+
export function resolveProjectScope(explicitCwd) {
|
|
84
|
+
const cwd = (typeof explicitCwd === 'string' && explicitCwd) ? explicitCwd : explicitSessionCwd();
|
|
85
|
+
return cwd ? resolveProjectId(cwd) : null;
|
|
86
|
+
}
|