@trim21/personal-pi-extensions 0.1.654 → 0.1.655
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 +25 -0
- package/package.json +4 -2
- package/src/codemode/declarations.ts +103 -0
- package/src/codemode/prelude.ts +298 -0
- package/src/codemode/protocol.ts +156 -0
- package/src/codemode/sandbox.ts +192 -0
- package/src/codemode/source.ts +91 -0
- package/src/codemode/tool.ts +353 -0
- package/src/codemode/wasm.ts +20 -0
- package/src/codemode/worker.js +583 -0
- package/src/codemode/worker.ts +279 -0
- package/src/index.ts +13 -0
- package/src/lib/tool-bus.ts +7 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// src/codemode/worker.ts
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
import { JSException, MAX_STACK_SIZE, QuickJS } from "quickjs-wasi";
|
|
4
|
+
|
|
5
|
+
// src/codemode/prelude.ts
|
|
6
|
+
var MAX_STORE_VALUE_CHARS = 256 * 1024;
|
|
7
|
+
var MAX_STORE_TOTAL_CHARS = 1024 * 1024;
|
|
8
|
+
var IMAGE_HELPER_EXPECTS =
|
|
9
|
+
"image expects a non-empty image URL string, an object with image_url, or a raw MCP image block";
|
|
10
|
+
var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
11
|
+
"use strict";
|
|
12
|
+
const stringify = JSON.stringify;
|
|
13
|
+
const parse = JSON.parse;
|
|
14
|
+
const promiseThen = Promise.prototype.then;
|
|
15
|
+
const ErrorCtor = Error;
|
|
16
|
+
const TypeErrorCtor = TypeError;
|
|
17
|
+
const pending = new Map();
|
|
18
|
+
let nextId = 1;
|
|
19
|
+
let finished = false;
|
|
20
|
+
// exit() 用它解开脚本栈:成功结果已经报过了。
|
|
21
|
+
const EXIT = Object.freeze({});
|
|
22
|
+
|
|
23
|
+
function done(ok, payload, writes) {
|
|
24
|
+
if (finished) return;
|
|
25
|
+
finished = true;
|
|
26
|
+
bridge("done", ok, payload, writes);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function serialize(value) {
|
|
30
|
+
return value === undefined ? undefined : stringify(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// QuickJS 的 stack 只有帧,没有 "Name: message" 头;补上并去掉本 prelude 的帧,
|
|
34
|
+
// 读起来与 Node 的报错一致。
|
|
35
|
+
function errorText(error) {
|
|
36
|
+
const head = error.message ? error.name + ": " + error.message : String(error.name);
|
|
37
|
+
const frames =
|
|
38
|
+
typeof error.stack === "string"
|
|
39
|
+
? error.stack.split("\n").filter((line) => line.trim() && !line.includes("codemode-prelude.js"))
|
|
40
|
+
: [];
|
|
41
|
+
return [head, ...frames].join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function format(value) {
|
|
45
|
+
if (typeof value === "string") return value;
|
|
46
|
+
if (value instanceof ErrorCtor) return errorText(value);
|
|
47
|
+
try {
|
|
48
|
+
const json = stringify(value);
|
|
49
|
+
return json === undefined ? String(value) : json;
|
|
50
|
+
} catch {
|
|
51
|
+
return String(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function describeError(error) {
|
|
56
|
+
if (error instanceof ErrorCtor) {
|
|
57
|
+
return stringify({ name: error.name, message: error.message, stack: errorText(error) });
|
|
58
|
+
}
|
|
59
|
+
return stringify({ message: format(error) });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function caller(name) {
|
|
63
|
+
return (args) =>
|
|
64
|
+
new Promise((resolve, reject) => {
|
|
65
|
+
let json;
|
|
66
|
+
try {
|
|
67
|
+
json = serialize(args);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
reject(error);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const id = nextId++;
|
|
73
|
+
pending.set(id, { resolve, reject });
|
|
74
|
+
bridge("call", id, name, json);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const tools = Object.create(null);
|
|
79
|
+
const allTools = [];
|
|
80
|
+
for (const { name, jsName, description } of parse(toolsJson)) {
|
|
81
|
+
const fn = caller(name);
|
|
82
|
+
// 两个名字归一化成同一个标识符时,第一个赢
|
|
83
|
+
if (!(jsName in tools)) {
|
|
84
|
+
tools[jsName] = fn;
|
|
85
|
+
allTools.push(Object.freeze({ name: jsName, description }));
|
|
86
|
+
}
|
|
87
|
+
if (!(name in tools)) tools[name] = fn;
|
|
88
|
+
}
|
|
89
|
+
Object.freeze(tools);
|
|
90
|
+
Object.freeze(allTools);
|
|
91
|
+
|
|
92
|
+
// key -> JSON 文本;容量按 key 与 JSON 的字符数计
|
|
93
|
+
const stored = new Map(Object.entries(parse(storeJson)));
|
|
94
|
+
const writes = new Map();
|
|
95
|
+
let storedChars = 0;
|
|
96
|
+
for (const [key, json] of stored) storedChars += key.length + json.length;
|
|
97
|
+
|
|
98
|
+
function checkKey(name, key) {
|
|
99
|
+
if (typeof key !== "string") throw new TypeErrorCtor(name + "() key must be a string");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function store(key, value) {
|
|
103
|
+
checkKey("store", key);
|
|
104
|
+
const previous = stored.has(key) ? key.length + stored.get(key).length : 0;
|
|
105
|
+
if (value === undefined) {
|
|
106
|
+
stored.delete(key);
|
|
107
|
+
storedChars -= previous;
|
|
108
|
+
writes.set(key, undefined);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let json;
|
|
112
|
+
try {
|
|
113
|
+
json = stringify(value);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new TypeErrorCtor("store(" + stringify(key) + ") value is not JSON-serializable: " + format(error));
|
|
116
|
+
}
|
|
117
|
+
if (json === undefined) {
|
|
118
|
+
throw new TypeErrorCtor("store(" + stringify(key) + ") value is not JSON-serializable");
|
|
119
|
+
}
|
|
120
|
+
if (json.length > ${MAX_STORE_VALUE_CHARS}) {
|
|
121
|
+
throw new RangeError("store(" + stringify(key) + ") value exceeds ${MAX_STORE_VALUE_CHARS} characters of JSON");
|
|
122
|
+
}
|
|
123
|
+
const next = storedChars - previous + key.length + json.length;
|
|
124
|
+
if (next > ${MAX_STORE_TOTAL_CHARS}) {
|
|
125
|
+
throw new RangeError("store is full: stored values would exceed ${MAX_STORE_TOTAL_CHARS} characters of JSON");
|
|
126
|
+
}
|
|
127
|
+
stored.set(key, json);
|
|
128
|
+
storedChars = next;
|
|
129
|
+
writes.set(key, json);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function load(key) {
|
|
133
|
+
checkKey("load", key);
|
|
134
|
+
const json = stored.get(key);
|
|
135
|
+
return json === undefined ? undefined : parse(json);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function serializeWrites() {
|
|
139
|
+
const entries = [];
|
|
140
|
+
for (const [key, json] of writes) entries.push(json === undefined ? [key] : [key, json]);
|
|
141
|
+
return stringify(entries);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Object.defineProperty(globalThis, "store", { value: store, enumerable: true });
|
|
145
|
+
Object.defineProperty(globalThis, "load", { value: load, enumerable: true });
|
|
146
|
+
|
|
147
|
+
// 原始值转字符串,其余 JSON 化
|
|
148
|
+
function outputText(value) {
|
|
149
|
+
if (value === undefined || value === null || (typeof value !== "object" && typeof value !== "function")) {
|
|
150
|
+
return String(value);
|
|
151
|
+
}
|
|
152
|
+
const json = stringify(value);
|
|
153
|
+
return json === undefined ? String(value) : json;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function text(value) {
|
|
157
|
+
let rendered;
|
|
158
|
+
try {
|
|
159
|
+
rendered = outputText(value);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new TypeErrorCtor(error instanceof ErrorCtor ? error.message : String(error));
|
|
162
|
+
}
|
|
163
|
+
if (!finished) bridge("output", "text", rendered);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function imageUrl(value) {
|
|
167
|
+
if (typeof value === "string") return value;
|
|
168
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
169
|
+
throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
170
|
+
}
|
|
171
|
+
if (value.image_url !== undefined) {
|
|
172
|
+
if (typeof value.image_url !== "string") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
173
|
+
return value.image_url;
|
|
174
|
+
}
|
|
175
|
+
if (typeof value.type !== "string") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
176
|
+
if (value.type !== "image") {
|
|
177
|
+
throw new TypeErrorCtor('image only accepts MCP image blocks, got "' + value.type + '"');
|
|
178
|
+
}
|
|
179
|
+
if (typeof value.data !== "string" || value.data === "") throw new TypeErrorCtor("image expected MCP image data");
|
|
180
|
+
if (value.data.toLowerCase().startsWith("data:")) return value.data;
|
|
181
|
+
const mimeType = typeof value.mimeType === "string" && value.mimeType ? value.mimeType : "application/octet-stream";
|
|
182
|
+
return "data:" + mimeType + ";base64," + value.data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function image(value) {
|
|
186
|
+
const url = imageUrl(value);
|
|
187
|
+
if (url === "") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
188
|
+
const colon = url.indexOf(":");
|
|
189
|
+
const scheme = colon === -1 ? "" : url.slice(0, colon).toLowerCase();
|
|
190
|
+
if (scheme === "http" || scheme === "https") {
|
|
191
|
+
throw new TypeErrorCtor("remote image URLs are not supported in tool outputs. Pass a base64 data URI instead");
|
|
192
|
+
}
|
|
193
|
+
const comma = url.indexOf(",");
|
|
194
|
+
const header = comma === -1 ? [] : url.slice(colon + 1, comma).split(";");
|
|
195
|
+
if (scheme !== "data" || comma === -1 || header.slice(1).every((part) => part.toLowerCase() !== "base64")) {
|
|
196
|
+
throw new TypeErrorCtor("invalid image output. Pass a base64 data URI instead");
|
|
197
|
+
}
|
|
198
|
+
if (!finished) bridge("output", "image", url.slice(comma + 1), header[0] || "application/octet-stream");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function exit() {
|
|
202
|
+
let writesJson;
|
|
203
|
+
try {
|
|
204
|
+
writesJson = serializeWrites();
|
|
205
|
+
} catch (error) {
|
|
206
|
+
done(false, describeError(error));
|
|
207
|
+
throw EXIT;
|
|
208
|
+
}
|
|
209
|
+
done(true, undefined, writesJson);
|
|
210
|
+
throw EXIT;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const console = {};
|
|
214
|
+
for (const level of ["log", "info", "warn", "error", "debug"]) {
|
|
215
|
+
console[level] = (...args) => {
|
|
216
|
+
if (!finished) bridge("output", "text", args.map(format).join(" "));
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
Object.freeze(console);
|
|
220
|
+
|
|
221
|
+
Object.defineProperty(globalThis, "tools", { value: tools, enumerable: true });
|
|
222
|
+
Object.defineProperty(globalThis, "ALL_TOOLS", { value: allTools, enumerable: true });
|
|
223
|
+
Object.defineProperty(globalThis, "console", { value: console, enumerable: true });
|
|
224
|
+
Object.defineProperty(globalThis, "text", { value: text, enumerable: true });
|
|
225
|
+
Object.defineProperty(globalThis, "image", { value: image, enumerable: true });
|
|
226
|
+
Object.defineProperty(globalThis, "exit", { value: exit, enumerable: true });
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
settle(id, ok, payload) {
|
|
230
|
+
const entry = pending.get(id);
|
|
231
|
+
if (!entry) return;
|
|
232
|
+
pending.delete(id);
|
|
233
|
+
if (!ok) {
|
|
234
|
+
entry.reject(new ErrorCtor(payload));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
let value;
|
|
238
|
+
try {
|
|
239
|
+
value = payload === undefined ? undefined : parse(payload);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
entry.reject(error);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
entry.resolve(value);
|
|
245
|
+
},
|
|
246
|
+
run(fn) {
|
|
247
|
+
let promise;
|
|
248
|
+
try {
|
|
249
|
+
promise = fn(tools, console);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
done(false, describeError(error));
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
promiseThen.call(
|
|
255
|
+
promise,
|
|
256
|
+
(value) => {
|
|
257
|
+
let json;
|
|
258
|
+
try {
|
|
259
|
+
json = serialize(value);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
done(false, describeError(error));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
done(true, json, serializeWrites());
|
|
265
|
+
},
|
|
266
|
+
(error) => {
|
|
267
|
+
done(false, describeError(error));
|
|
268
|
+
},
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
stalled() {
|
|
272
|
+
if (finished || pending.size > 0) return false;
|
|
273
|
+
done(
|
|
274
|
+
false,
|
|
275
|
+
stringify({
|
|
276
|
+
name: "Error",
|
|
277
|
+
message:
|
|
278
|
+
"The script is waiting on a promise that can never settle: no tool call is pending, and timers do not exist here.",
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
return true;
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
})`;
|
|
285
|
+
|
|
286
|
+
// src/codemode/protocol.ts
|
|
287
|
+
import { Type } from "typebox";
|
|
288
|
+
import { Value } from "typebox/value";
|
|
289
|
+
var outputItemSchema = Type.Union([
|
|
290
|
+
Type.Object({ type: Type.Literal("text"), text: Type.String() }),
|
|
291
|
+
Type.Object({ type: Type.Literal("image"), data: Type.String(), mimeType: Type.String() }),
|
|
292
|
+
]);
|
|
293
|
+
var toolDeclSchema = Type.Object({
|
|
294
|
+
name: Type.String(),
|
|
295
|
+
description: Type.Optional(Type.String()),
|
|
296
|
+
});
|
|
297
|
+
var startSchema = Type.Object({
|
|
298
|
+
t: Type.Literal("start"),
|
|
299
|
+
code: Type.String(),
|
|
300
|
+
tools: Type.Array(toolDeclSchema),
|
|
301
|
+
store: Type.Record(Type.String(), Type.Unknown()),
|
|
302
|
+
});
|
|
303
|
+
var resultSchema = Type.Union([
|
|
304
|
+
Type.Object({
|
|
305
|
+
t: Type.Literal("result"),
|
|
306
|
+
id: Type.Number(),
|
|
307
|
+
ok: Type.Literal(true),
|
|
308
|
+
value: Type.Unknown(),
|
|
309
|
+
}),
|
|
310
|
+
Type.Object({
|
|
311
|
+
t: Type.Literal("result"),
|
|
312
|
+
id: Type.Number(),
|
|
313
|
+
ok: Type.Literal(false),
|
|
314
|
+
error: Type.String(),
|
|
315
|
+
}),
|
|
316
|
+
]);
|
|
317
|
+
var callSchema = Type.Object({
|
|
318
|
+
t: Type.Literal("call"),
|
|
319
|
+
id: Type.Number(),
|
|
320
|
+
name: Type.String(),
|
|
321
|
+
args: Type.Unknown(),
|
|
322
|
+
});
|
|
323
|
+
var outputSchema = Type.Object({
|
|
324
|
+
t: Type.Literal("output"),
|
|
325
|
+
items: Type.Array(outputItemSchema),
|
|
326
|
+
});
|
|
327
|
+
var scriptErrorSchema = Type.Object({
|
|
328
|
+
kind: Type.Union([
|
|
329
|
+
Type.Literal("script"),
|
|
330
|
+
Type.Literal("timeout"),
|
|
331
|
+
Type.Literal("aborted"),
|
|
332
|
+
Type.Literal("sandbox"),
|
|
333
|
+
]),
|
|
334
|
+
name: Type.Optional(Type.String()),
|
|
335
|
+
message: Type.String(),
|
|
336
|
+
stack: Type.Optional(Type.String()),
|
|
337
|
+
});
|
|
338
|
+
var storeWritesSchema = Type.Object({
|
|
339
|
+
set: Type.Record(Type.String(), Type.Unknown()),
|
|
340
|
+
delete: Type.Array(Type.String()),
|
|
341
|
+
});
|
|
342
|
+
var doneSchema = Type.Union([
|
|
343
|
+
Type.Object({
|
|
344
|
+
t: Type.Literal("done"),
|
|
345
|
+
ok: Type.Literal(true),
|
|
346
|
+
value: Type.Unknown(),
|
|
347
|
+
writes: storeWritesSchema,
|
|
348
|
+
}),
|
|
349
|
+
Type.Object({
|
|
350
|
+
t: Type.Literal("done"),
|
|
351
|
+
ok: Type.Literal(false),
|
|
352
|
+
error: scriptErrorSchema,
|
|
353
|
+
writes: storeWritesSchema,
|
|
354
|
+
}),
|
|
355
|
+
]);
|
|
356
|
+
var hostMessageSchema = Type.Union([startSchema, resultSchema]);
|
|
357
|
+
var workerMessageSchema = Type.Union([callSchema, outputSchema, doneSchema]);
|
|
358
|
+
function decode(schema, value) {
|
|
359
|
+
if (!Value.Check(schema, value)) {
|
|
360
|
+
const preview = JSON.stringify(value).slice(0, 200);
|
|
361
|
+
return { ok: false, error: `unexpected message shape: ${preview}` };
|
|
362
|
+
}
|
|
363
|
+
return { ok: true, frame: value };
|
|
364
|
+
}
|
|
365
|
+
function decodeHostMessage(value) {
|
|
366
|
+
return decode(hostMessageSchema, value);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/codemode/worker.ts
|
|
370
|
+
var MEMORY_LIMIT_BYTES = 512 * 1024 * 1024;
|
|
371
|
+
function toScriptIdentifier(name) {
|
|
372
|
+
return name.replaceAll(/[^A-Za-z0-9_$]/g, "_").replaceAll(/^(\d)/, "_$1");
|
|
373
|
+
}
|
|
374
|
+
function discardOutput(memory) {
|
|
375
|
+
return {
|
|
376
|
+
fd_write(_fd, iovsPtr, iovsLen, nwrittenPtr) {
|
|
377
|
+
const view = new DataView(memory.buffer);
|
|
378
|
+
let written = 0;
|
|
379
|
+
for (let index = 0; index < iovsLen; index++) {
|
|
380
|
+
written += view.getUint32(iovsPtr + index * 8 + 4, true);
|
|
381
|
+
}
|
|
382
|
+
view.setUint32(nwrittenPtr, written, true);
|
|
383
|
+
return 0;
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function post(message) {
|
|
388
|
+
parentPort?.postMessage(message);
|
|
389
|
+
}
|
|
390
|
+
function emptyWrites() {
|
|
391
|
+
return { set: {}, delete: [] };
|
|
392
|
+
}
|
|
393
|
+
function parseWrites(json) {
|
|
394
|
+
try {
|
|
395
|
+
const entries = JSON.parse(json);
|
|
396
|
+
const writes = { set: {}, delete: [] };
|
|
397
|
+
for (const [key, value] of entries) {
|
|
398
|
+
if (value === void 0) {
|
|
399
|
+
writes.delete.push(key);
|
|
400
|
+
} else {
|
|
401
|
+
writes.set[key] = JSON.parse(value);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return writes;
|
|
405
|
+
} catch {
|
|
406
|
+
return emptyWrites();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function scriptError(payload, kind) {
|
|
410
|
+
if (payload === void 0) {
|
|
411
|
+
return { kind, message: "the script failed without an error message" };
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const parsed = JSON.parse(payload);
|
|
415
|
+
return { kind, name: parsed.name, message: parsed.message ?? payload, stack: parsed.stack };
|
|
416
|
+
} catch {
|
|
417
|
+
return { kind, message: payload };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function runScript(wasm, start) {
|
|
421
|
+
const port = parentPort;
|
|
422
|
+
if (!port) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const vm = await QuickJS.create({
|
|
426
|
+
wasm,
|
|
427
|
+
memoryLimit: MEMORY_LIMIT_BYTES,
|
|
428
|
+
// 没有这个上限时深递归会打穿 wasm 栈变成 trap,而不是脚本里可捕获的 RangeError
|
|
429
|
+
maxStackSize: MAX_STACK_SIZE,
|
|
430
|
+
wasi: discardOutput,
|
|
431
|
+
});
|
|
432
|
+
const bridge = vm.newFunction("bridge", (kind, a, b, c) => {
|
|
433
|
+
const stringOr = (value, fallback) =>
|
|
434
|
+
value === void 0 || value.isUndefined ? fallback : value.toString();
|
|
435
|
+
switch (kind.toString()) {
|
|
436
|
+
case "call": {
|
|
437
|
+
post({
|
|
438
|
+
t: "call",
|
|
439
|
+
id: a.toNumber(),
|
|
440
|
+
name: stringOr(b, ""),
|
|
441
|
+
args: c === void 0 || c.isUndefined ? void 0 : JSON.parse(c.toString()),
|
|
442
|
+
});
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
case "output": {
|
|
446
|
+
post({
|
|
447
|
+
t: "output",
|
|
448
|
+
items:
|
|
449
|
+
a.toString() === "image"
|
|
450
|
+
? [
|
|
451
|
+
{
|
|
452
|
+
type: "image",
|
|
453
|
+
data: stringOr(b, ""),
|
|
454
|
+
mimeType: stringOr(c, "application/octet-stream"),
|
|
455
|
+
},
|
|
456
|
+
]
|
|
457
|
+
: [{ type: "text", text: stringOr(b, "") }],
|
|
458
|
+
});
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
case "done": {
|
|
462
|
+
const writes = c === void 0 || c.isUndefined ? emptyWrites() : parseWrites(c.toString());
|
|
463
|
+
if (a.toBoolean()) {
|
|
464
|
+
post({
|
|
465
|
+
t: "done",
|
|
466
|
+
ok: true,
|
|
467
|
+
value: b === void 0 || b.isUndefined ? void 0 : JSON.parse(b.toString()),
|
|
468
|
+
writes,
|
|
469
|
+
});
|
|
470
|
+
} else {
|
|
471
|
+
post({
|
|
472
|
+
t: "done",
|
|
473
|
+
ok: false,
|
|
474
|
+
error: scriptError(b === void 0 ? void 0 : b.toString(), "script"),
|
|
475
|
+
writes,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return vm.undefined;
|
|
482
|
+
});
|
|
483
|
+
const api = vm.withScope((scope) =>
|
|
484
|
+
scope.escape(
|
|
485
|
+
vm.callFunction(
|
|
486
|
+
vm.evalCode(PRELUDE_SOURCE, "codemode-prelude.js"),
|
|
487
|
+
vm.undefined,
|
|
488
|
+
bridge,
|
|
489
|
+
vm.newString(
|
|
490
|
+
JSON.stringify(
|
|
491
|
+
start.tools.map((tool) => ({
|
|
492
|
+
name: tool.name,
|
|
493
|
+
jsName: toScriptIdentifier(tool.name),
|
|
494
|
+
description: tool.description,
|
|
495
|
+
})),
|
|
496
|
+
),
|
|
497
|
+
),
|
|
498
|
+
vm.newString(JSON.stringify(start.store)),
|
|
499
|
+
),
|
|
500
|
+
),
|
|
501
|
+
);
|
|
502
|
+
const settle = api.getProp("settle");
|
|
503
|
+
const run = api.getProp("run");
|
|
504
|
+
const stalled = api.getProp("stalled");
|
|
505
|
+
const drain = () => {
|
|
506
|
+
vm.executePendingJobs();
|
|
507
|
+
vm.callFunction(stalled, api).dispose();
|
|
508
|
+
};
|
|
509
|
+
port.on("message", (value) => {
|
|
510
|
+
const message = decodeHostMessage(value);
|
|
511
|
+
if (!message.ok || message.frame.t !== "result") {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const result = message.frame;
|
|
515
|
+
try {
|
|
516
|
+
vm.withScope(() => {
|
|
517
|
+
vm.callFunction(
|
|
518
|
+
settle,
|
|
519
|
+
api,
|
|
520
|
+
vm.newNumber(result.id),
|
|
521
|
+
result.ok ? vm.true : vm.false,
|
|
522
|
+
result.ok
|
|
523
|
+
? result.value === void 0
|
|
524
|
+
? vm.undefined
|
|
525
|
+
: vm.newString(JSON.stringify(result.value))
|
|
526
|
+
: vm.newString(result.error),
|
|
527
|
+
);
|
|
528
|
+
});
|
|
529
|
+
} catch {}
|
|
530
|
+
drain();
|
|
531
|
+
});
|
|
532
|
+
try {
|
|
533
|
+
const fn = vm.evalCode(
|
|
534
|
+
`(async (tools, console) => {${start.code}
|
|
535
|
+
})`,
|
|
536
|
+
"codemode.js",
|
|
537
|
+
);
|
|
538
|
+
vm.callFunction(run, api, fn).dispose();
|
|
539
|
+
fn.dispose();
|
|
540
|
+
drain();
|
|
541
|
+
} catch (error) {
|
|
542
|
+
if (error instanceof JSException) {
|
|
543
|
+
post({
|
|
544
|
+
t: "done",
|
|
545
|
+
ok: false,
|
|
546
|
+
error: scriptError(
|
|
547
|
+
JSON.stringify({ name: error.name, message: error.message, stack: error.stack }),
|
|
548
|
+
"script",
|
|
549
|
+
),
|
|
550
|
+
writes: emptyWrites(),
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
post({
|
|
554
|
+
t: "done",
|
|
555
|
+
ok: false,
|
|
556
|
+
error: { kind: "sandbox", message: error instanceof Error ? error.message : String(error) },
|
|
557
|
+
writes: emptyWrites(),
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function main() {
|
|
563
|
+
const port = parentPort;
|
|
564
|
+
if (!port) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const { wasm } = workerData;
|
|
568
|
+
port.once("message", (value) => {
|
|
569
|
+
const message = decodeHostMessage(value);
|
|
570
|
+
if (!message.ok || message.frame.t !== "start") {
|
|
571
|
+
post({
|
|
572
|
+
t: "done",
|
|
573
|
+
ok: false,
|
|
574
|
+
error: { kind: "sandbox", message: "codemode worker: expected a start message first" },
|
|
575
|
+
writes: emptyWrites(),
|
|
576
|
+
});
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
void runScript(wasm, message.frame);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
main();
|
|
583
|
+
export { toScriptIdentifier };
|