@mastra/core 1.2.1-alpha.0 → 1.3.0-alpha.2
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/CHANGELOG.md +341 -0
- package/dist/_types/@internal_ai-sdk-v5/dist/index.d.ts +2093 -262
- package/dist/agent/agent-legacy.d.ts.map +1 -1
- package/dist/agent/agent.d.ts +12 -3
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/index.cjs +13 -13
- package/dist/agent/index.js +2 -2
- package/dist/agent/message-list/index.cjs +18 -18
- package/dist/agent/message-list/index.js +1 -1
- package/dist/agent/message-list/message-list.d.ts.map +1 -1
- package/dist/agent/types.d.ts +6 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/index.d.ts +3 -1
- package/dist/agent/workflows/prepare-stream/index.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/map-results-step.d.ts.map +1 -1
- package/dist/agent/workflows/prepare-stream/stream-step.d.ts +3 -1
- package/dist/agent/workflows/prepare-stream/stream-step.d.ts.map +1 -1
- package/dist/base.cjs +2 -2
- package/dist/base.d.ts +13 -1
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +1 -1
- package/dist/bundler/index.cjs +2 -2
- package/dist/bundler/index.js +1 -1
- package/dist/cache/index.cjs +3 -3
- package/dist/cache/index.js +1 -1
- package/dist/{chunk-ENCTSDWC.js → chunk-2GWTJFVM.js} +2410 -1402
- package/dist/chunk-2GWTJFVM.js.map +1 -0
- package/dist/{chunk-OB4V67IX.cjs → chunk-2K5PNW2U.cjs} +4 -4
- package/dist/{chunk-OB4V67IX.cjs.map → chunk-2K5PNW2U.cjs.map} +1 -1
- package/dist/{chunk-VZXYBFCX.cjs → chunk-2P6DD7M5.cjs} +10 -10
- package/dist/{chunk-VZXYBFCX.cjs.map → chunk-2P6DD7M5.cjs.map} +1 -1
- package/dist/{chunk-Q2D7LERO.cjs → chunk-2VD5OGOT.cjs} +4 -4
- package/dist/{chunk-Q2D7LERO.cjs.map → chunk-2VD5OGOT.cjs.map} +1 -1
- package/dist/{chunk-2DMSFLJY.cjs → chunk-6TZKQI4R.cjs} +8 -8
- package/dist/chunk-6TZKQI4R.cjs.map +1 -0
- package/dist/{chunk-4NG7CKEG.js → chunk-6VGCVSP4.js} +3 -3
- package/dist/{chunk-4NG7CKEG.js.map → chunk-6VGCVSP4.js.map} +1 -1
- package/dist/{chunk-IIZF4W7B.cjs → chunk-7UWHFWST.cjs} +52 -5
- package/dist/chunk-7UWHFWST.cjs.map +1 -0
- package/dist/{chunk-VW7YQWDW.cjs → chunk-BFIOQFGF.cjs} +28 -2
- package/dist/chunk-BFIOQFGF.cjs.map +1 -0
- package/dist/{chunk-E3VFKTIA.js → chunk-BXLLXTT4.js} +2245 -179
- package/dist/chunk-BXLLXTT4.js.map +1 -0
- package/dist/{chunk-HMCXNOF6.cjs → chunk-CGPH7CMG.cjs} +2431 -1417
- package/dist/chunk-CGPH7CMG.cjs.map +1 -0
- package/dist/{chunk-GVLPTDJA.cjs → chunk-D5JZT6EK.cjs} +236 -63
- package/dist/chunk-D5JZT6EK.cjs.map +1 -0
- package/dist/{chunk-5SOS47PH.cjs → chunk-FLHFB23E.cjs} +454 -222
- package/dist/chunk-FLHFB23E.cjs.map +1 -0
- package/dist/{chunk-XCPEEIHI.cjs → chunk-GCTAD6B7.cjs} +3012 -927
- package/dist/chunk-GCTAD6B7.cjs.map +1 -0
- package/dist/{chunk-W3AQUG66.js → chunk-GIY5BINT.js} +4 -4
- package/dist/{chunk-W3AQUG66.js.map → chunk-GIY5BINT.js.map} +1 -1
- package/dist/{chunk-M6VFJX5A.js → chunk-GMSAGYTB.js} +3 -3
- package/dist/{chunk-M6VFJX5A.js.map → chunk-GMSAGYTB.js.map} +1 -1
- package/dist/{chunk-JRJJ5VQE.cjs → chunk-GZD6443M.cjs} +99 -55
- package/dist/chunk-GZD6443M.cjs.map +1 -0
- package/dist/{chunk-Z6NRYYOH.js → chunk-ILQXPZCD.js} +9 -4
- package/dist/chunk-ILQXPZCD.js.map +1 -0
- package/dist/{chunk-5YVR7B4R.js → chunk-JQNREL64.js} +29 -9
- package/dist/chunk-JQNREL64.js.map +1 -0
- package/dist/{chunk-QOFI2WBM.cjs → chunk-JU6K7UDX.cjs} +991 -229
- package/dist/chunk-JU6K7UDX.cjs.map +1 -0
- package/dist/{chunk-HN2MSTR6.cjs → chunk-KAJNBNWP.cjs} +283 -38
- package/dist/chunk-KAJNBNWP.cjs.map +1 -0
- package/dist/{chunk-MSWXEOZC.js → chunk-KL2JPSRX.js} +5 -5
- package/dist/chunk-KL2JPSRX.js.map +1 -0
- package/dist/{chunk-MQB7XFXP.js → chunk-LHRHOPUC.js} +3 -3
- package/dist/{chunk-MQB7XFXP.js.map → chunk-LHRHOPUC.js.map} +1 -1
- package/dist/{chunk-VX7UA3SO.js → chunk-MOOJ3H3C.js} +974 -217
- package/dist/chunk-MOOJ3H3C.js.map +1 -0
- package/dist/{chunk-HU2ONA2W.cjs → chunk-ON2KVIUJ.cjs} +17 -17
- package/dist/{chunk-HU2ONA2W.cjs.map → chunk-ON2KVIUJ.cjs.map} +1 -1
- package/dist/{chunk-I66TMZJ3.cjs → chunk-OOCEAC6U.cjs} +21 -18
- package/dist/chunk-OOCEAC6U.cjs.map +1 -0
- package/dist/{chunk-MNWW2R3U.js → chunk-OV7OOUUR.js} +90 -54
- package/dist/chunk-OV7OOUUR.js.map +1 -0
- package/dist/{chunk-C4WWWQHT.cjs → chunk-RO47SMI7.cjs} +23 -3
- package/dist/chunk-RO47SMI7.cjs.map +1 -0
- package/dist/{chunk-6FG6FU5Y.cjs → chunk-RQ56ZSIR.cjs} +4 -4
- package/dist/{chunk-6FG6FU5Y.cjs.map → chunk-RQ56ZSIR.cjs.map} +1 -1
- package/dist/{chunk-IW3BNL7A.js → chunk-RS6CZXGA.js} +50 -3
- package/dist/chunk-RS6CZXGA.js.map +1 -0
- package/dist/{chunk-AIJLACR2.js → chunk-S53FKKVL.js} +27 -3
- package/dist/chunk-S53FKKVL.js.map +1 -0
- package/dist/{chunk-G6E3QNJC.js → chunk-SBPPGJL6.js} +4078 -4333
- package/dist/chunk-SBPPGJL6.js.map +1 -0
- package/dist/{chunk-UZL4H5P2.cjs → chunk-SH4PCZ3X.cjs} +5375 -5648
- package/dist/chunk-SH4PCZ3X.cjs.map +1 -0
- package/dist/{chunk-ZWM2CAIM.js → chunk-STKNQDVA.js} +4 -4
- package/dist/{chunk-ZWM2CAIM.js.map → chunk-STKNQDVA.js.map} +1 -1
- package/dist/{chunk-A5QFWX67.cjs → chunk-U2CABSMC.cjs} +79 -59
- package/dist/chunk-U2CABSMC.cjs.map +1 -0
- package/dist/{chunk-7MDVYPWX.cjs → chunk-UE2G2LRP.cjs} +9 -4
- package/dist/chunk-UE2G2LRP.cjs.map +1 -0
- package/dist/{chunk-AUF6U2BL.js → chunk-VM25PDSW.js} +5 -5
- package/dist/{chunk-AUF6U2BL.js.map → chunk-VM25PDSW.js.map} +1 -1
- package/dist/{chunk-RXD5EGQF.js → chunk-VVD56FI4.js} +228 -55
- package/dist/chunk-VVD56FI4.js.map +1 -0
- package/dist/{chunk-JIT2OY3X.js → chunk-WCAFTXGK.js} +23 -3
- package/dist/chunk-WCAFTXGK.js.map +1 -0
- package/dist/{chunk-P62OJXQ4.js → chunk-WL3AW3YA.js} +282 -37
- package/dist/chunk-WL3AW3YA.js.map +1 -0
- package/dist/{chunk-YLODOPYM.cjs → chunk-XDD5V446.cjs} +4163 -5352
- package/dist/chunk-XDD5V446.cjs.map +1 -0
- package/dist/{chunk-B4M33FCS.cjs → chunk-XQVYEOI7.cjs} +7 -7
- package/dist/{chunk-B4M33FCS.cjs.map → chunk-XQVYEOI7.cjs.map} +1 -1
- package/dist/{chunk-BP2TSCBW.js → chunk-ZATLLPIH.js} +4391 -5561
- package/dist/chunk-ZATLLPIH.js.map +1 -0
- package/dist/{chunk-T6PRRKMW.js → chunk-ZHFM7HCQ.js} +9 -6
- package/dist/chunk-ZHFM7HCQ.js.map +1 -0
- package/dist/{chunk-WFUNLRQX.js → chunk-ZRUTE56J.js} +366 -134
- package/dist/chunk-ZRUTE56J.js.map +1 -0
- package/dist/deployer/index.cjs +2 -2
- package/dist/deployer/index.js +1 -1
- package/dist/docs/SKILL.md +2 -9
- package/dist/docs/assets/SOURCE_MAP.json +399 -342
- package/dist/docs/references/docs-agents-agent-memory.md +45 -1
- package/dist/docs/references/docs-agents-network-approval.md +1 -1
- package/dist/docs/references/docs-agents-networks.md +3 -3
- package/dist/docs/references/docs-agents-overview.md +8 -0
- package/dist/docs/references/docs-agents-using-tools.md +82 -72
- package/dist/docs/references/docs-memory-observational-memory.md +11 -8
- package/dist/docs/references/docs-observability-overview.md +1 -1
- package/dist/docs/references/docs-observability-tracing-exporters-langsmith.md +70 -0
- package/dist/docs/references/docs-observability-tracing-overview.md +1 -1
- package/dist/docs/references/docs-server-middleware.md +0 -2
- package/dist/docs/references/docs-server-request-context.md +17 -0
- package/dist/docs/references/docs-workflows-agents-and-tools.md +2 -2
- package/dist/docs/references/docs-workflows-overview.md +1 -1
- package/dist/docs/references/docs-workspace-filesystem.md +2 -0
- package/dist/docs/references/docs-workspace-overview.md +3 -1
- package/dist/docs/references/docs-workspace-sandbox.md +2 -0
- package/dist/docs/references/docs-workspace-search.md +2 -0
- package/dist/docs/references/docs-workspace-skills.md +3 -1
- package/dist/docs/references/reference-agents-getTools.md +1 -6
- package/dist/docs/references/reference-agents-listAgents.md +1 -1
- package/dist/docs/references/reference-agents-network.md +0 -2
- package/dist/docs/references/reference-cli-mastra.md +29 -4
- package/dist/docs/references/reference-client-js-agents.md +1 -1
- package/dist/docs/references/reference-configuration.md +1 -1
- package/dist/docs/references/reference-core-getStoredAgentById.md +2 -2
- package/dist/docs/references/reference-core-listStoredAgents.md +1 -1
- package/dist/docs/references/reference-memory-observational-memory.md +2 -0
- package/dist/docs/references/reference-tools-mcp-client.md +0 -2
- package/dist/docs/references/reference-workflows-step.md +2 -0
- package/dist/docs/references/reference-workflows-workflow-methods-map.md +2 -2
- package/dist/docs/references/reference-workspace-filesystem.md +2 -0
- package/dist/docs/references/reference-workspace-local-filesystem.md +2 -0
- package/dist/docs/references/reference-workspace-local-sandbox.md +2 -0
- package/dist/docs/references/reference-workspace-sandbox.md +2 -0
- package/dist/docs/references/reference-workspace-workspace-class.md +2 -0
- package/dist/docs/references/reference.md +7 -2
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/types.d.ts +53 -48
- package/dist/editor/types.d.ts.map +1 -1
- package/dist/evals/base.d.ts +15 -0
- package/dist/evals/base.d.ts.map +1 -1
- package/dist/evals/index.cjs +20 -20
- package/dist/evals/index.js +3 -3
- package/dist/evals/run/index.d.ts +3 -3
- package/dist/evals/run/index.d.ts.map +1 -1
- package/dist/evals/scoreTraces/index.cjs +5 -5
- package/dist/evals/scoreTraces/index.js +2 -2
- package/dist/features/index.cjs +1 -1
- package/dist/features/index.cjs.map +1 -1
- package/dist/features/index.d.ts.map +1 -1
- package/dist/features/index.js +1 -1
- package/dist/features/index.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/integration/index.cjs +2 -2
- package/dist/integration/index.js +1 -1
- package/dist/llm/index.cjs +16 -16
- package/dist/llm/index.js +5 -5
- package/dist/llm/model/gateways/constants.d.ts.map +1 -1
- package/dist/llm/model/gateways/models-dev.d.ts.map +1 -1
- package/dist/llm/model/model.loop.d.ts +1 -1
- package/dist/llm/model/model.loop.d.ts.map +1 -1
- package/dist/llm/model/provider-types.generated.d.ts +128 -10
- package/dist/llm/model/resolve-model.d.ts.map +1 -1
- package/dist/loop/index.cjs +12 -12
- package/dist/loop/index.js +1 -1
- package/dist/loop/network/index.d.ts +3 -3
- package/dist/loop/network/index.d.ts.map +1 -1
- package/dist/loop/network/run-command-tool.d.ts +1 -1
- package/dist/loop/types.d.ts +13 -0
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/index.d.ts +9 -9
- package/dist/loop/workflows/agentic-execution/llm-execution-step.d.ts +7 -7
- package/dist/loop/workflows/agentic-execution/llm-execution-step.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-execution/llm-mapping-step.d.ts +3 -3
- package/dist/loop/workflows/agentic-execution/tool-call-step.d.ts.map +1 -1
- package/dist/loop/workflows/agentic-loop/index.d.ts +9 -9
- package/dist/loop/workflows/schema.d.ts +12 -12
- package/dist/mastra/index.cjs +2 -2
- package/dist/mastra/index.d.ts +91 -5
- package/dist/mastra/index.d.ts.map +1 -1
- package/dist/mastra/index.js +1 -1
- package/dist/mcp/index.cjs +2 -2
- package/dist/mcp/index.js +1 -1
- package/dist/memory/index.cjs +14 -14
- package/dist/memory/index.js +1 -1
- package/dist/memory/mock.d.ts.map +1 -1
- package/dist/memory/types.d.ts +128 -0
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/models-dev-FQVUTQ7L.js +3 -0
- package/dist/{models-dev-Z45JSLWD.js.map → models-dev-FQVUTQ7L.js.map} +1 -1
- package/dist/models-dev-PPIXUUCU.cjs +12 -0
- package/dist/{models-dev-OQKVMEIT.cjs.map → models-dev-PPIXUUCU.cjs.map} +1 -1
- package/dist/netlify-4RIKF7Y3.js +3 -0
- package/dist/{netlify-SSWMYSAX.js.map → netlify-4RIKF7Y3.js.map} +1 -1
- package/dist/netlify-V5F7JEJH.cjs +12 -0
- package/dist/{netlify-TXZZCT6N.cjs.map → netlify-V5F7JEJH.cjs.map} +1 -1
- package/dist/processors/index.cjs +41 -41
- package/dist/processors/index.js +1 -1
- package/dist/processors/step-schema.d.ts +44 -44
- package/dist/provider-registry-C6XCYX44.cjs +40 -0
- package/dist/{provider-registry-NR7FXV2Q.cjs.map → provider-registry-C6XCYX44.cjs.map} +1 -1
- package/dist/provider-registry-NWU4YFQW.js +3 -0
- package/dist/{provider-registry-RPOTQNHI.js.map → provider-registry-NWU4YFQW.js.map} +1 -1
- package/dist/provider-registry.json +278 -33
- package/dist/relevance/index.cjs +3 -3
- package/dist/relevance/index.js +1 -1
- package/dist/server/index.cjs +3 -3
- package/dist/server/index.js +1 -1
- package/dist/storage/base.d.ts +3 -1
- package/dist/storage/base.d.ts.map +1 -1
- package/dist/storage/constants.cjs +51 -19
- package/dist/storage/constants.d.ts +10 -2
- package/dist/storage/constants.d.ts.map +1 -1
- package/dist/storage/constants.js +1 -1
- package/dist/storage/domains/agents/base.d.ts +13 -164
- package/dist/storage/domains/agents/base.d.ts.map +1 -1
- package/dist/storage/domains/agents/index.d.ts +0 -1
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/agents/inmemory.d.ts +6 -10
- package/dist/storage/domains/agents/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/index.d.ts +3 -0
- package/dist/storage/domains/index.d.ts.map +1 -1
- package/dist/storage/domains/inmemory-db.d.ts +7 -1
- package/dist/storage/domains/inmemory-db.d.ts.map +1 -1
- package/dist/storage/domains/memory/base.d.ts +45 -5
- package/dist/storage/domains/memory/base.d.ts.map +1 -1
- package/dist/storage/domains/memory/inmemory.d.ts +7 -7
- package/dist/storage/domains/memory/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/observability/types.d.ts +62 -62
- package/dist/storage/domains/operations/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/prompt-blocks/base.d.ts +47 -0
- package/dist/storage/domains/prompt-blocks/base.d.ts.map +1 -0
- package/dist/storage/domains/prompt-blocks/index.d.ts +3 -0
- package/dist/storage/domains/prompt-blocks/index.d.ts.map +1 -0
- package/dist/storage/domains/prompt-blocks/inmemory.d.ts +31 -0
- package/dist/storage/domains/prompt-blocks/inmemory.d.ts.map +1 -0
- package/dist/storage/domains/scorer-definitions/base.d.ts +47 -0
- package/dist/storage/domains/scorer-definitions/base.d.ts.map +1 -0
- package/dist/storage/domains/scorer-definitions/index.d.ts +3 -0
- package/dist/storage/domains/scorer-definitions/index.d.ts.map +1 -0
- package/dist/storage/domains/scorer-definitions/inmemory.d.ts +31 -0
- package/dist/storage/domains/scorer-definitions/inmemory.d.ts.map +1 -0
- package/dist/storage/domains/versioned.d.ts +136 -0
- package/dist/storage/domains/versioned.d.ts.map +1 -0
- package/dist/storage/index.cjs +156 -104
- package/dist/storage/index.js +2 -2
- package/dist/storage/mock.d.ts.map +1 -1
- package/dist/storage/types.d.ts +481 -27
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/stream/MastraAgentNetworkStream.d.ts +1 -1
- package/dist/stream/MastraWorkflowStream.d.ts +1 -1
- package/dist/stream/RunOutput.d.ts +1 -1
- package/dist/stream/RunOutput.d.ts.map +1 -1
- package/dist/stream/base/output.d.ts +4 -0
- package/dist/stream/base/output.d.ts.map +1 -1
- package/dist/stream/index.cjs +11 -11
- package/dist/stream/index.js +2 -2
- package/dist/test-utils/llm-mock.cjs +4 -4
- package/dist/test-utils/llm-mock.js +1 -1
- package/dist/tool-loop-agent/index.cjs +4 -4
- package/dist/tool-loop-agent/index.js +1 -1
- package/dist/tools/index.cjs +4 -4
- package/dist/tools/index.js +1 -1
- package/dist/tools/is-vercel-tool.cjs +2 -2
- package/dist/tools/is-vercel-tool.js +1 -1
- package/dist/tools/tool-builder/builder.d.ts.map +1 -1
- package/dist/tools/types.d.ts +15 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/validation.d.ts.map +1 -1
- package/dist/tts/index.cjs +2 -2
- package/dist/tts/index.js +1 -1
- package/dist/types/zod-compat.d.ts +2 -2
- package/dist/types/zod-compat.d.ts.map +1 -1
- package/dist/utils/zod-utils.d.ts +19 -0
- package/dist/utils/zod-utils.d.ts.map +1 -1
- package/dist/utils.cjs +28 -28
- package/dist/utils.d.ts +7 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/vector/index.cjs +9 -9
- package/dist/vector/index.js +2 -2
- package/dist/voice/index.cjs +6 -6
- package/dist/voice/index.js +1 -1
- package/dist/workflows/evented/index.cjs +10 -10
- package/dist/workflows/evented/index.js +1 -1
- package/dist/workflows/evented/step-executor.d.ts.map +1 -1
- package/dist/workflows/evented/workflow.d.ts +3 -1
- package/dist/workflows/evented/workflow.d.ts.map +1 -1
- package/dist/workflows/execution-engine.d.ts.map +1 -1
- package/dist/workflows/index.cjs +25 -25
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/step.d.ts +4 -3
- package/dist/workflows/step.d.ts.map +1 -1
- package/dist/workflows/types.d.ts +2 -1
- package/dist/workflows/types.d.ts.map +1 -1
- package/dist/workflows/workflow.d.ts +12 -4
- package/dist/workflows/workflow.d.ts.map +1 -1
- package/dist/workspace/errors.d.ts +6 -0
- package/dist/workspace/errors.d.ts.map +1 -1
- package/dist/workspace/filesystem/composite-filesystem.d.ts +93 -0
- package/dist/workspace/filesystem/composite-filesystem.d.ts.map +1 -0
- package/dist/workspace/filesystem/filesystem.d.ts +35 -0
- package/dist/workspace/filesystem/filesystem.d.ts.map +1 -1
- package/dist/workspace/filesystem/index.d.ts +2 -0
- package/dist/workspace/filesystem/index.d.ts.map +1 -1
- package/dist/workspace/filesystem/local-filesystem.d.ts +9 -1
- package/dist/workspace/filesystem/local-filesystem.d.ts.map +1 -1
- package/dist/workspace/filesystem/mastra-filesystem.d.ts +92 -3
- package/dist/workspace/filesystem/mastra-filesystem.d.ts.map +1 -1
- package/dist/workspace/filesystem/mount.d.ts +34 -0
- package/dist/workspace/filesystem/mount.d.ts.map +1 -0
- package/dist/workspace/index.cjs +62 -34
- package/dist/workspace/index.d.ts +6 -2
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +1 -1
- package/dist/workspace/lifecycle.d.ts +29 -0
- package/dist/workspace/lifecycle.d.ts.map +1 -1
- package/dist/workspace/sandbox/errors.d.ts +50 -0
- package/dist/workspace/sandbox/errors.d.ts.map +1 -0
- package/dist/workspace/sandbox/index.d.ts +3 -0
- package/dist/workspace/sandbox/index.d.ts.map +1 -1
- package/dist/workspace/sandbox/local-sandbox.d.ts +17 -2
- package/dist/workspace/sandbox/local-sandbox.d.ts.map +1 -1
- package/dist/workspace/sandbox/mastra-sandbox.d.ts +153 -6
- package/dist/workspace/sandbox/mastra-sandbox.d.ts.map +1 -1
- package/dist/workspace/sandbox/mount-manager.d.ts +195 -0
- package/dist/workspace/sandbox/mount-manager.d.ts.map +1 -0
- package/dist/workspace/sandbox/sandbox.d.ts +37 -84
- package/dist/workspace/sandbox/sandbox.d.ts.map +1 -1
- package/dist/workspace/sandbox/types.d.ts +92 -0
- package/dist/workspace/sandbox/types.d.ts.map +1 -0
- package/dist/workspace/tools/tools.d.ts.map +1 -1
- package/dist/workspace/tools/tree-formatter.d.ts.map +1 -1
- package/dist/workspace/workspace.d.ts +73 -3
- package/dist/workspace/workspace.d.ts.map +1 -1
- package/package.json +16 -16
- package/src/llm/model/provider-types.generated.d.ts +128 -10
- package/dist/chunk-2DMSFLJY.cjs.map +0 -1
- package/dist/chunk-5SOS47PH.cjs.map +0 -1
- package/dist/chunk-5YVR7B4R.js.map +0 -1
- package/dist/chunk-7MDVYPWX.cjs.map +0 -1
- package/dist/chunk-A5QFWX67.cjs.map +0 -1
- package/dist/chunk-AIJLACR2.js.map +0 -1
- package/dist/chunk-BP2TSCBW.js.map +0 -1
- package/dist/chunk-C4WWWQHT.cjs.map +0 -1
- package/dist/chunk-E3VFKTIA.js.map +0 -1
- package/dist/chunk-ENCTSDWC.js.map +0 -1
- package/dist/chunk-G6E3QNJC.js.map +0 -1
- package/dist/chunk-GVLPTDJA.cjs.map +0 -1
- package/dist/chunk-HMCXNOF6.cjs.map +0 -1
- package/dist/chunk-HN2MSTR6.cjs.map +0 -1
- package/dist/chunk-I66TMZJ3.cjs.map +0 -1
- package/dist/chunk-IIZF4W7B.cjs.map +0 -1
- package/dist/chunk-IW3BNL7A.js.map +0 -1
- package/dist/chunk-JIT2OY3X.js.map +0 -1
- package/dist/chunk-JRJJ5VQE.cjs.map +0 -1
- package/dist/chunk-MNWW2R3U.js.map +0 -1
- package/dist/chunk-MSWXEOZC.js.map +0 -1
- package/dist/chunk-P62OJXQ4.js.map +0 -1
- package/dist/chunk-QOFI2WBM.cjs.map +0 -1
- package/dist/chunk-RXD5EGQF.js.map +0 -1
- package/dist/chunk-T6PRRKMW.js.map +0 -1
- package/dist/chunk-UZL4H5P2.cjs.map +0 -1
- package/dist/chunk-VW7YQWDW.cjs.map +0 -1
- package/dist/chunk-VX7UA3SO.js.map +0 -1
- package/dist/chunk-WFUNLRQX.js.map +0 -1
- package/dist/chunk-XCPEEIHI.cjs.map +0 -1
- package/dist/chunk-YLODOPYM.cjs.map +0 -1
- package/dist/chunk-Z6NRYYOH.js.map +0 -1
- package/dist/docs/references/docs-tools-mcp-advanced-usage.md +0 -143
- package/dist/docs/references/docs-tools-mcp-mcp-overview.md +0 -383
- package/dist/docs/references/docs-tools-mcp-overview.md +0 -78
- package/dist/docs/references/docs-workflows-input-data-mapping.md +0 -102
- package/dist/docs/references/reference-deployer-netlify.md +0 -14
- package/dist/docs/references/reference-deployer-vercel.md +0 -39
- package/dist/docs/references/reference-tools-client.md +0 -228
- package/dist/models-dev-OQKVMEIT.cjs +0 -12
- package/dist/models-dev-Z45JSLWD.js +0 -3
- package/dist/netlify-SSWMYSAX.js +0 -3
- package/dist/netlify-TXZZCT6N.cjs +0 -12
- package/dist/provider-registry-NR7FXV2Q.cjs +0 -40
- package/dist/provider-registry-RPOTQNHI.js +0 -3
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunk7UWHFWST_cjs = require('./chunk-7UWHFWST.cjs');
|
|
4
|
+
var chunkRO47SMI7_cjs = require('./chunk-RO47SMI7.cjs');
|
|
5
5
|
var chunk7XAECHYL_cjs = require('./chunk-7XAECHYL.cjs');
|
|
6
|
+
var fs = require('fs');
|
|
6
7
|
var fs2 = require('fs/promises');
|
|
7
8
|
var nodePath = require('path');
|
|
8
|
-
var
|
|
9
|
+
var crypto = require('crypto');
|
|
9
10
|
var matter = require('gray-matter');
|
|
10
11
|
var childProcess = require('child_process');
|
|
11
|
-
var crypto = require('crypto');
|
|
12
12
|
var os = require('os');
|
|
13
13
|
var zod = require('zod');
|
|
14
14
|
|
|
@@ -34,9 +34,9 @@ function _interopNamespace(e) {
|
|
|
34
34
|
|
|
35
35
|
var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
|
|
36
36
|
var nodePath__namespace = /*#__PURE__*/_interopNamespace(nodePath);
|
|
37
|
+
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
37
38
|
var matter__default = /*#__PURE__*/_interopDefault(matter);
|
|
38
39
|
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
39
|
-
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
40
40
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
41
41
|
|
|
42
42
|
// src/workspace/errors.ts
|
|
@@ -141,1495 +141,2521 @@ var FileReadRequiredError = class extends FilesystemError {
|
|
|
141
141
|
this.name = "FileReadRequiredError";
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
super({ name: options.name, component: chunk7XAECHYL_cjs.RegisteredLogger.WORKSPACE });
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// src/workspace/sandbox/mastra-sandbox.ts
|
|
153
|
-
var MastraSandbox = class extends chunkC4WWWQHT_cjs.MastraBase {
|
|
154
|
-
constructor(options) {
|
|
155
|
-
super({ name: options.name, component: chunk7XAECHYL_cjs.RegisteredLogger.WORKSPACE });
|
|
144
|
+
var FilesystemNotReadyError = class extends FilesystemError {
|
|
145
|
+
constructor(id) {
|
|
146
|
+
super(`Filesystem "${id}" is not ready. Call init() first or use ensureReady().`, "ENOTREADY", id);
|
|
147
|
+
this.name = "FilesystemNotReadyError";
|
|
156
148
|
}
|
|
157
149
|
};
|
|
158
150
|
|
|
159
|
-
// src/workspace/
|
|
160
|
-
function
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const extractedLines = allLines.slice(start - 1, end);
|
|
166
|
-
return {
|
|
167
|
-
content: extractedLines.join("\n"),
|
|
168
|
-
lines: { start, end },
|
|
169
|
-
totalLines
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
function extractLinesWithLimit(content, offset, limit) {
|
|
173
|
-
const startLine = offset ?? 1;
|
|
174
|
-
const endLine = limit ? startLine + limit - 1 : void 0;
|
|
175
|
-
return extractLines(content, startLine, endLine);
|
|
176
|
-
}
|
|
177
|
-
function formatWithLineNumbers(content, startLineNumber = 1) {
|
|
178
|
-
const lines = content.split("\n");
|
|
179
|
-
const maxLineNum = startLineNumber + lines.length - 1;
|
|
180
|
-
const padWidth = Math.max(6, String(maxLineNum).length + 1);
|
|
181
|
-
return lines.map((line, i) => {
|
|
182
|
-
const lineNum = startLineNumber + i;
|
|
183
|
-
return `${String(lineNum).padStart(padWidth)}\u2192${line}`;
|
|
184
|
-
}).join("\n");
|
|
185
|
-
}
|
|
186
|
-
function countOccurrences(content, searchString) {
|
|
187
|
-
if (!searchString) return 0;
|
|
188
|
-
let count = 0;
|
|
189
|
-
let position = 0;
|
|
190
|
-
while ((position = content.indexOf(searchString, position)) !== -1) {
|
|
191
|
-
count++;
|
|
192
|
-
position += searchString.length;
|
|
193
|
-
}
|
|
194
|
-
return count;
|
|
195
|
-
}
|
|
196
|
-
function replaceString(content, oldString, newString, replaceAll = false) {
|
|
197
|
-
const count = countOccurrences(content, oldString);
|
|
198
|
-
if (count === 0) {
|
|
199
|
-
throw new StringNotFoundError(oldString);
|
|
200
|
-
}
|
|
201
|
-
if (!replaceAll && count > 1) {
|
|
202
|
-
throw new StringNotUniqueError(oldString, count);
|
|
203
|
-
}
|
|
204
|
-
if (replaceAll) {
|
|
205
|
-
const result = content.split(oldString).join(newString);
|
|
206
|
-
return { content: result, replacements: count };
|
|
151
|
+
// src/workspace/lifecycle.ts
|
|
152
|
+
async function callLifecycle(provider, method) {
|
|
153
|
+
const wrapped = `_${method}`;
|
|
154
|
+
const wrappedFn = provider[wrapped];
|
|
155
|
+
if (typeof wrappedFn === "function") {
|
|
156
|
+
await wrappedFn.call(provider);
|
|
207
157
|
} else {
|
|
208
|
-
const
|
|
209
|
-
|
|
158
|
+
const plainFn = provider[method];
|
|
159
|
+
if (typeof plainFn === "function") {
|
|
160
|
+
await plainFn.call(provider);
|
|
161
|
+
}
|
|
210
162
|
}
|
|
211
163
|
}
|
|
212
|
-
var StringNotFoundError = class extends Error {
|
|
213
|
-
constructor(searchString) {
|
|
214
|
-
super(`The specified text was not found. Make sure you use the exact text from the file.`);
|
|
215
|
-
this.searchString = searchString;
|
|
216
|
-
this.name = "StringNotFoundError";
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
var StringNotUniqueError = class extends Error {
|
|
220
|
-
constructor(searchString, occurrences) {
|
|
221
|
-
super(
|
|
222
|
-
`The specified text appears ${occurrences} times. Provide more surrounding context to make the match unique, or use replace_all to replace all occurrences.`
|
|
223
|
-
);
|
|
224
|
-
this.searchString = searchString;
|
|
225
|
-
this.occurrences = occurrences;
|
|
226
|
-
this.name = "StringNotUniqueError";
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
164
|
|
|
230
|
-
// src/workspace/
|
|
231
|
-
var
|
|
232
|
-
|
|
233
|
-
"
|
|
234
|
-
"
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"the",
|
|
253
|
-
"to",
|
|
254
|
-
"was",
|
|
255
|
-
"were",
|
|
256
|
-
"will",
|
|
257
|
-
"with"
|
|
258
|
-
]);
|
|
259
|
-
var DEFAULT_TOKENIZE_OPTIONS = {
|
|
260
|
-
lowercase: true,
|
|
261
|
-
removePunctuation: true,
|
|
262
|
-
minLength: 2,
|
|
263
|
-
stopwords: DEFAULT_STOPWORDS,
|
|
264
|
-
splitPattern: /\s+/
|
|
265
|
-
};
|
|
266
|
-
function tokenize(text, options = {}) {
|
|
267
|
-
const opts = { ...DEFAULT_TOKENIZE_OPTIONS, ...options };
|
|
268
|
-
let processed = text;
|
|
269
|
-
if (opts.lowercase) {
|
|
270
|
-
processed = processed.toLowerCase();
|
|
271
|
-
}
|
|
272
|
-
if (opts.removePunctuation) {
|
|
273
|
-
processed = processed.replace(/[^\w\s]/g, " ");
|
|
274
|
-
}
|
|
275
|
-
const tokens = processed.split(opts.splitPattern).filter((token) => {
|
|
276
|
-
if (token.length < opts.minLength) {
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
if (opts.stopwords?.has(token)) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
return true;
|
|
283
|
-
});
|
|
284
|
-
return tokens;
|
|
285
|
-
}
|
|
286
|
-
function findLineRange(content, queryTerms, options = {}) {
|
|
287
|
-
if (queryTerms.length === 0) return void 0;
|
|
288
|
-
const lines = content.split("\n");
|
|
289
|
-
const defaultOpts = { lowercase: true, removePunctuation: true, minLength: 2 };
|
|
290
|
-
const opts = { ...defaultOpts, ...options };
|
|
291
|
-
const normalizedTerms = new Set(queryTerms.map((t) => opts.lowercase ? t.toLowerCase() : t));
|
|
292
|
-
let firstMatchLine;
|
|
293
|
-
let lastMatchLine;
|
|
294
|
-
for (let i = 0; i < lines.length; i++) {
|
|
295
|
-
const lineTokens = tokenize(lines[i], options);
|
|
296
|
-
for (const token of lineTokens) {
|
|
297
|
-
if (normalizedTerms.has(token)) {
|
|
298
|
-
const lineNum = i + 1;
|
|
299
|
-
if (firstMatchLine === void 0) {
|
|
300
|
-
firstMatchLine = lineNum;
|
|
165
|
+
// src/workspace/filesystem/composite-filesystem.ts
|
|
166
|
+
var CompositeFilesystem = class {
|
|
167
|
+
id;
|
|
168
|
+
name = "CompositeFilesystem";
|
|
169
|
+
provider = "composite";
|
|
170
|
+
status = "ready";
|
|
171
|
+
_mounts;
|
|
172
|
+
constructor(config) {
|
|
173
|
+
this.id = `cfs-${Date.now().toString(36)}`;
|
|
174
|
+
this._mounts = /* @__PURE__ */ new Map();
|
|
175
|
+
for (const [path4, fs5] of Object.entries(config.mounts)) {
|
|
176
|
+
const normalized = this.normalizePath(path4);
|
|
177
|
+
this._mounts.set(normalized, fs5);
|
|
178
|
+
}
|
|
179
|
+
if (this._mounts.size === 0) {
|
|
180
|
+
throw new Error("CompositeFilesystem requires at least one mount");
|
|
181
|
+
}
|
|
182
|
+
const mountPaths = [...this._mounts.keys()];
|
|
183
|
+
for (const a of mountPaths) {
|
|
184
|
+
for (const b of mountPaths) {
|
|
185
|
+
if (a !== b && b.startsWith(a + "/")) {
|
|
186
|
+
throw new Error(`Nested mount paths are not supported: "${b}" is nested under "${a}"`);
|
|
301
187
|
}
|
|
302
|
-
lastMatchLine = lineNum;
|
|
303
|
-
break;
|
|
304
188
|
}
|
|
305
189
|
}
|
|
306
190
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
function computeTermFrequencies(tokens) {
|
|
313
|
-
const frequencies = /* @__PURE__ */ new Map();
|
|
314
|
-
for (const token of tokens) {
|
|
315
|
-
frequencies.set(token, (frequencies.get(token) || 0) + 1);
|
|
191
|
+
/**
|
|
192
|
+
* Get all mount paths.
|
|
193
|
+
*/
|
|
194
|
+
get mountPaths() {
|
|
195
|
+
return Array.from(this._mounts.keys());
|
|
316
196
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
/** BM25 b parameter */
|
|
323
|
-
b;
|
|
324
|
-
/** Documents in the index */
|
|
325
|
-
#documents = /* @__PURE__ */ new Map();
|
|
326
|
-
/** Inverted index: term -> document IDs containing the term */
|
|
327
|
-
#invertedIndex = /* @__PURE__ */ new Map();
|
|
328
|
-
/** Document frequency: term -> number of documents containing the term */
|
|
329
|
-
#documentFrequency = /* @__PURE__ */ new Map();
|
|
330
|
-
/** Average document length */
|
|
331
|
-
#avgDocLength = 0;
|
|
332
|
-
/** Total number of documents */
|
|
333
|
-
#docCount = 0;
|
|
334
|
-
/** Tokenization options */
|
|
335
|
-
#tokenizeOptions;
|
|
336
|
-
constructor(config = {}, tokenizeOptions = {}) {
|
|
337
|
-
this.k1 = config.k1 ?? 1.5;
|
|
338
|
-
this.b = config.b ?? 0.75;
|
|
339
|
-
this.#tokenizeOptions = tokenizeOptions;
|
|
197
|
+
/**
|
|
198
|
+
* Get the mounts map.
|
|
199
|
+
*/
|
|
200
|
+
get mounts() {
|
|
201
|
+
return this._mounts;
|
|
340
202
|
}
|
|
341
203
|
/**
|
|
342
|
-
*
|
|
204
|
+
* Get the underlying filesystem for a given path.
|
|
205
|
+
* Returns undefined if the path doesn't resolve to any mount.
|
|
343
206
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
const tokens = tokenize(content, this.#tokenizeOptions);
|
|
349
|
-
const termFrequencies = computeTermFrequencies(tokens);
|
|
350
|
-
const doc = {
|
|
351
|
-
id,
|
|
352
|
-
content,
|
|
353
|
-
tokens,
|
|
354
|
-
termFrequencies,
|
|
355
|
-
length: tokens.length,
|
|
356
|
-
metadata
|
|
357
|
-
};
|
|
358
|
-
this.#documents.set(id, doc);
|
|
359
|
-
this.#docCount++;
|
|
360
|
-
for (const term of termFrequencies.keys()) {
|
|
361
|
-
if (!this.#invertedIndex.has(term)) {
|
|
362
|
-
this.#invertedIndex.set(term, /* @__PURE__ */ new Set());
|
|
363
|
-
}
|
|
364
|
-
this.#invertedIndex.get(term).add(id);
|
|
365
|
-
this.#documentFrequency.set(term, (this.#documentFrequency.get(term) || 0) + 1);
|
|
366
|
-
}
|
|
367
|
-
this.#updateAvgDocLength();
|
|
207
|
+
getFilesystemForPath(path4) {
|
|
208
|
+
const resolved = this.resolveMount(path4);
|
|
209
|
+
return resolved?.fs;
|
|
368
210
|
}
|
|
369
211
|
/**
|
|
370
|
-
*
|
|
212
|
+
* Get the mount path for a given path.
|
|
213
|
+
* Returns undefined if the path doesn't resolve to any mount.
|
|
371
214
|
*/
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
215
|
+
getMountPathForPath(path4) {
|
|
216
|
+
const resolved = this.resolveMount(path4);
|
|
217
|
+
return resolved?.mountPath;
|
|
218
|
+
}
|
|
219
|
+
normalizePath(path4) {
|
|
220
|
+
if (!path4 || path4 === "/") return "/";
|
|
221
|
+
let n = path4.startsWith("/") ? path4 : `/${path4}`;
|
|
222
|
+
if (n.length > 1 && n.endsWith("/")) n = n.slice(0, -1);
|
|
223
|
+
return n;
|
|
224
|
+
}
|
|
225
|
+
resolveMount(path4) {
|
|
226
|
+
const normalized = this.normalizePath(path4);
|
|
227
|
+
let best = null;
|
|
228
|
+
for (const [mountPath, fs5] of this._mounts) {
|
|
229
|
+
if (normalized === mountPath || normalized.startsWith(mountPath + "/")) {
|
|
230
|
+
if (!best || mountPath.length > best.mountPath.length) {
|
|
231
|
+
best = { mountPath, fs: fs5 };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
376
234
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
235
|
+
if (!best) return null;
|
|
236
|
+
let fsPath = normalized.slice(best.mountPath.length);
|
|
237
|
+
if (!fsPath) fsPath = "/";
|
|
238
|
+
if (!fsPath.startsWith("/")) fsPath = "/" + fsPath;
|
|
239
|
+
return { fs: best.fs, fsPath, mountPath: best.mountPath };
|
|
240
|
+
}
|
|
241
|
+
getVirtualEntries(path4) {
|
|
242
|
+
const normalized = this.normalizePath(path4);
|
|
243
|
+
if (this.resolveMount(normalized)) return null;
|
|
244
|
+
const entriesMap = /* @__PURE__ */ new Map();
|
|
245
|
+
for (const [mountPath, fs5] of this._mounts.entries()) {
|
|
246
|
+
const isUnder = normalized === "/" ? mountPath.startsWith("/") : mountPath.startsWith(normalized + "/");
|
|
247
|
+
if (isUnder) {
|
|
248
|
+
const remaining = normalized === "/" ? mountPath.slice(1) : mountPath.slice(normalized.length + 1);
|
|
249
|
+
const next = remaining.split("/")[0];
|
|
250
|
+
if (next && !entriesMap.has(next)) {
|
|
251
|
+
const isDirectMount = remaining === next;
|
|
252
|
+
const entry = { name: next, type: "directory" };
|
|
253
|
+
if (isDirectMount) {
|
|
254
|
+
entry.mount = {
|
|
255
|
+
provider: fs5.provider,
|
|
256
|
+
icon: fs5.icon,
|
|
257
|
+
displayName: fs5.displayName,
|
|
258
|
+
description: fs5.description,
|
|
259
|
+
status: fs5.status,
|
|
260
|
+
error: fs5.error
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
entriesMap.set(next, entry);
|
|
386
264
|
}
|
|
387
265
|
}
|
|
388
266
|
}
|
|
389
|
-
|
|
390
|
-
this.#docCount--;
|
|
391
|
-
this.#updateAvgDocLength();
|
|
392
|
-
return true;
|
|
267
|
+
return entriesMap.size > 0 ? Array.from(entriesMap.values()) : null;
|
|
393
268
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
this.#docCount = 0;
|
|
402
|
-
this.#avgDocLength = 0;
|
|
269
|
+
isVirtualPath(path4) {
|
|
270
|
+
const normalized = this.normalizePath(path4);
|
|
271
|
+
if (normalized === "/" && !this._mounts.has("/")) return true;
|
|
272
|
+
for (const mountPath of this._mounts.keys()) {
|
|
273
|
+
if (mountPath.startsWith(normalized + "/")) return true;
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
403
276
|
}
|
|
404
277
|
/**
|
|
405
|
-
*
|
|
278
|
+
* Assert that a filesystem is writable (not read-only).
|
|
279
|
+
* @throws {PermissionError} if the filesystem is read-only
|
|
406
280
|
*/
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
return [];
|
|
281
|
+
assertWritable(fs5, path4, operation) {
|
|
282
|
+
if (fs5.readOnly) {
|
|
283
|
+
throw new PermissionError(path4, `${operation} (filesystem is read-only)`);
|
|
411
284
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
scores.set(docId, (scores.get(docId) || 0) + termScore);
|
|
285
|
+
}
|
|
286
|
+
// ===========================================================================
|
|
287
|
+
// WorkspaceFilesystem Implementation
|
|
288
|
+
// ===========================================================================
|
|
289
|
+
async init() {
|
|
290
|
+
this.status = "initializing";
|
|
291
|
+
for (const [mountPath, fs5] of this._mounts.entries()) {
|
|
292
|
+
try {
|
|
293
|
+
await callLifecycle(fs5, "init");
|
|
294
|
+
} catch (e) {
|
|
295
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
296
|
+
console.warn(`[CompositeFilesystem] Mount "${mountPath}" failed to initialize: ${message}`);
|
|
425
297
|
}
|
|
426
298
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
299
|
+
this.status = "ready";
|
|
300
|
+
}
|
|
301
|
+
async destroy() {
|
|
302
|
+
this.status = "destroying";
|
|
303
|
+
const errors = [];
|
|
304
|
+
for (const fs5 of this._mounts.values()) {
|
|
305
|
+
try {
|
|
306
|
+
await callLifecycle(fs5, "destroy");
|
|
307
|
+
} catch (e) {
|
|
308
|
+
errors.push(e instanceof Error ? e : new Error(String(e)));
|
|
437
309
|
}
|
|
438
310
|
}
|
|
439
|
-
|
|
440
|
-
|
|
311
|
+
if (errors.length > 0) {
|
|
312
|
+
this.status = "error";
|
|
313
|
+
throw new AggregateError(errors, "Some filesystems failed to destroy");
|
|
314
|
+
}
|
|
315
|
+
this.status = "destroyed";
|
|
441
316
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
return this.#documents.get(id);
|
|
317
|
+
async readFile(path4, options) {
|
|
318
|
+
const r = this.resolveMount(path4);
|
|
319
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
320
|
+
return r.fs.readFile(r.fsPath, options);
|
|
447
321
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
return
|
|
322
|
+
async writeFile(path4, content, options) {
|
|
323
|
+
const r = this.resolveMount(path4);
|
|
324
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
325
|
+
this.assertWritable(r.fs, path4, "writeFile");
|
|
326
|
+
return r.fs.writeFile(r.fsPath, content, options);
|
|
453
327
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return
|
|
328
|
+
async appendFile(path4, content) {
|
|
329
|
+
const r = this.resolveMount(path4);
|
|
330
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
331
|
+
this.assertWritable(r.fs, path4, "appendFile");
|
|
332
|
+
return r.fs.appendFile(r.fsPath, content);
|
|
459
333
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return
|
|
334
|
+
async deleteFile(path4, options) {
|
|
335
|
+
const r = this.resolveMount(path4);
|
|
336
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
337
|
+
this.assertWritable(r.fs, path4, "deleteFile");
|
|
338
|
+
return r.fs.deleteFile(r.fsPath, options);
|
|
465
339
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
content: doc.content,
|
|
475
|
-
tokens: doc.tokens,
|
|
476
|
-
termFrequencies: Object.fromEntries(doc.termFrequencies),
|
|
477
|
-
length: doc.length,
|
|
478
|
-
metadata: doc.metadata
|
|
479
|
-
});
|
|
340
|
+
async copyFile(src, dest, options) {
|
|
341
|
+
const srcR = this.resolveMount(src);
|
|
342
|
+
const destR = this.resolveMount(dest);
|
|
343
|
+
if (!srcR) throw new Error(`No mount for source: ${src}`);
|
|
344
|
+
if (!destR) throw new Error(`No mount for dest: ${dest}`);
|
|
345
|
+
this.assertWritable(destR.fs, dest, "copyFile");
|
|
346
|
+
if (srcR.mountPath === destR.mountPath) {
|
|
347
|
+
return srcR.fs.copyFile(srcR.fsPath, destR.fsPath, options);
|
|
480
348
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
b: this.b,
|
|
484
|
-
documents,
|
|
485
|
-
avgDocLength: this.#avgDocLength
|
|
486
|
-
};
|
|
349
|
+
const content = await srcR.fs.readFile(srcR.fsPath);
|
|
350
|
+
await destR.fs.writeFile(destR.fsPath, content, { overwrite: options?.overwrite });
|
|
487
351
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
352
|
+
async moveFile(src, dest, options) {
|
|
353
|
+
const srcR = this.resolveMount(src);
|
|
354
|
+
const destR = this.resolveMount(dest);
|
|
355
|
+
if (!srcR) throw new Error(`No mount for source: ${src}`);
|
|
356
|
+
if (!destR) throw new Error(`No mount for dest: ${dest}`);
|
|
357
|
+
this.assertWritable(destR.fs, dest, "moveFile");
|
|
358
|
+
this.assertWritable(srcR.fs, src, "moveFile");
|
|
359
|
+
if (srcR.mountPath === destR.mountPath) {
|
|
360
|
+
return srcR.fs.moveFile(srcR.fsPath, destR.fsPath, options);
|
|
361
|
+
}
|
|
362
|
+
await this.copyFile(src, dest, options);
|
|
363
|
+
await srcR.fs.deleteFile(srcR.fsPath);
|
|
364
|
+
}
|
|
365
|
+
async readdir(path4, options) {
|
|
366
|
+
const virtual = this.getVirtualEntries(path4);
|
|
367
|
+
if (virtual) return virtual;
|
|
368
|
+
const r = this.resolveMount(path4);
|
|
369
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
370
|
+
return r.fs.readdir(r.fsPath, options);
|
|
371
|
+
}
|
|
372
|
+
async mkdir(path4, options) {
|
|
373
|
+
const r = this.resolveMount(path4);
|
|
374
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
375
|
+
this.assertWritable(r.fs, path4, "mkdir");
|
|
376
|
+
return r.fs.mkdir(r.fsPath, options);
|
|
377
|
+
}
|
|
378
|
+
async rmdir(path4, options) {
|
|
379
|
+
const r = this.resolveMount(path4);
|
|
380
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
381
|
+
this.assertWritable(r.fs, path4, "rmdir");
|
|
382
|
+
return r.fs.rmdir(r.fsPath, options);
|
|
383
|
+
}
|
|
384
|
+
async exists(path4) {
|
|
385
|
+
if (this.isVirtualPath(path4)) return true;
|
|
386
|
+
const r = this.resolveMount(path4);
|
|
387
|
+
if (!r) return false;
|
|
388
|
+
if (r.fsPath === "/") return true;
|
|
389
|
+
return r.fs.exists(r.fsPath);
|
|
390
|
+
}
|
|
391
|
+
async stat(path4) {
|
|
392
|
+
const normalized = this.normalizePath(path4);
|
|
393
|
+
if (this.isVirtualPath(path4)) {
|
|
394
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
395
|
+
const now = /* @__PURE__ */ new Date();
|
|
396
|
+
return {
|
|
397
|
+
name: parts[parts.length - 1] || "",
|
|
398
|
+
path: normalized,
|
|
399
|
+
type: "directory",
|
|
400
|
+
size: 0,
|
|
401
|
+
createdAt: now,
|
|
402
|
+
modifiedAt: now
|
|
502
403
|
};
|
|
503
|
-
index.#documents.set(doc.id, document);
|
|
504
|
-
index.#docCount++;
|
|
505
|
-
for (const term of termFrequencies.keys()) {
|
|
506
|
-
if (!index.#invertedIndex.has(term)) {
|
|
507
|
-
index.#invertedIndex.set(term, /* @__PURE__ */ new Set());
|
|
508
|
-
}
|
|
509
|
-
index.#invertedIndex.get(term).add(doc.id);
|
|
510
|
-
index.#documentFrequency.set(term, (index.#documentFrequency.get(term) || 0) + 1);
|
|
511
|
-
}
|
|
512
404
|
}
|
|
513
|
-
|
|
514
|
-
|
|
405
|
+
const r = this.resolveMount(path4);
|
|
406
|
+
if (!r) throw new Error(`No mount for path: ${path4}`);
|
|
407
|
+
if (r.fsPath === "/") {
|
|
408
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
409
|
+
const now = /* @__PURE__ */ new Date();
|
|
410
|
+
return {
|
|
411
|
+
name: parts[parts.length - 1] || "",
|
|
412
|
+
path: normalized,
|
|
413
|
+
type: "directory",
|
|
414
|
+
size: 0,
|
|
415
|
+
createdAt: now,
|
|
416
|
+
modifiedAt: now
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return r.fs.stat(r.fsPath);
|
|
420
|
+
}
|
|
421
|
+
async isFile(path4) {
|
|
422
|
+
if (this.isVirtualPath(path4)) return false;
|
|
423
|
+
const r = this.resolveMount(path4);
|
|
424
|
+
if (!r) return false;
|
|
425
|
+
try {
|
|
426
|
+
const stat3 = await r.fs.stat(r.fsPath);
|
|
427
|
+
return stat3.type === "file";
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
async isDirectory(path4) {
|
|
433
|
+
if (this.isVirtualPath(path4)) return true;
|
|
434
|
+
const r = this.resolveMount(path4);
|
|
435
|
+
if (!r) return false;
|
|
436
|
+
if (r.fsPath === "/") return true;
|
|
437
|
+
try {
|
|
438
|
+
const stat3 = await r.fs.stat(r.fsPath);
|
|
439
|
+
return stat3.type === "directory";
|
|
440
|
+
} catch {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
515
443
|
}
|
|
516
444
|
/**
|
|
517
|
-
*
|
|
445
|
+
* Get instructions describing the mounted filesystems.
|
|
446
|
+
* Used by agents to understand available storage locations.
|
|
518
447
|
*/
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
448
|
+
getInstructions() {
|
|
449
|
+
const mountDescriptions = Array.from(this._mounts.entries()).map(([mountPath, fs5]) => {
|
|
450
|
+
const name = fs5.displayName || fs5.provider;
|
|
451
|
+
const access2 = fs5.readOnly ? "(read-only)" : "(read-write)";
|
|
452
|
+
return `- ${mountPath}: ${name} ${access2}`;
|
|
453
|
+
}).join("\n");
|
|
454
|
+
return `Mounted filesystems:
|
|
455
|
+
${mountDescriptions}
|
|
456
|
+
Files written via workspace tools are accessible at the same paths in sandbox commands.`;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// src/workspace/filesystem/mastra-filesystem.ts
|
|
461
|
+
var MastraFilesystem = class extends chunkRO47SMI7_cjs.MastraBase {
|
|
462
|
+
/** Error message when status is 'error' */
|
|
463
|
+
error;
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
465
|
+
// Lifecycle Promise Tracking (prevents race conditions)
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
/** Promise for _init() to prevent race conditions from concurrent calls */
|
|
468
|
+
_initPromise;
|
|
469
|
+
/** Promise for _destroy() to prevent race conditions from concurrent calls */
|
|
470
|
+
_destroyPromise;
|
|
471
|
+
constructor(options) {
|
|
472
|
+
super({ name: options.name, component: chunk7XAECHYL_cjs.RegisteredLogger.WORKSPACE });
|
|
473
|
+
}
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
// Lifecycle Wrappers (race-condition-safe)
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
477
|
+
/**
|
|
478
|
+
* Initialize the filesystem (wrapper with status management and race-condition safety).
|
|
479
|
+
*
|
|
480
|
+
* This method is race-condition-safe - concurrent calls will return the same promise.
|
|
481
|
+
* Handles status management automatically.
|
|
482
|
+
*
|
|
483
|
+
* Subclasses override `init()` to provide their initialization logic.
|
|
484
|
+
*/
|
|
485
|
+
async _init() {
|
|
486
|
+
if (this.status === "ready") {
|
|
522
487
|
return;
|
|
523
488
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
489
|
+
if (this._destroyPromise) {
|
|
490
|
+
try {
|
|
491
|
+
await this._destroyPromise;
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (this._initPromise) {
|
|
496
|
+
return this._initPromise;
|
|
497
|
+
}
|
|
498
|
+
this._initPromise = this._executeInit();
|
|
499
|
+
try {
|
|
500
|
+
await this._initPromise;
|
|
501
|
+
} finally {
|
|
502
|
+
this._initPromise = void 0;
|
|
527
503
|
}
|
|
528
|
-
this.#avgDocLength = totalLength / this.#docCount;
|
|
529
504
|
}
|
|
530
505
|
/**
|
|
531
|
-
*
|
|
506
|
+
* Internal init execution - handles status.
|
|
532
507
|
*/
|
|
533
|
-
|
|
534
|
-
|
|
508
|
+
async _executeInit() {
|
|
509
|
+
this.status = "initializing";
|
|
510
|
+
this.error = void 0;
|
|
511
|
+
try {
|
|
512
|
+
await this.init();
|
|
513
|
+
this.status = "ready";
|
|
514
|
+
} catch (error) {
|
|
515
|
+
this.status = "error";
|
|
516
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
517
|
+
this.logger.error("Failed to initialize filesystem", { error, id: this.id });
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
535
520
|
}
|
|
536
521
|
/**
|
|
537
|
-
*
|
|
522
|
+
* Override this method to implement filesystem initialization logic.
|
|
523
|
+
*
|
|
524
|
+
* Called by `_init()` after status is set to 'initializing'.
|
|
525
|
+
* Status will be set to 'ready' on success, 'error' on failure.
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```typescript
|
|
529
|
+
* async init(): Promise<void> {
|
|
530
|
+
* this._client = new StorageClient({ ... });
|
|
531
|
+
* await this._client.connect();
|
|
532
|
+
* }
|
|
533
|
+
* ```
|
|
538
534
|
*/
|
|
539
|
-
|
|
540
|
-
const numerator = tf * (this.k1 + 1);
|
|
541
|
-
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.#avgDocLength));
|
|
542
|
-
return idf * (numerator / denominator);
|
|
535
|
+
async init() {
|
|
543
536
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
this
|
|
563
|
-
this.#bm25Index = new BM25Index(config.bm25.bm25, this.#tokenizeOptions);
|
|
537
|
+
/**
|
|
538
|
+
* Ensure the filesystem is ready.
|
|
539
|
+
*
|
|
540
|
+
* Calls `_init()` if status is not 'ready'. Useful for lazy initialization
|
|
541
|
+
* where operations should automatically initialize the filesystem if needed.
|
|
542
|
+
*
|
|
543
|
+
* @throws {FilesystemNotReadyError} if the filesystem fails to reach 'ready' status
|
|
544
|
+
*
|
|
545
|
+
* @example
|
|
546
|
+
* ```typescript
|
|
547
|
+
* async readFile(path: string): Promise<string | Buffer> {
|
|
548
|
+
* await this.ensureReady();
|
|
549
|
+
* // Now safe to use the filesystem
|
|
550
|
+
* }
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
async ensureReady() {
|
|
554
|
+
if (this.status !== "ready") {
|
|
555
|
+
await this._init();
|
|
564
556
|
}
|
|
565
|
-
if (
|
|
566
|
-
|
|
557
|
+
if (this.status !== "ready") {
|
|
558
|
+
throw new FilesystemNotReadyError(this.id);
|
|
567
559
|
}
|
|
568
|
-
this.#lazyVectorIndex = config.lazyVectorIndex ?? false;
|
|
569
560
|
}
|
|
570
|
-
// ===========================================================================
|
|
571
|
-
// Public API
|
|
572
|
-
// ===========================================================================
|
|
573
561
|
/**
|
|
574
|
-
*
|
|
562
|
+
* Destroy the filesystem and clean up all resources (wrapper with status management).
|
|
563
|
+
*
|
|
564
|
+
* This method is race-condition-safe - concurrent calls will return the same promise.
|
|
565
|
+
* Handles status management.
|
|
566
|
+
*
|
|
567
|
+
* Subclasses override `destroy()` to provide their destroy logic.
|
|
575
568
|
*/
|
|
576
|
-
async
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
};
|
|
580
|
-
if (doc.startLineOffset !== void 0) {
|
|
581
|
-
metadata._startLineOffset = doc.startLineOffset;
|
|
569
|
+
async _destroy() {
|
|
570
|
+
if (this.status === "destroyed") {
|
|
571
|
+
return;
|
|
582
572
|
}
|
|
583
|
-
if (this
|
|
584
|
-
this
|
|
573
|
+
if (this.status === "pending") {
|
|
574
|
+
this.status = "destroyed";
|
|
575
|
+
return;
|
|
585
576
|
}
|
|
586
|
-
if (this
|
|
587
|
-
|
|
588
|
-
if (this.#lazyVectorIndex) {
|
|
589
|
-
this.#pendingVectorDocs.push(docWithMergedMetadata);
|
|
590
|
-
this.#vectorIndexBuilt = false;
|
|
591
|
-
} else {
|
|
592
|
-
await this.#indexVector(docWithMergedMetadata);
|
|
593
|
-
}
|
|
577
|
+
if (this._destroyPromise) {
|
|
578
|
+
return this._destroyPromise;
|
|
594
579
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
for (const doc of docs) {
|
|
601
|
-
await this.index(doc);
|
|
580
|
+
this._destroyPromise = this._executeDestroy();
|
|
581
|
+
try {
|
|
582
|
+
await this._destroyPromise;
|
|
583
|
+
} finally {
|
|
584
|
+
this._destroyPromise = void 0;
|
|
602
585
|
}
|
|
603
586
|
}
|
|
604
587
|
/**
|
|
605
|
-
*
|
|
588
|
+
* Internal destroy execution - handles status.
|
|
606
589
|
*/
|
|
607
|
-
async
|
|
608
|
-
if (this
|
|
609
|
-
this.#bm25Index.remove(id);
|
|
610
|
-
}
|
|
611
|
-
if (this.#vectorConfig) {
|
|
590
|
+
async _executeDestroy() {
|
|
591
|
+
if (this._initPromise) {
|
|
612
592
|
try {
|
|
613
|
-
await this
|
|
614
|
-
indexName: this.#vectorConfig.indexName,
|
|
615
|
-
id
|
|
616
|
-
});
|
|
593
|
+
await this._initPromise;
|
|
617
594
|
} catch {
|
|
618
595
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
596
|
+
}
|
|
597
|
+
this.status = "destroying";
|
|
598
|
+
try {
|
|
599
|
+
await this.destroy();
|
|
600
|
+
this.status = "destroyed";
|
|
601
|
+
} catch (error) {
|
|
602
|
+
this.status = "error";
|
|
603
|
+
this.logger.error("Failed to destroy filesystem", { error, id: this.id });
|
|
604
|
+
throw error;
|
|
622
605
|
}
|
|
623
606
|
}
|
|
624
607
|
/**
|
|
625
|
-
*
|
|
608
|
+
* Override this method to implement filesystem destroy logic.
|
|
609
|
+
*
|
|
610
|
+
* Called by `_destroy()` after status is set to 'destroying'.
|
|
611
|
+
* Status will be set to 'destroyed' on success, 'error' on failure.
|
|
626
612
|
*/
|
|
627
|
-
|
|
628
|
-
if (this.#bm25Index) {
|
|
629
|
-
this.#bm25Index.clear();
|
|
630
|
-
}
|
|
631
|
-
this.#pendingVectorDocs = [];
|
|
632
|
-
this.#vectorIndexBuilt = false;
|
|
613
|
+
async destroy() {
|
|
633
614
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
615
|
+
};
|
|
616
|
+
function isEnoentError(error) {
|
|
617
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
618
|
+
}
|
|
619
|
+
function isEexistError(error) {
|
|
620
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "EEXIST";
|
|
621
|
+
}
|
|
622
|
+
var MIME_TYPES = {
|
|
623
|
+
// Text
|
|
624
|
+
txt: "text/plain",
|
|
625
|
+
html: "text/html",
|
|
626
|
+
htm: "text/html",
|
|
627
|
+
css: "text/css",
|
|
628
|
+
csv: "text/csv",
|
|
629
|
+
md: "text/markdown",
|
|
630
|
+
// Code
|
|
631
|
+
js: "application/javascript",
|
|
632
|
+
mjs: "application/javascript",
|
|
633
|
+
ts: "application/typescript",
|
|
634
|
+
tsx: "application/typescript",
|
|
635
|
+
jsx: "application/javascript",
|
|
636
|
+
json: "application/json",
|
|
637
|
+
xml: "application/xml",
|
|
638
|
+
yaml: "text/yaml",
|
|
639
|
+
yml: "text/yaml",
|
|
640
|
+
// Programming languages
|
|
641
|
+
py: "text/x-python",
|
|
642
|
+
rb: "text/x-ruby",
|
|
643
|
+
go: "text/x-go",
|
|
644
|
+
rs: "text/x-rust",
|
|
645
|
+
java: "text/x-java",
|
|
646
|
+
c: "text/x-c",
|
|
647
|
+
cpp: "text/x-c++",
|
|
648
|
+
h: "text/x-c",
|
|
649
|
+
hpp: "text/x-c++",
|
|
650
|
+
sh: "text/x-sh",
|
|
651
|
+
bash: "text/x-sh",
|
|
652
|
+
zsh: "text/x-sh",
|
|
653
|
+
// Config
|
|
654
|
+
toml: "text/toml",
|
|
655
|
+
ini: "text/plain",
|
|
656
|
+
env: "text/plain",
|
|
657
|
+
// Database/Query
|
|
658
|
+
sql: "text/x-sql",
|
|
659
|
+
graphql: "application/graphql",
|
|
660
|
+
gql: "application/graphql",
|
|
661
|
+
// Frameworks
|
|
662
|
+
vue: "text/x-vue",
|
|
663
|
+
// Images
|
|
664
|
+
png: "image/png",
|
|
665
|
+
jpg: "image/jpeg",
|
|
666
|
+
jpeg: "image/jpeg",
|
|
667
|
+
gif: "image/gif",
|
|
668
|
+
svg: "image/svg+xml",
|
|
669
|
+
webp: "image/webp",
|
|
670
|
+
ico: "image/x-icon",
|
|
671
|
+
// Documents
|
|
672
|
+
pdf: "application/pdf"
|
|
673
|
+
};
|
|
674
|
+
function getMimeType(filename) {
|
|
675
|
+
const ext = nodePath__namespace.extname(filename).slice(1).toLowerCase();
|
|
676
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
677
|
+
}
|
|
678
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
679
|
+
".md",
|
|
680
|
+
".txt",
|
|
681
|
+
".json",
|
|
682
|
+
".yaml",
|
|
683
|
+
".yml",
|
|
684
|
+
".js",
|
|
685
|
+
".mjs",
|
|
686
|
+
".ts",
|
|
687
|
+
".tsx",
|
|
688
|
+
".jsx",
|
|
689
|
+
".py",
|
|
690
|
+
".rb",
|
|
691
|
+
".go",
|
|
692
|
+
".rs",
|
|
693
|
+
".java",
|
|
694
|
+
".c",
|
|
695
|
+
".cpp",
|
|
696
|
+
".h",
|
|
697
|
+
".hpp",
|
|
698
|
+
".sh",
|
|
699
|
+
".bash",
|
|
700
|
+
".zsh",
|
|
701
|
+
".html",
|
|
702
|
+
".htm",
|
|
703
|
+
".css",
|
|
704
|
+
".xml",
|
|
705
|
+
".toml",
|
|
706
|
+
".ini",
|
|
707
|
+
".env",
|
|
708
|
+
".csv",
|
|
709
|
+
".sql",
|
|
710
|
+
".graphql",
|
|
711
|
+
".gql",
|
|
712
|
+
".vue",
|
|
713
|
+
".svg"
|
|
714
|
+
]);
|
|
715
|
+
function isTextFile(filename) {
|
|
716
|
+
const ext = nodePath__namespace.extname(filename).toLowerCase();
|
|
717
|
+
return TEXT_EXTENSIONS.has(ext);
|
|
718
|
+
}
|
|
719
|
+
async function fsExists(absolutePath) {
|
|
720
|
+
try {
|
|
721
|
+
await fs2__namespace.access(absolutePath);
|
|
722
|
+
return true;
|
|
723
|
+
} catch {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async function fsStat(absolutePath, userPath) {
|
|
728
|
+
try {
|
|
729
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
730
|
+
return {
|
|
731
|
+
name: nodePath__namespace.basename(absolutePath),
|
|
732
|
+
type: stats.isDirectory() ? "directory" : "file",
|
|
733
|
+
size: stats.size,
|
|
734
|
+
createdAt: stats.birthtime,
|
|
735
|
+
modifiedAt: stats.mtime,
|
|
736
|
+
mimeType: stats.isFile() ? getMimeType(absolutePath) : void 0
|
|
737
|
+
};
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (isEnoentError(error)) {
|
|
740
|
+
throw new FileNotFoundError(userPath);
|
|
645
741
|
}
|
|
646
|
-
|
|
742
|
+
throw error;
|
|
647
743
|
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/workspace/filesystem/local-filesystem.ts
|
|
747
|
+
var LocalFilesystem = class extends MastraFilesystem {
|
|
748
|
+
id;
|
|
749
|
+
name = "LocalFilesystem";
|
|
750
|
+
provider = "local";
|
|
751
|
+
readOnly;
|
|
752
|
+
status = "pending";
|
|
753
|
+
_basePath;
|
|
754
|
+
_contained;
|
|
648
755
|
/**
|
|
649
|
-
*
|
|
756
|
+
* The absolute base path on disk where files are stored.
|
|
757
|
+
* Useful for understanding how workspace paths map to disk paths.
|
|
650
758
|
*/
|
|
651
|
-
get
|
|
652
|
-
return
|
|
759
|
+
get basePath() {
|
|
760
|
+
return this._basePath;
|
|
653
761
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
762
|
+
constructor(options) {
|
|
763
|
+
super({ name: "LocalFilesystem" });
|
|
764
|
+
this.id = options.id ?? this.generateId();
|
|
765
|
+
this._basePath = nodePath__namespace.resolve(options.basePath);
|
|
766
|
+
this._contained = options.contained ?? true;
|
|
767
|
+
this.readOnly = options.readOnly;
|
|
659
768
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
*/
|
|
663
|
-
get canHybrid() {
|
|
664
|
-
return this.canBM25 && this.canVector;
|
|
769
|
+
generateId() {
|
|
770
|
+
return `local-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
665
771
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return this.#bm25Index;
|
|
772
|
+
toBuffer(content) {
|
|
773
|
+
if (Buffer.isBuffer(content)) return content;
|
|
774
|
+
if (content instanceof Uint8Array) return Buffer.from(content);
|
|
775
|
+
return Buffer.from(content, "utf-8");
|
|
671
776
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
if (requestedMode === "vector" && !this.canVector) {
|
|
681
|
-
throw new Error("Vector search requires vector configuration.");
|
|
682
|
-
}
|
|
683
|
-
if (requestedMode === "bm25" && !this.canBM25) {
|
|
684
|
-
throw new Error("BM25 search requires BM25 configuration.");
|
|
685
|
-
}
|
|
686
|
-
if (requestedMode === "hybrid" && !this.canHybrid) {
|
|
687
|
-
throw new Error("Hybrid search requires both vector and BM25 configuration.");
|
|
777
|
+
resolvePath(inputPath) {
|
|
778
|
+
const cleanedPath = inputPath.replace(/^\/+/, "");
|
|
779
|
+
const normalizedInput = nodePath__namespace.normalize(cleanedPath);
|
|
780
|
+
const absolutePath = nodePath__namespace.resolve(this._basePath, normalizedInput);
|
|
781
|
+
if (this._contained) {
|
|
782
|
+
const relative2 = nodePath__namespace.relative(this._basePath, absolutePath);
|
|
783
|
+
if (relative2.startsWith("..") || nodePath__namespace.isAbsolute(relative2)) {
|
|
784
|
+
throw new PermissionError(inputPath, "access");
|
|
688
785
|
}
|
|
689
|
-
return requestedMode;
|
|
690
|
-
}
|
|
691
|
-
if (this.canHybrid) {
|
|
692
|
-
return "hybrid";
|
|
693
|
-
}
|
|
694
|
-
if (this.canVector) {
|
|
695
|
-
return "vector";
|
|
696
786
|
}
|
|
697
|
-
|
|
698
|
-
return "bm25";
|
|
699
|
-
}
|
|
700
|
-
throw new Error("No search configuration available. Provide bm25 or vector config.");
|
|
787
|
+
return absolutePath;
|
|
701
788
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
*/
|
|
705
|
-
async #indexVector(doc) {
|
|
706
|
-
if (!this.#vectorConfig) return;
|
|
707
|
-
const { vectorStore, embedder, indexName } = this.#vectorConfig;
|
|
708
|
-
const embedding = await embedder(doc.content);
|
|
709
|
-
await vectorStore.upsert({
|
|
710
|
-
indexName,
|
|
711
|
-
vectors: [embedding],
|
|
712
|
-
metadata: [
|
|
713
|
-
{
|
|
714
|
-
id: doc.id,
|
|
715
|
-
text: doc.content,
|
|
716
|
-
...doc.metadata
|
|
717
|
-
}
|
|
718
|
-
],
|
|
719
|
-
ids: [doc.id]
|
|
720
|
-
});
|
|
789
|
+
toRelativePath(absolutePath) {
|
|
790
|
+
return "/" + nodePath__namespace.relative(this._basePath, absolutePath).replace(/\\/g, "/");
|
|
721
791
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
async #ensureVectorIndex() {
|
|
726
|
-
if (!this.#lazyVectorIndex || this.#vectorIndexBuilt || this.#pendingVectorDocs.length === 0) {
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
for (const doc of this.#pendingVectorDocs) {
|
|
730
|
-
await this.#indexVector(doc);
|
|
792
|
+
assertWritable(operation) {
|
|
793
|
+
if (this.readOnly) {
|
|
794
|
+
throw new WorkspaceReadOnlyError(operation);
|
|
731
795
|
}
|
|
732
|
-
this.#pendingVectorDocs = [];
|
|
733
|
-
this.#vectorIndexBuilt = true;
|
|
734
796
|
}
|
|
735
797
|
/**
|
|
736
|
-
*
|
|
737
|
-
|
|
738
|
-
#searchBM25(query, topK, minScore) {
|
|
739
|
-
if (!this.#bm25Index) {
|
|
740
|
-
throw new Error("BM25 search requires BM25 configuration.");
|
|
741
|
-
}
|
|
742
|
-
const results = this.#bm25Index.search(query, topK, minScore);
|
|
743
|
-
const queryTokens = tokenize(query, this.#tokenizeOptions);
|
|
744
|
-
return results.map((result) => {
|
|
745
|
-
const rawLineRange = findLineRange(result.content, queryTokens, this.#tokenizeOptions);
|
|
746
|
-
const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);
|
|
747
|
-
const { _startLineOffset, ...cleanMetadata } = result.metadata ?? {};
|
|
748
|
-
return {
|
|
749
|
-
id: result.id,
|
|
750
|
-
content: result.content,
|
|
751
|
-
score: result.score,
|
|
752
|
-
lineRange,
|
|
753
|
-
metadata: Object.keys(cleanMetadata).length > 0 ? cleanMetadata : void 0,
|
|
754
|
-
scoreDetails: { bm25: result.score }
|
|
755
|
-
};
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Vector semantic search
|
|
798
|
+
* Verify that the resolved path doesn't escape basePath via symlinks.
|
|
799
|
+
* Uses realpath to resolve symlinks and check the actual target.
|
|
760
800
|
*/
|
|
761
|
-
async
|
|
762
|
-
if (!this
|
|
763
|
-
|
|
801
|
+
async assertPathContained(absolutePath) {
|
|
802
|
+
if (!this._contained) return;
|
|
803
|
+
let baseReal;
|
|
804
|
+
try {
|
|
805
|
+
baseReal = await fs2__namespace.realpath(this._basePath);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
if (isEnoentError(error)) {
|
|
808
|
+
throw new DirectoryNotFoundError(this._basePath);
|
|
809
|
+
}
|
|
810
|
+
throw error;
|
|
764
811
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
812
|
+
let targetReal;
|
|
813
|
+
try {
|
|
814
|
+
targetReal = await fs2__namespace.realpath(absolutePath);
|
|
815
|
+
} catch (error) {
|
|
816
|
+
if (isEnoentError(error)) {
|
|
817
|
+
let parentPath = absolutePath;
|
|
818
|
+
while (true) {
|
|
819
|
+
const nextParent = nodePath__namespace.dirname(parentPath);
|
|
820
|
+
if (nextParent === parentPath) {
|
|
821
|
+
throw new DirectoryNotFoundError(absolutePath);
|
|
822
|
+
}
|
|
823
|
+
parentPath = nextParent;
|
|
824
|
+
try {
|
|
825
|
+
targetReal = await fs2__namespace.realpath(parentPath);
|
|
826
|
+
break;
|
|
827
|
+
} catch (parentError) {
|
|
828
|
+
if (!isEnoentError(parentError)) {
|
|
829
|
+
throw parentError;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
} else {
|
|
834
|
+
throw error;
|
|
779
835
|
}
|
|
780
|
-
const id = result.metadata?.id ?? result.id;
|
|
781
|
-
const content = result.metadata?.text ?? "";
|
|
782
|
-
const { id: _id, text: _text, _startLineOffset, ...restMetadata } = result.metadata ?? {};
|
|
783
|
-
const rawLineRange = findLineRange(content, queryTokens, this.#tokenizeOptions);
|
|
784
|
-
const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);
|
|
785
|
-
results.push({
|
|
786
|
-
id,
|
|
787
|
-
content,
|
|
788
|
-
score: result.score,
|
|
789
|
-
lineRange,
|
|
790
|
-
metadata: Object.keys(restMetadata).length > 0 ? restMetadata : void 0,
|
|
791
|
-
scoreDetails: { vector: result.score }
|
|
792
|
-
});
|
|
793
836
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
/**
|
|
797
|
-
* Hybrid search combining vector and BM25 scores
|
|
798
|
-
*/
|
|
799
|
-
async #searchHybrid(query, topK, minScore, vectorWeight = 0.5, filter) {
|
|
800
|
-
const expandedTopK = Math.min(topK * 2, 50);
|
|
801
|
-
const [vectorResults, bm25Results] = await Promise.all([
|
|
802
|
-
this.#searchVector(query, expandedTopK, void 0, filter),
|
|
803
|
-
Promise.resolve(this.#searchBM25(query, expandedTopK, void 0))
|
|
804
|
-
]);
|
|
805
|
-
const normalizedBM25 = this.#normalizeBM25Scores(bm25Results);
|
|
806
|
-
const bm25Map = /* @__PURE__ */ new Map();
|
|
807
|
-
for (const result of normalizedBM25) {
|
|
808
|
-
bm25Map.set(result.id, result);
|
|
837
|
+
if (targetReal !== baseReal && !targetReal.startsWith(baseReal + nodePath__namespace.sep)) {
|
|
838
|
+
throw new PermissionError(absolutePath, "access");
|
|
809
839
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
840
|
+
}
|
|
841
|
+
async readFile(inputPath, options) {
|
|
842
|
+
this.logger.debug("Reading file", { path: inputPath, encoding: options?.encoding });
|
|
843
|
+
await this.ensureReady();
|
|
844
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
845
|
+
await this.assertPathContained(absolutePath);
|
|
846
|
+
try {
|
|
847
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
848
|
+
if (stats.isDirectory()) {
|
|
849
|
+
throw new IsDirectoryError(inputPath);
|
|
850
|
+
}
|
|
851
|
+
if (options?.encoding) {
|
|
852
|
+
return await fs2__namespace.readFile(absolutePath, { encoding: options.encoding });
|
|
853
|
+
}
|
|
854
|
+
return await fs2__namespace.readFile(absolutePath);
|
|
855
|
+
} catch (error) {
|
|
856
|
+
if (error instanceof IsDirectoryError) throw error;
|
|
857
|
+
if (isEnoentError(error)) {
|
|
858
|
+
throw new FileNotFoundError(inputPath);
|
|
859
|
+
}
|
|
860
|
+
throw error;
|
|
813
861
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
metadata: baseResult.metadata,
|
|
830
|
-
scoreDetails: {
|
|
831
|
-
vector: vectorResult?.scoreDetails?.vector,
|
|
832
|
-
bm25: bm25Result?.scoreDetails?.bm25
|
|
862
|
+
}
|
|
863
|
+
async writeFile(inputPath, content, options) {
|
|
864
|
+
const contentSize = Buffer.isBuffer(content) ? content.length : content.length;
|
|
865
|
+
this.logger.debug("Writing file", { path: inputPath, size: contentSize, recursive: options?.recursive });
|
|
866
|
+
await this.ensureReady();
|
|
867
|
+
this.assertWritable("writeFile");
|
|
868
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
869
|
+
await this.assertPathContained(absolutePath);
|
|
870
|
+
if (options?.recursive === false) {
|
|
871
|
+
const dir = nodePath__namespace.dirname(absolutePath);
|
|
872
|
+
const parentPath = nodePath__namespace.dirname(inputPath);
|
|
873
|
+
try {
|
|
874
|
+
const stat3 = await fs2__namespace.stat(dir);
|
|
875
|
+
if (!stat3.isDirectory()) {
|
|
876
|
+
throw new NotDirectoryError(parentPath);
|
|
833
877
|
}
|
|
834
|
-
})
|
|
878
|
+
} catch (error) {
|
|
879
|
+
if (error instanceof NotDirectoryError) throw error;
|
|
880
|
+
if (isEnoentError(error)) {
|
|
881
|
+
throw new DirectoryNotFoundError(parentPath);
|
|
882
|
+
}
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
835
885
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
results = results.filter((r) => r.score >= minScore);
|
|
886
|
+
if (options?.recursive !== false) {
|
|
887
|
+
const dir = nodePath__namespace.dirname(absolutePath);
|
|
888
|
+
await fs2__namespace.mkdir(dir, { recursive: true });
|
|
840
889
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
const maxScore = Math.max(...scores);
|
|
850
|
-
const minScore = Math.min(...scores);
|
|
851
|
-
const range = maxScore - minScore;
|
|
852
|
-
if (range === 0) {
|
|
853
|
-
return results.map((r) => ({ ...r, score: 1 }));
|
|
890
|
+
const writeFlag = options?.overwrite === false ? "wx" : "w";
|
|
891
|
+
try {
|
|
892
|
+
await fs2__namespace.writeFile(absolutePath, this.toBuffer(content), { flag: writeFlag });
|
|
893
|
+
} catch (error) {
|
|
894
|
+
if (options?.overwrite === false && isEexistError(error)) {
|
|
895
|
+
throw new FileExistsError(inputPath);
|
|
896
|
+
}
|
|
897
|
+
throw error;
|
|
854
898
|
}
|
|
855
|
-
return results.map((r) => ({
|
|
856
|
-
...r,
|
|
857
|
-
score: ((r.scoreDetails?.bm25 ?? r.score) - minScore) / range
|
|
858
|
-
}));
|
|
859
899
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
900
|
+
async appendFile(inputPath, content) {
|
|
901
|
+
const contentSize = Buffer.isBuffer(content) ? content.length : content.length;
|
|
902
|
+
this.logger.debug("Appending to file", { path: inputPath, size: contentSize });
|
|
903
|
+
await this.ensureReady();
|
|
904
|
+
this.assertWritable("appendFile");
|
|
905
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
906
|
+
await this.assertPathContained(absolutePath);
|
|
907
|
+
const dir = nodePath__namespace.dirname(absolutePath);
|
|
908
|
+
await fs2__namespace.mkdir(dir, { recursive: true });
|
|
909
|
+
await fs2__namespace.appendFile(absolutePath, this.toBuffer(content));
|
|
910
|
+
}
|
|
911
|
+
async deleteFile(inputPath, options) {
|
|
912
|
+
this.logger.debug("Deleting file", { path: inputPath, force: options?.force });
|
|
913
|
+
await this.ensureReady();
|
|
914
|
+
this.assertWritable("deleteFile");
|
|
915
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
916
|
+
await this.assertPathContained(absolutePath);
|
|
917
|
+
try {
|
|
918
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
919
|
+
if (stats.isDirectory()) {
|
|
920
|
+
throw new IsDirectoryError(inputPath);
|
|
921
|
+
}
|
|
922
|
+
await fs2__namespace.unlink(absolutePath);
|
|
923
|
+
} catch (error) {
|
|
924
|
+
if (error instanceof IsDirectoryError) throw error;
|
|
925
|
+
if (isEnoentError(error)) {
|
|
926
|
+
if (!options?.force) {
|
|
927
|
+
throw new FileNotFoundError(inputPath);
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
throw error;
|
|
931
|
+
}
|
|
870
932
|
}
|
|
871
|
-
return {
|
|
872
|
-
start: lineRange.start + startLineOffset - 1,
|
|
873
|
-
end: lineRange.end + startLineOffset - 1
|
|
874
|
-
};
|
|
875
933
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
934
|
+
async copyFile(src, dest, options) {
|
|
935
|
+
this.logger.debug("Copying file", { src, dest, recursive: options?.recursive });
|
|
936
|
+
await this.ensureReady();
|
|
937
|
+
this.assertWritable("copyFile");
|
|
938
|
+
const srcPath = this.resolvePath(src);
|
|
939
|
+
const destPath = this.resolvePath(dest);
|
|
940
|
+
await this.assertPathContained(srcPath);
|
|
941
|
+
await this.assertPathContained(destPath);
|
|
942
|
+
try {
|
|
943
|
+
const stats = await fs2__namespace.stat(srcPath);
|
|
944
|
+
if (stats.isDirectory()) {
|
|
945
|
+
if (!options?.recursive) {
|
|
946
|
+
throw new IsDirectoryError(src);
|
|
947
|
+
}
|
|
948
|
+
await this.copyDirectory(srcPath, destPath, options);
|
|
949
|
+
} else {
|
|
950
|
+
await fs2__namespace.mkdir(nodePath__namespace.dirname(destPath), { recursive: true });
|
|
951
|
+
const copyFlags = options?.overwrite === false ? fs.constants.COPYFILE_EXCL : 0;
|
|
952
|
+
try {
|
|
953
|
+
await fs2__namespace.copyFile(srcPath, destPath, copyFlags);
|
|
954
|
+
} catch (error) {
|
|
955
|
+
if (options?.overwrite === false && isEexistError(error)) {
|
|
956
|
+
throw new FileExistsError(dest);
|
|
957
|
+
}
|
|
958
|
+
throw error;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (error instanceof IsDirectoryError || error instanceof FileExistsError) throw error;
|
|
963
|
+
if (isEnoentError(error)) {
|
|
964
|
+
throw new FileNotFoundError(src);
|
|
965
|
+
}
|
|
966
|
+
throw error;
|
|
967
|
+
}
|
|
894
968
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
969
|
+
async copyDirectory(src, dest, options) {
|
|
970
|
+
await this.ensureReady();
|
|
971
|
+
await fs2__namespace.mkdir(dest, { recursive: true });
|
|
972
|
+
const entries = await fs2__namespace.readdir(src, { withFileTypes: true });
|
|
973
|
+
for (const entry of entries) {
|
|
974
|
+
const srcEntry = nodePath__namespace.join(src, entry.name);
|
|
975
|
+
const destEntry = nodePath__namespace.join(dest, entry.name);
|
|
976
|
+
await this.assertPathContained(srcEntry);
|
|
977
|
+
await this.assertPathContained(destEntry);
|
|
978
|
+
if (entry.isDirectory()) {
|
|
979
|
+
await this.copyDirectory(srcEntry, destEntry, options);
|
|
980
|
+
} else {
|
|
981
|
+
const copyFlags = options?.overwrite === false ? fs.constants.COPYFILE_EXCL : 0;
|
|
982
|
+
try {
|
|
983
|
+
await fs2__namespace.copyFile(srcEntry, destEntry, copyFlags);
|
|
984
|
+
} catch (error) {
|
|
985
|
+
if (options?.overwrite === false && isEexistError(error)) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
throw error;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
898
992
|
}
|
|
899
|
-
|
|
900
|
-
|
|
993
|
+
async moveFile(src, dest, options) {
|
|
994
|
+
this.logger.debug("Moving file", { src, dest, overwrite: options?.overwrite });
|
|
995
|
+
await this.ensureReady();
|
|
996
|
+
this.assertWritable("moveFile");
|
|
997
|
+
const srcPath = this.resolvePath(src);
|
|
998
|
+
const destPath = this.resolvePath(dest);
|
|
999
|
+
await this.assertPathContained(srcPath);
|
|
1000
|
+
await this.assertPathContained(destPath);
|
|
1001
|
+
try {
|
|
1002
|
+
await fs2__namespace.mkdir(nodePath__namespace.dirname(destPath), { recursive: true });
|
|
1003
|
+
if (options?.overwrite === false) {
|
|
1004
|
+
await this.copyFile(src, dest, { ...options, overwrite: false });
|
|
1005
|
+
await fs2__namespace.rm(srcPath, { recursive: true, force: true });
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
try {
|
|
1009
|
+
await fs2__namespace.rename(srcPath, destPath);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
const code = error.code;
|
|
1012
|
+
if (code !== "EXDEV") {
|
|
1013
|
+
throw error;
|
|
1014
|
+
}
|
|
1015
|
+
await this.copyFile(src, dest, options);
|
|
1016
|
+
await fs2__namespace.rm(srcPath, { recursive: true, force: true });
|
|
1017
|
+
}
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
if (error instanceof FileExistsError) throw error;
|
|
1020
|
+
if (isEnoentError(error)) {
|
|
1021
|
+
throw new FileNotFoundError(src);
|
|
1022
|
+
}
|
|
1023
|
+
throw error;
|
|
1024
|
+
}
|
|
901
1025
|
}
|
|
902
|
-
|
|
903
|
-
|
|
1026
|
+
async mkdir(inputPath, options) {
|
|
1027
|
+
this.logger.debug("Creating directory", { path: inputPath, recursive: options?.recursive });
|
|
1028
|
+
await this.ensureReady();
|
|
1029
|
+
this.assertWritable("mkdir");
|
|
1030
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
1031
|
+
await this.assertPathContained(absolutePath);
|
|
1032
|
+
try {
|
|
1033
|
+
await fs2__namespace.mkdir(absolutePath, { recursive: options?.recursive ?? true });
|
|
1034
|
+
} catch (error) {
|
|
1035
|
+
if (isEexistError(error)) {
|
|
1036
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
1037
|
+
if (!stats.isDirectory()) {
|
|
1038
|
+
throw new FileExistsError(inputPath);
|
|
1039
|
+
}
|
|
1040
|
+
} else if (isEnoentError(error)) {
|
|
1041
|
+
const parentPath = nodePath__namespace.dirname(inputPath);
|
|
1042
|
+
throw new DirectoryNotFoundError(parentPath);
|
|
1043
|
+
} else {
|
|
1044
|
+
throw error;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
904
1047
|
}
|
|
905
|
-
|
|
906
|
-
|
|
1048
|
+
async rmdir(inputPath, options) {
|
|
1049
|
+
this.logger.debug("Removing directory", { path: inputPath, recursive: options?.recursive, force: options?.force });
|
|
1050
|
+
await this.ensureReady();
|
|
1051
|
+
this.assertWritable("rmdir");
|
|
1052
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
1053
|
+
await this.assertPathContained(absolutePath);
|
|
1054
|
+
try {
|
|
1055
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
1056
|
+
if (!stats.isDirectory()) {
|
|
1057
|
+
throw new NotDirectoryError(inputPath);
|
|
1058
|
+
}
|
|
1059
|
+
if (options?.recursive) {
|
|
1060
|
+
await fs2__namespace.rm(absolutePath, { recursive: true, force: options?.force ?? false });
|
|
1061
|
+
} else {
|
|
1062
|
+
const entries = await fs2__namespace.readdir(absolutePath);
|
|
1063
|
+
if (entries.length > 0) {
|
|
1064
|
+
throw new DirectoryNotEmptyError(inputPath);
|
|
1065
|
+
}
|
|
1066
|
+
await fs2__namespace.rmdir(absolutePath);
|
|
1067
|
+
}
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
if (error instanceof NotDirectoryError || error instanceof DirectoryNotEmptyError) {
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
1072
|
+
if (isEnoentError(error)) {
|
|
1073
|
+
if (!options?.force) {
|
|
1074
|
+
throw new DirectoryNotFoundError(inputPath);
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
throw error;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
907
1080
|
}
|
|
908
|
-
|
|
909
|
-
|
|
1081
|
+
async readdir(inputPath, options) {
|
|
1082
|
+
this.logger.debug("Reading directory", { path: inputPath, recursive: options?.recursive });
|
|
1083
|
+
await this.ensureReady();
|
|
1084
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
1085
|
+
await this.assertPathContained(absolutePath);
|
|
1086
|
+
try {
|
|
1087
|
+
const stats = await fs2__namespace.stat(absolutePath);
|
|
1088
|
+
if (!stats.isDirectory()) {
|
|
1089
|
+
throw new NotDirectoryError(inputPath);
|
|
1090
|
+
}
|
|
1091
|
+
const entries = await fs2__namespace.readdir(absolutePath, { withFileTypes: true });
|
|
1092
|
+
const result = [];
|
|
1093
|
+
for (const entry of entries) {
|
|
1094
|
+
const entryPath = nodePath__namespace.join(absolutePath, entry.name);
|
|
1095
|
+
if (options?.extension) {
|
|
1096
|
+
const extensions = Array.isArray(options.extension) ? options.extension : [options.extension];
|
|
1097
|
+
if (entry.isFile()) {
|
|
1098
|
+
const ext = nodePath__namespace.extname(entry.name);
|
|
1099
|
+
if (!extensions.some((e) => e === ext || e === ext.slice(1))) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
const isSymlink = entry.isSymbolicLink();
|
|
1105
|
+
let symlinkTarget;
|
|
1106
|
+
let resolvedType = "file";
|
|
1107
|
+
if (isSymlink) {
|
|
1108
|
+
try {
|
|
1109
|
+
symlinkTarget = await fs2__namespace.readlink(entryPath);
|
|
1110
|
+
const targetStat = await fs2__namespace.stat(entryPath);
|
|
1111
|
+
resolvedType = targetStat.isDirectory() ? "directory" : "file";
|
|
1112
|
+
} catch {
|
|
1113
|
+
resolvedType = "file";
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
resolvedType = entry.isDirectory() ? "directory" : "file";
|
|
1117
|
+
}
|
|
1118
|
+
const fileEntry = {
|
|
1119
|
+
name: entry.name,
|
|
1120
|
+
type: resolvedType,
|
|
1121
|
+
isSymlink: isSymlink || void 0,
|
|
1122
|
+
symlinkTarget
|
|
1123
|
+
};
|
|
1124
|
+
if (resolvedType === "file" && !isSymlink) {
|
|
1125
|
+
try {
|
|
1126
|
+
const stat3 = await fs2__namespace.stat(entryPath);
|
|
1127
|
+
fileEntry.size = stat3.size;
|
|
1128
|
+
} catch {
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
result.push(fileEntry);
|
|
1132
|
+
if (options?.recursive && resolvedType === "directory") {
|
|
1133
|
+
const depth = options.maxDepth ?? 100;
|
|
1134
|
+
if (depth > 0) {
|
|
1135
|
+
const subEntries = await this.readdir(this.toRelativePath(entryPath), { ...options, maxDepth: depth - 1 });
|
|
1136
|
+
result.push(
|
|
1137
|
+
...subEntries.map((e) => ({
|
|
1138
|
+
...e,
|
|
1139
|
+
name: `${entry.name}/${e.name}`
|
|
1140
|
+
}))
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
} catch (error) {
|
|
1147
|
+
if (error instanceof NotDirectoryError) throw error;
|
|
1148
|
+
if (isEnoentError(error)) {
|
|
1149
|
+
throw new DirectoryNotFoundError(inputPath);
|
|
1150
|
+
}
|
|
1151
|
+
throw error;
|
|
1152
|
+
}
|
|
910
1153
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
if (typeof description !== "string") {
|
|
917
|
-
errors.push(`${fieldPath}: Expected string, received ${typeof description}`);
|
|
918
|
-
return errors;
|
|
1154
|
+
async exists(inputPath) {
|
|
1155
|
+
await this.ensureReady();
|
|
1156
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
1157
|
+
await this.assertPathContained(absolutePath);
|
|
1158
|
+
return fsExists(absolutePath);
|
|
919
1159
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1160
|
+
async stat(inputPath) {
|
|
1161
|
+
await this.ensureReady();
|
|
1162
|
+
const absolutePath = this.resolvePath(inputPath);
|
|
1163
|
+
await this.assertPathContained(absolutePath);
|
|
1164
|
+
const result = await fsStat(absolutePath, inputPath);
|
|
1165
|
+
return {
|
|
1166
|
+
...result,
|
|
1167
|
+
path: this.toRelativePath(absolutePath)
|
|
1168
|
+
};
|
|
923
1169
|
}
|
|
924
|
-
|
|
925
|
-
|
|
1170
|
+
/**
|
|
1171
|
+
* Initialize the local filesystem by creating the base directory.
|
|
1172
|
+
* Status management is handled by the base class.
|
|
1173
|
+
*/
|
|
1174
|
+
async init() {
|
|
1175
|
+
this.logger.debug("Initializing filesystem", { basePath: this._basePath });
|
|
1176
|
+
await fs2__namespace.mkdir(this._basePath, { recursive: true });
|
|
1177
|
+
this.logger.debug("Filesystem initialized", { basePath: this._basePath });
|
|
926
1178
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const errors = [];
|
|
934
|
-
const fieldPath = "license";
|
|
935
|
-
if (license === void 0 || license === null) {
|
|
936
|
-
return errors;
|
|
937
|
-
}
|
|
938
|
-
if (typeof license !== "string") {
|
|
939
|
-
errors.push(`${fieldPath}: Expected string, received ${typeof license}`);
|
|
1179
|
+
/**
|
|
1180
|
+
* Clean up the local filesystem.
|
|
1181
|
+
* LocalFilesystem doesn't delete files on destroy by default.
|
|
1182
|
+
* Status management is handled by the base class.
|
|
1183
|
+
*/
|
|
1184
|
+
async destroy() {
|
|
940
1185
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
return errors;
|
|
1186
|
+
getInfo() {
|
|
1187
|
+
return {
|
|
1188
|
+
id: this.id,
|
|
1189
|
+
name: this.name,
|
|
1190
|
+
provider: this.provider,
|
|
1191
|
+
readOnly: this.readOnly,
|
|
1192
|
+
basePath: this.basePath,
|
|
1193
|
+
status: this.status
|
|
1194
|
+
};
|
|
951
1195
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
return errors;
|
|
1196
|
+
getInstructions() {
|
|
1197
|
+
return `Local filesystem at "${this.basePath}". Files at workspace path "/foo" are stored at "${this.basePath}/foo" on disk.`;
|
|
955
1198
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const errors = [];
|
|
967
|
-
const warnings = [];
|
|
968
|
-
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
|
|
969
|
-
errors.push(
|
|
970
|
-
`Expected object, received ${metadata === null ? "null" : Array.isArray(metadata) ? "array" : typeof metadata}`
|
|
971
|
-
);
|
|
972
|
-
return { valid: false, errors, warnings };
|
|
1199
|
+
};
|
|
1200
|
+
var InMemoryFileReadTracker = class {
|
|
1201
|
+
records = /* @__PURE__ */ new Map();
|
|
1202
|
+
recordRead(path4, modifiedAt) {
|
|
1203
|
+
const normalizedPath = this.normalizePath(path4);
|
|
1204
|
+
this.records.set(normalizedPath, {
|
|
1205
|
+
path: normalizedPath,
|
|
1206
|
+
readAt: /* @__PURE__ */ new Date(),
|
|
1207
|
+
modifiedAtRead: modifiedAt
|
|
1208
|
+
});
|
|
973
1209
|
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
errors.push(...validateSkillDescription(data.description));
|
|
977
|
-
errors.push(...validateSkillLicense(data.license));
|
|
978
|
-
errors.push(...validateSkillCompatibility());
|
|
979
|
-
errors.push(...validateSkillMetadataField(data.metadata));
|
|
980
|
-
if (dirName && typeof data.name === "string" && data.name !== dirName) {
|
|
981
|
-
errors.push(`Skill name "${data.name}" must match directory name "${dirName}"`);
|
|
1210
|
+
getReadRecord(path4) {
|
|
1211
|
+
return this.records.get(this.normalizePath(path4));
|
|
982
1212
|
}
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
`
|
|
989
|
-
|
|
1213
|
+
needsReRead(path4, currentModifiedAt) {
|
|
1214
|
+
const record = this.getReadRecord(path4);
|
|
1215
|
+
if (!record) {
|
|
1216
|
+
return {
|
|
1217
|
+
needsReRead: true,
|
|
1218
|
+
reason: `File "${path4}" has not been read. You must read a file before writing to it.`
|
|
1219
|
+
};
|
|
990
1220
|
}
|
|
991
|
-
if (
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1221
|
+
if (currentModifiedAt.getTime() > record.modifiedAtRead.getTime()) {
|
|
1222
|
+
return {
|
|
1223
|
+
needsReRead: true,
|
|
1224
|
+
reason: `File "${path4}" was modified since last read (read at: ${record.modifiedAtRead.toISOString()}, current: ${currentModifiedAt.toISOString()}). Please re-read the file to get the latest contents.`
|
|
1225
|
+
};
|
|
995
1226
|
}
|
|
1227
|
+
return { needsReRead: false };
|
|
1228
|
+
}
|
|
1229
|
+
clearReadRecord(path4) {
|
|
1230
|
+
this.records.delete(this.normalizePath(path4));
|
|
1231
|
+
}
|
|
1232
|
+
clear() {
|
|
1233
|
+
this.records.clear();
|
|
1234
|
+
}
|
|
1235
|
+
normalizePath(pathStr) {
|
|
1236
|
+
const normalized = nodePath__namespace.posix.normalize(pathStr.replace(/\\/g, "/"));
|
|
1237
|
+
return normalized.replace(/\/$/, "") || "/";
|
|
996
1238
|
}
|
|
997
|
-
return {
|
|
998
|
-
valid: errors.length === 0,
|
|
999
|
-
errors,
|
|
1000
|
-
warnings
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
function isEnoentError(error) {
|
|
1004
|
-
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
1005
|
-
}
|
|
1006
|
-
function isEexistError(error) {
|
|
1007
|
-
return error !== null && typeof error === "object" && "code" in error && error.code === "EEXIST";
|
|
1008
|
-
}
|
|
1009
|
-
var MIME_TYPES = {
|
|
1010
|
-
// Text
|
|
1011
|
-
txt: "text/plain",
|
|
1012
|
-
html: "text/html",
|
|
1013
|
-
htm: "text/html",
|
|
1014
|
-
css: "text/css",
|
|
1015
|
-
csv: "text/csv",
|
|
1016
|
-
md: "text/markdown",
|
|
1017
|
-
// Code
|
|
1018
|
-
js: "application/javascript",
|
|
1019
|
-
mjs: "application/javascript",
|
|
1020
|
-
ts: "application/typescript",
|
|
1021
|
-
tsx: "application/typescript",
|
|
1022
|
-
jsx: "application/javascript",
|
|
1023
|
-
json: "application/json",
|
|
1024
|
-
xml: "application/xml",
|
|
1025
|
-
yaml: "text/yaml",
|
|
1026
|
-
yml: "text/yaml",
|
|
1027
|
-
// Programming languages
|
|
1028
|
-
py: "text/x-python",
|
|
1029
|
-
rb: "text/x-ruby",
|
|
1030
|
-
go: "text/x-go",
|
|
1031
|
-
rs: "text/x-rust",
|
|
1032
|
-
java: "text/x-java",
|
|
1033
|
-
c: "text/x-c",
|
|
1034
|
-
cpp: "text/x-c++",
|
|
1035
|
-
h: "text/x-c",
|
|
1036
|
-
hpp: "text/x-c++",
|
|
1037
|
-
sh: "text/x-sh",
|
|
1038
|
-
bash: "text/x-sh",
|
|
1039
|
-
zsh: "text/x-sh",
|
|
1040
|
-
// Config
|
|
1041
|
-
toml: "text/toml",
|
|
1042
|
-
ini: "text/plain",
|
|
1043
|
-
env: "text/plain",
|
|
1044
|
-
// Database/Query
|
|
1045
|
-
sql: "text/x-sql",
|
|
1046
|
-
graphql: "application/graphql",
|
|
1047
|
-
gql: "application/graphql",
|
|
1048
|
-
// Frameworks
|
|
1049
|
-
vue: "text/x-vue",
|
|
1050
|
-
// Images
|
|
1051
|
-
png: "image/png",
|
|
1052
|
-
jpg: "image/jpeg",
|
|
1053
|
-
jpeg: "image/jpeg",
|
|
1054
|
-
gif: "image/gif",
|
|
1055
|
-
svg: "image/svg+xml",
|
|
1056
|
-
webp: "image/webp",
|
|
1057
|
-
ico: "image/x-icon",
|
|
1058
|
-
// Documents
|
|
1059
|
-
pdf: "application/pdf"
|
|
1060
1239
|
};
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
".yaml",
|
|
1070
|
-
".yml",
|
|
1071
|
-
".js",
|
|
1072
|
-
".mjs",
|
|
1073
|
-
".ts",
|
|
1074
|
-
".tsx",
|
|
1075
|
-
".jsx",
|
|
1076
|
-
".py",
|
|
1077
|
-
".rb",
|
|
1078
|
-
".go",
|
|
1079
|
-
".rs",
|
|
1080
|
-
".java",
|
|
1081
|
-
".c",
|
|
1082
|
-
".cpp",
|
|
1083
|
-
".h",
|
|
1084
|
-
".hpp",
|
|
1085
|
-
".sh",
|
|
1086
|
-
".bash",
|
|
1087
|
-
".zsh",
|
|
1088
|
-
".html",
|
|
1089
|
-
".htm",
|
|
1090
|
-
".css",
|
|
1091
|
-
".xml",
|
|
1092
|
-
".toml",
|
|
1093
|
-
".ini",
|
|
1094
|
-
".env",
|
|
1095
|
-
".csv",
|
|
1096
|
-
".sql",
|
|
1097
|
-
".graphql",
|
|
1098
|
-
".gql",
|
|
1099
|
-
".vue",
|
|
1100
|
-
".svg"
|
|
1101
|
-
]);
|
|
1102
|
-
function isTextFile(filename) {
|
|
1103
|
-
const ext = nodePath__namespace.extname(filename).toLowerCase();
|
|
1104
|
-
return TEXT_EXTENSIONS.has(ext);
|
|
1105
|
-
}
|
|
1106
|
-
async function fsExists(absolutePath) {
|
|
1107
|
-
try {
|
|
1108
|
-
await fs2__namespace.access(absolutePath);
|
|
1109
|
-
return true;
|
|
1110
|
-
} catch {
|
|
1111
|
-
return false;
|
|
1240
|
+
|
|
1241
|
+
// src/workspace/sandbox/errors.ts
|
|
1242
|
+
var SandboxError = class extends Error {
|
|
1243
|
+
constructor(message, code, details) {
|
|
1244
|
+
super(message);
|
|
1245
|
+
this.code = code;
|
|
1246
|
+
this.details = details;
|
|
1247
|
+
this.name = "SandboxError";
|
|
1112
1248
|
}
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1249
|
+
};
|
|
1250
|
+
var SandboxExecutionError = class extends SandboxError {
|
|
1251
|
+
constructor(message, exitCode, stdout, stderr) {
|
|
1252
|
+
super(message, "EXECUTION_FAILED", { exitCode, stdout, stderr });
|
|
1253
|
+
this.exitCode = exitCode;
|
|
1254
|
+
this.stdout = stdout;
|
|
1255
|
+
this.stderr = stderr;
|
|
1256
|
+
this.name = "SandboxExecutionError";
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
var SandboxTimeoutError = class extends SandboxError {
|
|
1260
|
+
constructor(timeoutMs, operation) {
|
|
1261
|
+
super(`Execution timed out after ${timeoutMs}ms`, "TIMEOUT", { timeoutMs, operation });
|
|
1262
|
+
this.timeoutMs = timeoutMs;
|
|
1263
|
+
this.operation = operation;
|
|
1264
|
+
this.name = "SandboxTimeoutError";
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
var SandboxNotReadyError = class extends SandboxError {
|
|
1268
|
+
constructor(idOrStatus) {
|
|
1269
|
+
super(`Sandbox is not ready: ${idOrStatus}`, "NOT_READY", { id: idOrStatus });
|
|
1270
|
+
this.name = "SandboxNotReadyError";
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
var IsolationUnavailableError = class extends SandboxError {
|
|
1274
|
+
constructor(backend, reason) {
|
|
1275
|
+
super(`Isolation backend '${backend}' is not available: ${reason}`, "ISOLATION_UNAVAILABLE", { backend, reason });
|
|
1276
|
+
this.backend = backend;
|
|
1277
|
+
this.reason = reason;
|
|
1278
|
+
this.name = "IsolationUnavailableError";
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
var MountError = class extends SandboxError {
|
|
1282
|
+
constructor(message, mountPath, details) {
|
|
1283
|
+
super(message, "MOUNT_ERROR", { ...details, mountPath });
|
|
1284
|
+
this.mountPath = mountPath;
|
|
1285
|
+
this.name = "MountError";
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
var MountNotSupportedError = class extends SandboxError {
|
|
1289
|
+
constructor(sandboxProvider) {
|
|
1290
|
+
super(`Sandbox provider '${sandboxProvider}' does not support mounting`, "MOUNT_NOT_SUPPORTED", {
|
|
1291
|
+
sandboxProvider
|
|
1292
|
+
});
|
|
1293
|
+
this.name = "MountNotSupportedError";
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
var FilesystemNotMountableError = class extends SandboxError {
|
|
1297
|
+
constructor(filesystemProvider, reason) {
|
|
1298
|
+
const message = reason ? `Filesystem '${filesystemProvider}' cannot be mounted: ${reason}` : `Filesystem '${filesystemProvider}' does not support mounting`;
|
|
1299
|
+
super(message, "FILESYSTEM_NOT_MOUNTABLE", { filesystemProvider, reason });
|
|
1300
|
+
this.name = "FilesystemNotMountableError";
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
var MountManager = class {
|
|
1304
|
+
_entries = /* @__PURE__ */ new Map();
|
|
1305
|
+
_mountFn;
|
|
1306
|
+
_onMount;
|
|
1307
|
+
_sandbox;
|
|
1308
|
+
_workspace;
|
|
1309
|
+
logger;
|
|
1310
|
+
constructor(config) {
|
|
1311
|
+
this._mountFn = config.mount;
|
|
1312
|
+
this.logger = config.logger;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Set the sandbox and workspace references for onMount hook args.
|
|
1316
|
+
* Called by Workspace during construction.
|
|
1317
|
+
*/
|
|
1318
|
+
setContext(context) {
|
|
1319
|
+
this._sandbox = context.sandbox;
|
|
1320
|
+
this._workspace = context.workspace;
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Set the onMount hook for custom mount handling.
|
|
1324
|
+
* Called before each mount - can skip, handle, or defer to default.
|
|
1325
|
+
*/
|
|
1326
|
+
setOnMount(hook) {
|
|
1327
|
+
this._onMount = hook;
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Update the logger instance.
|
|
1331
|
+
* Called when the sandbox receives a logger from Mastra.
|
|
1332
|
+
* @internal
|
|
1333
|
+
*/
|
|
1334
|
+
__setLogger(logger) {
|
|
1335
|
+
this.logger = logger;
|
|
1336
|
+
}
|
|
1337
|
+
// ---------------------------------------------------------------------------
|
|
1338
|
+
// Entry Access
|
|
1339
|
+
// ---------------------------------------------------------------------------
|
|
1340
|
+
/**
|
|
1341
|
+
* Get all mount entries.
|
|
1342
|
+
*/
|
|
1343
|
+
get entries() {
|
|
1344
|
+
return this._entries;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Get a mount entry by path.
|
|
1348
|
+
*/
|
|
1349
|
+
get(path4) {
|
|
1350
|
+
return this._entries.get(path4);
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Check if a mount exists at the given path.
|
|
1354
|
+
*/
|
|
1355
|
+
has(path4) {
|
|
1356
|
+
return this._entries.has(path4);
|
|
1357
|
+
}
|
|
1358
|
+
// ---------------------------------------------------------------------------
|
|
1359
|
+
// Entry Modification
|
|
1360
|
+
// ---------------------------------------------------------------------------
|
|
1361
|
+
/**
|
|
1362
|
+
* Add pending mounts from workspace config.
|
|
1363
|
+
* These will be processed when `processPending()` is called.
|
|
1364
|
+
*/
|
|
1365
|
+
add(mounts) {
|
|
1366
|
+
const paths = Object.keys(mounts);
|
|
1367
|
+
this.logger.debug(`Adding ${paths.length} pending mount(s)`, { paths });
|
|
1368
|
+
for (const [path4, filesystem] of Object.entries(mounts)) {
|
|
1369
|
+
this._entries.set(path4, {
|
|
1370
|
+
filesystem,
|
|
1371
|
+
state: "pending"
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Update a mount entry's state.
|
|
1377
|
+
* Creates the entry if it doesn't exist.
|
|
1378
|
+
*/
|
|
1379
|
+
set(path4, updates) {
|
|
1380
|
+
const existing = this._entries.get(path4);
|
|
1381
|
+
if (existing) {
|
|
1382
|
+
existing.state = updates.state;
|
|
1383
|
+
if (updates.config) {
|
|
1384
|
+
existing.config = updates.config;
|
|
1385
|
+
existing.configHash = this.hashConfig(updates.config);
|
|
1386
|
+
}
|
|
1387
|
+
if ("error" in updates) {
|
|
1388
|
+
existing.error = updates.error;
|
|
1389
|
+
}
|
|
1390
|
+
} else if (updates.filesystem) {
|
|
1391
|
+
this._entries.set(path4, {
|
|
1392
|
+
filesystem: updates.filesystem,
|
|
1393
|
+
state: updates.state,
|
|
1394
|
+
config: updates.config,
|
|
1395
|
+
configHash: updates.config ? this.hashConfig(updates.config) : void 0,
|
|
1396
|
+
error: updates.error
|
|
1397
|
+
});
|
|
1398
|
+
} else {
|
|
1399
|
+
this.logger.debug(`set() called for unknown path "${path4}" without filesystem \u2014 no entry created`);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Delete a mount entry.
|
|
1404
|
+
*/
|
|
1405
|
+
delete(path4) {
|
|
1406
|
+
return this._entries.delete(path4);
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Clear all mount entries.
|
|
1410
|
+
*/
|
|
1411
|
+
clear() {
|
|
1412
|
+
this._entries.clear();
|
|
1413
|
+
}
|
|
1414
|
+
// ---------------------------------------------------------------------------
|
|
1415
|
+
// Mount Processing
|
|
1416
|
+
// ---------------------------------------------------------------------------
|
|
1417
|
+
/**
|
|
1418
|
+
* Process all pending mounts.
|
|
1419
|
+
* Call this after sandbox is ready (in start()).
|
|
1420
|
+
*/
|
|
1421
|
+
async processPending() {
|
|
1422
|
+
const pendingCount = [...this._entries.values()].filter((e) => e.state === "pending").length;
|
|
1423
|
+
if (pendingCount === 0) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
this.logger.debug(`Processing ${pendingCount} pending mount(s)`);
|
|
1427
|
+
for (const [path4, entry] of this._entries) {
|
|
1428
|
+
if (entry.state !== "pending") {
|
|
1429
|
+
continue;
|
|
1430
|
+
}
|
|
1431
|
+
const fsProvider = entry.filesystem.provider;
|
|
1432
|
+
const config = entry.filesystem.getMountConfig?.();
|
|
1433
|
+
if (this._onMount) {
|
|
1434
|
+
try {
|
|
1435
|
+
const hookResult = await this._onMount({
|
|
1436
|
+
filesystem: entry.filesystem,
|
|
1437
|
+
mountPath: path4,
|
|
1438
|
+
config,
|
|
1439
|
+
sandbox: this._sandbox,
|
|
1440
|
+
workspace: this._workspace
|
|
1441
|
+
});
|
|
1442
|
+
if (hookResult === false) {
|
|
1443
|
+
entry.state = "unsupported";
|
|
1444
|
+
entry.error = "Skipped by onMount hook";
|
|
1445
|
+
this.logger.debug(`Mount skipped by onMount hook`, { path: path4, provider: fsProvider });
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
if (hookResult && typeof hookResult === "object") {
|
|
1449
|
+
if (hookResult.success) {
|
|
1450
|
+
entry.state = "mounted";
|
|
1451
|
+
entry.config = config;
|
|
1452
|
+
entry.configHash = config ? this.hashConfig(config) : void 0;
|
|
1453
|
+
this.logger.info(`Mount handled by onMount hook`, { path: path4, provider: fsProvider });
|
|
1454
|
+
} else {
|
|
1455
|
+
entry.state = "error";
|
|
1456
|
+
entry.error = hookResult.error ?? "Mount hook failed";
|
|
1457
|
+
this.logger.error(`Mount hook failed`, { path: path4, provider: fsProvider, error: entry.error });
|
|
1458
|
+
}
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
} catch (err) {
|
|
1462
|
+
entry.state = "error";
|
|
1463
|
+
entry.error = `Mount hook error: ${String(err)}`;
|
|
1464
|
+
this.logger.error(`Mount hook threw error`, { path: path4, provider: fsProvider, error: entry.error });
|
|
1465
|
+
continue;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (!config) {
|
|
1469
|
+
entry.state = "unsupported";
|
|
1470
|
+
entry.error = "Filesystem does not support mounting";
|
|
1471
|
+
this.logger.debug(`Filesystem does not support mounting`, { path: path4, provider: fsProvider });
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
entry.config = config;
|
|
1475
|
+
entry.configHash = this.hashConfig(config);
|
|
1476
|
+
entry.state = "mounting";
|
|
1477
|
+
this.logger.debug(`Mounting filesystem`, { path: path4, provider: fsProvider, type: config.type });
|
|
1478
|
+
try {
|
|
1479
|
+
const result = await this._mountFn(entry.filesystem, path4);
|
|
1480
|
+
if (result.success) {
|
|
1481
|
+
entry.state = "mounted";
|
|
1482
|
+
this.logger.info(`Mount successful`, { path: path4, provider: fsProvider });
|
|
1483
|
+
} else {
|
|
1484
|
+
entry.state = "error";
|
|
1485
|
+
entry.error = result.error ?? "Mount failed";
|
|
1486
|
+
this.logger.error(`Mount failed`, { path: path4, provider: fsProvider, error: entry.error });
|
|
1487
|
+
}
|
|
1488
|
+
} catch (err) {
|
|
1489
|
+
entry.state = "error";
|
|
1490
|
+
entry.error = String(err);
|
|
1491
|
+
this.logger.error(`Mount threw error`, { path: path4, provider: fsProvider, error: entry.error });
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
// ---------------------------------------------------------------------------
|
|
1496
|
+
// Marker File Helpers
|
|
1497
|
+
// ---------------------------------------------------------------------------
|
|
1498
|
+
/**
|
|
1499
|
+
* Generate a marker filename for a mount path.
|
|
1500
|
+
* Used by sandboxes to store mount metadata for reconnection detection.
|
|
1501
|
+
*
|
|
1502
|
+
* @param mountPath - The mount path to generate a filename for
|
|
1503
|
+
* @returns A safe filename like "mount-abc123"
|
|
1504
|
+
*/
|
|
1505
|
+
markerFilename(mountPath) {
|
|
1506
|
+
let hash = 0;
|
|
1507
|
+
for (let i = 0; i < mountPath.length; i++) {
|
|
1508
|
+
const char = mountPath.charCodeAt(i);
|
|
1509
|
+
hash = (hash << 5) - hash + char;
|
|
1510
|
+
hash |= 0;
|
|
1511
|
+
}
|
|
1512
|
+
return `mount-${Math.abs(hash).toString(36)}`;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Generate marker file content for a mount path.
|
|
1516
|
+
* Format: "path|configHash" - used for detecting config changes on reconnect.
|
|
1517
|
+
*
|
|
1518
|
+
* @param mountPath - The mount path
|
|
1519
|
+
* @returns Marker content string, or null if no config hash available
|
|
1520
|
+
*/
|
|
1521
|
+
getMarkerContent(mountPath) {
|
|
1522
|
+
const entry = this._entries.get(mountPath);
|
|
1523
|
+
if (!entry?.configHash) {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
return `${mountPath}|${entry.configHash}`;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Parse marker file content.
|
|
1530
|
+
*
|
|
1531
|
+
* @param content - The marker file content (format: "path|configHash")
|
|
1532
|
+
* @returns Parsed path and configHash, or null if invalid format
|
|
1533
|
+
*/
|
|
1534
|
+
parseMarkerContent(content) {
|
|
1535
|
+
const separatorIndex = content.lastIndexOf("|");
|
|
1536
|
+
if (separatorIndex <= 0) {
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
const path4 = content.slice(0, separatorIndex);
|
|
1540
|
+
const configHash = content.slice(separatorIndex + 1);
|
|
1541
|
+
if (!path4 || !configHash) return null;
|
|
1542
|
+
return { path: path4, configHash };
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Check if a config hash matches the expected hash for a mount path.
|
|
1546
|
+
*
|
|
1547
|
+
* @param mountPath - The mount path to check
|
|
1548
|
+
* @param storedHash - The hash from the marker file
|
|
1549
|
+
* @returns true if the hashes match
|
|
1550
|
+
*/
|
|
1551
|
+
isConfigMatching(mountPath, storedHash) {
|
|
1552
|
+
const entry = this._entries.get(mountPath);
|
|
1553
|
+
return entry?.configHash === storedHash;
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Compute a hash for a mount config. Used for comparing configs across mounts.
|
|
1557
|
+
*
|
|
1558
|
+
* @param config - The config to hash
|
|
1559
|
+
* @returns A hash string suitable for comparison
|
|
1560
|
+
*/
|
|
1561
|
+
computeConfigHash(config) {
|
|
1562
|
+
return this.hashConfig(config);
|
|
1563
|
+
}
|
|
1564
|
+
// ---------------------------------------------------------------------------
|
|
1565
|
+
// Internal
|
|
1566
|
+
// ---------------------------------------------------------------------------
|
|
1567
|
+
/**
|
|
1568
|
+
* Hash a mount config for comparison.
|
|
1569
|
+
*/
|
|
1570
|
+
hashConfig(config) {
|
|
1571
|
+
const normalized = JSON.stringify(this.sortKeysDeep(config));
|
|
1572
|
+
return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
1573
|
+
}
|
|
1574
|
+
sortKeysDeep(obj) {
|
|
1575
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1576
|
+
if (Array.isArray(obj)) return obj.map((item) => this.sortKeysDeep(item));
|
|
1577
|
+
return Object.keys(obj).sort().reduce(
|
|
1578
|
+
(acc, key) => {
|
|
1579
|
+
acc[key] = this.sortKeysDeep(obj[key]);
|
|
1580
|
+
return acc;
|
|
1581
|
+
},
|
|
1582
|
+
{}
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
};
|
|
1586
|
+
|
|
1587
|
+
// src/workspace/sandbox/mastra-sandbox.ts
|
|
1588
|
+
var MastraSandbox = class extends chunkRO47SMI7_cjs.MastraBase {
|
|
1589
|
+
/** Mount manager - automatically created if subclass implements mount() */
|
|
1590
|
+
mounts;
|
|
1591
|
+
// ---------------------------------------------------------------------------
|
|
1592
|
+
// Lifecycle Promise Tracking (prevents race conditions)
|
|
1593
|
+
// ---------------------------------------------------------------------------
|
|
1594
|
+
/** Promise for _start() to prevent race conditions from concurrent calls */
|
|
1595
|
+
_startPromise;
|
|
1596
|
+
/** Promise for _stop() to prevent race conditions from concurrent calls */
|
|
1597
|
+
_stopPromise;
|
|
1598
|
+
/** Promise for _destroy() to prevent race conditions from concurrent calls */
|
|
1599
|
+
_destroyPromise;
|
|
1600
|
+
/** Lifecycle callbacks */
|
|
1601
|
+
_onStart;
|
|
1602
|
+
_onStop;
|
|
1603
|
+
_onDestroy;
|
|
1604
|
+
constructor(options) {
|
|
1605
|
+
super({ name: options.name, component: chunk7XAECHYL_cjs.RegisteredLogger.WORKSPACE });
|
|
1606
|
+
this._onStart = options.onStart;
|
|
1607
|
+
this._onStop = options.onStop;
|
|
1608
|
+
this._onDestroy = options.onDestroy;
|
|
1609
|
+
if (this.mount) {
|
|
1610
|
+
this.mounts = new MountManager({
|
|
1611
|
+
mount: this.mount.bind(this),
|
|
1612
|
+
logger: this.logger
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
// ---------------------------------------------------------------------------
|
|
1617
|
+
// Lifecycle Wrappers (race-condition-safe)
|
|
1618
|
+
// ---------------------------------------------------------------------------
|
|
1619
|
+
/**
|
|
1620
|
+
* Start the sandbox (wrapper with status management and race-condition safety).
|
|
1621
|
+
*
|
|
1622
|
+
* This method is race-condition-safe - concurrent calls will return the same promise.
|
|
1623
|
+
* Handles status management and automatically processes pending mounts after startup.
|
|
1624
|
+
*
|
|
1625
|
+
* Subclasses override `start()` to provide their startup logic.
|
|
1626
|
+
*/
|
|
1627
|
+
async _start() {
|
|
1628
|
+
if (this.status === "running") {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
if (this._stopPromise) await this._stopPromise;
|
|
1632
|
+
if (this._destroyPromise) await this._destroyPromise;
|
|
1633
|
+
if (this.status === "destroyed") {
|
|
1634
|
+
throw new Error("Cannot start a destroyed sandbox");
|
|
1635
|
+
}
|
|
1636
|
+
if (this._startPromise) {
|
|
1637
|
+
return this._startPromise;
|
|
1638
|
+
}
|
|
1639
|
+
this._startPromise = this._executeStart();
|
|
1640
|
+
try {
|
|
1641
|
+
await this._startPromise;
|
|
1642
|
+
} finally {
|
|
1643
|
+
this._startPromise = void 0;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Internal start execution - handles status and mount processing.
|
|
1648
|
+
*/
|
|
1649
|
+
async _executeStart() {
|
|
1650
|
+
this.status = "starting";
|
|
1651
|
+
try {
|
|
1652
|
+
await this.start();
|
|
1653
|
+
this.status = "running";
|
|
1654
|
+
try {
|
|
1655
|
+
await this._onStart?.({ sandbox: this });
|
|
1656
|
+
} catch (error) {
|
|
1657
|
+
this.logger.warn("onStart callback failed", { error });
|
|
1658
|
+
}
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
this.status = "error";
|
|
1661
|
+
throw error;
|
|
1662
|
+
}
|
|
1663
|
+
try {
|
|
1664
|
+
await this.mounts?.processPending();
|
|
1665
|
+
} catch (error) {
|
|
1666
|
+
this.logger.warn("Unexpected error processing pending mounts", { error });
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* Override this method to implement sandbox startup logic.
|
|
1671
|
+
*
|
|
1672
|
+
* Called by `_start()` after status is set to 'starting'.
|
|
1673
|
+
* Status will be set to 'running' on success, 'error' on failure.
|
|
1674
|
+
*
|
|
1675
|
+
* @example
|
|
1676
|
+
* ```typescript
|
|
1677
|
+
* async start(): Promise<void> {
|
|
1678
|
+
* this._sandbox = await Sandbox.create({ ... });
|
|
1679
|
+
* }
|
|
1680
|
+
* ```
|
|
1681
|
+
*/
|
|
1682
|
+
async start() {
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Ensure the sandbox is running.
|
|
1686
|
+
*
|
|
1687
|
+
* Calls `_start()` if status is not 'running'. Useful for lazy initialization
|
|
1688
|
+
* where operations should automatically start the sandbox if needed.
|
|
1689
|
+
*
|
|
1690
|
+
* @throws {SandboxNotReadyError} if the sandbox fails to reach 'running' status
|
|
1691
|
+
*
|
|
1692
|
+
* @example
|
|
1693
|
+
* ```typescript
|
|
1694
|
+
* async executeCommand(command: string): Promise<CommandResult> {
|
|
1695
|
+
* await this.ensureRunning();
|
|
1696
|
+
* // Now safe to use the sandbox
|
|
1697
|
+
* }
|
|
1698
|
+
* ```
|
|
1699
|
+
*/
|
|
1700
|
+
async ensureRunning() {
|
|
1701
|
+
if (this.status !== "running") {
|
|
1702
|
+
await this._start();
|
|
1703
|
+
}
|
|
1704
|
+
if (this.status !== "running") {
|
|
1705
|
+
throw new SandboxNotReadyError(this.id);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Stop the sandbox (wrapper with status management and race-condition safety).
|
|
1710
|
+
*
|
|
1711
|
+
* This method is race-condition-safe - concurrent calls will return the same promise.
|
|
1712
|
+
* Handles status management.
|
|
1713
|
+
*
|
|
1714
|
+
* Subclasses override `stop()` to provide their stop logic.
|
|
1715
|
+
*/
|
|
1716
|
+
async _stop() {
|
|
1717
|
+
if (this.status === "stopped") {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
if (this._startPromise) await this._startPromise.catch(() => {
|
|
1721
|
+
});
|
|
1722
|
+
if (this._stopPromise) {
|
|
1723
|
+
return this._stopPromise;
|
|
1724
|
+
}
|
|
1725
|
+
this._stopPromise = this._executeStop();
|
|
1726
|
+
try {
|
|
1727
|
+
await this._stopPromise;
|
|
1728
|
+
} finally {
|
|
1729
|
+
this._stopPromise = void 0;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Internal stop execution - handles status.
|
|
1734
|
+
*/
|
|
1735
|
+
async _executeStop() {
|
|
1736
|
+
this.status = "stopping";
|
|
1737
|
+
try {
|
|
1738
|
+
await this._onStop?.({ sandbox: this });
|
|
1739
|
+
await this.stop();
|
|
1740
|
+
this.status = "stopped";
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
this.status = "error";
|
|
1743
|
+
throw error;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Override this method to implement sandbox stop logic.
|
|
1748
|
+
*
|
|
1749
|
+
* Called by `_stop()` after status is set to 'stopping'.
|
|
1750
|
+
* Status will be set to 'stopped' on success, 'error' on failure.
|
|
1751
|
+
*/
|
|
1752
|
+
async stop() {
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* Destroy the sandbox and clean up all resources (wrapper with status management).
|
|
1756
|
+
*
|
|
1757
|
+
* This method is race-condition-safe - concurrent calls will return the same promise.
|
|
1758
|
+
* Handles status management.
|
|
1759
|
+
*
|
|
1760
|
+
* Subclasses override `destroy()` to provide their destroy logic.
|
|
1761
|
+
*/
|
|
1762
|
+
async _destroy() {
|
|
1763
|
+
if (this.status === "destroyed") {
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
if (this._startPromise) await this._startPromise.catch(() => {
|
|
1767
|
+
});
|
|
1768
|
+
if (this._stopPromise) await this._stopPromise.catch(() => {
|
|
1769
|
+
});
|
|
1770
|
+
if (this._destroyPromise) {
|
|
1771
|
+
return this._destroyPromise;
|
|
1772
|
+
}
|
|
1773
|
+
this._destroyPromise = this._executeDestroy();
|
|
1774
|
+
try {
|
|
1775
|
+
await this._destroyPromise;
|
|
1776
|
+
} finally {
|
|
1777
|
+
this._destroyPromise = void 0;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Internal destroy execution - handles status.
|
|
1782
|
+
*/
|
|
1783
|
+
async _executeDestroy() {
|
|
1784
|
+
this.status = "destroying";
|
|
1785
|
+
try {
|
|
1786
|
+
await this._onDestroy?.({ sandbox: this });
|
|
1787
|
+
await this.destroy();
|
|
1788
|
+
this.status = "destroyed";
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
this.status = "error";
|
|
1791
|
+
throw error;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Override this method to implement sandbox destroy logic.
|
|
1796
|
+
*
|
|
1797
|
+
* Called by `_destroy()` after status is set to 'destroying'.
|
|
1798
|
+
* Status will be set to 'destroyed' on success, 'error' on failure.
|
|
1799
|
+
*/
|
|
1800
|
+
async destroy() {
|
|
1801
|
+
}
|
|
1802
|
+
// ---------------------------------------------------------------------------
|
|
1803
|
+
// Logger Propagation
|
|
1804
|
+
// ---------------------------------------------------------------------------
|
|
1805
|
+
/**
|
|
1806
|
+
* Override to propagate logger to MountManager.
|
|
1807
|
+
* @internal
|
|
1808
|
+
*/
|
|
1809
|
+
__setLogger(logger) {
|
|
1810
|
+
super.__setLogger(logger);
|
|
1811
|
+
this.mounts?.__setLogger(logger);
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
|
|
1815
|
+
// src/workspace/line-utils.ts
|
|
1816
|
+
function extractLines(content, startLine, endLine) {
|
|
1817
|
+
const allLines = content.split("\n");
|
|
1818
|
+
const totalLines = allLines.length;
|
|
1819
|
+
const start = Math.max(1, startLine ?? 1);
|
|
1820
|
+
const end = Math.min(totalLines, endLine ?? totalLines);
|
|
1821
|
+
const extractedLines = allLines.slice(start - 1, end);
|
|
1822
|
+
return {
|
|
1823
|
+
content: extractedLines.join("\n"),
|
|
1824
|
+
lines: { start, end },
|
|
1825
|
+
totalLines
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
function extractLinesWithLimit(content, offset, limit) {
|
|
1829
|
+
const startLine = offset ?? 1;
|
|
1830
|
+
const endLine = limit ? startLine + limit - 1 : void 0;
|
|
1831
|
+
return extractLines(content, startLine, endLine);
|
|
1832
|
+
}
|
|
1833
|
+
function formatWithLineNumbers(content, startLineNumber = 1) {
|
|
1834
|
+
const lines = content.split("\n");
|
|
1835
|
+
const maxLineNum = startLineNumber + lines.length - 1;
|
|
1836
|
+
const padWidth = Math.max(6, String(maxLineNum).length + 1);
|
|
1837
|
+
return lines.map((line, i) => {
|
|
1838
|
+
const lineNum = startLineNumber + i;
|
|
1839
|
+
return `${String(lineNum).padStart(padWidth)}\u2192${line}`;
|
|
1840
|
+
}).join("\n");
|
|
1841
|
+
}
|
|
1842
|
+
function countOccurrences(content, searchString) {
|
|
1843
|
+
if (!searchString) return 0;
|
|
1844
|
+
let count = 0;
|
|
1845
|
+
let position = 0;
|
|
1846
|
+
while ((position = content.indexOf(searchString, position)) !== -1) {
|
|
1847
|
+
count++;
|
|
1848
|
+
position += searchString.length;
|
|
1849
|
+
}
|
|
1850
|
+
return count;
|
|
1851
|
+
}
|
|
1852
|
+
function replaceString(content, oldString, newString, replaceAll = false) {
|
|
1853
|
+
const count = countOccurrences(content, oldString);
|
|
1854
|
+
if (count === 0) {
|
|
1855
|
+
throw new StringNotFoundError(oldString);
|
|
1856
|
+
}
|
|
1857
|
+
if (!replaceAll && count > 1) {
|
|
1858
|
+
throw new StringNotUniqueError(oldString, count);
|
|
1859
|
+
}
|
|
1860
|
+
if (replaceAll) {
|
|
1861
|
+
const result = content.split(oldString).join(newString);
|
|
1862
|
+
return { content: result, replacements: count };
|
|
1863
|
+
} else {
|
|
1864
|
+
const result = content.replace(oldString, newString);
|
|
1865
|
+
return { content: result, replacements: 1 };
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
var StringNotFoundError = class extends Error {
|
|
1869
|
+
constructor(searchString) {
|
|
1870
|
+
super(`The specified text was not found. Make sure you use the exact text from the file.`);
|
|
1871
|
+
this.searchString = searchString;
|
|
1872
|
+
this.name = "StringNotFoundError";
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
var StringNotUniqueError = class extends Error {
|
|
1876
|
+
constructor(searchString, occurrences) {
|
|
1877
|
+
super(
|
|
1878
|
+
`The specified text appears ${occurrences} times. Provide more surrounding context to make the match unique, or use replace_all to replace all occurrences.`
|
|
1879
|
+
);
|
|
1880
|
+
this.searchString = searchString;
|
|
1881
|
+
this.occurrences = occurrences;
|
|
1882
|
+
this.name = "StringNotUniqueError";
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
|
|
1886
|
+
// src/workspace/search/bm25.ts
|
|
1887
|
+
var DEFAULT_STOPWORDS = /* @__PURE__ */ new Set([
|
|
1888
|
+
"a",
|
|
1889
|
+
"an",
|
|
1890
|
+
"and",
|
|
1891
|
+
"are",
|
|
1892
|
+
"as",
|
|
1893
|
+
"at",
|
|
1894
|
+
"be",
|
|
1895
|
+
"by",
|
|
1896
|
+
"for",
|
|
1897
|
+
"from",
|
|
1898
|
+
"has",
|
|
1899
|
+
"he",
|
|
1900
|
+
"in",
|
|
1901
|
+
"is",
|
|
1902
|
+
"it",
|
|
1903
|
+
"its",
|
|
1904
|
+
"of",
|
|
1905
|
+
"on",
|
|
1906
|
+
"or",
|
|
1907
|
+
"that",
|
|
1908
|
+
"the",
|
|
1909
|
+
"to",
|
|
1910
|
+
"was",
|
|
1911
|
+
"were",
|
|
1912
|
+
"will",
|
|
1913
|
+
"with"
|
|
1914
|
+
]);
|
|
1915
|
+
var DEFAULT_TOKENIZE_OPTIONS = {
|
|
1916
|
+
lowercase: true,
|
|
1917
|
+
removePunctuation: true,
|
|
1918
|
+
minLength: 2,
|
|
1919
|
+
stopwords: DEFAULT_STOPWORDS,
|
|
1920
|
+
splitPattern: /\s+/
|
|
1921
|
+
};
|
|
1922
|
+
function tokenize(text, options = {}) {
|
|
1923
|
+
const opts = { ...DEFAULT_TOKENIZE_OPTIONS, ...options };
|
|
1924
|
+
let processed = text;
|
|
1925
|
+
if (opts.lowercase) {
|
|
1926
|
+
processed = processed.toLowerCase();
|
|
1927
|
+
}
|
|
1928
|
+
if (opts.removePunctuation) {
|
|
1929
|
+
processed = processed.replace(/[^\w\s]/g, " ");
|
|
1930
|
+
}
|
|
1931
|
+
const tokens = processed.split(opts.splitPattern).filter((token) => {
|
|
1932
|
+
if (token.length < opts.minLength) {
|
|
1933
|
+
return false;
|
|
1934
|
+
}
|
|
1935
|
+
if (opts.stopwords?.has(token)) {
|
|
1936
|
+
return false;
|
|
1937
|
+
}
|
|
1938
|
+
return true;
|
|
1939
|
+
});
|
|
1940
|
+
return tokens;
|
|
1941
|
+
}
|
|
1942
|
+
function findLineRange(content, queryTerms, options = {}) {
|
|
1943
|
+
if (queryTerms.length === 0) return void 0;
|
|
1944
|
+
const lines = content.split("\n");
|
|
1945
|
+
const defaultOpts = { lowercase: true, removePunctuation: true, minLength: 2 };
|
|
1946
|
+
const opts = { ...defaultOpts, ...options };
|
|
1947
|
+
const normalizedTerms = new Set(queryTerms.map((t) => opts.lowercase ? t.toLowerCase() : t));
|
|
1948
|
+
let firstMatchLine;
|
|
1949
|
+
let lastMatchLine;
|
|
1950
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1951
|
+
const lineTokens = tokenize(lines[i], options);
|
|
1952
|
+
for (const token of lineTokens) {
|
|
1953
|
+
if (normalizedTerms.has(token)) {
|
|
1954
|
+
const lineNum = i + 1;
|
|
1955
|
+
if (firstMatchLine === void 0) {
|
|
1956
|
+
firstMatchLine = lineNum;
|
|
1957
|
+
}
|
|
1958
|
+
lastMatchLine = lineNum;
|
|
1959
|
+
break;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
if (firstMatchLine !== void 0 && lastMatchLine !== void 0) {
|
|
1964
|
+
return { start: firstMatchLine, end: lastMatchLine };
|
|
1965
|
+
}
|
|
1966
|
+
return void 0;
|
|
1967
|
+
}
|
|
1968
|
+
function computeTermFrequencies(tokens) {
|
|
1969
|
+
const frequencies = /* @__PURE__ */ new Map();
|
|
1970
|
+
for (const token of tokens) {
|
|
1971
|
+
frequencies.set(token, (frequencies.get(token) || 0) + 1);
|
|
1972
|
+
}
|
|
1973
|
+
return frequencies;
|
|
1974
|
+
}
|
|
1975
|
+
var BM25Index = class _BM25Index {
|
|
1976
|
+
/** BM25 k1 parameter */
|
|
1977
|
+
k1;
|
|
1978
|
+
/** BM25 b parameter */
|
|
1979
|
+
b;
|
|
1980
|
+
/** Documents in the index */
|
|
1981
|
+
#documents = /* @__PURE__ */ new Map();
|
|
1982
|
+
/** Inverted index: term -> document IDs containing the term */
|
|
1983
|
+
#invertedIndex = /* @__PURE__ */ new Map();
|
|
1984
|
+
/** Document frequency: term -> number of documents containing the term */
|
|
1985
|
+
#documentFrequency = /* @__PURE__ */ new Map();
|
|
1986
|
+
/** Average document length */
|
|
1987
|
+
#avgDocLength = 0;
|
|
1988
|
+
/** Total number of documents */
|
|
1989
|
+
#docCount = 0;
|
|
1990
|
+
/** Tokenization options */
|
|
1991
|
+
#tokenizeOptions;
|
|
1992
|
+
constructor(config = {}, tokenizeOptions = {}) {
|
|
1993
|
+
this.k1 = config.k1 ?? 1.5;
|
|
1994
|
+
this.b = config.b ?? 0.75;
|
|
1995
|
+
this.#tokenizeOptions = tokenizeOptions;
|
|
1996
|
+
}
|
|
1997
|
+
/**
|
|
1998
|
+
* Add a document to the index
|
|
1999
|
+
*/
|
|
2000
|
+
add(id, content, metadata) {
|
|
2001
|
+
if (this.#documents.has(id)) {
|
|
2002
|
+
this.remove(id);
|
|
2003
|
+
}
|
|
2004
|
+
const tokens = tokenize(content, this.#tokenizeOptions);
|
|
2005
|
+
const termFrequencies = computeTermFrequencies(tokens);
|
|
2006
|
+
const doc = {
|
|
2007
|
+
id,
|
|
2008
|
+
content,
|
|
2009
|
+
tokens,
|
|
2010
|
+
termFrequencies,
|
|
2011
|
+
length: tokens.length,
|
|
2012
|
+
metadata
|
|
2013
|
+
};
|
|
2014
|
+
this.#documents.set(id, doc);
|
|
2015
|
+
this.#docCount++;
|
|
2016
|
+
for (const term of termFrequencies.keys()) {
|
|
2017
|
+
if (!this.#invertedIndex.has(term)) {
|
|
2018
|
+
this.#invertedIndex.set(term, /* @__PURE__ */ new Set());
|
|
2019
|
+
}
|
|
2020
|
+
this.#invertedIndex.get(term).add(id);
|
|
2021
|
+
this.#documentFrequency.set(term, (this.#documentFrequency.get(term) || 0) + 1);
|
|
2022
|
+
}
|
|
2023
|
+
this.#updateAvgDocLength();
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Remove a document from the index
|
|
2027
|
+
*/
|
|
2028
|
+
remove(id) {
|
|
2029
|
+
const doc = this.#documents.get(id);
|
|
2030
|
+
if (!doc) {
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
for (const term of doc.termFrequencies.keys()) {
|
|
2034
|
+
const docIds = this.#invertedIndex.get(term);
|
|
2035
|
+
if (docIds) {
|
|
2036
|
+
docIds.delete(id);
|
|
2037
|
+
if (docIds.size === 0) {
|
|
2038
|
+
this.#invertedIndex.delete(term);
|
|
2039
|
+
this.#documentFrequency.delete(term);
|
|
2040
|
+
} else {
|
|
2041
|
+
this.#documentFrequency.set(term, (this.#documentFrequency.get(term) || 1) - 1);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
this.#documents.delete(id);
|
|
2046
|
+
this.#docCount--;
|
|
2047
|
+
this.#updateAvgDocLength();
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2050
|
+
/**
|
|
2051
|
+
* Clear all documents from the index
|
|
2052
|
+
*/
|
|
2053
|
+
clear() {
|
|
2054
|
+
this.#documents.clear();
|
|
2055
|
+
this.#invertedIndex.clear();
|
|
2056
|
+
this.#documentFrequency.clear();
|
|
2057
|
+
this.#docCount = 0;
|
|
2058
|
+
this.#avgDocLength = 0;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Search for documents matching the query
|
|
2062
|
+
*/
|
|
2063
|
+
search(query, topK = 10, minScore = 0) {
|
|
2064
|
+
const queryTokens = tokenize(query, this.#tokenizeOptions);
|
|
2065
|
+
if (queryTokens.length === 0 || this.#docCount === 0) {
|
|
2066
|
+
return [];
|
|
2067
|
+
}
|
|
2068
|
+
const scores = /* @__PURE__ */ new Map();
|
|
2069
|
+
for (const queryTerm of queryTokens) {
|
|
2070
|
+
const docIds = this.#invertedIndex.get(queryTerm);
|
|
2071
|
+
if (!docIds) {
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
const df = this.#documentFrequency.get(queryTerm) || 0;
|
|
2075
|
+
const idf = this.#computeIDF(df);
|
|
2076
|
+
for (const docId of docIds) {
|
|
2077
|
+
const doc = this.#documents.get(docId);
|
|
2078
|
+
const tf = doc.termFrequencies.get(queryTerm) || 0;
|
|
2079
|
+
const termScore = this.#computeTermScore(tf, doc.length, idf);
|
|
2080
|
+
scores.set(docId, (scores.get(docId) || 0) + termScore);
|
|
2081
|
+
}
|
|
1128
2082
|
}
|
|
1129
|
-
|
|
2083
|
+
const results = [];
|
|
2084
|
+
for (const [docId, score] of scores.entries()) {
|
|
2085
|
+
if (score >= minScore) {
|
|
2086
|
+
const doc = this.#documents.get(docId);
|
|
2087
|
+
results.push({
|
|
2088
|
+
id: docId,
|
|
2089
|
+
content: doc.content,
|
|
2090
|
+
score,
|
|
2091
|
+
metadata: doc.metadata
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
results.sort((a, b) => b.score - a.score);
|
|
2096
|
+
return results.slice(0, topK);
|
|
1130
2097
|
}
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// src/workspace/filesystem/local-filesystem.ts
|
|
1134
|
-
var LocalFilesystem = class extends MastraFilesystem {
|
|
1135
|
-
id;
|
|
1136
|
-
name = "LocalFilesystem";
|
|
1137
|
-
provider = "local";
|
|
1138
|
-
readOnly;
|
|
1139
|
-
status = "stopped";
|
|
1140
|
-
_basePath;
|
|
1141
|
-
_contained;
|
|
1142
2098
|
/**
|
|
1143
|
-
*
|
|
1144
|
-
* Useful for understanding how workspace paths map to disk paths.
|
|
2099
|
+
* Get a document by ID
|
|
1145
2100
|
*/
|
|
1146
|
-
get
|
|
1147
|
-
return this.
|
|
1148
|
-
}
|
|
1149
|
-
constructor(options) {
|
|
1150
|
-
super({ name: "LocalFilesystem" });
|
|
1151
|
-
this.id = options.id ?? this.generateId();
|
|
1152
|
-
this._basePath = nodePath__namespace.resolve(options.basePath);
|
|
1153
|
-
this._contained = options.contained ?? true;
|
|
1154
|
-
this.readOnly = options.readOnly;
|
|
1155
|
-
}
|
|
1156
|
-
generateId() {
|
|
1157
|
-
return `local-fs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2101
|
+
get(id) {
|
|
2102
|
+
return this.#documents.get(id);
|
|
1158
2103
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
2104
|
+
/**
|
|
2105
|
+
* Check if a document exists in the index
|
|
2106
|
+
*/
|
|
2107
|
+
has(id) {
|
|
2108
|
+
return this.#documents.has(id);
|
|
1163
2109
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
const relative2 = nodePath__namespace.relative(this._basePath, absolutePath);
|
|
1170
|
-
if (relative2.startsWith("..") || nodePath__namespace.isAbsolute(relative2)) {
|
|
1171
|
-
throw new PermissionError(inputPath, "access");
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
return absolutePath;
|
|
2110
|
+
/**
|
|
2111
|
+
* Get the number of documents in the index
|
|
2112
|
+
*/
|
|
2113
|
+
get size() {
|
|
2114
|
+
return this.#docCount;
|
|
1175
2115
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
2116
|
+
/**
|
|
2117
|
+
* Get all document IDs
|
|
2118
|
+
*/
|
|
2119
|
+
get documentIds() {
|
|
2120
|
+
return Array.from(this.#documents.keys());
|
|
1178
2121
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
2122
|
+
/**
|
|
2123
|
+
* Serialize the index to a JSON-compatible object
|
|
2124
|
+
*/
|
|
2125
|
+
serialize() {
|
|
2126
|
+
const documents = [];
|
|
2127
|
+
for (const [id, doc] of this.#documents.entries()) {
|
|
2128
|
+
documents.push({
|
|
2129
|
+
id,
|
|
2130
|
+
content: doc.content,
|
|
2131
|
+
tokens: doc.tokens,
|
|
2132
|
+
termFrequencies: Object.fromEntries(doc.termFrequencies),
|
|
2133
|
+
length: doc.length,
|
|
2134
|
+
metadata: doc.metadata
|
|
2135
|
+
});
|
|
1182
2136
|
}
|
|
2137
|
+
return {
|
|
2138
|
+
k1: this.k1,
|
|
2139
|
+
b: this.b,
|
|
2140
|
+
documents,
|
|
2141
|
+
avgDocLength: this.#avgDocLength
|
|
2142
|
+
};
|
|
1183
2143
|
}
|
|
1184
2144
|
/**
|
|
1185
|
-
*
|
|
1186
|
-
* Uses realpath to resolve symlinks and check the actual target.
|
|
2145
|
+
* Deserialize an index from a JSON object
|
|
1187
2146
|
*/
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
while (true) {
|
|
1206
|
-
const nextParent = nodePath__namespace.dirname(parentPath);
|
|
1207
|
-
if (nextParent === parentPath) {
|
|
1208
|
-
throw new DirectoryNotFoundError(absolutePath);
|
|
1209
|
-
}
|
|
1210
|
-
parentPath = nextParent;
|
|
1211
|
-
try {
|
|
1212
|
-
targetReal = await fs2__namespace.realpath(parentPath);
|
|
1213
|
-
break;
|
|
1214
|
-
} catch (parentError) {
|
|
1215
|
-
if (!isEnoentError(parentError)) {
|
|
1216
|
-
throw parentError;
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
2147
|
+
static deserialize(data, tokenizeOptions = {}) {
|
|
2148
|
+
const index = new _BM25Index({ k1: data.k1, b: data.b }, tokenizeOptions);
|
|
2149
|
+
for (const doc of data.documents) {
|
|
2150
|
+
const termFrequencies = new Map(Object.entries(doc.termFrequencies));
|
|
2151
|
+
const document = {
|
|
2152
|
+
id: doc.id,
|
|
2153
|
+
content: doc.content,
|
|
2154
|
+
tokens: doc.tokens,
|
|
2155
|
+
termFrequencies,
|
|
2156
|
+
length: doc.length,
|
|
2157
|
+
metadata: doc.metadata
|
|
2158
|
+
};
|
|
2159
|
+
index.#documents.set(doc.id, document);
|
|
2160
|
+
index.#docCount++;
|
|
2161
|
+
for (const term of termFrequencies.keys()) {
|
|
2162
|
+
if (!index.#invertedIndex.has(term)) {
|
|
2163
|
+
index.#invertedIndex.set(term, /* @__PURE__ */ new Set());
|
|
1219
2164
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
2165
|
+
index.#invertedIndex.get(term).add(doc.id);
|
|
2166
|
+
index.#documentFrequency.set(term, (index.#documentFrequency.get(term) || 0) + 1);
|
|
1222
2167
|
}
|
|
1223
2168
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
2169
|
+
index.#avgDocLength = data.avgDocLength;
|
|
2170
|
+
return index;
|
|
1227
2171
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
2172
|
+
/**
|
|
2173
|
+
* Update average document length after add/remove operations
|
|
2174
|
+
*/
|
|
2175
|
+
#updateAvgDocLength() {
|
|
2176
|
+
if (this.#docCount === 0) {
|
|
2177
|
+
this.#avgDocLength = 0;
|
|
2178
|
+
return;
|
|
1231
2179
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
await this.ensureInitialized();
|
|
1236
|
-
const absolutePath = this.resolvePath(inputPath);
|
|
1237
|
-
await this.assertPathContained(absolutePath);
|
|
1238
|
-
try {
|
|
1239
|
-
const stats = await fs2__namespace.stat(absolutePath);
|
|
1240
|
-
if (stats.isDirectory()) {
|
|
1241
|
-
throw new IsDirectoryError(inputPath);
|
|
1242
|
-
}
|
|
1243
|
-
if (options?.encoding) {
|
|
1244
|
-
return await fs2__namespace.readFile(absolutePath, { encoding: options.encoding });
|
|
1245
|
-
}
|
|
1246
|
-
return await fs2__namespace.readFile(absolutePath);
|
|
1247
|
-
} catch (error) {
|
|
1248
|
-
if (error instanceof IsDirectoryError) throw error;
|
|
1249
|
-
if (isEnoentError(error)) {
|
|
1250
|
-
throw new FileNotFoundError(inputPath);
|
|
1251
|
-
}
|
|
1252
|
-
throw error;
|
|
2180
|
+
let totalLength = 0;
|
|
2181
|
+
for (const doc of this.#documents.values()) {
|
|
2182
|
+
totalLength += doc.length;
|
|
1253
2183
|
}
|
|
2184
|
+
this.#avgDocLength = totalLength / this.#docCount;
|
|
1254
2185
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
this.
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
2186
|
+
/**
|
|
2187
|
+
* Compute IDF (Inverse Document Frequency) for a term
|
|
2188
|
+
*/
|
|
2189
|
+
#computeIDF(df) {
|
|
2190
|
+
return Math.log((this.#docCount - df + 0.5) / (df + 0.5) + 1);
|
|
2191
|
+
}
|
|
2192
|
+
/**
|
|
2193
|
+
* Compute the BM25 score component for a single term
|
|
2194
|
+
*/
|
|
2195
|
+
#computeTermScore(tf, docLength, idf) {
|
|
2196
|
+
const numerator = tf * (this.k1 + 1);
|
|
2197
|
+
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.#avgDocLength));
|
|
2198
|
+
return idf * (numerator / denominator);
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
|
|
2202
|
+
// src/workspace/search/search-engine.ts
|
|
2203
|
+
var SearchEngine = class {
|
|
2204
|
+
/** BM25 index for keyword search */
|
|
2205
|
+
#bm25Index;
|
|
2206
|
+
/** Tokenization options (stored for lineRange computation) */
|
|
2207
|
+
#tokenizeOptions;
|
|
2208
|
+
/** Vector configuration */
|
|
2209
|
+
#vectorConfig;
|
|
2210
|
+
/** Whether to use lazy vector indexing */
|
|
2211
|
+
#lazyVectorIndex;
|
|
2212
|
+
/** Documents pending vector indexing (for lazy mode) */
|
|
2213
|
+
#pendingVectorDocs = [];
|
|
2214
|
+
/** Whether vector index has been built (for lazy mode) */
|
|
2215
|
+
#vectorIndexBuilt = false;
|
|
2216
|
+
constructor(config = {}) {
|
|
2217
|
+
if (config.bm25 !== void 0) {
|
|
2218
|
+
this.#tokenizeOptions = config.bm25.tokenize;
|
|
2219
|
+
this.#bm25Index = new BM25Index(config.bm25.bm25, this.#tokenizeOptions);
|
|
1277
2220
|
}
|
|
1278
|
-
if (
|
|
1279
|
-
|
|
1280
|
-
await fs2__namespace.mkdir(dir, { recursive: true });
|
|
2221
|
+
if (config.vector) {
|
|
2222
|
+
this.#vectorConfig = config.vector;
|
|
1281
2223
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
2224
|
+
this.#lazyVectorIndex = config.lazyVectorIndex ?? false;
|
|
2225
|
+
}
|
|
2226
|
+
// ===========================================================================
|
|
2227
|
+
// Public API
|
|
2228
|
+
// ===========================================================================
|
|
2229
|
+
/**
|
|
2230
|
+
* Index a document for search
|
|
2231
|
+
*/
|
|
2232
|
+
async index(doc) {
|
|
2233
|
+
const metadata = {
|
|
2234
|
+
...doc.metadata
|
|
2235
|
+
};
|
|
2236
|
+
if (doc.startLineOffset !== void 0) {
|
|
2237
|
+
metadata._startLineOffset = doc.startLineOffset;
|
|
2238
|
+
}
|
|
2239
|
+
if (this.#bm25Index) {
|
|
2240
|
+
this.#bm25Index.add(doc.id, doc.content, metadata);
|
|
2241
|
+
}
|
|
2242
|
+
if (this.#vectorConfig) {
|
|
2243
|
+
const docWithMergedMetadata = { ...doc, metadata };
|
|
2244
|
+
if (this.#lazyVectorIndex) {
|
|
2245
|
+
this.#pendingVectorDocs.push(docWithMergedMetadata);
|
|
2246
|
+
this.#vectorIndexBuilt = false;
|
|
2247
|
+
} else {
|
|
2248
|
+
await this.#indexVector(docWithMergedMetadata);
|
|
1288
2249
|
}
|
|
1289
|
-
throw error;
|
|
1290
2250
|
}
|
|
1291
2251
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const dir = nodePath__namespace.dirname(absolutePath);
|
|
1300
|
-
await fs2__namespace.mkdir(dir, { recursive: true });
|
|
1301
|
-
await fs2__namespace.appendFile(absolutePath, this.toBuffer(content));
|
|
2252
|
+
/**
|
|
2253
|
+
* Index multiple documents
|
|
2254
|
+
*/
|
|
2255
|
+
async indexMany(docs) {
|
|
2256
|
+
for (const doc of docs) {
|
|
2257
|
+
await this.index(doc);
|
|
2258
|
+
}
|
|
1302
2259
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
2260
|
+
/**
|
|
2261
|
+
* Remove a document from the index
|
|
2262
|
+
*/
|
|
2263
|
+
async remove(id) {
|
|
2264
|
+
if (this.#bm25Index) {
|
|
2265
|
+
this.#bm25Index.remove(id);
|
|
2266
|
+
}
|
|
2267
|
+
if (this.#vectorConfig) {
|
|
2268
|
+
try {
|
|
2269
|
+
await this.#vectorConfig.vectorStore.deleteVector({
|
|
2270
|
+
indexName: this.#vectorConfig.indexName,
|
|
2271
|
+
id
|
|
2272
|
+
});
|
|
2273
|
+
} catch {
|
|
1313
2274
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
if (error instanceof IsDirectoryError) throw error;
|
|
1317
|
-
if (isEnoentError(error)) {
|
|
1318
|
-
if (!options?.force) {
|
|
1319
|
-
throw new FileNotFoundError(inputPath);
|
|
1320
|
-
}
|
|
1321
|
-
} else {
|
|
1322
|
-
throw error;
|
|
2275
|
+
if (this.#lazyVectorIndex) {
|
|
2276
|
+
this.#pendingVectorDocs = this.#pendingVectorDocs.filter((d) => d.id !== id);
|
|
1323
2277
|
}
|
|
1324
2278
|
}
|
|
1325
2279
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
await this.assertPathContained(srcPath);
|
|
1333
|
-
await this.assertPathContained(destPath);
|
|
1334
|
-
try {
|
|
1335
|
-
const stats = await fs2__namespace.stat(srcPath);
|
|
1336
|
-
if (stats.isDirectory()) {
|
|
1337
|
-
if (!options?.recursive) {
|
|
1338
|
-
throw new IsDirectoryError(src);
|
|
1339
|
-
}
|
|
1340
|
-
await this.copyDirectory(srcPath, destPath, options);
|
|
1341
|
-
} else {
|
|
1342
|
-
await fs2__namespace.mkdir(nodePath__namespace.dirname(destPath), { recursive: true });
|
|
1343
|
-
const copyFlags = options?.overwrite === false ? fs.constants.COPYFILE_EXCL : 0;
|
|
1344
|
-
try {
|
|
1345
|
-
await fs2__namespace.copyFile(srcPath, destPath, copyFlags);
|
|
1346
|
-
} catch (error) {
|
|
1347
|
-
if (options?.overwrite === false && isEexistError(error)) {
|
|
1348
|
-
throw new FileExistsError(dest);
|
|
1349
|
-
}
|
|
1350
|
-
throw error;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
} catch (error) {
|
|
1354
|
-
if (error instanceof IsDirectoryError || error instanceof FileExistsError) throw error;
|
|
1355
|
-
if (isEnoentError(error)) {
|
|
1356
|
-
throw new FileNotFoundError(src);
|
|
1357
|
-
}
|
|
1358
|
-
throw error;
|
|
2280
|
+
/**
|
|
2281
|
+
* Clear all indexed documents
|
|
2282
|
+
*/
|
|
2283
|
+
clear() {
|
|
2284
|
+
if (this.#bm25Index) {
|
|
2285
|
+
this.#bm25Index.clear();
|
|
1359
2286
|
}
|
|
2287
|
+
this.#pendingVectorDocs = [];
|
|
2288
|
+
this.#vectorIndexBuilt = false;
|
|
1360
2289
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
await this.assertPathContained(destEntry);
|
|
1370
|
-
if (entry.isDirectory()) {
|
|
1371
|
-
await this.copyDirectory(srcEntry, destEntry, options);
|
|
1372
|
-
} else {
|
|
1373
|
-
const copyFlags = options?.overwrite === false ? fs.constants.COPYFILE_EXCL : 0;
|
|
1374
|
-
try {
|
|
1375
|
-
await fs2__namespace.copyFile(srcEntry, destEntry, copyFlags);
|
|
1376
|
-
} catch (error) {
|
|
1377
|
-
if (options?.overwrite === false && isEexistError(error)) {
|
|
1378
|
-
continue;
|
|
1379
|
-
}
|
|
1380
|
-
throw error;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Search for documents
|
|
2292
|
+
*/
|
|
2293
|
+
async search(query, options = {}) {
|
|
2294
|
+
const { topK = 10, minScore, mode, vectorWeight = 0.5, filter } = options;
|
|
2295
|
+
const effectiveMode = this.#determineSearchMode(mode);
|
|
2296
|
+
if (effectiveMode === "bm25") {
|
|
2297
|
+
return this.#searchBM25(query, topK, minScore);
|
|
1383
2298
|
}
|
|
2299
|
+
if (effectiveMode === "vector") {
|
|
2300
|
+
return this.#searchVector(query, topK, minScore, filter);
|
|
2301
|
+
}
|
|
2302
|
+
return this.#searchHybrid(query, topK, minScore, vectorWeight, filter);
|
|
1384
2303
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
2304
|
+
/**
|
|
2305
|
+
* Check if BM25 search is available
|
|
2306
|
+
*/
|
|
2307
|
+
get canBM25() {
|
|
2308
|
+
return !!this.#bm25Index;
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Check if vector search is available
|
|
2312
|
+
*/
|
|
2313
|
+
get canVector() {
|
|
2314
|
+
return !!this.#vectorConfig;
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Check if hybrid search is available
|
|
2318
|
+
*/
|
|
2319
|
+
get canHybrid() {
|
|
2320
|
+
return this.canBM25 && this.canVector;
|
|
2321
|
+
}
|
|
2322
|
+
/**
|
|
2323
|
+
* Get the BM25 index (for serialization/debugging)
|
|
2324
|
+
*/
|
|
2325
|
+
get bm25Index() {
|
|
2326
|
+
return this.#bm25Index;
|
|
2327
|
+
}
|
|
2328
|
+
// ===========================================================================
|
|
2329
|
+
// Private Methods
|
|
2330
|
+
// ===========================================================================
|
|
2331
|
+
/**
|
|
2332
|
+
* Determine the effective search mode
|
|
2333
|
+
*/
|
|
2334
|
+
#determineSearchMode(requestedMode) {
|
|
2335
|
+
if (requestedMode) {
|
|
2336
|
+
if (requestedMode === "vector" && !this.canVector) {
|
|
2337
|
+
throw new Error("Vector search requires vector configuration.");
|
|
1399
2338
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
} catch (error) {
|
|
1403
|
-
const code = error.code;
|
|
1404
|
-
if (code !== "EXDEV") {
|
|
1405
|
-
throw error;
|
|
1406
|
-
}
|
|
1407
|
-
await this.copyFile(src, dest, options);
|
|
1408
|
-
await fs2__namespace.rm(srcPath, { recursive: true, force: true });
|
|
2339
|
+
if (requestedMode === "bm25" && !this.canBM25) {
|
|
2340
|
+
throw new Error("BM25 search requires BM25 configuration.");
|
|
1409
2341
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (isEnoentError(error)) {
|
|
1413
|
-
throw new FileNotFoundError(src);
|
|
2342
|
+
if (requestedMode === "hybrid" && !this.canHybrid) {
|
|
2343
|
+
throw new Error("Hybrid search requires both vector and BM25 configuration.");
|
|
1414
2344
|
}
|
|
1415
|
-
|
|
2345
|
+
return requestedMode;
|
|
2346
|
+
}
|
|
2347
|
+
if (this.canHybrid) {
|
|
2348
|
+
return "hybrid";
|
|
2349
|
+
}
|
|
2350
|
+
if (this.canVector) {
|
|
2351
|
+
return "vector";
|
|
2352
|
+
}
|
|
2353
|
+
if (this.canBM25) {
|
|
2354
|
+
return "bm25";
|
|
2355
|
+
}
|
|
2356
|
+
throw new Error("No search configuration available. Provide bm25 or vector config.");
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Index a single document in the vector store
|
|
2360
|
+
*/
|
|
2361
|
+
async #indexVector(doc) {
|
|
2362
|
+
if (!this.#vectorConfig) return;
|
|
2363
|
+
const { vectorStore, embedder, indexName } = this.#vectorConfig;
|
|
2364
|
+
const embedding = await embedder(doc.content);
|
|
2365
|
+
await vectorStore.upsert({
|
|
2366
|
+
indexName,
|
|
2367
|
+
vectors: [embedding],
|
|
2368
|
+
metadata: [
|
|
2369
|
+
{
|
|
2370
|
+
id: doc.id,
|
|
2371
|
+
text: doc.content,
|
|
2372
|
+
...doc.metadata
|
|
2373
|
+
}
|
|
2374
|
+
],
|
|
2375
|
+
ids: [doc.id]
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Ensure vector index is built (for lazy mode)
|
|
2380
|
+
*/
|
|
2381
|
+
async #ensureVectorIndex() {
|
|
2382
|
+
if (!this.#lazyVectorIndex || this.#vectorIndexBuilt || this.#pendingVectorDocs.length === 0) {
|
|
2383
|
+
return;
|
|
1416
2384
|
}
|
|
2385
|
+
for (const doc of this.#pendingVectorDocs) {
|
|
2386
|
+
await this.#indexVector(doc);
|
|
2387
|
+
}
|
|
2388
|
+
this.#pendingVectorDocs = [];
|
|
2389
|
+
this.#vectorIndexBuilt = true;
|
|
1417
2390
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
try {
|
|
1425
|
-
await fs2__namespace.mkdir(absolutePath, { recursive: options?.recursive ?? true });
|
|
1426
|
-
} catch (error) {
|
|
1427
|
-
if (isEexistError(error)) {
|
|
1428
|
-
const stats = await fs2__namespace.stat(absolutePath);
|
|
1429
|
-
if (!stats.isDirectory()) {
|
|
1430
|
-
throw new FileExistsError(inputPath);
|
|
1431
|
-
}
|
|
1432
|
-
} else if (isEnoentError(error)) {
|
|
1433
|
-
const parentPath = nodePath__namespace.dirname(inputPath);
|
|
1434
|
-
throw new DirectoryNotFoundError(parentPath);
|
|
1435
|
-
} else {
|
|
1436
|
-
throw error;
|
|
1437
|
-
}
|
|
2391
|
+
/**
|
|
2392
|
+
* BM25 keyword search
|
|
2393
|
+
*/
|
|
2394
|
+
#searchBM25(query, topK, minScore) {
|
|
2395
|
+
if (!this.#bm25Index) {
|
|
2396
|
+
throw new Error("BM25 search requires BM25 configuration.");
|
|
1438
2397
|
}
|
|
2398
|
+
const results = this.#bm25Index.search(query, topK, minScore);
|
|
2399
|
+
const queryTokens = tokenize(query, this.#tokenizeOptions);
|
|
2400
|
+
return results.map((result) => {
|
|
2401
|
+
const rawLineRange = findLineRange(result.content, queryTokens, this.#tokenizeOptions);
|
|
2402
|
+
const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);
|
|
2403
|
+
const { _startLineOffset, ...cleanMetadata } = result.metadata ?? {};
|
|
2404
|
+
return {
|
|
2405
|
+
id: result.id,
|
|
2406
|
+
content: result.content,
|
|
2407
|
+
score: result.score,
|
|
2408
|
+
lineRange,
|
|
2409
|
+
metadata: Object.keys(cleanMetadata).length > 0 ? cleanMetadata : void 0,
|
|
2410
|
+
scoreDetails: { bm25: result.score }
|
|
2411
|
+
};
|
|
2412
|
+
});
|
|
1439
2413
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
if (error instanceof NotDirectoryError || error instanceof DirectoryNotEmptyError) {
|
|
1462
|
-
throw error;
|
|
1463
|
-
}
|
|
1464
|
-
if (isEnoentError(error)) {
|
|
1465
|
-
if (!options?.force) {
|
|
1466
|
-
throw new DirectoryNotFoundError(inputPath);
|
|
1467
|
-
}
|
|
1468
|
-
} else {
|
|
1469
|
-
throw error;
|
|
2414
|
+
/**
|
|
2415
|
+
* Vector semantic search
|
|
2416
|
+
*/
|
|
2417
|
+
async #searchVector(query, topK, minScore, filter) {
|
|
2418
|
+
if (!this.#vectorConfig) {
|
|
2419
|
+
throw new Error("Vector search requires vector configuration.");
|
|
2420
|
+
}
|
|
2421
|
+
await this.#ensureVectorIndex();
|
|
2422
|
+
const { vectorStore, embedder, indexName } = this.#vectorConfig;
|
|
2423
|
+
const queryEmbedding = await embedder(query);
|
|
2424
|
+
const vectorResults = await vectorStore.query({
|
|
2425
|
+
indexName,
|
|
2426
|
+
queryVector: queryEmbedding,
|
|
2427
|
+
topK,
|
|
2428
|
+
filter
|
|
2429
|
+
});
|
|
2430
|
+
const queryTokens = tokenize(query, this.#tokenizeOptions);
|
|
2431
|
+
const results = [];
|
|
2432
|
+
for (const result of vectorResults) {
|
|
2433
|
+
if (minScore !== void 0 && result.score < minScore) {
|
|
2434
|
+
continue;
|
|
1470
2435
|
}
|
|
2436
|
+
const id = result.metadata?.id ?? result.id;
|
|
2437
|
+
const content = result.metadata?.text ?? "";
|
|
2438
|
+
const { id: _id, text: _text, _startLineOffset, ...restMetadata } = result.metadata ?? {};
|
|
2439
|
+
const rawLineRange = findLineRange(content, queryTokens, this.#tokenizeOptions);
|
|
2440
|
+
const lineRange = this.#adjustLineRange(rawLineRange, result.metadata);
|
|
2441
|
+
results.push({
|
|
2442
|
+
id,
|
|
2443
|
+
content,
|
|
2444
|
+
score: result.score,
|
|
2445
|
+
lineRange,
|
|
2446
|
+
metadata: Object.keys(restMetadata).length > 0 ? restMetadata : void 0,
|
|
2447
|
+
scoreDetails: { vector: result.score }
|
|
2448
|
+
});
|
|
1471
2449
|
}
|
|
2450
|
+
return results;
|
|
1472
2451
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
const fileEntry = {
|
|
1511
|
-
name: entry.name,
|
|
1512
|
-
type: resolvedType,
|
|
1513
|
-
isSymlink: isSymlink || void 0,
|
|
1514
|
-
symlinkTarget
|
|
1515
|
-
};
|
|
1516
|
-
if (resolvedType === "file" && !isSymlink) {
|
|
1517
|
-
try {
|
|
1518
|
-
const stat3 = await fs2__namespace.stat(entryPath);
|
|
1519
|
-
fileEntry.size = stat3.size;
|
|
1520
|
-
} catch {
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
result.push(fileEntry);
|
|
1524
|
-
if (options?.recursive && resolvedType === "directory") {
|
|
1525
|
-
const depth = options.maxDepth ?? 100;
|
|
1526
|
-
if (depth > 0) {
|
|
1527
|
-
const subEntries = await this.readdir(this.toRelativePath(entryPath), { ...options, maxDepth: depth - 1 });
|
|
1528
|
-
result.push(
|
|
1529
|
-
...subEntries.map((e) => ({
|
|
1530
|
-
...e,
|
|
1531
|
-
name: `${entry.name}/${e.name}`
|
|
1532
|
-
}))
|
|
1533
|
-
);
|
|
1534
|
-
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Hybrid search combining vector and BM25 scores
|
|
2454
|
+
*/
|
|
2455
|
+
async #searchHybrid(query, topK, minScore, vectorWeight = 0.5, filter) {
|
|
2456
|
+
const expandedTopK = Math.min(topK * 2, 50);
|
|
2457
|
+
const [vectorResults, bm25Results] = await Promise.all([
|
|
2458
|
+
this.#searchVector(query, expandedTopK, void 0, filter),
|
|
2459
|
+
Promise.resolve(this.#searchBM25(query, expandedTopK, void 0))
|
|
2460
|
+
]);
|
|
2461
|
+
const normalizedBM25 = this.#normalizeBM25Scores(bm25Results);
|
|
2462
|
+
const bm25Map = /* @__PURE__ */ new Map();
|
|
2463
|
+
for (const result of normalizedBM25) {
|
|
2464
|
+
bm25Map.set(result.id, result);
|
|
2465
|
+
}
|
|
2466
|
+
const vectorMap = /* @__PURE__ */ new Map();
|
|
2467
|
+
for (const result of vectorResults) {
|
|
2468
|
+
vectorMap.set(result.id, result);
|
|
2469
|
+
}
|
|
2470
|
+
const combinedResults = /* @__PURE__ */ new Map();
|
|
2471
|
+
const allIds = /* @__PURE__ */ new Set([...vectorMap.keys(), ...bm25Map.keys()]);
|
|
2472
|
+
const bm25Weight = 1 - vectorWeight;
|
|
2473
|
+
for (const id of allIds) {
|
|
2474
|
+
const vectorResult = vectorMap.get(id);
|
|
2475
|
+
const bm25Result = bm25Map.get(id);
|
|
2476
|
+
const vectorScore = vectorResult?.scoreDetails?.vector ?? 0;
|
|
2477
|
+
const bm25Score = bm25Result?.score ?? 0;
|
|
2478
|
+
const combinedScore = vectorWeight * vectorScore + bm25Weight * bm25Score;
|
|
2479
|
+
const baseResult = vectorResult ?? bm25Result;
|
|
2480
|
+
combinedResults.set(id, {
|
|
2481
|
+
id,
|
|
2482
|
+
content: baseResult.content,
|
|
2483
|
+
score: combinedScore,
|
|
2484
|
+
lineRange: bm25Result?.lineRange ?? vectorResult?.lineRange,
|
|
2485
|
+
metadata: baseResult.metadata,
|
|
2486
|
+
scoreDetails: {
|
|
2487
|
+
vector: vectorResult?.scoreDetails?.vector,
|
|
2488
|
+
bm25: bm25Result?.scoreDetails?.bm25
|
|
1535
2489
|
}
|
|
1536
|
-
}
|
|
1537
|
-
return result;
|
|
1538
|
-
} catch (error) {
|
|
1539
|
-
if (error instanceof NotDirectoryError) throw error;
|
|
1540
|
-
if (isEnoentError(error)) {
|
|
1541
|
-
throw new DirectoryNotFoundError(inputPath);
|
|
1542
|
-
}
|
|
1543
|
-
throw error;
|
|
2490
|
+
});
|
|
1544
2491
|
}
|
|
2492
|
+
let results = Array.from(combinedResults.values());
|
|
2493
|
+
results.sort((a, b) => b.score - a.score);
|
|
2494
|
+
if (minScore !== void 0) {
|
|
2495
|
+
results = results.filter((r) => r.score >= minScore);
|
|
2496
|
+
}
|
|
2497
|
+
return results.slice(0, topK);
|
|
1545
2498
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2499
|
+
/**
|
|
2500
|
+
* Normalize BM25 scores to 0-1 range using min-max normalization
|
|
2501
|
+
*/
|
|
2502
|
+
#normalizeBM25Scores(results) {
|
|
2503
|
+
if (results.length === 0) return results;
|
|
2504
|
+
const scores = results.map((r) => r.scoreDetails?.bm25 ?? r.score);
|
|
2505
|
+
const maxScore = Math.max(...scores);
|
|
2506
|
+
const minScore = Math.min(...scores);
|
|
2507
|
+
const range = maxScore - minScore;
|
|
2508
|
+
if (range === 0) {
|
|
2509
|
+
return results.map((r) => ({ ...r, score: 1 }));
|
|
2510
|
+
}
|
|
2511
|
+
return results.map((r) => ({
|
|
2512
|
+
...r,
|
|
2513
|
+
score: ((r.scoreDetails?.bm25 ?? r.score) - minScore) / range
|
|
2514
|
+
}));
|
|
1551
2515
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
2516
|
+
/**
|
|
2517
|
+
* Adjust line range for chunked documents.
|
|
2518
|
+
* If the document has a _startLineOffset in metadata, adjust the line range
|
|
2519
|
+
* to reflect the original document's line numbers.
|
|
2520
|
+
*/
|
|
2521
|
+
#adjustLineRange(lineRange, metadata) {
|
|
2522
|
+
if (!lineRange) return void 0;
|
|
2523
|
+
const startLineOffset = metadata?._startLineOffset;
|
|
2524
|
+
if (typeof startLineOffset !== "number") {
|
|
2525
|
+
return lineRange;
|
|
2526
|
+
}
|
|
1557
2527
|
return {
|
|
1558
|
-
|
|
1559
|
-
|
|
2528
|
+
start: lineRange.start + startLineOffset - 1,
|
|
2529
|
+
end: lineRange.end + startLineOffset - 1
|
|
1560
2530
|
};
|
|
1561
2531
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2532
|
+
};
|
|
2533
|
+
|
|
2534
|
+
// src/workspace/skills/schemas.ts
|
|
2535
|
+
var SKILL_LIMITS = {
|
|
2536
|
+
/** Recommended max tokens for instructions */
|
|
2537
|
+
MAX_INSTRUCTION_TOKENS: 5e3,
|
|
2538
|
+
/** Recommended max lines for SKILL.md */
|
|
2539
|
+
MAX_INSTRUCTION_LINES: 500,
|
|
2540
|
+
/** Max characters for name field */
|
|
2541
|
+
MAX_NAME_LENGTH: 64,
|
|
2542
|
+
/** Max characters for description field */
|
|
2543
|
+
MAX_DESCRIPTION_LENGTH: 1024};
|
|
2544
|
+
function validateSkillName(name) {
|
|
2545
|
+
const errors = [];
|
|
2546
|
+
const fieldPath = "name";
|
|
2547
|
+
if (typeof name !== "string") {
|
|
2548
|
+
errors.push(`${fieldPath}: Expected string, received ${typeof name}`);
|
|
2549
|
+
return errors;
|
|
1574
2550
|
}
|
|
1575
|
-
|
|
2551
|
+
if (name.length === 0) {
|
|
2552
|
+
errors.push(`${fieldPath}: Skill name cannot be empty`);
|
|
2553
|
+
return errors;
|
|
1576
2554
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
id: this.id,
|
|
1580
|
-
name: this.name,
|
|
1581
|
-
provider: this.provider,
|
|
1582
|
-
readOnly: this.readOnly,
|
|
1583
|
-
basePath: this.basePath,
|
|
1584
|
-
status: this.status
|
|
1585
|
-
};
|
|
2555
|
+
if (name.length > SKILL_LIMITS.MAX_NAME_LENGTH) {
|
|
2556
|
+
errors.push(`${fieldPath}: Skill name must be ${SKILL_LIMITS.MAX_NAME_LENGTH} characters or less`);
|
|
1586
2557
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
2558
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
2559
|
+
errors.push(`${fieldPath}: Skill name must contain only lowercase letters, numbers, and hyphens`);
|
|
1589
2560
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
records = /* @__PURE__ */ new Map();
|
|
1593
|
-
recordRead(path4, modifiedAt) {
|
|
1594
|
-
const normalizedPath = this.normalizePath(path4);
|
|
1595
|
-
this.records.set(normalizedPath, {
|
|
1596
|
-
path: normalizedPath,
|
|
1597
|
-
readAt: /* @__PURE__ */ new Date(),
|
|
1598
|
-
modifiedAtRead: modifiedAt
|
|
1599
|
-
});
|
|
2561
|
+
if (name.startsWith("-") || name.endsWith("-")) {
|
|
2562
|
+
errors.push(`${fieldPath}: Skill name must not start or end with a hyphen`);
|
|
1600
2563
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
2564
|
+
if (name.includes("--")) {
|
|
2565
|
+
errors.push(`${fieldPath}: Skill name must not contain consecutive hyphens`);
|
|
1603
2566
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
if (currentModifiedAt.getTime() > record.modifiedAtRead.getTime()) {
|
|
1613
|
-
return {
|
|
1614
|
-
needsReRead: true,
|
|
1615
|
-
reason: `File "${path4}" was modified since last read (read at: ${record.modifiedAtRead.toISOString()}, current: ${currentModifiedAt.toISOString()}). Please re-read the file to get the latest contents.`
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
return { needsReRead: false };
|
|
2567
|
+
return errors;
|
|
2568
|
+
}
|
|
2569
|
+
function validateSkillDescription(description) {
|
|
2570
|
+
const errors = [];
|
|
2571
|
+
const fieldPath = "description";
|
|
2572
|
+
if (typeof description !== "string") {
|
|
2573
|
+
errors.push(`${fieldPath}: Expected string, received ${typeof description}`);
|
|
2574
|
+
return errors;
|
|
1619
2575
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
2576
|
+
if (description.length === 0) {
|
|
2577
|
+
errors.push(`${fieldPath}: Skill description cannot be empty`);
|
|
2578
|
+
return errors;
|
|
1622
2579
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
2580
|
+
if (description.length > SKILL_LIMITS.MAX_DESCRIPTION_LENGTH) {
|
|
2581
|
+
errors.push(`${fieldPath}: Skill description must be ${SKILL_LIMITS.MAX_DESCRIPTION_LENGTH} characters or less`);
|
|
1625
2582
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
return normalized.replace(/\/$/, "") || "/";
|
|
2583
|
+
if (description.trim().length === 0) {
|
|
2584
|
+
errors.push(`${fieldPath}: Skill description cannot be only whitespace`);
|
|
1629
2585
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2586
|
+
return errors;
|
|
2587
|
+
}
|
|
2588
|
+
function validateSkillLicense(license) {
|
|
2589
|
+
const errors = [];
|
|
2590
|
+
const fieldPath = "license";
|
|
2591
|
+
if (license === void 0 || license === null) {
|
|
2592
|
+
return errors;
|
|
2593
|
+
}
|
|
2594
|
+
if (typeof license !== "string") {
|
|
2595
|
+
errors.push(`${fieldPath}: Expected string, received ${typeof license}`);
|
|
2596
|
+
}
|
|
2597
|
+
return errors;
|
|
2598
|
+
}
|
|
2599
|
+
function validateSkillCompatibility(_compatibility) {
|
|
2600
|
+
return [];
|
|
2601
|
+
}
|
|
2602
|
+
function validateSkillMetadataField(metadata) {
|
|
2603
|
+
const errors = [];
|
|
2604
|
+
const fieldPath = "metadata";
|
|
2605
|
+
if (metadata === void 0 || metadata === null) {
|
|
2606
|
+
return errors;
|
|
2607
|
+
}
|
|
2608
|
+
if (typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
2609
|
+
errors.push(`${fieldPath}: Expected object, received ${Array.isArray(metadata) ? "array" : typeof metadata}`);
|
|
2610
|
+
return errors;
|
|
2611
|
+
}
|
|
2612
|
+
return errors;
|
|
2613
|
+
}
|
|
2614
|
+
function estimateTokens(text) {
|
|
2615
|
+
const words = text.split(/\s+/).filter(Boolean).length;
|
|
2616
|
+
return Math.ceil(words * 1.3);
|
|
2617
|
+
}
|
|
2618
|
+
function countLines(text) {
|
|
2619
|
+
return text.split("\n").length;
|
|
2620
|
+
}
|
|
2621
|
+
function validateSkillMetadata(metadata, dirName, instructions) {
|
|
2622
|
+
const errors = [];
|
|
2623
|
+
const warnings = [];
|
|
2624
|
+
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
|
|
2625
|
+
errors.push(
|
|
2626
|
+
`Expected object, received ${metadata === null ? "null" : Array.isArray(metadata) ? "array" : typeof metadata}`
|
|
2627
|
+
);
|
|
2628
|
+
return { valid: false, errors, warnings };
|
|
2629
|
+
}
|
|
2630
|
+
const data = metadata;
|
|
2631
|
+
errors.push(...validateSkillName(data.name));
|
|
2632
|
+
errors.push(...validateSkillDescription(data.description));
|
|
2633
|
+
errors.push(...validateSkillLicense(data.license));
|
|
2634
|
+
errors.push(...validateSkillCompatibility());
|
|
2635
|
+
errors.push(...validateSkillMetadataField(data.metadata));
|
|
2636
|
+
if (dirName && typeof data.name === "string" && data.name !== dirName) {
|
|
2637
|
+
errors.push(`Skill name "${data.name}" must match directory name "${dirName}"`);
|
|
2638
|
+
}
|
|
2639
|
+
if (instructions) {
|
|
2640
|
+
const lineCount = countLines(instructions);
|
|
2641
|
+
const tokenEstimate = estimateTokens(instructions);
|
|
2642
|
+
if (lineCount > SKILL_LIMITS.MAX_INSTRUCTION_LINES) {
|
|
2643
|
+
warnings.push(
|
|
2644
|
+
`Instructions have ${lineCount} lines (recommended: <${SKILL_LIMITS.MAX_INSTRUCTION_LINES}). Consider moving content to references/.`
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
if (tokenEstimate > SKILL_LIMITS.MAX_INSTRUCTION_TOKENS) {
|
|
2648
|
+
warnings.push(
|
|
2649
|
+
`Instructions have ~${tokenEstimate} estimated tokens (recommended: <${SKILL_LIMITS.MAX_INSTRUCTION_TOKENS}). Consider moving content to references/.`
|
|
2650
|
+
);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
return {
|
|
2654
|
+
valid: errors.length === 0,
|
|
2655
|
+
errors,
|
|
2656
|
+
warnings
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
1633
2659
|
var LocalSkillSource = class {
|
|
1634
2660
|
#basePath;
|
|
1635
2661
|
constructor(options = {}) {
|
|
@@ -2194,8 +3220,22 @@ var Workspace = class {
|
|
|
2194
3220
|
this.createdAt = /* @__PURE__ */ new Date();
|
|
2195
3221
|
this.lastAccessedAt = /* @__PURE__ */ new Date();
|
|
2196
3222
|
this._config = config;
|
|
2197
|
-
this._fs = config.filesystem;
|
|
2198
3223
|
this._sandbox = config.sandbox;
|
|
3224
|
+
if (config.mounts && Object.keys(config.mounts).length > 0) {
|
|
3225
|
+
if (config.filesystem) {
|
|
3226
|
+
throw new WorkspaceError('Cannot use both "filesystem" and "mounts"', "INVALID_CONFIG");
|
|
3227
|
+
}
|
|
3228
|
+
this._fs = new CompositeFilesystem({ mounts: config.mounts });
|
|
3229
|
+
if (this._sandbox?.mounts) {
|
|
3230
|
+
this._sandbox.mounts.setContext({ sandbox: this._sandbox, workspace: this });
|
|
3231
|
+
this._sandbox.mounts.add(config.mounts);
|
|
3232
|
+
if (config.onMount) {
|
|
3233
|
+
this._sandbox.mounts.setOnMount(config.onMount);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
} else {
|
|
3237
|
+
this._fs = config.filesystem;
|
|
3238
|
+
}
|
|
2199
3239
|
if (config.vectorStore && !config.embedder) {
|
|
2200
3240
|
throw new WorkspaceError("vectorStore requires an embedder", "INVALID_SEARCH_CONFIG");
|
|
2201
3241
|
}
|
|
@@ -2400,16 +3440,16 @@ var Workspace = class {
|
|
|
2400
3440
|
// ---------------------------------------------------------------------------
|
|
2401
3441
|
/**
|
|
2402
3442
|
* Initialize the workspace.
|
|
2403
|
-
* Starts the sandbox
|
|
3443
|
+
* Starts the sandbox, initializes the filesystem, and auto-mounts filesystems.
|
|
2404
3444
|
*/
|
|
2405
3445
|
async init() {
|
|
2406
3446
|
this._status = "initializing";
|
|
2407
3447
|
try {
|
|
2408
|
-
if (this._fs
|
|
2409
|
-
await this._fs
|
|
3448
|
+
if (this._fs) {
|
|
3449
|
+
await callLifecycle(this._fs, "init");
|
|
2410
3450
|
}
|
|
2411
|
-
if (this._sandbox
|
|
2412
|
-
await this._sandbox
|
|
3451
|
+
if (this._sandbox) {
|
|
3452
|
+
await callLifecycle(this._sandbox, "start");
|
|
2413
3453
|
}
|
|
2414
3454
|
if (this._searchEngine && this._config.autoIndexPaths && this._config.autoIndexPaths.length > 0) {
|
|
2415
3455
|
await this.rebuildSearchIndex(this._config.autoIndexPaths ?? []);
|
|
@@ -2426,11 +3466,11 @@ var Workspace = class {
|
|
|
2426
3466
|
async destroy() {
|
|
2427
3467
|
this._status = "destroying";
|
|
2428
3468
|
try {
|
|
2429
|
-
if (this._sandbox
|
|
2430
|
-
await this._sandbox
|
|
3469
|
+
if (this._sandbox) {
|
|
3470
|
+
await callLifecycle(this._sandbox, "destroy");
|
|
2431
3471
|
}
|
|
2432
|
-
if (this._fs
|
|
2433
|
-
await this._fs
|
|
3472
|
+
if (this._fs) {
|
|
3473
|
+
await callLifecycle(this._fs, "destroy");
|
|
2434
3474
|
}
|
|
2435
3475
|
this._status = "destroyed";
|
|
2436
3476
|
} catch (error) {
|
|
@@ -2454,6 +3494,8 @@ var Workspace = class {
|
|
|
2454
3494
|
const fsInfo = await this._fs.getInfo?.();
|
|
2455
3495
|
info.filesystem = {
|
|
2456
3496
|
provider: this._fs.provider,
|
|
3497
|
+
name: fsInfo?.name ?? this._fs.name,
|
|
3498
|
+
icon: fsInfo?.icon,
|
|
2457
3499
|
basePath: fsInfo?.basePath ?? this._fs.basePath,
|
|
2458
3500
|
readOnly: fsInfo?.readOnly ?? this._fs.readOnly,
|
|
2459
3501
|
status: fsInfo?.status,
|
|
@@ -2516,47 +3558,6 @@ var Workspace = class {
|
|
|
2516
3558
|
}
|
|
2517
3559
|
}
|
|
2518
3560
|
};
|
|
2519
|
-
|
|
2520
|
-
// src/workspace/sandbox/sandbox.ts
|
|
2521
|
-
var SandboxError = class extends Error {
|
|
2522
|
-
constructor(message, code, details) {
|
|
2523
|
-
super(message);
|
|
2524
|
-
this.code = code;
|
|
2525
|
-
this.details = details;
|
|
2526
|
-
this.name = "SandboxError";
|
|
2527
|
-
}
|
|
2528
|
-
};
|
|
2529
|
-
var SandboxExecutionError = class extends SandboxError {
|
|
2530
|
-
constructor(message, exitCode, stdout, stderr) {
|
|
2531
|
-
super(message, "EXECUTION_FAILED", { exitCode, stdout, stderr });
|
|
2532
|
-
this.exitCode = exitCode;
|
|
2533
|
-
this.stdout = stdout;
|
|
2534
|
-
this.stderr = stderr;
|
|
2535
|
-
this.name = "SandboxExecutionError";
|
|
2536
|
-
}
|
|
2537
|
-
};
|
|
2538
|
-
var SandboxTimeoutError = class extends SandboxError {
|
|
2539
|
-
constructor(timeoutMs, operation) {
|
|
2540
|
-
super(`Execution timed out after ${timeoutMs}ms`, "TIMEOUT", { timeoutMs, operation });
|
|
2541
|
-
this.timeoutMs = timeoutMs;
|
|
2542
|
-
this.operation = operation;
|
|
2543
|
-
this.name = "SandboxTimeoutError";
|
|
2544
|
-
}
|
|
2545
|
-
};
|
|
2546
|
-
var SandboxNotReadyError = class extends SandboxError {
|
|
2547
|
-
constructor(idOrStatus) {
|
|
2548
|
-
super(`Sandbox is not ready: ${idOrStatus}`, "NOT_READY", { id: idOrStatus });
|
|
2549
|
-
this.name = "SandboxNotReadyError";
|
|
2550
|
-
}
|
|
2551
|
-
};
|
|
2552
|
-
var IsolationUnavailableError = class extends SandboxError {
|
|
2553
|
-
constructor(backend, reason) {
|
|
2554
|
-
super(`Isolation backend '${backend}' is not available: ${reason}`, "ISOLATION_UNAVAILABLE", { backend, reason });
|
|
2555
|
-
this.backend = backend;
|
|
2556
|
-
this.reason = reason;
|
|
2557
|
-
this.name = "IsolationUnavailableError";
|
|
2558
|
-
}
|
|
2559
|
-
};
|
|
2560
3561
|
function commandExists(command) {
|
|
2561
3562
|
try {
|
|
2562
3563
|
childProcess.execFileSync("which", [command], { stdio: "ignore" });
|
|
@@ -2833,7 +3834,7 @@ var LocalSandbox = class extends MastraSandbox {
|
|
|
2833
3834
|
id;
|
|
2834
3835
|
name = "LocalSandbox";
|
|
2835
3836
|
provider = "local";
|
|
2836
|
-
status = "
|
|
3837
|
+
status = "pending";
|
|
2837
3838
|
_workingDirectory;
|
|
2838
3839
|
env;
|
|
2839
3840
|
timeout;
|
|
@@ -2872,7 +3873,7 @@ var LocalSandbox = class extends MastraSandbox {
|
|
|
2872
3873
|
return detectIsolation();
|
|
2873
3874
|
}
|
|
2874
3875
|
constructor(options = {}) {
|
|
2875
|
-
super({ name: "LocalSandbox" });
|
|
3876
|
+
super({ name: "LocalSandbox", onStart: options.onStart, onStop: options.onStop, onDestroy: options.onDestroy });
|
|
2876
3877
|
this.id = options.id ?? this.generateId();
|
|
2877
3878
|
this._createdAt = /* @__PURE__ */ new Date();
|
|
2878
3879
|
this._workingDirectory = options.workingDirectory ?? nodePath__namespace.join(process.cwd(), ".sandbox");
|
|
@@ -2902,44 +3903,52 @@ var LocalSandbox = class extends MastraSandbox {
|
|
|
2902
3903
|
...additionalEnv
|
|
2903
3904
|
};
|
|
2904
3905
|
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Start the local sandbox.
|
|
3908
|
+
* Creates working directory and sets up seatbelt profile if using macOS isolation.
|
|
3909
|
+
* Status management is handled by the base class.
|
|
3910
|
+
*/
|
|
2905
3911
|
async start() {
|
|
2906
3912
|
this.logger.debug("Starting sandbox", { workingDirectory: this._workingDirectory, isolation: this._isolation });
|
|
2907
|
-
this.
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
if (
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
this.
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);
|
|
2919
|
-
await fs2__namespace.mkdir(nodePath__namespace.dirname(userProvidedPath), { recursive: true });
|
|
2920
|
-
await fs2__namespace.writeFile(userProvidedPath, this._seatbeltProfile, "utf-8");
|
|
3913
|
+
await fs2__namespace.mkdir(this.workingDirectory, { recursive: true });
|
|
3914
|
+
if (this._isolation === "seatbelt") {
|
|
3915
|
+
const userProvidedPath = this._nativeSandboxConfig.seatbeltProfilePath;
|
|
3916
|
+
if (userProvidedPath) {
|
|
3917
|
+
this._seatbeltProfilePath = userProvidedPath;
|
|
3918
|
+
this._userProvidedProfilePath = true;
|
|
3919
|
+
try {
|
|
3920
|
+
this._seatbeltProfile = await fs2__namespace.readFile(userProvidedPath, "utf-8");
|
|
3921
|
+
} catch (err) {
|
|
3922
|
+
if (err instanceof Error && "code" in err && err.code !== "ENOENT") {
|
|
3923
|
+
throw err;
|
|
2921
3924
|
}
|
|
2922
|
-
} else {
|
|
2923
3925
|
this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
await fs2__namespace.mkdir(this._sandboxFolderPath, { recursive: true });
|
|
2927
|
-
this._seatbeltProfilePath = nodePath__namespace.join(this._sandboxFolderPath, `seatbelt-${configHash}.sb`);
|
|
2928
|
-
await fs2__namespace.writeFile(this._seatbeltProfilePath, this._seatbeltProfile, "utf-8");
|
|
3926
|
+
await fs2__namespace.mkdir(nodePath__namespace.dirname(userProvidedPath), { recursive: true });
|
|
3927
|
+
await fs2__namespace.writeFile(userProvidedPath, this._seatbeltProfile, "utf-8");
|
|
2929
3928
|
}
|
|
3929
|
+
} else {
|
|
3930
|
+
this._seatbeltProfile = generateSeatbeltProfile(this.workingDirectory, this._nativeSandboxConfig);
|
|
3931
|
+
const configHash = crypto__namespace.createHash("sha256").update(this.workingDirectory).update(JSON.stringify(this._nativeSandboxConfig)).digest("hex").slice(0, 8);
|
|
3932
|
+
this._sandboxFolderPath = nodePath__namespace.join(process.cwd(), ".sandbox-profiles");
|
|
3933
|
+
await fs2__namespace.mkdir(this._sandboxFolderPath, { recursive: true });
|
|
3934
|
+
this._seatbeltProfilePath = nodePath__namespace.join(this._sandboxFolderPath, `seatbelt-${configHash}.sb`);
|
|
3935
|
+
await fs2__namespace.writeFile(this._seatbeltProfilePath, this._seatbeltProfile, "utf-8");
|
|
2930
3936
|
}
|
|
2931
|
-
this.status = "running";
|
|
2932
|
-
this.logger.debug("Sandbox started", { workingDirectory: this._workingDirectory, status: this.status });
|
|
2933
|
-
} catch (error) {
|
|
2934
|
-
this.status = "error";
|
|
2935
|
-
this.logger.error("Failed to start sandbox", { workingDirectory: this._workingDirectory, error });
|
|
2936
|
-
throw error;
|
|
2937
3937
|
}
|
|
3938
|
+
this.logger.debug("Sandbox started", { workingDirectory: this._workingDirectory });
|
|
2938
3939
|
}
|
|
3940
|
+
/**
|
|
3941
|
+
* Stop the local sandbox.
|
|
3942
|
+
* Status management is handled by the base class.
|
|
3943
|
+
*/
|
|
2939
3944
|
async stop() {
|
|
2940
3945
|
this.logger.debug("Stopping sandbox", { workingDirectory: this._workingDirectory });
|
|
2941
|
-
this.status = "stopped";
|
|
2942
3946
|
}
|
|
3947
|
+
/**
|
|
3948
|
+
* Destroy the local sandbox and clean up resources.
|
|
3949
|
+
* Cleans up seatbelt profile if auto-generated.
|
|
3950
|
+
* Status management is handled by the base class.
|
|
3951
|
+
*/
|
|
2943
3952
|
async destroy() {
|
|
2944
3953
|
this.logger.debug("Destroying sandbox", { workingDirectory: this._workingDirectory });
|
|
2945
3954
|
if (this._seatbeltProfilePath && !this._userProvidedProfilePath) {
|
|
@@ -2958,7 +3967,6 @@ var LocalSandbox = class extends MastraSandbox {
|
|
|
2958
3967
|
}
|
|
2959
3968
|
this._sandboxFolderPath = void 0;
|
|
2960
3969
|
}
|
|
2961
|
-
await this.stop();
|
|
2962
3970
|
}
|
|
2963
3971
|
async isReady() {
|
|
2964
3972
|
return this.status === "running";
|
|
@@ -3009,15 +4017,13 @@ var LocalSandbox = class extends MastraSandbox {
|
|
|
3009
4017
|
}
|
|
3010
4018
|
async executeCommand(command, args = [], options = {}) {
|
|
3011
4019
|
this.logger.debug("Executing command", { command, args, cwd: options.cwd ?? this.workingDirectory });
|
|
3012
|
-
|
|
3013
|
-
await this.start();
|
|
3014
|
-
}
|
|
4020
|
+
await this.ensureRunning();
|
|
3015
4021
|
const startTime = Date.now();
|
|
3016
4022
|
const wrapped = this.wrapCommandForIsolation(command, args);
|
|
3017
4023
|
try {
|
|
3018
4024
|
const result = await execWithStreaming(wrapped.command, wrapped.args, {
|
|
3019
4025
|
cwd: options.cwd ?? this.workingDirectory,
|
|
3020
|
-
timeout:
|
|
4026
|
+
timeout: options.timeout ?? this.timeout ?? 3e4,
|
|
3021
4027
|
env: this.buildEnv(options.env),
|
|
3022
4028
|
onStdout: options.onStdout,
|
|
3023
4029
|
onStderr: options.onStderr
|
|
@@ -3093,7 +4099,10 @@ async function formatAsTree(fs5, path4, options) {
|
|
|
3093
4099
|
let entries;
|
|
3094
4100
|
try {
|
|
3095
4101
|
entries = await fs5.readdir(currentPath);
|
|
3096
|
-
} catch {
|
|
4102
|
+
} catch (error) {
|
|
4103
|
+
if (depth === 0) {
|
|
4104
|
+
throw error;
|
|
4105
|
+
}
|
|
3097
4106
|
return;
|
|
3098
4107
|
}
|
|
3099
4108
|
let filtered = entries;
|
|
@@ -3204,7 +4213,7 @@ function createWorkspaceTools(workspace) {
|
|
|
3204
4213
|
if (workspace.filesystem) {
|
|
3205
4214
|
const readFileConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.FILESYSTEM.READ_FILE);
|
|
3206
4215
|
if (readFileConfig.enabled) {
|
|
3207
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE] =
|
|
4216
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE] = chunk7UWHFWST_cjs.createTool({
|
|
3208
4217
|
id: WORKSPACE_TOOLS.FILESYSTEM.READ_FILE,
|
|
3209
4218
|
description: "Read the contents of a file from the workspace filesystem. Use offset/limit parameters to read specific line ranges for large files.",
|
|
3210
4219
|
requireApproval: readFileConfig.requireApproval,
|
|
@@ -3266,7 +4275,7 @@ function createWorkspaceTools(workspace) {
|
|
|
3266
4275
|
});
|
|
3267
4276
|
}
|
|
3268
4277
|
if (!isReadOnly && writeFileConfig.enabled) {
|
|
3269
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE] =
|
|
4278
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE] = chunk7UWHFWST_cjs.createTool({
|
|
3270
4279
|
id: WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE,
|
|
3271
4280
|
description: "Write content to a file in the workspace filesystem. Creates parent directories if needed.",
|
|
3272
4281
|
requireApproval: writeFileConfig.requireApproval,
|
|
@@ -3307,7 +4316,7 @@ function createWorkspaceTools(workspace) {
|
|
|
3307
4316
|
});
|
|
3308
4317
|
}
|
|
3309
4318
|
if (!isReadOnly && editFileConfig.enabled) {
|
|
3310
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE] =
|
|
4319
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE] = chunk7UWHFWST_cjs.createTool({
|
|
3311
4320
|
id: WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE,
|
|
3312
4321
|
description: `Edit a file by replacing specific text. The old_string must match exactly and be unique in the file.
|
|
3313
4322
|
|
|
@@ -3384,7 +4393,7 @@ Usage:
|
|
|
3384
4393
|
}
|
|
3385
4394
|
const listFilesConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES);
|
|
3386
4395
|
if (listFilesConfig.enabled) {
|
|
3387
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES] =
|
|
4396
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES] = chunk7UWHFWST_cjs.createTool({
|
|
3388
4397
|
id: WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES,
|
|
3389
4398
|
description: `List files and directories in the workspace filesystem.
|
|
3390
4399
|
Returns a tree-style view (like the Unix "tree" command) for easy visualization.
|
|
@@ -3450,7 +4459,7 @@ Examples:
|
|
|
3450
4459
|
}
|
|
3451
4460
|
const deleteConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.FILESYSTEM.DELETE);
|
|
3452
4461
|
if (!isReadOnly && deleteConfig.enabled) {
|
|
3453
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.DELETE] =
|
|
4462
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.DELETE] = chunk7UWHFWST_cjs.createTool({
|
|
3454
4463
|
id: WORKSPACE_TOOLS.FILESYSTEM.DELETE,
|
|
3455
4464
|
description: "Delete a file or directory from the workspace filesystem",
|
|
3456
4465
|
requireApproval: deleteConfig.requireApproval,
|
|
@@ -3477,7 +4486,7 @@ Examples:
|
|
|
3477
4486
|
}
|
|
3478
4487
|
const fileStatConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT);
|
|
3479
4488
|
if (fileStatConfig.enabled) {
|
|
3480
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT] =
|
|
4489
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT] = chunk7UWHFWST_cjs.createTool({
|
|
3481
4490
|
id: WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT,
|
|
3482
4491
|
description: "Get file or directory metadata from the workspace. Returns existence, type, size, and modification time.",
|
|
3483
4492
|
requireApproval: fileStatConfig.requireApproval,
|
|
@@ -3510,7 +4519,7 @@ Examples:
|
|
|
3510
4519
|
}
|
|
3511
4520
|
const mkdirConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.FILESYSTEM.MKDIR);
|
|
3512
4521
|
if (!isReadOnly && mkdirConfig.enabled) {
|
|
3513
|
-
tools[WORKSPACE_TOOLS.FILESYSTEM.MKDIR] =
|
|
4522
|
+
tools[WORKSPACE_TOOLS.FILESYSTEM.MKDIR] = chunk7UWHFWST_cjs.createTool({
|
|
3514
4523
|
id: WORKSPACE_TOOLS.FILESYSTEM.MKDIR,
|
|
3515
4524
|
description: "Create a directory in the workspace filesystem",
|
|
3516
4525
|
requireApproval: mkdirConfig.requireApproval,
|
|
@@ -3532,7 +4541,7 @@ Examples:
|
|
|
3532
4541
|
if (workspace.canBM25 || workspace.canVector) {
|
|
3533
4542
|
const searchConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.SEARCH.SEARCH);
|
|
3534
4543
|
if (searchConfig.enabled) {
|
|
3535
|
-
tools[WORKSPACE_TOOLS.SEARCH.SEARCH] =
|
|
4544
|
+
tools[WORKSPACE_TOOLS.SEARCH.SEARCH] = chunk7UWHFWST_cjs.createTool({
|
|
3536
4545
|
id: WORKSPACE_TOOLS.SEARCH.SEARCH,
|
|
3537
4546
|
description: "Search indexed content in the workspace. Supports keyword (BM25), semantic (vector), and hybrid search modes.",
|
|
3538
4547
|
requireApproval: searchConfig.requireApproval,
|
|
@@ -3578,7 +4587,7 @@ Examples:
|
|
|
3578
4587
|
}
|
|
3579
4588
|
const indexConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.SEARCH.INDEX);
|
|
3580
4589
|
if (!isReadOnly && indexConfig.enabled) {
|
|
3581
|
-
tools[WORKSPACE_TOOLS.SEARCH.INDEX] =
|
|
4590
|
+
tools[WORKSPACE_TOOLS.SEARCH.INDEX] = chunk7UWHFWST_cjs.createTool({
|
|
3582
4591
|
id: WORKSPACE_TOOLS.SEARCH.INDEX,
|
|
3583
4592
|
description: "Index content for search. The path becomes the document ID in search results.",
|
|
3584
4593
|
requireApproval: indexConfig.requireApproval,
|
|
@@ -3603,22 +4612,20 @@ Examples:
|
|
|
3603
4612
|
const pathInfo = pathContext.instructions ? ` ${pathContext.instructions}` : "";
|
|
3604
4613
|
const executeCommandConfig = resolveToolConfig(toolsConfig, WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND);
|
|
3605
4614
|
if (workspace.sandbox.executeCommand && executeCommandConfig.enabled) {
|
|
3606
|
-
tools[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND] =
|
|
4615
|
+
tools[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND] = chunk7UWHFWST_cjs.createTool({
|
|
3607
4616
|
id: WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND,
|
|
3608
4617
|
description: `Execute a shell command in the workspace sandbox.${pathInfo}
|
|
3609
4618
|
|
|
3610
4619
|
Usage:
|
|
3611
4620
|
- Verify parent directories exist before running commands that create files or directories.
|
|
3612
4621
|
- Always quote file paths that contain spaces (e.g., cd "/path/with spaces").
|
|
3613
|
-
-
|
|
4622
|
+
- Use the timeout parameter to limit execution time. Behavior when omitted depends on the sandbox provider.
|
|
3614
4623
|
- Use cwd to set the working directory, or commands run from the sandbox default.`,
|
|
3615
4624
|
requireApproval: executeCommandConfig.requireApproval,
|
|
3616
4625
|
inputSchema: zod.z.object({
|
|
3617
4626
|
command: zod.z.string().describe('The command to execute (e.g., "ls", "npm", "python")'),
|
|
3618
4627
|
args: zod.z.array(zod.z.string()).nullish().default([]).describe("Arguments to pass to the command"),
|
|
3619
|
-
timeout: zod.z.number().nullish().
|
|
3620
|
-
"Maximum execution time in milliseconds. Default is 30000 (30 seconds). Example: 60000 for 1 minute."
|
|
3621
|
-
),
|
|
4628
|
+
timeout: zod.z.number().nullish().describe("Maximum execution time in milliseconds. Example: 60000 for 1 minute."),
|
|
3622
4629
|
cwd: zod.z.string().nullish().describe("Working directory for the command")
|
|
3623
4630
|
}),
|
|
3624
4631
|
outputSchema: zod.z.object({
|
|
@@ -3644,7 +4651,7 @@ Usage:
|
|
|
3644
4651
|
const startedAt = Date.now();
|
|
3645
4652
|
try {
|
|
3646
4653
|
const result = await workspace.sandbox.executeCommand(command, args ?? [], {
|
|
3647
|
-
timeout: timeout ??
|
|
4654
|
+
timeout: timeout ?? void 0,
|
|
3648
4655
|
cwd: cwd ?? void 0,
|
|
3649
4656
|
// Stream stdout/stderr as tool-output chunks for proper UI integration
|
|
3650
4657
|
onStdout: async (data) => {
|
|
@@ -3696,6 +4703,7 @@ Usage:
|
|
|
3696
4703
|
}
|
|
3697
4704
|
|
|
3698
4705
|
exports.BM25Index = BM25Index;
|
|
4706
|
+
exports.CompositeFilesystem = CompositeFilesystem;
|
|
3699
4707
|
exports.DirectoryNotEmptyError = DirectoryNotEmptyError;
|
|
3700
4708
|
exports.DirectoryNotFoundError = DirectoryNotFoundError;
|
|
3701
4709
|
exports.FileExistsError = FileExistsError;
|
|
@@ -3703,12 +4711,17 @@ exports.FileNotFoundError = FileNotFoundError;
|
|
|
3703
4711
|
exports.FileReadRequiredError = FileReadRequiredError;
|
|
3704
4712
|
exports.FilesystemError = FilesystemError;
|
|
3705
4713
|
exports.FilesystemNotAvailableError = FilesystemNotAvailableError;
|
|
4714
|
+
exports.FilesystemNotMountableError = FilesystemNotMountableError;
|
|
4715
|
+
exports.FilesystemNotReadyError = FilesystemNotReadyError;
|
|
3706
4716
|
exports.IsDirectoryError = IsDirectoryError;
|
|
3707
4717
|
exports.IsolationUnavailableError = IsolationUnavailableError;
|
|
3708
4718
|
exports.LocalFilesystem = LocalFilesystem;
|
|
3709
4719
|
exports.LocalSandbox = LocalSandbox;
|
|
3710
4720
|
exports.MastraFilesystem = MastraFilesystem;
|
|
3711
4721
|
exports.MastraSandbox = MastraSandbox;
|
|
4722
|
+
exports.MountError = MountError;
|
|
4723
|
+
exports.MountManager = MountManager;
|
|
4724
|
+
exports.MountNotSupportedError = MountNotSupportedError;
|
|
3712
4725
|
exports.NotDirectoryError = NotDirectoryError;
|
|
3713
4726
|
exports.PermissionError = PermissionError;
|
|
3714
4727
|
exports.SandboxError = SandboxError;
|
|
@@ -3724,11 +4737,12 @@ exports.Workspace = Workspace;
|
|
|
3724
4737
|
exports.WorkspaceError = WorkspaceError;
|
|
3725
4738
|
exports.WorkspaceNotReadyError = WorkspaceNotReadyError;
|
|
3726
4739
|
exports.WorkspaceReadOnlyError = WorkspaceReadOnlyError;
|
|
4740
|
+
exports.callLifecycle = callLifecycle;
|
|
3727
4741
|
exports.createWorkspaceTools = createWorkspaceTools;
|
|
3728
4742
|
exports.detectIsolation = detectIsolation;
|
|
3729
4743
|
exports.extractLines = extractLines;
|
|
3730
4744
|
exports.getRecommendedIsolation = getRecommendedIsolation;
|
|
3731
4745
|
exports.isIsolationAvailable = isIsolationAvailable;
|
|
3732
4746
|
exports.resolveToolConfig = resolveToolConfig;
|
|
3733
|
-
//# sourceMappingURL=chunk-
|
|
3734
|
-
//# sourceMappingURL=chunk-
|
|
4747
|
+
//# sourceMappingURL=chunk-CGPH7CMG.cjs.map
|
|
4748
|
+
//# sourceMappingURL=chunk-CGPH7CMG.cjs.map
|