@cloudflare/sandbox 0.0.0-c87db11 → 0.0.0-cdb8197
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 +117 -0
- package/Dockerfile +32 -29
- package/README.md +127 -12
- package/container_src/bun.lock +31 -77
- package/container_src/control-process.ts +784 -0
- package/container_src/handler/exec.ts +99 -254
- package/container_src/handler/file.ts +253 -640
- package/container_src/handler/git.ts +28 -80
- package/container_src/handler/process.ts +443 -515
- package/container_src/handler/session.ts +92 -0
- package/container_src/index.ts +108 -163
- package/container_src/interpreter-service.ts +276 -0
- package/container_src/isolation.ts +1213 -0
- package/container_src/mime-processor.ts +1 -1
- package/container_src/package.json +4 -4
- package/container_src/runtime/executors/javascript/node_executor.ts +123 -0
- package/container_src/runtime/executors/python/ipython_executor.py +338 -0
- package/container_src/runtime/executors/typescript/ts_executor.ts +138 -0
- package/container_src/runtime/process-pool.ts +464 -0
- package/container_src/shell-escape.ts +42 -0
- package/container_src/startup.sh +6 -79
- package/container_src/types.ts +35 -12
- package/package.json +2 -2
- package/src/client.ts +214 -187
- package/src/errors.ts +15 -14
- package/src/file-stream.ts +162 -0
- package/src/index.ts +43 -16
- package/src/{jupyter-client.ts → interpreter-client.ts} +6 -3
- package/src/interpreter-types.ts +102 -95
- package/src/interpreter.ts +8 -8
- package/src/sandbox.ts +314 -336
- package/src/types.ts +194 -24
- package/container_src/jupyter-server.ts +0 -579
- package/container_src/jupyter-service.ts +0 -458
- package/container_src/jupyter_config.py +0 -48
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { SessionManager } from "../isolation";
|
|
2
|
+
import type { CreateSessionRequest } from "../types";
|
|
3
|
+
|
|
4
|
+
export async function handleCreateSession(
|
|
5
|
+
req: Request,
|
|
6
|
+
corsHeaders: Record<string, string>,
|
|
7
|
+
sessionManager: SessionManager
|
|
8
|
+
) {
|
|
9
|
+
try {
|
|
10
|
+
const body = (await req.json()) as CreateSessionRequest;
|
|
11
|
+
const { id, env, cwd, isolation } = body;
|
|
12
|
+
|
|
13
|
+
if (!id) {
|
|
14
|
+
return new Response(
|
|
15
|
+
JSON.stringify({ error: "Session ID is required" }),
|
|
16
|
+
{
|
|
17
|
+
status: 400,
|
|
18
|
+
headers: {
|
|
19
|
+
"Content-Type": "application/json",
|
|
20
|
+
...corsHeaders,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await sessionManager.createSession({
|
|
27
|
+
id,
|
|
28
|
+
env: env || {},
|
|
29
|
+
cwd: cwd || "/workspace",
|
|
30
|
+
isolation: isolation !== false,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(`[Container] Session '${id}' created successfully`);
|
|
34
|
+
console.log(
|
|
35
|
+
`[Container] Available sessions now: ${sessionManager
|
|
36
|
+
.listSessions()
|
|
37
|
+
.join(", ")}`
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return new Response(
|
|
41
|
+
JSON.stringify({
|
|
42
|
+
success: true,
|
|
43
|
+
id,
|
|
44
|
+
message: `Session '${id}' created with${
|
|
45
|
+
isolation !== false ? "" : "out"
|
|
46
|
+
} isolation`,
|
|
47
|
+
}),
|
|
48
|
+
{
|
|
49
|
+
headers: {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
...corsHeaders,
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("[Container] Failed to create session:", error);
|
|
57
|
+
return new Response(
|
|
58
|
+
JSON.stringify({
|
|
59
|
+
error: "Failed to create session",
|
|
60
|
+
message:
|
|
61
|
+
error instanceof Error ? error.message : String(error),
|
|
62
|
+
}),
|
|
63
|
+
{
|
|
64
|
+
status: 500,
|
|
65
|
+
headers: {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
...corsHeaders,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function handleListSessions(
|
|
75
|
+
corsHeaders: Record<string, string>,
|
|
76
|
+
sessionManager: SessionManager
|
|
77
|
+
) {
|
|
78
|
+
const sessionList = sessionManager.listSessions();
|
|
79
|
+
return new Response(
|
|
80
|
+
JSON.stringify({
|
|
81
|
+
count: sessionList.length,
|
|
82
|
+
sessions: sessionList,
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
}),
|
|
85
|
+
{
|
|
86
|
+
headers: {
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
...corsHeaders,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
package/container_src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
1
|
import { serve } from "bun";
|
|
3
2
|
import {
|
|
4
3
|
handleExecuteRequest,
|
|
@@ -6,9 +5,11 @@ import {
|
|
|
6
5
|
} from "./handler/exec";
|
|
7
6
|
import {
|
|
8
7
|
handleDeleteFileRequest,
|
|
8
|
+
handleListFilesRequest,
|
|
9
9
|
handleMkdirRequest,
|
|
10
10
|
handleMoveFileRequest,
|
|
11
11
|
handleReadFileRequest,
|
|
12
|
+
handleReadFileStreamRequest,
|
|
12
13
|
handleRenameFileRequest,
|
|
13
14
|
handleWriteFileRequest,
|
|
14
15
|
} from "./handler/file";
|
|
@@ -28,62 +29,65 @@ import {
|
|
|
28
29
|
handleStartProcessRequest,
|
|
29
30
|
handleStreamProcessLogsRequest,
|
|
30
31
|
} from "./handler/process";
|
|
31
|
-
import
|
|
32
|
-
import {
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
import { handleCreateSession, handleListSessions } from "./handler/session";
|
|
33
|
+
import type { CreateContextRequest } from "./interpreter-service";
|
|
34
|
+
import {
|
|
35
|
+
InterpreterNotReadyError,
|
|
36
|
+
InterpreterService,
|
|
37
|
+
} from "./interpreter-service";
|
|
38
|
+
import { hasNamespaceSupport, SessionManager } from "./isolation";
|
|
37
39
|
|
|
38
40
|
// In-memory storage for exposed ports
|
|
39
41
|
const exposedPorts = new Map<number, { name?: string; exposedAt: Date }>();
|
|
40
42
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
function cleanupOldSessions() {
|
|
51
|
-
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
|
52
|
-
for (const [sessionId, session] of sessions.entries()) {
|
|
53
|
-
if (session.createdAt < oneHourAgo && !session.activeProcess) {
|
|
54
|
-
sessions.delete(sessionId);
|
|
55
|
-
console.log(`[Server] Cleaned up old session: ${sessionId}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
43
|
+
// Check isolation capabilities on startup
|
|
44
|
+
const isolationAvailable = hasNamespaceSupport();
|
|
45
|
+
console.log(
|
|
46
|
+
`[Container] Process isolation: ${
|
|
47
|
+
isolationAvailable
|
|
48
|
+
? "ENABLED (production mode)"
|
|
49
|
+
: "DISABLED (development mode)"
|
|
50
|
+
}`
|
|
51
|
+
);
|
|
59
52
|
|
|
60
|
-
//
|
|
61
|
-
|
|
53
|
+
// Session manager for secure execution with isolation
|
|
54
|
+
const sessionManager = new SessionManager();
|
|
62
55
|
|
|
63
|
-
//
|
|
64
|
-
const
|
|
56
|
+
// Graceful shutdown handler
|
|
57
|
+
const SHUTDOWN_GRACE_PERIOD_MS = 5000; // Grace period for cleanup (5 seconds for proper async cleanup)
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
console.log("[Container]
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
);
|
|
59
|
+
process.on("SIGTERM", async () => {
|
|
60
|
+
console.log("[Container] SIGTERM received, cleaning up sessions...");
|
|
61
|
+
await sessionManager.destroyAll();
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}, SHUTDOWN_GRACE_PERIOD_MS);
|
|
65
|
+
});
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
67
|
+
process.on("SIGINT", async () => {
|
|
68
|
+
console.log("[Container] SIGINT received, cleaning up sessions...");
|
|
69
|
+
await sessionManager.destroyAll();
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}, SHUTDOWN_GRACE_PERIOD_MS);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Cleanup on uncaught exceptions (log but still exit)
|
|
76
|
+
process.on("uncaughtException", async (error) => {
|
|
77
|
+
console.error("[Container] Uncaught exception:", error);
|
|
78
|
+
await sessionManager.destroyAll();
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Initialize interpreter service
|
|
83
|
+
const interpreterService = new InterpreterService();
|
|
84
|
+
|
|
85
|
+
// No initialization needed - service is ready immediately!
|
|
86
|
+
console.log("[Container] Interpreter service ready - no cold start!");
|
|
87
|
+
console.log("[Container] All API endpoints available immediately");
|
|
85
88
|
|
|
86
89
|
const server = serve({
|
|
90
|
+
idleTimeout: 255,
|
|
87
91
|
async fetch(req: Request) {
|
|
88
92
|
const url = new URL(req.url);
|
|
89
93
|
const pathname = url.pathname;
|
|
@@ -117,115 +121,42 @@ const server = serve({
|
|
|
117
121
|
|
|
118
122
|
case "/api/session/create":
|
|
119
123
|
if (req.method === "POST") {
|
|
120
|
-
|
|
121
|
-
const sessionData: SessionData = {
|
|
122
|
-
activeProcess: null,
|
|
123
|
-
createdAt: new Date(),
|
|
124
|
-
sessionId,
|
|
125
|
-
};
|
|
126
|
-
sessions.set(sessionId, sessionData);
|
|
127
|
-
|
|
128
|
-
console.log(`[Server] Created new session: ${sessionId}`);
|
|
129
|
-
|
|
130
|
-
return new Response(
|
|
131
|
-
JSON.stringify({
|
|
132
|
-
message: "Session created successfully",
|
|
133
|
-
sessionId,
|
|
134
|
-
timestamp: new Date().toISOString(),
|
|
135
|
-
}),
|
|
136
|
-
{
|
|
137
|
-
headers: {
|
|
138
|
-
"Content-Type": "application/json",
|
|
139
|
-
...corsHeaders,
|
|
140
|
-
},
|
|
141
|
-
}
|
|
142
|
-
);
|
|
124
|
+
return handleCreateSession(req, corsHeaders, sessionManager);
|
|
143
125
|
}
|
|
144
126
|
break;
|
|
145
127
|
|
|
146
128
|
case "/api/session/list":
|
|
147
129
|
if (req.method === "GET") {
|
|
148
|
-
|
|
149
|
-
(session) => ({
|
|
150
|
-
createdAt: session.createdAt.toISOString(),
|
|
151
|
-
hasActiveProcess: !!session.activeProcess,
|
|
152
|
-
sessionId: session.sessionId,
|
|
153
|
-
})
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
return new Response(
|
|
157
|
-
JSON.stringify({
|
|
158
|
-
count: sessionList.length,
|
|
159
|
-
sessions: sessionList,
|
|
160
|
-
timestamp: new Date().toISOString(),
|
|
161
|
-
}),
|
|
162
|
-
{
|
|
163
|
-
headers: {
|
|
164
|
-
"Content-Type": "application/json",
|
|
165
|
-
...corsHeaders,
|
|
166
|
-
},
|
|
167
|
-
}
|
|
168
|
-
);
|
|
130
|
+
return handleListSessions(corsHeaders, sessionManager);
|
|
169
131
|
}
|
|
170
132
|
break;
|
|
171
133
|
|
|
172
134
|
case "/api/execute":
|
|
173
135
|
if (req.method === "POST") {
|
|
174
|
-
return handleExecuteRequest(
|
|
136
|
+
return handleExecuteRequest(req, corsHeaders, sessionManager);
|
|
175
137
|
}
|
|
176
138
|
break;
|
|
177
139
|
|
|
178
140
|
case "/api/execute/stream":
|
|
179
141
|
if (req.method === "POST") {
|
|
180
|
-
return handleStreamingExecuteRequest(
|
|
142
|
+
return handleStreamingExecuteRequest(
|
|
143
|
+
req,
|
|
144
|
+
sessionManager,
|
|
145
|
+
corsHeaders
|
|
146
|
+
);
|
|
181
147
|
}
|
|
182
148
|
break;
|
|
183
149
|
|
|
184
150
|
case "/api/ping":
|
|
185
151
|
if (req.method === "GET") {
|
|
186
|
-
const health = await
|
|
152
|
+
const health = await interpreterService.getHealthStatus();
|
|
187
153
|
return new Response(
|
|
188
154
|
JSON.stringify({
|
|
189
155
|
message: "pong",
|
|
190
156
|
timestamp: new Date().toISOString(),
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
? "initializing"
|
|
195
|
-
: "not ready",
|
|
196
|
-
jupyterHealth: health,
|
|
197
|
-
}),
|
|
198
|
-
{
|
|
199
|
-
headers: {
|
|
200
|
-
"Content-Type": "application/json",
|
|
201
|
-
...corsHeaders,
|
|
202
|
-
},
|
|
203
|
-
}
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
break;
|
|
207
|
-
|
|
208
|
-
case "/api/commands":
|
|
209
|
-
if (req.method === "GET") {
|
|
210
|
-
return new Response(
|
|
211
|
-
JSON.stringify({
|
|
212
|
-
availableCommands: [
|
|
213
|
-
"ls",
|
|
214
|
-
"pwd",
|
|
215
|
-
"echo",
|
|
216
|
-
"cat",
|
|
217
|
-
"grep",
|
|
218
|
-
"find",
|
|
219
|
-
"whoami",
|
|
220
|
-
"date",
|
|
221
|
-
"uptime",
|
|
222
|
-
"ps",
|
|
223
|
-
"top",
|
|
224
|
-
"df",
|
|
225
|
-
"du",
|
|
226
|
-
"free",
|
|
227
|
-
],
|
|
228
|
-
timestamp: new Date().toISOString(),
|
|
157
|
+
system: "interpreter (70x faster)",
|
|
158
|
+
status: health.ready ? "ready" : "initializing",
|
|
159
|
+
progress: health.progress,
|
|
229
160
|
}),
|
|
230
161
|
{
|
|
231
162
|
headers: {
|
|
@@ -239,43 +170,55 @@ const server = serve({
|
|
|
239
170
|
|
|
240
171
|
case "/api/git/checkout":
|
|
241
172
|
if (req.method === "POST") {
|
|
242
|
-
return handleGitCheckoutRequest(
|
|
173
|
+
return handleGitCheckoutRequest(req, corsHeaders, sessionManager);
|
|
243
174
|
}
|
|
244
175
|
break;
|
|
245
176
|
|
|
246
177
|
case "/api/mkdir":
|
|
247
178
|
if (req.method === "POST") {
|
|
248
|
-
return handleMkdirRequest(
|
|
179
|
+
return handleMkdirRequest(req, corsHeaders, sessionManager);
|
|
249
180
|
}
|
|
250
181
|
break;
|
|
251
182
|
|
|
252
183
|
case "/api/write":
|
|
253
184
|
if (req.method === "POST") {
|
|
254
|
-
return handleWriteFileRequest(req, corsHeaders);
|
|
185
|
+
return handleWriteFileRequest(req, corsHeaders, sessionManager);
|
|
255
186
|
}
|
|
256
187
|
break;
|
|
257
188
|
|
|
258
189
|
case "/api/read":
|
|
259
190
|
if (req.method === "POST") {
|
|
260
|
-
return handleReadFileRequest(req, corsHeaders);
|
|
191
|
+
return handleReadFileRequest(req, corsHeaders, sessionManager);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case "/api/read/stream":
|
|
196
|
+
if (req.method === "POST") {
|
|
197
|
+
return handleReadFileStreamRequest(req, corsHeaders, sessionManager);
|
|
261
198
|
}
|
|
262
199
|
break;
|
|
263
200
|
|
|
264
201
|
case "/api/delete":
|
|
265
202
|
if (req.method === "POST") {
|
|
266
|
-
return handleDeleteFileRequest(req, corsHeaders);
|
|
203
|
+
return handleDeleteFileRequest(req, corsHeaders, sessionManager);
|
|
267
204
|
}
|
|
268
205
|
break;
|
|
269
206
|
|
|
270
207
|
case "/api/rename":
|
|
271
208
|
if (req.method === "POST") {
|
|
272
|
-
return handleRenameFileRequest(req, corsHeaders);
|
|
209
|
+
return handleRenameFileRequest(req, corsHeaders, sessionManager);
|
|
273
210
|
}
|
|
274
211
|
break;
|
|
275
212
|
|
|
276
213
|
case "/api/move":
|
|
277
214
|
if (req.method === "POST") {
|
|
278
|
-
return handleMoveFileRequest(req, corsHeaders);
|
|
215
|
+
return handleMoveFileRequest(req, corsHeaders, sessionManager);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case "/api/list-files":
|
|
220
|
+
if (req.method === "POST") {
|
|
221
|
+
return handleListFilesRequest(req, corsHeaders, sessionManager);
|
|
279
222
|
}
|
|
280
223
|
break;
|
|
281
224
|
|
|
@@ -299,28 +242,31 @@ const server = serve({
|
|
|
299
242
|
|
|
300
243
|
case "/api/process/start":
|
|
301
244
|
if (req.method === "POST") {
|
|
302
|
-
return handleStartProcessRequest(
|
|
245
|
+
return handleStartProcessRequest(req, corsHeaders, sessionManager);
|
|
303
246
|
}
|
|
304
247
|
break;
|
|
305
248
|
|
|
306
249
|
case "/api/process/list":
|
|
307
250
|
if (req.method === "GET") {
|
|
308
|
-
return handleListProcessesRequest(
|
|
251
|
+
return handleListProcessesRequest(req, corsHeaders, sessionManager);
|
|
309
252
|
}
|
|
310
253
|
break;
|
|
311
254
|
|
|
312
255
|
case "/api/process/kill-all":
|
|
313
256
|
if (req.method === "DELETE") {
|
|
314
|
-
return handleKillAllProcessesRequest(
|
|
257
|
+
return handleKillAllProcessesRequest(
|
|
258
|
+
req,
|
|
259
|
+
corsHeaders,
|
|
260
|
+
sessionManager
|
|
261
|
+
);
|
|
315
262
|
}
|
|
316
263
|
break;
|
|
317
264
|
|
|
318
|
-
// Code interpreter endpoints
|
|
319
265
|
case "/api/contexts":
|
|
320
266
|
if (req.method === "POST") {
|
|
321
267
|
try {
|
|
322
268
|
const body = (await req.json()) as CreateContextRequest;
|
|
323
|
-
const context = await
|
|
269
|
+
const context = await interpreterService.createContext(body);
|
|
324
270
|
return new Response(
|
|
325
271
|
JSON.stringify({
|
|
326
272
|
id: context.id,
|
|
@@ -337,10 +283,9 @@ const server = serve({
|
|
|
337
283
|
}
|
|
338
284
|
);
|
|
339
285
|
} catch (error) {
|
|
340
|
-
if (error instanceof
|
|
341
|
-
// This happens when request times out waiting for Jupyter
|
|
286
|
+
if (error instanceof InterpreterNotReadyError) {
|
|
342
287
|
console.log(
|
|
343
|
-
`[Container] Request timed out waiting for
|
|
288
|
+
`[Container] Request timed out waiting for interpreter (${error.progress}% complete)`
|
|
344
289
|
);
|
|
345
290
|
return new Response(
|
|
346
291
|
JSON.stringify({
|
|
@@ -405,7 +350,7 @@ const server = serve({
|
|
|
405
350
|
);
|
|
406
351
|
}
|
|
407
352
|
} else if (req.method === "GET") {
|
|
408
|
-
const contexts = await
|
|
353
|
+
const contexts = await interpreterService.listContexts();
|
|
409
354
|
return new Response(JSON.stringify({ contexts }), {
|
|
410
355
|
headers: {
|
|
411
356
|
"Content-Type": "application/json",
|
|
@@ -423,7 +368,7 @@ const server = serve({
|
|
|
423
368
|
code: string;
|
|
424
369
|
language?: string;
|
|
425
370
|
};
|
|
426
|
-
return await
|
|
371
|
+
return await interpreterService.executeCode(
|
|
427
372
|
body.context_id,
|
|
428
373
|
body.code,
|
|
429
374
|
body.language
|
|
@@ -462,12 +407,12 @@ const server = serve({
|
|
|
462
407
|
error.message.includes("initializing")
|
|
463
408
|
) {
|
|
464
409
|
console.log(
|
|
465
|
-
"[Container] Code execution deferred -
|
|
410
|
+
"[Container] Code execution deferred - service still initializing"
|
|
466
411
|
);
|
|
467
412
|
} else {
|
|
468
413
|
console.error("[Container] Error executing code:", error);
|
|
469
414
|
}
|
|
470
|
-
// Error response is already handled by
|
|
415
|
+
// Error response is already handled by service.executeCode for not ready state
|
|
471
416
|
return new Response(
|
|
472
417
|
JSON.stringify({
|
|
473
418
|
error:
|
|
@@ -496,7 +441,7 @@ const server = serve({
|
|
|
496
441
|
const contextId = pathname.split("/")[3];
|
|
497
442
|
if (req.method === "DELETE") {
|
|
498
443
|
try {
|
|
499
|
-
await
|
|
444
|
+
await interpreterService.deleteContext(contextId);
|
|
500
445
|
return new Response(JSON.stringify({ success: true }), {
|
|
501
446
|
headers: {
|
|
502
447
|
"Content-Type": "application/json",
|
|
@@ -504,9 +449,9 @@ const server = serve({
|
|
|
504
449
|
},
|
|
505
450
|
});
|
|
506
451
|
} catch (error) {
|
|
507
|
-
if (error instanceof
|
|
452
|
+
if (error instanceof InterpreterNotReadyError) {
|
|
508
453
|
console.log(
|
|
509
|
-
`[Container] Request timed out waiting for
|
|
454
|
+
`[Container] Request timed out waiting for interpreter (${error.progress}% complete)`
|
|
510
455
|
);
|
|
511
456
|
return new Response(
|
|
512
457
|
JSON.stringify({
|
|
@@ -556,31 +501,31 @@ const server = serve({
|
|
|
556
501
|
|
|
557
502
|
if (!action && req.method === "GET") {
|
|
558
503
|
return handleGetProcessRequest(
|
|
559
|
-
processes,
|
|
560
504
|
req,
|
|
561
505
|
corsHeaders,
|
|
562
|
-
processId
|
|
506
|
+
processId,
|
|
507
|
+
sessionManager
|
|
563
508
|
);
|
|
564
509
|
} else if (!action && req.method === "DELETE") {
|
|
565
510
|
return handleKillProcessRequest(
|
|
566
|
-
processes,
|
|
567
511
|
req,
|
|
568
512
|
corsHeaders,
|
|
569
|
-
processId
|
|
513
|
+
processId,
|
|
514
|
+
sessionManager
|
|
570
515
|
);
|
|
571
516
|
} else if (action === "logs" && req.method === "GET") {
|
|
572
517
|
return handleGetProcessLogsRequest(
|
|
573
|
-
processes,
|
|
574
518
|
req,
|
|
575
519
|
corsHeaders,
|
|
576
|
-
processId
|
|
520
|
+
processId,
|
|
521
|
+
sessionManager
|
|
577
522
|
);
|
|
578
523
|
} else if (action === "stream" && req.method === "GET") {
|
|
579
524
|
return handleStreamProcessLogsRequest(
|
|
580
|
-
processes,
|
|
581
525
|
req,
|
|
582
526
|
corsHeaders,
|
|
583
|
-
processId
|
|
527
|
+
processId,
|
|
528
|
+
sessionManager
|
|
584
529
|
);
|
|
585
530
|
}
|
|
586
531
|
}
|
|
@@ -632,6 +577,7 @@ console.log(` POST /api/git/checkout - Checkout a git repository`);
|
|
|
632
577
|
console.log(` POST /api/mkdir - Create a directory`);
|
|
633
578
|
console.log(` POST /api/write - Write a file`);
|
|
634
579
|
console.log(` POST /api/read - Read a file`);
|
|
580
|
+
console.log(` POST /api/read/stream - Stream a file (SSE)`);
|
|
635
581
|
console.log(` POST /api/delete - Delete a file`);
|
|
636
582
|
console.log(` POST /api/rename - Rename a file`);
|
|
637
583
|
console.log(` POST /api/move - Move a file`);
|
|
@@ -653,4 +599,3 @@ console.log(
|
|
|
653
599
|
` POST /api/execute/code - Execute code in a context (streaming)`
|
|
654
600
|
);
|
|
655
601
|
console.log(` GET /api/ping - Health check`);
|
|
656
|
-
console.log(` GET /api/commands - List available commands`);
|