@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExecResult } from '../../runner.ts';
|
|
2
|
-
export
|
|
2
|
+
export interface PyodideLike {
|
|
3
3
|
runPython(code: string, options?: {
|
|
4
4
|
filename?: string;
|
|
5
5
|
}): unknown;
|
|
@@ -42,7 +42,7 @@ export type PyodideLike = {
|
|
|
42
42
|
unlink(path: string): void;
|
|
43
43
|
rmdir(path: string): void;
|
|
44
44
|
};
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
46
|
export type LoadPyodideFn = () => Promise<PyodideLike>;
|
|
47
47
|
export type PythonRunResult = ExecResult & {
|
|
48
48
|
files?: ReadonlyMap<string, Uint8Array>;
|
|
@@ -53,7 +53,7 @@ export type PythonRunResult = ExecResult & {
|
|
|
53
53
|
}[];
|
|
54
54
|
undeclared?: readonly string[];
|
|
55
55
|
};
|
|
56
|
-
export
|
|
56
|
+
export interface RunSpec {
|
|
57
57
|
readonly code: string;
|
|
58
58
|
readonly packages?: readonly string[] | undefined;
|
|
59
59
|
readonly inputs?: readonly {
|
|
@@ -66,15 +66,16 @@ export type RunSpec = {
|
|
|
66
66
|
readonly installTimeoutMs: number;
|
|
67
67
|
readonly network?: boolean | undefined;
|
|
68
68
|
readonly hosts?: readonly string[] | undefined;
|
|
69
|
-
}
|
|
70
|
-
export
|
|
69
|
+
}
|
|
70
|
+
export interface RunOutcome {
|
|
71
71
|
readonly result: PythonRunResult;
|
|
72
72
|
readonly evict: boolean;
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
export declare function cap(text: string, limit: number): string;
|
|
75
75
|
export declare function isPyodideFatal(error: unknown): boolean;
|
|
76
76
|
export declare function repairDoubleEscapedSource(code: string, message: string): string | null;
|
|
77
|
-
export
|
|
77
|
+
export interface PerformRunOptions {
|
|
78
78
|
readonly isCancelled?: () => boolean;
|
|
79
79
|
readonly askConnect?: ((host: string) => Promise<boolean>) | undefined;
|
|
80
|
-
}
|
|
80
|
+
}
|
|
81
|
+
export declare function performRun(py: PyodideLike, spec: RunSpec, opts?: PerformRunOptions): Promise<RunOutcome>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorMessage } from '../../../../guards.js';
|
|
1
2
|
import { AbortedError } from '../../../../helpers/race-abort.js';
|
|
2
3
|
import { hlog } from '../../../../log.js';
|
|
3
4
|
import { TRUNCATED_OUTPUT_SUFFIX } from '../../runner.js';
|
|
@@ -13,8 +14,9 @@ export function cap(text, limit) {
|
|
|
13
14
|
export function isPyodideFatal(error) {
|
|
14
15
|
if (typeof error !== 'object' || error === null)
|
|
15
16
|
return false;
|
|
16
|
-
if ('pyodide_fatal_error' in error && error.pyodide_fatal_error === true)
|
|
17
|
+
if ('pyodide_fatal_error' in error && error.pyodide_fatal_error === true) {
|
|
17
18
|
return true;
|
|
19
|
+
}
|
|
18
20
|
return error instanceof Error && error.message.includes('and can no longer be used');
|
|
19
21
|
}
|
|
20
22
|
const DOUBLE_ESCAPED = /\\[nt"']/;
|
|
@@ -24,8 +26,9 @@ function unescapeSource(code) {
|
|
|
24
26
|
return '\n';
|
|
25
27
|
if (char === 't')
|
|
26
28
|
return '\t';
|
|
27
|
-
if (char === 'r')
|
|
29
|
+
if (char === 'r') {
|
|
28
30
|
return '\r';
|
|
31
|
+
}
|
|
29
32
|
return char;
|
|
30
33
|
});
|
|
31
34
|
}
|
|
@@ -34,8 +37,9 @@ export function repairDoubleEscapedSource(code, message) {
|
|
|
34
37
|
return null;
|
|
35
38
|
if (code.includes('\n'))
|
|
36
39
|
return null;
|
|
37
|
-
if (!DOUBLE_ESCAPED.test(code))
|
|
40
|
+
if (!DOUBLE_ESCAPED.test(code)) {
|
|
38
41
|
return null;
|
|
42
|
+
}
|
|
39
43
|
const repaired = unescapeSource(code);
|
|
40
44
|
return repaired === code ? null : repaired;
|
|
41
45
|
}
|
|
@@ -60,7 +64,7 @@ async function enter(py, code, access) {
|
|
|
60
64
|
setConnectPolicy(null);
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
|
-
async function attempt(py, code, output, maxOutputChars, access) {
|
|
67
|
+
async function attempt(py, code, { output, maxOutputChars, access }) {
|
|
64
68
|
try {
|
|
65
69
|
await enter(py, code, access);
|
|
66
70
|
return {
|
|
@@ -74,7 +78,7 @@ async function attempt(py, code, output, maxOutputChars, access) {
|
|
|
74
78
|
};
|
|
75
79
|
}
|
|
76
80
|
catch (error) {
|
|
77
|
-
const message =
|
|
81
|
+
const message = errorMessage(error);
|
|
78
82
|
return {
|
|
79
83
|
result: {
|
|
80
84
|
stdout: cap(output.stdout, maxOutputChars),
|
|
@@ -86,16 +90,19 @@ async function attempt(py, code, output, maxOutputChars, access) {
|
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
|
-
async function runOnce(py, code,
|
|
90
|
-
const first = await attempt(py, code,
|
|
91
|
-
if (first.error === null)
|
|
93
|
+
async function runOnce(py, code, opts) {
|
|
94
|
+
const first = await attempt(py, code, opts);
|
|
95
|
+
if (first.error === null) {
|
|
92
96
|
return first;
|
|
97
|
+
}
|
|
93
98
|
const repaired = repairDoubleEscapedSource(code, first.error);
|
|
94
|
-
if (repaired === null)
|
|
99
|
+
if (repaired === null) {
|
|
95
100
|
return first;
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
}
|
|
102
|
+
const retry = await attempt(py, repaired, opts);
|
|
103
|
+
if (retry.error !== null && retry.error.includes('SyntaxError')) {
|
|
98
104
|
return first;
|
|
105
|
+
}
|
|
99
106
|
return retry;
|
|
100
107
|
}
|
|
101
108
|
const FIT_DECKS = `
|
|
@@ -168,16 +175,18 @@ class InstallTimeoutError extends Error {
|
|
|
168
175
|
}
|
|
169
176
|
function installSource(py, packages) {
|
|
170
177
|
const plan = planVendoredInstall(packages);
|
|
171
|
-
if (!plan)
|
|
178
|
+
if (!plan) {
|
|
172
179
|
return `import micropip\nawait micropip.install(${JSON.stringify(packages)})`;
|
|
180
|
+
}
|
|
173
181
|
const specs = [...stageVendoredWheels(py.FS, plan), ...plan.packages];
|
|
174
182
|
hlog('python: installing from vendored wheels', { specs });
|
|
175
183
|
return `import micropip\nawait micropip.install(${JSON.stringify(specs)}, deps=False)`;
|
|
176
184
|
}
|
|
177
|
-
async function installPackages(py, packages, timeoutMs, isCancelled) {
|
|
185
|
+
async function installPackages(py, packages, { timeoutMs, isCancelled }) {
|
|
178
186
|
await py.loadPackage('micropip');
|
|
179
|
-
if (isCancelled())
|
|
187
|
+
if (isCancelled()) {
|
|
180
188
|
throw new AbortedError();
|
|
189
|
+
}
|
|
181
190
|
const install = py.runPythonAsync(installSource(py, packages));
|
|
182
191
|
let timer;
|
|
183
192
|
const bound = new Promise((_, reject) => {
|
|
@@ -187,8 +196,9 @@ async function installPackages(py, packages, timeoutMs, isCancelled) {
|
|
|
187
196
|
await Promise.race([install, bound]);
|
|
188
197
|
}
|
|
189
198
|
finally {
|
|
190
|
-
if (timer)
|
|
199
|
+
if (timer) {
|
|
191
200
|
clearTimeout(timer);
|
|
201
|
+
}
|
|
192
202
|
}
|
|
193
203
|
}
|
|
194
204
|
const DELIVERABLE = /\.(pptx|docx|xlsx|pdf|png|jpe?g|gif|webp|csv|json|txt|md|zip|mp4|webm|m4a|mp3)$/i;
|
|
@@ -204,15 +214,16 @@ function newDeliverables(before, after) {
|
|
|
204
214
|
return [...after].filter((name) => !before.has(name) && DELIVERABLE.test(name));
|
|
205
215
|
}
|
|
206
216
|
function fitDecks(py, outputs) {
|
|
207
|
-
if (!outputs.some((name) => name.toLowerCase().endsWith('.pptx')))
|
|
217
|
+
if (!outputs.some((name) => name.toLowerCase().endsWith('.pptx'))) {
|
|
208
218
|
return false;
|
|
219
|
+
}
|
|
209
220
|
try {
|
|
210
221
|
py.runPython(`${FIT_DECKS}\n_qvac_fit_decks(${JSON.stringify(outputs)})`);
|
|
211
222
|
return false;
|
|
212
223
|
}
|
|
213
224
|
catch (error) {
|
|
214
225
|
hlog('python: deck fit skipped', {
|
|
215
|
-
err:
|
|
226
|
+
err: errorMessage(error)
|
|
216
227
|
});
|
|
217
228
|
return isPyodideFatal(error);
|
|
218
229
|
}
|
|
@@ -225,20 +236,25 @@ export async function performRun(py, spec, opts = {}) {
|
|
|
225
236
|
py.setStdout({ batched: (chunk) => (output.stdout += chunk + '\n') });
|
|
226
237
|
py.setStderr({ batched: (chunk) => (output.stderr += chunk + '\n') });
|
|
227
238
|
if (spec.packages?.length) {
|
|
228
|
-
if (isCancelled())
|
|
239
|
+
if (isCancelled()) {
|
|
229
240
|
throw new AbortedError();
|
|
241
|
+
}
|
|
230
242
|
hlog('python: installing packages', {
|
|
231
243
|
packages: [...spec.packages],
|
|
232
244
|
timeoutMs: spec.installTimeoutMs
|
|
233
245
|
});
|
|
234
246
|
try {
|
|
235
|
-
await installPackages(py, spec.packages,
|
|
247
|
+
await installPackages(py, spec.packages, {
|
|
248
|
+
timeoutMs: spec.installTimeoutMs,
|
|
249
|
+
isCancelled
|
|
250
|
+
});
|
|
236
251
|
hlog('python: packages ready', { packages: [...spec.packages] });
|
|
237
252
|
}
|
|
238
253
|
catch (error) {
|
|
239
|
-
if (error instanceof AbortedError)
|
|
254
|
+
if (error instanceof AbortedError) {
|
|
240
255
|
throw error;
|
|
241
|
-
|
|
256
|
+
}
|
|
257
|
+
const message = errorMessage(error);
|
|
242
258
|
const timedOut = error instanceof InstallTimeoutError;
|
|
243
259
|
const evict = timedOut || isPyodideFatal(error);
|
|
244
260
|
hlog(timedOut ? 'python: package install timed out' : 'python: package install failed', {
|
|
@@ -254,14 +270,20 @@ export async function performRun(py, spec, opts = {}) {
|
|
|
254
270
|
output.stdout = '';
|
|
255
271
|
output.stderr = '';
|
|
256
272
|
}
|
|
257
|
-
if (isCancelled())
|
|
273
|
+
if (isCancelled()) {
|
|
258
274
|
throw new AbortedError();
|
|
275
|
+
}
|
|
259
276
|
if (!spec.inputs?.length && !spec.outputs?.length && !spec.workdir) {
|
|
260
277
|
const cwd = py.FS.cwd();
|
|
261
278
|
const before = listFiles(py, cwd);
|
|
262
|
-
const { result, fatal } = await runOnce(py, spec.code,
|
|
263
|
-
|
|
279
|
+
const { result, fatal } = await runOnce(py, spec.code, {
|
|
280
|
+
output,
|
|
281
|
+
maxOutputChars: spec.maxOutputChars,
|
|
282
|
+
access
|
|
283
|
+
});
|
|
284
|
+
if (result.exitCode !== 0) {
|
|
264
285
|
return { result, evict: fatal };
|
|
286
|
+
}
|
|
265
287
|
const undeclared = newDeliverables(before, listFiles(py, cwd));
|
|
266
288
|
return { result: undeclared.length > 0 ? { ...result, undeclared } : result, evict: false };
|
|
267
289
|
}
|
|
@@ -270,12 +292,18 @@ export async function performRun(py, spec, opts = {}) {
|
|
|
270
292
|
py.FS.chdir(scratch.cwd);
|
|
271
293
|
stageInputFiles(py.FS, scratch.cwd, spec.inputs ?? []);
|
|
272
294
|
const before = listFiles(py, scratch.cwd);
|
|
273
|
-
const { result, fatal } = await runOnce(py, spec.code,
|
|
295
|
+
const { result, fatal } = await runOnce(py, spec.code, {
|
|
296
|
+
output,
|
|
297
|
+
maxOutputChars: spec.maxOutputChars,
|
|
298
|
+
access
|
|
299
|
+
});
|
|
274
300
|
let evict = fatal;
|
|
275
|
-
if (result.exitCode === 0)
|
|
301
|
+
if (result.exitCode === 0) {
|
|
276
302
|
evict = fitDecks(py, spec.outputs ?? []);
|
|
277
|
-
|
|
303
|
+
}
|
|
304
|
+
if (evict) {
|
|
278
305
|
return { result: { ...result, missing: spec.outputs ?? [] }, evict };
|
|
306
|
+
}
|
|
279
307
|
const outputs = collectOutputs(py.FS, scratch.cwd, spec.outputs ?? []);
|
|
280
308
|
const declared = new Set(spec.outputs ?? []);
|
|
281
309
|
const undeclared = result.exitCode === 0
|
|
@@ -287,7 +315,7 @@ export async function performRun(py, spec, opts = {}) {
|
|
|
287
315
|
files: outputs.files,
|
|
288
316
|
missing: outputs.missing,
|
|
289
317
|
rejected: outputs.rejected,
|
|
290
|
-
|
|
318
|
+
undeclared: undeclared.length > 0 ? undeclared : undefined
|
|
291
319
|
},
|
|
292
320
|
evict: false
|
|
293
321
|
};
|
|
@@ -17,28 +17,26 @@ declare const ManifestSchema: z.ZodObject<{
|
|
|
17
17
|
}, z.core.$strip>>;
|
|
18
18
|
}, z.core.$strip>;
|
|
19
19
|
export type WheelManifest = z.infer<typeof ManifestSchema>;
|
|
20
|
-
export
|
|
20
|
+
export interface VendoredInstall {
|
|
21
21
|
readonly wheels: readonly string[];
|
|
22
22
|
readonly packages: readonly string[];
|
|
23
|
-
}
|
|
24
|
-
|
|
23
|
+
}
|
|
24
|
+
interface WheelFs {
|
|
25
25
|
mkdirTree(path: string): void;
|
|
26
26
|
writeFile(path: string, data: Uint8Array): void;
|
|
27
27
|
analyzePath(path: string): {
|
|
28
28
|
exists: boolean;
|
|
29
29
|
};
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
export declare function wheelsDir(): string | undefined;
|
|
32
32
|
export declare function vendoredManifest(dir?: string | undefined): WheelManifest | null;
|
|
33
33
|
export declare function normalizeDistribution(name: string): string;
|
|
34
|
-
export
|
|
34
|
+
export interface VendoredOptions {
|
|
35
35
|
readonly dir?: string | undefined;
|
|
36
36
|
readonly manifest?: WheelManifest | null;
|
|
37
|
-
}
|
|
38
|
-
export declare function
|
|
39
|
-
|
|
40
|
-
readonly manifest?: WheelManifest | null;
|
|
41
|
-
}): VendoredInstall | null;
|
|
37
|
+
}
|
|
38
|
+
export declare function vendoredSpec(distribution: string, opts?: VendoredOptions): string | null;
|
|
39
|
+
export declare function planVendoredInstall(specs: readonly string[], opts?: VendoredOptions): VendoredInstall | null;
|
|
42
40
|
export declare function stageVendoredWheels(target: WheelFs, install: VendoredInstall): string[];
|
|
43
41
|
export declare function seedPackageCache(cacheDir: string, dir?: string | undefined): void;
|
|
44
42
|
export {};
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import process from 'process';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { errorMessage } from '../../../../guards.js';
|
|
5
6
|
import { hlog } from '../../../../log.js';
|
|
6
7
|
const ManifestSchema = z.object({
|
|
7
8
|
pyodide: z.string(),
|
|
@@ -16,12 +17,14 @@ const ManifestSchema = z.object({
|
|
|
16
17
|
const STAGE_DIR = '/qvac-wheels';
|
|
17
18
|
export function wheelsDir() {
|
|
18
19
|
const fromHost = process.env.QVAC_PYTHON_WHEELS_DIR;
|
|
19
|
-
if (fromHost)
|
|
20
|
+
if (fromHost) {
|
|
20
21
|
return fromHost;
|
|
22
|
+
}
|
|
21
23
|
const root = new URL('../../../../../', import.meta.url);
|
|
22
24
|
const source = new URL(root.pathname.endsWith('/dist-lib/') ? '../wheels' : 'wheels', root);
|
|
23
|
-
if (source.href.includes('.bundle/'))
|
|
25
|
+
if (source.href.includes('.bundle/')) {
|
|
24
26
|
return undefined;
|
|
27
|
+
}
|
|
25
28
|
return fileURLToPath(source.href);
|
|
26
29
|
}
|
|
27
30
|
export function vendoredManifest(dir = wheelsDir()) {
|
|
@@ -38,7 +41,7 @@ export function vendoredManifest(dir = wheelsDir()) {
|
|
|
38
41
|
catch (error) {
|
|
39
42
|
hlog('python: vendored wheel manifest unusable', {
|
|
40
43
|
file,
|
|
41
|
-
err:
|
|
44
|
+
err: errorMessage(error)
|
|
42
45
|
});
|
|
43
46
|
return null;
|
|
44
47
|
}
|
|
@@ -58,23 +61,26 @@ export function vendoredSpec(distribution, opts = {}) {
|
|
|
58
61
|
const dir = opts.dir ?? wheelsDir();
|
|
59
62
|
const manifest = opts.manifest === undefined ? vendoredManifest(dir) : opts.manifest;
|
|
60
63
|
const entry = manifest?.packages[name];
|
|
61
|
-
if (!entry)
|
|
64
|
+
if (!entry) {
|
|
62
65
|
return null;
|
|
66
|
+
}
|
|
63
67
|
const spec = entry.version ? `${name}==${entry.version}` : name;
|
|
64
68
|
return planVendoredInstall([spec], { dir, manifest }) ? spec : null;
|
|
65
69
|
}
|
|
66
70
|
export function planVendoredInstall(specs, opts = {}) {
|
|
67
71
|
const dir = opts.dir ?? wheelsDir();
|
|
68
72
|
const manifest = opts.manifest === undefined ? vendoredManifest(dir) : opts.manifest;
|
|
69
|
-
if (!manifest || !dir || specs.length === 0)
|
|
73
|
+
if (!manifest || !dir || specs.length === 0) {
|
|
70
74
|
return null;
|
|
75
|
+
}
|
|
71
76
|
const wheels = new Set();
|
|
72
77
|
const packages = new Set();
|
|
73
78
|
for (const spec of specs) {
|
|
74
79
|
const { name, version } = parseSpec(spec);
|
|
75
80
|
const entry = manifest.packages[name];
|
|
76
|
-
if (!entry || entry.version !== version)
|
|
81
|
+
if (!entry || entry.version !== version) {
|
|
77
82
|
return null;
|
|
83
|
+
}
|
|
78
84
|
for (const wheel of entry.pypi) {
|
|
79
85
|
if (!fs.existsSync(`${dir}/${wheel}`)) {
|
|
80
86
|
hlog('python: vendored wheel missing, installing from PyPI instead', { wheel });
|
|
@@ -82,14 +88,16 @@ export function planVendoredInstall(specs, opts = {}) {
|
|
|
82
88
|
}
|
|
83
89
|
wheels.add(`${dir}/${wheel}`);
|
|
84
90
|
}
|
|
85
|
-
for (const dependency of entry.pyodide)
|
|
91
|
+
for (const dependency of entry.pyodide) {
|
|
86
92
|
packages.add(dependency);
|
|
93
|
+
}
|
|
87
94
|
}
|
|
88
95
|
return { wheels: [...wheels], packages: [...packages] };
|
|
89
96
|
}
|
|
90
97
|
export function stageVendoredWheels(target, install) {
|
|
91
|
-
if (install.wheels.length > 0)
|
|
98
|
+
if (install.wheels.length > 0) {
|
|
92
99
|
target.mkdirTree(STAGE_DIR);
|
|
100
|
+
}
|
|
93
101
|
return install.wheels.map((wheel) => {
|
|
94
102
|
const staged = `${STAGE_DIR}/${wheel.slice(wheel.lastIndexOf('/') + 1)}`;
|
|
95
103
|
if (!target.analyzePath(staged).exists) {
|
|
@@ -100,18 +108,21 @@ export function stageVendoredWheels(target, install) {
|
|
|
100
108
|
}
|
|
101
109
|
export function seedPackageCache(cacheDir, dir = wheelsDir()) {
|
|
102
110
|
const manifest = vendoredManifest(dir);
|
|
103
|
-
if (!manifest || !dir)
|
|
111
|
+
if (!manifest || !dir) {
|
|
104
112
|
return;
|
|
113
|
+
}
|
|
105
114
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
106
115
|
let seeded = 0;
|
|
107
116
|
for (const [file, entry] of Object.entries(manifest.files)) {
|
|
108
|
-
if (entry.channel !== 'pyodide' || fs.existsSync(`${cacheDir}/${file}`))
|
|
117
|
+
if (entry.channel !== 'pyodide' || fs.existsSync(`${cacheDir}/${file}`)) {
|
|
109
118
|
continue;
|
|
119
|
+
}
|
|
110
120
|
if (!fs.existsSync(`${dir}/${file}`))
|
|
111
121
|
continue;
|
|
112
122
|
fs.copyFileSync(`${dir}/${file}`, `${cacheDir}/${file}`);
|
|
113
123
|
seeded++;
|
|
114
124
|
}
|
|
115
|
-
if (seeded > 0)
|
|
125
|
+
if (seeded > 0) {
|
|
116
126
|
hlog('python: seeded package cache', { cacheDir, wheels: seeded });
|
|
127
|
+
}
|
|
117
128
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
2
|
import type { PythonRunResult, RunSpec } from '../run-session.ts';
|
|
3
|
-
export
|
|
3
|
+
export interface RunSpecOptions {
|
|
4
4
|
readonly timeoutMs: number;
|
|
5
5
|
readonly signal?: BareAbortSignal | undefined;
|
|
6
6
|
readonly askConnect?: ((host: string) => Promise<boolean>) | undefined;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
|
+
export declare function runPythonInWorker(spec: RunSpec, opts: RunSpecOptions): Promise<PythonRunResult>;
|
|
8
9
|
export declare function closePythonWorker(): Promise<void>;
|
|
@@ -20,17 +20,20 @@ function workerEntry() {
|
|
|
20
20
|
async function readMessage(link) {
|
|
21
21
|
for (;;) {
|
|
22
22
|
const message = asWorkerMessage(await link.read());
|
|
23
|
-
if (message?.type !== 'log')
|
|
23
|
+
if (message?.type !== 'log') {
|
|
24
24
|
return message;
|
|
25
|
+
}
|
|
25
26
|
hlog(`python worker: ${message.message}`);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
async function awaitReady(link) {
|
|
29
30
|
const message = await readMessage(link);
|
|
30
|
-
if (message?.type === 'ready')
|
|
31
|
+
if (message?.type === 'ready') {
|
|
31
32
|
return;
|
|
32
|
-
|
|
33
|
+
}
|
|
34
|
+
if (message?.type === 'boot-error') {
|
|
33
35
|
throw new Error(`python worker: ${message.message}`);
|
|
36
|
+
}
|
|
34
37
|
throw new Error('python worker sent an unexpected first message');
|
|
35
38
|
}
|
|
36
39
|
function boot() {
|
|
@@ -44,28 +47,33 @@ function boot() {
|
|
|
44
47
|
nextId: 1
|
|
45
48
|
};
|
|
46
49
|
worker.ready.catch(() => {
|
|
47
|
-
if (active === worker)
|
|
50
|
+
if (active === worker) {
|
|
48
51
|
active = null;
|
|
52
|
+
}
|
|
49
53
|
link.kill();
|
|
50
54
|
});
|
|
51
55
|
return worker;
|
|
52
56
|
}
|
|
53
57
|
function destroy(worker) {
|
|
54
|
-
if (active === worker)
|
|
58
|
+
if (active === worker) {
|
|
55
59
|
active = null;
|
|
60
|
+
}
|
|
56
61
|
worker.link.kill();
|
|
57
62
|
}
|
|
58
63
|
async function drainAndReap(worker) {
|
|
59
|
-
if (active === worker)
|
|
64
|
+
if (active === worker) {
|
|
60
65
|
active = null;
|
|
66
|
+
}
|
|
61
67
|
const closed = readMessage(worker.link).then(() => 'message', () => 'closed');
|
|
62
68
|
const timer = grace(SHUTDOWN_GRACE_MS);
|
|
63
69
|
try {
|
|
64
70
|
const settled = await Promise.race([closed, timer.expired]);
|
|
65
|
-
if (settled === 'closed')
|
|
71
|
+
if (settled === 'closed') {
|
|
66
72
|
worker.link.reap();
|
|
67
|
-
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
68
75
|
worker.link.kill();
|
|
76
|
+
}
|
|
69
77
|
}
|
|
70
78
|
finally {
|
|
71
79
|
timer.clear();
|
|
@@ -79,12 +87,14 @@ async function resultFor(worker, id, askConnect) {
|
|
|
79
87
|
message = await readMessage(worker.link);
|
|
80
88
|
}
|
|
81
89
|
if (message?.type === 'result') {
|
|
82
|
-
if (message.id !== id)
|
|
90
|
+
if (message.id !== id) {
|
|
83
91
|
throw new Error('python worker answered a different run');
|
|
92
|
+
}
|
|
84
93
|
return { result: fromWireResult(message.result), evict: message.evict };
|
|
85
94
|
}
|
|
86
|
-
if (message?.type === 'boot-error')
|
|
95
|
+
if (message?.type === 'boot-error') {
|
|
87
96
|
throw new Error(`python worker: ${message.message}`);
|
|
97
|
+
}
|
|
88
98
|
throw new Error('python worker sent an unexpected message');
|
|
89
99
|
}
|
|
90
100
|
function grace(ms) {
|
|
@@ -129,8 +139,9 @@ export async function runPythonInWorker(spec, opts) {
|
|
|
129
139
|
const ask = opts.askConnect;
|
|
130
140
|
const outcome = (async () => {
|
|
131
141
|
await worker.ready;
|
|
132
|
-
if (cancelled)
|
|
142
|
+
if (cancelled) {
|
|
133
143
|
throw new AbortedError();
|
|
144
|
+
}
|
|
134
145
|
sent = true;
|
|
135
146
|
await worker.link.write({ type: 'run', id, spec });
|
|
136
147
|
return resultFor(worker, id, ask && ((host) => raceAbort(ask(host), stop.signal)));
|
|
@@ -139,38 +150,40 @@ export async function runPythonInWorker(spec, opts) {
|
|
|
139
150
|
const timeout = new Promise((resolve) => {
|
|
140
151
|
timer = setTimeout(() => resolve('timeout'), opts.timeoutMs);
|
|
141
152
|
});
|
|
153
|
+
const cancel = async () => {
|
|
154
|
+
cancelled = true;
|
|
155
|
+
stop.abort(undefined);
|
|
156
|
+
if (sent) {
|
|
157
|
+
await interrupt(worker, outcome);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
outcome.catch(noop);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
142
163
|
let first;
|
|
143
164
|
try {
|
|
144
165
|
first = await raceAbort(Promise.race([outcome, timeout]), opts.signal);
|
|
145
166
|
}
|
|
146
167
|
catch (error) {
|
|
147
168
|
if (error instanceof AbortedError) {
|
|
148
|
-
|
|
149
|
-
stop.abort(undefined);
|
|
150
|
-
if (sent)
|
|
151
|
-
await interrupt(worker, outcome);
|
|
152
|
-
else
|
|
153
|
-
outcome.catch(() => { });
|
|
169
|
+
await cancel();
|
|
154
170
|
throw error;
|
|
155
171
|
}
|
|
156
172
|
destroy(worker);
|
|
157
173
|
throw error;
|
|
158
174
|
}
|
|
159
175
|
finally {
|
|
160
|
-
if (timer)
|
|
176
|
+
if (timer) {
|
|
161
177
|
clearTimeout(timer);
|
|
178
|
+
}
|
|
162
179
|
}
|
|
163
180
|
if (first === 'timeout') {
|
|
164
|
-
|
|
165
|
-
stop.abort(undefined);
|
|
166
|
-
if (sent)
|
|
167
|
-
await interrupt(worker, outcome);
|
|
168
|
-
else
|
|
169
|
-
outcome.catch(() => { });
|
|
181
|
+
await cancel();
|
|
170
182
|
throw new Error(`Command timed out after ${opts.timeoutMs}ms`);
|
|
171
183
|
}
|
|
172
|
-
if (first.evict)
|
|
184
|
+
if (first.evict) {
|
|
173
185
|
await drainAndReap(worker);
|
|
186
|
+
}
|
|
174
187
|
return first.result;
|
|
175
188
|
}
|
|
176
189
|
finally {
|
|
@@ -179,8 +192,9 @@ export async function runPythonInWorker(spec, opts) {
|
|
|
179
192
|
}
|
|
180
193
|
export async function closePythonWorker() {
|
|
181
194
|
const worker = active;
|
|
182
|
-
if (!worker)
|
|
195
|
+
if (!worker) {
|
|
183
196
|
return;
|
|
197
|
+
}
|
|
184
198
|
active = null;
|
|
185
199
|
const timer = grace(SHUTDOWN_GRACE_MS);
|
|
186
200
|
try {
|
|
@@ -199,3 +213,4 @@ export async function closePythonWorker() {
|
|
|
199
213
|
timer.clear();
|
|
200
214
|
}
|
|
201
215
|
}
|
|
216
|
+
function noop() { }
|