@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/CHANGELOG.md +22 -0
- package/README.md +174 -247
- package/dist/ai.d.ts +27 -27
- package/dist/ai.js +67 -136
- package/dist/ai.js.map +1 -1
- package/dist/executor-Czw9jKZH.d.ts +96 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/types-B9g5T2nd.js +138 -0
- package/dist/types-B9g5T2nd.js.map +1 -0
- package/e2e/codemode.spec.ts +124 -0
- package/e2e/playwright.config.ts +24 -0
- package/e2e/worker.ts +144 -0
- package/e2e/wrangler.jsonc +14 -0
- package/package.json +15 -4
- package/scripts/build.ts +1 -2
- package/src/ai.ts +1 -247
- package/src/executor.ts +170 -0
- package/src/index.ts +13 -0
- package/src/tests/cloudflare-test.d.ts +5 -0
- package/src/tests/executor.test.ts +224 -0
- package/src/tests/tool.test.ts +454 -0
- package/src/tests/tsconfig.json +10 -0
- package/src/tests/types.test.ts +171 -0
- package/src/tool.ts +131 -0
- package/src/types.ts +202 -0
- package/vitest.config.ts +17 -0
- package/wrangler.jsonc +16 -0
package/dist/ai.js
CHANGED
|
@@ -1,148 +1,79 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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/
|
|
10
|
-
|
|
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
|
-
|
|
9
|
+
Available:
|
|
10
|
+
{{types}}
|
|
48
11
|
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
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 {
|
|
78
|
+
export { createCodeTool };
|
|
148
79
|
//# sourceMappingURL=ai.js.map
|
package/dist/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","names":[
|
|
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|