@cloudflare/sandbox 0.3.7 → 0.4.2
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 +44 -0
- package/CHANGELOG.md +8 -10
- package/Dockerfile +82 -18
- package/README.md +89 -824
- 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-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
- 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-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
- package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
- package/dist/file-stream.d.ts +16 -38
- package/dist/file-stream.js +1 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +45 -38
- package/dist/interpreter.d.ts +3 -3
- package/dist/interpreter.js +2 -2
- package/dist/request-handler.d.ts +4 -3
- package/dist/request-handler.js +4 -7
- package/dist/sandbox-D9K2ypln.d.ts +583 -0
- package/dist/sandbox.d.ts +3 -3
- package/dist/sandbox.js +4 -7
- package/dist/security.d.ts +4 -3
- package/dist/security.js +3 -3
- package/dist/sse-parser.js +1 -1
- package/package.json +12 -4
- 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/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
- 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 +119 -117
- package/src/index.ts +81 -69
- package/src/interpreter.ts +17 -8
- package/src/request-handler.ts +69 -43
- package/src/sandbox.ts +694 -533
- 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 -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 -457
- 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/interpreter-service.ts +0 -276
- package/container_src/isolation.ts +0 -1213
- 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/dist/chunk-32UDXUPC.js +0 -671
- package/dist/chunk-32UDXUPC.js.map +0 -1
- package/dist/chunk-5DILEXGY.js +0 -85
- package/dist/chunk-5DILEXGY.js.map +0 -1
- package/dist/chunk-D3U63BZP.js +0 -240
- package/dist/chunk-D3U63BZP.js.map +0 -1
- package/dist/chunk-FXYPFGOZ.js +0 -129
- package/dist/chunk-FXYPFGOZ.js.map +0 -1
- package/dist/chunk-JTKON2SH.js +0 -113
- package/dist/chunk-JTKON2SH.js.map +0 -1
- package/dist/chunk-NNGBXDMY.js.map +0 -1
- package/dist/chunk-SQLJNZ3K.js +0 -674
- package/dist/chunk-SQLJNZ3K.js.map +0 -1
- package/dist/chunk-W7TVRPBG.js +0 -108
- package/dist/chunk-W7TVRPBG.js.map +0 -1
- package/dist/client-B3RUab0s.d.ts +0 -225
- package/dist/client.d.ts +0 -4
- package/dist/client.js +0 -7
- package/dist/client.js.map +0 -1
- package/dist/errors.d.ts +0 -95
- package/dist/errors.js +0 -27
- package/dist/errors.js.map +0 -1
- package/dist/interpreter-client.d.ts +0 -4
- package/dist/interpreter-client.js +0 -9
- package/dist/interpreter-client.js.map +0 -1
- package/dist/interpreter-types.d.ts +0 -259
- package/dist/interpreter-types.js +0 -9
- package/dist/interpreter-types.js.map +0 -1
- package/dist/types.d.ts +0 -453
- package/dist/types.js +0 -45
- package/dist/types.js.map +0 -1
- package/src/client.ts +0 -1048
- package/src/errors.ts +0 -219
- package/src/interpreter-types.ts +0 -390
- package/src/types.ts +0 -571
package/container_src/index.ts
DELETED
|
@@ -1,601 +0,0 @@
|
|
|
1
|
-
import { serve } from "bun";
|
|
2
|
-
import {
|
|
3
|
-
handleExecuteRequest,
|
|
4
|
-
handleStreamingExecuteRequest,
|
|
5
|
-
} from "./handler/exec";
|
|
6
|
-
import {
|
|
7
|
-
handleDeleteFileRequest,
|
|
8
|
-
handleListFilesRequest,
|
|
9
|
-
handleMkdirRequest,
|
|
10
|
-
handleMoveFileRequest,
|
|
11
|
-
handleReadFileRequest,
|
|
12
|
-
handleReadFileStreamRequest,
|
|
13
|
-
handleRenameFileRequest,
|
|
14
|
-
handleWriteFileRequest,
|
|
15
|
-
} from "./handler/file";
|
|
16
|
-
import { handleGitCheckoutRequest } from "./handler/git";
|
|
17
|
-
import {
|
|
18
|
-
handleExposePortRequest,
|
|
19
|
-
handleGetExposedPortsRequest,
|
|
20
|
-
handleProxyRequest,
|
|
21
|
-
handleUnexposePortRequest,
|
|
22
|
-
} from "./handler/ports";
|
|
23
|
-
import {
|
|
24
|
-
handleGetProcessLogsRequest,
|
|
25
|
-
handleGetProcessRequest,
|
|
26
|
-
handleKillAllProcessesRequest,
|
|
27
|
-
handleKillProcessRequest,
|
|
28
|
-
handleListProcessesRequest,
|
|
29
|
-
handleStartProcessRequest,
|
|
30
|
-
handleStreamProcessLogsRequest,
|
|
31
|
-
} from "./handler/process";
|
|
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";
|
|
39
|
-
|
|
40
|
-
// In-memory storage for exposed ports
|
|
41
|
-
const exposedPorts = new Map<number, { name?: string; exposedAt: Date }>();
|
|
42
|
-
|
|
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
|
-
);
|
|
52
|
-
|
|
53
|
-
// Session manager for secure execution with isolation
|
|
54
|
-
const sessionManager = new SessionManager();
|
|
55
|
-
|
|
56
|
-
// Graceful shutdown handler
|
|
57
|
-
const SHUTDOWN_GRACE_PERIOD_MS = 5000; // Grace period for cleanup (5 seconds for proper async cleanup)
|
|
58
|
-
|
|
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
|
-
});
|
|
66
|
-
|
|
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");
|
|
88
|
-
|
|
89
|
-
const server = serve({
|
|
90
|
-
idleTimeout: 255,
|
|
91
|
-
async fetch(req: Request) {
|
|
92
|
-
const url = new URL(req.url);
|
|
93
|
-
const pathname = url.pathname;
|
|
94
|
-
|
|
95
|
-
console.log(`[Container] Incoming ${req.method} request to ${pathname}`);
|
|
96
|
-
|
|
97
|
-
// Handle CORS
|
|
98
|
-
const corsHeaders = {
|
|
99
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
100
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
101
|
-
"Access-Control-Allow-Origin": "*",
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Handle preflight requests
|
|
105
|
-
if (req.method === "OPTIONS") {
|
|
106
|
-
console.log(`[Container] Handling CORS preflight for ${pathname}`);
|
|
107
|
-
return new Response(null, { headers: corsHeaders, status: 200 });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
// Handle different routes
|
|
112
|
-
console.log(`[Container] Processing ${req.method} ${pathname}`);
|
|
113
|
-
switch (pathname) {
|
|
114
|
-
case "/":
|
|
115
|
-
return new Response("Hello from Bun server! 🚀", {
|
|
116
|
-
headers: {
|
|
117
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
118
|
-
...corsHeaders,
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
case "/api/session/create":
|
|
123
|
-
if (req.method === "POST") {
|
|
124
|
-
return handleCreateSession(req, corsHeaders, sessionManager);
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
|
|
128
|
-
case "/api/session/list":
|
|
129
|
-
if (req.method === "GET") {
|
|
130
|
-
return handleListSessions(corsHeaders, sessionManager);
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
133
|
-
|
|
134
|
-
case "/api/execute":
|
|
135
|
-
if (req.method === "POST") {
|
|
136
|
-
return handleExecuteRequest(req, corsHeaders, sessionManager);
|
|
137
|
-
}
|
|
138
|
-
break;
|
|
139
|
-
|
|
140
|
-
case "/api/execute/stream":
|
|
141
|
-
if (req.method === "POST") {
|
|
142
|
-
return handleStreamingExecuteRequest(
|
|
143
|
-
req,
|
|
144
|
-
sessionManager,
|
|
145
|
-
corsHeaders
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
break;
|
|
149
|
-
|
|
150
|
-
case "/api/ping":
|
|
151
|
-
if (req.method === "GET") {
|
|
152
|
-
const health = await interpreterService.getHealthStatus();
|
|
153
|
-
return new Response(
|
|
154
|
-
JSON.stringify({
|
|
155
|
-
message: "pong",
|
|
156
|
-
timestamp: new Date().toISOString(),
|
|
157
|
-
system: "interpreter (70x faster)",
|
|
158
|
-
status: health.ready ? "ready" : "initializing",
|
|
159
|
-
progress: health.progress,
|
|
160
|
-
}),
|
|
161
|
-
{
|
|
162
|
-
headers: {
|
|
163
|
-
"Content-Type": "application/json",
|
|
164
|
-
...corsHeaders,
|
|
165
|
-
},
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
break;
|
|
170
|
-
|
|
171
|
-
case "/api/git/checkout":
|
|
172
|
-
if (req.method === "POST") {
|
|
173
|
-
return handleGitCheckoutRequest(req, corsHeaders, sessionManager);
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
|
|
177
|
-
case "/api/mkdir":
|
|
178
|
-
if (req.method === "POST") {
|
|
179
|
-
return handleMkdirRequest(req, corsHeaders, sessionManager);
|
|
180
|
-
}
|
|
181
|
-
break;
|
|
182
|
-
|
|
183
|
-
case "/api/write":
|
|
184
|
-
if (req.method === "POST") {
|
|
185
|
-
return handleWriteFileRequest(req, corsHeaders, sessionManager);
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
|
|
189
|
-
case "/api/read":
|
|
190
|
-
if (req.method === "POST") {
|
|
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);
|
|
198
|
-
}
|
|
199
|
-
break;
|
|
200
|
-
|
|
201
|
-
case "/api/delete":
|
|
202
|
-
if (req.method === "POST") {
|
|
203
|
-
return handleDeleteFileRequest(req, corsHeaders, sessionManager);
|
|
204
|
-
}
|
|
205
|
-
break;
|
|
206
|
-
|
|
207
|
-
case "/api/rename":
|
|
208
|
-
if (req.method === "POST") {
|
|
209
|
-
return handleRenameFileRequest(req, corsHeaders, sessionManager);
|
|
210
|
-
}
|
|
211
|
-
break;
|
|
212
|
-
|
|
213
|
-
case "/api/move":
|
|
214
|
-
if (req.method === "POST") {
|
|
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);
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
|
|
225
|
-
case "/api/expose-port":
|
|
226
|
-
if (req.method === "POST") {
|
|
227
|
-
return handleExposePortRequest(exposedPorts, req, corsHeaders);
|
|
228
|
-
}
|
|
229
|
-
break;
|
|
230
|
-
|
|
231
|
-
case "/api/unexpose-port":
|
|
232
|
-
if (req.method === "DELETE") {
|
|
233
|
-
return handleUnexposePortRequest(exposedPorts, req, corsHeaders);
|
|
234
|
-
}
|
|
235
|
-
break;
|
|
236
|
-
|
|
237
|
-
case "/api/exposed-ports":
|
|
238
|
-
if (req.method === "GET") {
|
|
239
|
-
return handleGetExposedPortsRequest(exposedPorts, req, corsHeaders);
|
|
240
|
-
}
|
|
241
|
-
break;
|
|
242
|
-
|
|
243
|
-
case "/api/process/start":
|
|
244
|
-
if (req.method === "POST") {
|
|
245
|
-
return handleStartProcessRequest(req, corsHeaders, sessionManager);
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
|
|
249
|
-
case "/api/process/list":
|
|
250
|
-
if (req.method === "GET") {
|
|
251
|
-
return handleListProcessesRequest(req, corsHeaders, sessionManager);
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
|
|
255
|
-
case "/api/process/kill-all":
|
|
256
|
-
if (req.method === "DELETE") {
|
|
257
|
-
return handleKillAllProcessesRequest(
|
|
258
|
-
req,
|
|
259
|
-
corsHeaders,
|
|
260
|
-
sessionManager
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
break;
|
|
264
|
-
|
|
265
|
-
case "/api/contexts":
|
|
266
|
-
if (req.method === "POST") {
|
|
267
|
-
try {
|
|
268
|
-
const body = (await req.json()) as CreateContextRequest;
|
|
269
|
-
const context = await interpreterService.createContext(body);
|
|
270
|
-
return new Response(
|
|
271
|
-
JSON.stringify({
|
|
272
|
-
id: context.id,
|
|
273
|
-
language: context.language,
|
|
274
|
-
cwd: context.cwd,
|
|
275
|
-
createdAt: context.createdAt,
|
|
276
|
-
lastUsed: context.lastUsed,
|
|
277
|
-
}),
|
|
278
|
-
{
|
|
279
|
-
headers: {
|
|
280
|
-
"Content-Type": "application/json",
|
|
281
|
-
...corsHeaders,
|
|
282
|
-
},
|
|
283
|
-
}
|
|
284
|
-
);
|
|
285
|
-
} catch (error) {
|
|
286
|
-
if (error instanceof InterpreterNotReadyError) {
|
|
287
|
-
console.log(
|
|
288
|
-
`[Container] Request timed out waiting for interpreter (${error.progress}% complete)`
|
|
289
|
-
);
|
|
290
|
-
return new Response(
|
|
291
|
-
JSON.stringify({
|
|
292
|
-
error: error.message,
|
|
293
|
-
status: "initializing",
|
|
294
|
-
progress: error.progress,
|
|
295
|
-
}),
|
|
296
|
-
{
|
|
297
|
-
status: 503,
|
|
298
|
-
headers: {
|
|
299
|
-
"Content-Type": "application/json",
|
|
300
|
-
"Retry-After": String(error.retryAfter),
|
|
301
|
-
...corsHeaders,
|
|
302
|
-
},
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Check if it's a circuit breaker error
|
|
308
|
-
if (
|
|
309
|
-
error instanceof Error &&
|
|
310
|
-
error.message.includes("Circuit breaker is open")
|
|
311
|
-
) {
|
|
312
|
-
console.log(
|
|
313
|
-
"[Container] Circuit breaker is open:",
|
|
314
|
-
error.message
|
|
315
|
-
);
|
|
316
|
-
return new Response(
|
|
317
|
-
JSON.stringify({
|
|
318
|
-
error:
|
|
319
|
-
"Service temporarily unavailable due to high error rate. Please try again later.",
|
|
320
|
-
status: "circuit_open",
|
|
321
|
-
details: error.message,
|
|
322
|
-
}),
|
|
323
|
-
{
|
|
324
|
-
status: 503,
|
|
325
|
-
headers: {
|
|
326
|
-
"Content-Type": "application/json",
|
|
327
|
-
"Retry-After": "60",
|
|
328
|
-
...corsHeaders,
|
|
329
|
-
},
|
|
330
|
-
}
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Only log actual errors with stack traces
|
|
335
|
-
console.error("[Container] Error creating context:", error);
|
|
336
|
-
return new Response(
|
|
337
|
-
JSON.stringify({
|
|
338
|
-
error:
|
|
339
|
-
error instanceof Error
|
|
340
|
-
? error.message
|
|
341
|
-
: "Failed to create context",
|
|
342
|
-
}),
|
|
343
|
-
{
|
|
344
|
-
status: 500,
|
|
345
|
-
headers: {
|
|
346
|
-
"Content-Type": "application/json",
|
|
347
|
-
...corsHeaders,
|
|
348
|
-
},
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
} else if (req.method === "GET") {
|
|
353
|
-
const contexts = await interpreterService.listContexts();
|
|
354
|
-
return new Response(JSON.stringify({ contexts }), {
|
|
355
|
-
headers: {
|
|
356
|
-
"Content-Type": "application/json",
|
|
357
|
-
...corsHeaders,
|
|
358
|
-
},
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
break;
|
|
362
|
-
|
|
363
|
-
case "/api/execute/code":
|
|
364
|
-
if (req.method === "POST") {
|
|
365
|
-
try {
|
|
366
|
-
const body = (await req.json()) as {
|
|
367
|
-
context_id: string;
|
|
368
|
-
code: string;
|
|
369
|
-
language?: string;
|
|
370
|
-
};
|
|
371
|
-
return await interpreterService.executeCode(
|
|
372
|
-
body.context_id,
|
|
373
|
-
body.code,
|
|
374
|
-
body.language
|
|
375
|
-
);
|
|
376
|
-
} catch (error) {
|
|
377
|
-
// Check if it's a circuit breaker error
|
|
378
|
-
if (
|
|
379
|
-
error instanceof Error &&
|
|
380
|
-
error.message.includes("Circuit breaker is open")
|
|
381
|
-
) {
|
|
382
|
-
console.log(
|
|
383
|
-
"[Container] Circuit breaker is open for code execution:",
|
|
384
|
-
error.message
|
|
385
|
-
);
|
|
386
|
-
return new Response(
|
|
387
|
-
JSON.stringify({
|
|
388
|
-
error:
|
|
389
|
-
"Service temporarily unavailable due to high error rate. Please try again later.",
|
|
390
|
-
status: "circuit_open",
|
|
391
|
-
details: error.message,
|
|
392
|
-
}),
|
|
393
|
-
{
|
|
394
|
-
status: 503,
|
|
395
|
-
headers: {
|
|
396
|
-
"Content-Type": "application/json",
|
|
397
|
-
"Retry-After": "30",
|
|
398
|
-
...corsHeaders,
|
|
399
|
-
},
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Don't log stack traces for expected initialization state
|
|
405
|
-
if (
|
|
406
|
-
error instanceof Error &&
|
|
407
|
-
error.message.includes("initializing")
|
|
408
|
-
) {
|
|
409
|
-
console.log(
|
|
410
|
-
"[Container] Code execution deferred - service still initializing"
|
|
411
|
-
);
|
|
412
|
-
} else {
|
|
413
|
-
console.error("[Container] Error executing code:", error);
|
|
414
|
-
}
|
|
415
|
-
// Error response is already handled by service.executeCode for not ready state
|
|
416
|
-
return new Response(
|
|
417
|
-
JSON.stringify({
|
|
418
|
-
error:
|
|
419
|
-
error instanceof Error
|
|
420
|
-
? error.message
|
|
421
|
-
: "Failed to execute code",
|
|
422
|
-
}),
|
|
423
|
-
{
|
|
424
|
-
status: 500,
|
|
425
|
-
headers: {
|
|
426
|
-
"Content-Type": "application/json",
|
|
427
|
-
...corsHeaders,
|
|
428
|
-
},
|
|
429
|
-
}
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
break;
|
|
434
|
-
|
|
435
|
-
default:
|
|
436
|
-
// Handle dynamic routes for contexts
|
|
437
|
-
if (
|
|
438
|
-
pathname.startsWith("/api/contexts/") &&
|
|
439
|
-
pathname.split("/").length === 4
|
|
440
|
-
) {
|
|
441
|
-
const contextId = pathname.split("/")[3];
|
|
442
|
-
if (req.method === "DELETE") {
|
|
443
|
-
try {
|
|
444
|
-
await interpreterService.deleteContext(contextId);
|
|
445
|
-
return new Response(JSON.stringify({ success: true }), {
|
|
446
|
-
headers: {
|
|
447
|
-
"Content-Type": "application/json",
|
|
448
|
-
...corsHeaders,
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
} catch (error) {
|
|
452
|
-
if (error instanceof InterpreterNotReadyError) {
|
|
453
|
-
console.log(
|
|
454
|
-
`[Container] Request timed out waiting for interpreter (${error.progress}% complete)`
|
|
455
|
-
);
|
|
456
|
-
return new Response(
|
|
457
|
-
JSON.stringify({
|
|
458
|
-
error: error.message,
|
|
459
|
-
status: "initializing",
|
|
460
|
-
progress: error.progress,
|
|
461
|
-
}),
|
|
462
|
-
{
|
|
463
|
-
status: 503,
|
|
464
|
-
headers: {
|
|
465
|
-
"Content-Type": "application/json",
|
|
466
|
-
"Retry-After": "5",
|
|
467
|
-
...corsHeaders,
|
|
468
|
-
},
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
return new Response(
|
|
473
|
-
JSON.stringify({
|
|
474
|
-
error:
|
|
475
|
-
error instanceof Error
|
|
476
|
-
? error.message
|
|
477
|
-
: "Failed to delete context",
|
|
478
|
-
}),
|
|
479
|
-
{
|
|
480
|
-
status:
|
|
481
|
-
error instanceof Error &&
|
|
482
|
-
error.message.includes("not found")
|
|
483
|
-
? 404
|
|
484
|
-
: 500,
|
|
485
|
-
headers: {
|
|
486
|
-
"Content-Type": "application/json",
|
|
487
|
-
...corsHeaders,
|
|
488
|
-
},
|
|
489
|
-
}
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Handle dynamic routes for individual processes
|
|
496
|
-
if (pathname.startsWith("/api/process/")) {
|
|
497
|
-
const segments = pathname.split("/");
|
|
498
|
-
if (segments.length >= 4) {
|
|
499
|
-
const processId = segments[3];
|
|
500
|
-
const action = segments[4]; // Optional: logs, stream, etc.
|
|
501
|
-
|
|
502
|
-
if (!action && req.method === "GET") {
|
|
503
|
-
return handleGetProcessRequest(
|
|
504
|
-
req,
|
|
505
|
-
corsHeaders,
|
|
506
|
-
processId,
|
|
507
|
-
sessionManager
|
|
508
|
-
);
|
|
509
|
-
} else if (!action && req.method === "DELETE") {
|
|
510
|
-
return handleKillProcessRequest(
|
|
511
|
-
req,
|
|
512
|
-
corsHeaders,
|
|
513
|
-
processId,
|
|
514
|
-
sessionManager
|
|
515
|
-
);
|
|
516
|
-
} else if (action === "logs" && req.method === "GET") {
|
|
517
|
-
return handleGetProcessLogsRequest(
|
|
518
|
-
req,
|
|
519
|
-
corsHeaders,
|
|
520
|
-
processId,
|
|
521
|
-
sessionManager
|
|
522
|
-
);
|
|
523
|
-
} else if (action === "stream" && req.method === "GET") {
|
|
524
|
-
return handleStreamProcessLogsRequest(
|
|
525
|
-
req,
|
|
526
|
-
corsHeaders,
|
|
527
|
-
processId,
|
|
528
|
-
sessionManager
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
// Check if this is a proxy request for an exposed port
|
|
534
|
-
if (pathname.startsWith("/proxy/")) {
|
|
535
|
-
return handleProxyRequest(exposedPorts, req, corsHeaders);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
console.log(`[Container] Route not found: ${pathname}`);
|
|
539
|
-
return new Response("Not Found", {
|
|
540
|
-
headers: corsHeaders,
|
|
541
|
-
status: 404,
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
} catch (error) {
|
|
545
|
-
console.error(
|
|
546
|
-
`[Container] Error handling ${req.method} ${pathname}:`,
|
|
547
|
-
error
|
|
548
|
-
);
|
|
549
|
-
return new Response(
|
|
550
|
-
JSON.stringify({
|
|
551
|
-
error: "Internal server error",
|
|
552
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
553
|
-
}),
|
|
554
|
-
{
|
|
555
|
-
headers: {
|
|
556
|
-
"Content-Type": "application/json",
|
|
557
|
-
...corsHeaders,
|
|
558
|
-
},
|
|
559
|
-
status: 500,
|
|
560
|
-
}
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
},
|
|
564
|
-
hostname: "0.0.0.0",
|
|
565
|
-
port: 3000,
|
|
566
|
-
// We don't need this, but typescript complains
|
|
567
|
-
websocket: { async message() {} },
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
console.log(`🚀 Bun server running on http://0.0.0.0:${server.port}`);
|
|
571
|
-
console.log(`📡 HTTP API endpoints available:`);
|
|
572
|
-
console.log(` POST /api/session/create - Create a new session`);
|
|
573
|
-
console.log(` GET /api/session/list - List all sessions`);
|
|
574
|
-
console.log(` POST /api/execute - Execute a command (non-streaming)`);
|
|
575
|
-
console.log(` POST /api/execute/stream - Execute a command (streaming)`);
|
|
576
|
-
console.log(` POST /api/git/checkout - Checkout a git repository`);
|
|
577
|
-
console.log(` POST /api/mkdir - Create a directory`);
|
|
578
|
-
console.log(` POST /api/write - Write a file`);
|
|
579
|
-
console.log(` POST /api/read - Read a file`);
|
|
580
|
-
console.log(` POST /api/read/stream - Stream a file (SSE)`);
|
|
581
|
-
console.log(` POST /api/delete - Delete a file`);
|
|
582
|
-
console.log(` POST /api/rename - Rename a file`);
|
|
583
|
-
console.log(` POST /api/move - Move a file`);
|
|
584
|
-
console.log(` POST /api/expose-port - Expose a port for external access`);
|
|
585
|
-
console.log(` DELETE /api/unexpose-port - Unexpose a port`);
|
|
586
|
-
console.log(` GET /api/exposed-ports - List exposed ports`);
|
|
587
|
-
console.log(` POST /api/process/start - Start a background process`);
|
|
588
|
-
console.log(` GET /api/process/list - List all processes`);
|
|
589
|
-
console.log(` GET /api/process/{id} - Get process status`);
|
|
590
|
-
console.log(` DELETE /api/process/{id} - Kill a process`);
|
|
591
|
-
console.log(` GET /api/process/{id}/logs - Get process logs`);
|
|
592
|
-
console.log(` GET /api/process/{id}/stream - Stream process logs (SSE)`);
|
|
593
|
-
console.log(` DELETE /api/process/kill-all - Kill all processes`);
|
|
594
|
-
console.log(` GET /proxy/{port}/* - Proxy requests to exposed ports`);
|
|
595
|
-
console.log(` POST /api/contexts - Create a code execution context`);
|
|
596
|
-
console.log(` GET /api/contexts - List all contexts`);
|
|
597
|
-
console.log(` DELETE /api/contexts/{id} - Delete a context`);
|
|
598
|
-
console.log(
|
|
599
|
-
` POST /api/execute/code - Execute code in a context (streaming)`
|
|
600
|
-
);
|
|
601
|
-
console.log(` GET /api/ping - Health check`);
|