@supertools-ai/core 0.1.0 → 0.1.2

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/dist/index.mjs DELETED
@@ -1,937 +0,0 @@
1
- // src/tool.ts
2
- import { z } from "zod";
3
- var TOOL_BRAND = /* @__PURE__ */ Symbol.for("supertools.tool");
4
- function defineTool(definition) {
5
- if (!definition.name || typeof definition.name !== "string") {
6
- throw new Error("Tool name is required");
7
- }
8
- if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(definition.name)) {
9
- throw new Error(`Invalid tool name "${definition.name}": must start with a letter and contain only alphanumeric characters and underscores`);
10
- }
11
- if (!definition.description || typeof definition.description !== "string") {
12
- throw new Error("Tool description is required");
13
- }
14
- if (definition.description.length < 5) {
15
- throw new Error("Tool description must be at least 5 characters");
16
- }
17
- if (!isZodSchema(definition.parameters)) {
18
- throw new Error("Tool parameters must be a Zod schema");
19
- }
20
- if (getZodType(definition.parameters) !== "object") {
21
- throw new Error(`Tool parameters must be z.object(), got z.${getZodType(definition.parameters)}()`);
22
- }
23
- if (typeof definition.execute !== "function") {
24
- throw new Error("Tool execute must be a function");
25
- }
26
- return Object.freeze({
27
- name: definition.name,
28
- description: definition.description,
29
- parameters: definition.parameters,
30
- execute: definition.execute,
31
- [TOOL_BRAND]: true
32
- });
33
- }
34
- function isTool(value) {
35
- return typeof value === "object" && value !== null && TOOL_BRAND in value && value[TOOL_BRAND] === true;
36
- }
37
- function normalizeTools(tools) {
38
- return tools.map((tool) => {
39
- if (!isTool(tool)) {
40
- throw new TypeError(`Expected Tool from defineTool(), got ${typeof tool}`);
41
- }
42
- return Object.freeze({
43
- name: toSnakeCase(tool.name),
44
- description: tool.description,
45
- parameters: Object.freeze(extractParameters(tool.parameters)),
46
- execute: tool.execute
47
- });
48
- });
49
- }
50
- function isZodSchema(value) {
51
- return typeof value === "object" && value !== null && "_zod" in value;
52
- }
53
- function getZodDef(schema) {
54
- const s = schema;
55
- if (!s._zod?.def) {
56
- throw new Error("Invalid Zod schema: missing _zod.def");
57
- }
58
- return s._zod.def;
59
- }
60
- function getZodType(schema) {
61
- const def = getZodDef(schema);
62
- if (typeof def.type !== "string") {
63
- throw new Error("Invalid Zod schema: missing type");
64
- }
65
- return def.type;
66
- }
67
- function extractParameters(schema) {
68
- if (getZodType(schema) !== "object") {
69
- return [parseField("input", schema)];
70
- }
71
- const def = getZodDef(schema);
72
- const shape = def.shape;
73
- const fields = typeof shape === "function" ? shape() : shape;
74
- if (!fields || typeof fields !== "object") {
75
- throw new Error("Invalid Zod object schema: missing shape");
76
- }
77
- return Object.entries(fields).map(([name, fieldSchema]) => parseField(name, fieldSchema));
78
- }
79
- function parseField(name, schema) {
80
- let current = schema;
81
- let isOptional2 = false;
82
- let defaultValue;
83
- let description = "";
84
- for (let depth = 0; depth < 10; depth++) {
85
- if (current.description) {
86
- description = current.description;
87
- }
88
- const type = getZodType(current);
89
- const def = getZodDef(current);
90
- const inner = def.innerType;
91
- if (type === "optional" && inner) {
92
- isOptional2 = true;
93
- current = inner;
94
- } else if (type === "default" && inner) {
95
- isOptional2 = true;
96
- const defaultFn = def.defaultValue;
97
- defaultValue = typeof defaultFn === "function" ? defaultFn() : defaultFn;
98
- current = inner;
99
- } else if (type === "nullable" && inner) {
100
- isOptional2 = true;
101
- current = inner;
102
- } else {
103
- break;
104
- }
105
- }
106
- return Object.freeze({
107
- name,
108
- type: inferBaseType(current),
109
- description,
110
- required: !isOptional2,
111
- default: defaultValue
112
- });
113
- }
114
- function inferBaseType(schema) {
115
- const type = getZodType(schema);
116
- const def = getZodDef(schema);
117
- switch (type) {
118
- case "string":
119
- case "enum":
120
- return "string";
121
- case "number": {
122
- const checks = def.checks;
123
- return checks?.some((c) => c.isInt) ? "integer" : "number";
124
- }
125
- case "bigint":
126
- return "integer";
127
- case "boolean":
128
- return "boolean";
129
- case "array":
130
- case "tuple":
131
- case "set":
132
- return "array";
133
- case "object":
134
- case "record":
135
- case "map":
136
- return "object";
137
- case "literal": {
138
- const val = def.value;
139
- if (typeof val === "string") return "string";
140
- if (typeof val === "number") return "number";
141
- if (typeof val === "boolean") return "boolean";
142
- return "any";
143
- }
144
- default:
145
- return "any";
146
- }
147
- }
148
- function toSnakeCase(str) {
149
- return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/-/g, "_").toLowerCase();
150
- }
151
-
152
- // src/executor.ts
153
- import { randomUUID } from "crypto";
154
- import { Sandbox } from "@e2b/code-interpreter";
155
-
156
- // src/relay/client.ts
157
- import WebSocket from "ws";
158
-
159
- // src/relay/protocol.ts
160
- import { z as z2 } from "zod";
161
-
162
- // src/errors.ts
163
- var OPTError = class extends Error {
164
- code;
165
- cause;
166
- constructor(message, code, cause) {
167
- super(message);
168
- this.name = "OPTError";
169
- this.code = code;
170
- this.cause = cause;
171
- Error.captureStackTrace?.(this, this.constructor);
172
- }
173
- toJSON() {
174
- return {
175
- name: this.name,
176
- code: this.code,
177
- message: this.message,
178
- cause: this.cause?.message
179
- };
180
- }
181
- };
182
- var CodeGenerationError = class extends OPTError {
183
- constructor(message, cause) {
184
- super(message, "CODE_GENERATION_ERROR", cause);
185
- this.name = "CodeGenerationError";
186
- }
187
- };
188
- var ExecutionError = class extends OPTError {
189
- output;
190
- constructor(message, output, cause) {
191
- super(message, "EXECUTION_ERROR", cause);
192
- this.name = "ExecutionError";
193
- this.output = output;
194
- }
195
- toJSON() {
196
- return { ...super.toJSON(), output: this.output };
197
- }
198
- };
199
- var ToolError = class extends OPTError {
200
- toolName;
201
- constructor(message, toolName2, cause) {
202
- super(message, "TOOL_ERROR", cause);
203
- this.name = "ToolError";
204
- this.toolName = toolName2;
205
- }
206
- toJSON() {
207
- return { ...super.toJSON(), toolName: this.toolName };
208
- }
209
- };
210
- var RelayConnectionError = class extends OPTError {
211
- constructor(message, cause) {
212
- super(message, "RELAY_CONNECTION_ERROR", cause);
213
- this.name = "RelayConnectionError";
214
- }
215
- };
216
- var RelayTimeoutError = class extends OPTError {
217
- constructor(message) {
218
- super(message, "RELAY_TIMEOUT_ERROR");
219
- this.name = "RelayTimeoutError";
220
- }
221
- };
222
- var SandboxError = class extends OPTError {
223
- constructor(message, cause) {
224
- super(message, "SANDBOX_ERROR", cause);
225
- this.name = "SandboxError";
226
- }
227
- };
228
- var AuthenticationError = class extends OPTError {
229
- constructor(message) {
230
- super(message, "AUTHENTICATION_ERROR");
231
- this.name = "AuthenticationError";
232
- }
233
- };
234
- var ConfigurationError = class extends OPTError {
235
- constructor(message) {
236
- super(message, "CONFIGURATION_ERROR");
237
- this.name = "ConfigurationError";
238
- }
239
- };
240
-
241
- // src/relay/protocol.ts
242
- var ProtocolError = class extends OPTError {
243
- protocolCode;
244
- zodError;
245
- constructor(message, code, zodError) {
246
- super(message, "PROTOCOL_ERROR");
247
- this.name = "ProtocolError";
248
- this.protocolCode = code;
249
- this.zodError = zodError;
250
- }
251
- toJSON() {
252
- return {
253
- ...super.toJSON(),
254
- protocolCode: this.protocolCode,
255
- issues: this.zodError?.issues
256
- };
257
- }
258
- };
259
- var messageId = z2.string().min(1).max(64);
260
- var toolName = z2.string().min(1).max(128).regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, "Invalid tool name format");
261
- var toolCallSchema = z2.object({
262
- type: z2.literal("tool_call"),
263
- id: messageId,
264
- tool: toolName,
265
- arguments: z2.record(z2.string(), z2.unknown())
266
- }).strict();
267
- var toolResultSchema = z2.object({
268
- type: z2.literal("tool_result"),
269
- id: messageId,
270
- success: z2.boolean(),
271
- result: z2.unknown().optional(),
272
- error: z2.string().max(4096).optional()
273
- }).strict();
274
- var errorSchema = z2.object({
275
- type: z2.literal("error"),
276
- id: messageId,
277
- error: z2.string().min(1).max(4096),
278
- code: z2.string().max(64).optional()
279
- }).strict();
280
- var pingSchema = z2.object({ type: z2.literal("ping"), id: messageId }).strict();
281
- var pongSchema = z2.object({ type: z2.literal("pong"), id: messageId }).strict();
282
- var messageSchema = z2.discriminatedUnion("type", [
283
- toolCallSchema,
284
- toolResultSchema,
285
- errorSchema,
286
- pingSchema,
287
- pongSchema
288
- ]);
289
- function parseMessage(input) {
290
- const data = typeof input === "string" ? parseJson(input) : input;
291
- const result = messageSchema.safeParse(data);
292
- if (!result.success) {
293
- const issue = result.error.issues[0];
294
- throw new ProtocolError(
295
- `Invalid message: ${issue?.message ?? "validation failed"}`,
296
- "INVALID_MESSAGE",
297
- result.error
298
- );
299
- }
300
- return result.data;
301
- }
302
- function validateOutgoing(message) {
303
- parseMessage(message);
304
- }
305
- function parseJson(str) {
306
- try {
307
- return JSON.parse(str);
308
- } catch {
309
- throw new ProtocolError("Invalid JSON", "INVALID_JSON");
310
- }
311
- }
312
-
313
- // src/relay/client.ts
314
- var DEFAULT_TIMEOUT = 3e4;
315
- var DEFAULT_MAX_RETRIES = 5;
316
- var MAX_BACKOFF = 3e4;
317
- var RelayClient = class {
318
- ws = null;
319
- retryCount = 0;
320
- closing = false;
321
- config;
322
- log;
323
- constructor(config) {
324
- this.config = {
325
- url: config.url,
326
- token: config.token,
327
- tools: config.tools,
328
- debug: config.debug ?? false,
329
- timeout: config.timeout ?? DEFAULT_TIMEOUT,
330
- reconnect: config.reconnect ?? true,
331
- maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES
332
- };
333
- this.log = this.config.debug ? (...args) => console.log("[RelayClient]", ...args) : () => {
334
- };
335
- }
336
- get connected() {
337
- return this.ws?.readyState === WebSocket.OPEN;
338
- }
339
- async connect() {
340
- if (this.ws?.readyState === WebSocket.OPEN) return;
341
- this.closing = false;
342
- await this.createConnection();
343
- }
344
- async disconnect() {
345
- this.closing = true;
346
- if (!this.ws) return;
347
- return new Promise((resolve) => {
348
- const ws = this.ws;
349
- const cleanup = () => {
350
- this.ws = null;
351
- resolve();
352
- };
353
- ws.once("close", cleanup);
354
- if (ws.readyState === WebSocket.OPEN) {
355
- ws.close(1e3, "Client disconnect");
356
- } else {
357
- cleanup();
358
- }
359
- setTimeout(() => {
360
- ws.terminate();
361
- cleanup();
362
- }, 5e3);
363
- });
364
- }
365
- createConnection() {
366
- return new Promise((resolve, reject) => {
367
- const timeout = setTimeout(() => {
368
- this.ws?.terminate();
369
- reject(new Error(`Connection timeout (${this.config.timeout}ms)`));
370
- }, this.config.timeout);
371
- this.log(`Connecting to ${this.config.url}`);
372
- this.ws = new WebSocket(this.config.url, {
373
- headers: { Authorization: `Bearer ${this.config.token}` }
374
- });
375
- this.ws.on("open", () => {
376
- clearTimeout(timeout);
377
- this.retryCount = 0;
378
- this.log("Connected");
379
- resolve();
380
- });
381
- this.ws.on("message", (data) => this.onMessage(data));
382
- this.ws.on("close", (code, reason) => {
383
- clearTimeout(timeout);
384
- this.log(`Closed: ${code} ${reason.toString()}`);
385
- this.ws = null;
386
- if (!this.closing && this.config.reconnect) {
387
- this.reconnect();
388
- }
389
- });
390
- this.ws.on("error", (error) => {
391
- clearTimeout(timeout);
392
- this.log(`Error: ${error.message}`);
393
- reject(error);
394
- });
395
- });
396
- }
397
- async reconnect() {
398
- if (this.retryCount >= this.config.maxRetries) {
399
- this.log(`Max retries (${this.config.maxRetries}) exceeded`);
400
- return;
401
- }
402
- this.retryCount++;
403
- const delay = Math.min(1e3 * Math.pow(2, this.retryCount - 1), MAX_BACKOFF);
404
- this.log(`Reconnecting in ${delay}ms (attempt ${this.retryCount})`);
405
- await new Promise((r) => setTimeout(r, delay));
406
- if (this.closing) return;
407
- try {
408
- await this.createConnection();
409
- this.log("Reconnected");
410
- } catch (error) {
411
- this.log(`Reconnect failed: ${error}`);
412
- if (!this.closing && this.config.reconnect) {
413
- this.reconnect();
414
- }
415
- }
416
- }
417
- onMessage(data) {
418
- try {
419
- const message = parseMessage(data.toString());
420
- switch (message.type) {
421
- case "tool_call":
422
- this.handleToolCall(message);
423
- break;
424
- case "ping":
425
- this.send({ type: "pong", id: message.id });
426
- break;
427
- case "pong":
428
- break;
429
- default:
430
- this.log(`Unexpected message type: ${message.type}`);
431
- }
432
- } catch (error) {
433
- if (error instanceof ProtocolError) {
434
- this.log(`Protocol error: ${error.message}`);
435
- } else {
436
- this.log(`Message error: ${error}`);
437
- }
438
- }
439
- }
440
- async handleToolCall(message) {
441
- const { id, tool: name, arguments: args } = message;
442
- this.log(`Tool call: ${name}`, args);
443
- const tool = this.config.tools.get(name);
444
- if (!tool) {
445
- this.send({ type: "error", id, error: `Unknown tool: ${name}`, code: "UNKNOWN_TOOL" });
446
- return;
447
- }
448
- try {
449
- const result = await tool.execute(args);
450
- this.send({ type: "tool_result", id, success: true, result });
451
- } catch (error) {
452
- const msg = error instanceof Error ? error.message : "Execution failed";
453
- this.send({ type: "tool_result", id, success: false, error: msg.slice(0, 4096) });
454
- }
455
- }
456
- send(message) {
457
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
458
- this.log("Cannot send: not connected");
459
- return;
460
- }
461
- try {
462
- validateOutgoing(message);
463
- this.ws.send(JSON.stringify(message));
464
- } catch (error) {
465
- this.log(`Invalid outgoing message:`, message, error);
466
- }
467
- }
468
- };
469
-
470
- // src/relay/server.ts
471
- function generateRelayServerCode(config) {
472
- return RELAY_SERVER_TEMPLATE.replace(/__PORT__/g, String(config.port)).replace(/__TOKEN__/g, config.token).replace(/__TIMEOUT__/g, String(config.timeout)).replace(/__DEBUG__/g, String(config.debug));
473
- }
474
- function generateToolFunctions(tools) {
475
- return tools.map((tool) => {
476
- const snakeName = toSnakeCase2(tool.name);
477
- return `async function ${snakeName}(args: Record<string, unknown> = {}) { return callTool('${tool.name}', args); }`;
478
- }).join("\n");
479
- }
480
- function toSnakeCase2(str) {
481
- return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/-/g, "_").toLowerCase();
482
- }
483
- var RELAY_SERVER_TEMPLATE = `
484
- const config = { port: __PORT__, token: '__TOKEN__', timeout: __TIMEOUT__, debug: __DEBUG__ };
485
- const log = (...a: unknown[]) => config.debug && console.error('[Relay]', ...a);
486
-
487
- let ws: any = null;
488
- const pending = new Map<string, { resolve: (r: any) => void; timer: any }>();
489
-
490
- Bun.serve({
491
- port: config.port,
492
- hostname: '127.0.0.1',
493
- async fetch(req: Request, server: any) {
494
- const url = new URL(req.url);
495
- if (url.pathname === '/ws') {
496
- if (req.headers.get('authorization') !== 'Bearer ' + config.token) return new Response('', { status: 401 });
497
- return server.upgrade(req) ? undefined : new Response('', { status: 500 });
498
- }
499
- if (req.method === 'POST' && url.pathname === '/tool') {
500
- const { tool, arguments: args } = await req.json();
501
- if (!ws) return Response.json({ success: false, error: 'Host not connected' });
502
- const id = crypto.randomUUID();
503
- return Response.json(await new Promise(resolve => {
504
- const timer = setTimeout(() => { pending.delete(id); resolve({ success: false, error: 'Timeout' }); }, config.timeout * 1000);
505
- pending.set(id, { resolve, timer });
506
- ws.send(JSON.stringify({ type: 'tool_call', id, tool, arguments: args ?? {} }));
507
- log('Tool:', tool);
508
- }));
509
- }
510
- if (url.pathname === '/health') return Response.json({ ok: true, connected: !!ws });
511
- return new Response('', { status: 404 });
512
- },
513
- websocket: {
514
- open(s: any) { log('Host connected'); ws = s; },
515
- message(_: any, data: any) {
516
- const msg = JSON.parse(String(data));
517
- if (msg.type === 'tool_result' || msg.type === 'error') {
518
- const p = pending.get(msg.id);
519
- if (p) { clearTimeout(p.timer); pending.delete(msg.id); p.resolve(msg); }
520
- } else if (msg.type === 'ping') ws?.send(JSON.stringify({ type: 'pong', id: msg.id }));
521
- },
522
- close() { log('Host disconnected'); ws = null; },
523
- },
524
- });
525
- log('Ready');
526
-
527
- class ToolError extends Error { constructor(m: string) { super(m); this.name = 'ToolError'; } }
528
- async function callTool(name: string, args: Record<string, unknown> = {}): Promise<unknown> {
529
- const r = await (await fetch('http://127.0.0.1:' + config.port + '/tool', {
530
- method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tool: name, arguments: args })
531
- })).json() as any;
532
- if (!r.success) throw new ToolError(name + ': ' + (r.error ?? 'failed'));
533
- return r.result;
534
- }
535
- `.trim();
536
-
537
- // src/relay/types.ts
538
- var DEFAULT_RELAY_CONFIG = {
539
- port: 8080,
540
- token: "",
541
- timeout: 300,
542
- debug: false
543
- };
544
-
545
- // src/prompts.ts
546
- var EXAMPLE_CODE = `\`\`\`javascript
547
- // Example: Analyze sales data across regions
548
-
549
- // 1. Fetch data using tools (can loop for multiple calls)
550
- const regions = ["West", "East", "Central"];
551
- const results = {};
552
- for (const region of regions) {
553
- const data = await query_database({ sql: \`SELECT SUM(revenue) FROM sales WHERE region = '\${region}'\` });
554
- results[region] = data;
555
- }
556
-
557
- // 2. Process and analyze the data
558
- const total = Object.values(results).reduce((a, b) => a + b, 0);
559
- const topRegion = Object.entries(results).sort((a, b) => b[1] - a[1])[0];
560
-
561
- // 3. Print final results
562
- console.log(\`Total revenue: $\${total.toLocaleString()}\`);
563
- console.log(\`Top region: \${topRegion[0]} with $\${topRegion[1].toLocaleString()}\`);
564
- \`\`\``;
565
- function buildSystemPrompt(toolDefinitions, additionalInstructions) {
566
- const sections = [
567
- "You are an expert JavaScript programmer. Write JavaScript code that uses the available tools to accomplish the user's request.",
568
- "",
569
- "## How This Works",
570
- "",
571
- "1. You have access to tools as async functions",
572
- "2. Write code that calls these tools, processes results, and logs output",
573
- "3. Your code runs in a secure Node.js sandbox",
574
- "4. Tool calls happen inside your code - loop, aggregate, and process results programmatically",
575
- "5. Only the console output is returned to the user",
576
- "",
577
- "## Available Tools",
578
- "",
579
- toolDefinitions,
580
- "",
581
- "## Guidelines",
582
- "",
583
- "1. Write complete, executable JavaScript code",
584
- "2. Tools are async - always use await: `const result = await tool_name({ arg1, arg2 })`",
585
- "3. Use `console.log()` to output results - this is what the user sees",
586
- "4. Process data in code before logging (don't dump raw data)",
587
- "5. Use loops for batch operations",
588
- "6. Wrap risky operations in try/catch",
589
- "",
590
- "## Example",
591
- "",
592
- EXAMPLE_CODE,
593
- "",
594
- "## Notes",
595
- "",
596
- "- All tools are async - use await",
597
- "- Use modern JavaScript (ES2022+)",
598
- "- Top-level await is supported"
599
- ];
600
- if (additionalInstructions) {
601
- sections.push("", "## Additional Instructions", "", additionalInstructions);
602
- }
603
- sections.push(
604
- "",
605
- "Write JavaScript code to solve the user's request. Return ONLY the code in a ```javascript code block."
606
- );
607
- return sections.join("\n");
608
- }
609
- function extractCode(response) {
610
- const jsMatch = response.match(/```(?:javascript|js)\s*\n([\s\S]*?)```/);
611
- if (jsMatch) return jsMatch[1].trim();
612
- const genericMatch = response.match(/```\s*\n([\s\S]*?)```/);
613
- if (genericMatch) return genericMatch[1].trim();
614
- return response.trim();
615
- }
616
-
617
- // src/type-hints.ts
618
- var MAX_DEPTH = 10;
619
- function generateTypeHints(tools) {
620
- if (tools.length === 0) return "// No tools available";
621
- const signatures = tools.map(generateToolSignature);
622
- return ["// Available Tools", "// Call these as: const result = await tool_name({ args })", "", ...signatures].join("\n");
623
- }
624
- function generateToolSignature(tool) {
625
- const snakeName = toSnakeCase3(tool.name);
626
- const params = generateParams(tool.parameters);
627
- const lines = [
628
- `/**`,
629
- ` * ${tool.description}`
630
- ];
631
- const shape = getObjectShape(tool.parameters);
632
- if (shape && Object.keys(shape).length > 0) {
633
- lines.push(` *`);
634
- for (const [name, schema] of Object.entries(shape)) {
635
- const desc = getDescription(schema);
636
- const opt = isOptional(schema) ? " (optional)" : "";
637
- lines.push(` * @param ${name} - ${desc || "No description"}${opt}`);
638
- }
639
- }
640
- lines.push(` */`);
641
- lines.push(`async function ${snakeName}(${params}): Promise<any>`);
642
- lines.push("");
643
- return lines.join("\n");
644
- }
645
- function generateParams(schema) {
646
- const shape = getObjectShape(schema);
647
- if (!shape || Object.keys(shape).length === 0) return "";
648
- const entries = Object.entries(shape);
649
- const required = [];
650
- const optional = [];
651
- for (const [name, fieldSchema] of entries) {
652
- const jsType = zodToJS(fieldSchema, 0);
653
- if (isOptional(fieldSchema)) {
654
- optional.push(`${name}?: ${jsType}`);
655
- } else {
656
- required.push(`${name}: ${jsType}`);
657
- }
658
- }
659
- const allParams = [...required, ...optional];
660
- if (allParams.length === 0) return "";
661
- return `{ ${allParams.join(", ")} }`;
662
- }
663
- function zodToJS(schema, depth) {
664
- if (depth > MAX_DEPTH) return "any";
665
- const def = getDef(schema);
666
- const typeName = def.type;
667
- switch (typeName) {
668
- case "string":
669
- return "string";
670
- case "number":
671
- return "number";
672
- case "boolean":
673
- return "boolean";
674
- case "bigint":
675
- return "bigint";
676
- case "literal": {
677
- const val = def.value;
678
- if (typeof val === "string") return `'${val}'`;
679
- return String(val);
680
- }
681
- case "enum": {
682
- const entries = def.entries;
683
- if (!entries || typeof entries !== "object") return "string";
684
- const values = Object.values(entries);
685
- if (values.length === 0) return "string";
686
- return values.map((v) => `'${v}'`).join(" | ");
687
- }
688
- case "array": {
689
- const element = def.element;
690
- if (element) return `${zodToJS(element, depth + 1)}[]`;
691
- return "any[]";
692
- }
693
- case "tuple": {
694
- const items = def.items ?? [];
695
- if (items.length === 0) return "[]";
696
- return `[${items.map((i) => zodToJS(i, depth + 1)).join(", ")}]`;
697
- }
698
- case "set": {
699
- const element = def.element;
700
- if (element) return `Set<${zodToJS(element, depth + 1)}>`;
701
- return "Set<any>";
702
- }
703
- case "object": {
704
- const shape = getObjectShape(schema);
705
- if (!shape || Object.keys(shape).length === 0) return "object";
706
- return "Record<string, any>";
707
- }
708
- case "record": {
709
- const valType = def.valueType ? zodToJS(def.valueType, depth + 1) : "any";
710
- return `Record<string, ${valType}>`;
711
- }
712
- case "map": {
713
- const keyType = def.keyType ? zodToJS(def.keyType, depth + 1) : "any";
714
- const valType = def.valueType ? zodToJS(def.valueType, depth + 1) : "any";
715
- return `Map<${keyType}, ${valType}>`;
716
- }
717
- case "union": {
718
- const options = def.options ?? [];
719
- if (options.length === 0) return "any";
720
- const types = options.map((o) => zodToJS(o, depth + 1));
721
- const unique = [...new Set(types)];
722
- return unique.join(" | ");
723
- }
724
- case "optional":
725
- case "nullable": {
726
- const inner = def.innerType;
727
- if (inner) return `${zodToJS(inner, depth + 1)} | null`;
728
- return "any | null";
729
- }
730
- case "default": {
731
- const inner = def.innerType;
732
- if (inner) return zodToJS(inner, depth + 1);
733
- return "any";
734
- }
735
- case "null":
736
- case "undefined":
737
- case "void":
738
- return "null";
739
- case "any":
740
- case "unknown":
741
- return "any";
742
- case "never":
743
- return "never";
744
- case "date":
745
- return "Date";
746
- default:
747
- return "any";
748
- }
749
- }
750
- function getDef(schema) {
751
- const s = schema;
752
- return s._zod?.def ?? { type: "unknown" };
753
- }
754
- function getObjectShape(schema) {
755
- const def = getDef(schema);
756
- if (def.type !== "object") return null;
757
- const shape = def.shape;
758
- if (!shape) return null;
759
- return typeof shape === "function" ? shape() : shape;
760
- }
761
- function getDescription(schema) {
762
- const s = schema;
763
- if (s.description) return s.description;
764
- const def = getDef(schema);
765
- if (def.innerType) return getDescription(def.innerType);
766
- return "";
767
- }
768
- function isOptional(schema) {
769
- const def = getDef(schema);
770
- return def.type === "optional" || def.type === "default" || def.type === "nullable";
771
- }
772
- function toSnakeCase3(str) {
773
- return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/-/g, "_").toLowerCase();
774
- }
775
-
776
- // src/executor.ts
777
- function createExecutor(options) {
778
- return new ProgrammaticExecutor(options);
779
- }
780
- var ProgrammaticExecutor = class {
781
- originalTools;
782
- normalizedTools;
783
- toolsMap;
784
- llm;
785
- timeout;
786
- e2bApiKey;
787
- instructions;
788
- debug;
789
- constructor(options) {
790
- this.originalTools = options.tools;
791
- this.normalizedTools = normalizeTools(options.tools);
792
- this.toolsMap = new Map(this.normalizedTools.map((t) => [t.name, t]));
793
- this.llm = options.llm;
794
- this.timeout = options.timeout ?? 3e5;
795
- this.e2bApiKey = options.e2bApiKey;
796
- this.instructions = options.instructions;
797
- this.debug = options.debug ?? false;
798
- }
799
- async run(userRequest) {
800
- const startTime = Date.now();
801
- const toolDocs = generateTypeHints(this.originalTools);
802
- const systemPrompt = buildSystemPrompt(toolDocs, this.instructions);
803
- this.log("Generating code for:", userRequest.slice(0, 100));
804
- const generated = await this.generateCode(userRequest, systemPrompt);
805
- const code = extractCode(generated.code);
806
- this.log("Generated code:", code.slice(0, 500));
807
- const result = await this.executeCode(code);
808
- return {
809
- code,
810
- explanation: generated.explanation,
811
- result: { ...result, executionTimeMs: Date.now() - startTime }
812
- };
813
- }
814
- async executeCode(code) {
815
- const startTime = Date.now();
816
- const token = randomUUID();
817
- const relayConfig = { ...DEFAULT_RELAY_CONFIG, token, debug: this.debug };
818
- const setupCode = [
819
- generateRelayServerCode(relayConfig),
820
- generateToolFunctions(this.normalizedTools)
821
- ].join("\n\n");
822
- this.log("Creating E2B sandbox...");
823
- const sandbox = await this.createSandbox();
824
- let relayClient = null;
825
- try {
826
- const relayUrl = sandbox.getHost(relayConfig.port);
827
- const wsUrl = `wss://${relayUrl}/ws`;
828
- this.log("Relay URL:", wsUrl);
829
- await sandbox.files.write("/tmp/relay.ts", setupCode);
830
- const relayProc = await sandbox.commands.run("bun run /tmp/relay.ts", { background: true });
831
- this.log("Relay server started, pid:", relayProc.pid);
832
- await sleep(500);
833
- this.log("Connecting to relay...");
834
- relayClient = await this.connectRelay(wsUrl, token);
835
- await sandbox.files.write("/tmp/main.ts", code);
836
- this.log("Executing user code...");
837
- const result = await sandbox.commands.run("bun run /tmp/main.ts");
838
- return {
839
- success: result.exitCode === 0,
840
- output: result.stdout,
841
- error: result.exitCode !== 0 ? result.stderr : void 0,
842
- executionTimeMs: Date.now() - startTime
843
- };
844
- } finally {
845
- await this.cleanup(relayClient, sandbox);
846
- }
847
- }
848
- getTools() {
849
- return this.normalizedTools;
850
- }
851
- getToolDocumentation() {
852
- return generateTypeHints(this.originalTools);
853
- }
854
- async generateCode(request, systemPrompt) {
855
- try {
856
- return await this.llm.generateCode(request, systemPrompt);
857
- } catch (error) {
858
- throw new CodeGenerationError(
859
- `Failed to generate code: ${error instanceof Error ? error.message : "Unknown error"}`,
860
- error instanceof Error ? error : void 0
861
- );
862
- }
863
- }
864
- async createSandbox() {
865
- try {
866
- return await Sandbox.create({
867
- apiKey: this.e2bApiKey,
868
- timeoutMs: this.timeout
869
- // template: 'bun', // Uncomment when using custom Bun template
870
- });
871
- } catch (error) {
872
- throw new SandboxError(
873
- `Failed to create sandbox: ${error instanceof Error ? error.message : "Unknown error"}`,
874
- error instanceof Error ? error : void 0
875
- );
876
- }
877
- }
878
- async connectRelay(url, token) {
879
- const client = new RelayClient({
880
- url,
881
- token,
882
- tools: this.toolsMap,
883
- debug: this.debug,
884
- timeout: 3e4,
885
- reconnect: false
886
- });
887
- try {
888
- await client.connect();
889
- return client;
890
- } catch (error) {
891
- throw new RelayConnectionError(
892
- `Failed to connect to relay: ${error instanceof Error ? error.message : "Unknown error"}`,
893
- error instanceof Error ? error : void 0
894
- );
895
- }
896
- }
897
- async cleanup(client, sandbox) {
898
- if (client) {
899
- try {
900
- await client.disconnect();
901
- this.log("Relay client disconnected");
902
- } catch {
903
- this.log("Error disconnecting relay");
904
- }
905
- }
906
- try {
907
- await sandbox.kill();
908
- this.log("Sandbox killed");
909
- } catch {
910
- this.log("Error killing sandbox");
911
- }
912
- }
913
- log(...args) {
914
- if (this.debug) console.log("[Executor]", ...args);
915
- }
916
- };
917
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
918
- export {
919
- AuthenticationError,
920
- CodeGenerationError,
921
- ConfigurationError,
922
- ExecutionError,
923
- OPTError,
924
- ProgrammaticExecutor,
925
- RelayConnectionError,
926
- RelayTimeoutError,
927
- SandboxError,
928
- ToolError,
929
- buildSystemPrompt,
930
- createExecutor,
931
- defineTool,
932
- extractCode,
933
- generateTypeHints,
934
- isTool,
935
- normalizeTools,
936
- z
937
- };