@earendil-works/pi-codemode 1.0.4 → 1.2.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.
Files changed (55) hide show
  1. package/README.md +177 -11
  2. package/dist/cloudflare.d.ts +53 -0
  3. package/dist/cloudflare.d.ts.map +1 -0
  4. package/dist/cloudflare.js +64 -0
  5. package/dist/cloudflare.js.map +1 -0
  6. package/dist/declarations.d.ts +6 -21
  7. package/dist/declarations.d.ts.map +1 -1
  8. package/dist/declarations.js +113 -127
  9. package/dist/declarations.js.map +1 -1
  10. package/dist/index.d.ts +5 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/portable.d.ts +15 -0
  15. package/dist/portable.d.ts.map +1 -0
  16. package/dist/portable.js +12 -0
  17. package/dist/portable.js.map +1 -0
  18. package/dist/runtime/execution.d.ts +66 -0
  19. package/dist/runtime/execution.d.ts.map +1 -0
  20. package/dist/runtime/execution.js +494 -0
  21. package/dist/runtime/execution.js.map +1 -0
  22. package/dist/runtime/host.d.ts +8 -30
  23. package/dist/runtime/host.d.ts.map +1 -1
  24. package/dist/runtime/host.js +39 -335
  25. package/dist/runtime/host.js.map +1 -1
  26. package/dist/runtime/inline.d.ts +63 -0
  27. package/dist/runtime/inline.d.ts.map +1 -0
  28. package/dist/runtime/inline.js +80 -0
  29. package/dist/runtime/inline.js.map +1 -0
  30. package/dist/runtime/prelude-source.d.ts +5 -3
  31. package/dist/runtime/prelude-source.d.ts.map +1 -1
  32. package/dist/runtime/prelude-source.js +19 -9
  33. package/dist/runtime/prelude-source.js.map +1 -1
  34. package/dist/runtime/protocol.d.ts +47 -6
  35. package/dist/runtime/protocol.d.ts.map +1 -1
  36. package/dist/runtime/protocol.js +6 -1
  37. package/dist/runtime/protocol.js.map +1 -1
  38. package/dist/runtime/remote.d.ts +102 -0
  39. package/dist/runtime/remote.d.ts.map +1 -0
  40. package/dist/runtime/remote.js +175 -0
  41. package/dist/runtime/remote.js.map +1 -0
  42. package/dist/runtime/vm.d.ts +24 -0
  43. package/dist/runtime/vm.d.ts.map +1 -0
  44. package/dist/runtime/vm.js +293 -0
  45. package/dist/runtime/vm.js.map +1 -0
  46. package/dist/runtime/worker.js +11 -110
  47. package/dist/runtime/worker.js.map +1 -1
  48. package/dist/source.d.ts +2 -1
  49. package/dist/source.d.ts.map +1 -1
  50. package/dist/source.js +8 -2
  51. package/dist/source.js.map +1 -1
  52. package/dist/types.d.ts +101 -6
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js.map +1 -1
  55. package/package.json +15 -3
@@ -11,127 +11,28 @@
11
11
  * or embedded-module string specifier as `workerUrl`.
12
12
  */
13
13
  import { parentPort, workerData } from "node:worker_threads";
14
- import { JSException, MAX_STACK_SIZE, QuickJS } from "quickjs-wasi";
15
- import { PRELUDE_SOURCE } from "./prelude-source.js";
16
14
  import { isHostToWorkerMessage } from "./protocol.js";
15
+ import { startVm } from "./vm.js";
17
16
  function post(message) {
18
17
  parentPort?.postMessage(message);
19
18
  }
20
- function crash(error) {
21
- post({ type: "crash", message: error instanceof Error ? `${error.name}: ${error.message}` : String(error) });
22
- }
23
- /**
24
- * QuickJS writes engine diagnostics to fd 1 and 2, which the default shim
25
- * forwards to the host's stdout and stderr. That output belongs to the host
26
- * application (for example a TUI), so it is discarded. Reporting every byte as
27
- * written keeps libc from retrying.
28
- */
29
- function discardOutput(memory) {
30
- return {
31
- fd_write(_fd, iovsPtr, iovsLen, nwrittenPtr) {
32
- const view = new DataView(memory.buffer);
33
- let written = 0;
34
- for (let i = 0; i < iovsLen; i++) {
35
- written += view.getUint32(iovsPtr + i * 8 + 4, true);
36
- }
37
- view.setUint32(nwrittenPtr, written, true);
38
- return 0;
39
- },
40
- };
41
- }
42
- function describeException(error) {
43
- const head = error.message ? `${error.name}: ${error.message}` : error.name;
44
- const stack = error.stack?.trimEnd();
45
- return JSON.stringify({ name: error.name, message: error.message, stack: stack ? `${head}\n${stack}` : head });
46
- }
47
19
  async function main(data) {
48
20
  const interrupt = new Int32Array(data.interrupt);
49
- const vm = await QuickJS.create({
21
+ // The VM lives until the host terminates the worker, so the session is never disposed.
22
+ const session = await startVm({
23
+ data,
50
24
  wasm: data.wasm,
51
- memoryLimit: data.memoryLimitBytes,
52
- // Without a guard, deep recursion overflows the wasm stack and traps instead of throwing a
53
- // catchable RangeError.
54
- maxStackSize: MAX_STACK_SIZE,
55
- interruptHandler: () => Atomics.load(interrupt, 0) !== 0,
56
- wasi: discardOutput,
57
- });
58
- // Called from the prelude with primitives only.
59
- const bridge = vm.newFunction("bridge", (kind, a, b, c) => {
60
- switch (kind.toString()) {
61
- case "call":
62
- case "global":
63
- post({
64
- type: "call",
65
- id: a.toNumber(),
66
- target: kind.toString() === "call" ? "tool" : "global",
67
- name: b.toString(),
68
- args: c === undefined || c.isUndefined ? undefined : c.toString(),
69
- });
70
- break;
71
- case "output":
72
- post({
73
- type: "output",
74
- item: a.toString() === "image"
75
- ? { type: "image", data: b.toString(), mimeType: c.toString() }
76
- : { type: "text", text: b.toString() },
77
- });
78
- break;
79
- case "done":
80
- if (a.toBoolean()) {
81
- post({
82
- type: "done",
83
- ok: true,
84
- value: b === undefined || b.isUndefined ? undefined : b.toString(),
85
- writes: c.toString(),
86
- });
87
- }
88
- else {
89
- post({ type: "done", ok: false, error: b.toString() });
90
- }
91
- break;
92
- }
93
- return vm.undefined;
25
+ post,
26
+ shouldInterrupt: () => Atomics.load(interrupt, 0) !== 0,
94
27
  });
95
- // The VM lives until the host terminates the worker, so these handles are never disposed.
96
- const api = vm.withScope((scope) => scope.escape(vm.callFunction(vm.evalCode(PRELUDE_SOURCE, "codemode-prelude.js"), vm.undefined, bridge, vm.newString(JSON.stringify(data.tools)), vm.newString(JSON.stringify(data.globals)), vm.newString(JSON.stringify(data.store)))));
97
- const settle = api.getProp("settle");
98
- const run = api.getProp("run");
99
- const stalled = api.getProp("stalled");
100
- /** Run queued jobs, then fail a script that waits on nothing that can ever resume it. */
101
- const drain = () => {
102
- vm.executePendingJobs();
103
- vm.callFunction(stalled, api).dispose();
104
- };
105
28
  parentPort?.on("message", (message) => {
106
- if (!isHostToWorkerMessage(message))
107
- return;
108
- try {
109
- vm.withScope(() => {
110
- vm.callFunction(settle, api, vm.newNumber(message.id), message.ok ? vm.true : vm.false, message.payload === undefined ? vm.undefined : vm.newString(message.payload));
111
- });
112
- drain();
113
- }
114
- catch (error) {
115
- crash(error);
116
- }
29
+ if (isHostToWorkerMessage(message))
30
+ session.deliver(message);
117
31
  });
118
- // The prefix shares the first line with the script so reported line numbers
119
- // match the script as written.
120
- let fn;
121
- try {
122
- fn = vm.evalCode(`(async (tools, console) => {${data.code}\n})`, "codemode.js");
123
- }
124
- catch (error) {
125
- if (!(error instanceof JSException))
126
- throw error;
127
- post({ type: "done", ok: false, error: describeException(error) });
128
- return;
129
- }
130
- vm.callFunction(run, api, fn).dispose();
131
- fn.dispose();
132
- drain();
133
32
  }
134
33
  if (parentPort) {
135
- main(workerData).catch(crash);
34
+ main(workerData).catch((error) => {
35
+ post({ type: "crash", message: error instanceof Error ? `${error.name}: ${error.message}` : String(error) });
36
+ });
136
37
  }
137
38
  //# sourceMappingURL=worker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/runtime/worker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Worker thread entry. One worker runs one script inside a fresh QuickJS VM\n * (a separate wasm instance), relays tool calls and output to the host, and\n * reports the result. The host terminates the worker when the script settles,\n * times out, or is aborted; the worker exists so that a spinning script never\n * blocks the host thread.\n *\n * Importing this module starts the worker. Hosts that bundle their code (for\n * example a Bun compiled executable) add a file that imports\n * `@earendil-works/pi-codemode/worker` as a separate entrypoint and pass its URL\n * or embedded-module string specifier as `workerUrl`.\n */\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { JSException, type JSValueHandle, MAX_STACK_SIZE, QuickJS } from \"quickjs-wasi\";\nimport { PRELUDE_SOURCE } from \"./prelude-source.ts\";\nimport { isHostToWorkerMessage, type WorkerData, type WorkerToHostMessage } from \"./protocol.ts\";\n\nfunction post(message: WorkerToHostMessage): void {\n\tparentPort?.postMessage(message);\n}\n\nfunction crash(error: unknown): void {\n\tpost({ type: \"crash\", message: error instanceof Error ? `${error.name}: ${error.message}` : String(error) });\n}\n\n/**\n * QuickJS writes engine diagnostics to fd 1 and 2, which the default shim\n * forwards to the host's stdout and stderr. That output belongs to the host\n * application (for example a TUI), so it is discarded. Reporting every byte as\n * written keeps libc from retrying.\n */\nfunction discardOutput(memory: { readonly buffer: ArrayBufferLike }) {\n\treturn {\n\t\tfd_write(_fd: number, iovsPtr: number, iovsLen: number, nwrittenPtr: number): number {\n\t\t\tconst view = new DataView(memory.buffer);\n\t\t\tlet written = 0;\n\t\t\tfor (let i = 0; i < iovsLen; i++) {\n\t\t\t\twritten += view.getUint32(iovsPtr + i * 8 + 4, true);\n\t\t\t}\n\t\t\tview.setUint32(nwrittenPtr, written, true);\n\t\t\treturn 0;\n\t\t},\n\t};\n}\n\nfunction describeException(error: JSException): string {\n\tconst head = error.message ? `${error.name}: ${error.message}` : error.name;\n\tconst stack = error.stack?.trimEnd();\n\treturn JSON.stringify({ name: error.name, message: error.message, stack: stack ? `${head}\\n${stack}` : head });\n}\n\nasync function main(data: WorkerData): Promise<void> {\n\tconst interrupt = new Int32Array(data.interrupt);\n\tconst vm = await QuickJS.create({\n\t\twasm: data.wasm,\n\t\tmemoryLimit: data.memoryLimitBytes,\n\t\t// Without a guard, deep recursion overflows the wasm stack and traps instead of throwing a\n\t\t// catchable RangeError.\n\t\tmaxStackSize: MAX_STACK_SIZE,\n\t\tinterruptHandler: () => Atomics.load(interrupt, 0) !== 0,\n\t\twasi: discardOutput,\n\t});\n\n\t// Called from the prelude with primitives only.\n\tconst bridge = vm.newFunction(\"bridge\", (kind, a, b, c) => {\n\t\tswitch (kind.toString()) {\n\t\t\tcase \"call\":\n\t\t\tcase \"global\":\n\t\t\t\tpost({\n\t\t\t\t\ttype: \"call\",\n\t\t\t\t\tid: a.toNumber(),\n\t\t\t\t\ttarget: kind.toString() === \"call\" ? \"tool\" : \"global\",\n\t\t\t\t\tname: b.toString(),\n\t\t\t\t\targs: c === undefined || c.isUndefined ? undefined : c.toString(),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"output\":\n\t\t\t\tpost({\n\t\t\t\t\ttype: \"output\",\n\t\t\t\t\titem:\n\t\t\t\t\t\ta.toString() === \"image\"\n\t\t\t\t\t\t\t? { type: \"image\", data: b.toString(), mimeType: c.toString() }\n\t\t\t\t\t\t\t: { type: \"text\", text: b.toString() },\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase \"done\":\n\t\t\t\tif (a.toBoolean()) {\n\t\t\t\t\tpost({\n\t\t\t\t\t\ttype: \"done\",\n\t\t\t\t\t\tok: true,\n\t\t\t\t\t\tvalue: b === undefined || b.isUndefined ? undefined : b.toString(),\n\t\t\t\t\t\twrites: c.toString(),\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tpost({ type: \"done\", ok: false, error: b.toString() });\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t\treturn vm.undefined;\n\t});\n\n\t// The VM lives until the host terminates the worker, so these handles are never disposed.\n\tconst api = vm.withScope((scope) =>\n\t\tscope.escape(\n\t\t\tvm.callFunction(\n\t\t\t\tvm.evalCode(PRELUDE_SOURCE, \"codemode-prelude.js\"),\n\t\t\t\tvm.undefined,\n\t\t\t\tbridge,\n\t\t\t\tvm.newString(JSON.stringify(data.tools)),\n\t\t\t\tvm.newString(JSON.stringify(data.globals)),\n\t\t\t\tvm.newString(JSON.stringify(data.store)),\n\t\t\t),\n\t\t),\n\t);\n\tconst settle = api.getProp(\"settle\");\n\tconst run = api.getProp(\"run\");\n\tconst stalled = api.getProp(\"stalled\");\n\t/** Run queued jobs, then fail a script that waits on nothing that can ever resume it. */\n\tconst drain = () => {\n\t\tvm.executePendingJobs();\n\t\tvm.callFunction(stalled, api).dispose();\n\t};\n\n\tparentPort?.on(\"message\", (message: unknown) => {\n\t\tif (!isHostToWorkerMessage(message)) return;\n\t\ttry {\n\t\t\tvm.withScope(() => {\n\t\t\t\tvm.callFunction(\n\t\t\t\t\tsettle,\n\t\t\t\t\tapi,\n\t\t\t\t\tvm.newNumber(message.id),\n\t\t\t\t\tmessage.ok ? vm.true : vm.false,\n\t\t\t\t\tmessage.payload === undefined ? vm.undefined : vm.newString(message.payload),\n\t\t\t\t);\n\t\t\t});\n\t\t\tdrain();\n\t\t} catch (error) {\n\t\t\tcrash(error);\n\t\t}\n\t});\n\n\t// The prefix shares the first line with the script so reported line numbers\n\t// match the script as written.\n\tlet fn: JSValueHandle;\n\ttry {\n\t\tfn = vm.evalCode(`(async (tools, console) => {${data.code}\\n})`, \"codemode.js\");\n\t} catch (error) {\n\t\tif (!(error instanceof JSException)) throw error;\n\t\tpost({ type: \"done\", ok: false, error: describeException(error) });\n\t\treturn;\n\t}\n\tvm.callFunction(run, api, fn).dispose();\n\tfn.dispose();\n\tdrain();\n}\n\nif (parentPort) {\n\tmain(workerData as WorkerData).catch(crash);\n}\n"]}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/runtime/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAA6C,MAAM,eAAe,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,SAAS,IAAI,CAAC,OAA4B;IACzC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,IAAgB;IACnC,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,uFAAuF;IACvF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;QAC7B,IAAI;QACJ,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI;QACJ,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC;KACvD,CAAC,CAAC;IACH,UAAU,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE;QAC9C,IAAI,qBAAqB,CAAC,OAAO,CAAC;YAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,UAAU,EAAE,CAAC;IAChB,IAAI,CAAC,UAAwB,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACvD,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Worker thread entry. One worker runs one script inside a fresh QuickJS VM\n * (a separate wasm instance), relays tool calls and output to the host, and\n * reports the result. The host terminates the worker when the script settles,\n * times out, or is aborted; the worker exists so that a spinning script never\n * blocks the host thread.\n *\n * Importing this module starts the worker. Hosts that bundle their code (for\n * example a Bun compiled executable) add a file that imports\n * `@earendil-works/pi-codemode/worker` as a separate entrypoint and pass its URL\n * or embedded-module string specifier as `workerUrl`.\n */\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport { isHostToWorkerMessage, type WorkerData, type WorkerToHostMessage } from \"./protocol.ts\";\nimport { startVm } from \"./vm.ts\";\n\nfunction post(message: WorkerToHostMessage): void {\n\tparentPort?.postMessage(message);\n}\n\nasync function main(data: WorkerData): Promise<void> {\n\tconst interrupt = new Int32Array(data.interrupt);\n\t// The VM lives until the host terminates the worker, so the session is never disposed.\n\tconst session = await startVm({\n\t\tdata,\n\t\twasm: data.wasm,\n\t\tpost,\n\t\tshouldInterrupt: () => Atomics.load(interrupt, 0) !== 0,\n\t});\n\tparentPort?.on(\"message\", (message: unknown) => {\n\t\tif (isHostToWorkerMessage(message)) session.deliver(message);\n\t});\n}\n\nif (parentPort) {\n\tmain(workerData as WorkerData).catch((error: unknown) => {\n\t\tpost({ type: \"crash\", message: error instanceof Error ? `${error.name}: ${error.message}` : String(error) });\n\t});\n}\n"]}
package/dist/source.d.ts CHANGED
@@ -29,7 +29,8 @@ export declare class CodemodeSourceError extends Error {
29
29
  }
30
30
  /**
31
31
  * Split an optional first-line `// @options: {...}` from the script. Throws
32
- * {@link CodemodeSourceError} for empty input and invalid options.
32
+ * {@link CodemodeSourceError} for empty input, invalid options, and an options line that is not the
33
+ * first line, which would otherwise be ignored without notice.
33
34
  */
34
35
  export declare function parseCodemodeSource(input: string): ParsedCodemodeSource;
35
36
  //# sourceMappingURL=source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAOtD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAQnC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACrC,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,qBAAqB,CAAC;CAC/B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AA8CD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAevE"}
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAOtD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,QAQnC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACrC,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACpC,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,qBAAqB,CAAC;CAC/B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACD;AA8CD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAwBvE"}
package/dist/source.js CHANGED
@@ -72,14 +72,20 @@ function parseOptions(directive) {
72
72
  }
73
73
  /**
74
74
  * Split an optional first-line `// @options: {...}` from the script. Throws
75
- * {@link CodemodeSourceError} for empty input and invalid options.
75
+ * {@link CodemodeSourceError} for empty input, invalid options, and an options line that is not the
76
+ * first line, which would otherwise be ignored without notice.
76
77
  */
77
78
  export function parseCodemodeSource(input) {
78
79
  if (input.trim() === "") {
79
80
  throw new CodemodeSourceError('Expected JavaScript source text (non-empty). Provide JS only, optionally with a first line `// @options: {"max_output_tokens": 1000}`.');
80
81
  }
82
+ const lines = input.split("\n");
83
+ const misplaced = lines.findIndex((line, index) => index > 0 && line.trimStart().startsWith(CODEMODE_OPTIONS_PREFIX));
84
+ if (misplaced !== -1) {
85
+ throw new CodemodeSourceError(`The ${CODEMODE_OPTIONS_PREFIX} line must be the first line of the input; found one on line ${misplaced + 1}`);
86
+ }
81
87
  const newline = input.indexOf("\n");
82
- const firstLine = (newline === -1 ? input : input.slice(0, newline)).replace(/\r$/, "");
88
+ const firstLine = lines[0].replace(/\r$/, "");
83
89
  const trimmed = firstLine.trimStart();
84
90
  if (!trimmed.startsWith(CODEMODE_OPTIONS_PREFIX))
85
91
  return { code: input, options: {} };
@@ -1 +1 @@
1
- {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Codemode source format: JavaScript, optionally preceded by one options line.\n *\n * ```js\n * // @options: {\"max_output_tokens\": 2000, \"timeout_ms\": 30000}\n * const text = await tools.read({ path: \"package.json\" });\n * text(JSON.parse(text).name);\n * ```\n */\n\nexport const CODEMODE_OPTIONS_PREFIX = \"// @options:\";\n\nconst SUPPORTED_FIELDS = [\"max_output_tokens\", \"timeout_ms\"] as const;\nconst SUPPORTED_FIELDS_TEXT = \"`max_output_tokens` and `timeout_ms`\";\n/** Largest delay `setTimeout` supports, which bounds `timeout_ms`. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Lark grammar for providers with grammar-constrained tool input. It only fixes the shape of the\n * options line; the options JSON and the code are checked by {@link parseCodemodeSource}.\n */\nexport const CODEMODE_SOURCE_GRAMMAR = String.raw`\nstart: options_source | plain_source\noptions_source: OPTIONS_LINE NEWLINE SOURCE\nplain_source: SOURCE\n\nOPTIONS_LINE: /[ \\t]*\\/\\/ @options:[^\\r\\n]*/\nNEWLINE: /\\r?\\n/\nSOURCE: /[\\s\\S]+/\n`;\n\nexport interface CodemodeSourceOptions {\n\t/** Token budget for the script's output. */\n\tmaxOutputTokens?: number;\n\t/** Hard deadline for the whole script in milliseconds, including tool calls. */\n\ttimeoutMs?: number;\n}\n\nexport interface ParsedCodemodeSource {\n\t/** The script with the options line replaced by an empty line, so line numbers are unchanged. */\n\tcode: string;\n\toptions: CodemodeSourceOptions;\n}\n\nexport class CodemodeSourceError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CodemodeSourceError\";\n\t}\n}\n\nfunction isSafeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction parseOptions(directive: string): CodemodeSourceOptions {\n\tif (directive === \"\") {\n\t\tthrow new CodemodeSourceError(`@options must be a JSON object with supported fields ${SUPPORTED_FIELDS_TEXT}`);\n\t}\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(directive);\n\t} catch (error) {\n\t\tthrow new CodemodeSourceError(\n\t\t\t`@options must be valid JSON with supported fields ${SUPPORTED_FIELDS_TEXT}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new CodemodeSourceError(`@options must be a JSON object with supported fields ${SUPPORTED_FIELDS_TEXT}`);\n\t}\n\tconst fields = value as Record<string, unknown>;\n\tfor (const key of Object.keys(fields)) {\n\t\tif (!(SUPPORTED_FIELDS as readonly string[]).includes(key)) {\n\t\t\tthrow new CodemodeSourceError(`@options only supports ${SUPPORTED_FIELDS_TEXT}; got \\`${key}\\``);\n\t\t}\n\t}\n\tconst options: CodemodeSourceOptions = {};\n\tconst { max_output_tokens, timeout_ms } = fields;\n\tif (max_output_tokens !== undefined) {\n\t\tif (!isSafeInteger(max_output_tokens)) {\n\t\t\tthrow new CodemodeSourceError(\"@options field `max_output_tokens` must be a non-negative safe integer\");\n\t\t}\n\t\toptions.maxOutputTokens = max_output_tokens;\n\t}\n\tif (timeout_ms !== undefined) {\n\t\tif (!isSafeInteger(timeout_ms) || timeout_ms === 0 || timeout_ms > MAX_TIMEOUT_MS) {\n\t\t\tthrow new CodemodeSourceError(\n\t\t\t\t`@options field \\`timeout_ms\\` must be a positive integer up to ${MAX_TIMEOUT_MS}`,\n\t\t\t);\n\t\t}\n\t\toptions.timeoutMs = timeout_ms;\n\t}\n\treturn options;\n}\n\n/**\n * Split an optional first-line `// @options: {...}` from the script. Throws\n * {@link CodemodeSourceError} for empty input and invalid options.\n */\nexport function parseCodemodeSource(input: string): ParsedCodemodeSource {\n\tif (input.trim() === \"\") {\n\t\tthrow new CodemodeSourceError(\n\t\t\t'Expected JavaScript source text (non-empty). Provide JS only, optionally with a first line `// @options: {\"max_output_tokens\": 1000}`.',\n\t\t);\n\t}\n\tconst newline = input.indexOf(\"\\n\");\n\tconst firstLine = (newline === -1 ? input : input.slice(0, newline)).replace(/\\r$/, \"\");\n\tconst trimmed = firstLine.trimStart();\n\tif (!trimmed.startsWith(CODEMODE_OPTIONS_PREFIX)) return { code: input, options: {} };\n\tconst code = newline === -1 ? \"\" : input.slice(newline);\n\tif (code.trim() === \"\") {\n\t\tthrow new CodemodeSourceError(\"The @options line must be followed by JavaScript source on subsequent lines\");\n\t}\n\treturn { code, options: parseOptions(trimmed.slice(CODEMODE_OPTIONS_PREFIX.length).trim()) };\n}\n"]}
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Codemode source format: JavaScript, optionally preceded by one options line.\n *\n * ```js\n * // @options: {\"max_output_tokens\": 2000, \"timeout_ms\": 30000}\n * const text = await tools.read({ path: \"package.json\" });\n * text(JSON.parse(text).name);\n * ```\n */\n\nexport const CODEMODE_OPTIONS_PREFIX = \"// @options:\";\n\nconst SUPPORTED_FIELDS = [\"max_output_tokens\", \"timeout_ms\"] as const;\nconst SUPPORTED_FIELDS_TEXT = \"`max_output_tokens` and `timeout_ms`\";\n/** Largest delay `setTimeout` supports, which bounds `timeout_ms`. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Lark grammar for providers with grammar-constrained tool input. It only fixes the shape of the\n * options line; the options JSON and the code are checked by {@link parseCodemodeSource}.\n */\nexport const CODEMODE_SOURCE_GRAMMAR = String.raw`\nstart: options_source | plain_source\noptions_source: OPTIONS_LINE NEWLINE SOURCE\nplain_source: SOURCE\n\nOPTIONS_LINE: /[ \\t]*\\/\\/ @options:[^\\r\\n]*/\nNEWLINE: /\\r?\\n/\nSOURCE: /[\\s\\S]+/\n`;\n\nexport interface CodemodeSourceOptions {\n\t/** Token budget for the script's output. */\n\tmaxOutputTokens?: number;\n\t/** Hard deadline for the whole script in milliseconds, including tool calls. */\n\ttimeoutMs?: number;\n}\n\nexport interface ParsedCodemodeSource {\n\t/** The script with the options line replaced by an empty line, so line numbers are unchanged. */\n\tcode: string;\n\toptions: CodemodeSourceOptions;\n}\n\nexport class CodemodeSourceError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CodemodeSourceError\";\n\t}\n}\n\nfunction isSafeInteger(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction parseOptions(directive: string): CodemodeSourceOptions {\n\tif (directive === \"\") {\n\t\tthrow new CodemodeSourceError(`@options must be a JSON object with supported fields ${SUPPORTED_FIELDS_TEXT}`);\n\t}\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(directive);\n\t} catch (error) {\n\t\tthrow new CodemodeSourceError(\n\t\t\t`@options must be valid JSON with supported fields ${SUPPORTED_FIELDS_TEXT}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new CodemodeSourceError(`@options must be a JSON object with supported fields ${SUPPORTED_FIELDS_TEXT}`);\n\t}\n\tconst fields = value as Record<string, unknown>;\n\tfor (const key of Object.keys(fields)) {\n\t\tif (!(SUPPORTED_FIELDS as readonly string[]).includes(key)) {\n\t\t\tthrow new CodemodeSourceError(`@options only supports ${SUPPORTED_FIELDS_TEXT}; got \\`${key}\\``);\n\t\t}\n\t}\n\tconst options: CodemodeSourceOptions = {};\n\tconst { max_output_tokens, timeout_ms } = fields;\n\tif (max_output_tokens !== undefined) {\n\t\tif (!isSafeInteger(max_output_tokens)) {\n\t\t\tthrow new CodemodeSourceError(\"@options field `max_output_tokens` must be a non-negative safe integer\");\n\t\t}\n\t\toptions.maxOutputTokens = max_output_tokens;\n\t}\n\tif (timeout_ms !== undefined) {\n\t\tif (!isSafeInteger(timeout_ms) || timeout_ms === 0 || timeout_ms > MAX_TIMEOUT_MS) {\n\t\t\tthrow new CodemodeSourceError(\n\t\t\t\t`@options field \\`timeout_ms\\` must be a positive integer up to ${MAX_TIMEOUT_MS}`,\n\t\t\t);\n\t\t}\n\t\toptions.timeoutMs = timeout_ms;\n\t}\n\treturn options;\n}\n\n/**\n * Split an optional first-line `// @options: {...}` from the script. Throws\n * {@link CodemodeSourceError} for empty input, invalid options, and an options line that is not the\n * first line, which would otherwise be ignored without notice.\n */\nexport function parseCodemodeSource(input: string): ParsedCodemodeSource {\n\tif (input.trim() === \"\") {\n\t\tthrow new CodemodeSourceError(\n\t\t\t'Expected JavaScript source text (non-empty). Provide JS only, optionally with a first line `// @options: {\"max_output_tokens\": 1000}`.',\n\t\t);\n\t}\n\tconst lines = input.split(\"\\n\");\n\tconst misplaced = lines.findIndex(\n\t\t(line, index) => index > 0 && line.trimStart().startsWith(CODEMODE_OPTIONS_PREFIX),\n\t);\n\tif (misplaced !== -1) {\n\t\tthrow new CodemodeSourceError(\n\t\t\t`The ${CODEMODE_OPTIONS_PREFIX} line must be the first line of the input; found one on line ${misplaced + 1}`,\n\t\t);\n\t}\n\tconst newline = input.indexOf(\"\\n\");\n\tconst firstLine = lines[0].replace(/\\r$/, \"\");\n\tconst trimmed = firstLine.trimStart();\n\tif (!trimmed.startsWith(CODEMODE_OPTIONS_PREFIX)) return { code: input, options: {} };\n\tconst code = newline === -1 ? \"\" : input.slice(newline);\n\tif (code.trim() === \"\") {\n\t\tthrow new CodemodeSourceError(\"The @options line must be followed by JavaScript source on subsequent lines\");\n\t}\n\treturn { code, options: parseOptions(trimmed.slice(CODEMODE_OPTIONS_PREFIX.length).trim()) };\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -40,11 +40,12 @@ export interface CodemodeTool {
40
40
  }
41
41
  /**
42
42
  * One item of the script's output, in the order the script produced it: `text()` and `console.*`
43
- * produce text items, `image()` image items. `data` is base64.
43
+ * produce text items, with `console: true` for `console.*`, and `image()` image items. `data` is base64.
44
44
  */
45
45
  export type CodemodeOutputItem = {
46
46
  type: "text";
47
47
  text: string;
48
+ console?: true;
48
49
  } | {
49
50
  type: "image";
50
51
  data: string;
@@ -59,11 +60,17 @@ export interface CodemodeCall {
59
60
  export type CodemodeErrorKind =
60
61
  /** The script threw or failed to parse. `name` and `stack` come from the script's error. */
61
62
  "script"
62
- /** The overall deadline expired. The worker was terminated. */
63
+ /**
64
+ * The overall deadline expired, or a VM on the calling thread used up its interrupt budget. The VM
65
+ * was stopped.
66
+ */
63
67
  | "timeout"
64
- /** The caller's signal fired or the sandbox was closed. The worker was terminated. */
68
+ /** The caller's signal fired or the sandbox was closed. The VM was stopped. */
65
69
  | "aborted"
66
- /** The worker or VM failed outside the script's control (for example a wasm trap or a missing worker file). */
70
+ /**
71
+ * The VM or its transport failed outside the script's control (for example a wasm trap, a missing
72
+ * worker file, or a remote sandbox that failed).
73
+ */
67
74
  | "sandbox";
68
75
  export interface CodemodeError {
69
76
  kind: CodemodeErrorKind;
@@ -77,20 +84,27 @@ export interface CodemodeStoreWrites {
77
84
  /** Keys stored as `undefined`. */
78
85
  delete: string[];
79
86
  }
80
- /** `output` is kept for failed executions too, up to the failure. `exit()` completes with `value: undefined`. */
87
+ /**
88
+ * `output` is kept for failed executions too, up to the failure. `exit()` completes with `value: undefined`.
89
+ * `during`: in a {@link CodemodeSandboxBase.start} run that the script itself ended (it returned, threw, or exited),
90
+ * the result being processed when it did.
91
+ */
81
92
  export type CodemodeResult = {
82
93
  ok: true;
83
94
  value: unknown;
84
95
  output: CodemodeOutputItem[];
85
96
  calls: CodemodeCall[];
86
97
  storeWrites: CodemodeStoreWrites;
98
+ during?: CodemodeDuring;
87
99
  } | {
88
100
  ok: false;
89
101
  error: CodemodeError;
90
102
  output: CodemodeOutputItem[];
91
103
  calls: CodemodeCall[];
104
+ during?: CodemodeDuring;
92
105
  };
93
- export interface CodemodeSandboxOptions {
106
+ /** Options every sandbox accepts, wherever its VM runs. */
107
+ export interface CodemodeSandboxBaseOptions {
94
108
  tools?: CodemodeTool[];
95
109
  /**
96
110
  * Functions exposed as top-level identifiers instead of on `tools`, for host helpers such as
@@ -110,6 +124,9 @@ export interface CodemodeSandboxOptions {
110
124
  * `InternalError: out of memory`. Default: no limit beyond wasm32's 4 GiB address space.
111
125
  */
112
126
  memoryLimitBytes?: number;
127
+ }
128
+ /** Options of `CodemodeSandbox`, which runs each VM in a Node or Bun worker thread. */
129
+ export interface CodemodeSandboxOptions extends CodemodeSandboxBaseOptions {
113
130
  /**
114
131
  * Compiled `quickjs-wasi/quickjs.wasm`, usually from {@link loadQuickJSWasm}. Default:
115
132
  * `loadQuickJSWasm()`, the file in the installed `quickjs-wasi` package. Pass it when that file
@@ -124,6 +141,84 @@ export interface CodemodeSandboxOptions {
124
141
  */
125
142
  workerUrl?: string | URL;
126
143
  }
144
+ /** A call the script made in a {@link CodemodeSandboxBase.start} run, answered with `CodemodeRun.deliver()`. */
145
+ export interface CodemodeHostCall {
146
+ /** The VM's own number for the call, `1`, `2`, ... in the order the script made its calls (tools and globals). */
147
+ id: number;
148
+ target: "tool" | "global";
149
+ name: string;
150
+ /** What the script passed, after a JSON round trip; for spread globals the argument list. */
151
+ args: unknown;
152
+ }
153
+ /** A tool as a {@link CodemodeSandboxBase.start} run's script sees it: `tools.<name>` and its `ALL_TOOLS` entry. */
154
+ export interface CodemodeDeclaredTool {
155
+ name: string;
156
+ /**
157
+ * The identifier the script uses, `tools.<jsName>`. Default: `toCodemodeIdentifier(name)`. A host that replays a run
158
+ * passes the one the run started with, so a changed mapping cannot rename a tool under a resumed script.
159
+ */
160
+ jsName?: string;
161
+ description?: string;
162
+ }
163
+ /** A global as a {@link CodemodeSandboxBase.start} run's script sees it; the same names as `globals` allow. */
164
+ export interface CodemodeDeclaredGlobal {
165
+ name: string;
166
+ spread?: boolean;
167
+ }
168
+ /**
169
+ * A call's answer. A rejection becomes an `Error` with `message` in the script, carrying the own properties of
170
+ * `properties` (JSON values; `name`, `message`, and `stack` are ignored), for example `{ structuredOutput }`.
171
+ */
172
+ export type CodemodeOutcome = {
173
+ ok: true;
174
+ value: unknown;
175
+ } | {
176
+ ok: false;
177
+ message: string;
178
+ properties?: Record<string, unknown>;
179
+ };
180
+ /** In a {@link CodemodeSandboxBase.start} run: the result the VM was processing when the script finished, 0 for its first run, and the calls issued by then. */
181
+ export interface CodemodeDuring {
182
+ seq: number;
183
+ issued: number;
184
+ }
185
+ export interface CodemodeRunOptions {
186
+ /** The VM clock's start, in ms since the epoch: `Date.now()` until the first result arrives. */
187
+ startedAt: number;
188
+ /** Seeds `Math.random()` and the VM's other randomness. A 32-bit unsigned integer. */
189
+ seed: number;
190
+ tools?: CodemodeDeclaredTool[];
191
+ globals?: CodemodeDeclaredGlobal[];
192
+ store?: Readonly<Record<string, unknown>>;
193
+ signal?: AbortSignal;
194
+ /** Deadline of this run in ms. Default: none (not the sandbox's default). */
195
+ timeoutMs?: number;
196
+ /** The VM's fingerprint, before the script runs and before any of its calls. */
197
+ onStarted?(fingerprint: string): void;
198
+ /** The script made a call. Answer it with `CodemodeRun.deliver()`, now or later. */
199
+ onCall(call: CodemodeHostCall): void;
200
+ /**
201
+ * The VM processed result `seq` (`0`: the script's first run, before any result) and waits again, having issued
202
+ * `issued` calls in total.
203
+ */
204
+ onIdle?(seq: number, issued: number): void;
205
+ }
206
+ /** A running {@link CodemodeSandboxBase.start} execution. */
207
+ export interface CodemodeRun {
208
+ readonly result: Promise<CodemodeResult>;
209
+ /**
210
+ * Whether the run has its outcome: the script ended, timed out, or failed, or the run was aborted. Set when that is
211
+ * decided, before `result` settles (which waits for the VM to stop); calls can no longer be answered.
212
+ */
213
+ readonly finished: boolean;
214
+ /**
215
+ * Answer call `id` as result number `seq`. Results are numbered 1, 2, ... without gaps, and the VM applies them
216
+ * strictly in that order, whatever order they arrive in: after a gap it waits for the missing number. While it
217
+ * processes this one, its clock reads `now` (ms since the epoch), or the previous value if `now` is earlier.
218
+ * Throws for a call that is not waiting for an answer, a `seq` used before, or a `now` that is not finite.
219
+ */
220
+ deliver(seq: number, now: number, id: number, outcome: CodemodeOutcome): void;
221
+ }
127
222
  export interface CodemodeExecuteOptions {
128
223
  signal?: AbortSignal;
129
224
  /** Overrides the sandbox default for this execution. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,WAAW,mBAAmB;IACnC;;;OAGG;IACH,MAAM,EAAE,WAAW,CAAC;CACpB;AAED,qGAAqG;AACrG,MAAM,MAAM,kBAAkB,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6FAA6F;IAC7F,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,0FAA0F;IAC1F,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,gGAAgG;IAChG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACjF;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,OAAO,GAAG,WAAW,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,iBAAiB;AAC5B,4FAA4F;AAC1F,QAAQ;AACV,+DAA+D;GAC7D,SAAS;AACX,sFAAsF;GACpF,SAAS;AACX,+GAA+G;GAC7G,SAAS,CAAC;AAEb,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wFAAwF;AACxF,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,kCAAkC;IAClC,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,iHAAiH;AACjH,MAAM,MAAM,cAAc,GACvB;IACA,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,mBAAmB,CAAC;CAChC,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,sBAAsB;IACtC,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACxD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { CodemodeWasmModule } from \"./wasm.ts\";\n\nexport interface CodemodeToolContext {\n\t/**\n\t * Aborted when the script finishes (including unawaited calls), the\n\t * execution times out, the caller aborts, or the sandbox is closed.\n\t */\n\tsignal: AbortSignal;\n}\n\n/** A JSON Schema document. Only used to render declarations; values are not validated against it. */\nexport type CodemodeJsonSchema = { [key: string]: unknown } | boolean;\n\nexport interface CodemodeTool {\n\t/**\n\t * The script calls tools as `tools.<id>(args)`, where `<id>` is the name with characters that\n\t * are not valid in identifiers replaced by `_` (see `toCodemodeIdentifier`), and also as\n\t * `tools[\"<name>\"](args)`. Globals are called as `<name>(args)` and must be identifiers, or\n\t * `<namespace>.<member>`, which groups them into a frozen namespace object.\n\t */\n\tname: string;\n\t/** Shown as a doc comment in {@link renderDeclarations}, and listed in `ALL_TOOLS` for tools. */\n\tdescription?: string;\n\t/** Schema of the single argument. Rendered as the parameter type; `unknown` when omitted. */\n\tinputSchema?: CodemodeJsonSchema;\n\t/** Schema of the resolved value. Rendered as the promise type; `unknown` when omitted. */\n\toutputSchema?: CodemodeJsonSchema;\n\t/** Globals only: `execute` receives all call arguments as an array instead of the first one. */\n\tspread?: boolean;\n\t/**\n\t * Globals only: TypeScript parameter list and return type for {@link renderDeclarations}, for\n\t * example `(type: string, id?: string): Promise<Model[]>`. Replaces the rendering from the schemas.\n\t */\n\tsignature?: string;\n\t/**\n\t * `args` is whatever the script passed, after a JSON round trip. The return\n\t * value must be JSON-serializable; a thrown error surfaces in the script as\n\t * an `Error` with the same message.\n\t */\n\texecute(args: unknown, context: CodemodeToolContext): Promise<unknown> | unknown;\n}\n\n/**\n * One item of the script's output, in the order the script produced it: `text()` and `console.*`\n * produce text items, `image()` image items. `data` is base64.\n */\nexport type CodemodeOutputItem = { type: \"text\"; text: string } | { type: \"image\"; data: string; mimeType: string };\n\nexport type CodemodeCallStatus = \"ok\" | \"error\" | \"cancelled\";\n\nexport interface CodemodeCall {\n\tname: string;\n\tstatus: CodemodeCallStatus;\n\tdurationMs: number;\n}\n\nexport type CodemodeErrorKind =\n\t/** The script threw or failed to parse. `name` and `stack` come from the script's error. */\n\t| \"script\"\n\t/** The overall deadline expired. The worker was terminated. */\n\t| \"timeout\"\n\t/** The caller's signal fired or the sandbox was closed. The worker was terminated. */\n\t| \"aborted\"\n\t/** The worker or VM failed outside the script's control (for example a wasm trap or a missing worker file). */\n\t| \"sandbox\";\n\nexport interface CodemodeError {\n\tkind: CodemodeErrorKind;\n\tname?: string;\n\tmessage: string;\n\tstack?: string;\n}\n\n/** Keys the script changed with `store()`. Only successful executions report writes. */\nexport interface CodemodeStoreWrites {\n\tset: Record<string, unknown>;\n\t/** Keys stored as `undefined`. */\n\tdelete: string[];\n}\n\n/** `output` is kept for failed executions too, up to the failure. `exit()` completes with `value: undefined`. */\nexport type CodemodeResult =\n\t| {\n\t\t\tok: true;\n\t\t\tvalue: unknown;\n\t\t\toutput: CodemodeOutputItem[];\n\t\t\tcalls: CodemodeCall[];\n\t\t\tstoreWrites: CodemodeStoreWrites;\n\t }\n\t| { ok: false; error: CodemodeError; output: CodemodeOutputItem[]; calls: CodemodeCall[] };\n\nexport interface CodemodeSandboxOptions {\n\ttools?: CodemodeTool[];\n\t/**\n\t * Functions exposed as top-level identifiers instead of on `tools`, for host helpers such as\n\t * attaching an image to the result. They behave like tools (JSON round trip, promise result)\n\t * but are not recorded in `result.calls`. Names must be identifiers and may not shadow the\n\t * built-in globals (`tools`, `ALL_TOOLS`, `console`, `text`, `image`, `exit`, `store`, `load`).\n\t */\n\tglobals?: CodemodeTool[];\n\t/**\n\t * Overall deadline per execution, including time spent in tools. `Infinity` disables the\n\t * deadline; the execution then only ends when the script settles or is aborted.\n\t * Default: 300000.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Maximum memory the QuickJS VM may allocate. Allocations beyond it fail inside the script as\n\t * `InternalError: out of memory`. Default: no limit beyond wasm32's 4 GiB address space.\n\t */\n\tmemoryLimitBytes?: number;\n\t/**\n\t * Compiled `quickjs-wasi/quickjs.wasm`, usually from {@link loadQuickJSWasm}. Default:\n\t * `loadQuickJSWasm()`, the file in the installed `quickjs-wasi` package. Pass it when that file\n\t * is not on disk, for example in a Bun compiled executable.\n\t */\n\twasm?: CodemodeWasmModule | Promise<CodemodeWasmModule>;\n\t/**\n\t * Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: this package's own\n\t * worker file. Pass it when this package is bundled, since the default is resolved relative to\n\t * the module that creates the sandbox. Bun compiled executables require the relative string\n\t * specifier of an embedded build entrypoint; other hosts usually use a URL.\n\t */\n\tworkerUrl?: string | URL;\n}\n\nexport interface CodemodeExecuteOptions {\n\tsignal?: AbortSignal;\n\t/** Overrides the sandbox default for this execution. */\n\ttimeoutMs?: number;\n\t/**\n\t * Values the script reads with `load(key)`. Must be JSON-serializable. The script's own\n\t * `store()` calls come back as `result.storeWrites`; persisting them is up to the caller.\n\t */\n\tstore?: Readonly<Record<string, unknown>>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { CodemodeWasmModule } from \"./wasm.ts\";\n\nexport interface CodemodeToolContext {\n\t/**\n\t * Aborted when the script finishes (including unawaited calls), the\n\t * execution times out, the caller aborts, or the sandbox is closed.\n\t */\n\tsignal: AbortSignal;\n}\n\n/** A JSON Schema document. Only used to render declarations; values are not validated against it. */\nexport type CodemodeJsonSchema = { [key: string]: unknown } | boolean;\n\nexport interface CodemodeTool {\n\t/**\n\t * The script calls tools as `tools.<id>(args)`, where `<id>` is the name with characters that\n\t * are not valid in identifiers replaced by `_` (see `toCodemodeIdentifier`), and also as\n\t * `tools[\"<name>\"](args)`. Globals are called as `<name>(args)` and must be identifiers, or\n\t * `<namespace>.<member>`, which groups them into a frozen namespace object.\n\t */\n\tname: string;\n\t/** Shown as a doc comment in {@link renderDeclarations}, and listed in `ALL_TOOLS` for tools. */\n\tdescription?: string;\n\t/** Schema of the single argument. Rendered as the parameter type; `unknown` when omitted. */\n\tinputSchema?: CodemodeJsonSchema;\n\t/** Schema of the resolved value. Rendered as the promise type; `unknown` when omitted. */\n\toutputSchema?: CodemodeJsonSchema;\n\t/** Globals only: `execute` receives all call arguments as an array instead of the first one. */\n\tspread?: boolean;\n\t/**\n\t * Globals only: TypeScript parameter list and return type for {@link renderDeclarations}, for\n\t * example `(type: string, id?: string): Promise<Model[]>`. Replaces the rendering from the schemas.\n\t */\n\tsignature?: string;\n\t/**\n\t * `args` is whatever the script passed, after a JSON round trip. The return\n\t * value must be JSON-serializable; a thrown error surfaces in the script as\n\t * an `Error` with the same message.\n\t */\n\texecute(args: unknown, context: CodemodeToolContext): Promise<unknown> | unknown;\n}\n\n/**\n * One item of the script's output, in the order the script produced it: `text()` and `console.*`\n * produce text items, with `console: true` for `console.*`, and `image()` image items. `data` is base64.\n */\nexport type CodemodeOutputItem =\n\t| { type: \"text\"; text: string; console?: true }\n\t| { type: \"image\"; data: string; mimeType: string };\n\nexport type CodemodeCallStatus = \"ok\" | \"error\" | \"cancelled\";\n\nexport interface CodemodeCall {\n\tname: string;\n\tstatus: CodemodeCallStatus;\n\tdurationMs: number;\n}\n\nexport type CodemodeErrorKind =\n\t/** The script threw or failed to parse. `name` and `stack` come from the script's error. */\n\t| \"script\"\n\t/**\n\t * The overall deadline expired, or a VM on the calling thread used up its interrupt budget. The VM\n\t * was stopped.\n\t */\n\t| \"timeout\"\n\t/** The caller's signal fired or the sandbox was closed. The VM was stopped. */\n\t| \"aborted\"\n\t/**\n\t * The VM or its transport failed outside the script's control (for example a wasm trap, a missing\n\t * worker file, or a remote sandbox that failed).\n\t */\n\t| \"sandbox\";\n\nexport interface CodemodeError {\n\tkind: CodemodeErrorKind;\n\tname?: string;\n\tmessage: string;\n\tstack?: string;\n}\n\n/** Keys the script changed with `store()`. Only successful executions report writes. */\nexport interface CodemodeStoreWrites {\n\tset: Record<string, unknown>;\n\t/** Keys stored as `undefined`. */\n\tdelete: string[];\n}\n\n/**\n * `output` is kept for failed executions too, up to the failure. `exit()` completes with `value: undefined`.\n * `during`: in a {@link CodemodeSandboxBase.start} run that the script itself ended (it returned, threw, or exited),\n * the result being processed when it did.\n */\nexport type CodemodeResult =\n\t| {\n\t\t\tok: true;\n\t\t\tvalue: unknown;\n\t\t\toutput: CodemodeOutputItem[];\n\t\t\tcalls: CodemodeCall[];\n\t\t\tstoreWrites: CodemodeStoreWrites;\n\t\t\tduring?: CodemodeDuring;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\terror: CodemodeError;\n\t\t\toutput: CodemodeOutputItem[];\n\t\t\tcalls: CodemodeCall[];\n\t\t\tduring?: CodemodeDuring;\n\t };\n\n/** Options every sandbox accepts, wherever its VM runs. */\nexport interface CodemodeSandboxBaseOptions {\n\ttools?: CodemodeTool[];\n\t/**\n\t * Functions exposed as top-level identifiers instead of on `tools`, for host helpers such as\n\t * attaching an image to the result. They behave like tools (JSON round trip, promise result)\n\t * but are not recorded in `result.calls`. Names must be identifiers and may not shadow the\n\t * built-in globals (`tools`, `ALL_TOOLS`, `console`, `text`, `image`, `exit`, `store`, `load`).\n\t */\n\tglobals?: CodemodeTool[];\n\t/**\n\t * Overall deadline per execution, including time spent in tools. `Infinity` disables the\n\t * deadline; the execution then only ends when the script settles or is aborted.\n\t * Default: 300000.\n\t */\n\ttimeoutMs?: number;\n\t/**\n\t * Maximum memory the QuickJS VM may allocate. Allocations beyond it fail inside the script as\n\t * `InternalError: out of memory`. Default: no limit beyond wasm32's 4 GiB address space.\n\t */\n\tmemoryLimitBytes?: number;\n}\n\n/** Options of `CodemodeSandbox`, which runs each VM in a Node or Bun worker thread. */\nexport interface CodemodeSandboxOptions extends CodemodeSandboxBaseOptions {\n\t/**\n\t * Compiled `quickjs-wasi/quickjs.wasm`, usually from {@link loadQuickJSWasm}. Default:\n\t * `loadQuickJSWasm()`, the file in the installed `quickjs-wasi` package. Pass it when that file\n\t * is not on disk, for example in a Bun compiled executable.\n\t */\n\twasm?: CodemodeWasmModule | Promise<CodemodeWasmModule>;\n\t/**\n\t * Worker entry that imports `@earendil-works/pi-codemode/worker`. Default: this package's own\n\t * worker file. Pass it when this package is bundled, since the default is resolved relative to\n\t * the module that creates the sandbox. Bun compiled executables require the relative string\n\t * specifier of an embedded build entrypoint; other hosts usually use a URL.\n\t */\n\tworkerUrl?: string | URL;\n}\n\n/** A call the script made in a {@link CodemodeSandboxBase.start} run, answered with `CodemodeRun.deliver()`. */\nexport interface CodemodeHostCall {\n\t/** The VM's own number for the call, `1`, `2`, ... in the order the script made its calls (tools and globals). */\n\tid: number;\n\ttarget: \"tool\" | \"global\";\n\tname: string;\n\t/** What the script passed, after a JSON round trip; for spread globals the argument list. */\n\targs: unknown;\n}\n\n/** A tool as a {@link CodemodeSandboxBase.start} run's script sees it: `tools.<name>` and its `ALL_TOOLS` entry. */\nexport interface CodemodeDeclaredTool {\n\tname: string;\n\t/**\n\t * The identifier the script uses, `tools.<jsName>`. Default: `toCodemodeIdentifier(name)`. A host that replays a run\n\t * passes the one the run started with, so a changed mapping cannot rename a tool under a resumed script.\n\t */\n\tjsName?: string;\n\tdescription?: string;\n}\n\n/** A global as a {@link CodemodeSandboxBase.start} run's script sees it; the same names as `globals` allow. */\nexport interface CodemodeDeclaredGlobal {\n\tname: string;\n\tspread?: boolean;\n}\n\n/**\n * A call's answer. A rejection becomes an `Error` with `message` in the script, carrying the own properties of\n * `properties` (JSON values; `name`, `message`, and `stack` are ignored), for example `{ structuredOutput }`.\n */\nexport type CodemodeOutcome =\n\t| { ok: true; value: unknown }\n\t| { ok: false; message: string; properties?: Record<string, unknown> };\n\n/** In a {@link CodemodeSandboxBase.start} run: the result the VM was processing when the script finished, 0 for its first run, and the calls issued by then. */\nexport interface CodemodeDuring {\n\tseq: number;\n\tissued: number;\n}\n\nexport interface CodemodeRunOptions {\n\t/** The VM clock's start, in ms since the epoch: `Date.now()` until the first result arrives. */\n\tstartedAt: number;\n\t/** Seeds `Math.random()` and the VM's other randomness. A 32-bit unsigned integer. */\n\tseed: number;\n\ttools?: CodemodeDeclaredTool[];\n\tglobals?: CodemodeDeclaredGlobal[];\n\tstore?: Readonly<Record<string, unknown>>;\n\tsignal?: AbortSignal;\n\t/** Deadline of this run in ms. Default: none (not the sandbox's default). */\n\ttimeoutMs?: number;\n\t/** The VM's fingerprint, before the script runs and before any of its calls. */\n\tonStarted?(fingerprint: string): void;\n\t/** The script made a call. Answer it with `CodemodeRun.deliver()`, now or later. */\n\tonCall(call: CodemodeHostCall): void;\n\t/**\n\t * The VM processed result `seq` (`0`: the script's first run, before any result) and waits again, having issued\n\t * `issued` calls in total.\n\t */\n\tonIdle?(seq: number, issued: number): void;\n}\n\n/** A running {@link CodemodeSandboxBase.start} execution. */\nexport interface CodemodeRun {\n\treadonly result: Promise<CodemodeResult>;\n\t/**\n\t * Whether the run has its outcome: the script ended, timed out, or failed, or the run was aborted. Set when that is\n\t * decided, before `result` settles (which waits for the VM to stop); calls can no longer be answered.\n\t */\n\treadonly finished: boolean;\n\t/**\n\t * Answer call `id` as result number `seq`. Results are numbered 1, 2, ... without gaps, and the VM applies them\n\t * strictly in that order, whatever order they arrive in: after a gap it waits for the missing number. While it\n\t * processes this one, its clock reads `now` (ms since the epoch), or the previous value if `now` is earlier.\n\t * Throws for a call that is not waiting for an answer, a `seq` used before, or a `now` that is not finite.\n\t */\n\tdeliver(seq: number, now: number, id: number, outcome: CodemodeOutcome): void;\n}\n\nexport interface CodemodeExecuteOptions {\n\tsignal?: AbortSignal;\n\t/** Overrides the sandbox default for this execution. */\n\ttimeoutMs?: number;\n\t/**\n\t * Values the script reads with `load(key)`. Must be JSON-serializable. The script's own\n\t * `store()` calls come back as `result.storeWrites`; persisting them is up to the caller.\n\t */\n\tstore?: Readonly<Record<string, unknown>>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-codemode",
3
- "version": "1.0.4",
3
+ "version": "1.2.0",
4
4
  "description": "Sandboxed JavaScript execution where the only capability is calling injected tools",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,16 @@
21
21
  "types": "./dist/source.d.ts",
22
22
  "import": "./dist/source.js"
23
23
  },
24
+ "./portable": {
25
+ "source": "./src/portable.ts",
26
+ "types": "./dist/portable.d.ts",
27
+ "import": "./dist/portable.js"
28
+ },
29
+ "./cloudflare": {
30
+ "source": "./src/cloudflare.ts",
31
+ "types": "./dist/cloudflare.d.ts",
32
+ "import": "./dist/cloudflare.js"
33
+ },
24
34
  "./worker": {
25
35
  "source": "./src/runtime/worker.ts",
26
36
  "types": "./dist/runtime/worker.d.ts",
@@ -42,7 +52,9 @@
42
52
  "codemode",
43
53
  "sandbox",
44
54
  "quickjs",
45
- "wasm"
55
+ "wasm",
56
+ "cloudflare",
57
+ "durable-objects"
46
58
  ],
47
59
  "author": "Earendil Works",
48
60
  "license": "MIT",
@@ -58,7 +70,7 @@
58
70
  "quickjs-wasi": "3.6.2"
59
71
  },
60
72
  "devDependencies": {
61
- "shx": "0.4.0",
73
+ "shx": "0.3.4",
62
74
  "vitest": "4.1.11"
63
75
  }
64
76
  }