@ai-sdk/code-mode 1.0.20 → 1.0.22
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 +1 -1
- package/dist/continuation-capability.js +5 -0
- package/dist/continuation-capability.js.map +1 -1
- package/dist/errors.js +1 -136
- package/dist/errors.js.map +1 -1
- package/dist/host-interrupt.js +2 -14
- package/dist/host-interrupt.js.map +1 -1
- package/dist/index.d.ts +14 -44
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interrupt-continuation.js +3 -8
- package/dist/interrupt-continuation.js.map +1 -1
- package/dist/run-code-mode.js +543 -2
- package/dist/run-code-mode.js.map +1 -1
- package/dist/tool-invocation.js +9 -23
- package/dist/tool-invocation.js.map +1 -1
- package/dist/utils/serialization.js +4 -0
- package/dist/utils/serialization.js.map +1 -1
- package/package.json +8 -7
- package/src/continuation-capability.ts +18 -0
- package/src/errors.ts +0 -190
- package/src/host-interrupt.ts +2 -31
- package/src/index.ts +1 -1
- package/src/interrupt-continuation.ts +13 -15
- package/src/run-code-mode.ts +706 -3
- package/src/tool-invocation.ts +9 -23
- package/src/types.ts +38 -68
- package/src/utils/serialization.ts +4 -0
- package/dist/runtime/guest-sources.js +0 -350
- package/dist/runtime/guest-sources.js.map +0 -1
- package/dist/runtime/manager.js +0 -794
- package/dist/runtime/manager.js.map +0 -1
- package/dist/runtime/max-workers.js +0 -40
- package/dist/runtime/max-workers.js.map +0 -1
- package/dist/runtime/protocol.js +0 -1
- package/dist/runtime/protocol.js.map +0 -1
- package/dist/runtime/worker-source.d.ts +0 -11
- package/dist/runtime/worker-source.js +0 -2
- package/dist/utils/options.js +0 -76
- package/dist/utils/options.js.map +0 -1
- package/dist/utils/source-cache.js +0 -77
- package/dist/utils/source-cache.js.map +0 -1
- package/src/runtime/guest-sources.ts +0 -351
- package/src/runtime/manager.ts +0 -963
- package/src/runtime/max-workers.ts +0 -65
- package/src/runtime/protocol.ts +0 -74
- package/src/runtime/worker-source.ts +0 -10
- package/src/runtime/worker.ts +0 -607
- package/src/utils/options.ts +0 -83
- package/src/utils/source-cache.ts +0 -91
package/src/runtime/worker.ts
DELETED
|
@@ -1,607 +0,0 @@
|
|
|
1
|
-
import { writeSync } from 'node:fs';
|
|
2
|
-
import { formatWithOptions } from 'node:util';
|
|
3
|
-
import { parentPort } from 'node:worker_threads';
|
|
4
|
-
import {
|
|
5
|
-
newQuickJSAsyncWASMModule,
|
|
6
|
-
newVariant,
|
|
7
|
-
type QuickJSAsyncContext,
|
|
8
|
-
type QuickJSDeferredPromise,
|
|
9
|
-
type QuickJSHandle,
|
|
10
|
-
RELEASE_ASYNC,
|
|
11
|
-
} from 'quickjs-emscripten';
|
|
12
|
-
import { CodeModeProtocolError, serializeError } from '../errors.js';
|
|
13
|
-
import { assertJsonPayloadSize } from '../utils/serialization.js';
|
|
14
|
-
import { buildGuestRuntimeSetupSource, wrapUserCode } from './guest-sources.js';
|
|
15
|
-
import type {
|
|
16
|
-
MainToWorkerMessage,
|
|
17
|
-
WorkerBridgeResponse,
|
|
18
|
-
WorkerRunMessage,
|
|
19
|
-
} from './protocol.js';
|
|
20
|
-
|
|
21
|
-
if (!parentPort) {
|
|
22
|
-
throw new Error('code-mode worker must run inside a worker thread');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const pendingBridgeRequests = new Map<
|
|
26
|
-
string,
|
|
27
|
-
{
|
|
28
|
-
context: QuickJSAsyncContext;
|
|
29
|
-
deferred: QuickJSDeferredPromise;
|
|
30
|
-
invocationId: string;
|
|
31
|
-
resetDateNow?: QuickJSHandle;
|
|
32
|
-
}
|
|
33
|
-
>();
|
|
34
|
-
let activeInvocationId: string | undefined;
|
|
35
|
-
let bridgeRequestCounter = 0;
|
|
36
|
-
let embeddedQuickJsWasmModulePromise: Promise<WebAssembly.Module> | undefined;
|
|
37
|
-
|
|
38
|
-
parentPort.on('message', (message: MainToWorkerMessage) => {
|
|
39
|
-
if (message.type === 'bridge-drain') {
|
|
40
|
-
if (message.invocationId !== activeInvocationId) {
|
|
41
|
-
throw new CodeModeProtocolError(
|
|
42
|
-
`Bridge drain invocationId mismatch: expected ${activeInvocationId ?? 'no active invocation'}, received ${message.invocationId}.`,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
parentPort?.postMessage({
|
|
46
|
-
type: 'bridge-drained',
|
|
47
|
-
invocationId: message.invocationId,
|
|
48
|
-
drainId: message.drainId,
|
|
49
|
-
});
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (message.type === 'bridge-response') {
|
|
54
|
-
const pending = pendingBridgeRequests.get(message.requestId);
|
|
55
|
-
if (!pending) {
|
|
56
|
-
throw new CodeModeProtocolError(
|
|
57
|
-
`Unexpected bridge response requestId: ${message.requestId}.`,
|
|
58
|
-
{
|
|
59
|
-
invocationId: message.invocationId,
|
|
60
|
-
requestId: message.requestId,
|
|
61
|
-
},
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (pending.invocationId !== message.invocationId) {
|
|
66
|
-
throw new CodeModeProtocolError(
|
|
67
|
-
`Bridge response invocationId mismatch for request ${message.requestId}: expected ${pending.invocationId}, received ${message.invocationId}.`,
|
|
68
|
-
{
|
|
69
|
-
expectedInvocationId: pending.invocationId,
|
|
70
|
-
receivedInvocationId: message.invocationId,
|
|
71
|
-
requestId: message.requestId,
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
pendingBridgeRequests.delete(message.requestId);
|
|
77
|
-
resolveBridgeResponse(
|
|
78
|
-
pending.context,
|
|
79
|
-
pending.deferred,
|
|
80
|
-
message,
|
|
81
|
-
pending.resetDateNow,
|
|
82
|
-
);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (message.type === 'run') {
|
|
87
|
-
if (activeInvocationId !== undefined) {
|
|
88
|
-
throw new CodeModeProtocolError(
|
|
89
|
-
`Worker received run ${message.invocationId} while ${activeInvocationId} is still active.`,
|
|
90
|
-
{
|
|
91
|
-
activeInvocationId,
|
|
92
|
-
receivedInvocationId: message.invocationId,
|
|
93
|
-
},
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
activeInvocationId = message.invocationId;
|
|
98
|
-
void run(message).finally(() => {
|
|
99
|
-
activeInvocationId = undefined;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
async function run(message: WorkerRunMessage): Promise<void> {
|
|
105
|
-
try {
|
|
106
|
-
const valueJson = await execute(message);
|
|
107
|
-
parentPort?.postMessage({
|
|
108
|
-
type: 'result',
|
|
109
|
-
invocationId: message.invocationId,
|
|
110
|
-
success: true,
|
|
111
|
-
valueJson,
|
|
112
|
-
});
|
|
113
|
-
} catch (error) {
|
|
114
|
-
parentPort?.postMessage({
|
|
115
|
-
type: 'result',
|
|
116
|
-
invocationId: message.invocationId,
|
|
117
|
-
success: false,
|
|
118
|
-
error: serializeError(error),
|
|
119
|
-
});
|
|
120
|
-
} finally {
|
|
121
|
-
parentPort?.postMessage({
|
|
122
|
-
type: 'ready',
|
|
123
|
-
invocationId: message.invocationId,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function execute(message: WorkerRunMessage): Promise<string> {
|
|
129
|
-
const context = await createQuickJSContext();
|
|
130
|
-
const runtime = context.runtime;
|
|
131
|
-
const deadline = Date.now() + message.options.timeoutMs;
|
|
132
|
-
let interruptChecks = 0;
|
|
133
|
-
let bridgeFunctions: { invokeTool: QuickJSHandle } | undefined;
|
|
134
|
-
let determinismHandle: QuickJSHandle | undefined;
|
|
135
|
-
let resetDateNowHandle: QuickJSHandle | undefined;
|
|
136
|
-
|
|
137
|
-
runtime.setMemoryLimit(message.options.memoryLimitBytes);
|
|
138
|
-
runtime.setMaxStackSize(message.options.maxStackSizeBytes);
|
|
139
|
-
runtime.setInterruptHandler(() => {
|
|
140
|
-
interruptChecks++;
|
|
141
|
-
return interruptChecks > 10_000 || Date.now() > deadline;
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
installConsole(context, message.options.maxConsoleOutputBytes);
|
|
146
|
-
bridgeFunctions = createBridgeFunctions(
|
|
147
|
-
context,
|
|
148
|
-
message,
|
|
149
|
-
() => resetDateNowHandle,
|
|
150
|
-
);
|
|
151
|
-
determinismHandle = jsToHandle(context, message.determinism);
|
|
152
|
-
const setupSource = buildGuestRuntimeSetupSource();
|
|
153
|
-
const setupEvalResult = await context.evalCodeAsync(
|
|
154
|
-
setupSource,
|
|
155
|
-
'code-mode-setup.js',
|
|
156
|
-
);
|
|
157
|
-
if (setupEvalResult.error) {
|
|
158
|
-
const error = context.dump(setupEvalResult.error);
|
|
159
|
-
if (setupEvalResult.error.alive) {
|
|
160
|
-
setupEvalResult.error.dispose();
|
|
161
|
-
}
|
|
162
|
-
throw toError(error);
|
|
163
|
-
}
|
|
164
|
-
try {
|
|
165
|
-
const setupCallResult = context.callFunction(
|
|
166
|
-
setupEvalResult.value,
|
|
167
|
-
context.undefined,
|
|
168
|
-
bridgeFunctions.invokeTool,
|
|
169
|
-
determinismHandle,
|
|
170
|
-
);
|
|
171
|
-
if (setupCallResult.error) {
|
|
172
|
-
const error = context.dump(setupCallResult.error);
|
|
173
|
-
if (setupCallResult.error.alive) {
|
|
174
|
-
setupCallResult.error.dispose();
|
|
175
|
-
}
|
|
176
|
-
throw toError(error);
|
|
177
|
-
}
|
|
178
|
-
if (setupCallResult.value.alive) {
|
|
179
|
-
resetDateNowHandle = context.getProp(
|
|
180
|
-
setupCallResult.value,
|
|
181
|
-
'resetDateNow',
|
|
182
|
-
);
|
|
183
|
-
setupCallResult.value.dispose();
|
|
184
|
-
}
|
|
185
|
-
} finally {
|
|
186
|
-
if (setupEvalResult.value.alive) {
|
|
187
|
-
setupEvalResult.value.dispose();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const wrapped = wrapUserCode(message.js);
|
|
192
|
-
const evalResult = await context.evalCodeAsync(wrapped, 'code-mode.js');
|
|
193
|
-
|
|
194
|
-
if (evalResult.error) {
|
|
195
|
-
const error = context.dump(evalResult.error);
|
|
196
|
-
if (evalResult.error.alive) {
|
|
197
|
-
evalResult.error.dispose();
|
|
198
|
-
}
|
|
199
|
-
throw toError(error);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (evalResult.value.alive) {
|
|
203
|
-
evalResult.value.dispose();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const promiseHandle = context.getProp(context.global, '__codeModeResult');
|
|
207
|
-
const resolvedResult = await resolveQuickJSPromise(context, promiseHandle);
|
|
208
|
-
if (promiseHandle.alive) {
|
|
209
|
-
promiseHandle.dispose();
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (resolvedResult.error) {
|
|
213
|
-
const error = context.dump(resolvedResult.error);
|
|
214
|
-
if (resolvedResult.error.alive) {
|
|
215
|
-
resolvedResult.error.dispose();
|
|
216
|
-
}
|
|
217
|
-
throw toError(error);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const valueJson = serializeQuickJSJsonPayload(
|
|
221
|
-
context,
|
|
222
|
-
resolvedResult.value,
|
|
223
|
-
);
|
|
224
|
-
if (resolvedResult.value.alive) {
|
|
225
|
-
resolvedResult.value.dispose();
|
|
226
|
-
}
|
|
227
|
-
assertJsonPayloadSize(
|
|
228
|
-
valueJson,
|
|
229
|
-
message.options.maxResultBytes,
|
|
230
|
-
'Code mode result',
|
|
231
|
-
);
|
|
232
|
-
return valueJson;
|
|
233
|
-
} finally {
|
|
234
|
-
for (const pending of pendingBridgeRequests.values()) {
|
|
235
|
-
const error = pending.context.newError(
|
|
236
|
-
'Worker execution finished before bridge response',
|
|
237
|
-
);
|
|
238
|
-
pending.deferred.reject(error);
|
|
239
|
-
error.dispose();
|
|
240
|
-
}
|
|
241
|
-
pendingBridgeRequests.clear();
|
|
242
|
-
if (bridgeFunctions?.invokeTool.alive) {
|
|
243
|
-
bridgeFunctions.invokeTool.dispose();
|
|
244
|
-
}
|
|
245
|
-
if (determinismHandle?.alive) {
|
|
246
|
-
determinismHandle.dispose();
|
|
247
|
-
}
|
|
248
|
-
if (resetDateNowHandle?.alive) {
|
|
249
|
-
resetDateNowHandle.dispose();
|
|
250
|
-
}
|
|
251
|
-
context.dispose();
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
async function createQuickJSContext(): Promise<QuickJSAsyncContext> {
|
|
256
|
-
const embeddedWasmBase64 = getEmbeddedQuickJsWasmBase64();
|
|
257
|
-
if (embeddedWasmBase64 !== undefined) {
|
|
258
|
-
const variant = newVariant(RELEASE_ASYNC, {
|
|
259
|
-
wasmModule: () => getEmbeddedQuickJsWasmModule(embeddedWasmBase64),
|
|
260
|
-
});
|
|
261
|
-
return (await newQuickJSAsyncWASMModule(variant)).newContext();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return (await newQuickJSAsyncWASMModule(RELEASE_ASYNC)).newContext();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function getEmbeddedQuickJsWasmBase64(): string | undefined {
|
|
268
|
-
return (
|
|
269
|
-
globalThis as typeof globalThis & {
|
|
270
|
-
__CODE_MODE_QUICKJS_WASM_BASE64__?: unknown;
|
|
271
|
-
}
|
|
272
|
-
).__CODE_MODE_QUICKJS_WASM_BASE64__ as string | undefined;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function getEmbeddedQuickJsWasmModule(
|
|
276
|
-
wasmBase64: string,
|
|
277
|
-
): Promise<WebAssembly.Module> {
|
|
278
|
-
embeddedQuickJsWasmModulePromise ??= WebAssembly.compile(
|
|
279
|
-
decodeBase64ArrayBuffer(wasmBase64),
|
|
280
|
-
);
|
|
281
|
-
return embeddedQuickJsWasmModulePromise;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function decodeBase64ArrayBuffer(value: string): ArrayBuffer {
|
|
285
|
-
const bytes = Buffer.from(value, 'base64');
|
|
286
|
-
const copy = new Uint8Array(bytes.byteLength);
|
|
287
|
-
copy.set(bytes);
|
|
288
|
-
return copy.buffer as ArrayBuffer;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function serializeQuickJSJsonPayload(
|
|
292
|
-
context: QuickJSAsyncContext,
|
|
293
|
-
value: QuickJSHandle,
|
|
294
|
-
): string {
|
|
295
|
-
const serialize = context.getProp(
|
|
296
|
-
context.global,
|
|
297
|
-
'__codeModeSerializeJsonPayload',
|
|
298
|
-
);
|
|
299
|
-
try {
|
|
300
|
-
const result = context.callFunction(serialize, context.undefined, value);
|
|
301
|
-
if (result.error) {
|
|
302
|
-
const error = context.dump(result.error);
|
|
303
|
-
if (result.error.alive) {
|
|
304
|
-
result.error.dispose();
|
|
305
|
-
}
|
|
306
|
-
throw toError(error);
|
|
307
|
-
}
|
|
308
|
-
const valueJson = context.getString(result.value);
|
|
309
|
-
if (result.value.alive) {
|
|
310
|
-
result.value.dispose();
|
|
311
|
-
}
|
|
312
|
-
return valueJson;
|
|
313
|
-
} finally {
|
|
314
|
-
if (serialize.alive) {
|
|
315
|
-
serialize.dispose();
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function installConsole(
|
|
321
|
-
context: QuickJSAsyncContext,
|
|
322
|
-
maxOutputBytes: number,
|
|
323
|
-
): void {
|
|
324
|
-
const outputBudget = { remainingBytes: maxOutputBytes };
|
|
325
|
-
const consoleHandle = context.newObject();
|
|
326
|
-
const handles = [
|
|
327
|
-
['log', createConsoleFunction(context, 'stdout', outputBudget)],
|
|
328
|
-
['info', createConsoleFunction(context, 'stdout', outputBudget)],
|
|
329
|
-
['debug', createConsoleFunction(context, 'stdout', outputBudget)],
|
|
330
|
-
['error', createConsoleFunction(context, 'stderr', outputBudget)],
|
|
331
|
-
] as const;
|
|
332
|
-
|
|
333
|
-
for (const [name, handle] of handles) {
|
|
334
|
-
context.setProp(consoleHandle, name, handle);
|
|
335
|
-
}
|
|
336
|
-
context.setProp(context.global, 'console', consoleHandle);
|
|
337
|
-
|
|
338
|
-
for (const [, handle] of handles) {
|
|
339
|
-
handle.dispose();
|
|
340
|
-
}
|
|
341
|
-
consoleHandle.dispose();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function createConsoleFunction(
|
|
345
|
-
context: QuickJSAsyncContext,
|
|
346
|
-
stream: 'stdout' | 'stderr',
|
|
347
|
-
outputBudget: { remainingBytes: number },
|
|
348
|
-
): QuickJSHandle {
|
|
349
|
-
return context.newFunction('console', (...args: QuickJSHandle[]) => {
|
|
350
|
-
if (outputBudget.remainingBytes === 0) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
const values = args.map(arg => dumpConsoleArg(context, arg));
|
|
354
|
-
const line = formatWithOptions({ colors: false, depth: 4 }, ...values);
|
|
355
|
-
const output = `${line}\n`;
|
|
356
|
-
const outputBytes = Buffer.byteLength(output);
|
|
357
|
-
if (outputBytes > outputBudget.remainingBytes) {
|
|
358
|
-
outputBudget.remainingBytes = 0;
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
outputBudget.remainingBytes -= outputBytes;
|
|
362
|
-
writeSync(stream === 'stderr' ? 2 : 1, output);
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function dumpConsoleArg(
|
|
367
|
-
context: QuickJSAsyncContext,
|
|
368
|
-
arg: QuickJSHandle,
|
|
369
|
-
): unknown {
|
|
370
|
-
try {
|
|
371
|
-
return context.dump(arg);
|
|
372
|
-
} catch {
|
|
373
|
-
return '[Unprintable QuickJS value]';
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function createBridgeFunctions(
|
|
378
|
-
context: QuickJSAsyncContext,
|
|
379
|
-
message: WorkerRunMessage,
|
|
380
|
-
getResetDateNow: () => QuickJSHandle | undefined,
|
|
381
|
-
): { invokeTool: QuickJSHandle } {
|
|
382
|
-
const invokeTool = context.newFunction(
|
|
383
|
-
'__codeModeInvokeTool',
|
|
384
|
-
(toolNameHandle: QuickJSHandle, inputJsonHandle: QuickJSHandle) => {
|
|
385
|
-
const toolName = context.getString(toolNameHandle);
|
|
386
|
-
const inputJson = context.getString(inputJsonHandle);
|
|
387
|
-
return requestHost(
|
|
388
|
-
context,
|
|
389
|
-
message.invocationId,
|
|
390
|
-
{ toolName, inputJson },
|
|
391
|
-
getResetDateNow(),
|
|
392
|
-
);
|
|
393
|
-
},
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
return { invokeTool };
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function requestHost(
|
|
400
|
-
context: QuickJSAsyncContext,
|
|
401
|
-
invocationId: string,
|
|
402
|
-
payload: Record<string, unknown>,
|
|
403
|
-
resetDateNow: QuickJSHandle | undefined,
|
|
404
|
-
): QuickJSHandle {
|
|
405
|
-
const requestId = `${invocationId}:bridge-${++bridgeRequestCounter}`;
|
|
406
|
-
const deferred = context.newPromise();
|
|
407
|
-
pendingBridgeRequests.set(requestId, {
|
|
408
|
-
context,
|
|
409
|
-
deferred,
|
|
410
|
-
invocationId,
|
|
411
|
-
...(resetDateNow !== undefined ? { resetDateNow } : {}),
|
|
412
|
-
});
|
|
413
|
-
deferred.settled.then(() => {
|
|
414
|
-
context.runtime.executePendingJobs();
|
|
415
|
-
deferred.dispose();
|
|
416
|
-
});
|
|
417
|
-
parentPort?.postMessage({
|
|
418
|
-
type: 'tool-request',
|
|
419
|
-
invocationId,
|
|
420
|
-
requestId,
|
|
421
|
-
...payload,
|
|
422
|
-
});
|
|
423
|
-
return deferred.handle;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function resolveBridgeResponse(
|
|
427
|
-
context: QuickJSAsyncContext,
|
|
428
|
-
deferred: QuickJSDeferredPromise,
|
|
429
|
-
message: WorkerBridgeResponse,
|
|
430
|
-
resetDateNow?: QuickJSHandle,
|
|
431
|
-
): void {
|
|
432
|
-
resetGuestDateNow(context, resetDateNow, message.dateNowMs);
|
|
433
|
-
if (message.success) {
|
|
434
|
-
const value = context.newString(message.valueJson ?? '');
|
|
435
|
-
deferred.resolve(value);
|
|
436
|
-
value.dispose();
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const error = createBridgeErrorHandle(context, message.error);
|
|
440
|
-
deferred.reject(error);
|
|
441
|
-
error.dispose();
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function resetGuestDateNow(
|
|
445
|
-
context: QuickJSAsyncContext,
|
|
446
|
-
resetDateNow: QuickJSHandle | undefined,
|
|
447
|
-
dateNowMs: number,
|
|
448
|
-
): void {
|
|
449
|
-
if (resetDateNow === undefined) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
const value = context.newNumber(dateNowMs);
|
|
453
|
-
try {
|
|
454
|
-
const result = context.callFunction(resetDateNow, context.undefined, value);
|
|
455
|
-
if (result.error) {
|
|
456
|
-
const error = context.dump(result.error);
|
|
457
|
-
if (result.error.alive) {
|
|
458
|
-
result.error.dispose();
|
|
459
|
-
}
|
|
460
|
-
throw toError(error);
|
|
461
|
-
}
|
|
462
|
-
if (result.value.alive) {
|
|
463
|
-
result.value.dispose();
|
|
464
|
-
}
|
|
465
|
-
} finally {
|
|
466
|
-
value.dispose();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function drainPendingJobs(context: QuickJSAsyncContext): void {
|
|
471
|
-
while (context.runtime.hasPendingJob()) {
|
|
472
|
-
const pending = context.runtime.executePendingJobs();
|
|
473
|
-
if ('error' in pending && pending.error) {
|
|
474
|
-
const contextForError =
|
|
475
|
-
'context' in pending.error ? pending.error.context : context;
|
|
476
|
-
const error = contextForError.dump(pending.error);
|
|
477
|
-
if (pending.error.alive) {
|
|
478
|
-
pending.error.dispose();
|
|
479
|
-
}
|
|
480
|
-
throw toError(error);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
async function resolveQuickJSPromise(
|
|
486
|
-
context: QuickJSAsyncContext,
|
|
487
|
-
promiseHandle: QuickJSHandle,
|
|
488
|
-
) {
|
|
489
|
-
const resolved = context.resolvePromise(promiseHandle);
|
|
490
|
-
for (;;) {
|
|
491
|
-
drainPendingJobs(context);
|
|
492
|
-
const result = await Promise.race([
|
|
493
|
-
resolved.then(value => ({ settled: true as const, value })),
|
|
494
|
-
new Promise<{ settled: false }>(resolve =>
|
|
495
|
-
setTimeout(() => resolve({ settled: false }), 0),
|
|
496
|
-
),
|
|
497
|
-
]);
|
|
498
|
-
if (result.settled) {
|
|
499
|
-
drainPendingJobs(context);
|
|
500
|
-
return result.value;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function toError(value: unknown): Error {
|
|
506
|
-
if (
|
|
507
|
-
typeof value === 'object' &&
|
|
508
|
-
value !== null &&
|
|
509
|
-
'message' in value &&
|
|
510
|
-
typeof (value as { message?: unknown }).message === 'string'
|
|
511
|
-
) {
|
|
512
|
-
const errorValue = value as {
|
|
513
|
-
message: string;
|
|
514
|
-
name?: string;
|
|
515
|
-
stack?: string;
|
|
516
|
-
code?: string;
|
|
517
|
-
details?: unknown;
|
|
518
|
-
};
|
|
519
|
-
const error = new Error(errorValue.message);
|
|
520
|
-
if (
|
|
521
|
-
'name' in value &&
|
|
522
|
-
typeof (value as { name?: unknown }).name === 'string'
|
|
523
|
-
) {
|
|
524
|
-
error.name = errorValue.name!;
|
|
525
|
-
}
|
|
526
|
-
if (
|
|
527
|
-
'stack' in value &&
|
|
528
|
-
typeof (value as { stack?: unknown }).stack === 'string'
|
|
529
|
-
) {
|
|
530
|
-
error.stack = (value as { stack: string }).stack;
|
|
531
|
-
}
|
|
532
|
-
if (errorValue.code !== undefined) {
|
|
533
|
-
Object.defineProperty(error, 'code', {
|
|
534
|
-
value: errorValue.code,
|
|
535
|
-
enumerable: true,
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
if (errorValue.details !== undefined) {
|
|
539
|
-
Object.defineProperty(error, 'details', {
|
|
540
|
-
value: errorValue.details,
|
|
541
|
-
enumerable: true,
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
return error;
|
|
545
|
-
}
|
|
546
|
-
return new Error(String(value));
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function createBridgeErrorHandle(
|
|
550
|
-
context: QuickJSAsyncContext,
|
|
551
|
-
error: WorkerBridgeResponse['error'],
|
|
552
|
-
): QuickJSHandle {
|
|
553
|
-
const handle = context.newError(
|
|
554
|
-
error?.message ?? 'Host bridge request failed.',
|
|
555
|
-
);
|
|
556
|
-
if (!error) {
|
|
557
|
-
return handle;
|
|
558
|
-
}
|
|
559
|
-
const name = context.newString(error.name);
|
|
560
|
-
context.setProp(handle, 'name', name);
|
|
561
|
-
name.dispose();
|
|
562
|
-
if (error.code !== undefined) {
|
|
563
|
-
const code = context.newString(error.code);
|
|
564
|
-
context.setProp(handle, 'code', code);
|
|
565
|
-
code.dispose();
|
|
566
|
-
}
|
|
567
|
-
return handle;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function jsToHandle(
|
|
571
|
-
context: QuickJSAsyncContext,
|
|
572
|
-
value: unknown,
|
|
573
|
-
): QuickJSHandle {
|
|
574
|
-
if (value === null || value === undefined) {
|
|
575
|
-
return context.undefined;
|
|
576
|
-
}
|
|
577
|
-
if (typeof value === 'string') {
|
|
578
|
-
return context.newString(value);
|
|
579
|
-
}
|
|
580
|
-
if (typeof value === 'number') {
|
|
581
|
-
return context.newNumber(value);
|
|
582
|
-
}
|
|
583
|
-
if (typeof value === 'boolean') {
|
|
584
|
-
return value ? context.true : context.false;
|
|
585
|
-
}
|
|
586
|
-
if (Array.isArray(value)) {
|
|
587
|
-
const array = context.newArray();
|
|
588
|
-
value.forEach((item, index) => {
|
|
589
|
-
const itemHandle = jsToHandle(context, item);
|
|
590
|
-
context.setProp(array, index, itemHandle);
|
|
591
|
-
itemHandle.dispose();
|
|
592
|
-
});
|
|
593
|
-
return array;
|
|
594
|
-
}
|
|
595
|
-
if (typeof value === 'object') {
|
|
596
|
-
const object = context.newObject();
|
|
597
|
-
for (const [key, item] of Object.entries(
|
|
598
|
-
value as Record<string, unknown>,
|
|
599
|
-
)) {
|
|
600
|
-
const itemHandle = jsToHandle(context, item);
|
|
601
|
-
context.setProp(object, key, itemHandle);
|
|
602
|
-
itemHandle.dispose();
|
|
603
|
-
}
|
|
604
|
-
return object;
|
|
605
|
-
}
|
|
606
|
-
return context.undefined;
|
|
607
|
-
}
|
package/src/utils/options.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { CodeModeOptions, NormalizedCodeModeOptions } from '../types.js';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
4
|
-
const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
5
|
-
const DEFAULT_STACK_LIMIT_BYTES = 2 * 1024 * 1024;
|
|
6
|
-
const DEFAULT_MAX_RESULT_BYTES = 1024 * 1024;
|
|
7
|
-
const DEFAULT_MAX_CONSOLE_OUTPUT_BYTES = 64 * 1024;
|
|
8
|
-
const DEFAULT_MAX_SOURCE_BYTES = 256 * 1024;
|
|
9
|
-
const DEFAULT_MAX_TOOL_INPUT_BYTES = 1024 * 1024;
|
|
10
|
-
const DEFAULT_MAX_TOOL_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
11
|
-
const DEFAULT_MAX_BRIDGE_REQUESTS = 256;
|
|
12
|
-
const DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS = 32;
|
|
13
|
-
|
|
14
|
-
export function normalizeOptions(
|
|
15
|
-
options: CodeModeOptions = {},
|
|
16
|
-
): NormalizedCodeModeOptions {
|
|
17
|
-
const executionPolicy = options.executionPolicy ?? {};
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
timeoutMs: positiveInteger(
|
|
21
|
-
executionPolicy.timeoutMs,
|
|
22
|
-
DEFAULT_TIMEOUT_MS,
|
|
23
|
-
'executionPolicy.timeoutMs',
|
|
24
|
-
),
|
|
25
|
-
memoryLimitBytes: positiveInteger(
|
|
26
|
-
executionPolicy.memoryLimitBytes,
|
|
27
|
-
DEFAULT_MEMORY_LIMIT_BYTES,
|
|
28
|
-
'executionPolicy.memoryLimitBytes',
|
|
29
|
-
),
|
|
30
|
-
maxStackSizeBytes: positiveInteger(
|
|
31
|
-
executionPolicy.maxStackSizeBytes,
|
|
32
|
-
DEFAULT_STACK_LIMIT_BYTES,
|
|
33
|
-
'executionPolicy.maxStackSizeBytes',
|
|
34
|
-
),
|
|
35
|
-
maxResultBytes: positiveInteger(
|
|
36
|
-
executionPolicy.maxResultBytes,
|
|
37
|
-
DEFAULT_MAX_RESULT_BYTES,
|
|
38
|
-
'executionPolicy.maxResultBytes',
|
|
39
|
-
),
|
|
40
|
-
maxConsoleOutputBytes: positiveInteger(
|
|
41
|
-
executionPolicy.maxConsoleOutputBytes,
|
|
42
|
-
DEFAULT_MAX_CONSOLE_OUTPUT_BYTES,
|
|
43
|
-
'executionPolicy.maxConsoleOutputBytes',
|
|
44
|
-
),
|
|
45
|
-
maxSourceBytes: positiveInteger(
|
|
46
|
-
executionPolicy.maxSourceBytes,
|
|
47
|
-
DEFAULT_MAX_SOURCE_BYTES,
|
|
48
|
-
'executionPolicy.maxSourceBytes',
|
|
49
|
-
),
|
|
50
|
-
maxToolInputBytes: positiveInteger(
|
|
51
|
-
executionPolicy.maxToolInputBytes,
|
|
52
|
-
DEFAULT_MAX_TOOL_INPUT_BYTES,
|
|
53
|
-
'executionPolicy.maxToolInputBytes',
|
|
54
|
-
),
|
|
55
|
-
maxToolOutputBytes: positiveInteger(
|
|
56
|
-
executionPolicy.maxToolOutputBytes,
|
|
57
|
-
DEFAULT_MAX_TOOL_OUTPUT_BYTES,
|
|
58
|
-
'executionPolicy.maxToolOutputBytes',
|
|
59
|
-
),
|
|
60
|
-
maxBridgeRequests: positiveInteger(
|
|
61
|
-
executionPolicy.maxBridgeRequests,
|
|
62
|
-
DEFAULT_MAX_BRIDGE_REQUESTS,
|
|
63
|
-
'executionPolicy.maxBridgeRequests',
|
|
64
|
-
),
|
|
65
|
-
maxInFlightBridgeRequests: positiveInteger(
|
|
66
|
-
executionPolicy.maxInFlightBridgeRequests,
|
|
67
|
-
DEFAULT_MAX_IN_FLIGHT_BRIDGE_REQUESTS,
|
|
68
|
-
'executionPolicy.maxInFlightBridgeRequests',
|
|
69
|
-
),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function positiveInteger(
|
|
74
|
-
value: number | undefined,
|
|
75
|
-
fallback: number,
|
|
76
|
-
path: string,
|
|
77
|
-
): number {
|
|
78
|
-
const resolved = value ?? fallback;
|
|
79
|
-
if (!Number.isInteger(resolved) || resolved <= 0) {
|
|
80
|
-
throw new TypeError(`${path} must be a positive integer.`);
|
|
81
|
-
}
|
|
82
|
-
return resolved;
|
|
83
|
-
}
|