@cloudflare/sandbox 0.0.0-feafd32 → 0.0.0-ff2fa91
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 +130 -15
- package/Dockerfile +156 -68
- package/README.md +92 -769
- package/dist/index.d.ts +1889 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3146 -0
- package/dist/index.js.map +1 -0
- package/package.json +16 -8
- package/src/clients/base-client.ts +295 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +300 -0
- package/src/clients/git-client.ts +91 -0
- package/src/clients/index.ts +60 -0
- package/src/clients/interpreter-client.ts +333 -0
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +180 -0
- package/src/clients/sandbox-client.ts +39 -0
- package/src/clients/types.ts +88 -0
- package/src/clients/utility-client.ts +123 -0
- package/src/errors/adapter.ts +238 -0
- package/src/errors/classes.ts +594 -0
- package/src/errors/index.ts +109 -0
- package/src/file-stream.ts +169 -0
- package/src/index.ts +88 -63
- package/src/interpreter.ts +58 -40
- package/src/request-handler.ts +94 -55
- package/src/sandbox.ts +978 -490
- package/src/security.ts +34 -28
- package/src/sse-parser.ts +8 -11
- package/src/version.ts +6 -0
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +364 -0
- package/tests/command-client.test.ts +444 -0
- package/tests/file-client.test.ts +831 -0
- package/tests/file-stream.test.ts +310 -0
- package/tests/get-sandbox.test.ts +149 -0
- package/tests/git-client.test.ts +415 -0
- package/tests/port-client.test.ts +293 -0
- package/tests/process-client.test.ts +683 -0
- package/tests/request-handler.test.ts +292 -0
- package/tests/sandbox.test.ts +706 -0
- package/tests/sse-parser.test.ts +291 -0
- package/tests/utility-client.test.ts +339 -0
- package/tests/version.test.ts +16 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +31 -0
- package/container_src/bun.lock +0 -76
- package/container_src/circuit-breaker.ts +0 -121
- package/container_src/control-process.ts +0 -784
- package/container_src/handler/exec.ts +0 -185
- package/container_src/handler/file.ts +0 -406
- package/container_src/handler/git.ts +0 -130
- package/container_src/handler/ports.ts +0 -314
- package/container_src/handler/process.ts +0 -568
- package/container_src/handler/session.ts +0 -92
- package/container_src/index.ts +0 -592
- package/container_src/interpreter-service.ts +0 -276
- package/container_src/isolation.ts +0 -1049
- package/container_src/mime-processor.ts +0 -255
- package/container_src/package.json +0 -18
- package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
- package/container_src/runtime/executors/python/ipython_executor.py +0 -338
- package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
- package/container_src/runtime/process-pool.ts +0 -464
- package/container_src/shell-escape.ts +0 -42
- package/container_src/startup.sh +0 -11
- package/container_src/types.ts +0 -131
- package/src/client.ts +0 -1009
- package/src/errors.ts +0 -219
- package/src/interpreter-client.ts +0 -352
- package/src/interpreter-types.ts +0 -390
- package/src/types.ts +0 -502
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
export interface ExecutionResult {
|
|
2
|
-
type: 'result' | 'stdout' | 'stderr' | 'error' | 'execution_complete';
|
|
3
|
-
text?: string;
|
|
4
|
-
html?: string;
|
|
5
|
-
png?: string; // base64
|
|
6
|
-
jpeg?: string; // base64
|
|
7
|
-
svg?: string;
|
|
8
|
-
latex?: string;
|
|
9
|
-
markdown?: string;
|
|
10
|
-
javascript?: string;
|
|
11
|
-
json?: any;
|
|
12
|
-
chart?: ChartData;
|
|
13
|
-
data?: any;
|
|
14
|
-
metadata?: any;
|
|
15
|
-
execution_count?: number;
|
|
16
|
-
ename?: string;
|
|
17
|
-
evalue?: string;
|
|
18
|
-
traceback?: string[];
|
|
19
|
-
timestamp: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ChartData {
|
|
23
|
-
type: 'line' | 'bar' | 'scatter' | 'pie' | 'histogram' | 'heatmap' | 'unknown';
|
|
24
|
-
title?: string;
|
|
25
|
-
data: any;
|
|
26
|
-
layout?: any;
|
|
27
|
-
config?: any;
|
|
28
|
-
library?: 'matplotlib' | 'plotly' | 'altair' | 'seaborn' | 'unknown';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function processMessage(msg: any): ExecutionResult | null {
|
|
32
|
-
const msgType = msg.header?.msg_type || msg.msg_type;
|
|
33
|
-
|
|
34
|
-
switch (msgType) {
|
|
35
|
-
case 'execute_result':
|
|
36
|
-
case 'display_data':
|
|
37
|
-
return processDisplayData(msg.content.data, msg.content.metadata);
|
|
38
|
-
|
|
39
|
-
case 'stream':
|
|
40
|
-
return {
|
|
41
|
-
type: msg.content.name === 'stdout' ? 'stdout' : 'stderr',
|
|
42
|
-
text: msg.content.text,
|
|
43
|
-
timestamp: Date.now()
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
case 'error':
|
|
47
|
-
return {
|
|
48
|
-
type: 'error',
|
|
49
|
-
ename: msg.content.ename,
|
|
50
|
-
evalue: msg.content.evalue,
|
|
51
|
-
traceback: msg.content.traceback,
|
|
52
|
-
timestamp: Date.now()
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
default:
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function processDisplayData(data: any, metadata?: any): ExecutionResult {
|
|
61
|
-
const result: ExecutionResult = {
|
|
62
|
-
type: 'result',
|
|
63
|
-
timestamp: Date.now(),
|
|
64
|
-
metadata
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Process different MIME types in order of preference
|
|
68
|
-
|
|
69
|
-
// Interactive/Rich formats
|
|
70
|
-
if (data['application/vnd.plotly.v1+json']) {
|
|
71
|
-
result.chart = extractPlotlyChart(data['application/vnd.plotly.v1+json']);
|
|
72
|
-
result.json = data['application/vnd.plotly.v1+json'];
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (data['application/vnd.vega.v5+json']) {
|
|
76
|
-
result.chart = extractVegaChart(data['application/vnd.vega.v5+json'], 'vega');
|
|
77
|
-
result.json = data['application/vnd.vega.v5+json'];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (data['application/vnd.vegalite.v4+json'] || data['application/vnd.vegalite.v5+json']) {
|
|
81
|
-
const vegaData = data['application/vnd.vegalite.v4+json'] || data['application/vnd.vegalite.v5+json'];
|
|
82
|
-
result.chart = extractVegaChart(vegaData, 'vega-lite');
|
|
83
|
-
result.json = vegaData;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// HTML content (tables, formatted output)
|
|
87
|
-
if (data['text/html']) {
|
|
88
|
-
result.html = data['text/html'];
|
|
89
|
-
|
|
90
|
-
// Check if it's a pandas DataFrame
|
|
91
|
-
if (isPandasDataFrame(data['text/html'])) {
|
|
92
|
-
result.data = { type: 'dataframe', html: data['text/html'] };
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Images
|
|
97
|
-
if (data['image/png']) {
|
|
98
|
-
result.png = data['image/png'];
|
|
99
|
-
|
|
100
|
-
// Try to detect if it's a chart
|
|
101
|
-
if (isLikelyChart(data, metadata)) {
|
|
102
|
-
result.chart = {
|
|
103
|
-
type: 'unknown',
|
|
104
|
-
library: 'matplotlib',
|
|
105
|
-
data: { image: data['image/png'] }
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (data['image/jpeg']) {
|
|
111
|
-
result.jpeg = data['image/jpeg'];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (data['image/svg+xml']) {
|
|
115
|
-
result.svg = data['image/svg+xml'];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Mathematical content
|
|
119
|
-
if (data['text/latex']) {
|
|
120
|
-
result.latex = data['text/latex'];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Code
|
|
124
|
-
if (data['application/javascript']) {
|
|
125
|
-
result.javascript = data['application/javascript'];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Structured data
|
|
129
|
-
if (data['application/json']) {
|
|
130
|
-
result.json = data['application/json'];
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Markdown
|
|
134
|
-
if (data['text/markdown']) {
|
|
135
|
-
result.markdown = data['text/markdown'];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Plain text (fallback)
|
|
139
|
-
if (data['text/plain']) {
|
|
140
|
-
result.text = data['text/plain'];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return result;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function extractPlotlyChart(plotlyData: any): ChartData {
|
|
147
|
-
const data = plotlyData.data || plotlyData;
|
|
148
|
-
const layout = plotlyData.layout || {};
|
|
149
|
-
|
|
150
|
-
// Try to detect chart type from traces
|
|
151
|
-
let chartType: ChartData['type'] = 'unknown';
|
|
152
|
-
if (data && data.length > 0) {
|
|
153
|
-
const firstTrace = data[0];
|
|
154
|
-
if (firstTrace.type === 'scatter') {
|
|
155
|
-
chartType = firstTrace.mode?.includes('lines') ? 'line' : 'scatter';
|
|
156
|
-
} else if (firstTrace.type === 'bar') {
|
|
157
|
-
chartType = 'bar';
|
|
158
|
-
} else if (firstTrace.type === 'pie') {
|
|
159
|
-
chartType = 'pie';
|
|
160
|
-
} else if (firstTrace.type === 'histogram') {
|
|
161
|
-
chartType = 'histogram';
|
|
162
|
-
} else if (firstTrace.type === 'heatmap') {
|
|
163
|
-
chartType = 'heatmap';
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
type: chartType,
|
|
169
|
-
title: layout.title?.text || layout.title,
|
|
170
|
-
data: data,
|
|
171
|
-
layout: layout,
|
|
172
|
-
config: plotlyData.config,
|
|
173
|
-
library: 'plotly'
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function extractVegaChart(vegaData: any, format: 'vega' | 'vega-lite'): ChartData {
|
|
178
|
-
// Try to detect chart type from mark or encoding
|
|
179
|
-
let chartType: ChartData['type'] = 'unknown';
|
|
180
|
-
|
|
181
|
-
if (format === 'vega-lite' && vegaData.mark) {
|
|
182
|
-
const mark = typeof vegaData.mark === 'string' ? vegaData.mark : vegaData.mark.type;
|
|
183
|
-
switch (mark) {
|
|
184
|
-
case 'line':
|
|
185
|
-
chartType = 'line';
|
|
186
|
-
break;
|
|
187
|
-
case 'bar':
|
|
188
|
-
chartType = 'bar';
|
|
189
|
-
break;
|
|
190
|
-
case 'point':
|
|
191
|
-
case 'circle':
|
|
192
|
-
chartType = 'scatter';
|
|
193
|
-
break;
|
|
194
|
-
case 'arc':
|
|
195
|
-
chartType = 'pie';
|
|
196
|
-
break;
|
|
197
|
-
case 'rect':
|
|
198
|
-
if (vegaData.encoding?.color) {
|
|
199
|
-
chartType = 'heatmap';
|
|
200
|
-
}
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
type: chartType,
|
|
207
|
-
title: vegaData.title,
|
|
208
|
-
data: vegaData,
|
|
209
|
-
library: 'altair' // Altair outputs Vega-Lite
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function isPandasDataFrame(html: string): boolean {
|
|
214
|
-
// Simple heuristic to detect pandas DataFrame HTML
|
|
215
|
-
return html.includes('dataframe') ||
|
|
216
|
-
(html.includes('<table') && html.includes('<thead') && html.includes('<tbody'));
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function isLikelyChart(data: any, metadata?: any): boolean {
|
|
220
|
-
// Check metadata for hints
|
|
221
|
-
if (metadata?.needs?.includes('matplotlib')) {
|
|
222
|
-
return true;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Check if other chart formats are present
|
|
226
|
-
if (data['application/vnd.plotly.v1+json'] ||
|
|
227
|
-
data['application/vnd.vega.v5+json'] ||
|
|
228
|
-
data['application/vnd.vegalite.v4+json']) {
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// If only image output without text, likely a chart
|
|
233
|
-
if ((data['image/png'] || data['image/svg+xml']) && !data['text/plain']) {
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export function extractFormats(result: ExecutionResult): string[] {
|
|
241
|
-
const formats: string[] = [];
|
|
242
|
-
|
|
243
|
-
if (result.text) formats.push('text');
|
|
244
|
-
if (result.html) formats.push('html');
|
|
245
|
-
if (result.png) formats.push('png');
|
|
246
|
-
if (result.jpeg) formats.push('jpeg');
|
|
247
|
-
if (result.svg) formats.push('svg');
|
|
248
|
-
if (result.latex) formats.push('latex');
|
|
249
|
-
if (result.markdown) formats.push('markdown');
|
|
250
|
-
if (result.javascript) formats.push('javascript');
|
|
251
|
-
if (result.json) formats.push('json');
|
|
252
|
-
if (result.chart) formats.push('chart');
|
|
253
|
-
|
|
254
|
-
return formats;
|
|
255
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sandbox-server",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "A server for the sandbox package",
|
|
5
|
-
"main": "index.ts",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "bun run index.ts"
|
|
8
|
-
},
|
|
9
|
-
"dependencies": {
|
|
10
|
-
"esbuild": "^0.21.5",
|
|
11
|
-
"uuid": "^9.0.1"
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"@types/node": "^20.0.0",
|
|
15
|
-
"@types/uuid": "^9.0.7",
|
|
16
|
-
"typescript": "^5.3.0"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import * as readline from 'node:readline';
|
|
4
|
-
import * as util from 'node:util';
|
|
5
|
-
import * as vm from 'node:vm';
|
|
6
|
-
import type { RichOutput } from '../../process-pool';
|
|
7
|
-
|
|
8
|
-
const rl = readline.createInterface({
|
|
9
|
-
input: process.stdin,
|
|
10
|
-
output: process.stdout,
|
|
11
|
-
terminal: false
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const sandbox = {
|
|
15
|
-
console: console,
|
|
16
|
-
process: process,
|
|
17
|
-
require: require,
|
|
18
|
-
Buffer: Buffer,
|
|
19
|
-
setTimeout: setTimeout,
|
|
20
|
-
setInterval: setInterval,
|
|
21
|
-
clearTimeout: clearTimeout,
|
|
22
|
-
clearInterval: clearInterval,
|
|
23
|
-
setImmediate: setImmediate,
|
|
24
|
-
clearImmediate: clearImmediate,
|
|
25
|
-
global: global,
|
|
26
|
-
__dirname: __dirname,
|
|
27
|
-
__filename: __filename
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const context = vm.createContext(sandbox);
|
|
31
|
-
|
|
32
|
-
console.log(JSON.stringify({ status: "ready" }));
|
|
33
|
-
|
|
34
|
-
rl.on('line', async (line: string) => {
|
|
35
|
-
try {
|
|
36
|
-
const request = JSON.parse(line);
|
|
37
|
-
const { code, executionId } = request;
|
|
38
|
-
|
|
39
|
-
const originalStdoutWrite = process.stdout.write;
|
|
40
|
-
const originalStderrWrite = process.stderr.write;
|
|
41
|
-
|
|
42
|
-
let stdout = '';
|
|
43
|
-
let stderr = '';
|
|
44
|
-
|
|
45
|
-
(process.stdout.write as any) = (chunk: string | Buffer, encoding?: BufferEncoding, callback?: () => void) => {
|
|
46
|
-
stdout += chunk.toString();
|
|
47
|
-
if (callback) callback();
|
|
48
|
-
return true;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
(process.stderr.write as any) = (chunk: string | Buffer, encoding?: BufferEncoding, callback?: () => void) => {
|
|
52
|
-
stderr += chunk.toString();
|
|
53
|
-
if (callback) callback();
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
let result: unknown;
|
|
58
|
-
let success = true;
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
result = vm.runInContext(code, context, {
|
|
62
|
-
filename: `<execution-${executionId}>`,
|
|
63
|
-
timeout: 30000
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
} catch (error: unknown) {
|
|
67
|
-
const err = error as Error;
|
|
68
|
-
stderr += err.stack || err.toString();
|
|
69
|
-
success = false;
|
|
70
|
-
} finally {
|
|
71
|
-
process.stdout.write = originalStdoutWrite;
|
|
72
|
-
process.stderr.write = originalStderrWrite;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const outputs: RichOutput[] = [];
|
|
76
|
-
|
|
77
|
-
if (result !== undefined) {
|
|
78
|
-
if (typeof result === 'object' && result !== null) {
|
|
79
|
-
outputs.push({
|
|
80
|
-
type: 'json',
|
|
81
|
-
data: JSON.stringify(result, null, 2),
|
|
82
|
-
metadata: {}
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
outputs.push({
|
|
86
|
-
type: 'text',
|
|
87
|
-
data: util.inspect(result, { showHidden: false, depth: null, colors: false }),
|
|
88
|
-
metadata: {}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const response = {
|
|
94
|
-
stdout,
|
|
95
|
-
stderr,
|
|
96
|
-
success,
|
|
97
|
-
executionId,
|
|
98
|
-
outputs
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
console.log(JSON.stringify(response));
|
|
102
|
-
|
|
103
|
-
} catch (error: unknown) {
|
|
104
|
-
const err = error as Error;
|
|
105
|
-
console.log(JSON.stringify({
|
|
106
|
-
stdout: '',
|
|
107
|
-
stderr: `Error processing request: ${err.message}`,
|
|
108
|
-
success: false,
|
|
109
|
-
executionId: 'unknown',
|
|
110
|
-
outputs: []
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
process.on('SIGTERM', () => {
|
|
116
|
-
rl.close();
|
|
117
|
-
process.exit(0);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
process.on('SIGINT', () => {
|
|
121
|
-
rl.close();
|
|
122
|
-
process.exit(0);
|
|
123
|
-
});
|