@tanstack/ai-isolate-cloudflare 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/README.md +118 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/isolate-driver.d.ts +72 -0
- package/dist/esm/isolate-driver.js +174 -0
- package/dist/esm/isolate-driver.js.map +1 -0
- package/dist/esm/types.d.ts +72 -0
- package/dist/esm/worker/index.d.ts +35 -0
- package/dist/esm/worker/index.js +132 -0
- package/dist/esm/worker/index.js.map +1 -0
- package/dist/esm/worker/wrap-code.d.ts +13 -0
- package/dist/esm/worker/wrap-code.js +102 -0
- package/dist/esm/worker/wrap-code.js.map +1 -0
- package/package.json +69 -0
- package/src/index.ts +30 -0
- package/src/isolate-driver.ts +310 -0
- package/src/types.ts +80 -0
- package/src/worker/index.ts +207 -0
- package/src/worker/wrap-code.ts +125 -0
- package/wrangler.toml +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @tanstack/ai-isolate-cloudflare
|
|
2
|
+
|
|
3
|
+
Cloudflare Workers driver for TanStack AI Code Mode.
|
|
4
|
+
|
|
5
|
+
This package runs generated JavaScript in a Worker and keeps `external_*` tool execution on your host process through a request/response loop.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @tanstack/ai-isolate-cloudflare
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Environment Guidance (Conservative)
|
|
14
|
+
|
|
15
|
+
- **Local development:** supported with the package's Miniflare dev server (`pnpm dev:worker`)
|
|
16
|
+
- **Remote dev:** supported with `wrangler dev --remote`
|
|
17
|
+
- **Production:** evaluate carefully before rollout; dynamic code execution with `unsafe_eval` has platform/security constraints and is often treated as an advanced or enterprise setup
|
|
18
|
+
|
|
19
|
+
If you need a fully local setup without Cloudflare constraints, prefer `@tanstack/ai-isolate-node` or `@tanstack/ai-isolate-quickjs`.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { chat, toolDefinition } from '@tanstack/ai'
|
|
25
|
+
import { createCodeMode } from '@tanstack/ai-code-mode'
|
|
26
|
+
import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'
|
|
27
|
+
import { z } from 'zod'
|
|
28
|
+
|
|
29
|
+
const fetchWeather = toolDefinition({
|
|
30
|
+
name: 'fetchWeather',
|
|
31
|
+
description: 'Get weather for a city',
|
|
32
|
+
inputSchema: z.object({ location: z.string() }),
|
|
33
|
+
outputSchema: z.object({
|
|
34
|
+
temperature: z.number(),
|
|
35
|
+
condition: z.string(),
|
|
36
|
+
}),
|
|
37
|
+
}).server(async ({ location }) => {
|
|
38
|
+
return { temperature: 72, condition: `sunny in ${location}` }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const driver = createCloudflareIsolateDriver({
|
|
42
|
+
workerUrl: 'http://localhost:8787', // local dev server URL
|
|
43
|
+
authorization: 'Bearer your-secret-token', // optional
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const { tool, systemPrompt } = createCodeMode({
|
|
47
|
+
driver,
|
|
48
|
+
tools: [fetchWeather],
|
|
49
|
+
timeout: 30_000,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const result = await chat({
|
|
53
|
+
adapter: yourTextAdapter,
|
|
54
|
+
model: 'gpt-4o-mini',
|
|
55
|
+
systemPrompts: ['You are a helpful assistant.', systemPrompt],
|
|
56
|
+
tools: [tool],
|
|
57
|
+
messages: [{ role: 'user', content: 'Compare weather in Tokyo and Paris' }],
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Worker Setup
|
|
62
|
+
|
|
63
|
+
### Option 1: Local Miniflare server
|
|
64
|
+
|
|
65
|
+
From this package directory:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm dev:worker
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This starts a local Worker endpoint (default `http://localhost:8787`) with `UNSAFE_EVAL` configured for local testing.
|
|
72
|
+
|
|
73
|
+
### Option 2: Wrangler remote dev
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
wrangler dev --remote
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This runs through Cloudflare's network and can be useful when validating behavior against the hosted runtime.
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
### `createCloudflareIsolateDriver(config)`
|
|
84
|
+
|
|
85
|
+
Creates a driver that delegates code execution to a Worker endpoint.
|
|
86
|
+
|
|
87
|
+
- `workerUrl` (required): URL of the Worker endpoint
|
|
88
|
+
- `authorization` (optional): value sent as `Authorization` header
|
|
89
|
+
- `timeout` (optional): request timeout in ms (default: `30000`)
|
|
90
|
+
- `maxToolRounds` (optional): max Worker <-> host tool callback rounds (default: `10`)
|
|
91
|
+
|
|
92
|
+
## Worker Entry Export
|
|
93
|
+
|
|
94
|
+
The package also exports a Worker entrypoint:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import worker from '@tanstack/ai-isolate-cloudflare/worker'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use this when you want to bundle or compose the provided worker logic in your own Worker project.
|
|
101
|
+
|
|
102
|
+
## Security Notes
|
|
103
|
+
|
|
104
|
+
- Protect the worker endpoint if it is reachable outside trusted infrastructure.
|
|
105
|
+
- Validate auth headers server-side if you set `authorization` in the driver.
|
|
106
|
+
- Add rate limiting and request monitoring for untrusted traffic.
|
|
107
|
+
- Treat generated code execution as a high-risk surface; keep strict input and network boundaries.
|
|
108
|
+
|
|
109
|
+
## Architecture
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Host Driver Cloudflare Worker
|
|
113
|
+
----------- ------------------
|
|
114
|
+
1) send code + tool schemas -> execute until tool call or completion
|
|
115
|
+
2) receive tool requests <- need_tools payload
|
|
116
|
+
3) execute tools locally -> send toolResults
|
|
117
|
+
4) receive final result <- success/error payload
|
|
118
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tanstack/ai-isolate-cloudflare
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare Workers driver for TanStack AI Code Mode.
|
|
5
|
+
* Execute LLM-generated code on Cloudflare's global edge network.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'
|
|
10
|
+
*
|
|
11
|
+
* const driver = createCloudflareIsolateDriver({
|
|
12
|
+
* workerUrl: 'https://your-worker.workers.dev',
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
export { createCloudflareIsolateDriver, type CloudflareIsolateDriverConfig, } from './isolate-driver.js';
|
|
19
|
+
export type { ExecuteRequest, ExecuteResponse, ToolSchema, ToolCallRequest, ToolResultPayload, } from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { IsolateDriver } from '@tanstack/ai-code-mode';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the Cloudflare Workers isolate driver
|
|
4
|
+
*/
|
|
5
|
+
export interface CloudflareIsolateDriverConfig {
|
|
6
|
+
/**
|
|
7
|
+
* URL of the deployed Cloudflare Worker
|
|
8
|
+
* For local development, use: http://localhost:8787
|
|
9
|
+
*/
|
|
10
|
+
workerUrl: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional authorization header value
|
|
13
|
+
* Useful for protecting your Worker endpoint
|
|
14
|
+
*/
|
|
15
|
+
authorization?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Default execution timeout in ms (default: 30000)
|
|
18
|
+
*/
|
|
19
|
+
timeout?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Maximum number of tool callback rounds (default: 10)
|
|
22
|
+
* Prevents infinite loops
|
|
23
|
+
*/
|
|
24
|
+
maxToolRounds?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a Cloudflare Workers isolate driver
|
|
28
|
+
*
|
|
29
|
+
* This driver executes code on Cloudflare's global edge network,
|
|
30
|
+
* providing true distributed execution capabilities.
|
|
31
|
+
*
|
|
32
|
+
* Tool calls are handled via a request/response loop:
|
|
33
|
+
* 1. Code is sent to the Worker
|
|
34
|
+
* 2. Worker executes until it needs a tool
|
|
35
|
+
* 3. Tool call is returned to the driver
|
|
36
|
+
* 4. Driver executes the tool locally
|
|
37
|
+
* 5. Result is sent back to the Worker
|
|
38
|
+
* 6. Worker continues execution
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'
|
|
43
|
+
*
|
|
44
|
+
* // For local development with wrangler
|
|
45
|
+
* const driver = createCloudflareIsolateDriver({
|
|
46
|
+
* workerUrl: 'http://localhost:8787',
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* // For production
|
|
50
|
+
* const driver = createCloudflareIsolateDriver({
|
|
51
|
+
* workerUrl: 'https://code-mode-worker.your-account.workers.dev',
|
|
52
|
+
* authorization: 'Bearer your-secret-token',
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* const context = await driver.createContext({
|
|
56
|
+
* bindings: {
|
|
57
|
+
* readFile: {
|
|
58
|
+
* name: 'readFile',
|
|
59
|
+
* description: 'Read a file',
|
|
60
|
+
* inputSchema: { type: 'object', properties: { path: { type: 'string' } } },
|
|
61
|
+
* execute: async ({ path }) => fs.readFile(path, 'utf-8'),
|
|
62
|
+
* },
|
|
63
|
+
* },
|
|
64
|
+
* })
|
|
65
|
+
*
|
|
66
|
+
* const result = await context.execute(`
|
|
67
|
+
* const content = await readFile({ path: './data.json' })
|
|
68
|
+
* return JSON.parse(content)
|
|
69
|
+
* `)
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function createCloudflareIsolateDriver(config: CloudflareIsolateDriverConfig): IsolateDriver;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
function bindingsToSchemas(bindings) {
|
|
2
|
+
return Object.entries(bindings).map(([name, binding]) => ({
|
|
3
|
+
name,
|
|
4
|
+
description: binding.description,
|
|
5
|
+
inputSchema: binding.inputSchema
|
|
6
|
+
}));
|
|
7
|
+
}
|
|
8
|
+
function normalizeError(error) {
|
|
9
|
+
if (error instanceof Error) {
|
|
10
|
+
return { name: error.name, message: error.message };
|
|
11
|
+
}
|
|
12
|
+
if (typeof error === "object" && error !== null) {
|
|
13
|
+
const e = error;
|
|
14
|
+
return {
|
|
15
|
+
name: String(e.name || "Error"),
|
|
16
|
+
message: String(e.message || JSON.stringify(error))
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { name: "Error", message: String(error) };
|
|
20
|
+
}
|
|
21
|
+
class CloudflareIsolateContext {
|
|
22
|
+
workerUrl;
|
|
23
|
+
authorization;
|
|
24
|
+
timeout;
|
|
25
|
+
maxToolRounds;
|
|
26
|
+
bindings;
|
|
27
|
+
disposed = false;
|
|
28
|
+
constructor(workerUrl, bindings, timeout, maxToolRounds, authorization) {
|
|
29
|
+
this.workerUrl = workerUrl;
|
|
30
|
+
this.bindings = bindings;
|
|
31
|
+
this.timeout = timeout;
|
|
32
|
+
this.maxToolRounds = maxToolRounds;
|
|
33
|
+
this.authorization = authorization;
|
|
34
|
+
}
|
|
35
|
+
async execute(code) {
|
|
36
|
+
if (this.disposed) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
error: {
|
|
40
|
+
name: "DisposedError",
|
|
41
|
+
message: "Context has been disposed"
|
|
42
|
+
},
|
|
43
|
+
logs: []
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const tools = bindingsToSchemas(this.bindings);
|
|
47
|
+
let toolResults;
|
|
48
|
+
let allLogs = [];
|
|
49
|
+
let rounds = 0;
|
|
50
|
+
while (rounds < this.maxToolRounds) {
|
|
51
|
+
rounds++;
|
|
52
|
+
const request = {
|
|
53
|
+
code,
|
|
54
|
+
tools,
|
|
55
|
+
toolResults,
|
|
56
|
+
timeout: this.timeout
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
const headers = {
|
|
60
|
+
"Content-Type": "application/json"
|
|
61
|
+
};
|
|
62
|
+
if (this.authorization) {
|
|
63
|
+
headers["Authorization"] = this.authorization;
|
|
64
|
+
}
|
|
65
|
+
const response = await fetch(this.workerUrl, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers,
|
|
68
|
+
body: JSON.stringify(request)
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
const errorText = await response.text();
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: {
|
|
75
|
+
name: "WorkerError",
|
|
76
|
+
message: `Worker returned ${response.status}: ${errorText}`
|
|
77
|
+
},
|
|
78
|
+
logs: allLogs
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const result = await response.json();
|
|
82
|
+
if (result.status === "error") {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: result.error,
|
|
86
|
+
logs: allLogs
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (result.status === "done") {
|
|
90
|
+
allLogs = [...allLogs, ...result.logs];
|
|
91
|
+
return {
|
|
92
|
+
success: result.success,
|
|
93
|
+
value: result.value,
|
|
94
|
+
error: result.error,
|
|
95
|
+
logs: allLogs
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
allLogs = [...allLogs, ...result.logs];
|
|
99
|
+
toolResults = {};
|
|
100
|
+
for (const toolCall of result.toolCalls) {
|
|
101
|
+
const binding = this.bindings[toolCall.name];
|
|
102
|
+
if (!binding) {
|
|
103
|
+
toolResults[toolCall.id] = {
|
|
104
|
+
success: false,
|
|
105
|
+
error: `Unknown tool: ${toolCall.name}`
|
|
106
|
+
};
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const toolResult = await binding.execute(toolCall.args);
|
|
111
|
+
toolResults[toolCall.id] = {
|
|
112
|
+
success: true,
|
|
113
|
+
value: toolResult
|
|
114
|
+
};
|
|
115
|
+
} catch (toolError) {
|
|
116
|
+
const err = normalizeError(toolError);
|
|
117
|
+
toolResults[toolCall.id] = {
|
|
118
|
+
success: false,
|
|
119
|
+
error: err.message
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch (fetchError) {
|
|
124
|
+
const err = normalizeError(fetchError);
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: {
|
|
128
|
+
name: "NetworkError",
|
|
129
|
+
message: `Failed to communicate with Worker: ${err.message}`
|
|
130
|
+
},
|
|
131
|
+
logs: allLogs
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
error: {
|
|
138
|
+
name: "MaxRoundsExceeded",
|
|
139
|
+
message: `Exceeded maximum tool callback rounds (${this.maxToolRounds})`
|
|
140
|
+
},
|
|
141
|
+
logs: allLogs
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
dispose() {
|
|
145
|
+
this.disposed = true;
|
|
146
|
+
return Promise.resolve();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function createCloudflareIsolateDriver(config) {
|
|
150
|
+
const {
|
|
151
|
+
workerUrl,
|
|
152
|
+
authorization,
|
|
153
|
+
timeout: defaultTimeout = 3e4,
|
|
154
|
+
maxToolRounds = 10
|
|
155
|
+
} = config;
|
|
156
|
+
return {
|
|
157
|
+
createContext(isolateConfig) {
|
|
158
|
+
const timeout = isolateConfig.timeout ?? defaultTimeout;
|
|
159
|
+
return Promise.resolve(
|
|
160
|
+
new CloudflareIsolateContext(
|
|
161
|
+
workerUrl,
|
|
162
|
+
isolateConfig.bindings,
|
|
163
|
+
timeout,
|
|
164
|
+
maxToolRounds,
|
|
165
|
+
authorization
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export {
|
|
172
|
+
createCloudflareIsolateDriver
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=isolate-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolate-driver.js","sources":["../../src/isolate-driver.ts"],"sourcesContent":["import type {\n ExecutionResult,\n IsolateConfig,\n IsolateContext,\n IsolateDriver,\n ToolBinding,\n} from '@tanstack/ai-code-mode'\nimport type {\n ExecuteRequest,\n ExecuteResponse,\n ToolResultPayload,\n ToolSchema,\n} from './types'\n\n/**\n * Configuration for the Cloudflare Workers isolate driver\n */\nexport interface CloudflareIsolateDriverConfig {\n /**\n * URL of the deployed Cloudflare Worker\n * For local development, use: http://localhost:8787\n */\n workerUrl: string\n\n /**\n * Optional authorization header value\n * Useful for protecting your Worker endpoint\n */\n authorization?: string\n\n /**\n * Default execution timeout in ms (default: 30000)\n */\n timeout?: number\n\n /**\n * Maximum number of tool callback rounds (default: 10)\n * Prevents infinite loops\n */\n maxToolRounds?: number\n}\n\n/**\n * Convert tool bindings to schemas for the Worker\n */\nfunction bindingsToSchemas(\n bindings: Record<string, ToolBinding>,\n): Array<ToolSchema> {\n return Object.entries(bindings).map(([name, binding]) => ({\n name,\n description: binding.description,\n inputSchema: binding.inputSchema,\n }))\n}\n\n/**\n * Normalize errors from various sources\n */\nfunction normalizeError(error: unknown): { name: string; message: string } {\n if (error instanceof Error) {\n return { name: error.name, message: error.message }\n }\n if (typeof error === 'object' && error !== null) {\n const e = error as Record<string, unknown>\n return {\n name: String(e.name || 'Error'),\n message: String(e.message || JSON.stringify(error)),\n }\n }\n return { name: 'Error', message: String(error) }\n}\n\n/**\n * IsolateContext implementation using Cloudflare Workers\n */\nclass CloudflareIsolateContext implements IsolateContext {\n private workerUrl: string\n private authorization?: string\n private timeout: number\n private maxToolRounds: number\n private bindings: Record<string, ToolBinding>\n private disposed = false\n\n constructor(\n workerUrl: string,\n bindings: Record<string, ToolBinding>,\n timeout: number,\n maxToolRounds: number,\n authorization?: string,\n ) {\n this.workerUrl = workerUrl\n this.bindings = bindings\n this.timeout = timeout\n this.maxToolRounds = maxToolRounds\n this.authorization = authorization\n }\n\n async execute<T = unknown>(code: string): Promise<ExecutionResult<T>> {\n if (this.disposed) {\n return {\n success: false,\n error: {\n name: 'DisposedError',\n message: 'Context has been disposed',\n },\n logs: [],\n }\n }\n\n const tools = bindingsToSchemas(this.bindings)\n let toolResults: Record<string, ToolResultPayload> | undefined\n let allLogs: Array<string> = []\n let rounds = 0\n\n // Request/response loop for tool callbacks\n while (rounds < this.maxToolRounds) {\n rounds++\n\n const request: ExecuteRequest = {\n code,\n tools,\n toolResults,\n timeout: this.timeout,\n }\n\n try {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n\n if (this.authorization) {\n headers['Authorization'] = this.authorization\n }\n\n const response = await fetch(this.workerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify(request),\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n return {\n success: false,\n error: {\n name: 'WorkerError',\n message: `Worker returned ${response.status}: ${errorText}`,\n },\n logs: allLogs,\n }\n }\n\n const result: ExecuteResponse = await response.json()\n\n if (result.status === 'error') {\n return {\n success: false,\n error: result.error,\n logs: allLogs,\n }\n }\n\n if (result.status === 'done') {\n allLogs = [...allLogs, ...result.logs]\n return {\n success: result.success,\n value: result.value as T,\n error: result.error,\n logs: allLogs,\n }\n }\n\n // status === 'need_tools'\n // Collect logs from this round\n allLogs = [...allLogs, ...result.logs]\n\n // Execute tool calls locally\n toolResults = {}\n\n for (const toolCall of result.toolCalls) {\n const binding = this.bindings[toolCall.name] as\n | ToolBinding\n | undefined\n\n if (!binding) {\n toolResults[toolCall.id] = {\n success: false,\n error: `Unknown tool: ${toolCall.name}`,\n }\n continue\n }\n\n try {\n const toolResult = await binding.execute(toolCall.args)\n toolResults[toolCall.id] = {\n success: true,\n value: toolResult,\n }\n } catch (toolError) {\n const err = normalizeError(toolError)\n toolResults[toolCall.id] = {\n success: false,\n error: err.message,\n }\n }\n }\n\n // Continue loop to send results back to Worker\n } catch (fetchError) {\n const err = normalizeError(fetchError)\n return {\n success: false,\n error: {\n name: 'NetworkError',\n message: `Failed to communicate with Worker: ${err.message}`,\n },\n logs: allLogs,\n }\n }\n }\n\n // Max rounds exceeded\n return {\n success: false,\n error: {\n name: 'MaxRoundsExceeded',\n message: `Exceeded maximum tool callback rounds (${this.maxToolRounds})`,\n },\n logs: allLogs,\n }\n }\n\n dispose(): Promise<void> {\n this.disposed = true\n return Promise.resolve()\n }\n}\n\n/**\n * Create a Cloudflare Workers isolate driver\n *\n * This driver executes code on Cloudflare's global edge network,\n * providing true distributed execution capabilities.\n *\n * Tool calls are handled via a request/response loop:\n * 1. Code is sent to the Worker\n * 2. Worker executes until it needs a tool\n * 3. Tool call is returned to the driver\n * 4. Driver executes the tool locally\n * 5. Result is sent back to the Worker\n * 6. Worker continues execution\n *\n * @example\n * ```typescript\n * import { createCloudflareIsolateDriver } from '@tanstack/ai-isolate-cloudflare'\n *\n * // For local development with wrangler\n * const driver = createCloudflareIsolateDriver({\n * workerUrl: 'http://localhost:8787',\n * })\n *\n * // For production\n * const driver = createCloudflareIsolateDriver({\n * workerUrl: 'https://code-mode-worker.your-account.workers.dev',\n * authorization: 'Bearer your-secret-token',\n * })\n *\n * const context = await driver.createContext({\n * bindings: {\n * readFile: {\n * name: 'readFile',\n * description: 'Read a file',\n * inputSchema: { type: 'object', properties: { path: { type: 'string' } } },\n * execute: async ({ path }) => fs.readFile(path, 'utf-8'),\n * },\n * },\n * })\n *\n * const result = await context.execute(`\n * const content = await readFile({ path: './data.json' })\n * return JSON.parse(content)\n * `)\n * ```\n */\nexport function createCloudflareIsolateDriver(\n config: CloudflareIsolateDriverConfig,\n): IsolateDriver {\n const {\n workerUrl,\n authorization,\n timeout: defaultTimeout = 30000,\n maxToolRounds = 10,\n } = config\n\n return {\n createContext(isolateConfig: IsolateConfig): Promise<IsolateContext> {\n const timeout = isolateConfig.timeout ?? defaultTimeout\n\n return Promise.resolve(\n new CloudflareIsolateContext(\n workerUrl,\n isolateConfig.bindings,\n timeout,\n maxToolRounds,\n authorization,\n ),\n )\n },\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types between the Cloudflare Worker and the driver
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Tool schema passed to the worker
|
|
6
|
+
*/
|
|
7
|
+
export interface ToolSchema {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
inputSchema: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Request to execute code in the worker
|
|
14
|
+
*/
|
|
15
|
+
export interface ExecuteRequest {
|
|
16
|
+
/** The code to execute */
|
|
17
|
+
code: string;
|
|
18
|
+
/** Tool schemas available for the code to call */
|
|
19
|
+
tools: Array<ToolSchema>;
|
|
20
|
+
/** Results from previous tool calls (for continuation) */
|
|
21
|
+
toolResults?: Record<string, ToolResultPayload>;
|
|
22
|
+
/** Execution timeout in ms */
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tool call requested by the worker
|
|
27
|
+
*/
|
|
28
|
+
export interface ToolCallRequest {
|
|
29
|
+
/** Unique ID for this tool call */
|
|
30
|
+
id: string;
|
|
31
|
+
/** Name of the tool to call */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Arguments to pass to the tool */
|
|
34
|
+
args: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Result of a tool call
|
|
38
|
+
*/
|
|
39
|
+
export interface ToolResultPayload {
|
|
40
|
+
/** Whether the tool call succeeded */
|
|
41
|
+
success: boolean;
|
|
42
|
+
/** The result value if successful */
|
|
43
|
+
value?: unknown;
|
|
44
|
+
/** Error message if failed */
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Response from the worker - either done or needs tool calls
|
|
49
|
+
*/
|
|
50
|
+
export type ExecuteResponse = {
|
|
51
|
+
status: 'done';
|
|
52
|
+
success: boolean;
|
|
53
|
+
value?: unknown;
|
|
54
|
+
error?: {
|
|
55
|
+
name: string;
|
|
56
|
+
message: string;
|
|
57
|
+
stack?: string;
|
|
58
|
+
};
|
|
59
|
+
logs: Array<string>;
|
|
60
|
+
} | {
|
|
61
|
+
status: 'need_tools';
|
|
62
|
+
toolCalls: Array<ToolCallRequest>;
|
|
63
|
+
logs: Array<string>;
|
|
64
|
+
/** Continuation state to send back with tool results */
|
|
65
|
+
continuationId: string;
|
|
66
|
+
} | {
|
|
67
|
+
status: 'error';
|
|
68
|
+
error: {
|
|
69
|
+
name: string;
|
|
70
|
+
message: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Worker for Code Mode execution
|
|
3
|
+
*
|
|
4
|
+
* This Worker executes JavaScript code in a V8 isolate on Cloudflare's edge network.
|
|
5
|
+
* Tool calls are handled via a request/response loop with the driver.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. Receive code + tool schemas
|
|
9
|
+
* 2. Execute code, collecting any tool calls
|
|
10
|
+
* 3. If tool calls are needed, return them to the driver
|
|
11
|
+
* 4. Driver executes tools locally, sends results back
|
|
12
|
+
* 5. Re-execute with tool results injected
|
|
13
|
+
* 6. Return final result
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* UnsafeEval binding type
|
|
17
|
+
* This is only available in local development with wrangler dev
|
|
18
|
+
*/
|
|
19
|
+
interface UnsafeEval {
|
|
20
|
+
eval: (code: string) => unknown;
|
|
21
|
+
}
|
|
22
|
+
interface Env {
|
|
23
|
+
/**
|
|
24
|
+
* UnsafeEval binding - provides eval() for local development
|
|
25
|
+
* Configured in wrangler.toml as an unsafe binding
|
|
26
|
+
*/
|
|
27
|
+
UNSAFE_EVAL?: UnsafeEval;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Main Worker fetch handler
|
|
31
|
+
*/
|
|
32
|
+
declare const _default: {
|
|
33
|
+
fetch(request: Request, env: Env, _ctx: ExecutionContext): Promise<Response>;
|
|
34
|
+
};
|
|
35
|
+
export default _default;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { wrapCode } from "./wrap-code.js";
|
|
2
|
+
async function executeCode(request, env) {
|
|
3
|
+
const { code, tools, toolResults, timeout = 3e4 } = request;
|
|
4
|
+
if (!env.UNSAFE_EVAL) {
|
|
5
|
+
return {
|
|
6
|
+
status: "error",
|
|
7
|
+
error: {
|
|
8
|
+
name: "UnsafeEvalNotAvailable",
|
|
9
|
+
message: "UNSAFE_EVAL binding is not available. This Worker requires the unsafe_eval binding for local development. For production, consider using Workers for Platforms."
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const wrappedCode = wrapCode(code, tools, toolResults);
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
17
|
+
try {
|
|
18
|
+
const result = await env.UNSAFE_EVAL.eval(wrappedCode);
|
|
19
|
+
clearTimeout(timeoutId);
|
|
20
|
+
if (result.status === "need_tools") {
|
|
21
|
+
return {
|
|
22
|
+
status: "need_tools",
|
|
23
|
+
toolCalls: result.toolCalls || [],
|
|
24
|
+
logs: result.logs,
|
|
25
|
+
continuationId: crypto.randomUUID()
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
status: "done",
|
|
30
|
+
success: result.success ?? false,
|
|
31
|
+
value: result.value,
|
|
32
|
+
error: result.error,
|
|
33
|
+
logs: result.logs
|
|
34
|
+
};
|
|
35
|
+
} catch (evalError) {
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
if (controller.signal.aborted) {
|
|
38
|
+
return {
|
|
39
|
+
status: "error",
|
|
40
|
+
error: {
|
|
41
|
+
name: "TimeoutError",
|
|
42
|
+
message: `Execution timed out after ${timeout}ms`
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const error = evalError;
|
|
47
|
+
return {
|
|
48
|
+
status: "done",
|
|
49
|
+
success: false,
|
|
50
|
+
error: {
|
|
51
|
+
name: error.name || "EvalError",
|
|
52
|
+
message: error.message || String(error),
|
|
53
|
+
stack: error.stack
|
|
54
|
+
},
|
|
55
|
+
logs: []
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const err = error;
|
|
60
|
+
return {
|
|
61
|
+
status: "error",
|
|
62
|
+
error: {
|
|
63
|
+
name: err.name || "Error",
|
|
64
|
+
message: err.message || String(err)
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const index = {
|
|
70
|
+
async fetch(request, env, _ctx) {
|
|
71
|
+
if (request.method === "OPTIONS") {
|
|
72
|
+
return new Response(null, {
|
|
73
|
+
headers: {
|
|
74
|
+
"Access-Control-Allow-Origin": "*",
|
|
75
|
+
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
76
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (request.method !== "POST") {
|
|
81
|
+
return new Response(JSON.stringify({ error: "Method not allowed" }), {
|
|
82
|
+
status: 405,
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
"Access-Control-Allow-Origin": "*"
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const body = await request.json();
|
|
91
|
+
if (!body.code || typeof body.code !== "string") {
|
|
92
|
+
return new Response(JSON.stringify({ error: "Code is required" }), {
|
|
93
|
+
status: 400,
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
"Access-Control-Allow-Origin": "*"
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const result = await executeCode(body, env);
|
|
101
|
+
return new Response(JSON.stringify(result), {
|
|
102
|
+
status: 200,
|
|
103
|
+
headers: {
|
|
104
|
+
"Content-Type": "application/json",
|
|
105
|
+
"Access-Control-Allow-Origin": "*"
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
} catch (error) {
|
|
109
|
+
const err = error;
|
|
110
|
+
return new Response(
|
|
111
|
+
JSON.stringify({
|
|
112
|
+
status: "error",
|
|
113
|
+
error: {
|
|
114
|
+
name: "RequestError",
|
|
115
|
+
message: err.message || "Failed to process request"
|
|
116
|
+
}
|
|
117
|
+
}),
|
|
118
|
+
{
|
|
119
|
+
status: 500,
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
"Access-Control-Allow-Origin": "*"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
export {
|
|
130
|
+
index as default
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/worker/index.ts"],"sourcesContent":["/**\n * Cloudflare Worker for Code Mode execution\n *\n * This Worker executes JavaScript code in a V8 isolate on Cloudflare's edge network.\n * Tool calls are handled via a request/response loop with the driver.\n *\n * Flow:\n * 1. Receive code + tool schemas\n * 2. Execute code, collecting any tool calls\n * 3. If tool calls are needed, return them to the driver\n * 4. Driver executes tools locally, sends results back\n * 5. Re-execute with tool results injected\n * 6. Return final result\n */\n\nimport { wrapCode } from './wrap-code'\nimport type { ExecuteRequest, ExecuteResponse, ToolCallRequest } from '../types'\n\n/**\n * UnsafeEval binding type\n * This is only available in local development with wrangler dev\n */\ninterface UnsafeEval {\n eval: (code: string) => unknown\n}\n\ninterface Env {\n /**\n * UnsafeEval binding - provides eval() for local development\n * Configured in wrangler.toml as an unsafe binding\n */\n UNSAFE_EVAL?: UnsafeEval\n}\n\n/**\n * Execute code in the Worker's V8 isolate\n */\nasync function executeCode(\n request: ExecuteRequest,\n env: Env,\n): Promise<ExecuteResponse> {\n const { code, tools, toolResults, timeout = 30000 } = request\n\n // Check if UNSAFE_EVAL binding is available\n if (!env.UNSAFE_EVAL) {\n return {\n status: 'error',\n error: {\n name: 'UnsafeEvalNotAvailable',\n message:\n 'UNSAFE_EVAL binding is not available. ' +\n 'This Worker requires the unsafe_eval binding for local development. ' +\n 'For production, consider using Workers for Platforms.',\n },\n }\n }\n\n try {\n const wrappedCode = wrapCode(code, tools, toolResults)\n\n // Execute with timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n // Use UNSAFE_EVAL binding to execute the code\n // This is only available in local development with wrangler dev\n const result = (await env.UNSAFE_EVAL.eval(wrappedCode)) as {\n status: string\n success?: boolean\n value?: unknown\n error?: { name: string; message: string; stack?: string }\n logs: Array<string>\n toolCalls?: Array<ToolCallRequest>\n }\n\n clearTimeout(timeoutId)\n\n if (result.status === 'need_tools') {\n return {\n status: 'need_tools',\n toolCalls: result.toolCalls || [],\n logs: result.logs,\n continuationId: crypto.randomUUID(),\n }\n }\n\n return {\n status: 'done',\n success: result.success ?? false,\n value: result.value,\n error: result.error,\n logs: result.logs,\n }\n } catch (evalError: unknown) {\n clearTimeout(timeoutId)\n\n if (controller.signal.aborted) {\n return {\n status: 'error',\n error: {\n name: 'TimeoutError',\n message: `Execution timed out after ${timeout}ms`,\n },\n }\n }\n\n const error = evalError as Error\n return {\n status: 'done',\n success: false,\n error: {\n name: error.name || 'EvalError',\n message: error.message || String(error),\n stack: error.stack,\n },\n logs: [],\n }\n }\n } catch (error: unknown) {\n const err = error as Error\n return {\n status: 'error',\n error: {\n name: err.name || 'Error',\n message: err.message || String(err),\n },\n }\n }\n}\n\n/**\n * Main Worker fetch handler\n */\nexport default {\n async fetch(\n request: Request,\n env: Env,\n _ctx: ExecutionContext,\n ): Promise<Response> {\n // Handle CORS preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n headers: {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n },\n })\n }\n\n // Only accept POST requests\n if (request.method !== 'POST') {\n return new Response(JSON.stringify({ error: 'Method not allowed' }), {\n status: 405,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n },\n })\n }\n\n try {\n const body: ExecuteRequest = await request.json()\n\n // Validate request\n if (!body.code || typeof body.code !== 'string') {\n return new Response(JSON.stringify({ error: 'Code is required' }), {\n status: 400,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n },\n })\n }\n\n // Execute the code\n const result = await executeCode(body, env)\n\n return new Response(JSON.stringify(result), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n },\n })\n } catch (error: unknown) {\n const err = error as Error\n return new Response(\n JSON.stringify({\n status: 'error',\n error: {\n name: 'RequestError',\n message: err.message || 'Failed to process request',\n },\n }),\n {\n status: 500,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n },\n },\n )\n }\n },\n}\n"],"names":[],"mappings":"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"}
|