@qvac/harness 0.4.0 → 0.6.0
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 +740 -141
- package/README.md +75 -7
- package/dist/harness.bundle +72 -50
- package/dist-lib/index.d.ts +49 -39
- package/dist-lib/index.js +448 -161
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
- package/dist-lib/lib/audio-transcode-bare.js +28 -18
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
- package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
- package/dist-lib/lib/capabilities/audio-resample.js +30 -18
- package/dist-lib/lib/capabilities/completion.d.ts +20 -19
- package/dist-lib/lib/capabilities/completion.js +193 -132
- package/dist-lib/lib/capabilities/download.d.ts +6 -6
- package/dist-lib/lib/capabilities/download.js +15 -8
- package/dist-lib/lib/capabilities/embed.js +2 -1
- package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
- package/dist-lib/lib/capabilities/enhance.js +7 -4
- package/dist-lib/lib/capabilities/invoke.js +7 -4
- package/dist-lib/lib/capabilities/kv-cache.js +2 -1
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +9 -7
- package/dist-lib/lib/capabilities/preview-model-source.js +80 -28
- package/dist-lib/lib/capabilities/speech-text.js +5 -4
- package/dist-lib/lib/capabilities/tool-selection.js +10 -9
- package/dist-lib/lib/capabilities/tool-turns.js +30 -16
- package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
- package/dist-lib/lib/capabilities/voice.d.ts +5 -1
- package/dist-lib/lib/capabilities/voice.js +22 -12
- package/dist-lib/lib/connect.d.ts +13 -32
- package/dist-lib/lib/connect.js +170 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +35 -15
- package/dist-lib/lib/diagram-repair.d.ts +4 -4
- package/dist-lib/lib/diagram-repair.js +6 -3
- package/dist-lib/lib/docx/index.d.ts +3 -2
- package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
- package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
- package/dist-lib/lib/hardware/capability-probe.js +20 -10
- package/dist-lib/lib/hardware/gpu-classify.js +6 -3
- package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
- package/dist-lib/lib/hardware/gpu-probe.js +4 -2
- package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
- package/dist-lib/lib/hardware/parsers.js +8 -4
- package/dist-lib/lib/helpers/buffer-burst.js +4 -2
- package/dist-lib/lib/helpers/control-tokens.js +4 -2
- package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
- package/dist-lib/lib/helpers/join-guard.js +4 -2
- package/dist-lib/lib/helpers/load-stall.js +4 -2
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
- package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
- package/dist-lib/lib/helpers/race-abort.js +2 -1
- package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
- package/dist-lib/lib/helpers/race-timeout.js +14 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
- package/dist-lib/lib/helpers/run-outcome.js +36 -20
- package/dist-lib/lib/helpers/spawn-path.js +6 -3
- package/dist-lib/lib/helpers/text-coalescer.js +6 -3
- package/dist-lib/lib/helpers/thinking.js +38 -45
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
- package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
- package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
- package/dist-lib/lib/helpers/tool-intent.js +4 -2
- package/dist-lib/lib/helpers/tool-label.js +18 -9
- package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
- package/dist-lib/lib/hooks/pipeline.js +6 -3
- package/dist-lib/lib/hooks/run.js +12 -8
- package/dist-lib/lib/model-download-progress.js +10 -8
- package/dist-lib/lib/models/context.d.ts +1 -1
- package/dist-lib/lib/models/context.js +5 -1
- package/dist-lib/lib/models/custom-model-cache.d.ts +10 -1
- package/dist-lib/lib/models/custom-model-cache.js +43 -15
- package/dist-lib/lib/models/gguf-header.d.ts +3 -2
- package/dist-lib/lib/models/gguf-header.js +44 -22
- package/dist-lib/lib/models/idle-unload.d.ts +14 -0
- package/dist-lib/lib/models/idle-unload.js +51 -0
- package/dist-lib/lib/models/kv-cache.d.ts +3 -2
- package/dist-lib/lib/models/kv-cache.js +4 -3
- package/dist-lib/lib/models/llm-presets.d.ts +2 -2
- package/dist-lib/lib/models/llm-presets.js +4 -2
- package/dist-lib/lib/models/model-catalog-constants.js +3 -2
- package/dist-lib/lib/models/model-catalog.d.ts +15 -20
- package/dist-lib/lib/models/model-catalog.js +66 -31
- package/dist-lib/lib/models/model-delete.js +18 -8
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +8 -3
- package/dist-lib/lib/models/model-list.js +11 -6
- package/dist-lib/lib/models/model-pool.d.ts +48 -10
- package/dist-lib/lib/models/model-pool.js +106 -45
- package/dist-lib/lib/models/model-source.js +10 -5
- package/dist-lib/lib/models/music-catalog.d.ts +2 -2
- package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
- package/dist-lib/lib/models/nmt-catalog.js +10 -5
- package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
- package/dist-lib/lib/models/opencl-cache.js +5 -3
- package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
- package/dist-lib/lib/models/sdk-faults.js +9 -6
- package/dist-lib/lib/models/shards.d.ts +11 -0
- package/dist-lib/lib/models/shards.js +15 -0
- package/dist-lib/lib/models/tool-dialect.js +10 -5
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
- package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
- package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
- package/dist-lib/lib/office/index.d.ts +3 -2
- package/dist-lib/lib/office/python.d.ts +3 -2
- package/dist-lib/lib/office/python.js +2 -1
- package/dist-lib/lib/prompt.d.ts +5 -4
- package/dist-lib/lib/prompt.js +8 -4
- package/dist-lib/lib/queue.js +10 -5
- package/dist-lib/lib/resolution-detector.js +21 -11
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -1
- package/dist-lib/lib/sdk-storage.js +4 -2
- package/dist-lib/lib/serve.js +122 -66
- package/dist-lib/lib/spawn.js +7 -4
- package/dist-lib/lib/tmp-path.d.ts +7 -6
- package/dist-lib/lib/tool-call-display.d.ts +2 -2
- package/dist-lib/lib/tools/create-documents.js +18 -10
- package/dist-lib/lib/tools/edit-documents.js +23 -13
- package/dist-lib/lib/tools/edit-image.d.ts +2 -2
- package/dist-lib/lib/tools/edit-image.js +60 -36
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
- package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
- package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
- package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
- package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
- package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
- package/dist-lib/lib/tools/exec/runner.js +5 -3
- package/dist-lib/lib/tools/exec/scope.js +52 -28
- package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
- package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
- package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
- package/dist-lib/lib/tools/exec/video-tools.js +2 -2
- package/dist-lib/lib/tools/exec.d.ts +6 -8
- package/dist-lib/lib/tools/exec.js +113 -54
- package/dist-lib/lib/tools/file-glob.d.ts +4 -5
- package/dist-lib/lib/tools/file-glob.js +16 -11
- package/dist-lib/lib/tools/fs/glob.js +13 -18
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
- package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
- package/dist-lib/lib/tools/fs/remote.js +13 -7
- package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
- package/dist-lib/lib/tools/fs/scope.js +20 -12
- package/dist-lib/lib/tools/fs/walk.js +6 -3
- package/dist-lib/lib/tools/fs/write.d.ts +3 -2
- package/dist-lib/lib/tools/generate-image.d.ts +11 -9
- package/dist-lib/lib/tools/generate-image.js +73 -37
- package/dist-lib/lib/tools/generate-music.d.ts +22 -11
- package/dist-lib/lib/tools/generate-music.js +79 -48
- package/dist-lib/lib/tools/grep.js +66 -41
- package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
- package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
- package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
- package/dist-lib/lib/tools/http-request.d.ts +1 -1
- package/dist-lib/lib/tools/http-request.js +33 -18
- package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
- package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
- package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
- package/dist-lib/lib/tools/mcp-call.js +37 -16
- package/dist-lib/lib/tools/operations/builders.js +10 -6
- package/dist-lib/lib/tools/operations/compile.js +20 -10
- package/dist-lib/lib/tools/operations/decoders.js +12 -6
- package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
- package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
- package/dist-lib/lib/tools/outbound-guard.js +14 -8
- package/dist-lib/lib/tools/private-ip.js +8 -7
- package/dist-lib/lib/tools/rag-search.js +14 -7
- package/dist-lib/lib/tools/read-files.js +23 -20
- package/dist-lib/lib/tools/skill.js +19 -10
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
- package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
- package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bin-check.js +10 -3
- package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
- package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
- package/dist-lib/lib/tools/skills/catalog.js +33 -28
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
- package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
- package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
- package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
- package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
- package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
- package/dist-lib/lib/tools/skills/credential-check.js +24 -12
- package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
- package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
- package/dist-lib/lib/tools/skills/credential-health.js +5 -3
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
- package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
- package/dist-lib/lib/tools/skills/mention.js +7 -6
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
- package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
- package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
- package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
- package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
- package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
- package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
- package/dist-lib/lib/tools/skills/skill-state.js +48 -29
- package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
- package/dist-lib/lib/tools/toolsets.js +2 -1
- package/dist-lib/lib/tools/translate.d.ts +4 -5
- package/dist-lib/lib/tools/translate.js +31 -14
- package/dist-lib/lib/tools/url-guard.js +2 -1
- package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
- package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
- package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
- package/dist-lib/lib/tools/weather-lookup.js +18 -12
- package/dist-lib/lib/tools/web-fetch.js +47 -24
- package/dist-lib/lib/tools/web-search.js +8 -3
- package/dist-lib/lib/validating-lookup.d.ts +4 -4
- package/dist-lib/lib/wav-encode.d.ts +1 -1
- package/dist-lib/lib/wav-encode.js +11 -10
- package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
- package/dist-lib/lib/web-fetch/extract.js +4 -2
- package/dist-lib/lib/web-fetch/images.js +8 -4
- package/dist-lib/lib/web-fetch/video.d.ts +2 -2
- package/dist-lib/lib/web-fetch/video.js +8 -4
- package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
- package/dist-lib/lib/web-search/engines/aol.js +1 -1
- package/dist-lib/lib/web-search/engines/bing.js +1 -1
- package/dist-lib/lib/web-search/engines/brave.js +4 -1
- package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
- package/dist-lib/lib/web-search/engines/google.js +7 -3
- package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
- package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
- package/dist-lib/lib/web-search/fetch-html.js +13 -7
- package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
- package/dist-lib/lib/web-search/orchestrator.js +3 -3
- package/dist-lib/lib/wire.d.ts +3 -2
- package/dist-lib/lib/wire.js +23 -15
- package/dist-lib/schema/baseline.js +10 -5
- package/dist-lib/schema/build.js +2 -1
- package/dist-lib/schema/harness.d.ts +15 -2
- package/dist-lib/schema/harness.js +39 -2
- package/dist-lib/schema/release-tags.d.ts +3 -2
- package/dist-lib/schema/release-tags.js +6 -3
- package/dist-lib/spec/bind.js +9 -0
- package/dist-lib/spec/calls.js +3 -0
- package/dist-lib/spec/hrpc/hrpc.json +39 -0
- package/dist-lib/spec/hrpc/index.js +39 -3
- package/dist-lib/spec/hrpc/messages.js +105 -8
- package/dist-lib/spec/hyperschema/index.js +105 -8
- package/dist-lib/spec/hyperschema/schema.json +77 -1
- package/dist-lib/spec/hyperschema/types.d.ts +25 -1
- package/package.json +14 -14
- package/worker/harness-sidecar.mjs +15 -22
- package/worker/stow-entry.mjs +7 -4
- package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
- /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
|
@@ -25,12 +25,13 @@ function importedModules(source) {
|
|
|
25
25
|
continue;
|
|
26
26
|
}
|
|
27
27
|
const imported = /^\s*import\s+([\w.]+(?:\s*,\s*[\w.]+)*)/.exec(line)?.[1];
|
|
28
|
-
if (imported)
|
|
28
|
+
if (imported) {
|
|
29
29
|
modules.push(...imported.split(',').map((m) => m.trim()));
|
|
30
|
+
}
|
|
30
31
|
}
|
|
31
|
-
return modules.map((m) => m.split('.')[0]);
|
|
32
|
+
return modules.map((m) => m.split('.')[0] ?? '');
|
|
32
33
|
}
|
|
33
|
-
export function videoToolError(command, packages, python, network = false) {
|
|
34
|
+
export function videoToolError(command, { packages, python, network = false }) {
|
|
34
35
|
const names = [
|
|
35
36
|
dashModule(command) ?? '',
|
|
36
37
|
...(pipInstallTargets(command) ?? []),
|
|
@@ -38,14 +39,18 @@ export function videoToolError(command, packages, python, network = false) {
|
|
|
38
39
|
...(python ? importedModules(unwrapPythonSource(command) ?? command) : [commandBin(command)])
|
|
39
40
|
];
|
|
40
41
|
const tools = names.map(videoTool).filter((tool) => tool !== null);
|
|
41
|
-
if (tools.length === 0)
|
|
42
|
+
if (tools.length === 0) {
|
|
42
43
|
return null;
|
|
43
|
-
|
|
44
|
+
}
|
|
45
|
+
if (!network) {
|
|
44
46
|
return VIDEO_TOOLS_ERROR;
|
|
45
|
-
|
|
47
|
+
}
|
|
48
|
+
if (tools.some(isTranscoder)) {
|
|
46
49
|
return TRANSCODE_ERROR;
|
|
47
|
-
|
|
50
|
+
}
|
|
51
|
+
if (python) {
|
|
48
52
|
return null;
|
|
53
|
+
}
|
|
49
54
|
const program = videoTool(commandBin(command)) ?? videoTool(dashModule(command) ?? '');
|
|
50
55
|
return program ? downloaderShellError(program) : null;
|
|
51
56
|
}
|
|
@@ -59,25 +64,30 @@ function writesFile(words, curl) {
|
|
|
59
64
|
const long = curl ? '--output' : '--output-document';
|
|
60
65
|
let saves = !curl;
|
|
61
66
|
for (let i = 1; i < words.length; i++) {
|
|
62
|
-
const word = words[i];
|
|
63
|
-
if (curl && /^--remote-name(-all)?$/.test(word))
|
|
67
|
+
const word = words[i] ?? '';
|
|
68
|
+
if (curl && /^--remote-name(-all)?$/.test(word)) {
|
|
64
69
|
saves = true;
|
|
70
|
+
}
|
|
65
71
|
if (word === long || word.startsWith(`${long}=`)) {
|
|
66
72
|
const value = word === long ? words[++i] : word.slice(long.length + 1);
|
|
67
73
|
saves = value !== '-';
|
|
68
74
|
continue;
|
|
69
75
|
}
|
|
70
|
-
if (!/^-[A-Za-z]/.test(word))
|
|
76
|
+
if (!/^-[A-Za-z]/.test(word)) {
|
|
71
77
|
continue;
|
|
78
|
+
}
|
|
72
79
|
for (let j = 1; j < word.length; j++) {
|
|
73
|
-
const flag = word
|
|
74
|
-
if (curl && flag === 'O')
|
|
80
|
+
const flag = word.charAt(j);
|
|
81
|
+
if (curl && flag === 'O') {
|
|
75
82
|
saves = true;
|
|
76
|
-
|
|
83
|
+
}
|
|
84
|
+
if (!valued.has(flag)) {
|
|
77
85
|
continue;
|
|
86
|
+
}
|
|
78
87
|
const value = j + 1 < word.length ? word.slice(j + 1) : words[++i];
|
|
79
|
-
if (flag === output)
|
|
88
|
+
if (flag === output) {
|
|
80
89
|
saves = value !== '-';
|
|
90
|
+
}
|
|
81
91
|
break;
|
|
82
92
|
}
|
|
83
93
|
}
|
|
@@ -85,14 +95,16 @@ function writesFile(words, curl) {
|
|
|
85
95
|
}
|
|
86
96
|
export function shellDownloadNote(command) {
|
|
87
97
|
const bin = commandBin(command);
|
|
88
|
-
if (bin !== 'curl' && bin !== 'wget')
|
|
98
|
+
if (bin !== 'curl' && bin !== 'wget') {
|
|
89
99
|
return null;
|
|
100
|
+
}
|
|
90
101
|
return writesFile(shellArgv(command), bin === 'curl') ? SHELL_DOWNLOAD_NOTE : null;
|
|
91
102
|
}
|
|
92
103
|
export function pipError(command, sandbox = false) {
|
|
93
|
-
if (!PIP.test(commandBin(command)) && dashModule(command) !== 'pip')
|
|
104
|
+
if (!PIP.test(commandBin(command)) && dashModule(command) !== 'pip') {
|
|
94
105
|
return null;
|
|
95
|
-
|
|
106
|
+
}
|
|
107
|
+
return pipMessage(pipInstallTargets(command), { sandbox });
|
|
96
108
|
}
|
|
97
109
|
function firstCodeLine(command) {
|
|
98
110
|
return (command
|
|
@@ -106,18 +118,21 @@ export function startsLikePython(command) {
|
|
|
106
118
|
}
|
|
107
119
|
export function isPythonSourceShaped(command) {
|
|
108
120
|
const first = firstCodeLine(command);
|
|
109
|
-
if (/^from\s+[\w.]+\s+import\b/.test(first))
|
|
121
|
+
if (/^from\s+[\w.]+\s+import\b/.test(first)) {
|
|
110
122
|
return true;
|
|
123
|
+
}
|
|
111
124
|
if (/^[A-Za-z_][\w.]*\s*\(/.test(first) && !/^[A-Za-z_]\w*\s*\(\s*\)\s*\{/.test(first)) {
|
|
112
125
|
return true;
|
|
113
126
|
}
|
|
114
|
-
if (!PYTHON_START.test(first))
|
|
127
|
+
if (!PYTHON_START.test(first)) {
|
|
115
128
|
return false;
|
|
129
|
+
}
|
|
116
130
|
return command.trim().includes('\n') || sanitizeCommand(command) !== null;
|
|
117
131
|
}
|
|
118
132
|
export function pythonRedirectError(command, sandbox = false) {
|
|
119
133
|
const pip = pipError(command, sandbox);
|
|
120
|
-
if (pip)
|
|
134
|
+
if (pip) {
|
|
121
135
|
return pip;
|
|
136
|
+
}
|
|
122
137
|
return INTERPRETER.test(commandBin(command)) ? PYTHON_BIN_ERROR : null;
|
|
123
138
|
}
|
|
@@ -26,10 +26,10 @@ export function downloaderShellError(tool) {
|
|
|
26
26
|
'in outputs so it reaches the user.');
|
|
27
27
|
}
|
|
28
28
|
export function videoTool(name) {
|
|
29
|
-
const bare = name
|
|
29
|
+
const bare = (name
|
|
30
30
|
.trim()
|
|
31
31
|
.replace(/^['"]|['"]$/g, '')
|
|
32
|
-
.split(/[=<>!~[\s]/)[0]
|
|
32
|
+
.split(/[=<>!~[\s]/)[0] ?? '')
|
|
33
33
|
.toLowerCase()
|
|
34
34
|
.replace(/\.exe$/, '')
|
|
35
35
|
.replace(/[-_.]+/g, '-');
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
2
|
+
import type { ExecError } from './exec/adapters/python/output-policy.ts';
|
|
2
3
|
import type { LoadPyodideFn } from './exec/adapters/python/pyodide.ts';
|
|
4
|
+
import { type ShellRuntime } from './exec/adapters/shell/shell-adapter.ts';
|
|
3
5
|
import { type KillProcess, type SpawnFn } from './exec/runner.ts';
|
|
4
6
|
import { type Realpath } from './fs/scope.ts';
|
|
5
7
|
import type { CredentialStoreFn } from './skills/auth-resolver.ts';
|
|
@@ -17,14 +19,10 @@ export interface ExecToolOptions {
|
|
|
17
19
|
readonly deniedRoots?: readonly string[];
|
|
18
20
|
readonly skillsDir?: string;
|
|
19
21
|
}
|
|
20
|
-
export declare function bareRuntime(): Promise<
|
|
21
|
-
|
|
22
|
-
readonly killProcess: KillProcess;
|
|
23
|
-
}>;
|
|
24
|
-
export declare function resolveScriptRun(rawArgs: Record<string, unknown>, skillsDir: string | undefined): {
|
|
22
|
+
export declare function bareRuntime(): Promise<Required<ShellRuntime>>;
|
|
23
|
+
export interface ScriptRun {
|
|
25
24
|
args: Record<string, unknown>;
|
|
26
25
|
trusted: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
} | null;
|
|
26
|
+
}
|
|
27
|
+
export declare function resolveScriptRun(rawArgs: Record<string, unknown>, skillsDir: string | undefined): ScriptRun | ExecError | null;
|
|
30
28
|
export declare function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide, pythonNetwork, credentialStore, skillSettings, realpath, deniedRoots, skillsDir }?: ExecToolOptions): ToolDefinition;
|
|
@@ -31,18 +31,21 @@ function resolveShellOps(command, toolOps) {
|
|
|
31
31
|
}
|
|
32
32
|
function checkShellScope(command, toolOps) {
|
|
33
33
|
const unsafe = sanitizeCommand(command);
|
|
34
|
-
if (unsafe)
|
|
34
|
+
if (unsafe) {
|
|
35
35
|
return unsafe;
|
|
36
|
+
}
|
|
36
37
|
const { ops, argv } = resolveShellOps(command, toolOps);
|
|
37
|
-
if (ops.length === 0)
|
|
38
|
+
if (ops.length === 0) {
|
|
38
39
|
return null;
|
|
40
|
+
}
|
|
39
41
|
return checkCliOps(argv, ops);
|
|
40
42
|
}
|
|
41
43
|
function shellEffect(command, toolOps) {
|
|
42
44
|
const { ops, argv } = resolveShellOps(command, toolOps);
|
|
43
45
|
const matched = ops.filter((op) => op.check(argv) === null);
|
|
44
|
-
if (matched.length === 0)
|
|
46
|
+
if (matched.length === 0) {
|
|
45
47
|
return 'write';
|
|
48
|
+
}
|
|
46
49
|
return matched.every((op) => op.annotation(argv) === 'read') ? 'read' : 'write';
|
|
47
50
|
}
|
|
48
51
|
export async function bareRuntime() {
|
|
@@ -54,11 +57,12 @@ export async function bareRuntime() {
|
|
|
54
57
|
const spawnFn = (command, opts = {}) => rawSpawn(command, { ...opts, env: spawnEnv(process.env, process.platform, opts.env) });
|
|
55
58
|
return { spawn: spawnFn, killProcess: process.kill.bind(process) };
|
|
56
59
|
}
|
|
57
|
-
function resolveExecution(adapters, fallback, args, ctx) {
|
|
60
|
+
function resolveExecution({ adapters, fallback }, args, ctx) {
|
|
58
61
|
for (const adapter of adapters) {
|
|
59
62
|
const plan = adapter.resolve(args, ctx);
|
|
60
|
-
if (plan)
|
|
63
|
+
if (plan) {
|
|
61
64
|
return plan;
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
67
|
return fallback.resolve(args, ctx);
|
|
64
68
|
}
|
|
@@ -70,13 +74,16 @@ function checkFileArgs(args, python) {
|
|
|
70
74
|
return FILES_NEED_PYTHON_ERROR;
|
|
71
75
|
}
|
|
72
76
|
const inputs = parseInputs(args.inputs);
|
|
73
|
-
if (inputs && 'error' in inputs)
|
|
77
|
+
if (inputs && 'error' in inputs) {
|
|
74
78
|
return inputs.error;
|
|
79
|
+
}
|
|
75
80
|
const outputs = parseOutputs(args.outputs);
|
|
76
|
-
if (outputs && 'error' in outputs)
|
|
81
|
+
if (outputs && 'error' in outputs) {
|
|
77
82
|
return outputs.error;
|
|
78
|
-
|
|
83
|
+
}
|
|
84
|
+
if (!python) {
|
|
79
85
|
return null;
|
|
86
|
+
}
|
|
80
87
|
const workdir = parseWorkdir(args.workdir);
|
|
81
88
|
return workdir && typeof workdir === 'object' ? workdir.error : null;
|
|
82
89
|
}
|
|
@@ -107,8 +114,9 @@ function connectAsker(askUser) {
|
|
|
107
114
|
}
|
|
108
115
|
function networkPrefix(source) {
|
|
109
116
|
const hosts = namedHosts(source);
|
|
110
|
-
if (hosts.length === 0)
|
|
117
|
+
if (hosts.length === 0) {
|
|
111
118
|
return ' · network: no host named in the code';
|
|
119
|
+
}
|
|
112
120
|
return ` · network: ${hosts.join(', ')}`;
|
|
113
121
|
}
|
|
114
122
|
function packageSuffix(args) {
|
|
@@ -143,22 +151,25 @@ function commandRequiredError(args, network) {
|
|
|
143
151
|
'python source) or script + scriptArgs exactly as the loaded skill reference shows');
|
|
144
152
|
}
|
|
145
153
|
const packages = stringList(args.packages);
|
|
146
|
-
const impossible = videoToolError('', packages, true, network);
|
|
147
|
-
if (impossible)
|
|
154
|
+
const impossible = videoToolError('', { packages, python: true, network });
|
|
155
|
+
if (impossible) {
|
|
148
156
|
return impossible;
|
|
157
|
+
}
|
|
149
158
|
if (packages?.length) {
|
|
150
159
|
return `command is required: call exec again with language "python", packages: ${JSON.stringify(packages.map(pinned))} and the Python source that uses them in command. Installing and running are the same one call.`;
|
|
151
160
|
}
|
|
152
161
|
return 'command is required';
|
|
153
162
|
}
|
|
154
163
|
function parseScriptArgs(value) {
|
|
155
|
-
if (Array.isArray(value))
|
|
164
|
+
if (Array.isArray(value)) {
|
|
156
165
|
return value.map(String);
|
|
166
|
+
}
|
|
157
167
|
if (typeof value === 'string' && value.trim().startsWith('[')) {
|
|
158
168
|
try {
|
|
159
169
|
const parsed = JSON.parse(value);
|
|
160
|
-
if (Array.isArray(parsed))
|
|
170
|
+
if (Array.isArray(parsed)) {
|
|
161
171
|
return parsed.map(String);
|
|
172
|
+
}
|
|
162
173
|
}
|
|
163
174
|
catch {
|
|
164
175
|
}
|
|
@@ -168,16 +179,19 @@ function parseScriptArgs(value) {
|
|
|
168
179
|
function normalizeScriptCall(rawArgs) {
|
|
169
180
|
let script = typeof rawArgs.script === 'string' ? unquoteArg(rawArgs.script) : '';
|
|
170
181
|
let scriptArgs = parseScriptArgs(rawArgs.scriptArgs);
|
|
171
|
-
if (!script || script.includes('\n'))
|
|
182
|
+
if (!script || script.includes('\n')) {
|
|
172
183
|
return { script, scriptArgs };
|
|
184
|
+
}
|
|
173
185
|
if (script.includes(' ')) {
|
|
174
186
|
const tokens = script.split(/\s+/).filter(Boolean);
|
|
175
187
|
script = tokens[0] ?? '';
|
|
176
|
-
if (!scriptArgs.length)
|
|
188
|
+
if (!scriptArgs.length) {
|
|
177
189
|
scriptArgs = tokens.slice(1);
|
|
190
|
+
}
|
|
178
191
|
}
|
|
179
|
-
if (!script.startsWith('scripts/') && !script.includes('/'))
|
|
192
|
+
if (!script.startsWith('scripts/') && !script.includes('/')) {
|
|
180
193
|
script = `scripts/${script}`;
|
|
194
|
+
}
|
|
181
195
|
return { script, scriptArgs };
|
|
182
196
|
}
|
|
183
197
|
export function resolveScriptRun(rawArgs, skillsDir) {
|
|
@@ -203,10 +217,12 @@ export function resolveScriptRun(rawArgs, skillsDir) {
|
|
|
203
217
|
};
|
|
204
218
|
}
|
|
205
219
|
const skill = typeof rawArgs.skill === 'string' ? unquoteArg(rawArgs.skill) : '';
|
|
206
|
-
if (!skill)
|
|
220
|
+
if (!skill) {
|
|
207
221
|
return { error: 'script runs need the owning skill name in `skill`' };
|
|
208
|
-
|
|
222
|
+
}
|
|
223
|
+
if (!skillsDir) {
|
|
209
224
|
return { error: 'script execution is not available on this device' };
|
|
225
|
+
}
|
|
210
226
|
if (String(rawArgs.command ?? '').trim()) {
|
|
211
227
|
return { error: 'pass either command or script, never both' };
|
|
212
228
|
}
|
|
@@ -222,8 +238,9 @@ export function resolveScriptRun(rawArgs, skillsDir) {
|
|
|
222
238
|
return { error: `script not found in skill ${skill}: ${script}` };
|
|
223
239
|
}
|
|
224
240
|
const staged = stagingGap(rawArgs, scriptArgs);
|
|
225
|
-
if (staged)
|
|
241
|
+
if (staged) {
|
|
226
242
|
return { error: staged };
|
|
243
|
+
}
|
|
227
244
|
const argv = JSON.stringify([script.slice('scripts/'.length), ...scriptArgs]);
|
|
228
245
|
const command = `import sys\nsys.argv = ${argv}\n${source}`;
|
|
229
246
|
return { args: { ...rawArgs, language: 'python', command, scriptArgs }, trusted: true };
|
|
@@ -237,8 +254,9 @@ function stagingGap(rawArgs, scriptArgs) {
|
|
|
237
254
|
...(Array.isArray(outputs) ? outputs : [])
|
|
238
255
|
]);
|
|
239
256
|
const missing = scriptArgs.filter((arg) => SCRIPT_FILE_ARG.test(arg) && !known.has(arg));
|
|
240
|
-
if (!missing.length)
|
|
257
|
+
if (!missing.length) {
|
|
241
258
|
return null;
|
|
259
|
+
}
|
|
242
260
|
return (`scriptArgs name ${missing.map((name) => `"${name}"`).join(', ')} but the working directory ` +
|
|
243
261
|
'starts empty: stage each existing file in inputs [{ attachmentId, path }] and declare each ' +
|
|
244
262
|
'produced file in outputs');
|
|
@@ -260,24 +278,32 @@ function withInferredLanguage(args, disablePyodide) {
|
|
|
260
278
|
? { ...args, language: 'python' }
|
|
261
279
|
: args;
|
|
262
280
|
}
|
|
263
|
-
function checkExecutionScope(args, toolOps, disablePyodide, sandbox, network) {
|
|
281
|
+
function checkExecutionScope(args, { toolOps, disablePyodide, sandbox, network }) {
|
|
264
282
|
const command = String(args.command ?? '').trim();
|
|
265
|
-
const video = videoToolError(command,
|
|
266
|
-
|
|
283
|
+
const video = videoToolError(command, {
|
|
284
|
+
packages: stringList(args.packages),
|
|
285
|
+
python: runsThroughPyodide(args.language, disablePyodide),
|
|
286
|
+
network
|
|
287
|
+
});
|
|
288
|
+
if (video) {
|
|
267
289
|
return video;
|
|
268
|
-
|
|
290
|
+
}
|
|
291
|
+
if (runsThroughPyodide(args.language, disablePyodide)) {
|
|
269
292
|
return null;
|
|
293
|
+
}
|
|
270
294
|
if (!disablePyodide) {
|
|
271
295
|
const redirect = pythonRedirectError(command, sandbox);
|
|
272
|
-
if (redirect)
|
|
296
|
+
if (redirect) {
|
|
273
297
|
return redirect;
|
|
298
|
+
}
|
|
274
299
|
}
|
|
275
300
|
return checkShellScope(command, toolOps);
|
|
276
301
|
}
|
|
277
302
|
async function skillsEnv(skillSettings, ctx) {
|
|
278
303
|
const skills = ctx?.skills ?? [];
|
|
279
|
-
if (skills.length === 0)
|
|
304
|
+
if (skills.length === 0) {
|
|
280
305
|
return undefined;
|
|
306
|
+
}
|
|
281
307
|
const env = {};
|
|
282
308
|
for (const skill of skills) {
|
|
283
309
|
Object.assign(env, settingsEnv(skill, await skillSettings(skill, ctx?.agentId)));
|
|
@@ -404,21 +430,30 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
404
430
|
if (typeof args.script === 'string' && args.script.trim() && !args.script.includes('\n')) {
|
|
405
431
|
return 'read';
|
|
406
432
|
}
|
|
407
|
-
if (runsThroughPyodide(args.language, disablePyodide))
|
|
433
|
+
if (runsThroughPyodide(args.language, disablePyodide)) {
|
|
408
434
|
return pythonCreateEffect(args);
|
|
435
|
+
}
|
|
409
436
|
return shellEffect(String(args.command ?? '').trim(), ctx?.toolOps);
|
|
410
437
|
},
|
|
411
438
|
validate(args, ctx) {
|
|
412
439
|
const scriptRun = resolveScriptRun(args, skillsDir);
|
|
413
|
-
if (scriptRun && 'error' in scriptRun)
|
|
440
|
+
if (scriptRun && 'error' in scriptRun) {
|
|
414
441
|
return scriptRun.error;
|
|
442
|
+
}
|
|
415
443
|
const rawArgs = withInferredLanguage(scriptRun ? scriptRun.args : args, disablePyodide);
|
|
416
444
|
const command = String(rawArgs.command ?? '').trim();
|
|
417
|
-
if (!command)
|
|
445
|
+
if (!command) {
|
|
418
446
|
return commandRequiredError(rawArgs, pythonNetwork && ctx?.sandboxMode !== true);
|
|
419
|
-
|
|
420
|
-
|
|
447
|
+
}
|
|
448
|
+
const scopeError = checkExecutionScope(rawArgs, {
|
|
449
|
+
toolOps: ctx?.toolOps,
|
|
450
|
+
disablePyodide,
|
|
451
|
+
sandbox: ctx?.sandboxMode === true,
|
|
452
|
+
network: pythonNetwork && ctx?.sandboxMode !== true
|
|
453
|
+
});
|
|
454
|
+
if (scopeError) {
|
|
421
455
|
return scopeError;
|
|
456
|
+
}
|
|
422
457
|
const python = runsThroughPyodide(rawArgs.language, disablePyodide);
|
|
423
458
|
const workdirGiven = rawArgs.workdir !== undefined && rawArgs.workdir !== null;
|
|
424
459
|
if (workdirGiven && !python && (ctx?.fsRoots ?? []).length === 0) {
|
|
@@ -426,35 +461,44 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
426
461
|
}
|
|
427
462
|
if (python) {
|
|
428
463
|
const outputs = parseOutputs(rawArgs.outputs);
|
|
429
|
-
if (outputs && 'error' in outputs)
|
|
464
|
+
if (outputs && 'error' in outputs) {
|
|
430
465
|
return outputs.error;
|
|
466
|
+
}
|
|
431
467
|
const packages = parsePackages(rawArgs.packages);
|
|
432
|
-
if (packages && 'error' in packages)
|
|
468
|
+
if (packages && 'error' in packages) {
|
|
433
469
|
return packages.error;
|
|
470
|
+
}
|
|
434
471
|
if (!scriptRun || !scriptRun.trusted) {
|
|
435
472
|
const sourceError = checkPythonSource(command);
|
|
436
|
-
if (sourceError)
|
|
473
|
+
if (sourceError) {
|
|
437
474
|
return sourceError;
|
|
475
|
+
}
|
|
438
476
|
const pptxError = checkPptxBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
|
|
439
|
-
if (pptxError)
|
|
477
|
+
if (pptxError) {
|
|
440
478
|
return pptxError;
|
|
479
|
+
}
|
|
441
480
|
const pdfError = checkPdfBuildSource(unwrapPythonSource(command) ?? command, outputs, packages);
|
|
442
|
-
if (pdfError)
|
|
481
|
+
if (pdfError) {
|
|
443
482
|
return pdfError;
|
|
483
|
+
}
|
|
444
484
|
const bareLength = checkBareLengthArgs(unwrapPythonSource(command) ?? command, packages);
|
|
445
|
-
if (bareLength)
|
|
485
|
+
if (bareLength) {
|
|
446
486
|
return bareLength;
|
|
487
|
+
}
|
|
447
488
|
const inputs = parseInputs(rawArgs.inputs);
|
|
448
489
|
const unstagedRead = checkUnstagedRead(unwrapPythonSource(command) ?? command, Array.isArray(inputs) ? inputs : undefined);
|
|
449
|
-
if (unstagedRead)
|
|
490
|
+
if (unstagedRead) {
|
|
450
491
|
return unstagedRead;
|
|
492
|
+
}
|
|
451
493
|
}
|
|
452
494
|
}
|
|
453
495
|
const parsed = parsePackages(rawArgs.packages);
|
|
454
|
-
if (parsed && 'error' in parsed)
|
|
496
|
+
if (parsed && 'error' in parsed) {
|
|
455
497
|
return parsed.error;
|
|
456
|
-
|
|
498
|
+
}
|
|
499
|
+
if (parsed && !python) {
|
|
457
500
|
return PACKAGES_NEED_PYTHON_ERROR;
|
|
501
|
+
}
|
|
458
502
|
return checkFileArgs(rawArgs, python);
|
|
459
503
|
},
|
|
460
504
|
resource(rawArgs, ctx) {
|
|
@@ -464,8 +508,9 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
464
508
|
return { kind: 'command', label: scriptLabel(args) };
|
|
465
509
|
}
|
|
466
510
|
const command = String(args.command ?? '').trim() || script.trim();
|
|
467
|
-
if (!command)
|
|
511
|
+
if (!command) {
|
|
468
512
|
return null;
|
|
513
|
+
}
|
|
469
514
|
const workdir = typeof args.workdir === 'string' && args.workdir.trim() ? args.workdir.trim() : null;
|
|
470
515
|
const source = unwrapPythonSource(command) ?? command;
|
|
471
516
|
const online = pythonNetwork && ctx?.sandboxMode !== true;
|
|
@@ -479,16 +524,23 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
479
524
|
},
|
|
480
525
|
async execute(args, ctx) {
|
|
481
526
|
const scriptRun = resolveScriptRun(args, skillsDir);
|
|
482
|
-
if (scriptRun && 'error' in scriptRun)
|
|
527
|
+
if (scriptRun && 'error' in scriptRun) {
|
|
483
528
|
return { error: scriptRun.error };
|
|
529
|
+
}
|
|
484
530
|
const rawArgs = withInferredLanguage(scriptRun ? scriptRun.args : args, disablePyodide);
|
|
485
531
|
const command = String(rawArgs.command ?? '').trim();
|
|
486
532
|
if (!command) {
|
|
487
533
|
return { error: commandRequiredError(rawArgs, pythonNetwork && ctx?.sandboxMode !== true) };
|
|
488
534
|
}
|
|
489
|
-
const scopeError = checkExecutionScope(rawArgs,
|
|
490
|
-
|
|
535
|
+
const scopeError = checkExecutionScope(rawArgs, {
|
|
536
|
+
toolOps: ctx?.toolOps,
|
|
537
|
+
disablePyodide,
|
|
538
|
+
sandbox: ctx?.sandboxMode === true,
|
|
539
|
+
network: pythonNetwork && ctx?.sandboxMode !== true
|
|
540
|
+
});
|
|
541
|
+
if (scopeError) {
|
|
491
542
|
return { error: scopeError };
|
|
543
|
+
}
|
|
492
544
|
const workdir = rawArgs.workdir === undefined || rawArgs.workdir === null
|
|
493
545
|
? undefined
|
|
494
546
|
: String(rawArgs.workdir);
|
|
@@ -496,22 +548,26 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
496
548
|
let cwd = workdir;
|
|
497
549
|
if (workdir !== undefined && onShellPath) {
|
|
498
550
|
const roots = ctx?.fsRoots ?? [];
|
|
499
|
-
if (roots.length === 0)
|
|
551
|
+
if (roots.length === 0) {
|
|
500
552
|
return { error: WORKDIR_NO_ROOTS_ERROR };
|
|
553
|
+
}
|
|
501
554
|
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
502
|
-
const resolved = await resolveKey(workdir, roots, realpath, deny);
|
|
503
|
-
if ('error' in resolved)
|
|
555
|
+
const resolved = await resolveKey(workdir, roots, { realpath, deny });
|
|
556
|
+
if ('error' in resolved) {
|
|
504
557
|
return { error: resolved.error };
|
|
558
|
+
}
|
|
505
559
|
if (resolved.root.kind !== 'local') {
|
|
506
560
|
return { error: 'workdir must be a local directory, not a drive' };
|
|
507
561
|
}
|
|
508
|
-
if (!resolved.exists)
|
|
562
|
+
if (!resolved.exists) {
|
|
509
563
|
return { error: `workdir does not exist: ${workdir}` };
|
|
564
|
+
}
|
|
510
565
|
cwd = hostPath(resolved.root, resolved.key);
|
|
511
566
|
}
|
|
512
567
|
let timeout = parsePositiveInt(rawArgs.timeoutMs, 'timeoutMs', 600_000);
|
|
513
|
-
if (timeout && typeof timeout === 'object')
|
|
568
|
+
if (timeout && typeof timeout === 'object') {
|
|
514
569
|
return timeout;
|
|
570
|
+
}
|
|
515
571
|
if (ctx?.runBudget) {
|
|
516
572
|
const remaining = ctx.runBudget.deadlineMs - Date.now();
|
|
517
573
|
if (remaining <= 0) {
|
|
@@ -520,12 +576,14 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
520
576
|
guidance: true
|
|
521
577
|
};
|
|
522
578
|
}
|
|
523
|
-
if (timeout !== undefined)
|
|
579
|
+
if (timeout !== undefined) {
|
|
524
580
|
timeout = Math.min(timeout, remaining);
|
|
581
|
+
}
|
|
525
582
|
}
|
|
526
583
|
const maxOutputChars = parsePositiveInt(rawArgs.maxOutputChars, 'maxOutputChars', MAX_OUTPUT_CHARS_LIMIT);
|
|
527
|
-
if (maxOutputChars && typeof maxOutputChars === 'object')
|
|
584
|
+
if (maxOutputChars && typeof maxOutputChars === 'object') {
|
|
528
585
|
return maxOutputChars;
|
|
586
|
+
}
|
|
529
587
|
if (!isPythonLanguage(rawArgs.language)) {
|
|
530
588
|
const obsidian = await routeObsidianCommand(command, ctx, {
|
|
531
589
|
credentialStore,
|
|
@@ -535,12 +593,13 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
535
593
|
timeoutMs: timeout,
|
|
536
594
|
maxOutputChars
|
|
537
595
|
});
|
|
538
|
-
if (obsidian)
|
|
596
|
+
if (obsidian) {
|
|
539
597
|
return obsidian;
|
|
598
|
+
}
|
|
540
599
|
}
|
|
541
600
|
const skillEnv = skillSettings ? await skillsEnv(skillSettings, ctx) : undefined;
|
|
542
601
|
try {
|
|
543
|
-
const execution = resolveExecution(adapters, fallback, rawArgs, {
|
|
602
|
+
const execution = resolveExecution({ adapters, fallback }, rawArgs, {
|
|
544
603
|
attach: ctx?.attach,
|
|
545
604
|
fetchAttachment: ctx?.fetchAttachment,
|
|
546
605
|
turnImages: ctx?.turnImages,
|
|
@@ -555,7 +614,7 @@ export function createExecTool({ spawn, killProcess, loadPyodide, disablePyodide
|
|
|
555
614
|
timeoutMs: timeout,
|
|
556
615
|
maxOutputChars,
|
|
557
616
|
signal: ctx?.signal,
|
|
558
|
-
|
|
617
|
+
env: skillEnv
|
|
559
618
|
});
|
|
560
619
|
}
|
|
561
620
|
catch (err) {
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
2
|
-
import { type DriveRoot, type FsToolOptions, type
|
|
2
|
+
import { type DriveRoot, type FsToolError, type FsToolOptions, type ResolveKeyOptions } from './fs/scope.ts';
|
|
3
3
|
export declare const DEFAULT_GLOB_MATCHES = 100;
|
|
4
4
|
export declare const MAX_GLOB_MATCHES = 1000;
|
|
5
5
|
export interface SearchTarget {
|
|
6
6
|
root: DriveRoot;
|
|
7
7
|
folder: string;
|
|
8
8
|
}
|
|
9
|
-
export
|
|
9
|
+
export interface ResolvedTargets {
|
|
10
10
|
targets: SearchTarget[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function searchTargets(dir: unknown, roots: readonly DriveRoot[], { realpath, deny }: ResolveKeyOptions): Promise<ResolvedTargets | FsToolError>;
|
|
14
13
|
export declare function relativeKey(key: string, folder: string): string;
|
|
15
14
|
export declare function createFileGlobTool({ realpath, deniedRoots }?: FsToolOptions): ToolDefinition;
|
|
@@ -3,14 +3,16 @@ import { NO_DIRS_GRANTED, canonicalDenyRoots, defaultRealpath, isDeniedPath, res
|
|
|
3
3
|
import { walkFiles } from './fs/walk.js';
|
|
4
4
|
export const DEFAULT_GLOB_MATCHES = 100;
|
|
5
5
|
export const MAX_GLOB_MATCHES = 1000;
|
|
6
|
-
export async function searchTargets(dir, roots, realpath, deny) {
|
|
6
|
+
export async function searchTargets(dir, roots, { realpath, deny }) {
|
|
7
7
|
if (typeof dir === 'string' && dir.trim()) {
|
|
8
|
-
const resolved = await resolveKey(dir, roots, realpath, deny);
|
|
9
|
-
if ('error' in resolved)
|
|
8
|
+
const resolved = await resolveKey(dir, roots, { realpath, deny });
|
|
9
|
+
if ('error' in resolved) {
|
|
10
10
|
return resolved;
|
|
11
|
+
}
|
|
11
12
|
if (resolved.root.kind === 'local') {
|
|
12
|
-
if (!resolved.exists)
|
|
13
|
+
if (!resolved.exists) {
|
|
13
14
|
return { error: `no such file: ${dir}` };
|
|
15
|
+
}
|
|
14
16
|
}
|
|
15
17
|
else {
|
|
16
18
|
let hasEntry = false;
|
|
@@ -24,8 +26,9 @@ export async function searchTargets(dir, roots, realpath, deny) {
|
|
|
24
26
|
}
|
|
25
27
|
return { targets: [{ root: resolved.root, folder: resolved.key }] };
|
|
26
28
|
}
|
|
27
|
-
if (roots.length === 0)
|
|
29
|
+
if (roots.length === 0) {
|
|
28
30
|
return { error: NO_DIRS_GRANTED };
|
|
31
|
+
}
|
|
29
32
|
return { targets: roots.map((root) => ({ root, folder: '/' })) };
|
|
30
33
|
}
|
|
31
34
|
export function relativeKey(key, folder) {
|
|
@@ -76,13 +79,15 @@ export function createFileGlobTool({ realpath = defaultRealpath, deniedRoots } =
|
|
|
76
79
|
async execute(rawArgs, ctx) {
|
|
77
80
|
const roots = ctx?.fsRoots ?? [];
|
|
78
81
|
const pattern = String(rawArgs.pattern ?? '').trim();
|
|
79
|
-
if (!pattern)
|
|
82
|
+
if (!pattern) {
|
|
80
83
|
return { error: 'pattern is required' };
|
|
84
|
+
}
|
|
81
85
|
const limit = rawArgs.limit ? Number(rawArgs.limit) : DEFAULT_GLOB_MATCHES;
|
|
82
86
|
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
83
|
-
const scoped = await searchTargets(rawArgs.dir, roots, realpath, deny);
|
|
84
|
-
if ('error' in scoped)
|
|
87
|
+
const scoped = await searchTargets(rawArgs.dir, roots, { realpath, deny });
|
|
88
|
+
if ('error' in scoped) {
|
|
85
89
|
return scoped;
|
|
90
|
+
}
|
|
86
91
|
const match = compileGlob(pattern);
|
|
87
92
|
const matches = [];
|
|
88
93
|
let truncated = false;
|
|
@@ -98,14 +103,14 @@ export function createFileGlobTool({ realpath = defaultRealpath, deniedRoots } =
|
|
|
98
103
|
continue;
|
|
99
104
|
matches.push(file.path);
|
|
100
105
|
if (matches.length >= limit) {
|
|
101
|
-
return { matches, truncated: true,
|
|
106
|
+
return { matches, truncated: true, skippedLinks: skippedLinks || undefined };
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
return {
|
|
106
111
|
matches,
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
truncated: truncated || undefined,
|
|
113
|
+
skippedLinks: skippedLinks || undefined
|
|
109
114
|
};
|
|
110
115
|
}
|
|
111
116
|
};
|