@ny4n/codemode 1.1.0 → 1.3.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/LICENSE +1 -0
- package/README.md +56 -7
- package/dist/declarations.d.ts +6 -21
- package/dist/declarations.d.ts.map +1 -1
- package/dist/declarations.js +100 -122
- package/dist/declarations.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/portable.d.ts +1 -1
- package/dist/portable.d.ts.map +1 -1
- package/dist/portable.js.map +1 -1
- package/dist/runtime/execution.d.ts +14 -2
- package/dist/runtime/execution.d.ts.map +1 -1
- package/dist/runtime/execution.js +158 -35
- package/dist/runtime/execution.js.map +1 -1
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/host.js +25 -11
- package/dist/runtime/host.js.map +1 -1
- package/dist/runtime/inline.d.ts.map +1 -1
- package/dist/runtime/inline.js +1 -0
- package/dist/runtime/inline.js.map +1 -1
- package/dist/runtime/prelude-source.d.ts +3 -1
- package/dist/runtime/prelude-source.d.ts.map +1 -1
- package/dist/runtime/prelude-source.js +16 -6
- package/dist/runtime/prelude-source.js.map +1 -1
- package/dist/runtime/protocol.d.ts +38 -3
- package/dist/runtime/protocol.d.ts.map +1 -1
- package/dist/runtime/protocol.js +6 -1
- package/dist/runtime/protocol.js.map +1 -1
- package/dist/runtime/remote.d.ts.map +1 -1
- package/dist/runtime/remote.js +5 -0
- package/dist/runtime/remote.js.map +1 -1
- package/dist/runtime/vm.d.ts +2 -0
- package/dist/runtime/vm.d.ts.map +1 -1
- package/dist/runtime/vm.js +131 -9
- package/dist/runtime/vm.js.map +1 -1
- package/dist/search.d.ts +33 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +112 -0
- package/dist/search.js.map +1 -0
- package/dist/source.d.ts +2 -1
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +8 -2
- package/dist/source.js.map +1 -1
- package/dist/types.d.ts +102 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -1
package/dist/runtime/host.js
CHANGED
|
@@ -10,26 +10,31 @@ function defaultWorkerUrl() {
|
|
|
10
10
|
* termination simple: a runaway script, including one that only spins the microtask queue, is
|
|
11
11
|
* killed with `terminate()` and cannot poison a later run. Before terminating, the host sets a
|
|
12
12
|
* shared interrupt flag that the VM polls, because Bun's `terminate()` cannot stop a thread that
|
|
13
|
-
* is spinning in wasm.
|
|
13
|
+
* is spinning in wasm. Stopping requests termination without waiting for exit: some built-ins do
|
|
14
|
+
* not poll the flag, so on Bun a worker can outlive the script's result.
|
|
14
15
|
*/
|
|
15
|
-
function workerLauncher(
|
|
16
|
+
function workerLauncher(options) {
|
|
16
17
|
return async (data, events) => {
|
|
17
18
|
let module;
|
|
18
19
|
try {
|
|
19
|
-
module = await (wasm ?? loadQuickJSWasm());
|
|
20
|
+
module = await (options.wasm ?? loadQuickJSWasm());
|
|
20
21
|
}
|
|
21
22
|
catch (error) {
|
|
22
23
|
throw new Error(`Failed to load QuickJS: ${errorMessage(error)}`);
|
|
23
24
|
}
|
|
24
25
|
const interrupt = new SharedArrayBuffer(4);
|
|
25
26
|
const workerData = { ...data, wasm: module, interrupt };
|
|
27
|
+
const releaseWorker = options.acquireWorker?.();
|
|
26
28
|
let worker;
|
|
27
29
|
try {
|
|
28
|
-
worker = new Worker(workerUrl, { workerData });
|
|
30
|
+
worker = new Worker(options.workerUrl ?? defaultWorkerUrl(), { workerData });
|
|
29
31
|
}
|
|
30
32
|
catch (error) {
|
|
33
|
+
releaseWorker?.();
|
|
31
34
|
throw new Error(`Failed to start worker: ${errorMessage(error)}`);
|
|
32
35
|
}
|
|
36
|
+
if (releaseWorker)
|
|
37
|
+
worker.once("exit", releaseWorker);
|
|
33
38
|
worker.on("message", (message) => {
|
|
34
39
|
// Node 24/26 --watch leaks dependency reports onto the worker channel (nodejs/node#65044).
|
|
35
40
|
if (typeof message === "object" &&
|
|
@@ -48,13 +53,22 @@ function workerLauncher(wasm, workerUrl) {
|
|
|
48
53
|
worker.on("exit", (code) => {
|
|
49
54
|
events.failure({ kind: "sandbox", message: `Worker exited with code ${code} before the script settled` });
|
|
50
55
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const stop = async () => {
|
|
57
|
+
Atomics.store(new Int32Array(interrupt), 0, 1);
|
|
58
|
+
// A non-interruptible built-in can delay Bun's terminate() until it returns.
|
|
59
|
+
// Do not hold up the result while that worker exits.
|
|
60
|
+
void worker.terminate().catch(() => undefined);
|
|
61
|
+
// terminate() refs the worker internally; unref it after requesting termination.
|
|
62
|
+
worker.unref();
|
|
57
63
|
};
|
|
64
|
+
try {
|
|
65
|
+
options.onWorkerStarted?.(new Promise((resolve) => worker.once("exit", () => resolve())));
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
void stop();
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
return { post: (message) => worker.postMessage(message), stop };
|
|
58
72
|
};
|
|
59
73
|
}
|
|
60
74
|
/**
|
|
@@ -66,7 +80,7 @@ function workerLauncher(wasm, workerUrl) {
|
|
|
66
80
|
*/
|
|
67
81
|
export class CodemodeSandbox extends CodemodeSandboxBase {
|
|
68
82
|
constructor(options = {}) {
|
|
69
|
-
super(options, workerLauncher(options
|
|
83
|
+
super(options, workerLauncher(options));
|
|
70
84
|
}
|
|
71
85
|
}
|
|
72
86
|
//# sourceMappingURL=host.js.map
|
package/dist/runtime/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/runtime/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAGpF,SAAS,gBAAgB;IACxB,8EAA8E;IAC9E,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAClG,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/runtime/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C,OAAO,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AAGpF,SAAS,gBAAgB;IACxB,8EAA8E;IAC9E,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAClG,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,cAAc,CAAC,OAA+B;IACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC7B,IAAI,MAA0B,CAAC;QAC/B,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,eAAe,EAAE,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAe,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACpE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;QAChD,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,gBAAgB,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC9E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,aAAa,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,2BAA2B,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,aAAa;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACtD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE;YACzC,2FAA2F;YAC3F,IACC,OAAO,OAAO,KAAK,QAAQ;gBAC3B,OAAO,KAAK,IAAI;gBAChB,CAAC,cAAc,IAAI,OAAO,IAAI,eAAe,IAAI,OAAO,CAAC;gBAEzD,OAAO;YACR,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAc,EAAE,EAAE;YACrC,MAAM,CAAC,OAAO,CAAC;gBACd,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBACrD,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC1B,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,2BAA2B,IAAI,4BAA4B,EAAE,CAAC,CAAC;QAC3G,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;YACtC,OAAO,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/C,6EAA6E;YAC7E,qDAAqD;YACrD,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC/C,iFAAiF;YACjF,MAAM,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC;QACF,IAAI,CAAC;YACJ,OAAO,CAAC,eAAe,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QACjG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,KAAK,IAAI,EAAE,CAAC;YACZ,MAAM,KAAK,CAAC;QACb,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;IACjE,CAAC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,eAAgB,SAAQ,mBAAmB;IACvD,YAAY,OAAO,GAA2B,EAAE;QAC/C,KAAK,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;CACD","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport type { CodemodeSandboxOptions } from \"../types.ts\";\nimport { type CodemodeWasmModule, loadQuickJSWasm } from \"../wasm.ts\";\nimport { CodemodeSandboxBase, errorMessage, type VmLauncher } from \"./execution.ts\";\nimport type { WorkerData } from \"./protocol.ts\";\n\nfunction defaultWorkerUrl(): URL {\n\t// `.ts` when running from source (tests, tsx), `.js` from the published dist.\n\treturn new URL(import.meta.url.endsWith(\".ts\") ? \"./worker.ts\" : \"./worker.js\", import.meta.url);\n}\n\n/**\n * Each execution gets its own worker thread and QuickJS VM. A fresh worker per run keeps\n * termination simple: a runaway script, including one that only spins the microtask queue, is\n * killed with `terminate()` and cannot poison a later run. Before terminating, the host sets a\n * shared interrupt flag that the VM polls, because Bun's `terminate()` cannot stop a thread that\n * is spinning in wasm. Stopping requests termination without waiting for exit: some built-ins do\n * not poll the flag, so on Bun a worker can outlive the script's result.\n */\nfunction workerLauncher(options: CodemodeSandboxOptions): VmLauncher {\n\treturn async (data, events) => {\n\t\tlet module: CodemodeWasmModule;\n\t\ttry {\n\t\t\tmodule = await (options.wasm ?? loadQuickJSWasm());\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to load QuickJS: ${errorMessage(error)}`);\n\t\t}\n\t\tconst interrupt = new SharedArrayBuffer(4);\n\t\tconst workerData: WorkerData = { ...data, wasm: module, interrupt };\n\t\tconst releaseWorker = options.acquireWorker?.();\n\t\tlet worker: Worker;\n\t\ttry {\n\t\t\tworker = new Worker(options.workerUrl ?? defaultWorkerUrl(), { workerData });\n\t\t} catch (error) {\n\t\t\treleaseWorker?.();\n\t\t\tthrow new Error(`Failed to start worker: ${errorMessage(error)}`);\n\t\t}\n\t\tif (releaseWorker) worker.once(\"exit\", releaseWorker);\n\t\tworker.on(\"message\", (message: unknown) => {\n\t\t\t// Node 24/26 --watch leaks dependency reports onto the worker channel (nodejs/node#65044).\n\t\t\tif (\n\t\t\t\ttypeof message === \"object\" &&\n\t\t\t\tmessage !== null &&\n\t\t\t\t(\"watch:import\" in message || \"watch:require\" in message)\n\t\t\t)\n\t\t\t\treturn;\n\t\t\tevents.message(message);\n\t\t});\n\t\tworker.on(\"error\", (error: unknown) => {\n\t\t\tevents.failure({\n\t\t\t\tkind: \"sandbox\",\n\t\t\t\tname: error instanceof Error ? error.name : undefined,\n\t\t\t\tmessage: errorMessage(error),\n\t\t\t});\n\t\t});\n\t\tworker.on(\"exit\", (code) => {\n\t\t\tevents.failure({ kind: \"sandbox\", message: `Worker exited with code ${code} before the script settled` });\n\t\t});\n\t\tconst stop = async (): Promise<void> => {\n\t\t\tAtomics.store(new Int32Array(interrupt), 0, 1);\n\t\t\t// A non-interruptible built-in can delay Bun's terminate() until it returns.\n\t\t\t// Do not hold up the result while that worker exits.\n\t\t\tvoid worker.terminate().catch(() => undefined);\n\t\t\t// terminate() refs the worker internally; unref it after requesting termination.\n\t\t\tworker.unref();\n\t\t};\n\t\ttry {\n\t\t\toptions.onWorkerStarted?.(new Promise<void>((resolve) => worker.once(\"exit\", () => resolve())));\n\t\t} catch (error) {\n\t\t\tvoid stop();\n\t\t\tthrow error;\n\t\t}\n\t\treturn { post: (message) => worker.postMessage(message), stop };\n\t};\n}\n\n/**\n * Runs JavaScript in a QuickJS VM (a separate wasm instance) inside a worker thread. The worker\n * keeps script execution off the host thread: QuickJS runs synchronously, so a spinning script on\n * the host thread would block its event loop. Requires `node:worker_threads` (Node or Bun); see\n * `InlineCodemodeSandbox` and `RemoteCodemodeSandbox` in `@ny4n/codemode/portable`\n * for other runtimes.\n */\nexport class CodemodeSandbox extends CodemodeSandboxBase {\n\tconstructor(options: CodemodeSandboxOptions = {}) {\n\t\tsuper(options, workerLauncher(options));\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/runtime/inline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGpF,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAEhD,iGAAiG;AACjG,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAEhD,MAAM,WAAW,4BAA6B,SAAQ,0BAA0B;IAC/E;;;OAGG;IACH,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,cAAc,CAC7B,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,EACtD,eAAe,GAAE,MAAiC,GAChD,UAAU,
|
|
1
|
+
{"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../src/runtime/inline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGpF,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAEhD,iGAAiG;AACjG,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAEhD,MAAM,WAAW,4BAA6B,SAAQ,0BAA0B;IAC/E;;;OAGG;IACH,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,cAAc,CAC7B,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,EACtD,eAAe,GAAE,MAAiC,GAChD,UAAU,CAqDZ;AAED;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,mBAAmB;IAC7D,YAAY,OAAO,EAAE,4BAA4B,EAEhD;CACD"}
|
package/dist/runtime/inline.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../../src/runtime/inline.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,iGAAiG;AACjG,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC;AAehD,MAAM,UAAU,cAAc,CAC7B,IAAsD,EACtD,eAAe,GAAW,wBAAwB;IAElD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC7B,IAAI,MAA0B,CAAC;QAC/B,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;YAC7B,IAAI;YACJ,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,qBAAqB;YACnC,eAAe,EAAE,GAAG,EAAE;gBACrB,IAAI,OAAO,IAAI,OAAO,IAAI,SAAS;oBAAE,OAAO,IAAI,CAAC;gBACjD,IAAI,EAAE,KAAK,IAAI,eAAe;oBAAE,OAAO,KAAK,CAAC;gBAC7C,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO,IAAI,CAAC;YACb,CAAC;YACD,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;gBACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC;gBACrE,gGAAgG;gBAChG,IAAI,QAAQ,IAAI,OAAO;oBAAE,OAAO;gBAChC,IAAI,QAAQ;oBAAE,OAAO,GAAG,IAAI,CAAC;gBAC7B,cAAc,CAAC,GAAG,EAAE;oBACnB,IAAI,OAAO;wBAAE,OAAO;oBACpB,qFAAqF;oBACrF,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,CAAC;wBACxE,MAAM,CAAC,OAAO,CAAC;4BACd,IAAI,EAAE,SAAS;4BACf,OAAO,EAAE,8CAA8C,eAAe,EAAE;yBACxE,CAAC,CAAC;wBACH,OAAO;oBACR,CAAC;oBACD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACzB,CAAC,CAAC,CAAC;YACJ,CAAC;SACD,CAAC,CAAC;QACH,OAAO;YACN,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;gBACjB,IAAI,CAAC,OAAO;oBAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,CAAC;YACD,IAAI,EAAE,KAAK,IAAI,EAAE;gBAChB,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;SACD,CAAC;IACH,CAAC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,qBAAsB,SAAQ,mBAAmB;IAC7D,YAAY,OAAqC;QAChD,KAAK,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IACvE,CAAC;CACD","sourcesContent":["/**\n * Runs the VM on the calling thread, for runtimes without worker threads (Cloudflare Workers and\n * Durable Objects, browsers) and for the server side of `RemoteCodemodeSandbox`.\n *\n * Problem: QuickJS runs synchronously. While a script computes, the host's event loop does not\n * run, so neither the deadline timer nor an abort signal can fire. On Workers even `Date.now()` is\n * frozen during synchronous execution, so the VM cannot check a clock either. Example: with a\n * worker thread, `while (true) {}` is stopped by the host's 200 ms timer; on the calling thread\n * that timer never gets a chance to run.\n *\n * Solution: an interrupt budget. QuickJS polls its interrupt handler periodically while a script\n * runs; this launcher counts the polls across the whole execution and interrupts the script once\n * the count passes the budget. The execution then fails with `kind: \"timeout\"`. The deadline and\n * abort signal still work whenever the script waits on a tool, since the event loop runs then.\n *\n * The rate of polls depends on the code and the machine. Measured with a tight loop: about 50,000\n * per second of CPU on an Apple M-series laptop and about 10,000 per second on Cloudflare Durable\n * Objects (October 2026). The default of 100,000 allows roughly 2 to 10 seconds of uninterrupted\n * computation; keep it well below the platform's own CPU limit, because hitting that limit resets\n * a Durable Object instead of failing one script.\n *\n * Once the VM reports `done` or `crash`, the result is decided, so the VM is interrupted at the\n * next poll instead of running on until the host stops it. Example: a script that keeps printing in\n * a `for (;;)` loop after its output limit was reached would otherwise run until the budget ran out\n * and be reported as a timeout.\n *\n * Stack: the VM runs on the caller's stack, which is smaller than a worker thread's (about 1 MB\n * on Node's main thread), so QuickJS's stack guard is set to {@link INLINE_MAX_STACK_SIZE}.\n *\n * Reentrancy: VM callbacks run inside QuickJS. Messages from the VM are delivered to the host in\n * a microtask, after the VM has returned, so host code (tool calls, `finish()`, disposal) never\n * runs inside a VM callback and never re-enters the VM.\n */\nimport type { CodemodeSandboxBaseOptions } from \"../types.ts\";\nimport type { CodemodeWasmModule } from \"../wasm.ts\";\nimport { CodemodeSandboxBase, errorMessage, type VmLauncher } from \"./execution.ts\";\nimport { startVm } from \"./vm.ts\";\n\nexport const DEFAULT_INTERRUPT_BUDGET = 100_000;\n\n/** Allows recursion about 1,000 calls deep and trips well before a 1 MB host stack overflows. */\nexport const INLINE_MAX_STACK_SIZE = 256 * 1024;\n\nexport interface InlineCodemodeSandboxOptions extends CodemodeSandboxBaseOptions {\n\t/**\n\t * Compiled `quickjs-wasi/quickjs.wasm`. On Cloudflare, import it with Wrangler's `CompiledWasm`\n\t * rule (`@ny4n/codemode/cloudflare` does this); on Node, `loadQuickJSWasm()`.\n\t */\n\twasm: CodemodeWasmModule | Promise<CodemodeWasmModule>;\n\t/**\n\t * Interrupt polls an execution may use before it fails with `kind: \"timeout\"`. The only limit on\n\t * a script that computes without awaiting a tool. Default: {@link DEFAULT_INTERRUPT_BUDGET}.\n\t */\n\tinterruptBudget?: number;\n}\n\nexport function inlineLauncher(\n\twasm: CodemodeWasmModule | Promise<CodemodeWasmModule>,\n\tinterruptBudget: number = DEFAULT_INTERRUPT_BUDGET,\n): VmLauncher {\n\treturn async (data, events) => {\n\t\tlet module: CodemodeWasmModule;\n\t\ttry {\n\t\t\tmodule = await wasm;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to load QuickJS: ${errorMessage(error)}`);\n\t\t}\n\t\tlet stopped = false;\n\t\tlet settled = false;\n\t\tlet polls = 0;\n\t\tlet exhausted = false;\n\t\tconst session = await startVm({\n\t\t\tdata,\n\t\t\twasm: module,\n\t\t\tmaxStackSize: INLINE_MAX_STACK_SIZE,\n\t\t\tshouldInterrupt: () => {\n\t\t\t\tif (stopped || settled || exhausted) return true;\n\t\t\t\tif (++polls <= interruptBudget) return false;\n\t\t\t\texhausted = true;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tpost: (message) => {\n\t\t\t\tconst terminal = message.type === \"done\" || message.type === \"crash\";\n\t\t\t\t// Only the first terminal message counts: a crash caused by interrupting a settled VM is noise.\n\t\t\t\tif (terminal && settled) return;\n\t\t\t\tif (terminal) settled = true;\n\t\t\t\tqueueMicrotask(() => {\n\t\t\t\t\tif (stopped) return;\n\t\t\t\t\t// The interrupt surfaces as a crash or a failed done; report what caused it instead.\n\t\t\t\t\tif (exhausted && (message.type === \"done\" || message.type === \"crash\")) {\n\t\t\t\t\t\tevents.failure({\n\t\t\t\t\t\t\tkind: \"timeout\",\n\t\t\t\t\t\t\tmessage: `Execution exceeded its interrupt budget of ${interruptBudget}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tevents.message(message);\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\treturn {\n\t\t\tpost: (message) => {\n\t\t\t\tif (!stopped) session.deliver(message);\n\t\t\t},\n\t\t\tstop: async () => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tstopped = true;\n\t\t\t\tsession.dispose();\n\t\t\t},\n\t\t};\n\t};\n}\n\n/**\n * Runs each execution's QuickJS VM on the calling thread. Works in any runtime with WebAssembly\n * and microtasks; needs no worker threads, files, or Node built-ins. A script that computes\n * blocks the caller's thread until it awaits a tool or uses up its interrupt budget, so prefer\n * `CodemodeSandbox` where worker threads exist, and `RemoteCodemodeSandbox` to keep scripts off a\n * thread that must stay responsive (such as a Durable Object that serves a session).\n */\nexport class InlineCodemodeSandbox extends CodemodeSandboxBase {\n\tconstructor(options: InlineCodemodeSandboxOptions) {\n\t\tsuper(options, inlineLauncher(options.wasm, options.interruptBudget));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"inline.js","sourceRoot":"","sources":["../../src/runtime/inline.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAmB,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,iGAAiG;AACjG,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC;AAehD,MAAM,UAAU,cAAc,CAC7B,IAAsD,EACtD,eAAe,GAAW,wBAAwB;IAElD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QAC7B,IAAI,MAA0B,CAAC;QAC/B,IAAI,CAAC;YACJ,MAAM,GAAG,MAAM,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,2BAA2B,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;YAC7B,IAAI;YACJ,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,qBAAqB;YACnC,eAAe;YACf,eAAe,EAAE,GAAG,EAAE;gBACrB,IAAI,OAAO,IAAI,OAAO,IAAI,SAAS;oBAAE,OAAO,IAAI,CAAC;gBACjD,IAAI,EAAE,KAAK,IAAI,eAAe;oBAAE,OAAO,KAAK,CAAC;gBAC7C,SAAS,GAAG,IAAI,CAAC;gBACjB,OAAO,IAAI,CAAC;YACb,CAAC;YACD,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;gBACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC;gBACrE,gGAAgG;gBAChG,IAAI,QAAQ,IAAI,OAAO;oBAAE,OAAO;gBAChC,IAAI,QAAQ;oBAAE,OAAO,GAAG,IAAI,CAAC;gBAC7B,cAAc,CAAC,GAAG,EAAE;oBACnB,IAAI,OAAO;wBAAE,OAAO;oBACpB,qFAAqF;oBACrF,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,EAAE,CAAC;wBACxE,MAAM,CAAC,OAAO,CAAC;4BACd,IAAI,EAAE,SAAS;4BACf,OAAO,EAAE,8CAA8C,eAAe,EAAE;yBACxE,CAAC,CAAC;wBACH,OAAO;oBACR,CAAC;oBACD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACzB,CAAC,CAAC,CAAC;YACJ,CAAC;SACD,CAAC,CAAC;QACH,OAAO;YACN,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;gBACjB,IAAI,CAAC,OAAO;oBAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACxC,CAAC;YACD,IAAI,EAAE,KAAK,IAAI,EAAE;gBAChB,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO,CAAC,OAAO,EAAE,CAAC;YACnB,CAAC;SACD,CAAC;IACH,CAAC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,qBAAsB,SAAQ,mBAAmB;IAC7D,YAAY,OAAqC;QAChD,KAAK,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IACvE,CAAC;CACD","sourcesContent":["/**\n * Runs the VM on the calling thread, for runtimes without worker threads (Cloudflare Workers and\n * Durable Objects, browsers) and for the server side of `RemoteCodemodeSandbox`.\n *\n * Problem: QuickJS runs synchronously. While a script computes, the host's event loop does not\n * run, so neither the deadline timer nor an abort signal can fire. On Workers even `Date.now()` is\n * frozen during synchronous execution, so the VM cannot check a clock either. Example: with a\n * worker thread, `while (true) {}` is stopped by the host's 200 ms timer; on the calling thread\n * that timer never gets a chance to run.\n *\n * Solution: an interrupt budget. QuickJS polls its interrupt handler periodically while a script\n * runs; this launcher counts the polls across the whole execution and interrupts the script once\n * the count passes the budget. The execution then fails with `kind: \"timeout\"`. The deadline and\n * abort signal still work whenever the script waits on a tool, since the event loop runs then.\n *\n * The rate of polls depends on the code and the machine. Measured with a tight loop: about 50,000\n * per second of CPU on an Apple M-series laptop and about 10,000 per second on Cloudflare Durable\n * Objects (October 2026). The default of 100,000 allows roughly 2 to 10 seconds of uninterrupted\n * computation; keep it well below the platform's own CPU limit, because hitting that limit resets\n * a Durable Object instead of failing one script.\n *\n * Once the VM reports `done` or `crash`, the result is decided, so the VM is interrupted at the\n * next poll instead of running on until the host stops it. Example: a script that keeps printing in\n * a `for (;;)` loop after its output limit was reached would otherwise run until the budget ran out\n * and be reported as a timeout.\n *\n * Stack: the VM runs on the caller's stack, which is smaller than a worker thread's (about 1 MB\n * on Node's main thread), so QuickJS's stack guard is set to {@link INLINE_MAX_STACK_SIZE}.\n *\n * Reentrancy: VM callbacks run inside QuickJS. Messages from the VM are delivered to the host in\n * a microtask, after the VM has returned, so host code (tool calls, `finish()`, disposal) never\n * runs inside a VM callback and never re-enters the VM.\n */\nimport type { CodemodeSandboxBaseOptions } from \"../types.ts\";\nimport type { CodemodeWasmModule } from \"../wasm.ts\";\nimport { CodemodeSandboxBase, errorMessage, type VmLauncher } from \"./execution.ts\";\nimport { startVm } from \"./vm.ts\";\n\nexport const DEFAULT_INTERRUPT_BUDGET = 100_000;\n\n/** Allows recursion about 1,000 calls deep and trips well before a 1 MB host stack overflows. */\nexport const INLINE_MAX_STACK_SIZE = 256 * 1024;\n\nexport interface InlineCodemodeSandboxOptions extends CodemodeSandboxBaseOptions {\n\t/**\n\t * Compiled `quickjs-wasi/quickjs.wasm`. On Cloudflare, import it with Wrangler's `CompiledWasm`\n\t * rule (`@ny4n/codemode/cloudflare` does this); on Node, `loadQuickJSWasm()`.\n\t */\n\twasm: CodemodeWasmModule | Promise<CodemodeWasmModule>;\n\t/**\n\t * Interrupt polls an execution may use before it fails with `kind: \"timeout\"`. The only limit on\n\t * a script that computes without awaiting a tool. Default: {@link DEFAULT_INTERRUPT_BUDGET}.\n\t */\n\tinterruptBudget?: number;\n}\n\nexport function inlineLauncher(\n\twasm: CodemodeWasmModule | Promise<CodemodeWasmModule>,\n\tinterruptBudget: number = DEFAULT_INTERRUPT_BUDGET,\n): VmLauncher {\n\treturn async (data, events) => {\n\t\tlet module: CodemodeWasmModule;\n\t\ttry {\n\t\t\tmodule = await wasm;\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Failed to load QuickJS: ${errorMessage(error)}`);\n\t\t}\n\t\tlet stopped = false;\n\t\tlet settled = false;\n\t\tlet polls = 0;\n\t\tlet exhausted = false;\n\t\tconst session = await startVm({\n\t\t\tdata,\n\t\t\twasm: module,\n\t\t\tmaxStackSize: INLINE_MAX_STACK_SIZE,\n\t\t\tinterruptBudget,\n\t\t\tshouldInterrupt: () => {\n\t\t\t\tif (stopped || settled || exhausted) return true;\n\t\t\t\tif (++polls <= interruptBudget) return false;\n\t\t\t\texhausted = true;\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tpost: (message) => {\n\t\t\t\tconst terminal = message.type === \"done\" || message.type === \"crash\";\n\t\t\t\t// Only the first terminal message counts: a crash caused by interrupting a settled VM is noise.\n\t\t\t\tif (terminal && settled) return;\n\t\t\t\tif (terminal) settled = true;\n\t\t\t\tqueueMicrotask(() => {\n\t\t\t\t\tif (stopped) return;\n\t\t\t\t\t// The interrupt surfaces as a crash or a failed done; report what caused it instead.\n\t\t\t\t\tif (exhausted && (message.type === \"done\" || message.type === \"crash\")) {\n\t\t\t\t\t\tevents.failure({\n\t\t\t\t\t\t\tkind: \"timeout\",\n\t\t\t\t\t\t\tmessage: `Execution exceeded its interrupt budget of ${interruptBudget}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tevents.message(message);\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t\treturn {\n\t\t\tpost: (message) => {\n\t\t\t\tif (!stopped) session.deliver(message);\n\t\t\t},\n\t\t\tstop: async () => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tstopped = true;\n\t\t\t\tsession.dispose();\n\t\t\t},\n\t\t};\n\t};\n}\n\n/**\n * Runs each execution's QuickJS VM on the calling thread. Works in any runtime with WebAssembly\n * and microtasks; needs no worker threads, files, or Node built-ins. A script that computes\n * blocks the caller's thread until it awaits a tool or uses up its interrupt budget, so prefer\n * `CodemodeSandbox` where worker threads exist, and `RemoteCodemodeSandbox` to keep scripts off a\n * thread that must stay responsive (such as a Durable Object that serves a session).\n */\nexport class InlineCodemodeSandbox extends CodemodeSandboxBase {\n\tconstructor(options: InlineCodemodeSandboxOptions) {\n\t\tsuper(options, inlineLauncher(options.wasm, options.interruptBudget));\n\t}\n}\n"]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Output helpers: `text(value)` appends a text item (non-strings are
|
|
11
11
|
* JSON-stringified), `image(urlOrItem)` appends an image from a base64 `data:` URL, an
|
|
12
|
-
* `{ image_url }` object, or an
|
|
12
|
+
* `{ image_url }` object, or an image block `{ type: "image", data, mimeType }`, and `exit()` ends the script
|
|
13
13
|
* successfully. `console.*` appends text items marked as console output.
|
|
14
14
|
*
|
|
15
15
|
* `store(key, value)` and `load(key)` are synchronous: they work on a snapshot of
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* with a successful "done".
|
|
18
18
|
*
|
|
19
19
|
* Evaluates to a function `(bridge, toolsJson, globalsJson, storeJson) => { settle, run, stalled }`.
|
|
20
|
+
* `settle(id, ok, payload, propertiesJson)` settles call `id`; a rejection's `Error` gets the own properties of the
|
|
21
|
+
* `propertiesJson` object, except `name`, `message`, and `stack`.
|
|
20
22
|
* `toolsJson` lists `{ name, jsName, description }`: `tools[jsName]` and `tools[name]` call the
|
|
21
23
|
* tool, and `ALL_TOOLS` lists `{ name: jsName, description }`.
|
|
22
24
|
* `stalled()` reports a script that has not finished while no host call is pending: with no timers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prelude-source.d.ts","sourceRoot":"","sources":["../../src/runtime/prelude-source.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"prelude-source.d.ts","sourceRoot":"","sources":["../../src/runtime/prelude-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAChD,eAAO,MAAM,qBAAqB,QAAc,CAAC;AACjD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAmB,CAAC;AACjD,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAKxC,eAAO,MAAM,cAAc,EAAE,MA0d1B,CAAC"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Output helpers: `text(value)` appends a text item (non-strings are
|
|
11
11
|
* JSON-stringified), `image(urlOrItem)` appends an image from a base64 `data:` URL, an
|
|
12
|
-
* `{ image_url }` object, or an
|
|
12
|
+
* `{ image_url }` object, or an image block `{ type: "image", data, mimeType }`, and `exit()` ends the script
|
|
13
13
|
* successfully. `console.*` appends text items marked as console output.
|
|
14
14
|
*
|
|
15
15
|
* `store(key, value)` and `load(key)` are synchronous: they work on a snapshot of
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* with a successful "done".
|
|
18
18
|
*
|
|
19
19
|
* Evaluates to a function `(bridge, toolsJson, globalsJson, storeJson) => { settle, run, stalled }`.
|
|
20
|
+
* `settle(id, ok, payload, propertiesJson)` settles call `id`; a rejection's `Error` gets the own properties of the
|
|
21
|
+
* `propertiesJson` object, except `name`, `message`, and `stack`.
|
|
20
22
|
* `toolsJson` lists `{ name, jsName, description }`: `tools[jsName]` and `tools[name]` call the
|
|
21
23
|
* tool, and `ALL_TOOLS` lists `{ name: jsName, description }`.
|
|
22
24
|
* `stalled()` reports a script that has not finished while no host call is pending: with no timers
|
|
@@ -39,7 +41,7 @@ export const MAX_STORE_TOTAL_CHARS = 1024 * 1024;
|
|
|
39
41
|
*/
|
|
40
42
|
export const MAX_OUTPUT_CHARS = 16 * 1024 * 1024;
|
|
41
43
|
export const MAX_OUTPUT_ITEMS = 100_000;
|
|
42
|
-
const IMAGE_HELPER_EXPECTS =
|
|
44
|
+
const IMAGE_HELPER_EXPECTS = 'image expects a non-empty image URL string, an object with image_url, or an image block { type: "image", data, mimeType }';
|
|
43
45
|
export const PRELUDE_SOURCE = `(function (bridge, toolsJson, globalsJson, storeJson) {
|
|
44
46
|
"use strict";
|
|
45
47
|
|
|
@@ -379,9 +381,9 @@ export const PRELUDE_SOURCE = `(function (bridge, toolsJson, globalsJson, storeJ
|
|
|
379
381
|
}
|
|
380
382
|
if (typeof value.type !== "string") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
381
383
|
if (value.type !== "image") {
|
|
382
|
-
throw new TypeErrorCtor('image only accepts
|
|
384
|
+
throw new TypeErrorCtor('image only accepts image blocks, got "' + value.type + '"');
|
|
383
385
|
}
|
|
384
|
-
if (typeof value.data !== "string" || value.data === "") throw new TypeErrorCtor("image expected
|
|
386
|
+
if (typeof value.data !== "string" || value.data === "") throw new TypeErrorCtor("image expected image block data");
|
|
385
387
|
if (value.data.toLowerCase().startsWith("data:")) return value.data;
|
|
386
388
|
return "data:;base64," + value.data;
|
|
387
389
|
}
|
|
@@ -451,12 +453,20 @@ export const PRELUDE_SOURCE = `(function (bridge, toolsJson, globalsJson, storeJ
|
|
|
451
453
|
Object.defineProperty(globalThis, "exit", { value: exit, enumerable: true });
|
|
452
454
|
|
|
453
455
|
return {
|
|
454
|
-
settle(id, ok, payload) {
|
|
456
|
+
settle(id, ok, payload, propertiesJson) {
|
|
455
457
|
const entry = pending.get(id);
|
|
456
458
|
if (!entry) return;
|
|
457
459
|
pending.delete(id);
|
|
458
460
|
if (!ok) {
|
|
459
|
-
|
|
461
|
+
const error = new ErrorCtor(payload);
|
|
462
|
+
if (propertiesJson !== undefined) {
|
|
463
|
+
const properties = parse(propertiesJson);
|
|
464
|
+
for (const key of Object.keys(properties)) {
|
|
465
|
+
if (key === "name" || key === "message" || key === "stack") continue;
|
|
466
|
+
Object.defineProperty(error, key, { value: properties[key], writable: true, enumerable: true, configurable: true });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
entry.reject(error);
|
|
460
470
|
return;
|
|
461
471
|
}
|
|
462
472
|
let value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prelude-source.js","sourceRoot":"","sources":["../../src/runtime/prelude-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAAC;AACjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAExC,MAAM,oBAAoB,GACzB,gGAAgG,CAAC;AAElG,MAAM,CAAC,MAAM,cAAc,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAgQhB,qBAAqB;;8GAEmE,qBAAqB;;;;;eAKpH,qBAAqB;;iDAEa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAiChD,gBAAgB,qBAAqB,gBAAgB;;2CAEhC,gBAAgB,kBAAkB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA+BhE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;sEAGK,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;gEAG1C,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;4CAoBxD,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6G7E,CAAC","sourcesContent":["/**\n * JavaScript evaluated inside the QuickJS VM before the script runs.\n *\n * The VM is its own wasm instance, so nothing here guards a realm boundary.\n * The prelude keeps the host bridge in a closure so the script cannot call it\n * directly, and builds `tools`, `ALL_TOOLS`, the output helpers (`text`, `image`,\n * `exit`, `console`), and globals on top of it. Tool arguments and results cross\n * as JSON strings and are parsed on this side.\n *\n * Output helpers: `text(value)` appends a text item (non-strings are\n * JSON-stringified), `image(urlOrItem)` appends an image from a base64 `data:` URL, an\n * `{ image_url }` object, or an MCP `ImageContent` block, and `exit()` ends the script\n * successfully. `console.*` appends text items marked as console output.\n *\n * `store(key, value)` and `load(key)` are synchronous: they work on a snapshot of\n * JSON text passed in as `storeJson`, and the keys the script wrote are reported\n * with a successful \"done\".\n *\n * Evaluates to a function `(bridge, toolsJson, globalsJson, storeJson) => { settle, run, stalled }`.\n * `toolsJson` lists `{ name, jsName, description }`: `tools[jsName]` and `tools[name]` call the\n * tool, and `ALL_TOOLS` lists `{ name: jsName, description }`.\n * `stalled()` reports a script that has not finished while no host call is pending: with no timers\n * or I/O in the VM, nothing can ever resume it.\n * `globalsJson` lists `{ name, spread }`; `a.b` names are grouped into a frozen `a` object.\n * `bridge(kind, a, b, c)` with kind \"call\" or \"global\" (id, name, argsJson),\n * \"output\" (\"text\" or \"console\", text) or (\"image\", data, mimeType), or \"done\" (ok, valueJsonOrErrorJson, writesJson).\n *\n * Before anything else the prelude freezes the built-ins and makes the built-in globals read-only.\n * The prelude shares them with the script, so a script that patched one (for example\n * `Array.prototype.toJSON`) could otherwise corrupt what the prelude reports to the host.\n */\nexport const MAX_STORE_VALUE_CHARS = 256 * 1024;\nexport const MAX_STORE_TOTAL_CHARS = 1024 * 1024;\n/**\n * Output one script may produce with `text()`, `image()`, and `console.*`: characters of text and\n * base64 image data, and items. The host keeps all output until the script ends, so without a\n * limit a script that prints in a loop grows the host's memory until it crashes. The item limit\n * covers loops that print empty strings.\n */\nexport const MAX_OUTPUT_CHARS = 16 * 1024 * 1024;\nexport const MAX_OUTPUT_ITEMS = 100_000;\n\nconst IMAGE_HELPER_EXPECTS =\n\t\"image expects a non-empty image URL string, an object with image_url, or a raw MCP image block\";\n\nexport const PRELUDE_SOURCE: string = `(function (bridge, toolsJson, globalsJson, storeJson) {\n\t\"use strict\";\n\n\t// Freeze every object reachable from the built-in globals, plus the intrinsics that are only\n\t// reachable from instances (iterator and generator prototypes, %TypedArray%).\n\t//\n\t// Freezing alone breaks ordinary code through the \"override mistake\": a data property that is\n\t// read-only on a prototype cannot be assigned on an instance either, so\n\t// this.name = \"MyError\" in an Error subclass would throw. Commonly overridden properties\n\t// become accessors whose setter defines an own property on the instance instead.\n\t(function lockdown() {\n\t\tconst OVERRIDABLE = new Set([\"constructor\", \"name\", \"message\", \"toString\", \"toLocaleString\", \"valueOf\", \"toJSON\"]);\n\t\tconst seen = new Set([globalThis]);\n\t\tconst queue = [];\n\t\tconst add = (value) => {\n\t\t\tif ((typeof value === \"object\" && value !== null) || typeof value === \"function\") {\n\t\t\t\tif (!seen.has(value)) {\n\t\t\t\t\tseen.add(value);\n\t\t\t\t\tqueue.push(value);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Accessors from an object literal have no own prototype object, unlike function expressions,\n\t\t// whose prototype.constructor would be converted again without end.\n\t\tfunction allowOverride(object, key, value, enumerable) {\n\t\t\tconst { get, set } = Object.getOwnPropertyDescriptor(\n\t\t\t\t{\n\t\t\t\t\tget accessor() {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t},\n\t\t\t\t\tset accessor(next) {\n\t\t\t\t\t\tif (this === object) {\n\t\t\t\t\t\t\tthrow new TypeError(\"Cannot assign to read only property '\" + String(key) + \"' of a built-in\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((typeof this !== \"object\" || this === null) && typeof this !== \"function\") return;\n\t\t\t\t\t\tObject.defineProperty(this, key, { value: next, writable: true, enumerable: true, configurable: true });\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"accessor\",\n\t\t\t);\n\t\t\tObject.defineProperty(object, key, { get, set, enumerable, configurable: false });\n\t\t\tadd(get);\n\t\t\tadd(set);\n\t\t}\n\n\t\tfor (const key of Reflect.ownKeys(globalThis)) {\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(globalThis, key);\n\t\t\tadd(descriptor.value);\n\t\t\tadd(descriptor.get);\n\t\t\tadd(descriptor.set);\n\t\t\tif (\"value\" in descriptor && descriptor.configurable) {\n\t\t\t\tObject.defineProperty(globalThis, key, { writable: false, configurable: false });\n\t\t\t}\n\t\t}\n\t\tadd(Object.getPrototypeOf(function* () {}));\n\t\tadd(Object.getPrototypeOf(async function () {}));\n\t\tadd(Object.getPrototypeOf(async function* () {}));\n\t\tadd(Object.getPrototypeOf(Int8Array));\n\t\tadd(Object.getPrototypeOf([][Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(new Map()[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(new Set()[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(\"\"[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(/a/[Symbol.matchAll](\"\")));\n\t\tif (typeof Iterator === \"function\") {\n\t\t\tif (typeof Iterator.prototype.map === \"function\") add(Object.getPrototypeOf([].values().map((x) => x)));\n\t\t\tif (typeof Iterator.from === \"function\") add(Object.getPrototypeOf(Iterator.from({ next() {} })));\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst object = queue.pop();\n\t\t\tadd(Object.getPrototypeOf(object));\n\t\t\tconst descriptors = Object.getOwnPropertyDescriptors(object);\n\t\t\tfor (const key of Reflect.ownKeys(descriptors)) {\n\t\t\t\tconst descriptor = descriptors[key];\n\t\t\t\tif (\"value\" in descriptor) {\n\t\t\t\t\tadd(descriptor.value);\n\t\t\t\t\tif (\n\t\t\t\t\t\tdescriptor.writable &&\n\t\t\t\t\t\tdescriptor.configurable &&\n\t\t\t\t\t\t(object === Object.prototype || OVERRIDABLE.has(key))\n\t\t\t\t\t) {\n\t\t\t\t\t\tallowOverride(object, key, descriptor.value, descriptor.enumerable);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadd(descriptor.get);\n\t\t\t\t\tadd(descriptor.set);\n\t\t\t\t}\n\t\t\t}\n\t\t\tObject.freeze(object);\n\t\t}\n\t})();\n\tconst stringify = JSON.stringify;\n\tconst parse = JSON.parse;\n\tconst promiseThen = Promise.prototype.then;\n\tconst ErrorCtor = Error;\n\tconst TypeErrorCtor = TypeError;\n\tconst RangeErrorCtor = RangeError;\n\tconst pending = new Map();\n\tlet nextId = 1;\n\tlet finished = false;\n\t// Thrown by exit() to unwind the script after it already reported success.\n\tconst EXIT = Object.freeze({});\n\n\tfunction done(ok, payload, writes) {\n\t\tif (finished) return;\n\t\tfinished = true;\n\t\tbridge(\"done\", ok, payload, writes);\n\t}\n\n\tfunction serialize(value) {\n\t\treturn value === undefined ? undefined : stringify(value);\n\t}\n\n\t// QuickJS stacks list frames only. Prefix \"Name: message\" like V8 so the\n\t// text reads the same as a Node error, and drop this prelude's frames.\n\tfunction errorText(error) {\n\t\tconst head = error.message ? error.name + \": \" + error.message : String(error.name);\n\t\tconst frames =\n\t\t\ttypeof error.stack === \"string\"\n\t\t\t\t? error.stack.split(\"\\\\n\").filter((line) => line.trim() && !line.includes(\"codemode-prelude.js\"))\n\t\t\t\t: [];\n\t\treturn [head, ...frames].join(\"\\\\n\");\n\t}\n\n\tfunction format(value) {\n\t\tif (typeof value === \"string\") return value;\n\t\tif (value instanceof ErrorCtor) return errorText(value);\n\t\ttry {\n\t\t\tconst json = stringify(value);\n\t\t\treturn json === undefined ? String(value) : json;\n\t\t} catch {\n\t\t\treturn String(value);\n\t\t}\n\t}\n\n\t// The host requires string fields. A script can set an error's name or message to anything, so\n\t// they are coerced, and a value whose coercion throws gets a placeholder.\n\tfunction describeError(error) {\n\t\ttry {\n\t\t\tif (error instanceof ErrorCtor) {\n\t\t\t\treturn stringify({ name: String(error.name), message: String(error.message), stack: errorText(error) });\n\t\t\t}\n\t\t\treturn stringify({ message: String(format(error)) });\n\t\t} catch {\n\t\t\treturn stringify({ message: \"The script threw a value that cannot be described\" });\n\t\t}\n\t}\n\n\tfunction caller(kind, name, spread) {\n\t\treturn (...args) =>\n\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\tlet json;\n\t\t\t\ttry {\n\t\t\t\t\tjson = serialize(spread ? args : args[0]);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst id = nextId++;\n\t\t\t\tpending.set(id, { resolve, reject });\n\t\t\t\tbridge(kind, id, name, json);\n\t\t\t});\n\t}\n\n\tconst tools = Object.create(null);\n\tconst allTools = [];\n\tfor (const { name, jsName, description } of parse(toolsJson)) {\n\t\tconst fn = caller(\"call\", name);\n\t\t// The first tool wins when two names normalize to the same identifier.\n\t\tif (!(jsName in tools)) {\n\t\t\ttools[jsName] = fn;\n\t\t\tallTools.push(Object.freeze({ name: jsName, description }));\n\t\t}\n\t\tif (!(name in tools)) tools[name] = fn;\n\t}\n\tObject.freeze(tools);\n\tObject.freeze(allTools);\n\n\t// Reading a member that does not exist throws an error that names the close matches, instead of\n\t// a later \"not a function\". \\`in\\` checks still work.\n\tconst comparable = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n\tfunction guard(target, label, names, hint) {\n\t\treturn new Proxy(target, {\n\t\t\tget(object, property, receiver) {\n\t\t\t\tif (typeof property !== \"string\" || property in object || property in Object.prototype || property === \"then\" || property === \"toJSON\") {\n\t\t\t\t\treturn Reflect.get(object, property, receiver);\n\t\t\t\t}\n\t\t\t\tconst wanted = comparable(property);\n\t\t\t\tconst exact = names.filter((name) => comparable(name) === wanted);\n\t\t\t\tconst close = exact.length > 0 ? exact : names.filter((name) => wanted && (comparable(name).includes(wanted) || wanted.includes(comparable(name))));\n\t\t\t\tlet message = label + \".\" + property + \" does not exist.\";\n\t\t\t\tif (close.length > 0) message += \" Did you mean \" + close.slice(0, 5).map((name) => label + \".\" + name).join(\", \") + \"?\";\n\t\t\t\telse if (names.length <= 20) message += \" Available: \" + names.join(\", \") + \".\";\n\t\t\t\tif (hint) message += \" \" + hint;\n\t\t\t\tmessage += ' Check for a member with \"' + property + '\" in ' + label + \".\";\n\t\t\t\tthrow new TypeErrorCtor(message);\n\t\t\t},\n\t\t});\n\t}\n\tconst toolsProxy = guard(\n\t\ttools,\n\t\t\"tools\",\n\t\tallTools.map((tool) => tool.name),\n\t\t\"ALL_TOOLS lists every tool; searchTools(query) finds tools by topic.\",\n\t);\n\n\tconst namespaces = new Map();\n\tfor (const { name, spread } of parse(globalsJson)) {\n\t\tconst fn = caller(\"global\", name, spread);\n\t\tconst dot = name.indexOf(\".\");\n\t\tif (dot === -1) {\n\t\t\tObject.defineProperty(globalThis, name, { value: fn, enumerable: true });\n\t\t\tcontinue;\n\t\t}\n\t\tconst namespace = name.slice(0, dot);\n\t\tif (!namespaces.has(namespace)) namespaces.set(namespace, Object.create(null));\n\t\tnamespaces.get(namespace)[name.slice(dot + 1)] = fn;\n\t}\n\tfor (const [namespace, members] of namespaces) {\n\t\tObject.freeze(members);\n\t\tconst value = guard(members, namespace, Object.keys(members));\n\t\tObject.defineProperty(globalThis, namespace, { value, enumerable: true });\n\t}\n\n\t// key -> JSON text. Sizes count key and JSON characters.\n\tconst stored = new Map(Object.entries(parse(storeJson)));\n\tconst writes = new Map();\n\tlet storedChars = 0;\n\tfor (const [key, json] of stored) storedChars += key.length + json.length;\n\n\tconst STORE_HINT =\n\t\t\"store() is for small state such as IDs or summaries. Show images with image(), keep large data in variables, or write it to a file with a tool.\";\n\n\tfunction checkKey(name, key) {\n\t\tif (typeof key !== \"string\") throw new TypeError(name + \"() key must be a string\");\n\t}\n\n\tfunction store(key, value) {\n\t\tcheckKey(\"store\", key);\n\t\tconst previous = stored.has(key) ? key.length + stored.get(key).length : 0;\n\t\tif (value === undefined) {\n\t\t\tstored.delete(key);\n\t\t\tstoredChars -= previous;\n\t\t\twrites.set(key, undefined);\n\t\t\treturn;\n\t\t}\n\t\tlet json;\n\t\ttry {\n\t\t\tjson = stringify(value);\n\t\t} catch (error) {\n\t\t\tthrow new TypeError(\"store(\" + stringify(key) + \") value is not JSON-serializable: \" + format(error));\n\t\t}\n\t\tif (json === undefined) {\n\t\t\tthrow new TypeError(\"store(\" + stringify(key) + \") value is not JSON-serializable\");\n\t\t}\n\t\tif (json.length > ${MAX_STORE_VALUE_CHARS}) {\n\t\t\tthrow new RangeError(\n\t\t\t\t\"store(\" + stringify(key) + \") value has \" + json.length + \" characters of JSON, more than the limit of ${MAX_STORE_VALUE_CHARS}. \" +\n\t\t\t\t\tSTORE_HINT,\n\t\t\t);\n\t\t}\n\t\tconst next = storedChars - previous + key.length + json.length;\n\t\tif (next > ${MAX_STORE_TOTAL_CHARS}) {\n\t\t\tthrow new RangeError(\n\t\t\t\t\"store is full: stored values would exceed ${MAX_STORE_TOTAL_CHARS} characters of JSON. Delete keys with store(key, undefined). \" +\n\t\t\t\t\tSTORE_HINT,\n\t\t\t);\n\t\t}\n\t\tstored.set(key, json);\n\t\tstoredChars = next;\n\t\twrites.set(key, json);\n\t}\n\n\tfunction load(key) {\n\t\tcheckKey(\"load\", key);\n\t\tconst json = stored.get(key);\n\t\treturn json === undefined ? undefined : parse(json);\n\t}\n\n\tfunction serializeWrites() {\n\t\tconst entries = [];\n\t\tfor (const [key, json] of writes) entries.push(json === undefined ? [key] : [key, json]);\n\t\treturn stringify(entries);\n\t}\n\n\tObject.defineProperty(globalThis, \"store\", { value: store, enumerable: true });\n\tObject.defineProperty(globalThis, \"load\", { value: load, enumerable: true });\n\n\tlet outputChars = 0;\n\tlet outputItems = 0;\n\n\t// Past the output limits the script fails: done() reports the error, so catching it does not\n\t// resume output, and the host ends the script.\n\tfunction output(kind, data, mimeType) {\n\t\tif (finished) return;\n\t\toutputChars += data.length;\n\t\toutputItems++;\n\t\tif (outputChars > ${MAX_OUTPUT_CHARS} || outputItems > ${MAX_OUTPUT_ITEMS}) {\n\t\t\tconst error = new RangeErrorCtor(\n\t\t\t\t\"script output exceeded the limit of ${MAX_OUTPUT_CHARS} characters or ${MAX_OUTPUT_ITEMS} text(), image(), and console calls. \" +\n\t\t\t\t\t\"Print a summary instead, or write large data to a file with a tool.\",\n\t\t\t);\n\t\t\tdone(false, describeError(error));\n\t\t\tthrow error;\n\t\t}\n\t\tbridge(\"output\", kind, data, mimeType);\n\t}\n\n\t// Primitives become their string form, everything else JSON.\n\tfunction outputText(value) {\n\t\tif (value === undefined || value === null || typeof value !== \"object\" && typeof value !== \"function\") {\n\t\t\treturn String(value);\n\t\t}\n\t\tconst json = stringify(value);\n\t\treturn json === undefined ? String(value) : json;\n\t}\n\n\tfunction text(value) {\n\t\tlet rendered;\n\t\ttry {\n\t\t\trendered = outputText(value);\n\t\t} catch (error) {\n\t\t\tthrow new TypeErrorCtor(error instanceof ErrorCtor ? error.message : String(error));\n\t\t}\n\t\toutput(\"text\", rendered);\n\t}\n\n\tfunction imageUrl(value) {\n\t\tif (typeof value === \"string\") return value;\n\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\t\tthrow new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\t}\n\t\tif (value.image_url !== undefined) {\n\t\t\tif (typeof value.image_url !== \"string\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\t\treturn value.image_url;\n\t\t}\n\t\tif (typeof value.type !== \"string\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\tif (value.type !== \"image\") {\n\t\t\tthrow new TypeErrorCtor('image only accepts MCP image blocks, got \"' + value.type + '\"');\n\t\t}\n\t\tif (typeof value.data !== \"string\" || value.data === \"\") throw new TypeErrorCtor(\"image expected MCP image data\");\n\t\tif (value.data.toLowerCase().startsWith(\"data:\")) return value.data;\n\t\treturn \"data:;base64,\" + value.data;\n\t}\n\n\t// Base64 of the signatures of the formats providers accept inline (PNG, JPEG except\n\t// JPEG-LS, GIF, \"RIFF....WEBP\"). Signatures start at byte 0, so their encodings are prefixes.\n\tconst IMAGE_SIGNATURES = [\n\t\t[\"image/png\", /^iVBORw0KGg/],\n\t\t[\"image/jpeg\", /^[/]9j[/](?!9)/],\n\t\t[\"image/gif\", /^R0lGOD[dl]h/],\n\t\t[\"image/webp\", /^UklG.{8}RUJQ/],\n\t];\n\n\tfunction image(value) {\n\t\tconst url = imageUrl(value);\n\t\tif (url === \"\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\tconst colon = url.indexOf(\":\");\n\t\tconst scheme = colon === -1 ? \"\" : url.slice(0, colon).toLowerCase();\n\t\tif (scheme === \"http\" || scheme === \"https\") {\n\t\t\tthrow new TypeErrorCtor(\"remote image URLs are not supported in tool outputs. Pass a base64 data URI instead\");\n\t\t}\n\t\tconst comma = url.indexOf(\",\");\n\t\tconst header = comma === -1 ? [] : url.slice(colon + 1, comma).split(\";\");\n\t\tif (scheme !== \"data\" || comma === -1 || header.slice(1).every((part) => part.toLowerCase() !== \"base64\")) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. Pass a base64 data URI instead\");\n\t\t}\n\t\t// Providers reject the whole request on a bad image, and a persisted image block would be\n\t\t// resent on every later turn. Line breaks from wrapped base64 are dropped. The declared type\n\t\t// is ignored in favor of the detected one, as providers also reject mismatches.\n\t\tconst data = url.slice(comma + 1).replace(/\\\\s+/g, \"\");\n\t\tif (data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data)) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. The image data is not valid base64 (truncated or corrupted?)\");\n\t\t}\n\t\tconst head = data.slice(0, 16);\n\t\tconst signature = IMAGE_SIGNATURES.find(([, pattern]) => pattern.test(head));\n\t\tif (!signature) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. The image data is not a PNG, JPEG, GIF, or WebP image\");\n\t\t}\n\t\toutput(\"image\", data, signature[0]);\n\t}\n\n\tfunction exit() {\n\t\tlet writesJson;\n\t\ttry {\n\t\t\twritesJson = serializeWrites();\n\t\t} catch (error) {\n\t\t\tdone(false, describeError(error));\n\t\t\tthrow EXIT;\n\t\t}\n\t\tdone(true, undefined, writesJson);\n\t\tthrow EXIT;\n\t}\n\n\tconst console = {};\n\tfor (const level of [\"log\", \"info\", \"warn\", \"error\", \"debug\"]) {\n\t\tconsole[level] = (...args) => {\n\t\t\toutput(\"console\", args.map(format).join(\" \"));\n\t\t};\n\t}\n\tObject.freeze(console);\n\n\tObject.defineProperty(globalThis, \"tools\", { value: toolsProxy, enumerable: true });\n\tObject.defineProperty(globalThis, \"ALL_TOOLS\", { value: allTools, enumerable: true });\n\tObject.defineProperty(globalThis, \"console\", { value: console, enumerable: true });\n\tObject.defineProperty(globalThis, \"text\", { value: text, enumerable: true });\n\tObject.defineProperty(globalThis, \"image\", { value: image, enumerable: true });\n\tObject.defineProperty(globalThis, \"exit\", { value: exit, enumerable: true });\n\n\treturn {\n\t\tsettle(id, ok, payload) {\n\t\t\tconst entry = pending.get(id);\n\t\t\tif (!entry) return;\n\t\t\tpending.delete(id);\n\t\t\tif (!ok) {\n\t\t\t\tentry.reject(new ErrorCtor(payload));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet value;\n\t\t\ttry {\n\t\t\t\tvalue = payload === undefined ? undefined : parse(payload);\n\t\t\t} catch (error) {\n\t\t\t\tentry.reject(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tentry.resolve(value);\n\t\t},\n\t\trun(fn) {\n\t\t\tlet promise;\n\t\t\ttry {\n\t\t\t\tpromise = fn(toolsProxy, console);\n\t\t\t} catch (error) {\n\t\t\t\tdone(false, describeError(error));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpromiseThen.call(\n\t\t\t\tpromise,\n\t\t\t\t(value) => {\n\t\t\t\t\tlet json;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tjson = serialize(value);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tdone(false, describeError(error));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tdone(true, json, serializeWrites());\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\tdone(false, describeError(error));\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tstalled() {\n\t\t\tif (finished || pending.size > 0) return false;\n\t\t\tdone(\n\t\t\t\tfalse,\n\t\t\t\tstringify({\n\t\t\t\t\tname: \"Error\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The script is waiting on a promise that can never settle: no tool call is pending, and timers do not exist here.\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn true;\n\t\t},\n\t};\n})`;\n"]}
|
|
1
|
+
{"version":3,"file":"prelude-source.js","sourceRoot":"","sources":["../../src/runtime/prelude-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAAC;AACjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAExC,MAAM,oBAAoB,GACzB,2HAA2H,CAAC;AAE7H,MAAM,CAAC,MAAM,cAAc,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAgQhB,qBAAqB;;8GAEmE,qBAAqB;;;;;eAKpH,qBAAqB;;iDAEa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAiChD,gBAAgB,qBAAqB,gBAAgB;;2CAEhC,gBAAgB,kBAAkB,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA+BhE,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;sEAGK,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;gEAG1C,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;4CAoBxD,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqH7E,CAAC","sourcesContent":["/**\n * JavaScript evaluated inside the QuickJS VM before the script runs.\n *\n * The VM is its own wasm instance, so nothing here guards a realm boundary.\n * The prelude keeps the host bridge in a closure so the script cannot call it\n * directly, and builds `tools`, `ALL_TOOLS`, the output helpers (`text`, `image`,\n * `exit`, `console`), and globals on top of it. Tool arguments and results cross\n * as JSON strings and are parsed on this side.\n *\n * Output helpers: `text(value)` appends a text item (non-strings are\n * JSON-stringified), `image(urlOrItem)` appends an image from a base64 `data:` URL, an\n * `{ image_url }` object, or an image block `{ type: \"image\", data, mimeType }`, and `exit()` ends the script\n * successfully. `console.*` appends text items marked as console output.\n *\n * `store(key, value)` and `load(key)` are synchronous: they work on a snapshot of\n * JSON text passed in as `storeJson`, and the keys the script wrote are reported\n * with a successful \"done\".\n *\n * Evaluates to a function `(bridge, toolsJson, globalsJson, storeJson) => { settle, run, stalled }`.\n * `settle(id, ok, payload, propertiesJson)` settles call `id`; a rejection's `Error` gets the own properties of the\n * `propertiesJson` object, except `name`, `message`, and `stack`.\n * `toolsJson` lists `{ name, jsName, description }`: `tools[jsName]` and `tools[name]` call the\n * tool, and `ALL_TOOLS` lists `{ name: jsName, description }`.\n * `stalled()` reports a script that has not finished while no host call is pending: with no timers\n * or I/O in the VM, nothing can ever resume it.\n * `globalsJson` lists `{ name, spread }`; `a.b` names are grouped into a frozen `a` object.\n * `bridge(kind, a, b, c)` with kind \"call\" or \"global\" (id, name, argsJson),\n * \"output\" (\"text\" or \"console\", text) or (\"image\", data, mimeType), or \"done\" (ok, valueJsonOrErrorJson, writesJson).\n *\n * Before anything else the prelude freezes the built-ins and makes the built-in globals read-only.\n * The prelude shares them with the script, so a script that patched one (for example\n * `Array.prototype.toJSON`) could otherwise corrupt what the prelude reports to the host.\n */\nexport const MAX_STORE_VALUE_CHARS = 256 * 1024;\nexport const MAX_STORE_TOTAL_CHARS = 1024 * 1024;\n/**\n * Output one script may produce with `text()`, `image()`, and `console.*`: characters of text and\n * base64 image data, and items. The host keeps all output until the script ends, so without a\n * limit a script that prints in a loop grows the host's memory until it crashes. The item limit\n * covers loops that print empty strings.\n */\nexport const MAX_OUTPUT_CHARS = 16 * 1024 * 1024;\nexport const MAX_OUTPUT_ITEMS = 100_000;\n\nconst IMAGE_HELPER_EXPECTS =\n\t'image expects a non-empty image URL string, an object with image_url, or an image block { type: \"image\", data, mimeType }';\n\nexport const PRELUDE_SOURCE: string = `(function (bridge, toolsJson, globalsJson, storeJson) {\n\t\"use strict\";\n\n\t// Freeze every object reachable from the built-in globals, plus the intrinsics that are only\n\t// reachable from instances (iterator and generator prototypes, %TypedArray%).\n\t//\n\t// Freezing alone breaks ordinary code through the \"override mistake\": a data property that is\n\t// read-only on a prototype cannot be assigned on an instance either, so\n\t// this.name = \"MyError\" in an Error subclass would throw. Commonly overridden properties\n\t// become accessors whose setter defines an own property on the instance instead.\n\t(function lockdown() {\n\t\tconst OVERRIDABLE = new Set([\"constructor\", \"name\", \"message\", \"toString\", \"toLocaleString\", \"valueOf\", \"toJSON\"]);\n\t\tconst seen = new Set([globalThis]);\n\t\tconst queue = [];\n\t\tconst add = (value) => {\n\t\t\tif ((typeof value === \"object\" && value !== null) || typeof value === \"function\") {\n\t\t\t\tif (!seen.has(value)) {\n\t\t\t\t\tseen.add(value);\n\t\t\t\t\tqueue.push(value);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Accessors from an object literal have no own prototype object, unlike function expressions,\n\t\t// whose prototype.constructor would be converted again without end.\n\t\tfunction allowOverride(object, key, value, enumerable) {\n\t\t\tconst { get, set } = Object.getOwnPropertyDescriptor(\n\t\t\t\t{\n\t\t\t\t\tget accessor() {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t},\n\t\t\t\t\tset accessor(next) {\n\t\t\t\t\t\tif (this === object) {\n\t\t\t\t\t\t\tthrow new TypeError(\"Cannot assign to read only property '\" + String(key) + \"' of a built-in\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ((typeof this !== \"object\" || this === null) && typeof this !== \"function\") return;\n\t\t\t\t\t\tObject.defineProperty(this, key, { value: next, writable: true, enumerable: true, configurable: true });\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t\"accessor\",\n\t\t\t);\n\t\t\tObject.defineProperty(object, key, { get, set, enumerable, configurable: false });\n\t\t\tadd(get);\n\t\t\tadd(set);\n\t\t}\n\n\t\tfor (const key of Reflect.ownKeys(globalThis)) {\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(globalThis, key);\n\t\t\tadd(descriptor.value);\n\t\t\tadd(descriptor.get);\n\t\t\tadd(descriptor.set);\n\t\t\tif (\"value\" in descriptor && descriptor.configurable) {\n\t\t\t\tObject.defineProperty(globalThis, key, { writable: false, configurable: false });\n\t\t\t}\n\t\t}\n\t\tadd(Object.getPrototypeOf(function* () {}));\n\t\tadd(Object.getPrototypeOf(async function () {}));\n\t\tadd(Object.getPrototypeOf(async function* () {}));\n\t\tadd(Object.getPrototypeOf(Int8Array));\n\t\tadd(Object.getPrototypeOf([][Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(new Map()[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(new Set()[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(\"\"[Symbol.iterator]()));\n\t\tadd(Object.getPrototypeOf(/a/[Symbol.matchAll](\"\")));\n\t\tif (typeof Iterator === \"function\") {\n\t\t\tif (typeof Iterator.prototype.map === \"function\") add(Object.getPrototypeOf([].values().map((x) => x)));\n\t\t\tif (typeof Iterator.from === \"function\") add(Object.getPrototypeOf(Iterator.from({ next() {} })));\n\t\t}\n\n\t\twhile (queue.length > 0) {\n\t\t\tconst object = queue.pop();\n\t\t\tadd(Object.getPrototypeOf(object));\n\t\t\tconst descriptors = Object.getOwnPropertyDescriptors(object);\n\t\t\tfor (const key of Reflect.ownKeys(descriptors)) {\n\t\t\t\tconst descriptor = descriptors[key];\n\t\t\t\tif (\"value\" in descriptor) {\n\t\t\t\t\tadd(descriptor.value);\n\t\t\t\t\tif (\n\t\t\t\t\t\tdescriptor.writable &&\n\t\t\t\t\t\tdescriptor.configurable &&\n\t\t\t\t\t\t(object === Object.prototype || OVERRIDABLE.has(key))\n\t\t\t\t\t) {\n\t\t\t\t\t\tallowOverride(object, key, descriptor.value, descriptor.enumerable);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadd(descriptor.get);\n\t\t\t\t\tadd(descriptor.set);\n\t\t\t\t}\n\t\t\t}\n\t\t\tObject.freeze(object);\n\t\t}\n\t})();\n\tconst stringify = JSON.stringify;\n\tconst parse = JSON.parse;\n\tconst promiseThen = Promise.prototype.then;\n\tconst ErrorCtor = Error;\n\tconst TypeErrorCtor = TypeError;\n\tconst RangeErrorCtor = RangeError;\n\tconst pending = new Map();\n\tlet nextId = 1;\n\tlet finished = false;\n\t// Thrown by exit() to unwind the script after it already reported success.\n\tconst EXIT = Object.freeze({});\n\n\tfunction done(ok, payload, writes) {\n\t\tif (finished) return;\n\t\tfinished = true;\n\t\tbridge(\"done\", ok, payload, writes);\n\t}\n\n\tfunction serialize(value) {\n\t\treturn value === undefined ? undefined : stringify(value);\n\t}\n\n\t// QuickJS stacks list frames only. Prefix \"Name: message\" like V8 so the\n\t// text reads the same as a Node error, and drop this prelude's frames.\n\tfunction errorText(error) {\n\t\tconst head = error.message ? error.name + \": \" + error.message : String(error.name);\n\t\tconst frames =\n\t\t\ttypeof error.stack === \"string\"\n\t\t\t\t? error.stack.split(\"\\\\n\").filter((line) => line.trim() && !line.includes(\"codemode-prelude.js\"))\n\t\t\t\t: [];\n\t\treturn [head, ...frames].join(\"\\\\n\");\n\t}\n\n\tfunction format(value) {\n\t\tif (typeof value === \"string\") return value;\n\t\tif (value instanceof ErrorCtor) return errorText(value);\n\t\ttry {\n\t\t\tconst json = stringify(value);\n\t\t\treturn json === undefined ? String(value) : json;\n\t\t} catch {\n\t\t\treturn String(value);\n\t\t}\n\t}\n\n\t// The host requires string fields. A script can set an error's name or message to anything, so\n\t// they are coerced, and a value whose coercion throws gets a placeholder.\n\tfunction describeError(error) {\n\t\ttry {\n\t\t\tif (error instanceof ErrorCtor) {\n\t\t\t\treturn stringify({ name: String(error.name), message: String(error.message), stack: errorText(error) });\n\t\t\t}\n\t\t\treturn stringify({ message: String(format(error)) });\n\t\t} catch {\n\t\t\treturn stringify({ message: \"The script threw a value that cannot be described\" });\n\t\t}\n\t}\n\n\tfunction caller(kind, name, spread) {\n\t\treturn (...args) =>\n\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\tlet json;\n\t\t\t\ttry {\n\t\t\t\t\tjson = serialize(spread ? args : args[0]);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treject(error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst id = nextId++;\n\t\t\t\tpending.set(id, { resolve, reject });\n\t\t\t\tbridge(kind, id, name, json);\n\t\t\t});\n\t}\n\n\tconst tools = Object.create(null);\n\tconst allTools = [];\n\tfor (const { name, jsName, description } of parse(toolsJson)) {\n\t\tconst fn = caller(\"call\", name);\n\t\t// The first tool wins when two names normalize to the same identifier.\n\t\tif (!(jsName in tools)) {\n\t\t\ttools[jsName] = fn;\n\t\t\tallTools.push(Object.freeze({ name: jsName, description }));\n\t\t}\n\t\tif (!(name in tools)) tools[name] = fn;\n\t}\n\tObject.freeze(tools);\n\tObject.freeze(allTools);\n\n\t// Reading a member that does not exist throws an error that names the close matches, instead of\n\t// a later \"not a function\". \\`in\\` checks still work.\n\tconst comparable = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n\tfunction guard(target, label, names, hint) {\n\t\treturn new Proxy(target, {\n\t\t\tget(object, property, receiver) {\n\t\t\t\tif (typeof property !== \"string\" || property in object || property in Object.prototype || property === \"then\" || property === \"toJSON\") {\n\t\t\t\t\treturn Reflect.get(object, property, receiver);\n\t\t\t\t}\n\t\t\t\tconst wanted = comparable(property);\n\t\t\t\tconst exact = names.filter((name) => comparable(name) === wanted);\n\t\t\t\tconst close = exact.length > 0 ? exact : names.filter((name) => wanted && (comparable(name).includes(wanted) || wanted.includes(comparable(name))));\n\t\t\t\tlet message = label + \".\" + property + \" does not exist.\";\n\t\t\t\tif (close.length > 0) message += \" Did you mean \" + close.slice(0, 5).map((name) => label + \".\" + name).join(\", \") + \"?\";\n\t\t\t\telse if (names.length <= 20) message += \" Available: \" + names.join(\", \") + \".\";\n\t\t\t\tif (hint) message += \" \" + hint;\n\t\t\t\tmessage += ' Check for a member with \"' + property + '\" in ' + label + \".\";\n\t\t\t\tthrow new TypeErrorCtor(message);\n\t\t\t},\n\t\t});\n\t}\n\tconst toolsProxy = guard(\n\t\ttools,\n\t\t\"tools\",\n\t\tallTools.map((tool) => tool.name),\n\t\t\"ALL_TOOLS lists every tool; searchTools(query) finds tools by topic.\",\n\t);\n\n\tconst namespaces = new Map();\n\tfor (const { name, spread } of parse(globalsJson)) {\n\t\tconst fn = caller(\"global\", name, spread);\n\t\tconst dot = name.indexOf(\".\");\n\t\tif (dot === -1) {\n\t\t\tObject.defineProperty(globalThis, name, { value: fn, enumerable: true });\n\t\t\tcontinue;\n\t\t}\n\t\tconst namespace = name.slice(0, dot);\n\t\tif (!namespaces.has(namespace)) namespaces.set(namespace, Object.create(null));\n\t\tnamespaces.get(namespace)[name.slice(dot + 1)] = fn;\n\t}\n\tfor (const [namespace, members] of namespaces) {\n\t\tObject.freeze(members);\n\t\tconst value = guard(members, namespace, Object.keys(members));\n\t\tObject.defineProperty(globalThis, namespace, { value, enumerable: true });\n\t}\n\n\t// key -> JSON text. Sizes count key and JSON characters.\n\tconst stored = new Map(Object.entries(parse(storeJson)));\n\tconst writes = new Map();\n\tlet storedChars = 0;\n\tfor (const [key, json] of stored) storedChars += key.length + json.length;\n\n\tconst STORE_HINT =\n\t\t\"store() is for small state such as IDs or summaries. Show images with image(), keep large data in variables, or write it to a file with a tool.\";\n\n\tfunction checkKey(name, key) {\n\t\tif (typeof key !== \"string\") throw new TypeError(name + \"() key must be a string\");\n\t}\n\n\tfunction store(key, value) {\n\t\tcheckKey(\"store\", key);\n\t\tconst previous = stored.has(key) ? key.length + stored.get(key).length : 0;\n\t\tif (value === undefined) {\n\t\t\tstored.delete(key);\n\t\t\tstoredChars -= previous;\n\t\t\twrites.set(key, undefined);\n\t\t\treturn;\n\t\t}\n\t\tlet json;\n\t\ttry {\n\t\t\tjson = stringify(value);\n\t\t} catch (error) {\n\t\t\tthrow new TypeError(\"store(\" + stringify(key) + \") value is not JSON-serializable: \" + format(error));\n\t\t}\n\t\tif (json === undefined) {\n\t\t\tthrow new TypeError(\"store(\" + stringify(key) + \") value is not JSON-serializable\");\n\t\t}\n\t\tif (json.length > ${MAX_STORE_VALUE_CHARS}) {\n\t\t\tthrow new RangeError(\n\t\t\t\t\"store(\" + stringify(key) + \") value has \" + json.length + \" characters of JSON, more than the limit of ${MAX_STORE_VALUE_CHARS}. \" +\n\t\t\t\t\tSTORE_HINT,\n\t\t\t);\n\t\t}\n\t\tconst next = storedChars - previous + key.length + json.length;\n\t\tif (next > ${MAX_STORE_TOTAL_CHARS}) {\n\t\t\tthrow new RangeError(\n\t\t\t\t\"store is full: stored values would exceed ${MAX_STORE_TOTAL_CHARS} characters of JSON. Delete keys with store(key, undefined). \" +\n\t\t\t\t\tSTORE_HINT,\n\t\t\t);\n\t\t}\n\t\tstored.set(key, json);\n\t\tstoredChars = next;\n\t\twrites.set(key, json);\n\t}\n\n\tfunction load(key) {\n\t\tcheckKey(\"load\", key);\n\t\tconst json = stored.get(key);\n\t\treturn json === undefined ? undefined : parse(json);\n\t}\n\n\tfunction serializeWrites() {\n\t\tconst entries = [];\n\t\tfor (const [key, json] of writes) entries.push(json === undefined ? [key] : [key, json]);\n\t\treturn stringify(entries);\n\t}\n\n\tObject.defineProperty(globalThis, \"store\", { value: store, enumerable: true });\n\tObject.defineProperty(globalThis, \"load\", { value: load, enumerable: true });\n\n\tlet outputChars = 0;\n\tlet outputItems = 0;\n\n\t// Past the output limits the script fails: done() reports the error, so catching it does not\n\t// resume output, and the host ends the script.\n\tfunction output(kind, data, mimeType) {\n\t\tif (finished) return;\n\t\toutputChars += data.length;\n\t\toutputItems++;\n\t\tif (outputChars > ${MAX_OUTPUT_CHARS} || outputItems > ${MAX_OUTPUT_ITEMS}) {\n\t\t\tconst error = new RangeErrorCtor(\n\t\t\t\t\"script output exceeded the limit of ${MAX_OUTPUT_CHARS} characters or ${MAX_OUTPUT_ITEMS} text(), image(), and console calls. \" +\n\t\t\t\t\t\"Print a summary instead, or write large data to a file with a tool.\",\n\t\t\t);\n\t\t\tdone(false, describeError(error));\n\t\t\tthrow error;\n\t\t}\n\t\tbridge(\"output\", kind, data, mimeType);\n\t}\n\n\t// Primitives become their string form, everything else JSON.\n\tfunction outputText(value) {\n\t\tif (value === undefined || value === null || typeof value !== \"object\" && typeof value !== \"function\") {\n\t\t\treturn String(value);\n\t\t}\n\t\tconst json = stringify(value);\n\t\treturn json === undefined ? String(value) : json;\n\t}\n\n\tfunction text(value) {\n\t\tlet rendered;\n\t\ttry {\n\t\t\trendered = outputText(value);\n\t\t} catch (error) {\n\t\t\tthrow new TypeErrorCtor(error instanceof ErrorCtor ? error.message : String(error));\n\t\t}\n\t\toutput(\"text\", rendered);\n\t}\n\n\tfunction imageUrl(value) {\n\t\tif (typeof value === \"string\") return value;\n\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\t\tthrow new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\t}\n\t\tif (value.image_url !== undefined) {\n\t\t\tif (typeof value.image_url !== \"string\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\t\treturn value.image_url;\n\t\t}\n\t\tif (typeof value.type !== \"string\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\tif (value.type !== \"image\") {\n\t\t\tthrow new TypeErrorCtor('image only accepts image blocks, got \"' + value.type + '\"');\n\t\t}\n\t\tif (typeof value.data !== \"string\" || value.data === \"\") throw new TypeErrorCtor(\"image expected image block data\");\n\t\tif (value.data.toLowerCase().startsWith(\"data:\")) return value.data;\n\t\treturn \"data:;base64,\" + value.data;\n\t}\n\n\t// Base64 of the signatures of the formats providers accept inline (PNG, JPEG except\n\t// JPEG-LS, GIF, \"RIFF....WEBP\"). Signatures start at byte 0, so their encodings are prefixes.\n\tconst IMAGE_SIGNATURES = [\n\t\t[\"image/png\", /^iVBORw0KGg/],\n\t\t[\"image/jpeg\", /^[/]9j[/](?!9)/],\n\t\t[\"image/gif\", /^R0lGOD[dl]h/],\n\t\t[\"image/webp\", /^UklG.{8}RUJQ/],\n\t];\n\n\tfunction image(value) {\n\t\tconst url = imageUrl(value);\n\t\tif (url === \"\") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});\n\t\tconst colon = url.indexOf(\":\");\n\t\tconst scheme = colon === -1 ? \"\" : url.slice(0, colon).toLowerCase();\n\t\tif (scheme === \"http\" || scheme === \"https\") {\n\t\t\tthrow new TypeErrorCtor(\"remote image URLs are not supported in tool outputs. Pass a base64 data URI instead\");\n\t\t}\n\t\tconst comma = url.indexOf(\",\");\n\t\tconst header = comma === -1 ? [] : url.slice(colon + 1, comma).split(\";\");\n\t\tif (scheme !== \"data\" || comma === -1 || header.slice(1).every((part) => part.toLowerCase() !== \"base64\")) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. Pass a base64 data URI instead\");\n\t\t}\n\t\t// Providers reject the whole request on a bad image, and a persisted image block would be\n\t\t// resent on every later turn. Line breaks from wrapped base64 are dropped. The declared type\n\t\t// is ignored in favor of the detected one, as providers also reject mismatches.\n\t\tconst data = url.slice(comma + 1).replace(/\\\\s+/g, \"\");\n\t\tif (data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data)) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. The image data is not valid base64 (truncated or corrupted?)\");\n\t\t}\n\t\tconst head = data.slice(0, 16);\n\t\tconst signature = IMAGE_SIGNATURES.find(([, pattern]) => pattern.test(head));\n\t\tif (!signature) {\n\t\t\tthrow new TypeErrorCtor(\"invalid image output. The image data is not a PNG, JPEG, GIF, or WebP image\");\n\t\t}\n\t\toutput(\"image\", data, signature[0]);\n\t}\n\n\tfunction exit() {\n\t\tlet writesJson;\n\t\ttry {\n\t\t\twritesJson = serializeWrites();\n\t\t} catch (error) {\n\t\t\tdone(false, describeError(error));\n\t\t\tthrow EXIT;\n\t\t}\n\t\tdone(true, undefined, writesJson);\n\t\tthrow EXIT;\n\t}\n\n\tconst console = {};\n\tfor (const level of [\"log\", \"info\", \"warn\", \"error\", \"debug\"]) {\n\t\tconsole[level] = (...args) => {\n\t\t\toutput(\"console\", args.map(format).join(\" \"));\n\t\t};\n\t}\n\tObject.freeze(console);\n\n\tObject.defineProperty(globalThis, \"tools\", { value: toolsProxy, enumerable: true });\n\tObject.defineProperty(globalThis, \"ALL_TOOLS\", { value: allTools, enumerable: true });\n\tObject.defineProperty(globalThis, \"console\", { value: console, enumerable: true });\n\tObject.defineProperty(globalThis, \"text\", { value: text, enumerable: true });\n\tObject.defineProperty(globalThis, \"image\", { value: image, enumerable: true });\n\tObject.defineProperty(globalThis, \"exit\", { value: exit, enumerable: true });\n\n\treturn {\n\t\tsettle(id, ok, payload, propertiesJson) {\n\t\t\tconst entry = pending.get(id);\n\t\t\tif (!entry) return;\n\t\t\tpending.delete(id);\n\t\t\tif (!ok) {\n\t\t\t\tconst error = new ErrorCtor(payload);\n\t\t\t\tif (propertiesJson !== undefined) {\n\t\t\t\t\tconst properties = parse(propertiesJson);\n\t\t\t\t\tfor (const key of Object.keys(properties)) {\n\t\t\t\t\t\tif (key === \"name\" || key === \"message\" || key === \"stack\") continue;\n\t\t\t\t\t\tObject.defineProperty(error, key, { value: properties[key], writable: true, enumerable: true, configurable: true });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tentry.reject(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet value;\n\t\t\ttry {\n\t\t\t\tvalue = payload === undefined ? undefined : parse(payload);\n\t\t\t} catch (error) {\n\t\t\t\tentry.reject(error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tentry.resolve(value);\n\t\t},\n\t\trun(fn) {\n\t\t\tlet promise;\n\t\t\ttry {\n\t\t\t\tpromise = fn(toolsProxy, console);\n\t\t\t} catch (error) {\n\t\t\t\tdone(false, describeError(error));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpromiseThen.call(\n\t\t\t\tpromise,\n\t\t\t\t(value) => {\n\t\t\t\t\tlet json;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tjson = serialize(value);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tdone(false, describeError(error));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tdone(true, json, serializeWrites());\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\tdone(false, describeError(error));\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tstalled() {\n\t\t\tif (finished || pending.size > 0) return false;\n\t\t\tdone(\n\t\t\t\tfalse,\n\t\t\t\tstringify({\n\t\t\t\t\tname: \"Error\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The script is waiting on a promise that can never settle: no tool call is pending, and timers do not exist here.\",\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn true;\n\t\t},\n\t};\n})`;\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodemodeOutputItem } from "../types.ts";
|
|
1
|
+
import type { CodemodeDuring, CodemodeOutputItem } from "../types.ts";
|
|
2
2
|
import type { CodemodeWasmModule } from "../wasm.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Messages between the host (main thread) and the worker. Tool arguments,
|
|
@@ -24,6 +24,15 @@ export interface VmStartData {
|
|
|
24
24
|
memoryLimitBytes: number | undefined;
|
|
25
25
|
/** Snapshot for `load()`: key to JSON text. */
|
|
26
26
|
store: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Run deterministically (`CodemodeSandboxBase.start()`): UTC, a virtual clock starting at `startedAt` (ms since
|
|
29
|
+
* the epoch) that moves only with delivered results, randomness from `seed`, no `WeakRef`, results applied in
|
|
30
|
+
* `seq` order, and `started`/`idle` messages.
|
|
31
|
+
*/
|
|
32
|
+
deterministic?: {
|
|
33
|
+
startedAt: number;
|
|
34
|
+
seed: number;
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
export interface WorkerData extends VmStartData {
|
|
29
38
|
/** Compiled `quickjs-wasi` module. Structured clone shares the compiled code with the worker. */
|
|
@@ -46,29 +55,55 @@ export type WorkerToHostMessage = {
|
|
|
46
55
|
type: "output";
|
|
47
56
|
item: CodemodeOutputItem;
|
|
48
57
|
}
|
|
49
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* `writes` is a JSON array of `[key, json]` for `store()` and `[key]` for deletions. Deterministic mode: `during`
|
|
60
|
+
* is the result the VM was processing when the script finished (0: its first run) and the calls issued by then.
|
|
61
|
+
*/
|
|
50
62
|
| {
|
|
51
63
|
type: "done";
|
|
52
64
|
ok: true;
|
|
53
65
|
value: string | undefined;
|
|
54
66
|
writes: string;
|
|
67
|
+
during?: CodemodeDuring;
|
|
55
68
|
} | {
|
|
56
69
|
type: "done";
|
|
57
70
|
ok: false;
|
|
58
71
|
error: ScriptErrorJson;
|
|
72
|
+
during?: CodemodeDuring;
|
|
59
73
|
}
|
|
60
74
|
/** The VM failed outside the script's control, for example a wasm trap. */
|
|
61
75
|
| {
|
|
62
76
|
type: "crash";
|
|
63
77
|
message: string;
|
|
78
|
+
}
|
|
79
|
+
/** Deterministic mode, before the script runs: what the VM is, so a replay can refuse a different one. */
|
|
80
|
+
| {
|
|
81
|
+
type: "started";
|
|
82
|
+
fingerprint: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Deterministic mode, after the script waits again: having processed result `seq` (0: the script's first run), it
|
|
86
|
+
* has issued `issued` calls in total.
|
|
87
|
+
*/
|
|
88
|
+
| {
|
|
89
|
+
type: "idle";
|
|
90
|
+
seq: number;
|
|
91
|
+
issued: number;
|
|
64
92
|
};
|
|
65
93
|
export type HostToWorkerMessage =
|
|
66
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* `payload` is the JSON result when `ok`, otherwise the error message. Deterministic mode: `seq` numbers the
|
|
96
|
+
* results from 1 in the order the VM applies them, and the VM's clock reads `now` while it processes this one.
|
|
97
|
+
*/
|
|
67
98
|
{
|
|
68
99
|
type: "result";
|
|
69
100
|
id: number;
|
|
70
101
|
ok: boolean;
|
|
71
102
|
payload: string | undefined;
|
|
103
|
+
seq?: number;
|
|
104
|
+
now?: number;
|
|
105
|
+
/** Deterministic mode, `ok: false`: a JSON object whose properties the script's `Error` gets. */
|
|
106
|
+
properties?: string;
|
|
72
107
|
};
|
|
73
108
|
export declare function isWorkerToHostMessage(value: unknown): value is WorkerToHostMessage;
|
|
74
109
|
export declare function isHostToWorkerMessage(value: unknown): value is HostToWorkerMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/D,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC7C,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B;;;;OAIG;IACH,aAAa,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,MAAM,WAAW,UAAW,SAAQ,WAAW;IAC9C,iGAAiG;IACjG,IAAI,EAAE,kBAAkB,CAAC;IACzB;;;OAGG;IACH,SAAS,EAAE,iBAAiB,CAAC;CAC7B;AAED,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC,MAAM,MAAM,mBAAmB,GAC5B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE;AAC9C;;;GAGG;GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAE,GAC9F;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAE;AAC9E,2EAA2E;GACzE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACpC,0GAA0G;GACxG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAC1C;;;GAGG;GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,mBAAmB;AAC9B;;;GAGG;AACH;IACC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAWlF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAElF"}
|
package/dist/runtime/protocol.js
CHANGED
|
@@ -2,7 +2,12 @@ export function isWorkerToHostMessage(value) {
|
|
|
2
2
|
if (typeof value !== "object" || value === null)
|
|
3
3
|
return false;
|
|
4
4
|
const type = value.type;
|
|
5
|
-
return type === "call" ||
|
|
5
|
+
return (type === "call" ||
|
|
6
|
+
type === "output" ||
|
|
7
|
+
type === "done" ||
|
|
8
|
+
type === "crash" ||
|
|
9
|
+
type === "started" ||
|
|
10
|
+
type === "idle");
|
|
6
11
|
}
|
|
7
12
|
export function isHostToWorkerMessage(value) {
|
|
8
13
|
return typeof value === "object" && value !== null && value.type === "result";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"AA6EA,MAAM,UAAU,qBAAqB,CAAC,KAAc;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAC;IAChD,OAAO,CACN,IAAI,KAAK,MAAM;QACf,IAAI,KAAK,QAAQ;QACjB,IAAI,KAAK,MAAM;QACf,IAAI,KAAK,OAAO;QAChB,IAAI,KAAK,SAAS;QAClB,IAAI,KAAK,MAAM,CACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAc;IACnD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAK,KAA4B,CAAC,IAAI,KAAK,QAAQ,CAAC;AACvG,CAAC","sourcesContent":["import type { CodemodeDuring, CodemodeOutputItem } from \"../types.ts\";\nimport type { CodemodeWasmModule } from \"../wasm.ts\";\n\n/**\n * Messages between the host (main thread) and the worker. Tool arguments,\n * results, and values cross as JSON strings: the worker passes them into and\n * out of the QuickJS VM as strings and never builds structured values itself.\n */\n\n/**\n * Everything a VM needs to run one script, independent of where it runs. Plain data, so it can\n * cross a `postMessage` or a Workers RPC call unchanged.\n */\nexport interface VmStartData {\n\tcode: string;\n\t/** `jsName` is the identifier the script uses; `description` is listed in `ALL_TOOLS`. */\n\ttools: { name: string; jsName: string; description: string }[];\n\tglobals: { name: string; spread: boolean }[];\n\tmemoryLimitBytes: number | undefined;\n\t/** Snapshot for `load()`: key to JSON text. */\n\tstore: Record<string, string>;\n\t/**\n\t * Run deterministically (`CodemodeSandboxBase.start()`): UTC, a virtual clock starting at `startedAt` (ms since\n\t * the epoch) that moves only with delivered results, randomness from `seed`, no `WeakRef`, results applied in\n\t * `seq` order, and `started`/`idle` messages.\n\t */\n\tdeterministic?: { startedAt: number; seed: number };\n}\n\nexport interface WorkerData extends VmStartData {\n\t/** Compiled `quickjs-wasi` module. Structured clone shares the compiled code with the worker. */\n\twasm: CodemodeWasmModule;\n\t/**\n\t * One Int32 the host sets to non-zero before terminating the worker. The VM's interrupt handler\n\t * polls it, because Bun's `worker.terminate()` cannot stop a thread that is spinning in wasm.\n\t */\n\tinterrupt: SharedArrayBuffer;\n}\n\n/** JSON-encoded `{ name?, message, stack? }` of an error thrown by the script. */\nexport type ScriptErrorJson = string;\n\nexport type WorkerToHostMessage =\n\t| { type: \"call\"; id: number; target: \"tool\" | \"global\"; name: string; args: string | undefined }\n\t| { type: \"output\"; item: CodemodeOutputItem }\n\t/**\n\t * `writes` is a JSON array of `[key, json]` for `store()` and `[key]` for deletions. Deterministic mode: `during`\n\t * is the result the VM was processing when the script finished (0: its first run) and the calls issued by then.\n\t */\n\t| { type: \"done\"; ok: true; value: string | undefined; writes: string; during?: CodemodeDuring }\n\t| { type: \"done\"; ok: false; error: ScriptErrorJson; during?: CodemodeDuring }\n\t/** The VM failed outside the script's control, for example a wasm trap. */\n\t| { type: \"crash\"; message: string }\n\t/** Deterministic mode, before the script runs: what the VM is, so a replay can refuse a different one. */\n\t| { type: \"started\"; fingerprint: string }\n\t/**\n\t * Deterministic mode, after the script waits again: having processed result `seq` (0: the script's first run), it\n\t * has issued `issued` calls in total.\n\t */\n\t| { type: \"idle\"; seq: number; issued: number };\n\nexport type HostToWorkerMessage =\n\t/**\n\t * `payload` is the JSON result when `ok`, otherwise the error message. Deterministic mode: `seq` numbers the\n\t * results from 1 in the order the VM applies them, and the VM's clock reads `now` while it processes this one.\n\t */\n\t{\n\t\ttype: \"result\";\n\t\tid: number;\n\t\tok: boolean;\n\t\tpayload: string | undefined;\n\t\tseq?: number;\n\t\tnow?: number;\n\t\t/** Deterministic mode, `ok: false`: a JSON object whose properties the script's `Error` gets. */\n\t\tproperties?: string;\n\t};\n\nexport function isWorkerToHostMessage(value: unknown): value is WorkerToHostMessage {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst type = (value as { type?: unknown }).type;\n\treturn (\n\t\ttype === \"call\" ||\n\t\ttype === \"output\" ||\n\t\ttype === \"done\" ||\n\t\ttype === \"crash\" ||\n\t\ttype === \"started\" ||\n\t\ttype === \"idle\"\n\t);\n}\n\nexport function isHostToWorkerMessage(value: unknown): value is HostToWorkerMessage {\n\treturn typeof value === \"object\" && value !== null && (value as { type?: unknown }).type === \"result\";\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/runtime/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAiD,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,WAAW,CAAC;IAClB,6FAA6F;IAC7F,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,4FAA4F;AAC5F,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAEpG;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,qBAAqB,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAEzG;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,4BAA6B,SAAQ,0BAA0B;IAC/E;;;OAGG;IACH,MAAM,EAAE,MAAM,cAAc,CAAC;IAC7B,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/runtime/remote.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAiD,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EACN,KAAK,mBAAmB,EAExB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,WAAW,CAAC;IAClB,6FAA6F;IAC7F,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,4FAA4F;AAC5F,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAEpG;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,qBAAqB,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAEzG;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,CAAC,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,4BAA6B,SAAQ,0BAA0B;IAC/E;;;OAGG;IACH,MAAM,EAAE,MAAM,cAAc,CAAC;IAC7B,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAyFf;AAqED;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,mBAAmB;IAC7D,YAAY,OAAO,EAAE,4BAA4B,EAEhD;CACD"}
|
package/dist/runtime/remote.js
CHANGED
|
@@ -69,7 +69,12 @@ export function serveCodemodeRemote(request, exchange, options) {
|
|
|
69
69
|
}
|
|
70
70
|
break;
|
|
71
71
|
}
|
|
72
|
+
case "started":
|
|
73
|
+
buffered.push(typed);
|
|
74
|
+
break;
|
|
75
|
+
// Sent at once: a host replaying a script waits for it to see that the script issued no further call.
|
|
72
76
|
case "call":
|
|
77
|
+
case "idle":
|
|
73
78
|
exchange([...takeBuffer(), typed]).then(reply, () => end(undefined));
|
|
74
79
|
break;
|
|
75
80
|
default:
|