@execbox/quickjs 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -38
- package/dist/index.cjs +23 -309
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -304
- package/dist/index.js.map +1 -1
- package/dist/{protocolEndpoint-DGOUVf6J.cjs → protocolEndpoint-BGyrwlr_.cjs} +5 -40
- package/dist/protocolEndpoint-BGyrwlr_.cjs.map +1 -0
- package/dist/{protocolEndpoint-T_JEz8YY.js → protocolEndpoint-Ceadcq_L.js} +4 -39
- package/dist/protocolEndpoint-Ceadcq_L.js.map +1 -0
- package/dist/remoteEndpoint.cjs +45 -0
- package/dist/remoteEndpoint.cjs.map +1 -0
- package/dist/remoteEndpoint.d.cts +29 -0
- package/dist/remoteEndpoint.d.cts.map +1 -0
- package/dist/remoteEndpoint.d.ts +29 -0
- package/dist/remoteEndpoint.d.ts.map +1 -0
- package/dist/remoteEndpoint.js +45 -0
- package/dist/remoteEndpoint.js.map +1 -0
- package/dist/runner/index.cjs +1 -1
- package/dist/runner/index.d.cts +2 -2
- package/dist/runner/index.d.cts.map +1 -1
- package/dist/runner/index.d.ts +2 -2
- package/dist/runner/index.d.ts.map +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/protocolEndpoint.cjs +2 -2
- package/dist/runner/protocolEndpoint.d.cts +2 -2
- package/dist/runner/protocolEndpoint.d.cts.map +1 -1
- package/dist/runner/protocolEndpoint.d.ts +2 -2
- package/dist/runner/protocolEndpoint.d.ts.map +1 -1
- package/dist/runner/protocolEndpoint.js +2 -2
- package/dist/runner-DRLfwiqY.cjs +348 -0
- package/dist/runner-DRLfwiqY.cjs.map +1 -0
- package/dist/runner-oZXbguX3.js +343 -0
- package/dist/runner-oZXbguX3.js.map +1 -0
- package/dist/{types-BeVqrcj8.d.ts → types-C-XfFJ7u.d.cts} +8 -21
- package/dist/types-C-XfFJ7u.d.cts.map +1 -0
- package/dist/{types-CnNmLawC.d.cts → types-CE7SvejR.d.ts} +8 -21
- package/dist/types-CE7SvejR.d.ts.map +1 -0
- package/dist/workerEntry.cjs +2 -2
- package/dist/workerEntry.js +2 -2
- package/package.json +12 -3
- package/dist/processEntry.cjs +0 -17
- package/dist/processEntry.cjs.map +0 -1
- package/dist/processEntry.d.cts +0 -5
- package/dist/processEntry.d.ts +0 -5
- package/dist/processEntry.js +0 -18
- package/dist/processEntry.js.map +0 -1
- package/dist/protocolEndpoint-DGOUVf6J.cjs.map +0 -1
- package/dist/protocolEndpoint-T_JEz8YY.js.map +0 -1
- package/dist/runner-CteKTaPD.js +0 -5654
- package/dist/runner-CteKTaPD.js.map +0 -1
- package/dist/runner-DRt0kpEk.cjs +0 -5707
- package/dist/runner-DRt0kpEk.cjs.map +0 -1
- package/dist/types-BeVqrcj8.d.ts.map +0 -1
- package/dist/types-CnNmLawC.d.cts.map +0 -1
package/README.md
CHANGED
|
@@ -1,41 +1,16 @@
|
|
|
1
1
|
# @execbox/quickjs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Default execbox executor for most deployments. It runs guest JavaScript in QuickJS and lets you keep the same API as you move between inline and worker-hosted execution.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@execbox/quickjs)
|
|
6
6
|
[](https://github.com/aallam/execbox/blob/main/LICENSE)
|
|
7
|
+
[](https://execbox.aallam.com)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
## Use `@execbox/quickjs` When
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- you want
|
|
13
|
-
- you do not want a native addon in CI or local development
|
|
14
|
-
- you want fresh runtimes, captured `console.*` output, and JSON-only tool boundaries
|
|
15
|
-
- you want to move from inline execution to worker or child-process hosts without changing packages
|
|
16
|
-
|
|
17
|
-
## Security Notes
|
|
18
|
-
|
|
19
|
-
- Each execution gets a fresh QuickJS runtime with no ambient Node globals injected by execbox.
|
|
20
|
-
- Tool calls cross a JSON-only bridge, and executor timeouts propagate abort signals to in-flight provider work.
|
|
21
|
-
- In the default deployment model, provider definitions are controlled by the host application, while hostile users control guest code and tool inputs.
|
|
22
|
-
- This package is designed for host-controlled deployments and does not by itself create a hard isolation boundary for hostile code.
|
|
23
|
-
- If you need a stronger boundary, use `host: "process"`, move execution behind `@execbox/remote`, or place the runtime behind a container or VM.
|
|
24
|
-
|
|
25
|
-
## Architecture Docs
|
|
26
|
-
|
|
27
|
-
- [Execbox architecture overview](https://github.com/aallam/execbox/blob/main/docs/architecture/README.md)
|
|
28
|
-
- [Execbox executors](https://github.com/aallam/execbox/blob/main/docs/architecture/execbox-executors.md)
|
|
29
|
-
- [Execbox MCP adapters and protocol](https://github.com/aallam/execbox/blob/main/docs/architecture/execbox-mcp-and-protocol.md)
|
|
30
|
-
|
|
31
|
-
## Examples
|
|
32
|
-
|
|
33
|
-
- [Basic provider execution](https://github.com/aallam/execbox/blob/main/examples/execbox-basic.ts)
|
|
34
|
-
- [Process-hosted QuickJS execution](https://github.com/aallam/execbox/blob/main/examples/execbox-process.ts)
|
|
35
|
-
- [Worker-backed QuickJS execution](https://github.com/aallam/execbox/blob/main/examples/execbox-worker.ts)
|
|
36
|
-
- [MCP provider wrapping](https://github.com/aallam/execbox/blob/main/examples/execbox-mcp-provider.ts)
|
|
37
|
-
- [MCP server wrapper](https://github.com/aallam/execbox/blob/main/examples/execbox-mcp-server.ts)
|
|
38
|
-
- [Full examples index](https://github.com/aallam/execbox/tree/main/examples)
|
|
11
|
+
- you want the default execbox path with the easiest setup
|
|
12
|
+
- you do not want a native addon in local development or CI
|
|
13
|
+
- you want one package that can stay inline or move off-thread later
|
|
39
14
|
|
|
40
15
|
## Install
|
|
41
16
|
|
|
@@ -43,16 +18,14 @@ Docs: https://execbox.aallam.com
|
|
|
43
18
|
npm install @execbox/core @execbox/quickjs
|
|
44
19
|
```
|
|
45
20
|
|
|
46
|
-
|
|
47
|
-
Hosted worker/process execution and `@execbox/remote` also reuse the shared QuickJS protocol endpoint from `@execbox/quickjs/runner/protocol-endpoint`.
|
|
48
|
-
|
|
49
|
-
## Usage
|
|
21
|
+
## Smallest Working Usage
|
|
50
22
|
|
|
51
23
|
```ts
|
|
52
24
|
import { resolveProvider } from "@execbox/core";
|
|
53
25
|
import { QuickJsExecutor } from "@execbox/quickjs";
|
|
54
26
|
|
|
55
27
|
const provider = resolveProvider({
|
|
28
|
+
name: "tools",
|
|
56
29
|
tools: {
|
|
57
30
|
echo: {
|
|
58
31
|
execute: async (input) => input,
|
|
@@ -61,14 +34,21 @@ const provider = resolveProvider({
|
|
|
61
34
|
});
|
|
62
35
|
|
|
63
36
|
const executor = new QuickJsExecutor();
|
|
64
|
-
const result = await executor.execute(
|
|
37
|
+
const result = await executor.execute(`await tools.echo({ ok: true })`, [
|
|
65
38
|
provider,
|
|
66
39
|
]);
|
|
40
|
+
|
|
41
|
+
console.log(result);
|
|
67
42
|
```
|
|
68
43
|
|
|
69
|
-
|
|
44
|
+
## Host Modes
|
|
45
|
+
|
|
46
|
+
`QuickJsExecutor` keeps the same execution API while changing where the runtime lives:
|
|
70
47
|
|
|
71
|
-
|
|
48
|
+
| Mode | Use it when |
|
|
49
|
+
| ---------------- | --------------------------------------------------------------- |
|
|
50
|
+
| Inline (default) | You want the lowest-friction development path. |
|
|
51
|
+
| `host: "worker"` | You want QuickJS off the main thread with pooled worker shells. |
|
|
72
52
|
|
|
73
53
|
```ts
|
|
74
54
|
const executor = new QuickJsExecutor({
|
|
@@ -81,3 +61,23 @@ const executor = new QuickJsExecutor({
|
|
|
81
61
|
|
|
82
62
|
await executor.prewarm();
|
|
83
63
|
```
|
|
64
|
+
|
|
65
|
+
## Advanced Imports
|
|
66
|
+
|
|
67
|
+
- `@execbox/quickjs/runner` exports the reusable QuickJS runner
|
|
68
|
+
- `@execbox/quickjs/runner/protocol-endpoint` exports the low-level QuickJS protocol loop used by worker-hosted integrations
|
|
69
|
+
- `@execbox/quickjs/remote-endpoint` adapts the QuickJS protocol loop to `@execbox/remote` runner ports
|
|
70
|
+
|
|
71
|
+
## Operational Notes
|
|
72
|
+
|
|
73
|
+
- Each execution gets a fresh QuickJS runtime with JSON-only tool and result boundaries.
|
|
74
|
+
- This package is the default deployment path, not a hard security boundary for hostile or multi-tenant code.
|
|
75
|
+
- If you need a stronger deployment boundary, move execution behind `@execbox/remote` and a process, container, VM, or network boundary your application owns.
|
|
76
|
+
|
|
77
|
+
## Read Next
|
|
78
|
+
|
|
79
|
+
- [Getting Started](https://execbox.aallam.com/getting-started)
|
|
80
|
+
- [Examples](https://execbox.aallam.com/examples)
|
|
81
|
+
- [Security & Boundaries](https://execbox.aallam.com/security)
|
|
82
|
+
- [Executors](https://execbox.aallam.com/architecture/execbox-executors)
|
|
83
|
+
- [MCP And Protocol](https://execbox.aallam.com/architecture/execbox-mcp-and-protocol)
|
package/dist/index.cjs
CHANGED
|
@@ -1,82 +1,17 @@
|
|
|
1
|
-
const require_runner = require('./runner-
|
|
2
|
-
let
|
|
3
|
-
let node_url = require("node:url");
|
|
4
|
-
let __execbox_core_protocol = require("@execbox/core/protocol");
|
|
5
|
-
let node_crypto = require("node:crypto");
|
|
1
|
+
const require_runner = require('./runner-DRLfwiqY.cjs');
|
|
2
|
+
let __execbox_core_runtime = require("@execbox/core/runtime");
|
|
6
3
|
let node_os = require("node:os");
|
|
7
4
|
let node_worker_threads = require("node:worker_threads");
|
|
5
|
+
let __execbox_core_protocol = require("@execbox/core/protocol");
|
|
6
|
+
let node_crypto = require("node:crypto");
|
|
8
7
|
|
|
9
|
-
//#region ../core/src/runner.ts
|
|
10
|
-
function toTrustedExecuteError(error) {
|
|
11
|
-
if (require_runner.isExecuteFailure(error)) return {
|
|
12
|
-
code: error.code,
|
|
13
|
-
message: error.message
|
|
14
|
-
};
|
|
15
|
-
return {
|
|
16
|
-
code: "tool_error",
|
|
17
|
-
message: require_runner.normalizeThrownMessage(error)
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Converts resolved providers into manifest metadata that reveals only namespace details.
|
|
22
|
-
*/
|
|
23
|
-
function extractProviderManifests(providers) {
|
|
24
|
-
return providers.map((provider) => ({
|
|
25
|
-
name: provider.name,
|
|
26
|
-
tools: Object.fromEntries(Object.entries(provider.tools).map(([safeToolName, descriptor]) => [safeToolName, {
|
|
27
|
-
description: descriptor.description,
|
|
28
|
-
originalName: descriptor.originalName,
|
|
29
|
-
safeName: descriptor.safeName
|
|
30
|
-
}])),
|
|
31
|
-
types: provider.types
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Creates a host-side dispatcher for runner-emitted tool calls.
|
|
36
|
-
*/
|
|
37
|
-
function createToolCallDispatcher(providers, signal) {
|
|
38
|
-
const providerMap = new Map(providers.map((provider) => [provider.name, provider]));
|
|
39
|
-
return async (call) => {
|
|
40
|
-
const provider = providerMap.get(call.providerName);
|
|
41
|
-
const descriptor = provider?.tools[call.safeToolName];
|
|
42
|
-
if (!provider || !descriptor) return {
|
|
43
|
-
error: {
|
|
44
|
-
code: "internal_error",
|
|
45
|
-
message: `Unknown tool ${call.providerName}.${call.safeToolName}`
|
|
46
|
-
},
|
|
47
|
-
ok: false
|
|
48
|
-
};
|
|
49
|
-
try {
|
|
50
|
-
if (signal.aborted) return {
|
|
51
|
-
error: {
|
|
52
|
-
code: "timeout",
|
|
53
|
-
message: require_runner.getExecutionTimeoutMessage()
|
|
54
|
-
},
|
|
55
|
-
ok: false
|
|
56
|
-
};
|
|
57
|
-
const result = await descriptor.execute(call.input, require_runner.createExecutionContext(signal, provider.name, descriptor.safeName, descriptor.originalName));
|
|
58
|
-
if (result !== void 0 && !require_runner.isJsonSerializable(result)) throw new require_runner.ExecuteFailure("serialization_error", "Host value is not JSON-serializable");
|
|
59
|
-
return {
|
|
60
|
-
ok: true,
|
|
61
|
-
result
|
|
62
|
-
};
|
|
63
|
-
} catch (error) {
|
|
64
|
-
return {
|
|
65
|
-
error: toTrustedExecuteError(error),
|
|
66
|
-
ok: false
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
//#endregion
|
|
73
8
|
//#region src/hosted/shared.ts
|
|
74
9
|
/**
|
|
75
|
-
* Default grace period before a hosted shell is forcefully terminated.
|
|
10
|
+
* Default grace period before a worker-hosted shell is forcefully terminated.
|
|
76
11
|
*/
|
|
77
12
|
const DEFAULT_CANCEL_GRACE_MS = 25;
|
|
78
13
|
/**
|
|
79
|
-
* Default pooling limits shared by
|
|
14
|
+
* Default pooling limits shared by worker-hosted QuickJS executors.
|
|
80
15
|
*/
|
|
81
16
|
const DEFAULT_POOL_OPTIONS = {
|
|
82
17
|
idleTimeoutMs: 3e4,
|
|
@@ -85,7 +20,7 @@ const DEFAULT_POOL_OPTIONS = {
|
|
|
85
20
|
prewarm: false
|
|
86
21
|
};
|
|
87
22
|
/**
|
|
88
|
-
* Minimal code used to warm a
|
|
23
|
+
* Minimal code used to warm a worker shell without touching user providers.
|
|
89
24
|
*/
|
|
90
25
|
const DEFAULT_PREWARM_CODE = "undefined";
|
|
91
26
|
/**
|
|
@@ -140,7 +75,7 @@ async function runHostedTransportSession(options) {
|
|
|
140
75
|
executionId: (0, node_crypto.randomUUID)(),
|
|
141
76
|
onSettled: options.onSettled,
|
|
142
77
|
providers: options.providers,
|
|
143
|
-
runtimeOptions:
|
|
78
|
+
runtimeOptions: (0, __execbox_core_runtime.resolveExecutorRuntimeOptions)(options.executorOptions, options.requestOptions),
|
|
144
79
|
signal: options.requestOptions?.signal,
|
|
145
80
|
transport: options.transport
|
|
146
81
|
});
|
|
@@ -163,227 +98,6 @@ async function warmHostedPool(options) {
|
|
|
163
98
|
if (rejected?.status === "rejected") throw rejected.reason;
|
|
164
99
|
}
|
|
165
100
|
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/hosted/processHostedExecutor.ts
|
|
168
|
-
function resolveProcessEntryPath() {
|
|
169
|
-
const extension = require("url").pathToFileURL(__filename).href.endsWith(".ts") ? ".ts" : ".js";
|
|
170
|
-
return (0, node_url.fileURLToPath)(new URL(`../processEntry${extension}`, require("url").pathToFileURL(__filename).href));
|
|
171
|
-
}
|
|
172
|
-
function createUnexpectedExitMessage(code, signal) {
|
|
173
|
-
if (code !== null) return `Child process exited unexpectedly with code ${code}`;
|
|
174
|
-
if (signal) return `Child process exited unexpectedly with signal ${signal}`;
|
|
175
|
-
return "Child process exited unexpectedly";
|
|
176
|
-
}
|
|
177
|
-
function createChildProcess() {
|
|
178
|
-
return (0, node_child_process.fork)(resolveProcessEntryPath(), [], {
|
|
179
|
-
execArgv: (0, __execbox_core_protocol.getNodeTransportExecArgv)(require("url").pathToFileURL(__filename).href),
|
|
180
|
-
stdio: [
|
|
181
|
-
"ignore",
|
|
182
|
-
"ignore",
|
|
183
|
-
"ignore",
|
|
184
|
-
"ipc"
|
|
185
|
-
]
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
function createProcessTransport(child) {
|
|
189
|
-
let terminated = false;
|
|
190
|
-
let closeReason;
|
|
191
|
-
const closeHandlers = /* @__PURE__ */ new Set();
|
|
192
|
-
const errorHandlers = /* @__PURE__ */ new Set();
|
|
193
|
-
const messageHandlers = /* @__PURE__ */ new Set();
|
|
194
|
-
const terminateChild = () => {
|
|
195
|
-
if (terminated) return;
|
|
196
|
-
terminated = true;
|
|
197
|
-
child.kill("SIGKILL");
|
|
198
|
-
};
|
|
199
|
-
const notifyClose = (reason) => {
|
|
200
|
-
if (closeReason) return;
|
|
201
|
-
closeReason = reason;
|
|
202
|
-
for (const handler of closeHandlers) handler(reason);
|
|
203
|
-
};
|
|
204
|
-
const onDisconnect = () => {
|
|
205
|
-
notifyClose({ message: "Child process disconnected unexpectedly" });
|
|
206
|
-
};
|
|
207
|
-
const onExit = (code, signal) => {
|
|
208
|
-
notifyClose({
|
|
209
|
-
code,
|
|
210
|
-
message: createUnexpectedExitMessage(code, signal),
|
|
211
|
-
signal
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
const onError = (error) => {
|
|
215
|
-
for (const handler of errorHandlers) handler(error);
|
|
216
|
-
};
|
|
217
|
-
const onMessage = (message) => {
|
|
218
|
-
for (const handler of messageHandlers) handler(message);
|
|
219
|
-
};
|
|
220
|
-
child.on("disconnect", onDisconnect);
|
|
221
|
-
child.on("exit", onExit);
|
|
222
|
-
child.on("error", onError);
|
|
223
|
-
child.on("message", onMessage);
|
|
224
|
-
return {
|
|
225
|
-
dispose: () => {
|
|
226
|
-
child.off("disconnect", onDisconnect);
|
|
227
|
-
child.off("exit", onExit);
|
|
228
|
-
child.off("error", onError);
|
|
229
|
-
child.off("message", onMessage);
|
|
230
|
-
terminateChild();
|
|
231
|
-
},
|
|
232
|
-
onClose: (handler) => {
|
|
233
|
-
closeHandlers.add(handler);
|
|
234
|
-
if (closeReason) queueMicrotask(() => {
|
|
235
|
-
if (closeHandlers.has(handler)) handler(closeReason);
|
|
236
|
-
});
|
|
237
|
-
return () => {
|
|
238
|
-
closeHandlers.delete(handler);
|
|
239
|
-
};
|
|
240
|
-
},
|
|
241
|
-
onError: (handler) => {
|
|
242
|
-
errorHandlers.add(handler);
|
|
243
|
-
return () => errorHandlers.delete(handler);
|
|
244
|
-
},
|
|
245
|
-
onMessage: (handler) => {
|
|
246
|
-
messageHandlers.add(handler);
|
|
247
|
-
return () => messageHandlers.delete(handler);
|
|
248
|
-
},
|
|
249
|
-
send: (message) => new Promise((resolve, reject) => {
|
|
250
|
-
if (!child.connected || typeof child.send !== "function") {
|
|
251
|
-
reject(/* @__PURE__ */ new Error("Child process disconnected unexpectedly"));
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
child.send(message, (error) => {
|
|
255
|
-
if (error) {
|
|
256
|
-
reject(error);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
resolve();
|
|
260
|
-
});
|
|
261
|
-
}),
|
|
262
|
-
terminate: () => {
|
|
263
|
-
terminateChild();
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
function createProcessShell() {
|
|
268
|
-
const child = createChildProcess();
|
|
269
|
-
return {
|
|
270
|
-
child,
|
|
271
|
-
transport: createProcessTransport(child)
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
function resolvePoolOptions$1(options) {
|
|
275
|
-
if (options.mode === "ephemeral") return;
|
|
276
|
-
return {
|
|
277
|
-
...DEFAULT_POOL_OPTIONS,
|
|
278
|
-
...options.pool
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Child-process executor that runs guest code inside a dedicated QuickJS runtime per call.
|
|
283
|
-
*/
|
|
284
|
-
var ProcessHostedQuickJsExecutor = class {
|
|
285
|
-
cancelGraceMs;
|
|
286
|
-
options;
|
|
287
|
-
pool;
|
|
288
|
-
poolOptions;
|
|
289
|
-
warmup;
|
|
290
|
-
/**
|
|
291
|
-
* Creates a hosted QuickJS executor that launches child-process shells on demand.
|
|
292
|
-
*/
|
|
293
|
-
constructor(options) {
|
|
294
|
-
this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
|
|
295
|
-
this.options = options;
|
|
296
|
-
const poolOptions = resolvePoolOptions$1(options);
|
|
297
|
-
this.poolOptions = poolOptions;
|
|
298
|
-
if (poolOptions) {
|
|
299
|
-
this.pool = (0, __execbox_core_protocol.createResourcePool)({
|
|
300
|
-
create: async () => createProcessShell(),
|
|
301
|
-
destroy: async (shell) => {
|
|
302
|
-
await shell.transport.dispose();
|
|
303
|
-
},
|
|
304
|
-
idleTimeoutMs: poolOptions.idleTimeoutMs,
|
|
305
|
-
maxSize: poolOptions.maxSize,
|
|
306
|
-
minSize: poolOptions.minSize
|
|
307
|
-
});
|
|
308
|
-
const prewarmCount = getPrewarmCount(poolOptions);
|
|
309
|
-
if (prewarmCount > 0) this.warmup = this.warmPool(prewarmCount);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Disposes any pooled child-process shells owned by this executor.
|
|
314
|
-
*/
|
|
315
|
-
async dispose() {
|
|
316
|
-
await this.pool?.dispose();
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Prewarms pooled child-process shells up to the requested count.
|
|
320
|
-
*/
|
|
321
|
-
async prewarm(count) {
|
|
322
|
-
if (!this.pool || !this.poolOptions) return;
|
|
323
|
-
const target = getWarmupTarget(count, this.poolOptions);
|
|
324
|
-
if (target <= 0) return;
|
|
325
|
-
await this.warmPool(target);
|
|
326
|
-
}
|
|
327
|
-
async runTransportSession(transport, code, providers, options = {}, onSettled) {
|
|
328
|
-
return await runHostedTransportSession({
|
|
329
|
-
cancelGraceMs: this.cancelGraceMs,
|
|
330
|
-
code,
|
|
331
|
-
executorOptions: this.options,
|
|
332
|
-
onSettled,
|
|
333
|
-
providers,
|
|
334
|
-
requestOptions: options,
|
|
335
|
-
transport
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
async warmPool(count) {
|
|
339
|
-
if (!this.pool) return;
|
|
340
|
-
await this.pool.prewarm(count);
|
|
341
|
-
const leases = [];
|
|
342
|
-
try {
|
|
343
|
-
for (let index = 0; index < count; index += 1) leases.push(await this.pool.acquire());
|
|
344
|
-
} catch (error) {
|
|
345
|
-
await Promise.allSettled(leases.map(async (lease) => await lease.release(false)));
|
|
346
|
-
throw error;
|
|
347
|
-
}
|
|
348
|
-
await warmHostedPool({
|
|
349
|
-
count,
|
|
350
|
-
getTransport: (lease) => lease.value.transport,
|
|
351
|
-
label: "child process",
|
|
352
|
-
onRelease: async (lease, reusable) => await lease.release(reusable),
|
|
353
|
-
runSession: async (transport, code, providers) => await this.runTransportSession(transport, code, providers),
|
|
354
|
-
shells: leases
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Executes guest code in a child-process-hosted QuickJS shell.
|
|
359
|
-
*/
|
|
360
|
-
async execute(code, providers, options = {}) {
|
|
361
|
-
if (options.signal?.aborted) return require_runner.createTimeoutExecuteResult();
|
|
362
|
-
await this.warmup;
|
|
363
|
-
if (this.pool) {
|
|
364
|
-
const lease = await this.pool.acquire();
|
|
365
|
-
return await this.runTransportSession(createBorrowedTransport(lease.value.transport), code, providers, options, async (result) => {
|
|
366
|
-
await lease.release(isReusableResult(result));
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
let child;
|
|
370
|
-
try {
|
|
371
|
-
child = createChildProcess();
|
|
372
|
-
} catch (error) {
|
|
373
|
-
return {
|
|
374
|
-
durationMs: 0,
|
|
375
|
-
error: {
|
|
376
|
-
code: "internal_error",
|
|
377
|
-
message: error instanceof Error ? error.message : String(error)
|
|
378
|
-
},
|
|
379
|
-
logs: [],
|
|
380
|
-
ok: false
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
return await this.runTransportSession(createProcessTransport(child), code, providers, options);
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
|
|
387
101
|
//#endregion
|
|
388
102
|
//#region src/hosted/workerHostedExecutor.ts
|
|
389
103
|
const DEFAULT_POOLED_WORKER_MAX_SIZE = 4;
|
|
@@ -487,7 +201,7 @@ var WorkerHostedQuickJsExecutor = class {
|
|
|
487
201
|
poolOptions;
|
|
488
202
|
warmup;
|
|
489
203
|
/**
|
|
490
|
-
* Creates a
|
|
204
|
+
* Creates a QuickJS executor that launches worker-thread shells on demand.
|
|
491
205
|
*/
|
|
492
206
|
constructor(options) {
|
|
493
207
|
this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
|
|
@@ -557,7 +271,7 @@ var WorkerHostedQuickJsExecutor = class {
|
|
|
557
271
|
* Executes guest code in a worker-thread-hosted QuickJS shell.
|
|
558
272
|
*/
|
|
559
273
|
async execute(code, providers, options = {}) {
|
|
560
|
-
if (options.signal?.aborted) return
|
|
274
|
+
if (options.signal?.aborted) return (0, __execbox_core_runtime.createTimeoutExecuteResult)();
|
|
561
275
|
await this.warmup;
|
|
562
276
|
if (this.pool) {
|
|
563
277
|
const lease = await this.pool.acquire();
|
|
@@ -578,28 +292,28 @@ var WorkerHostedQuickJsExecutor = class {
|
|
|
578
292
|
function isWorkerOptions(options) {
|
|
579
293
|
return options.host === "worker";
|
|
580
294
|
}
|
|
581
|
-
function
|
|
582
|
-
|
|
295
|
+
function getUnsupportedHost(options) {
|
|
296
|
+
const host = options.host;
|
|
297
|
+
if (host === void 0 || host === "inline" || host === "worker") return;
|
|
298
|
+
return String(host);
|
|
583
299
|
}
|
|
584
300
|
/**
|
|
585
|
-
* QuickJS-backed executor for inline
|
|
301
|
+
* QuickJS-backed executor for inline or worker-backed JavaScript runs.
|
|
586
302
|
*/
|
|
587
303
|
var QuickJsExecutor = class {
|
|
588
304
|
hostedExecutor;
|
|
589
305
|
options;
|
|
590
306
|
/**
|
|
591
307
|
* Creates a QuickJS executor with inline QuickJS by default, or a hosted
|
|
592
|
-
* worker
|
|
308
|
+
* worker shell when `host` is explicitly set.
|
|
593
309
|
*/
|
|
594
310
|
constructor(options = {}) {
|
|
311
|
+
const unsupportedHost = getUnsupportedHost(options);
|
|
312
|
+
if (unsupportedHost !== void 0) throw new Error(`QuickJsExecutor host "${unsupportedHost}" is no longer supported. Use host "worker" for local hosted execution, or @execbox/remote for process, container, or VM boundaries.`);
|
|
595
313
|
if (isWorkerOptions(options)) {
|
|
596
314
|
this.hostedExecutor = new WorkerHostedQuickJsExecutor(options);
|
|
597
315
|
return;
|
|
598
316
|
}
|
|
599
|
-
if (isProcessOptions(options)) {
|
|
600
|
-
this.hostedExecutor = new ProcessHostedQuickJsExecutor(options);
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
603
317
|
this.options = options;
|
|
604
318
|
}
|
|
605
319
|
/**
|
|
@@ -609,8 +323,8 @@ var QuickJsExecutor = class {
|
|
|
609
323
|
await this.hostedExecutor?.dispose?.();
|
|
610
324
|
}
|
|
611
325
|
/**
|
|
612
|
-
* Prewarms pooled hosted shells when the executor is running in worker
|
|
613
|
-
*
|
|
326
|
+
* Prewarms pooled hosted shells when the executor is running in worker mode.
|
|
327
|
+
* Inline mode treats this as a no-op.
|
|
614
328
|
*/
|
|
615
329
|
async prewarm(count) {
|
|
616
330
|
await this.hostedExecutor?.prewarm?.(count);
|
|
@@ -620,9 +334,9 @@ var QuickJsExecutor = class {
|
|
|
620
334
|
*/
|
|
621
335
|
async execute(code, providers, options = {}) {
|
|
622
336
|
if (this.hostedExecutor) return await this.hostedExecutor.execute(code, providers, options);
|
|
623
|
-
if (options.signal?.aborted) return
|
|
337
|
+
if (options.signal?.aborted) return (0, __execbox_core_runtime.createTimeoutExecuteResult)();
|
|
624
338
|
const abortController = new AbortController();
|
|
625
|
-
const onToolCall = createToolCallDispatcher(providers, abortController.signal);
|
|
339
|
+
const onToolCall = (0, __execbox_core_runtime.createToolCallDispatcher)(providers, abortController.signal);
|
|
626
340
|
const onAbort = () => {
|
|
627
341
|
abortController.abort();
|
|
628
342
|
};
|
|
@@ -632,7 +346,7 @@ var QuickJsExecutor = class {
|
|
|
632
346
|
abortController,
|
|
633
347
|
code,
|
|
634
348
|
onToolCall,
|
|
635
|
-
providers: extractProviderManifests(providers)
|
|
349
|
+
providers: (0, __execbox_core_runtime.extractProviderManifests)(providers)
|
|
636
350
|
}, {
|
|
637
351
|
...this.options,
|
|
638
352
|
...options
|