@qvac/harness 0.1.0 → 0.1.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/dist/harness.bundle +13 -64
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +240 -0
- package/dist-lib/index.js +1063 -0
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -0
- package/dist-lib/lib/audio-transcode-bare.js +145 -0
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -0
- package/dist-lib/lib/audio-transcode-node.js +6 -0
- package/dist-lib/lib/capabilities/audio-resample.d.ts +14 -0
- package/dist-lib/lib/capabilities/audio-resample.js +72 -0
- package/dist-lib/lib/capabilities/completion.d.ts +288 -0
- package/dist-lib/lib/capabilities/completion.js +1077 -0
- package/dist-lib/lib/capabilities/download.d.ts +46 -0
- package/dist-lib/lib/capabilities/download.js +124 -0
- package/dist-lib/lib/capabilities/embed.d.ts +20 -0
- package/dist-lib/lib/capabilities/embed.js +13 -0
- package/dist-lib/lib/capabilities/kv-cache.d.ts +13 -0
- package/dist-lib/lib/capabilities/kv-cache.js +21 -0
- package/dist-lib/lib/capabilities/prompt-prefix.d.ts +6 -0
- package/dist-lib/lib/capabilities/prompt-prefix.js +10 -0
- package/dist-lib/lib/capabilities/speech-lexicon.d.ts +5 -0
- package/dist-lib/lib/capabilities/speech-lexicon.js +32 -0
- package/dist-lib/lib/capabilities/speech-text.d.ts +6 -0
- package/dist-lib/lib/capabilities/speech-text.js +79 -0
- package/dist-lib/lib/capabilities/tool-names.d.ts +3 -0
- package/dist-lib/lib/capabilities/tool-names.js +3 -0
- package/dist-lib/lib/capabilities/tool-selection.d.ts +43 -0
- package/dist-lib/lib/capabilities/tool-selection.js +60 -0
- package/dist-lib/lib/capabilities/tool-turns.d.ts +9 -0
- package/dist-lib/lib/capabilities/tool-turns.js +101 -0
- package/dist-lib/lib/capabilities/usage-breakdown.d.ts +27 -0
- package/dist-lib/lib/capabilities/usage-breakdown.js +44 -0
- package/dist-lib/lib/capabilities/vision-history.d.ts +3 -0
- package/dist-lib/lib/capabilities/vision-history.js +21 -0
- package/dist-lib/lib/capabilities/voice.d.ts +78 -0
- package/dist-lib/lib/capabilities/voice.js +189 -0
- package/dist-lib/lib/connect.d.ts +76 -0
- package/dist-lib/lib/connect.js +789 -0
- package/dist-lib/lib/context-window.d.ts +7 -0
- package/dist-lib/lib/context-window.js +90 -0
- package/dist-lib/lib/contract-types.d.ts +210 -0
- package/dist-lib/lib/contract-types.js +1 -0
- package/dist-lib/lib/diagram-repair.d.ts +21 -0
- package/dist-lib/lib/diagram-repair.js +58 -0
- package/dist-lib/lib/dns-lookup.d.ts +1 -0
- package/dist-lib/lib/dns-lookup.js +13 -0
- package/dist-lib/lib/error-fields.d.ts +2 -0
- package/dist-lib/lib/error-fields.js +8 -0
- package/dist-lib/lib/fetch-node.d.ts +1 -0
- package/dist-lib/lib/fetch-node.js +1 -0
- package/dist-lib/lib/hardware/capability-probe.d.ts +9 -0
- package/dist-lib/lib/hardware/capability-probe.js +99 -0
- package/dist-lib/lib/hardware/gpu-classify.d.ts +2 -0
- package/dist-lib/lib/hardware/gpu-classify.js +32 -0
- package/dist-lib/lib/hardware/gpu-probe-addon.d.ts +2 -0
- package/dist-lib/lib/hardware/gpu-probe-addon.js +42 -0
- package/dist-lib/lib/hardware/gpu-stats.bare.d.ts +1 -0
- package/dist-lib/lib/hardware/gpu-stats.bare.js +24 -0
- package/dist-lib/lib/hardware/gpu-stats.stub.d.ts +1 -0
- package/dist-lib/lib/hardware/gpu-stats.stub.js +3 -0
- package/dist-lib/lib/hardware/memory-budget.d.ts +8 -0
- package/dist-lib/lib/hardware/memory-budget.js +1 -0
- package/dist-lib/lib/hardware/parsers.d.ts +7 -0
- package/dist-lib/lib/hardware/parsers.js +41 -0
- package/dist-lib/lib/headers-bare.d.ts +1 -0
- package/dist-lib/lib/headers-bare.js +1 -0
- package/dist-lib/lib/headers-node.d.ts +4 -0
- package/dist-lib/lib/headers-node.js +1 -0
- package/dist-lib/lib/helpers/buffer-burst.d.ts +1 -0
- package/dist-lib/lib/helpers/buffer-burst.js +20 -0
- package/dist-lib/lib/helpers/control-tokens.d.ts +7 -0
- package/dist-lib/lib/helpers/control-tokens.js +32 -0
- package/dist-lib/lib/helpers/join-guard.d.ts +6 -0
- package/dist-lib/lib/helpers/join-guard.js +46 -0
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +5 -0
- package/dist-lib/lib/helpers/markup-stream-filter.js +75 -0
- package/dist-lib/lib/helpers/race-abort.d.ts +5 -0
- package/dist-lib/lib/helpers/race-abort.js +18 -0
- package/dist-lib/lib/helpers/retry-memo.d.ts +5 -0
- package/dist-lib/lib/helpers/retry-memo.js +12 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +33 -0
- package/dist-lib/lib/helpers/run-outcome.js +204 -0
- package/dist-lib/lib/helpers/spawn-path.d.ts +5 -0
- package/dist-lib/lib/helpers/spawn-path.js +57 -0
- package/dist-lib/lib/helpers/text-coalescer.d.ts +15 -0
- package/dist-lib/lib/helpers/text-coalescer.js +34 -0
- package/dist-lib/lib/helpers/thinking.d.ts +8 -0
- package/dist-lib/lib/helpers/thinking.js +95 -0
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +20 -0
- package/dist-lib/lib/helpers/tool-call-markup.js +201 -0
- package/dist-lib/lib/helpers/tool-intent.d.ts +7 -0
- package/dist-lib/lib/helpers/tool-intent.js +21 -0
- package/dist-lib/lib/helpers/tool-label.d.ts +8 -0
- package/dist-lib/lib/helpers/tool-label.js +131 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.d.ts +5 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.js +88 -0
- package/dist-lib/lib/hooks/built-in/egress.d.ts +5 -0
- package/dist-lib/lib/hooks/built-in/egress.js +29 -0
- package/dist-lib/lib/hooks/built-in/index.d.ts +3 -0
- package/dist-lib/lib/hooks/built-in/index.js +3 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.d.ts +10 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +17 -0
- package/dist-lib/lib/hooks/pipeline.d.ts +20 -0
- package/dist-lib/lib/hooks/pipeline.js +49 -0
- package/dist-lib/lib/hooks/registry.d.ts +2 -0
- package/dist-lib/lib/hooks/registry.js +6 -0
- package/dist-lib/lib/hooks/run.d.ts +5 -0
- package/dist-lib/lib/hooks/run.js +83 -0
- package/dist-lib/lib/hooks/types.d.ts +57 -0
- package/dist-lib/lib/hooks/types.js +1 -0
- package/dist-lib/lib/log.d.ts +5 -0
- package/dist-lib/lib/log.js +37 -0
- package/dist-lib/lib/model-download-progress.d.ts +22 -0
- package/dist-lib/lib/model-download-progress.js +95 -0
- package/dist-lib/lib/models/context.d.ts +5 -0
- package/dist-lib/lib/models/context.js +12 -0
- package/dist-lib/lib/models/embed-config.d.ts +14 -0
- package/dist-lib/lib/models/embed-config.js +31 -0
- package/dist-lib/lib/models/gpu-selection.d.ts +3 -0
- package/dist-lib/lib/models/gpu-selection.js +26 -0
- package/dist-lib/lib/models/kv-cache.d.ts +4 -0
- package/dist-lib/lib/models/kv-cache.js +6 -0
- package/dist-lib/lib/models/llm-presets.d.ts +10 -0
- package/dist-lib/lib/models/llm-presets.js +58 -0
- package/dist-lib/lib/models/model-catalog-constants.d.ts +29 -0
- package/dist-lib/lib/models/model-catalog-constants.js +103 -0
- package/dist-lib/lib/models/model-catalog.d.ts +125 -0
- package/dist-lib/lib/models/model-catalog.js +371 -0
- package/dist-lib/lib/models/model-delete.d.ts +18 -0
- package/dist-lib/lib/models/model-delete.js +47 -0
- package/dist-lib/lib/models/model-handle.d.ts +18 -0
- package/dist-lib/lib/models/model-handle.js +33 -0
- package/dist-lib/lib/models/model-list.d.ts +42 -0
- package/dist-lib/lib/models/model-list.js +123 -0
- package/dist-lib/lib/models/model-pool.d.ts +116 -0
- package/dist-lib/lib/models/model-pool.js +356 -0
- package/dist-lib/lib/models/model-source.d.ts +6 -0
- package/dist-lib/lib/models/model-source.js +21 -0
- package/dist-lib/lib/models/music-catalog.d.ts +12 -0
- package/dist-lib/lib/models/music-catalog.js +26 -0
- package/dist-lib/lib/models/opencl-cache.d.ts +5 -0
- package/dist-lib/lib/models/opencl-cache.js +50 -0
- package/dist-lib/lib/models/sdk-faults.d.ts +21 -0
- package/dist-lib/lib/models/sdk-faults.js +66 -0
- package/dist-lib/lib/models/tool-dialect.d.ts +2 -0
- package/dist-lib/lib/models/tool-dialect.js +19 -0
- package/dist-lib/lib/models/tts-model-default.d.ts +3 -0
- package/dist-lib/lib/models/tts-model-default.js +13 -0
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +15 -0
- package/dist-lib/lib/no-redirect-fetch-bare.js +59 -0
- package/dist-lib/lib/no-redirect-fetch-impl.d.ts +39 -0
- package/dist-lib/lib/no-redirect-fetch-impl.js +80 -0
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +4 -0
- package/dist-lib/lib/no-redirect-fetch-node.js +52 -0
- package/dist-lib/lib/prompt.d.ts +22 -0
- package/dist-lib/lib/prompt.js +77 -0
- package/dist-lib/lib/queue.d.ts +9 -0
- package/dist-lib/lib/queue.js +48 -0
- package/dist-lib/lib/resolution-detector.d.ts +5 -0
- package/dist-lib/lib/resolution-detector.js +100 -0
- package/dist-lib/lib/rpc-session.d.ts +8 -0
- package/dist-lib/lib/rpc-session.js +20 -0
- package/dist-lib/lib/sdk-config-loader.bare.d.ts +1 -0
- package/dist-lib/lib/sdk-config-loader.bare.js +9 -0
- package/dist-lib/lib/sdk-config-loader.node.d.ts +1 -0
- package/dist-lib/lib/sdk-config-loader.node.js +1 -0
- package/dist-lib/lib/sdk-loader.d.ts +30 -0
- package/dist-lib/lib/sdk-loader.js +53 -0
- package/dist-lib/lib/sdk-storage.d.ts +8 -0
- package/dist-lib/lib/sdk-storage.js +24 -0
- package/dist-lib/lib/serve.d.ts +3 -0
- package/dist-lib/lib/serve.js +612 -0
- package/dist-lib/lib/spawn.d.ts +62 -0
- package/dist-lib/lib/spawn.js +71 -0
- package/dist-lib/lib/staging.d.ts +4 -0
- package/dist-lib/lib/staging.js +28 -0
- package/dist-lib/lib/storage.d.ts +18 -0
- package/dist-lib/lib/storage.js +26 -0
- package/dist-lib/lib/tmp-path.d.ts +7 -0
- package/dist-lib/lib/tmp-path.js +17 -0
- package/dist-lib/lib/tool-call-display.d.ts +21 -0
- package/dist-lib/lib/tool-call-display.js +1 -0
- package/dist-lib/lib/tools/catalog.d.ts +13 -0
- package/dist-lib/lib/tools/catalog.js +13 -0
- package/dist-lib/lib/tools/create-documents.d.ts +3 -0
- package/dist-lib/lib/tools/create-documents.js +87 -0
- package/dist-lib/lib/tools/edit-documents.d.ts +3 -0
- package/dist-lib/lib/tools/edit-documents.js +122 -0
- package/dist-lib/lib/tools/edit-image.d.ts +25 -0
- package/dist-lib/lib/tools/edit-image.js +271 -0
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +27 -0
- package/dist-lib/lib/tools/exec/adapters/adapter.js +1 -0
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +26 -0
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +160 -0
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +7 -0
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +290 -0
- package/dist-lib/lib/tools/exec/adapters/python/loader.d.ts +11 -0
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +94 -0
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +47 -0
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +85 -0
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +13 -0
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +129 -0
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +19 -0
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +107 -0
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +75 -0
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +1094 -0
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +75 -0
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +273 -0
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +38 -0
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +102 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +7 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +188 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.d.ts +1 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +105 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +38 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +41 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.d.ts +2 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.js +30 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +13 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +45 -0
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +12 -0
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +20 -0
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +8 -0
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +74 -0
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +18 -0
- package/dist-lib/lib/tools/exec/obsidian/cli.js +153 -0
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +5 -0
- package/dist-lib/lib/tools/exec/obsidian/command.js +18 -0
- package/dist-lib/lib/tools/exec/obsidian/guidance.d.ts +5 -0
- package/dist-lib/lib/tools/exec/obsidian/guidance.js +18 -0
- package/dist-lib/lib/tools/exec/obsidian/note-name.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/note-name.js +6 -0
- package/dist-lib/lib/tools/exec/obsidian/output.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/output.js +31 -0
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +19 -0
- package/dist-lib/lib/tools/exec/obsidian/route.js +85 -0
- package/dist-lib/lib/tools/exec/obsidian/safe-path.d.ts +3 -0
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +56 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-create.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +40 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.d.ts +3 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +57 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +20 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +171 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-read.d.ts +11 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +119 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-write.d.ts +7 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +134 -0
- package/dist-lib/lib/tools/exec/runner.d.ts +62 -0
- package/dist-lib/lib/tools/exec/runner.js +87 -0
- package/dist-lib/lib/tools/exec/scope.d.ts +3 -0
- package/dist-lib/lib/tools/exec/scope.js +97 -0
- package/dist-lib/lib/tools/exec/shell-argv.d.ts +2 -0
- package/dist-lib/lib/tools/exec/shell-argv.js +48 -0
- package/dist-lib/lib/tools/exec.d.ts +29 -0
- package/dist-lib/lib/tools/exec.js +459 -0
- package/dist-lib/lib/tools/file-glob.d.ts +15 -0
- package/dist-lib/lib/tools/file-glob.js +112 -0
- package/dist-lib/lib/tools/fs/drive.d.ts +26 -0
- package/dist-lib/lib/tools/fs/drive.js +1 -0
- package/dist-lib/lib/tools/fs/glob.d.ts +1 -0
- package/dist-lib/lib/tools/fs/glob.js +34 -0
- package/dist-lib/lib/tools/fs/local.d.ts +2 -0
- package/dist-lib/lib/tools/fs/local.js +29 -0
- package/dist-lib/lib/tools/fs/read.d.ts +2 -0
- package/dist-lib/lib/tools/fs/read.js +17 -0
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +40 -0
- package/dist-lib/lib/tools/fs/remote-protocol.js +1 -0
- package/dist-lib/lib/tools/fs/remote.d.ts +17 -0
- package/dist-lib/lib/tools/fs/remote.js +131 -0
- package/dist-lib/lib/tools/fs/scope.d.ts +37 -0
- package/dist-lib/lib/tools/fs/scope.js +130 -0
- package/dist-lib/lib/tools/fs/walk.d.ts +15 -0
- package/dist-lib/lib/tools/fs/walk.js +38 -0
- package/dist-lib/lib/tools/fs/write.d.ts +4 -0
- package/dist-lib/lib/tools/fs/write.js +5 -0
- package/dist-lib/lib/tools/generate-image.d.ts +111 -0
- package/dist-lib/lib/tools/generate-image.js +369 -0
- package/dist-lib/lib/tools/generate-music.d.ts +124 -0
- package/dist-lib/lib/tools/generate-music.js +522 -0
- package/dist-lib/lib/tools/grep.d.ts +5 -0
- package/dist-lib/lib/tools/grep.js +158 -0
- package/dist-lib/lib/tools/http-methods.d.ts +1 -0
- package/dist-lib/lib/tools/http-methods.js +1 -0
- package/dist-lib/lib/tools/http-request/encode-fields.d.ts +2 -0
- package/dist-lib/lib/tools/http-request/encode-fields.js +69 -0
- package/dist-lib/lib/tools/http-request/truncate-response.d.ts +1 -0
- package/dist-lib/lib/tools/http-request/truncate-response.js +83 -0
- package/dist-lib/lib/tools/http-request/utils.d.ts +27 -0
- package/dist-lib/lib/tools/http-request/utils.js +84 -0
- package/dist-lib/lib/tools/http-request.d.ts +20 -0
- package/dist-lib/lib/tools/http-request.js +177 -0
- package/dist-lib/lib/tools/mcp/utils.d.ts +47 -0
- package/dist-lib/lib/tools/mcp/utils.js +172 -0
- package/dist-lib/lib/tools/mcp-call.d.ts +14 -0
- package/dist-lib/lib/tools/mcp-call.js +155 -0
- package/dist-lib/lib/tools/model-args.d.ts +1 -0
- package/dist-lib/lib/tools/model-args.js +4 -0
- package/dist-lib/lib/tools/operations/builders.d.ts +17 -0
- package/dist-lib/lib/tools/operations/builders.js +179 -0
- package/dist-lib/lib/tools/operations/compile.d.ts +7 -0
- package/dist-lib/lib/tools/operations/compile.js +92 -0
- package/dist-lib/lib/tools/operations/gmail-message.d.ts +15 -0
- package/dist-lib/lib/tools/operations/gmail-message.js +62 -0
- package/dist-lib/lib/tools/operations/manifest.d.ts +60 -0
- package/dist-lib/lib/tools/operations/manifest.js +50 -0
- package/dist-lib/lib/tools/outbound-guard.d.ts +10 -0
- package/dist-lib/lib/tools/outbound-guard.js +127 -0
- package/dist-lib/lib/tools/private-ip.d.ts +4 -0
- package/dist-lib/lib/tools/private-ip.js +126 -0
- package/dist-lib/lib/tools/rag-search.d.ts +2 -0
- package/dist-lib/lib/tools/rag-search.js +98 -0
- package/dist-lib/lib/tools/read-files.d.ts +5 -0
- package/dist-lib/lib/tools/read-files.js +153 -0
- package/dist-lib/lib/tools/skill.d.ts +10 -0
- package/dist-lib/lib/tools/skill.js +123 -0
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +43 -0
- package/dist-lib/lib/tools/skills/auth-resolver.js +121 -0
- package/dist-lib/lib/tools/skills/bin-check.d.ts +9 -0
- package/dist-lib/lib/tools/skills/bin-check.js +56 -0
- package/dist-lib/lib/tools/skills/bundled-dir.d.ts +2 -0
- package/dist-lib/lib/tools/skills/bundled-dir.js +117 -0
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +4 -0
- package/dist-lib/lib/tools/skills/bundled-hash.js +12 -0
- package/dist-lib/lib/tools/skills/catalog.d.ts +60 -0
- package/dist-lib/lib/tools/skills/catalog.js +203 -0
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +9 -0
- package/dist-lib/lib/tools/skills/cli-argv.js +147 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.d.ts +3 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.js +62 -0
- package/dist-lib/lib/tools/skills/cli-schema-load.d.ts +4 -0
- package/dist-lib/lib/tools/skills/cli-schema-load.js +90 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +30 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.js +1 -0
- package/dist-lib/lib/tools/skills/cli-schema.d.ts +7 -0
- package/dist-lib/lib/tools/skills/cli-schema.js +35 -0
- package/dist-lib/lib/tools/skills/credential-check.d.ts +20 -0
- package/dist-lib/lib/tools/skills/credential-check.js +94 -0
- package/dist-lib/lib/tools/skills/credential-grants.d.ts +9 -0
- package/dist-lib/lib/tools/skills/credential-grants.js +25 -0
- package/dist-lib/lib/tools/skills/credential-health.d.ts +8 -0
- package/dist-lib/lib/tools/skills/credential-health.js +51 -0
- package/dist-lib/lib/tools/skills/credential-slots.d.ts +7 -0
- package/dist-lib/lib/tools/skills/credential-slots.js +8 -0
- package/dist-lib/lib/tools/skills/frontmatter-parser.d.ts +23 -0
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +68 -0
- package/dist-lib/lib/tools/skills/index.d.ts +5 -0
- package/dist-lib/lib/tools/skills/index.js +5 -0
- package/dist-lib/lib/tools/skills/manifest-parse.d.ts +12 -0
- package/dist-lib/lib/tools/skills/manifest-parse.js +199 -0
- package/dist-lib/lib/tools/skills/manifest.d.ts +47 -0
- package/dist-lib/lib/tools/skills/manifest.js +1 -0
- package/dist-lib/lib/tools/skills/mention-name.d.ts +1 -0
- package/dist-lib/lib/tools/skills/mention-name.js +4 -0
- package/dist-lib/lib/tools/skills/mention.d.ts +6 -0
- package/dist-lib/lib/tools/skills/mention.js +42 -0
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +16 -0
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +95 -0
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +18 -0
- package/dist-lib/lib/tools/skills/overlay-crud.js +98 -0
- package/dist-lib/lib/tools/skills/overlay-dir.d.ts +2 -0
- package/dist-lib/lib/tools/skills/overlay-dir.js +22 -0
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +13 -0
- package/dist-lib/lib/tools/skills/overlay-watch.js +44 -0
- package/dist-lib/lib/tools/skills/scope-key.d.ts +7 -0
- package/dist-lib/lib/tools/skills/scope-key.js +7 -0
- package/dist-lib/lib/tools/skills/setup-routes.d.ts +4 -0
- package/dist-lib/lib/tools/skills/setup-routes.js +14 -0
- package/dist-lib/lib/tools/skills/skill-settings.d.ts +16 -0
- package/dist-lib/lib/tools/skills/skill-settings.js +33 -0
- package/dist-lib/lib/tools/skills/skill-state-constants.d.ts +12 -0
- package/dist-lib/lib/tools/skills/skill-state-constants.js +14 -0
- package/dist-lib/lib/tools/skills/skill-state.d.ts +40 -0
- package/dist-lib/lib/tools/skills/skill-state.js +117 -0
- package/dist-lib/lib/tools/skills/tool-grants.d.ts +5 -0
- package/dist-lib/lib/tools/skills/tool-grants.js +14 -0
- package/dist-lib/lib/tools/toolsets.d.ts +8 -0
- package/dist-lib/lib/tools/toolsets.js +20 -0
- package/dist-lib/lib/tools/url-guard.d.ts +8 -0
- package/dist-lib/lib/tools/url-guard.js +40 -0
- package/dist-lib/lib/tools/web-fetch.d.ts +8 -0
- package/dist-lib/lib/tools/web-fetch.js +302 -0
- package/dist-lib/lib/tools/web-search.d.ts +6 -0
- package/dist-lib/lib/tools/web-search.js +55 -0
- package/dist-lib/lib/transport.d.ts +12 -0
- package/dist-lib/lib/transport.js +59 -0
- package/dist-lib/lib/user-agent.d.ts +1 -0
- package/dist-lib/lib/user-agent.js +1 -0
- package/dist-lib/lib/validating-lookup.d.ts +17 -0
- package/dist-lib/lib/validating-lookup.js +25 -0
- package/dist-lib/lib/wav-encode.d.ts +5 -0
- package/dist-lib/lib/wav-encode.js +24 -0
- package/dist-lib/lib/web-fetch/embedded-state.d.ts +1 -0
- package/dist-lib/lib/web-fetch/embedded-state.js +131 -0
- package/dist-lib/lib/web-fetch/extract.d.ts +6 -0
- package/dist-lib/lib/web-fetch/extract.js +65 -0
- package/dist-lib/lib/web-fetch/images.d.ts +3 -0
- package/dist-lib/lib/web-fetch/images.js +43 -0
- package/dist-lib/lib/web-fetch/video.d.ts +5 -0
- package/dist-lib/lib/web-fetch/video.js +42 -0
- package/dist-lib/lib/web-search/engine-registry.d.ts +7 -0
- package/dist-lib/lib/web-search/engine-registry.js +17 -0
- package/dist-lib/lib/web-search/engines/aol.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/aol.js +81 -0
- package/dist-lib/lib/web-search/engines/bing.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/bing.js +111 -0
- package/dist-lib/lib/web-search/engines/brave.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/brave.js +59 -0
- package/dist-lib/lib/web-search/engines/duckduckgo.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/duckduckgo.js +112 -0
- package/dist-lib/lib/web-search/engines/google.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/google.js +92 -0
- package/dist-lib/lib/web-search/engines/wikipedia.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/wikipedia.js +37 -0
- package/dist-lib/lib/web-search/engines/yahoo.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/yahoo.js +81 -0
- package/dist-lib/lib/web-search/fetch-html.d.ts +25 -0
- package/dist-lib/lib/web-search/fetch-html.js +97 -0
- package/dist-lib/lib/web-search/format-results.d.ts +3 -0
- package/dist-lib/lib/web-search/format-results.js +14 -0
- package/dist-lib/lib/web-search/html-helpers.d.ts +3 -0
- package/dist-lib/lib/web-search/html-helpers.js +24 -0
- package/dist-lib/lib/web-search/merge-results.d.ts +2 -0
- package/dist-lib/lib/web-search/merge-results.js +53 -0
- package/dist-lib/lib/web-search/orchestrator.d.ts +14 -0
- package/dist-lib/lib/web-search/orchestrator.js +50 -0
- package/dist-lib/lib/web-search/types.d.ts +34 -0
- package/dist-lib/lib/web-search/types.js +1 -0
- package/dist-lib/lib/web-search/url-helpers.d.ts +1 -0
- package/dist-lib/lib/web-search/url-helpers.js +11 -0
- package/dist-lib/lib/web-search/url-normalize.d.ts +1 -0
- package/dist-lib/lib/web-search/url-normalize.js +25 -0
- package/dist-lib/lib/web-search/user-agents.d.ts +3 -0
- package/dist-lib/lib/web-search/user-agents.js +3 -0
- package/dist-lib/lib/wire.d.ts +50 -0
- package/dist-lib/lib/wire.js +145 -0
- package/dist-lib/schema/baseline.d.ts +8 -0
- package/dist-lib/schema/baseline.js +35 -0
- package/dist-lib/schema/build.d.ts +1 -0
- package/dist-lib/schema/build.js +39 -0
- package/dist-lib/schema/generate.d.ts +20 -0
- package/dist-lib/schema/generate.js +149 -0
- package/dist-lib/schema/harness.d.ts +2 -0
- package/dist-lib/schema/harness.js +745 -0
- package/dist-lib/schema/release-tags.d.ts +5 -0
- package/dist-lib/schema/release-tags.js +58 -0
- package/dist-lib/spec/bind.d.ts +2 -0
- package/dist-lib/spec/bind.js +124 -0
- package/dist-lib/spec/calls.d.ts +3 -0
- package/dist-lib/spec/calls.js +41 -0
- package/dist-lib/spec/hrpc/hrpc.json +356 -0
- package/dist-lib/spec/hrpc/index.d.ts +3 -0
- package/dist-lib/spec/hrpc/index.js +463 -0
- package/dist-lib/spec/hrpc/messages.d.ts +4 -0
- package/dist-lib/spec/hrpc/messages.js +3102 -0
- package/dist-lib/spec/hyperschema/index.js +3102 -0
- package/dist-lib/spec/hyperschema/schema.json +2290 -0
- package/dist-lib/spec/hyperschema/types.d.ts +554 -0
- package/dist-lib/spec/index.d.ts +8 -0
- package/dist-lib/spec/index.js +1 -0
- package/package.json +239 -64
- package/.gitattributes +0 -1
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { AbortedError } from "../helpers/race-abort.js";
|
|
4
|
+
import { spawnEnv } from "../helpers/spawn-path.js";
|
|
5
|
+
import { checkPdfBuildSource, checkBareLengthArgs, checkPptxBuildSource, checkPythonSource, checkUnstagedRead, createPythonAdapter, isPythonLanguage, looksLikePythonSource, parseInputs, parseOutputs, parsePackages, parseWorkdir, runsThroughPyodide, unwrapPythonSource } from "./exec/adapters/python/python-adapter.js";
|
|
6
|
+
import { pythonCreateEffect } from "./exec/adapters/python/create-effect.js";
|
|
7
|
+
import { createShellAdapter } from "./exec/adapters/shell/shell-adapter.js";
|
|
8
|
+
import { routeObsidianCommand } from "./exec/obsidian/route.js";
|
|
9
|
+
import { MAX_OUTPUT_CHARS_LIMIT } from "./exec/runner.js";
|
|
10
|
+
import { bareBin, firstCommandToken, sanitizeCommand } from "./exec/scope.js";
|
|
11
|
+
import { shellArgv } from "./exec/shell-argv.js";
|
|
12
|
+
import { canonicalDenyRoots, defaultRealpath, hostPath, resolveKey } from "./fs/scope.js";
|
|
13
|
+
import { unquoteArg } from "./model-args.js";
|
|
14
|
+
import { settingsEnv } from "./skills/skill-settings.js";
|
|
15
|
+
import { checkCliOps } from "./skills/cli-schema.js";
|
|
16
|
+
function parsePositiveInt(value, name, max) {
|
|
17
|
+
if (value === undefined || value === null || value === '')
|
|
18
|
+
return undefined;
|
|
19
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
20
|
+
if (!Number.isInteger(n) || n <= 0 || n > max) {
|
|
21
|
+
return { error: `${name} must be a positive integer ≤ ${max}` };
|
|
22
|
+
}
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
function resolveShellOps(command, toolOps) {
|
|
26
|
+
const bin = bareBin(firstCommandToken(command));
|
|
27
|
+
return { ops: (bin ? toolOps?.get(bin) : undefined) ?? [], argv: shellArgv(command).slice(1) };
|
|
28
|
+
}
|
|
29
|
+
function checkShellScope(command, toolOps) {
|
|
30
|
+
const unsafe = sanitizeCommand(command);
|
|
31
|
+
if (unsafe)
|
|
32
|
+
return unsafe;
|
|
33
|
+
const { ops, argv } = resolveShellOps(command, toolOps);
|
|
34
|
+
if (ops.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
return checkCliOps(argv, ops);
|
|
37
|
+
}
|
|
38
|
+
function shellEffect(command, toolOps) {
|
|
39
|
+
const { ops, argv } = resolveShellOps(command, toolOps);
|
|
40
|
+
const matched = ops.filter((op) => op.check(argv) === null);
|
|
41
|
+
if (matched.length === 0)
|
|
42
|
+
return 'write';
|
|
43
|
+
return matched.every((op) => op.annotation(argv) === 'read') ? 'read' : 'write';
|
|
44
|
+
}
|
|
45
|
+
export async function bareRuntime() {
|
|
46
|
+
const [{ spawn }, { default: process }] = await Promise.all([
|
|
47
|
+
import('bare-subprocess'),
|
|
48
|
+
import('bare-process')
|
|
49
|
+
]);
|
|
50
|
+
const rawSpawn = spawn;
|
|
51
|
+
const spawnFn = (command, opts = {}) => rawSpawn(command, { ...opts, env: spawnEnv(process.env, process.platform, opts.env) });
|
|
52
|
+
return { spawn: spawnFn, killProcess: process.kill.bind(process) };
|
|
53
|
+
}
|
|
54
|
+
function resolveExecution(adapters, fallback, args, ctx) {
|
|
55
|
+
for (const adapter of adapters) {
|
|
56
|
+
const plan = adapter.resolve(args, ctx);
|
|
57
|
+
if (plan)
|
|
58
|
+
return plan;
|
|
59
|
+
}
|
|
60
|
+
return fallback.resolve(args, ctx);
|
|
61
|
+
}
|
|
62
|
+
function checkFileArgs(args, python) {
|
|
63
|
+
const inputs = parseInputs(args.inputs);
|
|
64
|
+
if (inputs && 'error' in inputs)
|
|
65
|
+
return inputs.error;
|
|
66
|
+
const outputs = parseOutputs(args.outputs);
|
|
67
|
+
if (outputs && 'error' in outputs)
|
|
68
|
+
return outputs.error;
|
|
69
|
+
if ((inputs || outputs) && !python) {
|
|
70
|
+
return 'inputs and outputs require language "python" with Pyodide enabled';
|
|
71
|
+
}
|
|
72
|
+
if (!python)
|
|
73
|
+
return null;
|
|
74
|
+
const workdir = parseWorkdir(args.workdir);
|
|
75
|
+
return workdir && typeof workdir === 'object' ? workdir.error : null;
|
|
76
|
+
}
|
|
77
|
+
function truncateLabel(text) {
|
|
78
|
+
return text.length > 120 ? `${text.slice(0, 120)}…` : text;
|
|
79
|
+
}
|
|
80
|
+
function packageSuffix(args) {
|
|
81
|
+
if (!Array.isArray(args.packages) || args.packages.length === 0)
|
|
82
|
+
return '';
|
|
83
|
+
const count = args.packages.length;
|
|
84
|
+
return ` (+${count} pkg${count === 1 ? '' : 's'}: ${truncateLabel(args.packages.join(', '))})`;
|
|
85
|
+
}
|
|
86
|
+
function uploadOrdinal(inputs, index) {
|
|
87
|
+
return inputs.slice(0, index + 1).filter((i) => i.attachmentId === undefined).length;
|
|
88
|
+
}
|
|
89
|
+
function fileSuffix(args) {
|
|
90
|
+
const inputs = parseInputs(args.inputs);
|
|
91
|
+
const outputs = parseOutputs(args.outputs);
|
|
92
|
+
const from = Array.isArray(inputs) && inputs.length > 0
|
|
93
|
+
? ` ← ${inputs.length} file${inputs.length === 1 ? '' : 's'} (${truncateLabel(inputs
|
|
94
|
+
.map((i, n) => `${i.attachmentId ?? `upload-${uploadOrdinal(inputs, n)}`}→${i.path}`)
|
|
95
|
+
.join(', '))})`
|
|
96
|
+
: '';
|
|
97
|
+
const to = Array.isArray(outputs) && outputs.length > 0 ? ` → ${truncateLabel(outputs.join(', '))}` : '';
|
|
98
|
+
return `${from}${to}`;
|
|
99
|
+
}
|
|
100
|
+
const PYTHON_BINS = new Set(['python', 'python3', 'py', 'pip', 'pip3']);
|
|
101
|
+
function commandRequiredError(args) {
|
|
102
|
+
const staged = Array.isArray(args.inputs) || Array.isArray(args.outputs);
|
|
103
|
+
return staged
|
|
104
|
+
? 'command is required: keep these same inputs/outputs and add either command (the bare ' +
|
|
105
|
+
'python source) or script + scriptArgs exactly as the loaded skill reference shows'
|
|
106
|
+
: 'command is required';
|
|
107
|
+
}
|
|
108
|
+
function parseScriptArgs(value) {
|
|
109
|
+
if (Array.isArray(value))
|
|
110
|
+
return value.map(String);
|
|
111
|
+
if (typeof value === 'string' && value.trim().startsWith('[')) {
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(value);
|
|
114
|
+
if (Array.isArray(parsed))
|
|
115
|
+
return parsed.map(String);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
function normalizeScriptCall(rawArgs) {
|
|
123
|
+
let script = typeof rawArgs.script === 'string' ? unquoteArg(rawArgs.script) : '';
|
|
124
|
+
let scriptArgs = parseScriptArgs(rawArgs.scriptArgs);
|
|
125
|
+
if (!script || script.includes('\n'))
|
|
126
|
+
return { script, scriptArgs };
|
|
127
|
+
if (script.includes(' ')) {
|
|
128
|
+
const tokens = script.split(/\s+/).filter(Boolean);
|
|
129
|
+
script = tokens[0] ?? '';
|
|
130
|
+
if (!scriptArgs.length)
|
|
131
|
+
scriptArgs = tokens.slice(1);
|
|
132
|
+
}
|
|
133
|
+
if (!script.startsWith('scripts/') && !script.includes('/'))
|
|
134
|
+
script = `scripts/${script}`;
|
|
135
|
+
return { script, scriptArgs };
|
|
136
|
+
}
|
|
137
|
+
export function resolveScriptRun(rawArgs, skillsDir) {
|
|
138
|
+
const { script, scriptArgs } = normalizeScriptCall(rawArgs);
|
|
139
|
+
if (!script) {
|
|
140
|
+
if (scriptArgs.length) {
|
|
141
|
+
return {
|
|
142
|
+
error: 'scriptArgs need `script`: pass the scripts/*.py path exactly as the skill instructions name it'
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (script.includes('\n')) {
|
|
148
|
+
return {
|
|
149
|
+
args: { ...rawArgs, language: 'python', command: script, script: undefined },
|
|
150
|
+
trusted: false
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const skill = typeof rawArgs.skill === 'string' ? unquoteArg(rawArgs.skill) : '';
|
|
154
|
+
if (!skill)
|
|
155
|
+
return { error: 'script runs need the owning skill name in `skill`' };
|
|
156
|
+
if (!skillsDir)
|
|
157
|
+
return { error: 'script execution is not available on this device' };
|
|
158
|
+
if (String(rawArgs.command ?? '').trim()) {
|
|
159
|
+
return { error: 'pass either command or script, never both' };
|
|
160
|
+
}
|
|
161
|
+
if (!script.startsWith('scripts/') || !script.endsWith('.py')) {
|
|
162
|
+
return {
|
|
163
|
+
error: `script must be a scripts/*.py path from the skill instructions, got: ${script}`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const segments = [...skill.split('/'), ...script.split('/')];
|
|
167
|
+
if (segments.some((segment) => segment === '' || segment === '..' || segment.includes('\\'))) {
|
|
168
|
+
return { error: `script not found in skill ${skill}: ${script}` };
|
|
169
|
+
}
|
|
170
|
+
let source;
|
|
171
|
+
try {
|
|
172
|
+
source = fs.readFileSync(path.join(skillsDir, skill, script), 'utf8');
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return { error: `script not found in skill ${skill}: ${script}` };
|
|
176
|
+
}
|
|
177
|
+
const staged = stagingGap(rawArgs, scriptArgs);
|
|
178
|
+
if (staged)
|
|
179
|
+
return { error: staged };
|
|
180
|
+
const argv = JSON.stringify([script.slice('scripts/'.length), ...scriptArgs]);
|
|
181
|
+
const command = `import sys\nsys.argv = ${argv}\n${source}`;
|
|
182
|
+
return { args: { ...rawArgs, language: 'python', command, scriptArgs }, trusted: true };
|
|
183
|
+
}
|
|
184
|
+
const SCRIPT_FILE_ARG = /^[\w][\w.-]*\.(pdf|txt|md|csv|docx|xlsx|pptx|png|jpe?g)$/i;
|
|
185
|
+
function stagingGap(rawArgs, scriptArgs) {
|
|
186
|
+
const inputs = parseInputs(rawArgs.inputs);
|
|
187
|
+
const outputs = parseOutputs(rawArgs.outputs);
|
|
188
|
+
const known = new Set([
|
|
189
|
+
...(Array.isArray(inputs) ? inputs.map((input) => input.path) : []),
|
|
190
|
+
...(Array.isArray(outputs) ? outputs : [])
|
|
191
|
+
]);
|
|
192
|
+
const missing = scriptArgs.filter((arg) => SCRIPT_FILE_ARG.test(arg) && !known.has(arg));
|
|
193
|
+
if (!missing.length)
|
|
194
|
+
return null;
|
|
195
|
+
return (`scriptArgs name ${missing.map((name) => `"${name}"`).join(', ')} but the working directory ` +
|
|
196
|
+
'starts empty: stage each existing file in inputs [{ attachmentId, path }] and declare each ' +
|
|
197
|
+
'produced file in outputs');
|
|
198
|
+
}
|
|
199
|
+
function scriptLabel(args) {
|
|
200
|
+
const { script, scriptArgs } = normalizeScriptCall(args);
|
|
201
|
+
const argsSuffix = scriptArgs.length ? ` ${scriptArgs.join(' ')}` : '';
|
|
202
|
+
return `python script ${unquoteArg(args.skill ?? '?')}/${script}${argsSuffix}`;
|
|
203
|
+
}
|
|
204
|
+
const WORKDIR_NO_ROOTS_ERROR = 'workdir is not available: no directories are granted to this device, so a working directory ' +
|
|
205
|
+
'cannot be validated. Omit workdir and use absolute paths in the command.';
|
|
206
|
+
function checkExecutionScope(args, toolOps, disablePyodide) {
|
|
207
|
+
const command = String(args.command ?? '').trim();
|
|
208
|
+
if (runsThroughPyodide(args.language, disablePyodide))
|
|
209
|
+
return null;
|
|
210
|
+
if (!disablePyodide && PYTHON_BINS.has(bareBin(firstCommandToken(command)) ?? '')) {
|
|
211
|
+
return 'run Python by passing language="python" with the bare Python source in command; the exec shell path does not run python or pip';
|
|
212
|
+
}
|
|
213
|
+
const callsFirst = /^[A-Za-z_][\w.]*\s*\(/.test(command);
|
|
214
|
+
const sourceShaped = (/^from\s+\w+\s+import\b/.test(command) || command.includes('\n')) &&
|
|
215
|
+
looksLikePythonSource(command);
|
|
216
|
+
if (!disablePyodide && (callsFirst || sourceShaped)) {
|
|
217
|
+
return 'this command is Python source: pass language "python" with the bare source in command (dependencies in packages, produced files in outputs)';
|
|
218
|
+
}
|
|
219
|
+
return checkShellScope(command, toolOps);
|
|
220
|
+
}
|
|
221
|
+
async function skillsEnv(skillSettings, ctx) {
|
|
222
|
+
const skills = ctx?.skills ?? [];
|
|
223
|
+
if (skills.length === 0)
|
|
224
|
+
return undefined;
|
|
225
|
+
const env = {};
|
|
226
|
+
for (const skill of skills) {
|
|
227
|
+
Object.assign(env, settingsEnv(skill, await skillSettings(skill, ctx?.agentId)));
|
|
228
|
+
}
|
|
229
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
230
|
+
}
|
|
231
|
+
export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide = false, credentialStore, skillSettings, realpath = defaultRealpath, deniedRoots, skillsDir } = {}) {
|
|
232
|
+
const adapters = [createPythonAdapter(disablePyodide, loadPyodide)];
|
|
233
|
+
const fallback = createShellAdapter({ spawn, killProcess, loadRuntime: bareRuntime });
|
|
234
|
+
return {
|
|
235
|
+
name: 'exec',
|
|
236
|
+
toolset: 'exec',
|
|
237
|
+
description: "Execute a shell command on the user's device, or Python via in-process Pyodide. " +
|
|
238
|
+
'One program per call: chaining, pipes, redirection and shell expansion are rejected. ' +
|
|
239
|
+
'Returns stdout, stderr, and exit code. ' +
|
|
240
|
+
'For Python: pass language="python" with the bare Python source in command — the first line of ' +
|
|
241
|
+
'command is the first line of the program, there is no shell or interpreter to invoke, and ' +
|
|
242
|
+
'packages are declared in packages rather than installed. ' +
|
|
243
|
+
'When a loaded skill names a bundled script, run it by passing skill and script (with scriptArgs) ' +
|
|
244
|
+
'instead of command — never retype a script as source. ' +
|
|
245
|
+
'Prefer one-shot commands; do not use for long-running daemons.',
|
|
246
|
+
parameters: {
|
|
247
|
+
type: 'object',
|
|
248
|
+
properties: {
|
|
249
|
+
command: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
description: 'Shell command, or — when language is python — the Python source itself, ' +
|
|
252
|
+
'whose first line is the first line of the program (no shell, no interpreter, no quoting)'
|
|
253
|
+
},
|
|
254
|
+
language: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description: 'Optional runtime. "python" runs command as Python via in-process Pyodide. Default is shell.'
|
|
257
|
+
},
|
|
258
|
+
packages: {
|
|
259
|
+
type: 'array',
|
|
260
|
+
description: 'Optional array of PyPI package names (strings) to install (via micropip) before the code runs. Only with language "python".'
|
|
261
|
+
},
|
|
262
|
+
inputs: {
|
|
263
|
+
type: 'array',
|
|
264
|
+
description: 'Optional chat attachments to place in the working directory before the code runs, as objects {attachmentId, path}. path is a bare file name (no directories). Omit attachmentId to stage the image the user attached to their latest message. Any other uploaded file — PDF, .docx, .xlsx, .pptx — is staged by attachmentId, copied verbatim from the [Attached file "…"] line on the user\'s message; ids also come from earlier tool results. Only with language "python".'
|
|
265
|
+
},
|
|
266
|
+
outputs: {
|
|
267
|
+
type: 'array',
|
|
268
|
+
description: 'Optional bare file names (strings) the code writes in the working directory; each is attached to the chat when the run succeeds. Only with language "python".'
|
|
269
|
+
},
|
|
270
|
+
workdir: {
|
|
271
|
+
type: 'string',
|
|
272
|
+
description: 'Optional working directory: a host path on the shell path, a bare directory name inside the in-memory Python filesystem with language "python".'
|
|
273
|
+
},
|
|
274
|
+
timeoutMs: {
|
|
275
|
+
type: 'number',
|
|
276
|
+
description: 'Optional timeout in milliseconds (default 120000, max 600000)'
|
|
277
|
+
},
|
|
278
|
+
maxOutputChars: {
|
|
279
|
+
type: 'number',
|
|
280
|
+
description: 'Optional stdout/stderr cap in characters (default 8192, max 65536). Raise when reading long content, e.g. a full note.'
|
|
281
|
+
},
|
|
282
|
+
script: {
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'Run a python script bundled with a loaded skill instead of passing source: a scripts/*.py path exactly as the skill instructions name it (e.g. "scripts/extract_text.py"). Requires skill; leave command unset; pass arguments in scriptArgs and stage files via inputs/outputs as the instructions show.'
|
|
285
|
+
},
|
|
286
|
+
skill: {
|
|
287
|
+
type: 'string',
|
|
288
|
+
description: 'With script: the name of the skill that bundles the script (e.g. "pdf").'
|
|
289
|
+
},
|
|
290
|
+
scriptArgs: {
|
|
291
|
+
type: 'array',
|
|
292
|
+
description: 'With script: string arguments handed to the script as its command line, in order.'
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
required: []
|
|
296
|
+
},
|
|
297
|
+
autoApprove: false,
|
|
298
|
+
effect(args, ctx) {
|
|
299
|
+
if (typeof args.script === 'string' && args.script.trim() && !args.script.includes('\n')) {
|
|
300
|
+
return 'read';
|
|
301
|
+
}
|
|
302
|
+
if (runsThroughPyodide(args.language, disablePyodide))
|
|
303
|
+
return pythonCreateEffect(args);
|
|
304
|
+
return shellEffect(String(args.command ?? '').trim(), ctx?.toolOps);
|
|
305
|
+
},
|
|
306
|
+
validate(args, ctx) {
|
|
307
|
+
const scriptRun = resolveScriptRun(args, skillsDir);
|
|
308
|
+
if (scriptRun && 'error' in scriptRun)
|
|
309
|
+
return scriptRun.error;
|
|
310
|
+
const rawArgs = scriptRun ? scriptRun.args : args;
|
|
311
|
+
const command = String(rawArgs.command ?? '').trim();
|
|
312
|
+
if (!command)
|
|
313
|
+
return commandRequiredError(rawArgs);
|
|
314
|
+
const scopeError = checkExecutionScope(rawArgs, ctx?.toolOps, disablePyodide);
|
|
315
|
+
if (scopeError)
|
|
316
|
+
return scopeError;
|
|
317
|
+
const python = runsThroughPyodide(rawArgs.language, disablePyodide);
|
|
318
|
+
const workdirGiven = rawArgs.workdir !== undefined && rawArgs.workdir !== null;
|
|
319
|
+
if (workdirGiven && !python && (ctx?.fsRoots ?? []).length === 0) {
|
|
320
|
+
return WORKDIR_NO_ROOTS_ERROR;
|
|
321
|
+
}
|
|
322
|
+
if (python) {
|
|
323
|
+
const outputs = parseOutputs(rawArgs.outputs);
|
|
324
|
+
if (outputs && 'error' in outputs)
|
|
325
|
+
return outputs.error;
|
|
326
|
+
const packages = parsePackages(rawArgs.packages);
|
|
327
|
+
if (packages && 'error' in packages)
|
|
328
|
+
return packages.error;
|
|
329
|
+
if (!scriptRun || !scriptRun.trusted) {
|
|
330
|
+
const sourceError = checkPythonSource(command);
|
|
331
|
+
if (sourceError)
|
|
332
|
+
return sourceError;
|
|
333
|
+
const pptxError = checkPptxBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
|
|
334
|
+
if (pptxError)
|
|
335
|
+
return pptxError;
|
|
336
|
+
const pdfError = checkPdfBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
|
|
337
|
+
if (pdfError)
|
|
338
|
+
return pdfError;
|
|
339
|
+
const bareLength = checkBareLengthArgs(unwrapPythonSource(command) ?? command, packages);
|
|
340
|
+
if (bareLength)
|
|
341
|
+
return bareLength;
|
|
342
|
+
const inputs = parseInputs(rawArgs.inputs);
|
|
343
|
+
const unstagedRead = checkUnstagedRead(unwrapPythonSource(command) ?? command, Array.isArray(inputs) ? inputs : undefined);
|
|
344
|
+
if (unstagedRead)
|
|
345
|
+
return unstagedRead;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const parsed = parsePackages(rawArgs.packages);
|
|
349
|
+
if (parsed && 'error' in parsed)
|
|
350
|
+
return parsed.error;
|
|
351
|
+
if (parsed && !python) {
|
|
352
|
+
return 'packages require language "python" with Pyodide enabled';
|
|
353
|
+
}
|
|
354
|
+
return checkFileArgs(rawArgs, python);
|
|
355
|
+
},
|
|
356
|
+
resource(args) {
|
|
357
|
+
const script = typeof args.script === 'string' ? args.script : '';
|
|
358
|
+
if (script.trim() && !script.includes('\n')) {
|
|
359
|
+
return { kind: 'command', label: scriptLabel(args) };
|
|
360
|
+
}
|
|
361
|
+
const command = String(args.command ?? '').trim() || script.trim();
|
|
362
|
+
if (!command)
|
|
363
|
+
return null;
|
|
364
|
+
const workdir = typeof args.workdir === 'string' && args.workdir.trim() ? args.workdir.trim() : null;
|
|
365
|
+
const body = runsThroughPyodide(args.language, disablePyodide)
|
|
366
|
+
? `python${packageSuffix(args)}${fileSuffix(args)}: ${(unwrapPythonSource(command) ?? command).slice(0, 120)}`
|
|
367
|
+
: command;
|
|
368
|
+
return {
|
|
369
|
+
kind: 'command',
|
|
370
|
+
label: workdir ? `${body} (cwd: ${workdir})` : body
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
async execute(args, ctx) {
|
|
374
|
+
const scriptRun = resolveScriptRun(args, skillsDir);
|
|
375
|
+
if (scriptRun && 'error' in scriptRun)
|
|
376
|
+
return { error: scriptRun.error };
|
|
377
|
+
const rawArgs = scriptRun ? scriptRun.args : args;
|
|
378
|
+
const command = String(rawArgs.command ?? '').trim();
|
|
379
|
+
if (!command)
|
|
380
|
+
return { error: commandRequiredError(rawArgs) };
|
|
381
|
+
const scopeError = checkExecutionScope(rawArgs, ctx?.toolOps, disablePyodide);
|
|
382
|
+
if (scopeError)
|
|
383
|
+
return { error: scopeError };
|
|
384
|
+
const workdir = rawArgs.workdir === undefined || rawArgs.workdir === null
|
|
385
|
+
? undefined
|
|
386
|
+
: String(rawArgs.workdir);
|
|
387
|
+
const onShellPath = !runsThroughPyodide(rawArgs.language, disablePyodide);
|
|
388
|
+
let cwd = workdir;
|
|
389
|
+
if (workdir !== undefined && onShellPath) {
|
|
390
|
+
const roots = ctx?.fsRoots ?? [];
|
|
391
|
+
if (roots.length === 0)
|
|
392
|
+
return { error: WORKDIR_NO_ROOTS_ERROR };
|
|
393
|
+
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
394
|
+
const resolved = await resolveKey(workdir, roots, realpath, deny);
|
|
395
|
+
if ('error' in resolved)
|
|
396
|
+
return { error: resolved.error };
|
|
397
|
+
if (resolved.root.kind !== 'local') {
|
|
398
|
+
return { error: 'workdir must be a local directory, not a drive' };
|
|
399
|
+
}
|
|
400
|
+
if (!resolved.exists)
|
|
401
|
+
return { error: `workdir does not exist: ${workdir}` };
|
|
402
|
+
cwd = hostPath(resolved.root, resolved.key);
|
|
403
|
+
}
|
|
404
|
+
let timeout = parsePositiveInt(rawArgs.timeoutMs, 'timeoutMs', 600_000);
|
|
405
|
+
if (timeout && typeof timeout === 'object')
|
|
406
|
+
return timeout;
|
|
407
|
+
if (ctx?.runBudget) {
|
|
408
|
+
const remaining = ctx.runBudget.deadlineMs - Date.now();
|
|
409
|
+
if (remaining <= 0) {
|
|
410
|
+
return {
|
|
411
|
+
error: 'run wall-clock budget exhausted; no time remains for this call',
|
|
412
|
+
guidance: true
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (timeout !== undefined)
|
|
416
|
+
timeout = Math.min(timeout, remaining);
|
|
417
|
+
}
|
|
418
|
+
const maxOutputChars = parsePositiveInt(rawArgs.maxOutputChars, 'maxOutputChars', MAX_OUTPUT_CHARS_LIMIT);
|
|
419
|
+
if (maxOutputChars && typeof maxOutputChars === 'object')
|
|
420
|
+
return maxOutputChars;
|
|
421
|
+
if (!isPythonLanguage(rawArgs.language)) {
|
|
422
|
+
const obsidian = await routeObsidianCommand(command, ctx, {
|
|
423
|
+
credentialStore,
|
|
424
|
+
spawn,
|
|
425
|
+
killProcess,
|
|
426
|
+
loadRuntime: spawn ? undefined : bareRuntime,
|
|
427
|
+
timeoutMs: timeout,
|
|
428
|
+
maxOutputChars
|
|
429
|
+
});
|
|
430
|
+
if (obsidian)
|
|
431
|
+
return obsidian;
|
|
432
|
+
}
|
|
433
|
+
const skillEnv = skillSettings ? await skillsEnv(skillSettings, ctx) : undefined;
|
|
434
|
+
try {
|
|
435
|
+
const execution = resolveExecution(adapters, fallback, rawArgs, {
|
|
436
|
+
attach: ctx?.attach,
|
|
437
|
+
fetchAttachment: ctx?.fetchAttachment,
|
|
438
|
+
turnImages: ctx?.turnImages,
|
|
439
|
+
runBudget: ctx?.runBudget
|
|
440
|
+
});
|
|
441
|
+
return await execution.run({
|
|
442
|
+
cwd,
|
|
443
|
+
timeoutMs: timeout,
|
|
444
|
+
maxOutputChars,
|
|
445
|
+
signal: ctx?.signal,
|
|
446
|
+
...(skillEnv ? { env: skillEnv } : {})
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
451
|
+
return {
|
|
452
|
+
error: error instanceof AbortedError || error.name === 'AbortError'
|
|
453
|
+
? 'command aborted'
|
|
454
|
+
: error.message
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
2
|
+
import { type DriveRoot, type FsToolOptions, type Realpath } from './fs/scope.ts';
|
|
3
|
+
export declare const DEFAULT_GLOB_MATCHES = 100;
|
|
4
|
+
export declare const MAX_GLOB_MATCHES = 1000;
|
|
5
|
+
export interface SearchTarget {
|
|
6
|
+
root: DriveRoot;
|
|
7
|
+
folder: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function searchTargets(dir: unknown, roots: readonly DriveRoot[], realpath: Realpath, deny?: readonly string[]): Promise<{
|
|
10
|
+
targets: SearchTarget[];
|
|
11
|
+
} | {
|
|
12
|
+
error: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function relativeKey(key: string, folder: string): string;
|
|
15
|
+
export declare function createFileGlobTool({ realpath, deniedRoots }?: FsToolOptions): ToolDefinition;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { compileGlob } from "./fs/glob.js";
|
|
2
|
+
import { NO_DIRS_GRANTED, canonicalDenyRoots, defaultRealpath, isDeniedPath, resolveKey } from "./fs/scope.js";
|
|
3
|
+
import { walkFiles } from "./fs/walk.js";
|
|
4
|
+
export const DEFAULT_GLOB_MATCHES = 100;
|
|
5
|
+
export const MAX_GLOB_MATCHES = 1000;
|
|
6
|
+
export async function searchTargets(dir, roots, realpath, deny) {
|
|
7
|
+
if (typeof dir === 'string' && dir.trim()) {
|
|
8
|
+
const resolved = await resolveKey(dir, roots, realpath, deny);
|
|
9
|
+
if ('error' in resolved)
|
|
10
|
+
return resolved;
|
|
11
|
+
if (resolved.root.kind === 'local') {
|
|
12
|
+
if (!resolved.exists)
|
|
13
|
+
return { error: `no such file: ${dir}` };
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
let hasEntry = false;
|
|
17
|
+
for await (const _entry of resolved.root.drive.list(resolved.key)) {
|
|
18
|
+
hasEntry = true;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
if (!hasEntry && (await resolved.root.drive.entry(resolved.key)) === null) {
|
|
22
|
+
return { error: `no such file: ${dir}` };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { targets: [{ root: resolved.root, folder: resolved.key }] };
|
|
26
|
+
}
|
|
27
|
+
if (roots.length === 0)
|
|
28
|
+
return { error: NO_DIRS_GRANTED };
|
|
29
|
+
return { targets: roots.map((root) => ({ root, folder: '/' })) };
|
|
30
|
+
}
|
|
31
|
+
export function relativeKey(key, folder) {
|
|
32
|
+
return folder === '/' ? key.slice(1) : key.slice(folder.length + 1);
|
|
33
|
+
}
|
|
34
|
+
export function createFileGlobTool({ realpath = defaultRealpath, deniedRoots } = {}) {
|
|
35
|
+
return {
|
|
36
|
+
name: 'file_glob',
|
|
37
|
+
toolset: 'files',
|
|
38
|
+
description: 'List files matching a glob pattern inside the granted directories. ' +
|
|
39
|
+
'Use ** to cross directories (e.g. "**/*.ts"). Returns absolute paths. ' +
|
|
40
|
+
'Symlinks are never followed; skippedLinks counts the ones left out.',
|
|
41
|
+
parameters: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
pattern: { type: 'string', description: 'Glob pattern relative to the searched directory' },
|
|
45
|
+
dir: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Optional directory to search (default: every granted directory)'
|
|
48
|
+
},
|
|
49
|
+
limit: {
|
|
50
|
+
type: 'number',
|
|
51
|
+
description: `Optional match cap (default ${DEFAULT_GLOB_MATCHES}, max ${MAX_GLOB_MATCHES})`
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ['pattern']
|
|
55
|
+
},
|
|
56
|
+
effect: () => 'read',
|
|
57
|
+
autoApprove: true,
|
|
58
|
+
rootsRequired: true,
|
|
59
|
+
validate(rawArgs) {
|
|
60
|
+
if (typeof rawArgs.pattern !== 'string' || !rawArgs.pattern.trim()) {
|
|
61
|
+
return 'pattern is required';
|
|
62
|
+
}
|
|
63
|
+
const limit = rawArgs.limit;
|
|
64
|
+
if (limit !== undefined && limit !== null) {
|
|
65
|
+
const n = Number(limit);
|
|
66
|
+
if (!Number.isInteger(n) || n <= 0 || n > MAX_GLOB_MATCHES) {
|
|
67
|
+
return `limit must be a positive integer ≤ ${MAX_GLOB_MATCHES}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
},
|
|
72
|
+
resource(args) {
|
|
73
|
+
const pattern = String(args.pattern ?? '').trim();
|
|
74
|
+
return pattern ? { kind: 'file', label: `glob ${pattern}` } : null;
|
|
75
|
+
},
|
|
76
|
+
async execute(rawArgs, ctx) {
|
|
77
|
+
const roots = ctx?.fsRoots ?? [];
|
|
78
|
+
const pattern = String(rawArgs.pattern ?? '').trim();
|
|
79
|
+
if (!pattern)
|
|
80
|
+
return { error: 'pattern is required' };
|
|
81
|
+
const limit = rawArgs.limit ? Number(rawArgs.limit) : DEFAULT_GLOB_MATCHES;
|
|
82
|
+
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
83
|
+
const scoped = await searchTargets(rawArgs.dir, roots, realpath, deny);
|
|
84
|
+
if ('error' in scoped)
|
|
85
|
+
return scoped;
|
|
86
|
+
const match = compileGlob(pattern);
|
|
87
|
+
const matches = [];
|
|
88
|
+
let truncated = false;
|
|
89
|
+
let skippedLinks = 0;
|
|
90
|
+
for (const target of scoped.targets) {
|
|
91
|
+
const walk = await walkFiles(target.root, target.folder);
|
|
92
|
+
truncated ||= walk.truncated;
|
|
93
|
+
skippedLinks += walk.skippedLinks;
|
|
94
|
+
for (const file of walk.files) {
|
|
95
|
+
if (isDeniedPath(file.path, deny))
|
|
96
|
+
continue;
|
|
97
|
+
if (!match(relativeKey(file.key, target.folder)))
|
|
98
|
+
continue;
|
|
99
|
+
matches.push(file.path);
|
|
100
|
+
if (matches.length >= limit) {
|
|
101
|
+
return { matches, truncated: true, ...(skippedLinks ? { skippedLinks } : {}) };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
matches,
|
|
107
|
+
...(truncated ? { truncated } : {}),
|
|
108
|
+
...(skippedLinks ? { skippedLinks } : {})
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Writable } from 'streamx';
|
|
2
|
+
export interface DriveEntry {
|
|
3
|
+
key: string;
|
|
4
|
+
value: {
|
|
5
|
+
executable: boolean;
|
|
6
|
+
linkname: string | null;
|
|
7
|
+
blob: {
|
|
8
|
+
byteLength: number;
|
|
9
|
+
} | null;
|
|
10
|
+
metadata: unknown;
|
|
11
|
+
};
|
|
12
|
+
mtime?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface Drive {
|
|
15
|
+
entry(key: string, opts?: {
|
|
16
|
+
follow?: boolean;
|
|
17
|
+
}): Promise<DriveEntry | null>;
|
|
18
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
19
|
+
list(folder?: string, opts?: {
|
|
20
|
+
skip?: ReadonlySet<string>;
|
|
21
|
+
}): AsyncIterable<DriveEntry>;
|
|
22
|
+
createReadStream(key: string): AsyncIterable<Uint8Array>;
|
|
23
|
+
createWriteStream(key: string, opts?: {
|
|
24
|
+
executable?: boolean;
|
|
25
|
+
}): Writable;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function compileGlob(pattern: string): (relPath: string) => boolean;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function compileGlob(pattern) {
|
|
2
|
+
let source = '';
|
|
3
|
+
let i = 0;
|
|
4
|
+
while (i < pattern.length) {
|
|
5
|
+
const c = pattern[i];
|
|
6
|
+
if (c === '*') {
|
|
7
|
+
if (pattern[i + 1] === '*') {
|
|
8
|
+
if (pattern[i + 2] === '/') {
|
|
9
|
+
source += '(?:.*/)?';
|
|
10
|
+
i += 3;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
source += '.*';
|
|
14
|
+
i += 2;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
source += '[^/]*';
|
|
19
|
+
i += 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if (c === '?') {
|
|
23
|
+
source += '[^/]';
|
|
24
|
+
i += 1;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const ch = c;
|
|
28
|
+
source += /[.+^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
|
|
29
|
+
i += 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const regex = new RegExp(`^${source}$`);
|
|
33
|
+
return (relPath) => regex.test(relPath);
|
|
34
|
+
}
|