@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
|
@@ -7,24 +7,27 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
9
|
import process from 'process';
|
|
10
|
+
import { errorMessage, isRecord } from '../../../../../guards.js';
|
|
10
11
|
import { setHarnessLog, setHarnessLogSink } from '../../../../../log.js';
|
|
11
12
|
import { defaultLoad } from '../loader.js';
|
|
12
13
|
import { performRun } from '../run-session.js';
|
|
13
14
|
import { asHostMessage, toWireResult } from './protocol.js';
|
|
14
15
|
function narrowThreadData(data) {
|
|
15
|
-
if (
|
|
16
|
+
if (isRecord(data) && 'sab' in data && 'handle' in data) {
|
|
16
17
|
const sab = data.sab;
|
|
17
18
|
const log = 'log' in data && data.log === true;
|
|
18
|
-
if (sab instanceof SharedArrayBuffer)
|
|
19
|
+
if (sab instanceof SharedArrayBuffer) {
|
|
19
20
|
return { handle: data.handle, sab, log };
|
|
21
|
+
}
|
|
20
22
|
}
|
|
21
23
|
throw new Error('python worker started without { handle, sab } thread data');
|
|
22
24
|
}
|
|
23
25
|
async function openBarePort() {
|
|
24
26
|
const { default: Channel } = await import('bare-channel');
|
|
25
27
|
const self = Bare.Thread.self;
|
|
26
|
-
if (!self)
|
|
28
|
+
if (!self) {
|
|
27
29
|
throw new Error('python worker entry loaded on the main thread');
|
|
30
|
+
}
|
|
28
31
|
const { handle, sab, log } = narrowThreadData(self.data);
|
|
29
32
|
const port = Channel.from(handle).connect();
|
|
30
33
|
return {
|
|
@@ -40,23 +43,27 @@ async function openBarePort() {
|
|
|
40
43
|
}
|
|
41
44
|
async function openNodePort() {
|
|
42
45
|
const { parentPort, workerData } = await import('worker_threads');
|
|
43
|
-
if (!parentPort)
|
|
46
|
+
if (!parentPort) {
|
|
44
47
|
throw new Error('python worker entry loaded on the main thread');
|
|
45
|
-
|
|
48
|
+
}
|
|
49
|
+
const data = narrowThreadData({ handle: parentPort, ...workerData });
|
|
46
50
|
const queued = [];
|
|
47
51
|
const waiting = [];
|
|
48
52
|
parentPort.on('message', (value) => {
|
|
49
53
|
const next = waiting.shift();
|
|
50
|
-
if (next)
|
|
54
|
+
if (next) {
|
|
51
55
|
next(value);
|
|
52
|
-
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
53
58
|
queued.push(value);
|
|
59
|
+
}
|
|
54
60
|
});
|
|
55
61
|
return {
|
|
56
62
|
port: {
|
|
57
63
|
read() {
|
|
58
|
-
if (queued.length > 0)
|
|
64
|
+
if (queued.length > 0) {
|
|
59
65
|
return Promise.resolve(queued.shift());
|
|
66
|
+
}
|
|
60
67
|
return new Promise((resolve) => waiting.push(resolve));
|
|
61
68
|
},
|
|
62
69
|
write: (message) => parentPort.postMessage(message),
|
|
@@ -67,13 +74,11 @@ async function openNodePort() {
|
|
|
67
74
|
}
|
|
68
75
|
async function loadInterpreter() {
|
|
69
76
|
const testLoader = process.env.QVAC_PYODIDE_TEST_LOADER;
|
|
70
|
-
if (!testLoader)
|
|
77
|
+
if (!testLoader) {
|
|
71
78
|
return defaultLoad;
|
|
79
|
+
}
|
|
72
80
|
const loaded = await import(__rewriteRelativeImportExtension(testLoader));
|
|
73
|
-
if (typeof loaded === '
|
|
74
|
-
loaded !== null &&
|
|
75
|
-
'default' in loaded &&
|
|
76
|
-
typeof loaded.default === 'function') {
|
|
81
|
+
if (isRecord(loaded) && 'default' in loaded && typeof loaded.default === 'function') {
|
|
77
82
|
return loaded.default;
|
|
78
83
|
}
|
|
79
84
|
throw new Error(`QVAC_PYODIDE_TEST_LOADER module has no default export: ${testLoader}`);
|
|
@@ -88,29 +93,33 @@ try {
|
|
|
88
93
|
port.write({ type: 'ready' });
|
|
89
94
|
for (;;) {
|
|
90
95
|
const message = asHostMessage(await port.read());
|
|
91
|
-
if (!message || message.type === 'close')
|
|
96
|
+
if (!message || message.type === 'close') {
|
|
92
97
|
break;
|
|
93
|
-
|
|
98
|
+
}
|
|
99
|
+
if (message.type === 'connect-answer') {
|
|
94
100
|
continue;
|
|
101
|
+
}
|
|
95
102
|
let closing = false;
|
|
96
103
|
const askConnect = async (host) => {
|
|
97
104
|
if (closing)
|
|
98
105
|
return false;
|
|
99
106
|
port.write({ type: 'connect-ask', host });
|
|
100
107
|
const answer = asHostMessage(await port.read());
|
|
101
|
-
if (answer?.type === 'connect-answer')
|
|
108
|
+
if (answer?.type === 'connect-answer') {
|
|
102
109
|
return answer.allowed;
|
|
110
|
+
}
|
|
103
111
|
closing = true;
|
|
104
112
|
return false;
|
|
105
113
|
};
|
|
106
114
|
const { result, evict } = await performRun(py, message.spec, { askConnect });
|
|
107
115
|
port.write({ type: 'result', id: message.id, result: toWireResult(result), evict });
|
|
108
|
-
if (evict || closing)
|
|
116
|
+
if (evict || closing) {
|
|
109
117
|
break;
|
|
118
|
+
}
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
121
|
catch (error) {
|
|
113
|
-
const message =
|
|
122
|
+
const message = errorMessage(error);
|
|
114
123
|
port.write({ type: 'boot-error', message });
|
|
115
124
|
}
|
|
116
125
|
finally {
|
|
@@ -9,7 +9,7 @@ export type HostMessage = {
|
|
|
9
9
|
readonly type: 'connect-answer';
|
|
10
10
|
readonly allowed: boolean;
|
|
11
11
|
};
|
|
12
|
-
export
|
|
12
|
+
export interface WireResult {
|
|
13
13
|
readonly stdout: string;
|
|
14
14
|
readonly stderr: string;
|
|
15
15
|
readonly exitCode: number;
|
|
@@ -20,7 +20,7 @@ export type WireResult = {
|
|
|
20
20
|
readonly reason: string;
|
|
21
21
|
}[];
|
|
22
22
|
readonly undeclared?: readonly string[];
|
|
23
|
-
}
|
|
23
|
+
}
|
|
24
24
|
export type WorkerMessage = {
|
|
25
25
|
readonly type: 'log';
|
|
26
26
|
readonly message: string;
|
|
@@ -21,25 +21,26 @@ export function fromWireResult(wire) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
function messageType(value) {
|
|
24
|
-
if (typeof value !== 'object' || value === null)
|
|
24
|
+
if (typeof value !== 'object' || value === null || !('type' in value)) {
|
|
25
25
|
return null;
|
|
26
|
-
|
|
27
|
-
return typeof type === 'string' ? type : null;
|
|
26
|
+
}
|
|
27
|
+
return typeof value.type === 'string' ? value.type : null;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
function isHostMessage(value) {
|
|
30
30
|
const type = messageType(value);
|
|
31
|
-
|
|
32
|
-
return value;
|
|
33
|
-
return null;
|
|
31
|
+
return type === 'run' || type === 'close' || type === 'connect-answer';
|
|
34
32
|
}
|
|
35
|
-
|
|
33
|
+
function isWorkerMessage(value) {
|
|
36
34
|
const type = messageType(value);
|
|
37
|
-
|
|
35
|
+
return (type === 'log' ||
|
|
38
36
|
type === 'ready' ||
|
|
39
37
|
type === 'boot-error' ||
|
|
40
38
|
type === 'result' ||
|
|
41
|
-
type === 'connect-ask')
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return null;
|
|
39
|
+
type === 'connect-ask');
|
|
40
|
+
}
|
|
41
|
+
export function asHostMessage(value) {
|
|
42
|
+
return isHostMessage(value) ? value : null;
|
|
43
|
+
}
|
|
44
|
+
export function asWorkerMessage(value) {
|
|
45
|
+
return isWorkerMessage(value) ? value : null;
|
|
45
46
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface WorkerLink {
|
|
2
2
|
read(): Promise<unknown>;
|
|
3
3
|
write(message: unknown): Promise<void>;
|
|
4
4
|
kill(): void;
|
|
5
5
|
reap(): void;
|
|
6
6
|
ref(): void;
|
|
7
7
|
unref(): void;
|
|
8
|
-
}
|
|
9
|
-
export
|
|
8
|
+
}
|
|
9
|
+
export interface WorkerData {
|
|
10
10
|
readonly sab: SharedArrayBuffer;
|
|
11
11
|
readonly log: boolean;
|
|
12
|
-
}
|
|
12
|
+
}
|
|
13
13
|
export declare function spawnPythonWorker(entry: string, data: WorkerData): WorkerLink;
|
|
14
14
|
export declare function spawnInlineThread(source: string, data: object): () => void;
|
|
@@ -9,17 +9,20 @@ export function spawnPythonWorker(entry, data) {
|
|
|
9
9
|
let died = null;
|
|
10
10
|
const push = (value) => {
|
|
11
11
|
const next = waiting.shift();
|
|
12
|
-
if (next)
|
|
12
|
+
if (next) {
|
|
13
13
|
next.resolve(value);
|
|
14
|
-
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
15
16
|
queued.push(value);
|
|
17
|
+
}
|
|
16
18
|
};
|
|
17
19
|
const fail = (error) => {
|
|
18
20
|
if (died)
|
|
19
21
|
return;
|
|
20
22
|
died = error;
|
|
21
|
-
for (const pending of waiting.splice(0))
|
|
23
|
+
for (const pending of waiting.splice(0)) {
|
|
22
24
|
pending.reject(error);
|
|
25
|
+
}
|
|
23
26
|
};
|
|
24
27
|
worker.on('message', push);
|
|
25
28
|
worker.on('error', (error) => fail(error instanceof Error ? error : new Error(String(error))));
|
|
@@ -27,10 +30,12 @@ export function spawnPythonWorker(entry, data) {
|
|
|
27
30
|
worker.unref();
|
|
28
31
|
return {
|
|
29
32
|
read() {
|
|
30
|
-
if (queued.length > 0)
|
|
33
|
+
if (queued.length > 0) {
|
|
31
34
|
return Promise.resolve(queued.shift());
|
|
32
|
-
|
|
35
|
+
}
|
|
36
|
+
if (died) {
|
|
33
37
|
return Promise.reject(died);
|
|
38
|
+
}
|
|
34
39
|
return new Promise((resolve, reject) => waiting.push({ resolve, reject }));
|
|
35
40
|
},
|
|
36
41
|
write(message) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type KillProcess, type SpawnFn } from '../../runner.ts';
|
|
2
2
|
import type { FallbackExecutionAdapter } from '../adapter.ts';
|
|
3
|
-
export
|
|
3
|
+
export interface ShellRuntime {
|
|
4
4
|
readonly spawn: SpawnFn;
|
|
5
5
|
readonly killProcess?: KillProcess;
|
|
6
|
-
}
|
|
7
|
-
export
|
|
6
|
+
}
|
|
7
|
+
export interface ShellAdapterOptions {
|
|
8
8
|
readonly spawn?: SpawnFn;
|
|
9
9
|
readonly killProcess?: KillProcess;
|
|
10
10
|
readonly loadRuntime: () => Promise<ShellRuntime>;
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
export declare function createShellAdapter(options: ShellAdapterOptions): FallbackExecutionAdapter;
|
|
@@ -15,8 +15,9 @@ export function createShellAdapter(options) {
|
|
|
15
15
|
killProcess: runtime.killProcess
|
|
16
16
|
});
|
|
17
17
|
const note = result.exitCode === 0 ? shellDownloadNote(command) : null;
|
|
18
|
-
if (!note)
|
|
18
|
+
if (!note) {
|
|
19
19
|
return result;
|
|
20
|
+
}
|
|
20
21
|
const stdout = `${result.stdout}${result.stdout && !result.stdout.endsWith('\n') ? '\n' : ''}${note}`;
|
|
21
22
|
return { ...result, stdout };
|
|
22
23
|
}
|
|
@@ -4,5 +4,10 @@ import type { KillProcess, SpawnFn } from '../runner.ts';
|
|
|
4
4
|
export declare function runningCheckCommand(os: string): string;
|
|
5
5
|
export declare function launchCommand(os: string): string;
|
|
6
6
|
export declare function obsidianPathEnv(platform?: NodeJS.Platform, env?: Record<string, string | undefined>, probe?: ObsidianPathProbe): Record<string, string> | undefined;
|
|
7
|
-
export
|
|
7
|
+
export interface ObsidianRunningOptions {
|
|
8
|
+
readonly killProcess?: KillProcess | undefined;
|
|
9
|
+
readonly signal?: BareAbortSignal | undefined;
|
|
10
|
+
readonly platform?: NodeJS.Platform;
|
|
11
|
+
}
|
|
12
|
+
export declare function isObsidianRunning(spawn: SpawnFn, { killProcess, signal, platform }?: ObsidianRunningOptions): Promise<boolean>;
|
|
8
13
|
export declare function launchObsidianApp(spawn: SpawnFn, platform?: NodeJS.Platform): void;
|
|
@@ -21,14 +21,15 @@ export function obsidianPathEnv(platform = process.platform, env = process.env,
|
|
|
21
21
|
const expand = probe.expand ?? ((candidate) => expandObsidianPath(candidate, env));
|
|
22
22
|
const exists = probe.exists ?? ((candidate) => fs.existsSync(candidate));
|
|
23
23
|
const bin = firstExistingPath(OBSIDIAN_BIN_CANDIDATES[platform] ?? [], expand, exists);
|
|
24
|
-
if (!bin)
|
|
24
|
+
if (!bin) {
|
|
25
25
|
return undefined;
|
|
26
|
+
}
|
|
26
27
|
const binDir = path.dirname(bin);
|
|
27
28
|
const scrubbed = execEnv(env, platform);
|
|
28
29
|
const prev = scrubbed.PATH ?? '';
|
|
29
30
|
return { ...scrubbed, PATH: prev ? `${binDir}${path.delimiter}${prev}` : binDir };
|
|
30
31
|
}
|
|
31
|
-
export function isObsidianRunning(spawn, killProcess, signal, platform = process.platform) {
|
|
32
|
+
export function isObsidianRunning(spawn, { killProcess, signal, platform = process.platform } = {}) {
|
|
32
33
|
return new Promise((resolve) => {
|
|
33
34
|
if (signal?.aborted) {
|
|
34
35
|
resolve(false);
|
|
@@ -43,10 +44,12 @@ export function isObsidianRunning(spawn, killProcess, signal, platform = process
|
|
|
43
44
|
globalThis.clearTimeout(timer);
|
|
44
45
|
signal?.removeEventListener('abort', onAbort);
|
|
45
46
|
try {
|
|
46
|
-
if (child.pid !== undefined && killProcess)
|
|
47
|
+
if (child.pid !== undefined && killProcess) {
|
|
47
48
|
killProcess(-child.pid, 'SIGKILL');
|
|
48
|
-
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
49
51
|
child.kill('SIGKILL');
|
|
52
|
+
}
|
|
50
53
|
}
|
|
51
54
|
catch {
|
|
52
55
|
child.kill('SIGKILL');
|
|
@@ -65,7 +68,7 @@ export function launchObsidianApp(spawn, platform = process.platform) {
|
|
|
65
68
|
const env = obsidianPathEnv(platform);
|
|
66
69
|
const child = spawn(launchCommand(platform), {
|
|
67
70
|
shell: true,
|
|
68
|
-
|
|
71
|
+
env
|
|
69
72
|
});
|
|
70
73
|
child.on('error', () => { });
|
|
71
74
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
2
|
import { type ExecResult, type KillProcess, type SpawnFn } from '../runner.ts';
|
|
3
|
-
export
|
|
3
|
+
export interface RunObsidianCliOptions {
|
|
4
4
|
readonly timeoutMs?: number;
|
|
5
5
|
readonly maxOutputChars?: number;
|
|
6
6
|
readonly signal?: BareAbortSignal;
|
|
7
|
-
}
|
|
8
|
-
export
|
|
7
|
+
}
|
|
8
|
+
export interface RunOutcome {
|
|
9
9
|
readonly result: string;
|
|
10
10
|
readonly stderr: string;
|
|
11
11
|
readonly exitCode: number | null;
|
|
12
|
-
}
|
|
13
|
-
export
|
|
12
|
+
}
|
|
13
|
+
export interface ObsidianCliRuntime {
|
|
14
14
|
readonly spawn: SpawnFn;
|
|
15
15
|
readonly killProcess?: KillProcess;
|
|
16
|
-
}
|
|
16
|
+
}
|
|
17
17
|
export declare function toResult(outcome: RunOutcome): ExecResult;
|
|
18
18
|
export declare function runObsidianCli(command: string, runtime: ObsidianCliRuntime, options?: RunObsidianCliOptions): Promise<ExecResult>;
|
|
@@ -67,7 +67,7 @@ export function toResult(outcome) {
|
|
|
67
67
|
exitCode: 1
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
function runOnce(command, runtime, timeoutMs, maxOutputChars, signal) {
|
|
70
|
+
function runOnce(command, runtime, { timeoutMs, maxOutputChars, signal }) {
|
|
71
71
|
return new Promise((resolve) => {
|
|
72
72
|
if (signal?.aborted) {
|
|
73
73
|
resolve({ result: '', stderr: '', exitCode: null });
|
|
@@ -77,7 +77,7 @@ function runOnce(command, runtime, timeoutMs, maxOutputChars, signal) {
|
|
|
77
77
|
const child = runtime.spawn(command, {
|
|
78
78
|
shell: true,
|
|
79
79
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
80
|
-
|
|
80
|
+
env
|
|
81
81
|
});
|
|
82
82
|
const stdout = createCapturedOutput();
|
|
83
83
|
const stderr = createCapturedOutput();
|
|
@@ -130,7 +130,7 @@ export async function runObsidianCli(command, runtime, options = {}) {
|
|
|
130
130
|
const signal = options.signal;
|
|
131
131
|
const maxOutputChars = options.maxOutputChars ?? MAX_OUTPUT_CHARS;
|
|
132
132
|
const timeoutMs = options.timeoutMs ?? COMMAND_MS;
|
|
133
|
-
if (!(await isObsidianRunning(runtime.spawn, runtime.killProcess, signal))) {
|
|
133
|
+
if (!(await isObsidianRunning(runtime.spawn, { killProcess: runtime.killProcess, signal }))) {
|
|
134
134
|
if (signal?.aborted) {
|
|
135
135
|
return { stdout: '', stderr: 'Obsidian command aborted.', exitCode: 1 };
|
|
136
136
|
}
|
|
@@ -140,7 +140,7 @@ export async function runObsidianCli(command, runtime, options = {}) {
|
|
|
140
140
|
if (signal?.aborted) {
|
|
141
141
|
return { stdout: '', stderr: 'Obsidian command aborted.', exitCode: 1 };
|
|
142
142
|
}
|
|
143
|
-
const outcome = await runOnce(command, runtime, timeoutMs, maxOutputChars, signal);
|
|
143
|
+
const outcome = await runOnce(command, runtime, { timeoutMs, maxOutputChars, signal });
|
|
144
144
|
if (signal?.aborted) {
|
|
145
145
|
return { stdout: '', stderr: 'Obsidian command aborted.', exitCode: 1 };
|
|
146
146
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface ObsidianCommand {
|
|
2
2
|
action: string;
|
|
3
3
|
args: string[];
|
|
4
|
-
}
|
|
4
|
+
}
|
|
5
|
+
export declare function parseObsidianCommand(command: string): ObsidianCommand | null;
|
|
5
6
|
export declare function normalizeObsidianCommand(command: string): string;
|
|
@@ -3,13 +3,16 @@ const OBSIDIAN_BIN = /^obsidian(?:\.exe|\.com)?$/i;
|
|
|
3
3
|
const OBSIDIAN_PREFIX = /^obsidian(?:\.exe|\.com)?(?:\s|$)/i;
|
|
4
4
|
export function parseObsidianCommand(command) {
|
|
5
5
|
const trimmed = command.trim();
|
|
6
|
-
if (!OBSIDIAN_PREFIX.test(trimmed))
|
|
6
|
+
if (!OBSIDIAN_PREFIX.test(trimmed)) {
|
|
7
7
|
return null;
|
|
8
|
+
}
|
|
8
9
|
const tokens = shellArgv(trimmed);
|
|
9
|
-
if (tokens.length === 0 || !OBSIDIAN_BIN.test(tokens[0] ?? ''))
|
|
10
|
+
if (tokens.length === 0 || !OBSIDIAN_BIN.test(tokens[0] ?? '')) {
|
|
10
11
|
return null;
|
|
11
|
-
|
|
12
|
+
}
|
|
13
|
+
if (tokens.length === 1) {
|
|
12
14
|
return { action: '', args: [] };
|
|
15
|
+
}
|
|
13
16
|
const [, action, ...args] = tokens;
|
|
14
17
|
return { action: action ?? '', args };
|
|
15
18
|
}
|
|
@@ -16,8 +16,9 @@ function isNoiseLine(line) {
|
|
|
16
16
|
return true;
|
|
17
17
|
if (rule.kind === 'equals' && line === rule.text)
|
|
18
18
|
return true;
|
|
19
|
-
if (rule.kind === 'includes' && line.includes(rule.text))
|
|
19
|
+
if (rule.kind === 'includes' && line.includes(rule.text)) {
|
|
20
20
|
return true;
|
|
21
|
+
}
|
|
21
22
|
}
|
|
22
23
|
return false;
|
|
23
24
|
}
|
|
@@ -2,18 +2,16 @@ import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
|
2
2
|
import type { ToolExecuteContext } from '../../../capabilities/completion.ts';
|
|
3
3
|
import { type CredentialStoreFn } from '../../skills/auth-resolver.ts';
|
|
4
4
|
import { type ExecResult, type KillProcess, type SpawnFn } from '../runner.ts';
|
|
5
|
-
|
|
5
|
+
import type { ShellRuntime } from '../adapters/shell/shell-adapter.ts';
|
|
6
|
+
export interface ObsidianRouteOptions {
|
|
6
7
|
readonly credentialStore?: CredentialStoreFn;
|
|
7
8
|
readonly spawn?: SpawnFn;
|
|
8
9
|
readonly killProcess?: KillProcess;
|
|
9
|
-
readonly loadRuntime?: () => Promise<
|
|
10
|
-
spawn: SpawnFn;
|
|
11
|
-
killProcess?: KillProcess;
|
|
12
|
-
}>;
|
|
10
|
+
readonly loadRuntime?: () => Promise<ShellRuntime>;
|
|
13
11
|
readonly platform?: string;
|
|
14
12
|
readonly env?: Record<string, string | undefined>;
|
|
15
13
|
readonly timeoutMs?: number;
|
|
16
14
|
readonly maxOutputChars?: number;
|
|
17
15
|
readonly signal?: BareAbortSignal;
|
|
18
|
-
}
|
|
16
|
+
}
|
|
19
17
|
export declare function routeObsidianCommand(command: string, ctx: ToolExecuteContext | undefined, options?: ObsidianRouteOptions): Promise<ExecResult | null>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import process from 'process';
|
|
2
|
+
import { errorMessage } from '../../../guards.js';
|
|
2
3
|
import { credentialRecord } from '../../skills/auth-resolver.js';
|
|
3
4
|
import { isObsidianVaultPath, isSandboxedWorkbenchInstall, OBSIDIAN_VAULT_PATH_CREDENTIAL_KEY } from '../../skills/obsidian-readiness.js';
|
|
4
5
|
import { capOutput, MAX_OUTPUT_CHARS } from '../runner.js';
|
|
@@ -7,6 +8,12 @@ import { normalizeObsidianCommand, parseObsidianCommand } from './command.js';
|
|
|
7
8
|
import { OBSIDIAN_DESKTOP_AGENT_REQUIRED, OBSIDIAN_NO_SHELL_FALLBACK, OBSIDIAN_VAULT_SETUP_REQUIRED } from './guidance.js';
|
|
8
9
|
import { executeVaultFsAction } from './vault-fs.js';
|
|
9
10
|
const DESKTOP_PLATFORMS = new Set(['darwin', 'linux', 'win32']);
|
|
11
|
+
async function runtimeOf(options) {
|
|
12
|
+
if (options.spawn) {
|
|
13
|
+
return { spawn: options.spawn, killProcess: options.killProcess };
|
|
14
|
+
}
|
|
15
|
+
return options.loadRuntime ? await options.loadRuntime() : null;
|
|
16
|
+
}
|
|
10
17
|
function limitResult(result, maxOutputChars) {
|
|
11
18
|
return {
|
|
12
19
|
stdout: capOutput(result.stdout, maxOutputChars),
|
|
@@ -19,12 +26,12 @@ function vaultFsResult(stdout, exitCode) {
|
|
|
19
26
|
? { stdout, stderr: '', exitCode: 0 }
|
|
20
27
|
: { stdout: '', stderr: `${stdout}\n\n${OBSIDIAN_NO_SHELL_FALLBACK}`, exitCode };
|
|
21
28
|
}
|
|
22
|
-
function runVaultFs(vaultPath, action, args, maxOutputChars) {
|
|
29
|
+
function runVaultFs(vaultPath, { action, args, maxOutputChars }) {
|
|
23
30
|
try {
|
|
24
31
|
return limitResult(vaultFsResult(executeVaultFsAction(vaultPath, action, args), 0), maxOutputChars);
|
|
25
32
|
}
|
|
26
33
|
catch (error) {
|
|
27
|
-
const message =
|
|
34
|
+
const message = errorMessage(error);
|
|
28
35
|
return limitResult(vaultFsResult(message, 1), maxOutputChars);
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -33,14 +40,16 @@ async function resolveVaultPath(credentialStore, ctx) {
|
|
|
33
40
|
return null;
|
|
34
41
|
const record = credentialRecord(await credentialStore(OBSIDIAN_VAULT_PATH_CREDENTIAL_KEY, ctx));
|
|
35
42
|
const token = record?.token?.trim();
|
|
36
|
-
if (!token || !isObsidianVaultPath(token))
|
|
43
|
+
if (!token || !isObsidianVaultPath(token)) {
|
|
37
44
|
return null;
|
|
45
|
+
}
|
|
38
46
|
return token;
|
|
39
47
|
}
|
|
40
48
|
export async function routeObsidianCommand(command, ctx, options = {}) {
|
|
41
49
|
const parsed = parseObsidianCommand(command);
|
|
42
|
-
if (!parsed)
|
|
50
|
+
if (!parsed) {
|
|
43
51
|
return null;
|
|
52
|
+
}
|
|
44
53
|
const maxOutputChars = options.maxOutputChars ?? MAX_OUTPUT_CHARS;
|
|
45
54
|
const signal = options.signal ?? ctx?.signal;
|
|
46
55
|
if (signal?.aborted) {
|
|
@@ -55,8 +64,9 @@ export async function routeObsidianCommand(command, ctx, options = {}) {
|
|
|
55
64
|
};
|
|
56
65
|
}
|
|
57
66
|
const vaultPath = await resolveVaultPath(options.credentialStore, ctx);
|
|
58
|
-
if (vaultPath)
|
|
59
|
-
return runVaultFs(vaultPath, parsed.action, parsed.args, maxOutputChars);
|
|
67
|
+
if (vaultPath) {
|
|
68
|
+
return runVaultFs(vaultPath, { action: parsed.action, args: parsed.args, maxOutputChars });
|
|
69
|
+
}
|
|
60
70
|
if (isSandboxedWorkbenchInstall(options.env ?? process.env)) {
|
|
61
71
|
return {
|
|
62
72
|
stdout: '',
|
|
@@ -64,11 +74,7 @@ export async function routeObsidianCommand(command, ctx, options = {}) {
|
|
|
64
74
|
exitCode: 1
|
|
65
75
|
};
|
|
66
76
|
}
|
|
67
|
-
const runtime = options
|
|
68
|
-
? { spawn: options.spawn, killProcess: options.killProcess }
|
|
69
|
-
: options.loadRuntime
|
|
70
|
-
? await options.loadRuntime()
|
|
71
|
-
: null;
|
|
77
|
+
const runtime = await runtimeOf(options);
|
|
72
78
|
if (!runtime) {
|
|
73
79
|
return {
|
|
74
80
|
stdout: '',
|
|
@@ -14,8 +14,9 @@ function rejectSymlinkComponents(root, target) {
|
|
|
14
14
|
for (const segment of segments) {
|
|
15
15
|
current = path.join(current, segment);
|
|
16
16
|
const stat = lstatOrNull(current);
|
|
17
|
-
if (!stat)
|
|
17
|
+
if (!stat) {
|
|
18
18
|
return;
|
|
19
|
+
}
|
|
19
20
|
if (stat.isSymbolicLink()) {
|
|
20
21
|
throw new Error(`Path "${target}" crosses a symlink inside the vault.`);
|
|
21
22
|
}
|
|
@@ -12,10 +12,11 @@ function createAtPath(vaultPath, target, content) {
|
|
|
12
12
|
writeNewFileText(filePath, content);
|
|
13
13
|
return `Created: ${path.relative(vaultPath, filePath)}`;
|
|
14
14
|
}
|
|
15
|
-
function createNamed(vaultPath, name, folder, content) {
|
|
15
|
+
function createNamed(vaultPath, { name, folder, content }) {
|
|
16
16
|
const dir = folder ? ensureWithinRoot(vaultPath, folder) : path.resolve(vaultPath);
|
|
17
|
-
if (!fs.existsSync(dir))
|
|
17
|
+
if (!fs.existsSync(dir)) {
|
|
18
18
|
fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
}
|
|
19
20
|
const fileName = markdownFileName(name);
|
|
20
21
|
const filePath = ensureWithinRoot(vaultPath, path.join(folder, fileName));
|
|
21
22
|
if (fs.existsSync(filePath)) {
|
|
@@ -27,14 +28,21 @@ function createNamed(vaultPath, name, folder, content) {
|
|
|
27
28
|
export function handleCreate(vaultPath, args) {
|
|
28
29
|
const content = getContentArg(args);
|
|
29
30
|
const name = getArg(args, 'name');
|
|
30
|
-
if (name)
|
|
31
|
-
return createNamed(vaultPath,
|
|
31
|
+
if (name) {
|
|
32
|
+
return createNamed(vaultPath, {
|
|
33
|
+
name,
|
|
34
|
+
folder: getArg(args, 'path') || '',
|
|
35
|
+
content: content || ''
|
|
36
|
+
});
|
|
37
|
+
}
|
|
32
38
|
const filePath = getArg(args, 'path');
|
|
33
39
|
const file = getArg(args, 'file');
|
|
34
|
-
if (filePath && file)
|
|
35
|
-
return createNamed(vaultPath, file, filePath, content || '');
|
|
40
|
+
if (filePath && file) {
|
|
41
|
+
return createNamed(vaultPath, { name: file, folder: filePath, content: content || '' });
|
|
42
|
+
}
|
|
36
43
|
const target = filePath ?? file;
|
|
37
|
-
if (!target)
|
|
44
|
+
if (!target) {
|
|
38
45
|
throw new Error('Missing required argument: name=, file=, or path=');
|
|
46
|
+
}
|
|
39
47
|
return createAtPath(vaultPath, target, content || '');
|
|
40
48
|
}
|
|
@@ -2,10 +2,10 @@ export { ensureWithinRoot, writeNewFileText } from './safe-path.ts';
|
|
|
2
2
|
export declare const FRONTMATTER_RE: RegExp;
|
|
3
3
|
export declare function resolveNotePath(vaultPath: string, args: string[]): string | null;
|
|
4
4
|
export declare function readFileText(filePath: string): string;
|
|
5
|
-
export
|
|
5
|
+
export interface FileSnapshot {
|
|
6
6
|
readonly text: string;
|
|
7
7
|
readonly mtimeMs: number;
|
|
8
|
-
}
|
|
8
|
+
}
|
|
9
9
|
export declare function readFileSnapshot(filePath: string): FileSnapshot | null;
|
|
10
10
|
export declare function writeIfUnchanged(filePath: string, content: string, expectedMtimeMs: number | null): void;
|
|
11
11
|
export declare function updateFileText(filePath: string, transform: (existing: string) => string): void;
|