@cloudflare/sandbox 0.4.12 → 0.4.14
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/.turbo/turbo-build.log +13 -47
- package/CHANGELOG.md +38 -16
- package/Dockerfile +15 -9
- package/README.md +0 -1
- package/dist/index.d.ts +1889 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3144 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/clients/base-client.ts +39 -24
- package/src/clients/command-client.ts +8 -8
- package/src/clients/file-client.ts +31 -26
- package/src/clients/git-client.ts +3 -4
- package/src/clients/index.ts +12 -16
- package/src/clients/interpreter-client.ts +51 -47
- package/src/clients/port-client.ts +10 -10
- package/src/clients/process-client.ts +11 -8
- package/src/clients/sandbox-client.ts +2 -4
- package/src/clients/types.ts +6 -2
- package/src/clients/utility-client.ts +10 -6
- package/src/errors/adapter.ts +90 -32
- package/src/errors/classes.ts +189 -64
- package/src/errors/index.ts +9 -5
- package/src/file-stream.ts +11 -6
- package/src/index.ts +22 -15
- package/src/interpreter.ts +50 -41
- package/src/request-handler.ts +24 -21
- package/src/sandbox.ts +339 -149
- package/src/security.ts +21 -6
- package/src/sse-parser.ts +4 -3
- package/src/version.ts +1 -1
- package/tests/base-client.test.ts +116 -80
- package/tests/command-client.test.ts +149 -112
- package/tests/file-client.test.ts +309 -197
- package/tests/file-stream.test.ts +24 -20
- package/tests/get-sandbox.test.ts +10 -10
- package/tests/git-client.test.ts +188 -101
- package/tests/port-client.test.ts +100 -108
- package/tests/process-client.test.ts +204 -179
- package/tests/request-handler.test.ts +117 -65
- package/tests/sandbox.test.ts +219 -67
- package/tests/sse-parser.test.ts +17 -16
- package/tests/utility-client.test.ts +79 -72
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +6 -6
- package/dist/chunk-BFVUNTP4.js +0 -104
- package/dist/chunk-BFVUNTP4.js.map +0 -1
- package/dist/chunk-EKSWCBCA.js +0 -86
- package/dist/chunk-EKSWCBCA.js.map +0 -1
- package/dist/chunk-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-UJ3TV4M6.js +0 -7
- package/dist/chunk-UJ3TV4M6.js.map +0 -1
- package/dist/chunk-YE265ASX.js +0 -2484
- package/dist/chunk-YE265ASX.js.map +0 -1
- package/dist/chunk-Z532A7QC.js +0 -78
- package/dist/chunk-Z532A7QC.js.map +0 -1
- package/dist/file-stream.d.ts +0 -43
- package/dist/file-stream.js +0 -9
- package/dist/file-stream.js.map +0 -1
- package/dist/interpreter.d.ts +0 -33
- package/dist/interpreter.js +0 -8
- package/dist/interpreter.js.map +0 -1
- package/dist/request-handler.d.ts +0 -18
- package/dist/request-handler.js +0 -13
- package/dist/request-handler.js.map +0 -1
- package/dist/sandbox-CLZWpfGc.d.ts +0 -613
- package/dist/sandbox.d.ts +0 -4
- package/dist/sandbox.js +0 -13
- package/dist/sandbox.js.map +0 -1
- package/dist/security.d.ts +0 -31
- package/dist/security.js +0 -13
- package/dist/security.js.map +0 -1
- package/dist/sse-parser.d.ts +0 -28
- package/dist/sse-parser.js +0 -11
- package/dist/sse-parser.js.map +0 -1
- package/dist/version.d.ts +0 -8
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
package/src/interpreter.ts
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
type OutputMessage,
|
|
7
7
|
type Result,
|
|
8
8
|
ResultImpl,
|
|
9
|
-
type RunCodeOptions
|
|
10
|
-
} from
|
|
11
|
-
import type { InterpreterClient } from
|
|
12
|
-
import type { Sandbox } from
|
|
13
|
-
import { validateLanguage } from
|
|
9
|
+
type RunCodeOptions
|
|
10
|
+
} from '@repo/shared';
|
|
11
|
+
import type { InterpreterClient } from './clients/interpreter-client.js';
|
|
12
|
+
import type { Sandbox } from './sandbox.js';
|
|
13
|
+
import { validateLanguage } from './security.js';
|
|
14
14
|
|
|
15
15
|
export class CodeInterpreter {
|
|
16
16
|
private interpreterClient: InterpreterClient;
|
|
@@ -18,7 +18,8 @@ export class CodeInterpreter {
|
|
|
18
18
|
|
|
19
19
|
constructor(sandbox: Sandbox) {
|
|
20
20
|
// In init-testing architecture, client is a SandboxClient with an interpreter property
|
|
21
|
-
this.interpreterClient = (sandbox.client as any)
|
|
21
|
+
this.interpreterClient = (sandbox.client as any)
|
|
22
|
+
.interpreter as InterpreterClient;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -46,7 +47,7 @@ export class CodeInterpreter {
|
|
|
46
47
|
let context = options.context;
|
|
47
48
|
if (!context) {
|
|
48
49
|
// Try to find or create a default context for the language
|
|
49
|
-
const language = options.language ||
|
|
50
|
+
const language = options.language || 'python';
|
|
50
51
|
context = await this.getOrCreateDefaultContext(language);
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -54,24 +55,29 @@ export class CodeInterpreter {
|
|
|
54
55
|
const execution = new Execution(code, context);
|
|
55
56
|
|
|
56
57
|
// Stream execution
|
|
57
|
-
await this.interpreterClient.runCodeStream(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
await this.interpreterClient.runCodeStream(
|
|
59
|
+
context.id,
|
|
60
|
+
code,
|
|
61
|
+
options.language,
|
|
62
|
+
{
|
|
63
|
+
onStdout: (output: OutputMessage) => {
|
|
64
|
+
execution.logs.stdout.push(output.text);
|
|
65
|
+
if (options.onStdout) return options.onStdout(output);
|
|
66
|
+
},
|
|
67
|
+
onStderr: (output: OutputMessage) => {
|
|
68
|
+
execution.logs.stderr.push(output.text);
|
|
69
|
+
if (options.onStderr) return options.onStderr(output);
|
|
70
|
+
},
|
|
71
|
+
onResult: async (result: Result) => {
|
|
72
|
+
execution.results.push(new ResultImpl(result) as any);
|
|
73
|
+
if (options.onResult) return options.onResult(result);
|
|
74
|
+
},
|
|
75
|
+
onError: (error: ExecutionError) => {
|
|
76
|
+
execution.error = error;
|
|
77
|
+
if (options.onError) return options.onError(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
);
|
|
75
81
|
|
|
76
82
|
return execution;
|
|
77
83
|
}
|
|
@@ -86,36 +92,39 @@ export class CodeInterpreter {
|
|
|
86
92
|
// Get or create context
|
|
87
93
|
let context = options.context;
|
|
88
94
|
if (!context) {
|
|
89
|
-
const language = options.language ||
|
|
95
|
+
const language = options.language || 'python';
|
|
90
96
|
context = await this.getOrCreateDefaultContext(language);
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
// Create streaming response
|
|
94
100
|
// Note: doFetch is protected but we need direct access for raw stream response
|
|
95
|
-
const response = await (this.interpreterClient as any).doFetch(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
const response = await (this.interpreterClient as any).doFetch(
|
|
102
|
+
'/api/execute/code',
|
|
103
|
+
{
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
Accept: 'text/event-stream'
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
context_id: context.id,
|
|
111
|
+
code,
|
|
112
|
+
language: options.language
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
);
|
|
107
116
|
|
|
108
117
|
if (!response.ok) {
|
|
109
118
|
const errorData = (await response
|
|
110
119
|
.json()
|
|
111
|
-
.catch(() => ({ error:
|
|
120
|
+
.catch(() => ({ error: 'Unknown error' }))) as { error?: string };
|
|
112
121
|
throw new Error(
|
|
113
122
|
errorData.error || `Failed to execute code: ${response.status}`
|
|
114
123
|
);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
if (!response.body) {
|
|
118
|
-
throw new Error(
|
|
127
|
+
throw new Error('No response body for streaming execution');
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
return response.body;
|
|
@@ -144,7 +153,7 @@ export class CodeInterpreter {
|
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
private async getOrCreateDefaultContext(
|
|
147
|
-
language:
|
|
156
|
+
language: 'python' | 'javascript' | 'typescript'
|
|
148
157
|
): Promise<CodeContext> {
|
|
149
158
|
// Check if we have a cached context for this language
|
|
150
159
|
for (const context of this.contexts.values()) {
|
package/src/request-handler.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { switchPort } from
|
|
2
|
-
import { createLogger, type LogContext, TraceContext } from
|
|
3
|
-
import { getSandbox, type Sandbox } from
|
|
4
|
-
import {
|
|
5
|
-
sanitizeSandboxId,
|
|
6
|
-
validatePort
|
|
7
|
-
} from "./security";
|
|
1
|
+
import { switchPort } from '@cloudflare/containers';
|
|
2
|
+
import { createLogger, type LogContext, TraceContext } from '@repo/shared';
|
|
3
|
+
import { getSandbox, type Sandbox } from './sandbox';
|
|
4
|
+
import { sanitizeSandboxId, validatePort } from './security';
|
|
8
5
|
|
|
9
6
|
export interface SandboxEnv {
|
|
10
7
|
Sandbox: DurableObjectNamespace<Sandbox>;
|
|
@@ -22,7 +19,8 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
22
19
|
env: E
|
|
23
20
|
): Promise<Response | null> {
|
|
24
21
|
// Create logger context for this request
|
|
25
|
-
const traceId =
|
|
22
|
+
const traceId =
|
|
23
|
+
TraceContext.fromHeaders(request.headers) || TraceContext.generate();
|
|
26
24
|
const logger = createLogger({
|
|
27
25
|
component: 'sandbox-do',
|
|
28
26
|
traceId,
|
|
@@ -98,16 +96,19 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
98
96
|
'X-Original-URL': request.url,
|
|
99
97
|
'X-Forwarded-Host': url.hostname,
|
|
100
98
|
'X-Forwarded-Proto': url.protocol.replace(':', ''),
|
|
101
|
-
'X-Sandbox-Name': sandboxId
|
|
99
|
+
'X-Sandbox-Name': sandboxId // Pass the friendly name
|
|
102
100
|
},
|
|
103
101
|
body: request.body,
|
|
104
102
|
// @ts-expect-error - duplex required for body streaming in modern runtimes
|
|
105
|
-
duplex: 'half'
|
|
103
|
+
duplex: 'half'
|
|
106
104
|
});
|
|
107
105
|
|
|
108
106
|
return await sandbox.containerFetch(proxyRequest, port);
|
|
109
107
|
} catch (error) {
|
|
110
|
-
logger.error(
|
|
108
|
+
logger.error(
|
|
109
|
+
'Proxy routing error',
|
|
110
|
+
error instanceof Error ? error : new Error(String(error))
|
|
111
|
+
);
|
|
111
112
|
return new Response('Proxy routing error', { status: 500 });
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -115,7 +116,9 @@ export async function proxyToSandbox<E extends SandboxEnv>(
|
|
|
115
116
|
function extractSandboxRoute(url: URL): RouteInfo | null {
|
|
116
117
|
// Parse subdomain pattern: port-sandboxId-token.domain (tokens mandatory)
|
|
117
118
|
// Token is always exactly 16 chars (generated by generatePortToken)
|
|
118
|
-
const subdomainMatch = url.hostname.match(
|
|
119
|
+
const subdomainMatch = url.hostname.match(
|
|
120
|
+
/^(\d{4,5})-([^.-][^.]*?[^.-]|[^.-])-([a-z0-9_-]{16})\.(.+)$/
|
|
121
|
+
);
|
|
119
122
|
|
|
120
123
|
if (!subdomainMatch) {
|
|
121
124
|
return null;
|
|
@@ -146,8 +149,8 @@ function extractSandboxRoute(url: URL): RouteInfo | null {
|
|
|
146
149
|
return {
|
|
147
150
|
port,
|
|
148
151
|
sandboxId: sanitizedSandboxId,
|
|
149
|
-
path: url.pathname ||
|
|
150
|
-
token
|
|
152
|
+
path: url.pathname || '/',
|
|
153
|
+
token
|
|
151
154
|
};
|
|
152
155
|
}
|
|
153
156
|
|
|
@@ -163,18 +166,18 @@ export function isLocalhostPattern(hostname: string): boolean {
|
|
|
163
166
|
return hostname === '[::1]';
|
|
164
167
|
}
|
|
165
168
|
}
|
|
166
|
-
|
|
169
|
+
|
|
167
170
|
// Handle bare IPv6 without brackets
|
|
168
171
|
if (hostname === '::1') {
|
|
169
172
|
return true;
|
|
170
173
|
}
|
|
171
|
-
|
|
174
|
+
|
|
172
175
|
// For IPv4 and regular hostnames, split on colon to remove port
|
|
173
|
-
const hostPart = hostname.split(
|
|
174
|
-
|
|
176
|
+
const hostPart = hostname.split(':')[0];
|
|
177
|
+
|
|
175
178
|
return (
|
|
176
|
-
hostPart ===
|
|
177
|
-
hostPart ===
|
|
178
|
-
hostPart ===
|
|
179
|
+
hostPart === 'localhost' ||
|
|
180
|
+
hostPart === '127.0.0.1' ||
|
|
181
|
+
hostPart === '0.0.0.0'
|
|
179
182
|
);
|
|
180
183
|
}
|