@qvac/harness 0.3.3 → 0.5.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 +700 -126
- package/README.md +156 -0
- package/dist/harness.bundle +71 -103
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +42 -40
- package/dist-lib/index.js +307 -117
- 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 +194 -135
- 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 +69 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
- 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 +15 -37
- package/dist-lib/lib/connect.js +161 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +26 -20
- 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 +13 -0
- package/dist-lib/lib/models/custom-model-cache.js +70 -0
- package/dist-lib/lib/models/gguf-header.d.ts +31 -0
- package/dist-lib/lib/models/gguf-header.js +230 -0
- 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 +19 -21
- package/dist-lib/lib/models/model-catalog.js +66 -33
- package/dist-lib/lib/models/model-delete.d.ts +4 -1
- package/dist-lib/lib/models/model-delete.js +30 -15
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +14 -3
- package/dist-lib/lib/models/model-list.js +33 -6
- package/dist-lib/lib/models/model-pool.d.ts +44 -10
- package/dist-lib/lib/models/model-pool.js +47 -42
- package/dist-lib/lib/models/model-source.js +11 -6
- 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 +128 -68
- 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 +55 -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 +6 -0
- package/dist-lib/spec/calls.js +2 -0
- package/dist-lib/spec/hrpc/hrpc.json +26 -0
- package/dist-lib/spec/hrpc/index.js +27 -3
- package/dist-lib/spec/hrpc/messages.js +207 -8
- package/dist-lib/spec/hyperschema/index.js +207 -8
- package/dist-lib/spec/hyperschema/schema.json +165 -1
- package/dist-lib/spec/hyperschema/types.d.ts +42 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +16 -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
|
@@ -13,8 +13,9 @@ export function resolveNotePath(vaultPath, args) {
|
|
|
13
13
|
const name = fileArg.slice('file='.length);
|
|
14
14
|
const withExt = name.endsWith('.md') ? name : `${name}.md`;
|
|
15
15
|
const direct = ensureWithinRoot(vaultPath, withExt);
|
|
16
|
-
if (fs.existsSync(direct))
|
|
16
|
+
if (fs.existsSync(direct)) {
|
|
17
17
|
return direct;
|
|
18
|
+
}
|
|
18
19
|
return findFileRecursive(vaultPath, withExt);
|
|
19
20
|
}
|
|
20
21
|
return null;
|
|
@@ -32,8 +33,9 @@ function readDirEntries(dir) {
|
|
|
32
33
|
if (name.startsWith('.'))
|
|
33
34
|
continue;
|
|
34
35
|
const full = path.join(dir, name);
|
|
35
|
-
if (isSymlink(full))
|
|
36
|
+
if (isSymlink(full)) {
|
|
36
37
|
continue;
|
|
38
|
+
}
|
|
37
39
|
let stat;
|
|
38
40
|
try {
|
|
39
41
|
stat = fs.statSync(full);
|
|
@@ -52,8 +54,9 @@ function findFileRecursive(dir, target) {
|
|
|
52
54
|
for (const entry of readDirEntries(dir)) {
|
|
53
55
|
if (entry.isDir) {
|
|
54
56
|
const found = findFileRecursive(entry.full, target);
|
|
55
|
-
if (found)
|
|
57
|
+
if (found) {
|
|
56
58
|
return found;
|
|
59
|
+
}
|
|
57
60
|
}
|
|
58
61
|
else if (namesMatch(entry.name, target)) {
|
|
59
62
|
return entry.full;
|
|
@@ -72,8 +75,9 @@ export function readFileSnapshot(filePath) {
|
|
|
72
75
|
catch {
|
|
73
76
|
return null;
|
|
74
77
|
}
|
|
75
|
-
if (!stat.isFile())
|
|
78
|
+
if (!stat.isFile()) {
|
|
76
79
|
return null;
|
|
80
|
+
}
|
|
77
81
|
return { text: fs.readFileSync(filePath, 'utf8'), mtimeMs: stat.mtimeMs };
|
|
78
82
|
}
|
|
79
83
|
export function writeIfUnchanged(filePath, content, expectedMtimeMs) {
|
|
@@ -116,10 +120,12 @@ export function listMarkdownFiles(dir, prefix = '') {
|
|
|
116
120
|
const results = [];
|
|
117
121
|
for (const entry of readDirEntries(dir)) {
|
|
118
122
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
119
|
-
if (entry.isDir)
|
|
123
|
+
if (entry.isDir) {
|
|
120
124
|
results.push(...listMarkdownFiles(entry.full, rel));
|
|
121
|
-
|
|
125
|
+
}
|
|
126
|
+
else if (entry.name.endsWith('.md')) {
|
|
122
127
|
results.push(rel);
|
|
128
|
+
}
|
|
123
129
|
}
|
|
124
130
|
return results;
|
|
125
131
|
}
|
|
@@ -136,24 +142,28 @@ export function listFolders(dir, prefix = '') {
|
|
|
136
142
|
}
|
|
137
143
|
export function parseFrontmatter(content) {
|
|
138
144
|
const match = content.match(FRONTMATTER_RE);
|
|
139
|
-
if (!match?.[1])
|
|
145
|
+
if (!match?.[1]) {
|
|
140
146
|
return {};
|
|
147
|
+
}
|
|
141
148
|
const props = {};
|
|
142
149
|
for (const line of match[1].split('\n')) {
|
|
143
150
|
const colonIdx = line.indexOf(':');
|
|
144
|
-
if (colonIdx === -1)
|
|
151
|
+
if (colonIdx === -1) {
|
|
145
152
|
continue;
|
|
153
|
+
}
|
|
146
154
|
const key = line.slice(0, colonIdx).trim();
|
|
147
155
|
const value = line.slice(colonIdx + 1).trim();
|
|
148
|
-
if (key)
|
|
156
|
+
if (key) {
|
|
149
157
|
props[key] = value;
|
|
158
|
+
}
|
|
150
159
|
}
|
|
151
160
|
return props;
|
|
152
161
|
}
|
|
153
162
|
export function getContentArg(args) {
|
|
154
163
|
const arg = args.find((a) => a.startsWith('content='));
|
|
155
|
-
if (!arg)
|
|
164
|
+
if (!arg) {
|
|
156
165
|
return '';
|
|
166
|
+
}
|
|
157
167
|
return arg.slice('content='.length).replace(/\\n/g, '\n');
|
|
158
168
|
}
|
|
159
169
|
export function getArg(args, key) {
|
|
@@ -35,14 +35,16 @@ const SEARCH_LIMIT_MAX = 200;
|
|
|
35
35
|
function parseLimit(args) {
|
|
36
36
|
const raw = getArg(args, 'limit');
|
|
37
37
|
const n = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
38
|
-
if (!Number.isFinite(n) || n <= 0)
|
|
38
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
39
39
|
return SEARCH_LIMIT_DEFAULT;
|
|
40
|
+
}
|
|
40
41
|
return Math.min(n, SEARCH_LIMIT_MAX);
|
|
41
42
|
}
|
|
42
43
|
export function handleSearch(vaultPath, args) {
|
|
43
44
|
const query = getArg(args, 'query');
|
|
44
|
-
if (!query)
|
|
45
|
+
if (!query) {
|
|
45
46
|
throw new Error('Missing required argument: query=');
|
|
47
|
+
}
|
|
46
48
|
const limit = parseLimit(args);
|
|
47
49
|
const lowerQuery = query.toLowerCase();
|
|
48
50
|
const files = listMarkdownFiles(path.resolve(vaultPath));
|
|
@@ -52,14 +54,16 @@ export function handleSearch(vaultPath, args) {
|
|
|
52
54
|
break;
|
|
53
55
|
const fullPath = path.join(path.resolve(vaultPath), relPath);
|
|
54
56
|
try {
|
|
55
|
-
if (readFileText(fullPath).toLowerCase().includes(lowerQuery))
|
|
57
|
+
if (readFileText(fullPath).toLowerCase().includes(lowerQuery)) {
|
|
56
58
|
matches.push(relPath);
|
|
59
|
+
}
|
|
57
60
|
}
|
|
58
61
|
catch {
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
|
-
if (matches.length === 0)
|
|
64
|
+
if (matches.length === 0) {
|
|
62
65
|
return `No results for "${query}"`;
|
|
66
|
+
}
|
|
63
67
|
const truncated = matches.length >= limit ? `\n(showing first ${limit}; narrow query for more)` : '';
|
|
64
68
|
return matches.join('\n') + truncated;
|
|
65
69
|
}
|
|
@@ -70,19 +74,21 @@ export function handleProperties(vaultPath, args) {
|
|
|
70
74
|
}
|
|
71
75
|
return JSON.stringify(parseFrontmatter(readFileText(notePath)), null, 2);
|
|
72
76
|
}
|
|
77
|
+
function countTags(tagCounts, content) {
|
|
78
|
+
for (const match of content.matchAll(/#([a-zA-Z0-9_/-]+)/g)) {
|
|
79
|
+
const tag = match[1];
|
|
80
|
+
if (tag === undefined) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
73
86
|
export function handleTags(vaultPath) {
|
|
74
87
|
const files = listMarkdownFiles(path.resolve(vaultPath));
|
|
75
88
|
const tagCounts = new Map();
|
|
76
|
-
const tagRe = /#([a-zA-Z0-9_/-]+)/g;
|
|
77
89
|
for (const relPath of files) {
|
|
78
90
|
try {
|
|
79
|
-
|
|
80
|
-
for (const match of content.matchAll(tagRe)) {
|
|
81
|
-
const tag = match[1];
|
|
82
|
-
if (tag === undefined)
|
|
83
|
-
continue;
|
|
84
|
-
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
|
85
|
-
}
|
|
91
|
+
countTags(tagCounts, readFileText(path.join(path.resolve(vaultPath), relPath)));
|
|
86
92
|
}
|
|
87
93
|
catch {
|
|
88
94
|
}
|
|
@@ -112,8 +118,9 @@ export function handleOutline(vaultPath, args) {
|
|
|
112
118
|
export function handleDailyRead(vaultPath) {
|
|
113
119
|
const dateStr = todayStamp();
|
|
114
120
|
for (const p of dailyCandidates(vaultPath, dateStr)) {
|
|
115
|
-
if (fs.existsSync(p))
|
|
121
|
+
if (fs.existsSync(p)) {
|
|
116
122
|
return readFileText(p);
|
|
123
|
+
}
|
|
117
124
|
}
|
|
118
125
|
return `No daily note found for ${dateStr}`;
|
|
119
126
|
}
|
|
@@ -8,8 +8,9 @@ export function handleAppend(vaultPath, args) {
|
|
|
8
8
|
throw new Error(`Note not found. Args: ${args.join(', ')}`);
|
|
9
9
|
}
|
|
10
10
|
const content = getContentArg(args);
|
|
11
|
-
if (!content)
|
|
11
|
+
if (!content) {
|
|
12
12
|
throw new Error('Missing required argument: content=');
|
|
13
|
+
}
|
|
13
14
|
appendToFile(notePath, content);
|
|
14
15
|
return `Appended to: ${path.relative(vaultPath, notePath)}`;
|
|
15
16
|
}
|
|
@@ -19,8 +20,9 @@ export function handlePrepend(vaultPath, args) {
|
|
|
19
20
|
throw new Error(`Note not found. Args: ${args.join(', ')}`);
|
|
20
21
|
}
|
|
21
22
|
const content = getContentArg(args);
|
|
22
|
-
if (!content)
|
|
23
|
+
if (!content) {
|
|
23
24
|
throw new Error('Missing required argument: content=');
|
|
25
|
+
}
|
|
24
26
|
updateFileText(notePath, (existing) => content + existing);
|
|
25
27
|
return `Prepended to: ${path.relative(vaultPath, notePath)}`;
|
|
26
28
|
}
|
|
@@ -30,8 +32,9 @@ function uniqueTrashPath(base) {
|
|
|
30
32
|
const ext = base.endsWith('.md') ? '.md' : '';
|
|
31
33
|
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
32
34
|
let n = 1;
|
|
33
|
-
while (fs.existsSync(`${stem} (${n})${ext}`))
|
|
35
|
+
while (fs.existsSync(`${stem} (${n})${ext}`)) {
|
|
34
36
|
n += 1;
|
|
37
|
+
}
|
|
35
38
|
return `${stem} (${n})${ext}`;
|
|
36
39
|
}
|
|
37
40
|
export function handleDelete(vaultPath, args) {
|
|
@@ -51,11 +54,13 @@ export function handleMove(vaultPath, args) {
|
|
|
51
54
|
throw new Error(`Note not found. Args: ${args.join(', ')}`);
|
|
52
55
|
}
|
|
53
56
|
const to = getArg(args, 'to');
|
|
54
|
-
if (!to)
|
|
57
|
+
if (!to) {
|
|
55
58
|
throw new Error('Missing required argument: to=');
|
|
59
|
+
}
|
|
56
60
|
const destDir = ensureWithinRoot(vaultPath, to);
|
|
57
|
-
if (!fs.existsSync(destDir))
|
|
61
|
+
if (!fs.existsSync(destDir)) {
|
|
58
62
|
fs.mkdirSync(destDir, { recursive: true });
|
|
63
|
+
}
|
|
59
64
|
const destPath = path.join(destDir, path.basename(notePath));
|
|
60
65
|
moveFile(notePath, destPath);
|
|
61
66
|
return `Moved to: ${path.relative(vaultPath, destPath)}`;
|
|
@@ -66,8 +71,9 @@ export function handleRename(vaultPath, args) {
|
|
|
66
71
|
throw new Error(`Note not found. Args: ${args.join(', ')}`);
|
|
67
72
|
}
|
|
68
73
|
const newName = getArg(args, 'name');
|
|
69
|
-
if (!newName)
|
|
74
|
+
if (!newName) {
|
|
70
75
|
throw new Error('Missing required argument: name=');
|
|
76
|
+
}
|
|
71
77
|
const fileName = markdownFileName(newName);
|
|
72
78
|
const relativeDir = path.relative(path.resolve(vaultPath), path.dirname(notePath));
|
|
73
79
|
const destPath = ensureWithinRoot(vaultPath, path.join(relativeDir, fileName));
|
|
@@ -78,10 +84,12 @@ function mergeIntoFrontmatter(fmBlock, newProps) {
|
|
|
78
84
|
let result = fmBlock;
|
|
79
85
|
for (const { key, value } of newProps) {
|
|
80
86
|
const lineRe = new RegExp(`^${key}:.*$`, 'm');
|
|
81
|
-
if (lineRe.test(result))
|
|
87
|
+
if (lineRe.test(result)) {
|
|
82
88
|
result = result.replace(lineRe, `${key}: ${value}`);
|
|
83
|
-
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
84
91
|
result += `\n${key}: ${value}`;
|
|
92
|
+
}
|
|
85
93
|
}
|
|
86
94
|
return result;
|
|
87
95
|
}
|
|
@@ -91,8 +99,9 @@ export function handlePropertySet(vaultPath, args) {
|
|
|
91
99
|
throw new Error(`Note not found. Args: ${args.join(', ')}`);
|
|
92
100
|
}
|
|
93
101
|
const propArgs = args.filter((a) => !a.startsWith('file=') && !a.startsWith('path=') && a.includes('='));
|
|
94
|
-
if (propArgs.length === 0)
|
|
102
|
+
if (propArgs.length === 0) {
|
|
95
103
|
throw new Error('No property key=value pairs provided.');
|
|
104
|
+
}
|
|
96
105
|
const newProps = propArgs.map((a) => {
|
|
97
106
|
const eqIdx = a.indexOf('=');
|
|
98
107
|
return { key: a.slice(0, eqIdx), value: a.slice(eqIdx + 1) };
|
|
@@ -110,8 +119,9 @@ export function handlePropertySet(vaultPath, args) {
|
|
|
110
119
|
}
|
|
111
120
|
export function handleDailyAppend(vaultPath, args) {
|
|
112
121
|
const content = getContentArg(args);
|
|
113
|
-
if (!content)
|
|
122
|
+
if (!content) {
|
|
114
123
|
throw new Error('Missing required argument: content=');
|
|
124
|
+
}
|
|
115
125
|
const dateStr = todayStamp();
|
|
116
126
|
let target = null;
|
|
117
127
|
for (const candidate of dailyCandidates(vaultPath, dateStr)) {
|
|
@@ -122,12 +132,14 @@ export function handleDailyAppend(vaultPath, args) {
|
|
|
122
132
|
}
|
|
123
133
|
if (!target) {
|
|
124
134
|
const dailyDir = ensureWithinRoot(vaultPath, 'Daily Notes');
|
|
125
|
-
if (!fs.existsSync(dailyDir))
|
|
135
|
+
if (!fs.existsSync(dailyDir)) {
|
|
126
136
|
fs.mkdirSync(dailyDir, { recursive: true });
|
|
137
|
+
}
|
|
127
138
|
target = ensureWithinRoot(vaultPath, path.join('Daily Notes', `${dateStr}.md`));
|
|
128
139
|
const snap = readFileSnapshot(target);
|
|
129
|
-
if (!snap)
|
|
140
|
+
if (!snap) {
|
|
130
141
|
writeIfUnchanged(target, '', null);
|
|
142
|
+
}
|
|
131
143
|
}
|
|
132
144
|
appendToFile(target, content);
|
|
133
145
|
return `Appended to daily note: ${dateStr}`;
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
|
-
export
|
|
2
|
+
export interface ExecAttachment {
|
|
3
3
|
readonly attachmentId: string;
|
|
4
4
|
readonly fileName: string;
|
|
5
5
|
readonly byteLength: number;
|
|
6
|
-
}
|
|
7
|
-
export
|
|
6
|
+
}
|
|
7
|
+
export interface RejectedOutput {
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly reason: string;
|
|
10
|
-
}
|
|
11
|
-
export
|
|
10
|
+
}
|
|
11
|
+
export interface ExecResult {
|
|
12
12
|
readonly stdout: string;
|
|
13
13
|
readonly stderr: string;
|
|
14
14
|
readonly exitCode: number;
|
|
15
15
|
readonly attachments?: readonly ExecAttachment[];
|
|
16
16
|
readonly missingOutputs?: readonly string[];
|
|
17
17
|
readonly rejectedOutputs?: readonly RejectedOutput[];
|
|
18
|
-
}
|
|
19
|
-
export
|
|
18
|
+
}
|
|
19
|
+
export interface SpawnOptions {
|
|
20
20
|
cwd?: string;
|
|
21
21
|
shell?: boolean;
|
|
22
22
|
detached?: boolean;
|
|
23
23
|
stdio?: [string, string, string] | string;
|
|
24
24
|
env?: Record<string, string>;
|
|
25
|
-
}
|
|
26
|
-
export
|
|
25
|
+
}
|
|
26
|
+
export interface Child {
|
|
27
27
|
readonly pid?: number;
|
|
28
28
|
readonly stdout: {
|
|
29
29
|
on(event: 'data', handler: (chunk: Buffer | string) => void): void;
|
|
@@ -34,9 +34,9 @@ export type Child = {
|
|
|
34
34
|
kill(signum?: number | string): void;
|
|
35
35
|
on(event: 'exit', handler: (code: number | null, signal?: string | null) => void): void;
|
|
36
36
|
on(event: 'error', handler: (err: Error) => void): void;
|
|
37
|
-
}
|
|
37
|
+
}
|
|
38
38
|
export type SpawnFn = (command: string, opts?: SpawnOptions) => Child;
|
|
39
|
-
export
|
|
39
|
+
export interface RunCommandOptions {
|
|
40
40
|
readonly cwd?: string;
|
|
41
41
|
readonly env?: Readonly<Record<string, string>>;
|
|
42
42
|
readonly timeoutMs?: number;
|
|
@@ -45,15 +45,15 @@ export type RunCommandOptions = {
|
|
|
45
45
|
readonly spawn: SpawnFn;
|
|
46
46
|
readonly killProcess?: KillProcess;
|
|
47
47
|
readonly platform?: string;
|
|
48
|
-
}
|
|
48
|
+
}
|
|
49
49
|
export declare const MAX_OUTPUT_CHARS = 8192;
|
|
50
50
|
export declare const MAX_OUTPUT_CHARS_LIMIT = 65536;
|
|
51
51
|
export declare const TRUNCATED_OUTPUT_SUFFIX = "\u2026 [truncated]";
|
|
52
52
|
export type KillProcess = (pid: number, signal: string) => void;
|
|
53
|
-
|
|
53
|
+
interface CapturedOutput {
|
|
54
54
|
value: string;
|
|
55
55
|
truncated: boolean;
|
|
56
|
-
}
|
|
56
|
+
}
|
|
57
57
|
export declare function capOutput(text: string, limit: number): string;
|
|
58
58
|
export declare function appendOutput(output: CapturedOutput, chunk: Buffer | string, limit: number): void;
|
|
59
59
|
export declare function formatOutput(output: CapturedOutput): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import b4a from 'b4a';
|
|
1
2
|
import process from 'process';
|
|
2
3
|
import { AbortedError } from '../../helpers/race-abort.js';
|
|
3
4
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
@@ -21,7 +22,7 @@ export function appendOutput(output, chunk, limit) {
|
|
|
21
22
|
output.truncated = true;
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
|
-
const text = typeof chunk === 'string' ? chunk :
|
|
25
|
+
const text = typeof chunk === 'string' ? chunk : b4a.toString(chunk, 'utf8');
|
|
25
26
|
output.value += text.slice(0, available);
|
|
26
27
|
output.truncated = text.length > available;
|
|
27
28
|
}
|
|
@@ -45,8 +46,9 @@ function terminateProcessTree(child, killProcess) {
|
|
|
45
46
|
export function runCommand(command, options) {
|
|
46
47
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
47
48
|
const maxOutputChars = options.maxOutputChars ?? MAX_OUTPUT_CHARS;
|
|
48
|
-
if (options.signal?.aborted)
|
|
49
|
+
if (options.signal?.aborted) {
|
|
49
50
|
return Promise.reject(new AbortedError());
|
|
51
|
+
}
|
|
50
52
|
return new Promise((resolve, reject) => {
|
|
51
53
|
const stdout = { value: '', truncated: false };
|
|
52
54
|
const stderr = { value: '', truncated: false };
|
|
@@ -56,7 +58,7 @@ export function runCommand(command, options) {
|
|
|
56
58
|
detached: (options.platform ?? process.platform) !== 'win32',
|
|
57
59
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
58
60
|
cwd: options.cwd,
|
|
59
|
-
|
|
61
|
+
env: options.env ? { ...options.env } : undefined
|
|
60
62
|
});
|
|
61
63
|
const finish = (fn) => {
|
|
62
64
|
if (settled)
|
|
@@ -6,8 +6,9 @@ export function bareBin(token) {
|
|
|
6
6
|
export function firstCommandToken(command) {
|
|
7
7
|
const chars = [...command];
|
|
8
8
|
let i = 0;
|
|
9
|
-
while (i < chars.length && (chars[i] === ' ' || chars[i] === '\t'))
|
|
9
|
+
while (i < chars.length && (chars[i] === ' ' || chars[i] === '\t')) {
|
|
10
10
|
i++;
|
|
11
|
+
}
|
|
11
12
|
let token = '';
|
|
12
13
|
while (i < chars.length) {
|
|
13
14
|
const c = chars[i];
|
|
@@ -21,10 +22,12 @@ export function firstCommandToken(command) {
|
|
|
21
22
|
i++;
|
|
22
23
|
continue;
|
|
23
24
|
}
|
|
24
|
-
if (c === ' ' || c === '\t' || c === '\n')
|
|
25
|
+
if (c === ' ' || c === '\t' || c === '\n') {
|
|
25
26
|
break;
|
|
26
|
-
|
|
27
|
+
}
|
|
28
|
+
if (c === ';' || c === '&' || c === '|' || c === '>' || c === '<' || c === '`') {
|
|
27
29
|
break;
|
|
30
|
+
}
|
|
28
31
|
token += c;
|
|
29
32
|
i++;
|
|
30
33
|
}
|
|
@@ -33,6 +36,26 @@ export function firstCommandToken(command) {
|
|
|
33
36
|
function isPad(c) {
|
|
34
37
|
return c === ' ' || c === '\t' || c === '\n';
|
|
35
38
|
}
|
|
39
|
+
function skipDoubleQuoted(chars, open) {
|
|
40
|
+
let i = open + 1;
|
|
41
|
+
while (i < chars.length && chars[i] !== '"') {
|
|
42
|
+
if (chars[i] === '\\') {
|
|
43
|
+
if (chars[i + 1] === '\n') {
|
|
44
|
+
return 'command must not use line continuations';
|
|
45
|
+
}
|
|
46
|
+
i += 2;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (chars[i] === '`') {
|
|
50
|
+
return 'command must not use backtick substitution';
|
|
51
|
+
}
|
|
52
|
+
if (chars[i] === '$') {
|
|
53
|
+
return 'command must not use shell expansion';
|
|
54
|
+
}
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
return i + 1;
|
|
58
|
+
}
|
|
36
59
|
export function sanitizeCommand(command) {
|
|
37
60
|
const chars = [...command];
|
|
38
61
|
let i = 0;
|
|
@@ -40,57 +63,58 @@ export function sanitizeCommand(command) {
|
|
|
40
63
|
const c = chars[i];
|
|
41
64
|
if (c === '\n') {
|
|
42
65
|
i++;
|
|
43
|
-
while (i < chars.length && isPad(chars[i]))
|
|
66
|
+
while (i < chars.length && isPad(chars[i])) {
|
|
44
67
|
i++;
|
|
45
|
-
|
|
68
|
+
}
|
|
69
|
+
if (i < chars.length) {
|
|
46
70
|
return 'command must not span multiple lines';
|
|
71
|
+
}
|
|
47
72
|
return null;
|
|
48
73
|
}
|
|
49
74
|
if (c === "'") {
|
|
50
75
|
i++;
|
|
51
|
-
while (i < chars.length && chars[i] !== "'")
|
|
76
|
+
while (i < chars.length && chars[i] !== "'") {
|
|
52
77
|
i++;
|
|
78
|
+
}
|
|
53
79
|
i++;
|
|
54
80
|
continue;
|
|
55
81
|
}
|
|
56
82
|
if (c === '"') {
|
|
57
|
-
i
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (chars[i + 1] === '\n')
|
|
61
|
-
return 'command must not use line continuations';
|
|
62
|
-
i += 2;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
if (chars[i] === '`')
|
|
66
|
-
return 'command must not use backtick substitution';
|
|
67
|
-
if (chars[i] === '$')
|
|
68
|
-
return 'command must not use shell expansion';
|
|
69
|
-
i++;
|
|
83
|
+
const end = skipDoubleQuoted(chars, i);
|
|
84
|
+
if (typeof end === 'string') {
|
|
85
|
+
return end;
|
|
70
86
|
}
|
|
71
|
-
i
|
|
87
|
+
i = end;
|
|
72
88
|
continue;
|
|
73
89
|
}
|
|
74
90
|
if (c === '\\') {
|
|
75
|
-
if (chars[i + 1] === '\n')
|
|
91
|
+
if (chars[i + 1] === '\n') {
|
|
76
92
|
return 'command must not use line continuations';
|
|
93
|
+
}
|
|
77
94
|
i += 2;
|
|
78
95
|
continue;
|
|
79
96
|
}
|
|
80
|
-
if (c === '`')
|
|
97
|
+
if (c === '`') {
|
|
81
98
|
return 'command must not use backtick substitution';
|
|
82
|
-
|
|
99
|
+
}
|
|
100
|
+
if (c === '$') {
|
|
83
101
|
return 'command must not use shell expansion';
|
|
84
|
-
|
|
102
|
+
}
|
|
103
|
+
if (c === ';') {
|
|
85
104
|
return 'command must not chain with ;';
|
|
86
|
-
|
|
105
|
+
}
|
|
106
|
+
if (c === '&') {
|
|
87
107
|
return 'command must not use & or &&';
|
|
88
|
-
|
|
108
|
+
}
|
|
109
|
+
if (c === '|') {
|
|
89
110
|
return 'command must not use pipes';
|
|
90
|
-
|
|
111
|
+
}
|
|
112
|
+
if (c === '>') {
|
|
91
113
|
return 'command must not redirect output';
|
|
92
|
-
|
|
114
|
+
}
|
|
115
|
+
if (c === '<') {
|
|
93
116
|
return 'command must not redirect input';
|
|
117
|
+
}
|
|
94
118
|
i++;
|
|
95
119
|
}
|
|
96
120
|
return null;
|
|
@@ -1,36 +1,48 @@
|
|
|
1
1
|
export function shellEscapeArg(value) {
|
|
2
2
|
return value.replace(/[\\'" \t\n]/g, '\\$&');
|
|
3
3
|
}
|
|
4
|
+
function readQuoted(chars, open) {
|
|
5
|
+
const quote = chars[open];
|
|
6
|
+
let text = '';
|
|
7
|
+
let i = open + 1;
|
|
8
|
+
while (i < chars.length && chars[i] !== quote) {
|
|
9
|
+
if (quote === '"' && chars[i] === '\\' && i + 1 < chars.length) {
|
|
10
|
+
text += chars[i + 1];
|
|
11
|
+
i += 2;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
text += chars[i];
|
|
15
|
+
i++;
|
|
16
|
+
}
|
|
17
|
+
if (i < chars.length) {
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
return { text, end: i };
|
|
21
|
+
}
|
|
4
22
|
export function shellArgv(command) {
|
|
5
23
|
const chars = [...command];
|
|
6
24
|
const out = [];
|
|
7
25
|
let i = 0;
|
|
8
26
|
while (i < chars.length) {
|
|
9
|
-
while (i < chars.length && (chars[i] === ' ' || chars[i] === '\t'))
|
|
27
|
+
while (i < chars.length && (chars[i] === ' ' || chars[i] === '\t')) {
|
|
10
28
|
i++;
|
|
11
|
-
|
|
29
|
+
}
|
|
30
|
+
if (i >= chars.length) {
|
|
12
31
|
break;
|
|
13
|
-
|
|
32
|
+
}
|
|
33
|
+
if (chars[i] === '\n') {
|
|
14
34
|
break;
|
|
35
|
+
}
|
|
15
36
|
let token = '';
|
|
16
37
|
while (i < chars.length) {
|
|
17
38
|
const c = chars[i];
|
|
18
|
-
if (c === ' ' || c === '\t' || c === '\n')
|
|
39
|
+
if (c === ' ' || c === '\t' || c === '\n') {
|
|
19
40
|
break;
|
|
41
|
+
}
|
|
20
42
|
if (c === "'" || c === '"') {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (quote === '"' && chars[i] === '\\' && i + 1 < chars.length) {
|
|
25
|
-
token += chars[i + 1];
|
|
26
|
-
i += 2;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
token += chars[i];
|
|
30
|
-
i++;
|
|
31
|
-
}
|
|
32
|
-
if (i < chars.length)
|
|
33
|
-
i++;
|
|
43
|
+
const quoted = readQuoted(chars, i);
|
|
44
|
+
token += quoted.text;
|
|
45
|
+
i = quoted.end;
|
|
34
46
|
continue;
|
|
35
47
|
}
|
|
36
48
|
if (c === '\\' && i + 1 < chars.length) {
|
|
@@ -41,8 +53,9 @@ export function shellArgv(command) {
|
|
|
41
53
|
token += c;
|
|
42
54
|
i++;
|
|
43
55
|
}
|
|
44
|
-
if (token.length > 0 || out.length > 0)
|
|
56
|
+
if (token.length > 0 || out.length > 0) {
|
|
45
57
|
out.push(token);
|
|
58
|
+
}
|
|
46
59
|
}
|
|
47
60
|
return out;
|
|
48
61
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export declare const PYTHON_BIN_ERROR: string;
|
|
2
2
|
export declare const PACKAGES_NEED_PYTHON_ERROR: string;
|
|
3
3
|
export declare const FILES_NEED_PYTHON_ERROR: string;
|
|
4
|
-
export
|
|
4
|
+
export interface VideoToolCall {
|
|
5
|
+
packages?: readonly string[] | undefined;
|
|
6
|
+
python: boolean;
|
|
7
|
+
network?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function videoToolError(command: string, { packages, python, network }: VideoToolCall): string | null;
|
|
5
10
|
export declare const SHELL_DOWNLOAD_NOTE: string;
|
|
6
11
|
export declare function shellDownloadNote(command: string): string | null;
|
|
7
12
|
export declare function pipError(command: string, sandbox?: boolean): string | null;
|