@cloudflare/codemode 0.0.7 → 0.1.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/dist/ai.js CHANGED
@@ -1,148 +1,79 @@
1
- import { generateObject, tool } from "ai";
1
+ import { n as sanitizeToolName, t as generateTypes } from "./types-B9g5T2nd.js";
2
+ import { tool } from "ai";
2
3
  import { z } from "zod";
3
- import { compile } from "json-schema-to-typescript";
4
- import { createTypeAlias, printNode, zodToTs } from "zod-to-ts";
5
- import { getAgentByName } from "agents";
6
- import { WorkerEntrypoint, env } from "cloudflare:workers";
7
- import { openai } from "@ai-sdk/openai";
4
+ import * as acorn from "acorn";
8
5
 
9
- //#region src/ai.ts
10
- function toCamelCase(str) {
11
- return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/^[a-z]/, (letter) => letter.toUpperCase());
12
- }
13
- var CodeModeProxy = class extends WorkerEntrypoint {
14
- async callFunction(options) {
15
- return (await getAgentByName(env[this.ctx.props.binding], this.ctx.props.name))[this.ctx.props.callback](options.functionName, options.args);
16
- }
17
- };
18
- async function experimental_codemode(options) {
19
- const generatedTypes = await generateTypes(options.tools);
20
- return {
21
- prompt: `${options.prompt}
22
- You are a helpful assistant. You have access to the "codemode" tool that can do different things:
23
-
24
- ${getToolDescriptions(options.tools)}
25
-
26
- If the user asks to do anything that be achieveable by the codemode tool, then simply pass over control to it by giving it a simple function description. Don't be too verbose.
27
-
28
- `,
29
- tools: { codemode: tool({
30
- description: "codemode: a tool that can generate code to achieve a goal",
31
- inputSchema: z.object({ functionDescription: z.string() }),
32
- outputSchema: z.object({
33
- code: z.string(),
34
- result: z.any()
35
- }),
36
- execute: async ({ functionDescription }) => {
37
- try {
38
- const response = await generateObject({
39
- model: options.model ? options.model : openai("gpt-4.1"),
40
- schema: z.object({ code: z.string() }),
41
- prompt: `You are a code generating machine.
42
-
43
- In addition to regular javascript, you can also use the following functions:
44
-
45
- ${generatedTypes}
6
+ //#region src/tool.ts
7
+ const DEFAULT_DESCRIPTION = `Execute code to achieve a goal.
46
8
 
47
- Respond only with the code, nothing else. Output javascript code.
9
+ Available:
10
+ {{types}}
48
11
 
49
- Generate an async function that achieves the goal. This async function doesn't accept any arguments.
12
+ Write an async arrow function that returns the result.
13
+ Do NOT define named functions then call them — just write the arrow function body directly.
50
14
 
51
- Here is user input: ${functionDescription}`
52
- });
53
- const result = await createEvaluator(response.object.code, {
54
- proxy: options.proxy,
55
- loader: options.loader
56
- })();
57
- return {
58
- code: response.object.code,
59
- result
60
- };
61
- } catch (error) {
62
- console.error("error", error);
63
- throw error;
64
- }
65
- }
66
- }) }
67
- };
15
+ Example: async () => { const r = await codemode.searchWeb({ query: "test" }); return r; }`;
16
+ const codeSchema = z.object({ code: z.string().describe("JavaScript async arrow function to execute") });
17
+ function normalizeCode(code) {
18
+ const trimmed = code.trim();
19
+ if (!trimmed) return "async () => {}";
20
+ try {
21
+ const ast = acorn.parse(trimmed, {
22
+ ecmaVersion: "latest",
23
+ sourceType: "module"
24
+ });
25
+ if (ast.body.length === 1 && ast.body[0].type === "ExpressionStatement") {
26
+ if (ast.body[0].expression.type === "ArrowFunctionExpression") return trimmed;
27
+ }
28
+ const last = ast.body[ast.body.length - 1];
29
+ if (last?.type === "ExpressionStatement") {
30
+ const exprStmt = last;
31
+ return `async () => {\n${trimmed.slice(0, last.start)}return (${trimmed.slice(exprStmt.expression.start, exprStmt.expression.end)})\n}`;
32
+ }
33
+ return `async () => {\n${trimmed}\n}`;
34
+ } catch {
35
+ return `async () => {\n${trimmed}\n}`;
36
+ }
68
37
  }
69
- function createEvaluator(code, options) {
70
- return async () => {
71
- return await options.loader.get(`code-${Math.random()}`, () => {
72
- return {
73
- compatibilityDate: "2025-06-01",
74
- compatibilityFlags: ["nodejs_compat"],
75
- mainModule: "foo.js",
76
- modules: { "foo.js": `
77
- import { env, WorkerEntrypoint } from "cloudflare:workers";
78
-
79
- export default class CodeModeWorker extends WorkerEntrypoint {
80
- async evaluate() {
81
- try {
82
- const { CodeModeProxy } = env;
83
- const codemode = new Proxy(
84
- {},
85
- {
86
- get: (target, prop) => {
87
- return (args) => {
88
- return CodeModeProxy.callFunction({
89
- functionName: prop,
90
- args: args,
91
- });
92
- };
93
- }
94
- }
95
- );
96
-
97
- return await ${code}();
98
- } catch (err) {
99
- return {
100
- err: err.message,
101
- stack: err.stack
102
- };
103
- }
104
- }
38
+ /**
39
+ * Create a codemode tool that allows LLMs to write and execute code
40
+ * with access to your tools in a sandboxed environment.
41
+ *
42
+ * Returns an AI SDK compatible tool.
43
+ */
44
+ function hasNeedsApproval(t) {
45
+ return "needsApproval" in t && t.needsApproval != null;
105
46
  }
106
-
107
- ` },
108
- env: { CodeModeProxy: options.proxy },
109
- globalOutbound: null
47
+ function createCodeTool(options) {
48
+ const tools = {};
49
+ for (const [name, t] of Object.entries(options.tools)) if (!hasNeedsApproval(t)) tools[name] = t;
50
+ const types = generateTypes(tools);
51
+ const executor = options.executor;
52
+ return tool({
53
+ description: (options.description ?? DEFAULT_DESCRIPTION).replace("{{types}}", types),
54
+ inputSchema: codeSchema,
55
+ execute: async ({ code }) => {
56
+ const fns = {};
57
+ for (const [name, t] of Object.entries(tools)) {
58
+ const execute = "execute" in t ? t.execute : void 0;
59
+ if (execute) fns[sanitizeToolName(name)] = execute;
60
+ }
61
+ const normalizedCode = normalizeCode(code);
62
+ const executeResult = await executor.execute(normalizedCode, fns);
63
+ if (executeResult.error) {
64
+ const logCtx = executeResult.logs?.length ? `\n\nConsole output:\n${executeResult.logs.join("\n")}` : "";
65
+ throw new Error(`Code execution failed: ${executeResult.error}${logCtx}`);
66
+ }
67
+ const output = {
68
+ code,
69
+ result: executeResult.result
110
70
  };
111
- }).getEntrypoint().evaluate();
112
- };
113
- }
114
- async function generateTypes(tools) {
115
- let availableTools = "";
116
- let availableTypes = "";
117
- for (const [toolName, tool] of Object.entries(tools)) {
118
- const inputJsonType = tool.inputSchema.jsonSchema ? await compile(tool.inputSchema.jsonSchema, `${toCamelCase(toolName)}Input`, {
119
- format: false,
120
- bannerComment: " "
121
- }) : printNode(createTypeAlias(zodToTs(tool.inputSchema, `${toCamelCase(toolName)}Input`).node, `${toCamelCase(toolName)}Input`));
122
- const outputJsonType = tool.outputSchema?.jsonSchema ? await compile(tool.outputSchema?.jsonSchema, `${toCamelCase(toolName)}Output`, {
123
- format: false,
124
- bannerComment: " "
125
- }) : tool.outputSchema ? printNode(createTypeAlias(zodToTs(tool.outputSchema, `${toCamelCase(toolName)}Output`).node, `${toCamelCase(toolName)}Output`)) : `interface ${toCamelCase(toolName)}Output { [key: string]: any }`;
126
- const InputType = inputJsonType.trim().replace("export interface", "interface");
127
- const OutputType = outputJsonType.trim().replace("export interface", "interface");
128
- availableTypes += `\n${InputType}`;
129
- availableTypes += `\n${OutputType}`;
130
- availableTools += `\n\t/*\n\t${tool.description?.trim()}\n\t*/`;
131
- availableTools += `\n\t${toolName}: (input: ${toCamelCase(toolName)}Input) => Promise<${toCamelCase(toolName)}Output>;`;
132
- availableTools += "\n";
133
- }
134
- availableTools = `\ndeclare const codemode: {${availableTools}}`;
135
- return `
136
- ${availableTypes}
137
- ${availableTools}
138
- `;
139
- }
140
- function getToolDescriptions(tools) {
141
- return Object.entries(tools).map(([_toolName, tool]) => {
142
- return `\n- ${tool.description?.trim()}`;
143
- }).join("");
71
+ if (executeResult.logs) output.logs = executeResult.logs;
72
+ return output;
73
+ }
74
+ });
144
75
  }
145
76
 
146
77
  //#endregion
147
- export { CodeModeProxy, experimental_codemode };
78
+ export { createCodeTool };
148
79
  //# sourceMappingURL=ai.js.map
package/dist/ai.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ai.js","names":["compileJsonSchemaToTs","printNodeZodToTs"],"sources":["../src/ai.ts"],"sourcesContent":["import { generateObject, tool, type ToolSet, type LanguageModel } from \"ai\";\nimport { z } from \"zod\";\nimport { compile as compileJsonSchemaToTs } from \"json-schema-to-typescript\";\nimport {\n zodToTs,\n printNode as printNodeZodToTs,\n createTypeAlias\n} from \"zod-to-ts\";\nimport { getAgentByName } from \"agents\";\nimport { env, WorkerEntrypoint } from \"cloudflare:workers\";\nimport { openai } from \"@ai-sdk/openai\";\n\nfunction toCamelCase(str: string) {\n return str\n .replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())\n .replace(/^[a-z]/, (letter) => letter.toUpperCase());\n}\n\nexport class CodeModeProxy extends WorkerEntrypoint<\n Cloudflare.Env,\n {\n binding: string;\n name: string;\n callback: string;\n }\n> {\n async callFunction(options: { functionName: string; args: unknown[] }) {\n const stub = (await getAgentByName(\n // @ts-expect-error\n env[this.ctx.props.binding] as DurableObjectNamespace<T>,\n this.ctx.props.name\n )) as DurableObjectStub;\n // @ts-expect-error\n return stub[this.ctx.props.callback](options.functionName, options.args);\n }\n}\n\nexport async function experimental_codemode(options: {\n tools: ToolSet;\n prompt: string;\n globalOutbound: Fetcher;\n loader: WorkerLoader;\n proxy: Fetcher<CodeModeProxy>;\n model?: LanguageModel;\n}): Promise<{\n prompt: string;\n tools: ToolSet;\n}> {\n const generatedTypes = await generateTypes(options.tools);\n const prompt = `${options.prompt}\n You are a helpful assistant. You have access to the \"codemode\" tool that can do different things: \n \n ${getToolDescriptions(options.tools)} \n \n If the user asks to do anything that be achieveable by the codemode tool, then simply pass over control to it by giving it a simple function description. Don't be too verbose.\n \n `;\n\n const codemodeTool = tool({\n description: \"codemode: a tool that can generate code to achieve a goal\",\n inputSchema: z.object({\n functionDescription: z.string()\n }),\n outputSchema: z.object({\n code: z.string(),\n result: z.any()\n }),\n execute: async ({ functionDescription }) => {\n try {\n const response = await generateObject({\n model: options.model ? options.model : openai(\"gpt-4.1\"),\n schema: z.object({\n code: z.string()\n }),\n prompt: `You are a code generating machine.\n\n In addition to regular javascript, you can also use the following functions:\n\n ${generatedTypes} \n\n Respond only with the code, nothing else. Output javascript code.\n\n Generate an async function that achieves the goal. This async function doesn't accept any arguments.\n\n Here is user input: ${functionDescription}` // insert ts types for the tools here\n });\n\n // console.log(\"args\", response.object.args);\n const evaluator = createEvaluator(response.object.code, {\n proxy: options.proxy,\n loader: options.loader\n });\n const result = await evaluator();\n return { code: response.object.code, result: result };\n } catch (error) {\n console.error(\"error\", error);\n throw error;\n // return { code: \"\", result: error };\n }\n }\n });\n\n return { prompt, tools: { codemode: codemodeTool } };\n}\n\nfunction createEvaluator(\n code: string,\n options: {\n loader: WorkerLoader;\n proxy: Fetcher<CodeModeProxy>;\n }\n) {\n return async () => {\n const worker = options.loader.get(`code-${Math.random()}`, () => {\n return {\n compatibilityDate: \"2025-06-01\",\n compatibilityFlags: [\"nodejs_compat\"],\n mainModule: \"foo.js\",\n modules: {\n \"foo.js\": `\nimport { env, WorkerEntrypoint } from \"cloudflare:workers\";\n\nexport default class CodeModeWorker extends WorkerEntrypoint {\n async evaluate() {\n try {\n const { CodeModeProxy } = env;\n const codemode = new Proxy(\n {},\n {\n get: (target, prop) => {\n return (args) => {\n return CodeModeProxy.callFunction({\n functionName: prop,\n args: args, \n });\n };\n }\n }\n );\n\n return await ${code}();\n } catch (err) {\n return {\n err: err.message,\n stack: err.stack\n };\n }\n }\n}\n \n `\n },\n env: {\n // insert keys and bindings to tools/ts functions here\n CodeModeProxy: options.proxy\n },\n globalOutbound: null\n };\n });\n\n // @ts-expect-error TODO: fix this\n return await worker.getEntrypoint().evaluate();\n };\n}\n\nasync function generateTypes(tools: ToolSet) {\n let availableTools = \"\";\n let availableTypes = \"\";\n\n for (const [toolName, tool] of Object.entries(tools)) {\n // @ts-expect-error TODO: fix this\n const inputJsonType = tool.inputSchema.jsonSchema\n ? await compileJsonSchemaToTs(\n // @ts-expect-error TODO: fix this\n tool.inputSchema.jsonSchema,\n `${toCamelCase(toolName)}Input`,\n {\n format: false,\n bannerComment: \" \"\n }\n )\n : printNodeZodToTs(\n createTypeAlias(\n zodToTs(\n // @ts-expect-error TODO: fix this\n tool.inputSchema,\n `${toCamelCase(toolName)}Input`\n ).node,\n `${toCamelCase(toolName)}Input`\n )\n );\n\n const outputJsonType =\n // @ts-expect-error TODO: fix this\n tool.outputSchema?.jsonSchema\n ? await compileJsonSchemaToTs(\n // @ts-expect-error TODO: fix this\n tool.outputSchema?.jsonSchema,\n `${toCamelCase(toolName)}Output`,\n {\n format: false,\n bannerComment: \" \"\n }\n )\n : tool.outputSchema\n ? printNodeZodToTs(\n createTypeAlias(\n zodToTs(\n // @ts-expect-error TODO: fix this\n tool.outputSchema,\n `${toCamelCase(toolName)}Output`\n ).node,\n `${toCamelCase(toolName)}Output`\n )\n )\n : `interface ${toCamelCase(toolName)}Output { [key: string]: any }`;\n\n const InputType = inputJsonType\n .trim()\n .replace(\"export interface\", \"interface\");\n\n const OutputType = outputJsonType\n .trim()\n .replace(\"export interface\", \"interface\");\n\n availableTypes += `\\n${InputType}`;\n availableTypes += `\\n${OutputType}`;\n availableTools += `\\n\\t/*\\n\\t${tool.description?.trim()}\\n\\t*/`;\n availableTools += `\\n\\t${toolName}: (input: ${toCamelCase(toolName)}Input) => Promise<${toCamelCase(toolName)}Output>;`;\n availableTools += \"\\n\";\n }\n\n availableTools = `\\ndeclare const codemode: {${availableTools}}`;\n\n return `\n${availableTypes}\n${availableTools}\n `;\n}\n\nfunction getToolDescriptions(tools: ToolSet) {\n return Object.entries(tools)\n .map(([_toolName, tool]) => {\n return `\\n- ${tool.description?.trim()}`;\n })\n .join(\"\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai.js","names":[],"sources":["../src/tool.ts"],"sourcesContent":["import { tool, type Tool } from \"ai\";\nimport { z } from \"zod\";\nimport type { ToolSet } from \"ai\";\nimport * as acorn from \"acorn\";\nimport { generateTypes, sanitizeToolName, type ToolDescriptors } from \"./types\";\nimport type { Executor } from \"./executor\";\n\nconst DEFAULT_DESCRIPTION = `Execute code to achieve a goal.\n\nAvailable:\n{{types}}\n\nWrite an async arrow function that returns the result.\nDo NOT define named functions then call them just write the arrow function body directly.\n\nExample: async () => { const r = await codemode.searchWeb({ query: \"test\" }); return r; }`;\n\nexport interface CreateCodeToolOptions {\n tools: ToolDescriptors | ToolSet;\n executor: Executor;\n /**\n * Custom tool description. Use {{types}} as a placeholder for the generated type definitions.\n */\n description?: string;\n}\n\nconst codeSchema = z.object({\n code: z.string().describe(\"JavaScript async arrow function to execute\")\n});\n\ntype CodeInput = z.infer<typeof codeSchema>;\ntype CodeOutput = { code: string; result: unknown; logs?: string[] };\n\nfunction normalizeCode(code: string): string {\n const trimmed = code.trim();\n if (!trimmed) return \"async () => {}\";\n\n try {\n const ast = acorn.parse(trimmed, {\n ecmaVersion: \"latest\",\n sourceType: \"module\"\n });\n\n // Already an arrow function pass through\n if (ast.body.length === 1 && ast.body[0].type === \"ExpressionStatement\") {\n const expr = (ast.body[0] as acorn.ExpressionStatement).expression;\n if (expr.type === \"ArrowFunctionExpression\") return trimmed;\n }\n\n // Last statement is expression splice in return\n const last = ast.body[ast.body.length - 1];\n if (last?.type === \"ExpressionStatement\") {\n const exprStmt = last as acorn.ExpressionStatement;\n const before = trimmed.slice(0, last.start);\n const exprText = trimmed.slice(\n exprStmt.expression.start,\n exprStmt.expression.end\n );\n return `async () => {\\n${before}return (${exprText})\\n}`;\n }\n\n return `async () => {\\n${trimmed}\\n}`;\n } catch {\n return `async () => {\\n${trimmed}\\n}`;\n }\n}\n\n/**\n * Create a codemode tool that allows LLMs to write and execute code\n * with access to your tools in a sandboxed environment.\n *\n * Returns an AI SDK compatible tool.\n */\nfunction hasNeedsApproval(t: Record<string, unknown>): boolean {\n return \"needsApproval\" in t && t.needsApproval != null;\n}\n\nexport function createCodeTool(\n options: CreateCodeToolOptions\n): Tool<CodeInput, CodeOutput> {\n const tools: ToolDescriptors | ToolSet = {};\n for (const [name, t] of Object.entries(options.tools)) {\n if (!hasNeedsApproval(t as Record<string, unknown>)) {\n (tools as Record<string, unknown>)[name] = t;\n }\n }\n\n const types = generateTypes(tools);\n const executor = options.executor;\n\n const description = (options.description ?? DEFAULT_DESCRIPTION).replace(\n \"{{types}}\",\n types\n );\n\n return tool({\n description,\n inputSchema: codeSchema,\n execute: async ({ code }) => {\n // Extract execute functions from tools, keyed by name\n const fns: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\n for (const [name, t] of Object.entries(tools)) {\n const execute =\n \"execute\" in t\n ? (t.execute as (args: unknown) => Promise<unknown>)\n : undefined;\n if (execute) {\n fns[sanitizeToolName(name)] = execute;\n }\n }\n\n const normalizedCode = normalizeCode(code);\n\n const executeResult = await executor.execute(normalizedCode, fns);\n\n if (executeResult.error) {\n const logCtx = executeResult.logs?.length\n ? `\\n\\nConsole output:\\n${executeResult.logs.join(\"\\n\")}`\n : \"\";\n throw new Error(\n `Code execution failed: ${executeResult.error}${logCtx}`\n );\n }\n\n const output: CodeOutput = { code, result: executeResult.result };\n if (executeResult.logs) output.logs = executeResult.logs;\n return output;\n }\n });\n}\n"],"mappings":";;;;;;AAOA,MAAM,sBAAsB;;;;;;;;;AAmB5B,MAAM,aAAa,EAAE,OAAO,EAC1B,MAAM,EAAE,QAAQ,CAAC,SAAS,6CAA6C,EACxE,CAAC;AAKF,SAAS,cAAc,MAAsB;CAC3C,MAAM,UAAU,KAAK,MAAM;AAC3B,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,SAAS;GAC/B,aAAa;GACb,YAAY;GACb,CAAC;AAGF,MAAI,IAAI,KAAK,WAAW,KAAK,IAAI,KAAK,GAAG,SAAS,uBAEhD;OADc,IAAI,KAAK,GAAiC,WAC/C,SAAS,0BAA2B,QAAO;;EAItD,MAAM,OAAO,IAAI,KAAK,IAAI,KAAK,SAAS;AACxC,MAAI,MAAM,SAAS,uBAAuB;GACxC,MAAM,WAAW;AAMjB,UAAO,kBALQ,QAAQ,MAAM,GAAG,KAAK,MAAM,CAKX,UAJf,QAAQ,MACvB,SAAS,WAAW,OACpB,SAAS,WAAW,IACrB,CACkD;;AAGrD,SAAO,kBAAkB,QAAQ;SAC3B;AACN,SAAO,kBAAkB,QAAQ;;;;;;;;;AAUrC,SAAS,iBAAiB,GAAqC;AAC7D,QAAO,mBAAmB,KAAK,EAAE,iBAAiB;;AAGpD,SAAgB,eACd,SAC6B;CAC7B,MAAM,QAAmC,EAAE;AAC3C,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,QAAQ,MAAM,CACnD,KAAI,CAAC,iBAAiB,EAA6B,CACjD,CAAC,MAAkC,QAAQ;CAI/C,MAAM,QAAQ,cAAc,MAAM;CAClC,MAAM,WAAW,QAAQ;AAOzB,QAAO,KAAK;EACV,cANmB,QAAQ,eAAe,qBAAqB,QAC/D,aACA,MACD;EAIC,aAAa;EACb,SAAS,OAAO,EAAE,WAAW;GAE3B,MAAM,MAAgE,EAAE;AAExE,QAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,MAAM,EAAE;IAC7C,MAAM,UACJ,aAAa,IACR,EAAE,UACH;AACN,QAAI,QACF,KAAI,iBAAiB,KAAK,IAAI;;GAIlC,MAAM,iBAAiB,cAAc,KAAK;GAE1C,MAAM,gBAAgB,MAAM,SAAS,QAAQ,gBAAgB,IAAI;AAEjE,OAAI,cAAc,OAAO;IACvB,MAAM,SAAS,cAAc,MAAM,SAC/B,wBAAwB,cAAc,KAAK,KAAK,KAAK,KACrD;AACJ,UAAM,IAAI,MACR,0BAA0B,cAAc,QAAQ,SACjD;;GAGH,MAAM,SAAqB;IAAE;IAAM,QAAQ,cAAc;IAAQ;AACjE,OAAI,cAAc,KAAM,QAAO,OAAO,cAAc;AACpD,UAAO;;EAEV,CAAC"}
@@ -0,0 +1,96 @@
1
+ import { RpcTarget } from "cloudflare:workers";
2
+ import { ToolSet } from "ai";
3
+ import { ZodType } from "zod";
4
+
5
+ //#region src/types.d.ts
6
+ /**
7
+ * Sanitize a tool name into a valid JavaScript identifier.
8
+ * Replaces hyphens, dots, and spaces with `_`, strips other invalid chars,
9
+ * prefixes digit-leading names with `_`, and appends `_` to JS reserved words.
10
+ */
11
+ declare function sanitizeToolName(name: string): string;
12
+ interface ToolDescriptor {
13
+ description?: string;
14
+ inputSchema: ZodType;
15
+ outputSchema?: ZodType;
16
+ execute?: (args: unknown) => Promise<unknown>;
17
+ }
18
+ type ToolDescriptors = Record<string, ToolDescriptor>;
19
+ /**
20
+ * Generate TypeScript type definitions from tool descriptors or an AI SDK ToolSet.
21
+ * These types can be included in tool descriptions to help LLMs write correct code.
22
+ */
23
+ declare function generateTypes(tools: ToolDescriptors | ToolSet): string;
24
+ //#endregion
25
+ //#region src/executor.d.ts
26
+ interface ExecuteResult {
27
+ result: unknown;
28
+ error?: string;
29
+ logs?: string[];
30
+ }
31
+ /**
32
+ * An executor runs LLM-generated code in a sandbox, making the provided
33
+ * tool functions callable as `codemode.*` inside the sandbox.
34
+ *
35
+ * Implementations should never throw — errors are returned in `ExecuteResult.error`.
36
+ */
37
+ interface Executor {
38
+ execute(
39
+ code: string,
40
+ fns: Record<string, (...args: unknown[]) => Promise<unknown>>
41
+ ): Promise<ExecuteResult>;
42
+ }
43
+ /**
44
+ * An RpcTarget that dispatches tool calls from the sandboxed Worker
45
+ * back to the host. Passed via Workers RPC to the dynamic Worker's
46
+ * evaluate() method — no globalOutbound or Fetcher bindings needed.
47
+ */
48
+ declare class ToolDispatcher extends RpcTarget {
49
+ #private;
50
+ constructor(fns: Record<string, (...args: unknown[]) => Promise<unknown>>);
51
+ call(name: string, argsJson: string): Promise<string>;
52
+ }
53
+ interface DynamicWorkerExecutorOptions {
54
+ loader: WorkerLoader;
55
+ /**
56
+ * Timeout in milliseconds for code execution. Defaults to 30000 (30s).
57
+ */
58
+ timeout?: number;
59
+ /**
60
+ * Controls outbound network access from sandboxed code.
61
+ * - `null` (default): fetch() and connect() throw — sandbox is fully isolated.
62
+ * - `undefined`: inherits parent Worker's network access (full internet).
63
+ * - A `Fetcher`: all outbound requests route through this handler.
64
+ */
65
+ globalOutbound?: Fetcher | null;
66
+ }
67
+ /**
68
+ * Executes code in an isolated Cloudflare Worker via WorkerLoader.
69
+ * Tool calls are dispatched via Workers RPC — the host passes a
70
+ * ToolDispatcher (RpcTarget) to the Worker's evaluate() method.
71
+ *
72
+ * External fetch() and connect() are blocked by default via
73
+ * `globalOutbound: null` (runtime-enforced). Pass a Fetcher to
74
+ * `globalOutbound` to allow controlled outbound access.
75
+ */
76
+ declare class DynamicWorkerExecutor implements Executor {
77
+ #private;
78
+ constructor(options: DynamicWorkerExecutorOptions);
79
+ execute(
80
+ code: string,
81
+ fns: Record<string, (...args: unknown[]) => Promise<unknown>>
82
+ ): Promise<ExecuteResult>;
83
+ }
84
+ //#endregion
85
+ export {
86
+ ToolDispatcher as a,
87
+ generateTypes as c,
88
+ Executor as i,
89
+ sanitizeToolName as l,
90
+ DynamicWorkerExecutorOptions as n,
91
+ ToolDescriptor as o,
92
+ ExecuteResult as r,
93
+ ToolDescriptors as s,
94
+ DynamicWorkerExecutor as t
95
+ };
96
+ //# sourceMappingURL=executor-Czw9jKZH.d.ts.map
@@ -0,0 +1,22 @@
1
+ import {
2
+ a as ToolDispatcher,
3
+ c as generateTypes,
4
+ i as Executor,
5
+ l as sanitizeToolName,
6
+ n as DynamicWorkerExecutorOptions,
7
+ o as ToolDescriptor,
8
+ r as ExecuteResult,
9
+ s as ToolDescriptors,
10
+ t as DynamicWorkerExecutor
11
+ } from "./executor-Czw9jKZH.js";
12
+ export {
13
+ DynamicWorkerExecutor,
14
+ type DynamicWorkerExecutorOptions,
15
+ type ExecuteResult,
16
+ type Executor,
17
+ type ToolDescriptor,
18
+ type ToolDescriptors,
19
+ ToolDispatcher,
20
+ generateTypes,
21
+ sanitizeToolName
22
+ };
package/dist/index.js ADDED
@@ -0,0 +1,109 @@
1
+ import { n as sanitizeToolName, t as generateTypes } from "./types-B9g5T2nd.js";
2
+ import { RpcTarget } from "cloudflare:workers";
3
+
4
+ //#region src/executor.ts
5
+ /**
6
+ * Executor interface and DynamicWorkerExecutor implementation.
7
+ *
8
+ * The Executor interface is the core abstraction — implement it to run
9
+ * LLM-generated code in any sandbox (Workers, QuickJS, Node VM, etc.).
10
+ */
11
+ /**
12
+ * An RpcTarget that dispatches tool calls from the sandboxed Worker
13
+ * back to the host. Passed via Workers RPC to the dynamic Worker's
14
+ * evaluate() method — no globalOutbound or Fetcher bindings needed.
15
+ */
16
+ var ToolDispatcher = class extends RpcTarget {
17
+ #fns;
18
+ constructor(fns) {
19
+ super();
20
+ this.#fns = fns;
21
+ }
22
+ async call(name, argsJson) {
23
+ const fn = this.#fns[name];
24
+ if (!fn) return JSON.stringify({ error: `Tool "${name}" not found` });
25
+ try {
26
+ const result = await fn(argsJson ? JSON.parse(argsJson) : {});
27
+ return JSON.stringify({ result });
28
+ } catch (err) {
29
+ return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
30
+ }
31
+ }
32
+ };
33
+ /**
34
+ * Executes code in an isolated Cloudflare Worker via WorkerLoader.
35
+ * Tool calls are dispatched via Workers RPC — the host passes a
36
+ * ToolDispatcher (RpcTarget) to the Worker's evaluate() method.
37
+ *
38
+ * External fetch() and connect() are blocked by default via
39
+ * `globalOutbound: null` (runtime-enforced). Pass a Fetcher to
40
+ * `globalOutbound` to allow controlled outbound access.
41
+ */
42
+ var DynamicWorkerExecutor = class {
43
+ #loader;
44
+ #timeout;
45
+ #globalOutbound;
46
+ constructor(options) {
47
+ this.#loader = options.loader;
48
+ this.#timeout = options.timeout ?? 3e4;
49
+ this.#globalOutbound = options.globalOutbound ?? null;
50
+ }
51
+ async execute(code, fns) {
52
+ const timeoutMs = this.#timeout;
53
+ const modulePrefix = [
54
+ "import { WorkerEntrypoint } from \"cloudflare:workers\";",
55
+ "",
56
+ "export default class CodeExecutor extends WorkerEntrypoint {",
57
+ " async evaluate(dispatcher) {",
58
+ " const __logs = [];",
59
+ " console.log = (...a) => { __logs.push(a.map(String).join(\" \")); };",
60
+ " console.warn = (...a) => { __logs.push(\"[warn] \" + a.map(String).join(\" \")); };",
61
+ " console.error = (...a) => { __logs.push(\"[error] \" + a.map(String).join(\" \")); };",
62
+ " const codemode = new Proxy({}, {",
63
+ " get: (_, toolName) => async (args) => {",
64
+ " const resJson = await dispatcher.call(String(toolName), JSON.stringify(args ?? {}));",
65
+ " const data = JSON.parse(resJson);",
66
+ " if (data.error) throw new Error(data.error);",
67
+ " return data.result;",
68
+ " }",
69
+ " });",
70
+ "",
71
+ " try {",
72
+ " const result = await Promise.race([",
73
+ " ("
74
+ ].join("\n");
75
+ const moduleSuffix = [
76
+ ")(),",
77
+ " new Promise((_, reject) => setTimeout(() => reject(new Error(\"Execution timed out\")), " + timeoutMs + "))",
78
+ " ]);",
79
+ " return { result, logs: __logs };",
80
+ " } catch (err) {",
81
+ " return { result: undefined, error: err.message, logs: __logs };",
82
+ " }",
83
+ " }",
84
+ "}"
85
+ ].join("\n");
86
+ const executorModule = modulePrefix + code + moduleSuffix;
87
+ const dispatcher = new ToolDispatcher(fns);
88
+ const response = await this.#loader.get(`codemode-${crypto.randomUUID()}`, () => ({
89
+ compatibilityDate: "2025-06-01",
90
+ compatibilityFlags: ["nodejs_compat"],
91
+ mainModule: "executor.js",
92
+ modules: { "executor.js": executorModule },
93
+ globalOutbound: this.#globalOutbound
94
+ })).getEntrypoint().evaluate(dispatcher);
95
+ if (response.error) return {
96
+ result: void 0,
97
+ error: response.error,
98
+ logs: response.logs
99
+ };
100
+ return {
101
+ result: response.result,
102
+ logs: response.logs
103
+ };
104
+ }
105
+ };
106
+
107
+ //#endregion
108
+ export { DynamicWorkerExecutor, ToolDispatcher, generateTypes, sanitizeToolName };
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["#fns","#loader","#timeout","#globalOutbound"],"sources":["../src/executor.ts"],"sourcesContent":["/**\n * Executor interface and DynamicWorkerExecutor implementation.\n *\n * The Executor interface is the core abstraction — implement it to run\n * LLM-generated code in any sandbox (Workers, QuickJS, Node VM, etc.).\n */\n\nimport { RpcTarget } from \"cloudflare:workers\";\n\nexport interface ExecuteResult {\n result: unknown;\n error?: string;\n logs?: string[];\n}\n\n/**\n * An executor runs LLM-generated code in a sandbox, making the provided\n * tool functions callable as `codemode.*` inside the sandbox.\n *\n * Implementations should never throw — errors are returned in `ExecuteResult.error`.\n */\nexport interface Executor {\n execute(\n code: string,\n fns: Record<string, (...args: unknown[]) => Promise<unknown>>\n ): Promise<ExecuteResult>;\n}\n\n// -- ToolDispatcher (RPC target for tool calls from sandboxed Workers) --\n\n/**\n * An RpcTarget that dispatches tool calls from the sandboxed Worker\n * back to the host. Passed via Workers RPC to the dynamic Worker's\n * evaluate() method — no globalOutbound or Fetcher bindings needed.\n */\nexport class ToolDispatcher extends RpcTarget {\n #fns: Record<string, (...args: unknown[]) => Promise<unknown>>;\n\n constructor(fns: Record<string, (...args: unknown[]) => Promise<unknown>>) {\n super();\n this.#fns = fns;\n }\n\n async call(name: string, argsJson: string): Promise<string> {\n const fn = this.#fns[name];\n if (!fn) {\n return JSON.stringify({ error: `Tool \"${name}\" not found` });\n }\n try {\n const args = argsJson ? JSON.parse(argsJson) : {};\n const result = await fn(args);\n return JSON.stringify({ result });\n } catch (err) {\n return JSON.stringify({\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n}\n\n// -- DynamicWorkerExecutor (Cloudflare Workers) --\n\nexport interface DynamicWorkerExecutorOptions {\n loader: WorkerLoader;\n /**\n * Timeout in milliseconds for code execution. Defaults to 30000 (30s).\n */\n timeout?: number;\n /**\n * Controls outbound network access from sandboxed code.\n * - `null` (default): fetch() and connect() throw — sandbox is fully isolated.\n * - `undefined`: inherits parent Worker's network access (full internet).\n * - A `Fetcher`: all outbound requests route through this handler.\n */\n globalOutbound?: Fetcher | null;\n}\n\n/**\n * Executes code in an isolated Cloudflare Worker via WorkerLoader.\n * Tool calls are dispatched via Workers RPC — the host passes a\n * ToolDispatcher (RpcTarget) to the Worker's evaluate() method.\n *\n * External fetch() and connect() are blocked by default via\n * `globalOutbound: null` (runtime-enforced). Pass a Fetcher to\n * `globalOutbound` to allow controlled outbound access.\n */\nexport class DynamicWorkerExecutor implements Executor {\n #loader: WorkerLoader;\n #timeout: number;\n #globalOutbound: Fetcher | null;\n\n constructor(options: DynamicWorkerExecutorOptions) {\n this.#loader = options.loader;\n this.#timeout = options.timeout ?? 30000;\n this.#globalOutbound = options.globalOutbound ?? null;\n }\n\n async execute(\n code: string,\n fns: Record<string, (...args: unknown[]) => Promise<unknown>>\n ): Promise<ExecuteResult> {\n const timeoutMs = this.#timeout;\n\n const modulePrefix = [\n 'import { WorkerEntrypoint } from \"cloudflare:workers\";',\n \"\",\n \"export default class CodeExecutor extends WorkerEntrypoint {\",\n \" async evaluate(dispatcher) {\",\n \" const __logs = [];\",\n ' console.log = (...a) => { __logs.push(a.map(String).join(\" \")); };',\n ' console.warn = (...a) => { __logs.push(\"[warn] \" + a.map(String).join(\" \")); };',\n ' console.error = (...a) => { __logs.push(\"[error] \" + a.map(String).join(\" \")); };',\n \" const codemode = new Proxy({}, {\",\n \" get: (_, toolName) => async (args) => {\",\n \" const resJson = await dispatcher.call(String(toolName), JSON.stringify(args ?? {}));\",\n \" const data = JSON.parse(resJson);\",\n \" if (data.error) throw new Error(data.error);\",\n \" return data.result;\",\n \" }\",\n \" });\",\n \"\",\n \" try {\",\n \" const result = await Promise.race([\",\n \" (\"\n ].join(\"\\n\");\n\n const moduleSuffix = [\n \")(),\",\n ' new Promise((_, reject) => setTimeout(() => reject(new Error(\"Execution timed out\")), ' +\n timeoutMs +\n \"))\",\n \" ]);\",\n \" return { result, logs: __logs };\",\n \" } catch (err) {\",\n \" return { result: undefined, error: err.message, logs: __logs };\",\n \" }\",\n \" }\",\n \"}\"\n ].join(\"\\n\");\n\n const executorModule = modulePrefix + code + moduleSuffix;\n\n const dispatcher = new ToolDispatcher(fns);\n\n const worker = this.#loader.get(`codemode-${crypto.randomUUID()}`, () => ({\n compatibilityDate: \"2025-06-01\",\n compatibilityFlags: [\"nodejs_compat\"],\n mainModule: \"executor.js\",\n modules: {\n \"executor.js\": executorModule\n },\n globalOutbound: this.#globalOutbound\n }));\n\n const entrypoint = worker.getEntrypoint() as unknown as {\n evaluate(dispatcher: ToolDispatcher): Promise<{\n result: unknown;\n error?: string;\n logs?: string[];\n }>;\n };\n const response = await entrypoint.evaluate(dispatcher);\n\n if (response.error) {\n return { result: undefined, error: response.error, logs: response.logs };\n }\n\n return { result: response.result, logs: response.logs };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmCA,IAAa,iBAAb,cAAoC,UAAU;CAC5C;CAEA,YAAY,KAA+D;AACzE,SAAO;AACP,QAAKA,MAAO;;CAGd,MAAM,KAAK,MAAc,UAAmC;EAC1D,MAAM,KAAK,MAAKA,IAAK;AACrB,MAAI,CAAC,GACH,QAAO,KAAK,UAAU,EAAE,OAAO,SAAS,KAAK,cAAc,CAAC;AAE9D,MAAI;GAEF,MAAM,SAAS,MAAM,GADR,WAAW,KAAK,MAAM,SAAS,GAAG,EAAE,CACpB;AAC7B,UAAO,KAAK,UAAU,EAAE,QAAQ,CAAC;WAC1B,KAAK;AACZ,UAAO,KAAK,UAAU,EACpB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACxD,CAAC;;;;;;;;;;;;;AA+BR,IAAa,wBAAb,MAAuD;CACrD;CACA;CACA;CAEA,YAAY,SAAuC;AACjD,QAAKC,SAAU,QAAQ;AACvB,QAAKC,UAAW,QAAQ,WAAW;AACnC,QAAKC,iBAAkB,QAAQ,kBAAkB;;CAGnD,MAAM,QACJ,MACA,KACwB;EACxB,MAAM,YAAY,MAAKD;EAEvB,MAAM,eAAe;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EAEZ,MAAM,eAAe;GACnB;GACA,qGACE,YACA;GACF;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EAEZ,MAAM,iBAAiB,eAAe,OAAO;EAE7C,MAAM,aAAa,IAAI,eAAe,IAAI;EAmB1C,MAAM,WAAW,MAjBF,MAAKD,OAAQ,IAAI,YAAY,OAAO,YAAY,WAAW;GACxE,mBAAmB;GACnB,oBAAoB,CAAC,gBAAgB;GACrC,YAAY;GACZ,SAAS,EACP,eAAe,gBAChB;GACD,gBAAgB,MAAKE;GACtB,EAAE,CAEuB,eAAe,CAOP,SAAS,WAAW;AAEtD,MAAI,SAAS,MACX,QAAO;GAAE,QAAQ;GAAW,OAAO,SAAS;GAAO,MAAM,SAAS;GAAM;AAG1E,SAAO;GAAE,QAAQ,SAAS;GAAQ,MAAM,SAAS;GAAM"}
@@ -0,0 +1,138 @@
1
+ import { createAuxiliaryTypeStore, createTypeAlias, printNode, zodToTs } from "zod-to-ts";
2
+
3
+ //#region src/types.ts
4
+ const JS_RESERVED = new Set([
5
+ "abstract",
6
+ "arguments",
7
+ "await",
8
+ "boolean",
9
+ "break",
10
+ "byte",
11
+ "case",
12
+ "catch",
13
+ "char",
14
+ "class",
15
+ "const",
16
+ "continue",
17
+ "debugger",
18
+ "default",
19
+ "delete",
20
+ "do",
21
+ "double",
22
+ "else",
23
+ "enum",
24
+ "eval",
25
+ "export",
26
+ "extends",
27
+ "false",
28
+ "final",
29
+ "finally",
30
+ "float",
31
+ "for",
32
+ "function",
33
+ "goto",
34
+ "if",
35
+ "implements",
36
+ "import",
37
+ "in",
38
+ "instanceof",
39
+ "int",
40
+ "interface",
41
+ "let",
42
+ "long",
43
+ "native",
44
+ "new",
45
+ "null",
46
+ "package",
47
+ "private",
48
+ "protected",
49
+ "public",
50
+ "return",
51
+ "short",
52
+ "static",
53
+ "super",
54
+ "switch",
55
+ "synchronized",
56
+ "this",
57
+ "throw",
58
+ "throws",
59
+ "transient",
60
+ "true",
61
+ "try",
62
+ "typeof",
63
+ "undefined",
64
+ "var",
65
+ "void",
66
+ "volatile",
67
+ "while",
68
+ "with",
69
+ "yield"
70
+ ]);
71
+ /**
72
+ * Sanitize a tool name into a valid JavaScript identifier.
73
+ * Replaces hyphens, dots, and spaces with `_`, strips other invalid chars,
74
+ * prefixes digit-leading names with `_`, and appends `_` to JS reserved words.
75
+ */
76
+ function sanitizeToolName(name) {
77
+ if (!name) return "_";
78
+ let sanitized = name.replace(/[-.\s]/g, "_");
79
+ sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, "");
80
+ if (!sanitized) return "_";
81
+ if (/^[0-9]/.test(sanitized)) sanitized = "_" + sanitized;
82
+ if (JS_RESERVED.has(sanitized)) sanitized = sanitized + "_";
83
+ return sanitized;
84
+ }
85
+ function toCamelCase(str) {
86
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/^[a-z]/, (letter) => letter.toUpperCase());
87
+ }
88
+ /**
89
+ * Extract field descriptions from a Zod object schema's `.shape`, if available.
90
+ * Returns an array of `@param input.fieldName - description` lines.
91
+ */
92
+ function extractParamDescriptions(schema) {
93
+ const descriptions = [];
94
+ const shape = schema.shape;
95
+ if (!shape || typeof shape !== "object") return descriptions;
96
+ for (const [fieldName, fieldSchema] of Object.entries(shape)) {
97
+ const desc = fieldSchema.description;
98
+ if (desc) descriptions.push(`@param input.${fieldName} - ${desc}`);
99
+ }
100
+ return descriptions;
101
+ }
102
+ /**
103
+ * Generate TypeScript type definitions from tool descriptors or an AI SDK ToolSet.
104
+ * These types can be included in tool descriptions to help LLMs write correct code.
105
+ */
106
+ function generateTypes(tools) {
107
+ let availableTools = "";
108
+ let availableTypes = "";
109
+ const auxiliaryTypeStore = createAuxiliaryTypeStore();
110
+ for (const [toolName, tool] of Object.entries(tools)) {
111
+ const inputSchema = "inputSchema" in tool ? tool.inputSchema : tool.parameters;
112
+ const outputSchema = "outputSchema" in tool ? tool.outputSchema : void 0;
113
+ const description = tool.description;
114
+ const safeName = sanitizeToolName(toolName);
115
+ const inputType = printNode(createTypeAlias(zodToTs(inputSchema, { auxiliaryTypeStore }).node, `${toCamelCase(safeName)}Input`));
116
+ const outputType = outputSchema ? printNode(createTypeAlias(zodToTs(outputSchema, { auxiliaryTypeStore }).node, `${toCamelCase(safeName)}Output`)) : `type ${toCamelCase(safeName)}Output = unknown`;
117
+ availableTypes += `\n${inputType.trim()}`;
118
+ availableTypes += `\n${outputType.trim()}`;
119
+ const paramDescs = extractParamDescriptions(inputSchema);
120
+ const jsdocLines = [];
121
+ if (description?.trim()) jsdocLines.push(description.trim());
122
+ else jsdocLines.push(toolName);
123
+ for (const pd of paramDescs) jsdocLines.push(pd);
124
+ const jsdocBody = jsdocLines.map((l) => `\t * ${l}`).join("\n");
125
+ availableTools += `\n\t/**\n${jsdocBody}\n\t */`;
126
+ availableTools += `\n\t${safeName}: (input: ${toCamelCase(safeName)}Input) => Promise<${toCamelCase(safeName)}Output>;`;
127
+ availableTools += "\n";
128
+ }
129
+ availableTools = `\ndeclare const codemode: {${availableTools}}`;
130
+ return `
131
+ ${availableTypes}
132
+ ${availableTools}
133
+ `.trim();
134
+ }
135
+
136
+ //#endregion
137
+ export { sanitizeToolName as n, generateTypes as t };
138
+ //# sourceMappingURL=types-B9g5T2nd.js.map