@cloudflare/sandbox 0.0.0-e1fa354 → 0.0.0-e489cbb
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 +46 -0
- package/Dockerfile +107 -38
- package/README.md +89 -771
- package/dist/chunk-53JFOF7F.js +2352 -0
- package/dist/chunk-53JFOF7F.js.map +1 -0
- package/dist/chunk-BFVUNTP4.js +104 -0
- package/dist/chunk-BFVUNTP4.js.map +1 -0
- package/dist/chunk-EKSWCBCA.js +86 -0
- package/dist/chunk-EKSWCBCA.js.map +1 -0
- package/dist/chunk-JXZMAU2C.js +559 -0
- package/dist/chunk-JXZMAU2C.js.map +1 -0
- package/dist/chunk-Z532A7QC.js +78 -0
- package/dist/chunk-Z532A7QC.js.map +1 -0
- package/dist/file-stream.d.ts +43 -0
- package/dist/file-stream.js +9 -0
- package/dist/file-stream.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/interpreter.d.ts +33 -0
- package/dist/interpreter.js +8 -0
- package/dist/interpreter.js.map +1 -0
- package/dist/request-handler.d.ts +18 -0
- package/dist/request-handler.js +12 -0
- package/dist/request-handler.js.map +1 -0
- package/dist/sandbox-D9K2ypln.d.ts +583 -0
- package/dist/sandbox.d.ts +4 -0
- package/dist/sandbox.js +12 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/security.d.ts +31 -0
- package/dist/security.js +13 -0
- package/dist/security.js.map +1 -0
- package/dist/sse-parser.d.ts +28 -0
- package/dist/sse-parser.js +11 -0
- package/dist/sse-parser.js.map +1 -0
- package/package.json +13 -5
- package/src/clients/base-client.ts +280 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +269 -0
- package/src/clients/git-client.ts +92 -0
- package/src/clients/index.ts +63 -0
- package/src/{jupyter-client.ts → clients/interpreter-client.ts} +148 -168
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +177 -0
- package/src/clients/sandbox-client.ts +41 -0
- package/src/clients/types.ts +84 -0
- package/src/clients/utility-client.ts +94 -0
- package/src/errors/adapter.ts +180 -0
- package/src/errors/classes.ts +469 -0
- package/src/errors/index.ts +105 -0
- package/src/file-stream.ts +164 -0
- package/src/index.ts +82 -53
- package/src/interpreter.ts +22 -13
- package/src/request-handler.ts +69 -43
- package/src/sandbox.ts +697 -527
- package/src/security.ts +14 -23
- package/src/sse-parser.ts +4 -8
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +328 -0
- package/tests/command-client.test.ts +407 -0
- package/tests/file-client.test.ts +643 -0
- package/tests/file-stream.test.ts +306 -0
- package/tests/git-client.test.ts +328 -0
- package/tests/port-client.test.ts +301 -0
- package/tests/process-client.test.ts +658 -0
- package/tests/sandbox.test.ts +465 -0
- package/tests/sse-parser.test.ts +290 -0
- package/tests/utility-client.test.ts +266 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/vitest.config.ts +31 -0
- package/container_src/bun.lock +0 -122
- 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 -601
- package/container_src/isolation.ts +0 -1038
- package/container_src/jupyter-server.ts +0 -579
- package/container_src/jupyter-service.ts +0 -461
- package/container_src/jupyter_config.py +0 -48
- package/container_src/mime-processor.ts +0 -255
- package/container_src/package.json +0 -18
- package/container_src/shell-escape.ts +0 -42
- package/container_src/startup.sh +0 -84
- package/container_src/types.ts +0 -131
- package/src/client.ts +0 -1009
- package/src/errors.ts +0 -218
- package/src/interpreter-types.ts +0 -383
- 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 processJupyterMessage(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
|
-
"@jupyterlab/services": "^7.0.0",
|
|
11
|
-
"ws": "^8.16.0",
|
|
12
|
-
"uuid": "^9.0.1"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@types/ws": "^8.5.10",
|
|
16
|
-
"@types/uuid": "^9.0.7"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Secure shell command utilities to prevent injection attacks
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Escapes a string for safe use in shell commands.
|
|
7
|
-
* This follows POSIX shell escaping rules to prevent command injection.
|
|
8
|
-
*
|
|
9
|
-
* @param str - The string to escape
|
|
10
|
-
* @returns The escaped string safe for shell use
|
|
11
|
-
*/
|
|
12
|
-
export function escapeShellArg(str: string): string {
|
|
13
|
-
// If string is empty, return empty quotes
|
|
14
|
-
if (str === '') {
|
|
15
|
-
return "''";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Check if string contains any characters that need escaping
|
|
19
|
-
// Safe characters: alphanumeric, dash, underscore, dot, slash
|
|
20
|
-
if (/^[a-zA-Z0-9._\-/]+$/.test(str)) {
|
|
21
|
-
return str;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// For strings with special characters, use single quotes and escape single quotes
|
|
25
|
-
// Single quotes preserve all characters literally except the single quote itself
|
|
26
|
-
// To include a single quote, we end the quoted string, add an escaped quote, and start a new quoted string
|
|
27
|
-
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Escapes a file path for safe use in shell commands.
|
|
32
|
-
*
|
|
33
|
-
* @param path - The file path to escape
|
|
34
|
-
* @returns The escaped path safe for shell use
|
|
35
|
-
*/
|
|
36
|
-
export function escapeShellPath(path: string): string {
|
|
37
|
-
// Normalize path to prevent issues with multiple slashes
|
|
38
|
-
const normalizedPath = path.replace(/\/+/g, '/');
|
|
39
|
-
|
|
40
|
-
// Apply standard shell escaping
|
|
41
|
-
return escapeShellArg(normalizedPath);
|
|
42
|
-
}
|
package/container_src/startup.sh
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Function to check if Jupyter is ready
|
|
4
|
-
check_jupyter_ready() {
|
|
5
|
-
# Check if API is responsive and kernelspecs are available
|
|
6
|
-
curl -s http://localhost:8888/api/kernelspecs > /dev/null 2>&1
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
# Function to notify Bun server that Jupyter is ready
|
|
10
|
-
notify_jupyter_ready() {
|
|
11
|
-
# Create a marker file that the Bun server can check
|
|
12
|
-
touch /tmp/jupyter-ready
|
|
13
|
-
echo "[Startup] Jupyter is ready, notified Bun server"
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# Start Jupyter server in background
|
|
17
|
-
echo "[Startup] Starting Jupyter server..."
|
|
18
|
-
jupyter server \
|
|
19
|
-
--config=/container-server/jupyter_config.py \
|
|
20
|
-
> /tmp/jupyter.log 2>&1 &
|
|
21
|
-
|
|
22
|
-
JUPYTER_PID=$!
|
|
23
|
-
|
|
24
|
-
# Start Bun server immediately (parallel startup)
|
|
25
|
-
echo "[Startup] Starting Bun server..."
|
|
26
|
-
bun index.ts &
|
|
27
|
-
BUN_PID=$!
|
|
28
|
-
|
|
29
|
-
# Monitor Jupyter readiness in background
|
|
30
|
-
(
|
|
31
|
-
echo "[Startup] Monitoring Jupyter readiness in background..."
|
|
32
|
-
MAX_ATTEMPTS=60
|
|
33
|
-
ATTEMPT=0
|
|
34
|
-
|
|
35
|
-
# Track start time for reporting
|
|
36
|
-
START_TIME=$(date +%s.%N)
|
|
37
|
-
|
|
38
|
-
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
|
39
|
-
if check_jupyter_ready; then
|
|
40
|
-
notify_jupyter_ready
|
|
41
|
-
END_TIME=$(date +%s.%N)
|
|
42
|
-
ELAPSED=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}")
|
|
43
|
-
echo "[Startup] Jupyter server is ready after $ELAPSED seconds ($ATTEMPT attempts)"
|
|
44
|
-
break
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# Check if Jupyter process is still running
|
|
48
|
-
if ! kill -0 $JUPYTER_PID 2>/dev/null; then
|
|
49
|
-
echo "[Startup] WARNING: Jupyter process died. Check /tmp/jupyter.log for details"
|
|
50
|
-
cat /tmp/jupyter.log
|
|
51
|
-
# Don't exit - let Bun server continue running in degraded mode
|
|
52
|
-
break
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
ATTEMPT=$((ATTEMPT + 1))
|
|
56
|
-
|
|
57
|
-
# Start with faster checks
|
|
58
|
-
if [ $ATTEMPT -eq 1 ]; then
|
|
59
|
-
DELAY=0.5 # Start at 0.5s
|
|
60
|
-
else
|
|
61
|
-
# Exponential backoff with 1.3x multiplier (less aggressive than 1.5x)
|
|
62
|
-
DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.3}")
|
|
63
|
-
# Cap at 2s max (instead of 5s)
|
|
64
|
-
if [ $(awk "BEGIN {print ($DELAY > 2)}") -eq 1 ]; then
|
|
65
|
-
DELAY=2
|
|
66
|
-
fi
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# Log with current delay for transparency
|
|
70
|
-
echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, next check in ${DELAY}s)"
|
|
71
|
-
|
|
72
|
-
sleep $DELAY
|
|
73
|
-
done
|
|
74
|
-
|
|
75
|
-
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
|
76
|
-
echo "[Startup] WARNING: Jupyter failed to become ready within attempts"
|
|
77
|
-
echo "[Startup] Jupyter logs:"
|
|
78
|
-
cat /tmp/jupyter.log
|
|
79
|
-
# Don't exit - let Bun server continue in degraded mode
|
|
80
|
-
fi
|
|
81
|
-
) &
|
|
82
|
-
|
|
83
|
-
# Wait for Bun server (main process)
|
|
84
|
-
wait $BUN_PID
|
package/container_src/types.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
// Process management types
|
|
4
|
-
export type ProcessStatus =
|
|
5
|
-
| 'starting'
|
|
6
|
-
| 'running'
|
|
7
|
-
| 'completed'
|
|
8
|
-
| 'failed'
|
|
9
|
-
| 'killed'
|
|
10
|
-
| 'error';
|
|
11
|
-
|
|
12
|
-
export interface ProcessRecord {
|
|
13
|
-
id: string;
|
|
14
|
-
pid?: number;
|
|
15
|
-
command: string;
|
|
16
|
-
status: ProcessStatus;
|
|
17
|
-
startTime: Date;
|
|
18
|
-
endTime?: Date;
|
|
19
|
-
exitCode?: number;
|
|
20
|
-
stdoutFile?: string; // Path to temp file containing stdout
|
|
21
|
-
stderrFile?: string; // Path to temp file containing stderr
|
|
22
|
-
stdout: string;
|
|
23
|
-
stderr: string;
|
|
24
|
-
outputListeners: Set<(stream: 'stdout' | 'stderr', data: string) => void>;
|
|
25
|
-
statusListeners: Set<(status: ProcessStatus) => void>;
|
|
26
|
-
monitoringInterval?: NodeJS.Timeout; // For polling temp files when streaming
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface StartProcessRequest {
|
|
30
|
-
command: string;
|
|
31
|
-
sessionId: string;
|
|
32
|
-
options?: {
|
|
33
|
-
processId?: string;
|
|
34
|
-
timeout?: number;
|
|
35
|
-
env?: Record<string, string>;
|
|
36
|
-
cwd?: string;
|
|
37
|
-
encoding?: string;
|
|
38
|
-
autoCleanup?: boolean;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface ExecuteOptions {
|
|
43
|
-
background?: boolean;
|
|
44
|
-
cwd?: string | URL;
|
|
45
|
-
env?: Record<string, string>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ExecuteRequest extends ExecuteOptions {
|
|
49
|
-
command: string;
|
|
50
|
-
sessionId: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface GitCheckoutRequest {
|
|
54
|
-
repoUrl: string;
|
|
55
|
-
branch?: string;
|
|
56
|
-
targetDir?: string;
|
|
57
|
-
sessionId: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface MkdirRequest {
|
|
61
|
-
path: string;
|
|
62
|
-
recursive?: boolean;
|
|
63
|
-
sessionId: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface WriteFileRequest {
|
|
67
|
-
path: string;
|
|
68
|
-
content: string;
|
|
69
|
-
encoding?: string;
|
|
70
|
-
sessionId: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface ReadFileRequest {
|
|
74
|
-
path: string;
|
|
75
|
-
encoding?: string;
|
|
76
|
-
sessionId: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface DeleteFileRequest {
|
|
80
|
-
path: string;
|
|
81
|
-
sessionId: string;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface RenameFileRequest {
|
|
85
|
-
oldPath: string;
|
|
86
|
-
newPath: string;
|
|
87
|
-
sessionId: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface MoveFileRequest {
|
|
91
|
-
sourcePath: string;
|
|
92
|
-
destinationPath: string;
|
|
93
|
-
sessionId: string;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface ListFilesRequest {
|
|
97
|
-
path: string;
|
|
98
|
-
options?: {
|
|
99
|
-
recursive?: boolean;
|
|
100
|
-
includeHidden?: boolean;
|
|
101
|
-
};
|
|
102
|
-
sessionId: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export interface ExposePortRequest {
|
|
106
|
-
port: number;
|
|
107
|
-
name?: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export interface UnexposePortRequest {
|
|
111
|
-
port: number;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface SessionData {
|
|
115
|
-
id: string;
|
|
116
|
-
activeProcess: ChildProcess | null;
|
|
117
|
-
createdAt: Date;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Session management API types
|
|
121
|
-
export interface CreateSessionRequest {
|
|
122
|
-
id: string;
|
|
123
|
-
env?: Record<string, string>;
|
|
124
|
-
cwd?: string;
|
|
125
|
-
isolation?: boolean;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface SessionExecRequest {
|
|
129
|
-
id: string;
|
|
130
|
-
command: string;
|
|
131
|
-
}
|