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,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IMPORTANT — cwd model role:
|
|
3
|
+
* pwd() resolves the user's working directory for RELATIVE PATH RESOLUTION only.
|
|
4
|
+
* It is NOT a sandbox boundary. Sandbox decisions are governed by Claude Code's
|
|
5
|
+
* settings.json permissions govern sandbox decisions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* user-cwd.mjs — shared helper to resolve the user's working directory
|
|
10
|
+
* from the persisted user-cwd.txt sentinel file, with an optional
|
|
11
|
+
* session-cwd override (process.env.MIXDOG_SESSION_CWD) that takes
|
|
12
|
+
* precedence when set.
|
|
13
|
+
*
|
|
14
|
+
* Single-source-of-truth model:
|
|
15
|
+
* - captureOriginalUserCwd() reads MIXDOG_SESSION_CWD first (if set
|
|
16
|
+
* to a valid directory), otherwise user-cwd.txt fresh on every
|
|
17
|
+
* call. No in-memory freeze.
|
|
18
|
+
* - rawUserCwd() reads ONLY user-cwd.txt (no env consult) — exposed
|
|
19
|
+
* for the cwd-tool auto-init path so the env-var fallback cannot
|
|
20
|
+
* become self-referential.
|
|
21
|
+
* - AsyncLocalStorage override (runWithCwdOverride) isolates concurrent worker cwds.
|
|
22
|
+
* - pwd() = override ?? originalCwd. Hot lookups short-circuit on the
|
|
23
|
+
* override, so the no-override fallback path is cold and per-call
|
|
24
|
+
* disk reads of user-cwd.txt are negligible.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { AsyncLocalStorage } from 'async_hooks'
|
|
28
|
+
import { readFileSync, statSync, writeFileSync } from 'fs'
|
|
29
|
+
import { join, resolve } from 'path'
|
|
30
|
+
import { homedir } from 'os'
|
|
31
|
+
|
|
32
|
+
const _cwdOverride = new AsyncLocalStorage()
|
|
33
|
+
|
|
34
|
+
// process.cwd() is the server's LAUNCH directory. In daemon mode that IS
|
|
35
|
+
// CLAUDE_PLUGIN_ROOT (the plugin install/cache root), so using it as a
|
|
36
|
+
// relative-path base silently resolves into the DEPLOYED plugin copy instead
|
|
37
|
+
// of the user's working tree — the exact cause of stale reads in a worker that
|
|
38
|
+
// inherited no explicit cwd. Treat that one case as "no usable cwd" and fall
|
|
39
|
+
// back to the home dir: a missing session signal then surfaces as ENOENT
|
|
40
|
+
// (absolute paths required) rather than a silent stale read. Invariant check
|
|
41
|
+
// (exact path equality with the known root), not a path-substring heuristic.
|
|
42
|
+
export function _safeProcessCwd() {
|
|
43
|
+
const cwd = process.cwd()
|
|
44
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT
|
|
45
|
+
if (pluginRoot) {
|
|
46
|
+
try { if (resolve(cwd) === resolve(pluginRoot)) return homedir() } catch { /* fall through to cwd */ }
|
|
47
|
+
}
|
|
48
|
+
return cwd
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Hook payloads can deliver POSIX paths on Windows (e.g. `/c/Project`); Node's
|
|
52
|
+
// path.resolve does not map MSYS-style drive prefixes, so the value must be
|
|
53
|
+
// rewritten to the platform-native shape before path resolution.
|
|
54
|
+
function _normalizePlatformCwd(p) {
|
|
55
|
+
if (!p || typeof p !== 'string') return p
|
|
56
|
+
if (process.platform !== 'win32') return resolve(p)
|
|
57
|
+
const m = p.match(/^[\/\\]([a-zA-Z])[\/\\](.*)$/)
|
|
58
|
+
const native = m ? `${m[1].toUpperCase()}:\\${m[2].replace(/\//g, '\\')}` : p
|
|
59
|
+
return resolve(native)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the session cwd from EXPLICIT signals only:
|
|
64
|
+
* 1. process.env.MIXDOG_SESSION_CWD — session-level override set via
|
|
65
|
+
* the `cwd` MCP tool. Honoured only when non-empty AND the resolved
|
|
66
|
+
* directory actually exists.
|
|
67
|
+
* 2. user-cwd.txt — single source of truth maintained by claude-code
|
|
68
|
+
* (rewritten at every session start).
|
|
69
|
+
* Returns null when neither is available.
|
|
70
|
+
*
|
|
71
|
+
* Unlike captureOriginalUserCwd()/pwd(), this NEVER falls back to
|
|
72
|
+
* process.cwd(): the server's launch directory is not a session signal,
|
|
73
|
+
* and letting it leak into PROJECT CLASSIFICATION (resolveProjectScope)
|
|
74
|
+
* would misclassify rows under the service/plugin cwd. Use this for
|
|
75
|
+
* project_id resolution; use pwd() for relative-path resolution.
|
|
76
|
+
*/
|
|
77
|
+
export function explicitSessionCwd() {
|
|
78
|
+
const sessionRaw = process.env.MIXDOG_SESSION_CWD
|
|
79
|
+
if (typeof sessionRaw === 'string' && sessionRaw.length > 0) {
|
|
80
|
+
const normalized = _normalizePlatformCwd(sessionRaw)
|
|
81
|
+
if (normalized) {
|
|
82
|
+
try {
|
|
83
|
+
const st = statSync(normalized)
|
|
84
|
+
if (st.isDirectory()) return normalized
|
|
85
|
+
} catch { /* fall through to user-cwd.txt */ }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const txt = readFileSync(join(process.env.CLAUDE_PLUGIN_DATA || '', 'user-cwd.txt'), 'utf8').trim()
|
|
90
|
+
return (txt && _normalizePlatformCwd(txt)) || null
|
|
91
|
+
} catch {
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the Claude Code SESSION ENTRY root from CLAUDE_PROJECT_DIR — the
|
|
98
|
+
* directory the user launched Claude Code in, injected by the host on every
|
|
99
|
+
* run. This is an explicit host-provided invariant (not a heuristic guess),
|
|
100
|
+
* so it is the correct relative-path base when no session cwd has been set
|
|
101
|
+
* yet AND user-cwd.txt is unwritten (e.g. a hook fired with an empty
|
|
102
|
+
* _event.cwd). Returns null when the var is absent or not a live directory,
|
|
103
|
+
* so callers chain it ahead of the process.cwd() last resort.
|
|
104
|
+
*/
|
|
105
|
+
function startRootCwd() {
|
|
106
|
+
const dir = process.env.CLAUDE_PROJECT_DIR
|
|
107
|
+
if (typeof dir === 'string' && dir.length > 0) {
|
|
108
|
+
const normalized = _normalizePlatformCwd(dir)
|
|
109
|
+
if (normalized) {
|
|
110
|
+
try {
|
|
111
|
+
if (statSync(normalized).isDirectory()) return normalized
|
|
112
|
+
} catch { /* not a live directory — fall through */ }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resolve the user's current working directory for RELATIVE PATH
|
|
120
|
+
* RESOLUTION. Same explicit precedence as explicitSessionCwd(), then the
|
|
121
|
+
* session-entry root (CLAUDE_PROJECT_DIR), with process.cwd() as the final
|
|
122
|
+
* fallback when no explicit session cwd exists.
|
|
123
|
+
*
|
|
124
|
+
* Read fresh on every call: hot lookups inside a worker are short-
|
|
125
|
+
* circuited by runWithCwdOverride AsyncLocalStorage long before reaching
|
|
126
|
+
* this function, so the no-override fallback path is cold and per-call
|
|
127
|
+
* disk reads are negligible.
|
|
128
|
+
*/
|
|
129
|
+
export function captureOriginalUserCwd() {
|
|
130
|
+
return explicitSessionCwd() ?? startRootCwd() ?? _safeProcessCwd()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Read the user-cwd.txt sentinel directly, with NO env-var consult.
|
|
135
|
+
* Used by the cwd-tool auto-init path to avoid self-reference when
|
|
136
|
+
* deciding whether to seed MIXDOG_SESSION_CWD from disk.
|
|
137
|
+
*/
|
|
138
|
+
export function rawUserCwd() {
|
|
139
|
+
try {
|
|
140
|
+
const txt = readFileSync(join(process.env.CLAUDE_PLUGIN_DATA || '', 'user-cwd.txt'), 'utf8').trim()
|
|
141
|
+
return _normalizePlatformCwd(txt) || startRootCwd() || _safeProcessCwd()
|
|
142
|
+
} catch {
|
|
143
|
+
return startRootCwd() ?? _safeProcessCwd()
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Path of the persisted last-session-cwd sentinel, KEYED by the supervisor
|
|
149
|
+
* (run-mcp) PID injected as MIXDOG_SUPERVISOR_PID. The supervisor is one
|
|
150
|
+
* per terminal/MCP client and is preserved across a dev-sync child restart
|
|
151
|
+
* (only the child is killed + respawned), so its PID is a stable, per-
|
|
152
|
+
* terminal key that survives the restart. Keying by it makes the restore
|
|
153
|
+
* multi-terminal safe: terminal A's `cwd set` writes session-cwd-<pidA>.txt
|
|
154
|
+
* and terminal B writes session-cwd-<pidB>.txt, so a restart in one terminal
|
|
155
|
+
* can never restore the other terminal's cwd. When no supervisor PID is
|
|
156
|
+
* present (direct launch with no respawn lifecycle) a single 'solo' file is
|
|
157
|
+
* used — there is no cross-restart key in that mode, matching the absence of
|
|
158
|
+
* a respawning supervisor.
|
|
159
|
+
*/
|
|
160
|
+
function _lastSessionCwdFile(keyPid) {
|
|
161
|
+
// Explicit keyPid (a connection's leadPid in daemon mode) wins; otherwise
|
|
162
|
+
// fall back to this process's MIXDOG_SUPERVISOR_PID. Under the shared daemon
|
|
163
|
+
// a single process serves N terminals, so keying writes by the per-connection
|
|
164
|
+
// leadPid is what keeps one terminal's `cwd set` out of another's sentinel.
|
|
165
|
+
const raw = (keyPid != null && keyPid !== '') ? String(keyPid) : process.env.MIXDOG_SUPERVISOR_PID
|
|
166
|
+
const key = (typeof raw === 'string' && /^\d+$/.test(raw)) ? raw : 'solo'
|
|
167
|
+
return join(process.env.CLAUDE_PLUGIN_DATA || '', `session-cwd-${key}.txt`)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Best-effort persist the last session cwd to disk (keyed per supervisor —
|
|
172
|
+
* see _lastSessionCwdFile) so the respawned child of the SAME terminal can
|
|
173
|
+
* restore it. Errors are swallowed — a convenience signal, not a contract.
|
|
174
|
+
*/
|
|
175
|
+
export function writeLastSessionCwd(cwd, keyPid) {
|
|
176
|
+
try {
|
|
177
|
+
writeFileSync(_lastSessionCwdFile(keyPid), String(cwd))
|
|
178
|
+
} catch { /* best-effort */ }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Read the persisted last session cwd for THIS terminal (keyed per supervisor
|
|
183
|
+
* PID). Returns the normalized path only when the directory still exists;
|
|
184
|
+
* otherwise null. Consulted by the boot-time cwd auto-init (server-main.mjs)
|
|
185
|
+
* ahead of the user-cwd.txt seed so the last `cwd set` survives a dev-sync
|
|
186
|
+
* child restart that dropped MIXDOG_SESSION_CWD.
|
|
187
|
+
*/
|
|
188
|
+
export function readLastSessionCwd(keyPid) {
|
|
189
|
+
try {
|
|
190
|
+
const content = readFileSync(_lastSessionCwdFile(keyPid), 'utf8')
|
|
191
|
+
const normalized = _normalizePlatformCwd(content.trim())
|
|
192
|
+
if (normalized && statSync(normalized).isDirectory()) return normalized
|
|
193
|
+
return null
|
|
194
|
+
} catch {
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Run fn inside an async context where pwd() returns cwd.
|
|
201
|
+
* All descendant async calls within fn see cwd as their working directory.
|
|
202
|
+
*/
|
|
203
|
+
export function runWithCwdOverride(cwd, fn) {
|
|
204
|
+
return _cwdOverride.run(cwd, fn)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Current effective working directory:
|
|
209
|
+
* override set by runWithCwdOverride (innermost wins) ?? original user cwd.
|
|
210
|
+
*/
|
|
211
|
+
export function pwd() {
|
|
212
|
+
return _cwdOverride.getStore() ?? captureOriginalUserCwd()
|
|
213
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import {
|
|
2
|
+
copyFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from 'fs';
|
|
11
|
+
import { dirname, join, resolve } from 'path';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
import { createHash } from 'crypto';
|
|
14
|
+
|
|
15
|
+
export function getBackupRoot() {
|
|
16
|
+
return process.env.MIXDOG_USER_DATA_BACKUP_ROOT
|
|
17
|
+
|| join(homedir(), '.claude', 'backups', 'mixdog-user-data');
|
|
18
|
+
}
|
|
19
|
+
const RECOVERY_NOTICE = 'RECOVERY-REQUIRED.txt';
|
|
20
|
+
|
|
21
|
+
const USER_DATA_FILES = [
|
|
22
|
+
'mixdog-config.json',
|
|
23
|
+
'user-workflow.json',
|
|
24
|
+
'user-workflow.md',
|
|
25
|
+
'history/user.md',
|
|
26
|
+
'history/bot.md',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const USER_DATA_DIRS = [
|
|
30
|
+
'schedules',
|
|
31
|
+
'webhooks',
|
|
32
|
+
'roles',
|
|
33
|
+
'workflows',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
function stamp() {
|
|
37
|
+
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function safeReason(reason) {
|
|
41
|
+
return String(reason || 'snapshot').replace(/[^a-z0-9_.-]+/gi, '-').slice(0, 48) || 'snapshot';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function initMarkerPath(dataDir) {
|
|
45
|
+
const id = createHash('sha256').update(String(dataDir || 'unknown')).digest('hex').slice(0, 16);
|
|
46
|
+
return join(getBackupRoot(), `.initialized-${id}.json`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isPlainObject(value) {
|
|
50
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function hasUserDataInitMarker(dataDir) {
|
|
54
|
+
return existsSync(initMarkerPath(dataDir));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Skip single-section wipe remnants (e.g. `{ search: … }` only). */
|
|
58
|
+
export function isStructurallyCompleteMixdogConfigBackup(parsed) {
|
|
59
|
+
if (!isPlainObject(parsed)) return false;
|
|
60
|
+
if (Object.keys(parsed).length <= 1) return false;
|
|
61
|
+
if (!parsed.agent && !parsed.channels) return false;
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Newest backup first: return the first structurally complete mixdog-config.json
|
|
67
|
+
* (skips degenerate single-section snapshots from a prior failed RMW).
|
|
68
|
+
*/
|
|
69
|
+
export function loadLatestMixdogConfigFromBackup(_dataDir) {
|
|
70
|
+
const root = getBackupRoot();
|
|
71
|
+
let entries = [];
|
|
72
|
+
try {
|
|
73
|
+
entries = readdirSync(root, { withFileTypes: true })
|
|
74
|
+
.filter((entry) => entry.isDirectory())
|
|
75
|
+
.map((entry) => entry.name)
|
|
76
|
+
.sort()
|
|
77
|
+
.reverse();
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
for (const name of entries) {
|
|
82
|
+
const cfgPath = join(root, name, 'mixdog-config.json');
|
|
83
|
+
if (!existsSync(cfgPath)) continue;
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(readFileSync(cfgPath, 'utf8'));
|
|
86
|
+
if (isStructurallyCompleteMixdogConfigBackup(parsed)) return parsed;
|
|
87
|
+
} catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function copyTree(src, dst, copied) {
|
|
95
|
+
const st = statSync(src);
|
|
96
|
+
if (st.isDirectory()) {
|
|
97
|
+
for (const name of readdirSync(src)) {
|
|
98
|
+
copyTree(join(src, name), join(dst, name), copied);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!st.isFile()) return;
|
|
103
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
104
|
+
copyFileSync(src, dst);
|
|
105
|
+
copied.push(dst);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function pruneBackups(keep = 40) {
|
|
109
|
+
let entries = [];
|
|
110
|
+
try {
|
|
111
|
+
entries = readdirSync(getBackupRoot(), { withFileTypes: true })
|
|
112
|
+
.filter((entry) => entry.isDirectory())
|
|
113
|
+
.map((entry) => entry.name)
|
|
114
|
+
.sort()
|
|
115
|
+
.reverse();
|
|
116
|
+
} catch {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const name of entries.slice(keep)) {
|
|
120
|
+
try { rmSync(join(getBackupRoot(), name), { recursive: true, force: true }); } catch {}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function markUserDataInitialized(dataDir) {
|
|
125
|
+
try {
|
|
126
|
+
mkdirSync(getBackupRoot(), { recursive: true });
|
|
127
|
+
writeFileSync(initMarkerPath(dataDir), JSON.stringify({
|
|
128
|
+
dataDir,
|
|
129
|
+
updatedAt: new Date().toISOString(),
|
|
130
|
+
}, null, 2) + '\n', 'utf8');
|
|
131
|
+
} catch {}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function backupUserData(dataDir, reason = 'snapshot') {
|
|
135
|
+
if (process.env.MIXDOG_SKIP_USER_DATA_BACKUP === '1' || process.env.MIXDOG_SKIP_USER_DATA_BACKUP === 'true') {
|
|
136
|
+
return { dir: null, copied: [] };
|
|
137
|
+
}
|
|
138
|
+
if (!dataDir || !existsSync(dataDir)) return { dir: null, copied: [] };
|
|
139
|
+
const backupDir = join(getBackupRoot(), `${stamp()}-${safeReason(reason)}`);
|
|
140
|
+
const copied = [];
|
|
141
|
+
for (const rel of USER_DATA_FILES) {
|
|
142
|
+
const src = join(dataDir, rel);
|
|
143
|
+
if (existsSync(src)) copyTree(src, join(backupDir, rel), copied);
|
|
144
|
+
}
|
|
145
|
+
for (const rel of USER_DATA_DIRS) {
|
|
146
|
+
const src = join(dataDir, rel);
|
|
147
|
+
if (existsSync(src)) copyTree(src, join(backupDir, rel), copied);
|
|
148
|
+
}
|
|
149
|
+
if (copied.length > 0) {
|
|
150
|
+
markUserDataInitialized(dataDir);
|
|
151
|
+
pruneBackups();
|
|
152
|
+
process.stderr.write(`[user-data-backup] ${reason}: copied ${copied.length} file(s) to ${backupDir}\n`);
|
|
153
|
+
}
|
|
154
|
+
return { dir: copied.length > 0 ? backupDir : null, copied };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function shouldSeedMissingUserData(dataDir, rel) {
|
|
158
|
+
if (!dataDir) return true;
|
|
159
|
+
if (existsSync(join(dataDir, rel))) {
|
|
160
|
+
markUserDataInitialized(dataDir);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
const markerPath = initMarkerPath(dataDir);
|
|
164
|
+
if (!existsSync(markerPath)) return true;
|
|
165
|
+
try {
|
|
166
|
+
mkdirSync(dataDir, { recursive: true });
|
|
167
|
+
writeFileSync(join(dataDir, RECOVERY_NOTICE), [
|
|
168
|
+
'Mixdog refused to recreate missing user data from defaults.',
|
|
169
|
+
'',
|
|
170
|
+
`Missing file: ${rel}`,
|
|
171
|
+
`Data dir: ${dataDir}`,
|
|
172
|
+
`Backup root: ${getBackupRoot()}`,
|
|
173
|
+
'',
|
|
174
|
+
'Restore the missing file from backup, or delete the backup marker',
|
|
175
|
+
markerPath,
|
|
176
|
+
'only if this is an intentional fresh reset.',
|
|
177
|
+
'',
|
|
178
|
+
].join('\n'), 'utf8');
|
|
179
|
+
} catch {}
|
|
180
|
+
process.stderr.write(`[seed-guard] refused default seed for missing ${rel}; restore from ${getBackupRoot()} or intentionally reset marker\n`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Names that mark a directory as the user-data ROOT. A package-manager run
|
|
185
|
+
// (bun install) or recursive delete targeting any directory that IS the data
|
|
186
|
+
// root, or that contains any of these, would wipe user state — so refuse.
|
|
187
|
+
const USER_DATA_SENTINELS = [
|
|
188
|
+
'mixdog-config.json',
|
|
189
|
+
'user-workflow.json',
|
|
190
|
+
'user-workflow.md',
|
|
191
|
+
'roles',
|
|
192
|
+
'schedules',
|
|
193
|
+
'webhooks',
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
// Subdir name this code OWNS and may freely create/install/delete inside.
|
|
197
|
+
const OWNED_SUBDIR = '.deps';
|
|
198
|
+
|
|
199
|
+
// On Windows the filesystem is case-insensitive, so path comparisons must be
|
|
200
|
+
// case-folded to stop variants like '.../Data/.deps' from bypassing the
|
|
201
|
+
// target===root / dirname===root equality checks.
|
|
202
|
+
const CASE_INSENSITIVE = process.platform === 'win32';
|
|
203
|
+
|
|
204
|
+
function normCase(p) {
|
|
205
|
+
return CASE_INSENSITIVE ? String(p).toLowerCase() : String(p);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Hard guard: throws unless `targetDir` is EXACTLY the owned subdir directly
|
|
210
|
+
* under the user-data root (`<dataDir>/<ownedSubdir>`). Every other path under
|
|
211
|
+
* the data root is refused — including the root itself, sentinel dirs, and
|
|
212
|
+
* arbitrary subdirs. Call BEFORE any `bun install` / recursive delete.
|
|
213
|
+
*
|
|
214
|
+
* @param {string} targetDir directory a destructive op is about to touch
|
|
215
|
+
* @param {string} dataDir the user-data root to protect
|
|
216
|
+
* @param {string} [op] label for the error message
|
|
217
|
+
* @param {string} [ownedSubdir] the single owned subdir name (default '.deps')
|
|
218
|
+
* @returns {string} the resolved owned path (only on allow)
|
|
219
|
+
*/
|
|
220
|
+
export function assertSafeOwnedDir(targetDir, dataDir, op = 'destructive op', ownedSubdir = OWNED_SUBDIR) {
|
|
221
|
+
if (!targetDir) throw new Error(`[data-guard] ${op} refused: empty target dir`);
|
|
222
|
+
if (!dataDir) throw new Error(`[data-guard] ${op} refused: empty data dir`);
|
|
223
|
+
|
|
224
|
+
// STRICT WHITELIST: the ONLY path this code may touch is the exact owned
|
|
225
|
+
// subdir directly under the data root (`<dataDir>/<ownedSubdir>`). Every
|
|
226
|
+
// other path — under the data root OR anywhere else on disk — is refused.
|
|
227
|
+
const target = resolve(targetDir);
|
|
228
|
+
const allowed = resolve(join(dataDir, ownedSubdir));
|
|
229
|
+
|
|
230
|
+
if (normCase(target) === normCase(allowed)) {
|
|
231
|
+
return target;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
throw new Error(
|
|
235
|
+
`[data-guard] ${op} refused: ${target} is not the owned dir. ` +
|
|
236
|
+
`Only the exact owned subdir (${allowed}) may be operated on.`
|
|
237
|
+
);
|
|
238
|
+
}
|