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,442 @@
|
|
|
1
|
+
// runtime-fetcher.mjs — P1 runtime fetcher for mixdog 0.4.0
|
|
2
|
+
// runtime-fetcher.mjs
|
|
3
|
+
// REQUIRES: `tar` (bsdtar-compatible) on PATH.
|
|
4
|
+
// On Windows, bsdtar ships with Windows 10 1803+ as %SystemRoot%\System32\tar.exe.
|
|
5
|
+
// If tar is missing, ensureRuntime() throws with an actionable error message.
|
|
6
|
+
//
|
|
7
|
+
// Downloads and verifies a prebuilt native PG runtime from the mixdog GitHub
|
|
8
|
+
// release manifest.
|
|
9
|
+
//
|
|
10
|
+
// Layout: <dataDir>/runtime/runtime-{ver}/ + <dataDir>/runtime/active-version
|
|
11
|
+
// Atomic swap: write active-version.tmp then rename → active-version.
|
|
12
|
+
// GC: removes stale runtime-* dirs and staging-* dirs on every ensureRuntime call.
|
|
13
|
+
//
|
|
14
|
+
// Public API: ensureRuntime(dataDir) → { runtimeDir, pgBinDir, libDir, sharePath, version }
|
|
15
|
+
|
|
16
|
+
import { createHash } from 'crypto'
|
|
17
|
+
import {
|
|
18
|
+
chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync,
|
|
19
|
+
readFileSync, readdirSync, rmSync, statSync, unlinkSync, writeFileSync,
|
|
20
|
+
} from 'fs'
|
|
21
|
+
import { readFile } from 'fs/promises'
|
|
22
|
+
import { join, resolve } from 'path'
|
|
23
|
+
import { fileURLToPath } from 'url'
|
|
24
|
+
import { pipeline } from 'stream/promises'
|
|
25
|
+
import { spawnSync } from 'child_process'
|
|
26
|
+
import { renameWithRetrySync, withFileLockSync, writeFileAtomicSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs'
|
|
27
|
+
|
|
28
|
+
// Bundled fallback manifest shipped alongside the plugin. fileURLToPath required
|
|
29
|
+
// for cross-platform path resolution (URL.pathname returns /C:/... on Windows).
|
|
30
|
+
const BUNDLED_MANIFEST_PATH = fileURLToPath(new URL('../data/runtime-manifest.json', import.meta.url))
|
|
31
|
+
|
|
32
|
+
// GitHub raw URL fallback — used only when no cached or bundled manifest exists.
|
|
33
|
+
const MANIFEST_URL = 'https://raw.githubusercontent.com/trib-plugin/mixdog/main/src/memory/data/runtime-manifest.json'
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Platform key
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
function platformKey() {
|
|
40
|
+
const os = process.platform === 'win32' ? 'win32' : process.platform
|
|
41
|
+
return `${os}-${process.arch}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fail-closed asset validation. A selected manifest asset is usable only if it
|
|
45
|
+
// is not explicitly marked unsupported AND carries a real downloadable payload:
|
|
46
|
+
// non-empty url, a well-formed 64-hex sha256, and a positive integer size.
|
|
47
|
+
// Placeholder / TBD entries fail every payload check and are rejected.
|
|
48
|
+
function isUsableAsset(asset) {
|
|
49
|
+
if (!asset || typeof asset !== 'object') return false
|
|
50
|
+
if (asset.unsupported === true) return false
|
|
51
|
+
if (typeof asset.url !== 'string' || asset.url.length === 0) return false
|
|
52
|
+
if (typeof asset.sha256 !== 'string' || !/^[0-9a-f]{64}$/.test(asset.sha256)) return false
|
|
53
|
+
if (!Number.isInteger(asset.size) || asset.size <= 0) return false
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Manifest resolution
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
async function loadManifest(dataDir) {
|
|
62
|
+
const runtimeManifestPath = join(dataDir, 'runtime', 'manifest.json')
|
|
63
|
+
if (existsSync(runtimeManifestPath)) {
|
|
64
|
+
try { return JSON.parse(readFileSync(runtimeManifestPath, 'utf8')) } catch {}
|
|
65
|
+
}
|
|
66
|
+
if (existsSync(BUNDLED_MANIFEST_PATH)) {
|
|
67
|
+
return JSON.parse(readFileSync(BUNDLED_MANIFEST_PATH, 'utf8'))
|
|
68
|
+
}
|
|
69
|
+
const res = await fetch(MANIFEST_URL, { signal: AbortSignal.timeout(30_000) })
|
|
70
|
+
if (!res.ok) throw new Error(`[runtime-fetcher] manifest fetch failed: ${res.status} ${res.statusText}`)
|
|
71
|
+
const manifest = await res.json()
|
|
72
|
+
mkdirSync(join(dataDir, 'runtime'), { recursive: true })
|
|
73
|
+
writeJsonAtomicSync(runtimeManifestPath, manifest, { lock: true, fsyncDir: true })
|
|
74
|
+
return manifest
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// SHA-256 verification
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
async function sha256File(filePath) {
|
|
82
|
+
const data = await readFile(filePath)
|
|
83
|
+
return createHash('sha256').update(data).digest('hex')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function verifySha256(filePath, expected) {
|
|
87
|
+
const actual = await sha256File(filePath)
|
|
88
|
+
if (actual !== expected) {
|
|
89
|
+
throw new Error(`[runtime-fetcher] sha256 mismatch for ${filePath}: expected ${expected}, got ${actual}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Active-runtime validation (pointer-file layout)
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
function activeVersionPath(runtimeDir) {
|
|
98
|
+
return join(runtimeDir, 'active-version')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function readActiveVersion(runtimeDir) {
|
|
102
|
+
try { return readFileSync(activeVersionPath(runtimeDir), 'utf8').trim() } catch { return null }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function runtimeVerDir(runtimeDir, ver) {
|
|
106
|
+
return join(runtimeDir, `runtime-${ver}`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function runtimePaths(verDir) {
|
|
110
|
+
return {
|
|
111
|
+
pgBinDir: join(verDir, 'bin'),
|
|
112
|
+
libDir: join(verDir, 'lib'),
|
|
113
|
+
sharePath: join(verDir, 'share'),
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Download with retry
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
async function downloadWithRetry(url, destPath) {
|
|
122
|
+
// 4 total attempts: 1 initial + 3 retries; waits between attempts: 1s, 3s, 9s.
|
|
123
|
+
const delays = [1000, 3000, 9000]
|
|
124
|
+
let lastErr
|
|
125
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
126
|
+
try {
|
|
127
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(180_000) })
|
|
128
|
+
if (res.status >= 400 && res.status < 500) {
|
|
129
|
+
// 4xx: terminal — do not retry.
|
|
130
|
+
throw new Error(`[runtime-fetcher] asset download HTTP ${res.status} (terminal) — ${url}`)
|
|
131
|
+
}
|
|
132
|
+
if (!res.ok) {
|
|
133
|
+
throw new Error(`[runtime-fetcher] asset download HTTP ${res.status} — ${url}`)
|
|
134
|
+
}
|
|
135
|
+
const out = createWriteStream(destPath)
|
|
136
|
+
await pipeline(res.body, out)
|
|
137
|
+
return // success
|
|
138
|
+
} catch (err) {
|
|
139
|
+
lastErr = err
|
|
140
|
+
// Terminal 4xx: do not retry.
|
|
141
|
+
if (err.message.includes('(terminal)')) throw err
|
|
142
|
+
if (attempt < 3) {
|
|
143
|
+
process.stderr.write(`[runtime-fetcher] download attempt ${attempt + 1} failed (${err.message}), retrying in ${delays[attempt]}ms…\n`)
|
|
144
|
+
await new Promise(r => setTimeout(r, delays[attempt]))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw lastErr
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Tar entry path validation + extraction
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
function extractTarGz(tarPath, destDir, stagingBase) {
|
|
156
|
+
mkdirSync(destDir, { recursive: true })
|
|
157
|
+
|
|
158
|
+
// List entries first and validate — reject any that escape staging.
|
|
159
|
+
const listResult = spawnSync('tar', ['-tzf', tarPath], { stdio: 'pipe', windowsHide: true })
|
|
160
|
+
if (listResult.status !== 0) {
|
|
161
|
+
throw new Error(`[runtime-fetcher] tar list failed: ${listResult.stderr?.toString() || 'unknown'}`)
|
|
162
|
+
}
|
|
163
|
+
const entries = (listResult.stdout?.toString() || '').split('\n').filter(Boolean)
|
|
164
|
+
const resolvedBase = resolve(stagingBase)
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
// Reject absolute paths and traversal sequences.
|
|
167
|
+
if (entry.startsWith('/') || entry.includes('..')) {
|
|
168
|
+
throw new Error(`[runtime-fetcher] tar entry path validation failed (unsafe entry): ${entry}`)
|
|
169
|
+
}
|
|
170
|
+
const resolved = resolve(join(stagingBase, entry))
|
|
171
|
+
if (!resolved.startsWith(resolvedBase)) {
|
|
172
|
+
throw new Error(`[runtime-fetcher] tar entry escapes staging dir: ${entry}`)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const r = spawnSync('tar', ['-xzf', tarPath, '-C', destDir], { stdio: 'pipe', windowsHide: true })
|
|
177
|
+
if (r.status !== 0) {
|
|
178
|
+
throw new Error(`[runtime-fetcher] tar extraction failed: ${r.stderr?.toString() || 'unknown error'}`)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Unix exec-bit normalization
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
function normalizeBinExecBit(verDir) {
|
|
187
|
+
if (process.platform === 'win32') return
|
|
188
|
+
const binDir = join(verDir, 'bin')
|
|
189
|
+
if (!existsSync(binDir)) return
|
|
190
|
+
try {
|
|
191
|
+
const entries = readdirSync(binDir)
|
|
192
|
+
for (const f of entries) {
|
|
193
|
+
try { chmodSync(join(binDir, f), 0o755) } catch {}
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Staging cross-process lock — protects an in-progress download/extract/swap
|
|
200
|
+
// from being GC'd by a concurrently-booting sibling process.
|
|
201
|
+
//
|
|
202
|
+
// Each staging-* dir is guarded by a sibling O_EXCL lockfile `<staging>.lock`
|
|
203
|
+
// stamped with `<pid> <epoch-ms>`. The lockfile is held (fd open) for the whole
|
|
204
|
+
// download→extract→swap lifetime. GC only removes a staging dir whose guarding
|
|
205
|
+
// lock is NOT live (file missing, owner pid dead, or lock older than
|
|
206
|
+
// STAGING_LOCK_STALE_MS), so a concurrent boot mid-extract is never wiped.
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
// Generous staleness budget: a cold download + extract on a slow link can take
|
|
210
|
+
// minutes. A shorter window would let a sibling reclaim a still-live staging.
|
|
211
|
+
const STAGING_LOCK_STALE_MS = 600_000
|
|
212
|
+
|
|
213
|
+
function stagingLockPath(stagingDir) {
|
|
214
|
+
return `${stagingDir}.lock`
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function _readStagingLockPid(lockPath) {
|
|
218
|
+
try {
|
|
219
|
+
const raw = readFileSync(lockPath, 'utf8')
|
|
220
|
+
const pid = Number.parseInt(String(raw).trim().split(/\s+/)[0], 10)
|
|
221
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null
|
|
222
|
+
} catch {
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function _stagingLockPidAlive(pid) {
|
|
228
|
+
if (pid === null) return false
|
|
229
|
+
if (pid === process.pid) return true
|
|
230
|
+
try {
|
|
231
|
+
process.kill(pid, 0)
|
|
232
|
+
return true // signal delivered → owner exists
|
|
233
|
+
} catch (err) {
|
|
234
|
+
// ESRCH = no such process → owner is gone. Any other error (e.g. EPERM:
|
|
235
|
+
// exists but unsignalable) → treat as alive so we never steal from a live
|
|
236
|
+
// holder.
|
|
237
|
+
return err?.code !== 'ESRCH'
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// A staging dir is protected iff its lockfile exists, names a live owner pid
|
|
242
|
+
// (or an unparseable-but-fresh stamp), and is not older than the stale budget.
|
|
243
|
+
function _stagingLockIsLive(lockPath) {
|
|
244
|
+
let st
|
|
245
|
+
try { st = statSync(lockPath) } catch { return false } // no lock → unprotected
|
|
246
|
+
const pid = _readStagingLockPid(lockPath)
|
|
247
|
+
if (pid !== null && !_stagingLockPidAlive(pid)) return false // owner dead
|
|
248
|
+
if (Date.now() - st.mtimeMs > STAGING_LOCK_STALE_MS) return false // abandoned
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function acquireStagingLock(stagingDir) {
|
|
253
|
+
const lockPath = stagingLockPath(stagingDir)
|
|
254
|
+
const fd = openSync(lockPath, 'wx') // O_EXCL: fails if a sibling already owns it
|
|
255
|
+
try { writeFileSync(fd, `${process.pid} ${Date.now()}\n`, 'utf8') } catch {}
|
|
256
|
+
return { fd, lockPath }
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function releaseStagingLock(lock) {
|
|
260
|
+
if (!lock) return
|
|
261
|
+
try { closeSync(lock.fd) } catch {}
|
|
262
|
+
// Only unlink if we still own the stamp; a stolen+replaced lock must not be
|
|
263
|
+
// destroyed out from under its new owner.
|
|
264
|
+
try {
|
|
265
|
+
if (_readStagingLockPid(lock.lockPath) === process.pid) unlinkSync(lock.lockPath)
|
|
266
|
+
} catch {}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// GC — remove stale runtime-* and staging-* dirs
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
function gcRuntimeDir(runtimeDir, keepVer) {
|
|
274
|
+
try {
|
|
275
|
+
const entries = readdirSync(runtimeDir)
|
|
276
|
+
const entrySet = new Set(entries)
|
|
277
|
+
for (const name of entries) {
|
|
278
|
+
if (name.startsWith('staging-')) {
|
|
279
|
+
if (name.endsWith('.lock')) {
|
|
280
|
+
// Orphan lockfile (crash after rename-away, or a failed unlink in
|
|
281
|
+
// releaseStagingLock) whose matching staging dir is gone. Lockfiles
|
|
282
|
+
// beside a live dir are handled by the dir branch below.
|
|
283
|
+
const dirName = name.slice(0, -'.lock'.length)
|
|
284
|
+
if (entrySet.has(dirName)) continue
|
|
285
|
+
const lockPath = join(runtimeDir, name)
|
|
286
|
+
// Reap only a provably-dead/stale orphan; never one whose owner pid is
|
|
287
|
+
// live AND fresh (_stagingLockIsLive covers both conditions).
|
|
288
|
+
if (_stagingLockIsLive(lockPath)) continue
|
|
289
|
+
try { unlinkSync(lockPath) } catch {}
|
|
290
|
+
continue
|
|
291
|
+
}
|
|
292
|
+
const dir = join(runtimeDir, name)
|
|
293
|
+
const lockPath = stagingLockPath(dir)
|
|
294
|
+
// Never wipe a staging dir guarded by a LIVE lock — that is a sibling
|
|
295
|
+
// process mid-extract/swap.
|
|
296
|
+
if (_stagingLockIsLive(lockPath)) continue
|
|
297
|
+
try { rmSync(dir, { recursive: true, force: true }) } catch {}
|
|
298
|
+
// Reap the now-orphaned (dead/stale) lockfile too.
|
|
299
|
+
try { if (existsSync(lockPath)) unlinkSync(lockPath) } catch {}
|
|
300
|
+
} else if (name.startsWith('runtime-') && name !== `runtime-${keepVer}`) {
|
|
301
|
+
try { rmSync(join(runtimeDir, name), { recursive: true, force: true }) } catch {}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch {}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// ensureRuntime — public API
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
const runtimeCache = new Map()
|
|
312
|
+
|
|
313
|
+
// One-shot tar availability probe; result cached after first call.
|
|
314
|
+
let _tarProbed = false
|
|
315
|
+
function probeTar() {
|
|
316
|
+
if (_tarProbed) return
|
|
317
|
+
const r = spawnSync('tar', ['--version'], { stdio: 'pipe', windowsHide: true })
|
|
318
|
+
if (r.status !== 0 || r.error) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
'[runtime-fetcher] `tar` not found or not executable. ' +
|
|
321
|
+
'On Windows, bsdtar (tar.exe) is required (available since Windows 10 1803). ' +
|
|
322
|
+
'Ensure tar.exe is on PATH (typically %SystemRoot%\\System32\\tar.exe).'
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
_tarProbed = true
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export async function ensureRuntime(dataDir) {
|
|
329
|
+
const key = resolve(dataDir)
|
|
330
|
+
if (runtimeCache.has(key)) return runtimeCache.get(key)
|
|
331
|
+
|
|
332
|
+
const runtimeBaseDir = join(key, 'runtime')
|
|
333
|
+
mkdirSync(runtimeBaseDir, { recursive: true })
|
|
334
|
+
|
|
335
|
+
// Entry GC: always clean staging-* (partial extracts from prior crashes), but
|
|
336
|
+
// preserve runtime-${currentVer} so a sibling child's just-completed swap is
|
|
337
|
+
// not wiped. multi-process race protection.
|
|
338
|
+
gcRuntimeDir(runtimeBaseDir, readActiveVersion(runtimeBaseDir))
|
|
339
|
+
|
|
340
|
+
const manifest = await loadManifest(key)
|
|
341
|
+
const pkey = platformKey()
|
|
342
|
+
const asset = manifest.assets?.[pkey]
|
|
343
|
+
if (!asset) {
|
|
344
|
+
throw new Error(
|
|
345
|
+
`[runtime-fetcher] no asset for platform ${pkey} in manifest. ` +
|
|
346
|
+
`Available: ${Object.keys(manifest.assets || {}).join(', ')}`
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
if (!isUsableAsset(asset)) {
|
|
350
|
+
throw new Error(`unsupported platform/arch: no validated runtime asset for ${pkey}`)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const { url, sha256, size } = asset
|
|
354
|
+
const version = `pg${manifest.pg?.major}.${manifest.pg?.minor}+pgvector-${manifest.pgvector?.version}`
|
|
355
|
+
|
|
356
|
+
// Fast path: active-version pointer exists and matches expected sha256.
|
|
357
|
+
const currentVer = readActiveVersion(runtimeBaseDir)
|
|
358
|
+
if (currentVer === version) {
|
|
359
|
+
const verDir = runtimeVerDir(runtimeBaseDir, version)
|
|
360
|
+
if (existsSync(join(verDir, '.version-sha256'))) {
|
|
361
|
+
const stored = readFileSync(join(verDir, '.version-sha256'), 'utf8').trim()
|
|
362
|
+
if (stored === sha256) {
|
|
363
|
+
const result = { runtimeDir: verDir, ...runtimePaths(verDir), version }
|
|
364
|
+
runtimeCache.set(key, result)
|
|
365
|
+
return result
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// tar is only required for the download/extract path. Probe here (not at
|
|
371
|
+
// function entry) so a machine without tar can still reuse an
|
|
372
|
+
// already-extracted, sha-matching cached runtime via the fast path above.
|
|
373
|
+
probeTar()
|
|
374
|
+
|
|
375
|
+
process.stderr.write(`[runtime-fetcher] downloading runtime ${version} for ${pkey} (~${size} bytes) …\n`)
|
|
376
|
+
|
|
377
|
+
// Unique staging suffix prevents two siblings from colliding on one dir and
|
|
378
|
+
// on its guarding lockfile (the O_EXCL acquire below would otherwise have one
|
|
379
|
+
// process fail to obtain the lock for its own staging dir).
|
|
380
|
+
const stagingTag = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`
|
|
381
|
+
const stagingDir = join(runtimeBaseDir, `staging-${stagingTag}`)
|
|
382
|
+
const tarPath = join(runtimeBaseDir, `runtime-${pkey}-${stagingTag}.tar.gz`)
|
|
383
|
+
|
|
384
|
+
const verDir = runtimeVerDir(runtimeBaseDir, version)
|
|
385
|
+
const avPath = activeVersionPath(runtimeBaseDir)
|
|
386
|
+
|
|
387
|
+
// Cross-process lock: hold the staging lockfile (O_EXCL) for the WHOLE
|
|
388
|
+
// download → extract → swap lifetime. GC in any concurrently-booting sibling
|
|
389
|
+
// treats a live lock as "in progress" and will not wipe this staging dir.
|
|
390
|
+
const stagingLock = acquireStagingLock(stagingDir)
|
|
391
|
+
try {
|
|
392
|
+
let downloadOk = false
|
|
393
|
+
try {
|
|
394
|
+
await downloadWithRetry(url, tarPath)
|
|
395
|
+
await verifySha256(tarPath, sha256)
|
|
396
|
+
downloadOk = true
|
|
397
|
+
extractTarGz(tarPath, stagingDir, stagingDir)
|
|
398
|
+
} finally {
|
|
399
|
+
try { rmSync(tarPath, { force: true }) } catch {}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!downloadOk) {
|
|
403
|
+
try { rmSync(stagingDir, { recursive: true, force: true }) } catch {}
|
|
404
|
+
throw new Error(`[runtime-fetcher] download or verify failed for ${version}`)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Stamp sha256 inside staging dir.
|
|
408
|
+
writeFileSync(join(stagingDir, '.version-sha256'), sha256)
|
|
409
|
+
normalizeBinExecBit(stagingDir)
|
|
410
|
+
|
|
411
|
+
// Atomic swap:
|
|
412
|
+
// 1. Rename staging → runtime-{ver}
|
|
413
|
+
// 2. Write active-version.tmp → rename to active-version
|
|
414
|
+
// Stale dirs cleaned up by GC after.
|
|
415
|
+
try {
|
|
416
|
+
// If a prior runtime-{ver} dir exists (interrupted earlier run), remove it.
|
|
417
|
+
if (existsSync(verDir)) {
|
|
418
|
+
rmSync(verDir, { recursive: true, force: true })
|
|
419
|
+
}
|
|
420
|
+
renameWithRetrySync(stagingDir, verDir)
|
|
421
|
+
writeFileAtomicSync(avPath, version, { fsyncDir: true })
|
|
422
|
+
} catch (swapErr) {
|
|
423
|
+
process.stderr.write(`[runtime-fetcher] atomic swap failed: ${swapErr.message}\n`)
|
|
424
|
+
// Attempt to leave things in a recoverable state: if verDir landed but
|
|
425
|
+
// active-version didn't update, next call will re-download.
|
|
426
|
+
throw swapErr
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// GC: remove stale runtime-* dirs (anything that isn't runtime-{version}).
|
|
430
|
+
// Still under the lock so the just-swapped staging lockfile reap below sees
|
|
431
|
+
// a consistent view.
|
|
432
|
+
gcRuntimeDir(runtimeBaseDir, version)
|
|
433
|
+
} finally {
|
|
434
|
+
releaseStagingLock(stagingLock)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
process.stderr.write(`[runtime-fetcher] runtime ready at ${verDir}\n`)
|
|
438
|
+
|
|
439
|
+
const result = { runtimeDir: verDir, ...runtimePaths(verDir), version }
|
|
440
|
+
runtimeCache.set(key, result)
|
|
441
|
+
return result
|
|
442
|
+
}
|