@cloudflare/sandbox 0.3.6 → 0.4.1
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 +6 -8
- package/Dockerfile +88 -18
- package/README.md +89 -824
- package/dist/{chunk-JTKON2SH.js → chunk-BCJ7SF3Q.js} +9 -5
- package/dist/chunk-BCJ7SF3Q.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-HGF554LH.js +2236 -0
- package/dist/chunk-HGF554LH.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 +35 -39
- package/dist/index.js.map +1 -1
- 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 +11 -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/{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.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
|
@@ -1,568 +0,0 @@
|
|
|
1
|
-
import type { Session, SessionManager } from "../isolation";
|
|
2
|
-
import type { ProcessRecord, ProcessStatus, StartProcessRequest } from "../types";
|
|
3
|
-
|
|
4
|
-
// Process management handlers - all processes are tracked per-session
|
|
5
|
-
|
|
6
|
-
// Helper types for process responses
|
|
7
|
-
interface ProcessInfo {
|
|
8
|
-
id: string;
|
|
9
|
-
pid?: number;
|
|
10
|
-
command: string;
|
|
11
|
-
status: ProcessStatus;
|
|
12
|
-
startTime: string;
|
|
13
|
-
endTime?: string | null;
|
|
14
|
-
exitCode?: number | null;
|
|
15
|
-
sessionId: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Helper functions to reduce repetition
|
|
19
|
-
function createErrorResponse(
|
|
20
|
-
error: string,
|
|
21
|
-
message?: string,
|
|
22
|
-
status: number = 500,
|
|
23
|
-
corsHeaders: Record<string, string> = {}
|
|
24
|
-
): Response {
|
|
25
|
-
return new Response(
|
|
26
|
-
JSON.stringify({
|
|
27
|
-
error,
|
|
28
|
-
...(message && { message })
|
|
29
|
-
}),
|
|
30
|
-
{
|
|
31
|
-
headers: {
|
|
32
|
-
"Content-Type": "application/json",
|
|
33
|
-
...corsHeaders,
|
|
34
|
-
},
|
|
35
|
-
status,
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function createSuccessResponse(
|
|
41
|
-
data: Record<string, unknown>,
|
|
42
|
-
corsHeaders: Record<string, string> = {}
|
|
43
|
-
): Response {
|
|
44
|
-
return new Response(
|
|
45
|
-
JSON.stringify(data),
|
|
46
|
-
{
|
|
47
|
-
headers: {
|
|
48
|
-
"Content-Type": "application/json",
|
|
49
|
-
...corsHeaders,
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function processRecordToInfo(
|
|
56
|
-
record: ProcessRecord,
|
|
57
|
-
sessionId: string
|
|
58
|
-
): ProcessInfo {
|
|
59
|
-
return {
|
|
60
|
-
id: record.id,
|
|
61
|
-
pid: record.pid,
|
|
62
|
-
command: record.command,
|
|
63
|
-
status: record.status,
|
|
64
|
-
startTime: record.startTime.toISOString(),
|
|
65
|
-
endTime: record.endTime ? record.endTime.toISOString() : null,
|
|
66
|
-
exitCode: record.exitCode ?? null,
|
|
67
|
-
sessionId
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function findProcessAcrossSessions(
|
|
72
|
-
processId: string,
|
|
73
|
-
sessionManager: SessionManager
|
|
74
|
-
): Promise<{ process: ProcessRecord; sessionId: string } | null> {
|
|
75
|
-
for (const sessionId of sessionManager.listSessions()) {
|
|
76
|
-
const session = sessionManager.getSession(sessionId);
|
|
77
|
-
if (session) {
|
|
78
|
-
const process = await session.getProcess(processId);
|
|
79
|
-
if (process) {
|
|
80
|
-
return { process, sessionId };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function handleStartProcessRequest(
|
|
88
|
-
req: Request,
|
|
89
|
-
corsHeaders: Record<string, string>,
|
|
90
|
-
sessionManager?: SessionManager
|
|
91
|
-
): Promise<Response> {
|
|
92
|
-
try {
|
|
93
|
-
const body = (await req.json()) as StartProcessRequest;
|
|
94
|
-
const { command, sessionId, options = {} } = body;
|
|
95
|
-
|
|
96
|
-
if (!command || typeof command !== "string") {
|
|
97
|
-
return createErrorResponse(
|
|
98
|
-
"Command is required and must be a string",
|
|
99
|
-
undefined,
|
|
100
|
-
400,
|
|
101
|
-
corsHeaders
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!sessionManager) {
|
|
106
|
-
return createErrorResponse(
|
|
107
|
-
"Session manager is required for process management",
|
|
108
|
-
undefined,
|
|
109
|
-
500,
|
|
110
|
-
corsHeaders
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
console.log(`[Server] Starting process: ${command}${sessionId ? ` in session: ${sessionId}` : ' (default session)'}`);
|
|
115
|
-
|
|
116
|
-
// Get the session (use default if not specified)
|
|
117
|
-
let session: Session;
|
|
118
|
-
|
|
119
|
-
if (sessionId) {
|
|
120
|
-
const specificSession = sessionManager.getSession(sessionId);
|
|
121
|
-
if (!specificSession) {
|
|
122
|
-
return createErrorResponse(
|
|
123
|
-
`Session '${sessionId}' not found`,
|
|
124
|
-
undefined,
|
|
125
|
-
404,
|
|
126
|
-
corsHeaders
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
session = specificSession;
|
|
130
|
-
} else {
|
|
131
|
-
// Use the centralized method to get or create default session
|
|
132
|
-
session = await sessionManager.getOrCreateDefaultSession();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const processRecord = await session.startProcess(command, options);
|
|
136
|
-
|
|
137
|
-
return createSuccessResponse({
|
|
138
|
-
process: processRecordToInfo(processRecord, sessionId || 'default')
|
|
139
|
-
}, corsHeaders);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error("[Server] Error starting process:", error);
|
|
142
|
-
return createErrorResponse(
|
|
143
|
-
"Failed to start process",
|
|
144
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
145
|
-
500,
|
|
146
|
-
corsHeaders
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export async function handleListProcessesRequest(
|
|
152
|
-
req: Request,
|
|
153
|
-
corsHeaders: Record<string, string>,
|
|
154
|
-
sessionManager?: SessionManager
|
|
155
|
-
): Promise<Response> {
|
|
156
|
-
try {
|
|
157
|
-
if (!sessionManager) {
|
|
158
|
-
return createErrorResponse(
|
|
159
|
-
"Session manager is required",
|
|
160
|
-
undefined,
|
|
161
|
-
500,
|
|
162
|
-
corsHeaders
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Get the session name from query params if provided
|
|
167
|
-
const url = new URL(req.url);
|
|
168
|
-
const sessionId = url.searchParams.get('session');
|
|
169
|
-
|
|
170
|
-
let allProcesses: ProcessInfo[] = [];
|
|
171
|
-
|
|
172
|
-
if (sessionId) {
|
|
173
|
-
// List processes from specific session
|
|
174
|
-
const session = sessionManager.getSession(sessionId);
|
|
175
|
-
if (!session) {
|
|
176
|
-
return createErrorResponse(
|
|
177
|
-
`Session '${sessionId}' not found`,
|
|
178
|
-
undefined,
|
|
179
|
-
404,
|
|
180
|
-
corsHeaders
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
const processes = await session.listProcesses();
|
|
184
|
-
allProcesses = processes.map(p => processRecordToInfo(p, sessionId));
|
|
185
|
-
} else {
|
|
186
|
-
// List processes from all sessions
|
|
187
|
-
for (const name of sessionManager.listSessions()) {
|
|
188
|
-
const session = sessionManager.getSession(name);
|
|
189
|
-
if (session) {
|
|
190
|
-
const processes = await session.listProcesses();
|
|
191
|
-
allProcesses.push(...processes.map(p => processRecordToInfo(p, name)));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return createSuccessResponse({
|
|
197
|
-
processes: allProcesses,
|
|
198
|
-
count: allProcesses.length,
|
|
199
|
-
timestamp: new Date().toISOString(),
|
|
200
|
-
}, corsHeaders);
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error("[Server] Error listing processes:", error);
|
|
203
|
-
return createErrorResponse(
|
|
204
|
-
"Failed to list processes",
|
|
205
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
206
|
-
500,
|
|
207
|
-
corsHeaders
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export async function handleGetProcessRequest(
|
|
213
|
-
req: Request,
|
|
214
|
-
corsHeaders: Record<string, string>,
|
|
215
|
-
processId: string,
|
|
216
|
-
sessionManager?: SessionManager
|
|
217
|
-
): Promise<Response> {
|
|
218
|
-
try {
|
|
219
|
-
if (!sessionManager) {
|
|
220
|
-
return createErrorResponse(
|
|
221
|
-
"Session manager is required",
|
|
222
|
-
undefined,
|
|
223
|
-
500,
|
|
224
|
-
corsHeaders
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const result = await findProcessAcrossSessions(processId, sessionManager);
|
|
229
|
-
if (!result) {
|
|
230
|
-
return createErrorResponse(
|
|
231
|
-
"Process not found",
|
|
232
|
-
processId,
|
|
233
|
-
404,
|
|
234
|
-
corsHeaders
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return createSuccessResponse({
|
|
239
|
-
process: processRecordToInfo(result.process, result.sessionId),
|
|
240
|
-
timestamp: new Date().toISOString(),
|
|
241
|
-
}, corsHeaders);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
console.error("[Server] Error getting process:", error);
|
|
244
|
-
return createErrorResponse(
|
|
245
|
-
"Failed to get process",
|
|
246
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
247
|
-
500,
|
|
248
|
-
corsHeaders
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export async function handleKillProcessRequest(
|
|
254
|
-
req: Request,
|
|
255
|
-
corsHeaders: Record<string, string>,
|
|
256
|
-
processId: string,
|
|
257
|
-
sessionManager?: SessionManager
|
|
258
|
-
): Promise<Response> {
|
|
259
|
-
try {
|
|
260
|
-
if (!sessionManager) {
|
|
261
|
-
return createErrorResponse(
|
|
262
|
-
"Session manager is required",
|
|
263
|
-
undefined,
|
|
264
|
-
500,
|
|
265
|
-
corsHeaders
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Search for and kill the process across all sessions
|
|
270
|
-
for (const sessionId of sessionManager.listSessions()) {
|
|
271
|
-
const session = sessionManager.getSession(sessionId);
|
|
272
|
-
if (session) {
|
|
273
|
-
const process = await session.getProcess(processId);
|
|
274
|
-
if (process) {
|
|
275
|
-
const killed = await session.killProcess(processId);
|
|
276
|
-
return createSuccessResponse({
|
|
277
|
-
success: killed,
|
|
278
|
-
processId,
|
|
279
|
-
sessionId,
|
|
280
|
-
message: killed ? `Process ${processId} killed` : `Failed to kill process ${processId}`,
|
|
281
|
-
timestamp: new Date().toISOString(),
|
|
282
|
-
}, corsHeaders);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return createErrorResponse(
|
|
288
|
-
"Process not found",
|
|
289
|
-
processId,
|
|
290
|
-
404,
|
|
291
|
-
corsHeaders
|
|
292
|
-
);
|
|
293
|
-
} catch (error) {
|
|
294
|
-
console.error("[Server] Error killing process:", error);
|
|
295
|
-
return createErrorResponse(
|
|
296
|
-
"Failed to kill process",
|
|
297
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
298
|
-
500,
|
|
299
|
-
corsHeaders
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export async function handleKillAllProcessesRequest(
|
|
305
|
-
req: Request,
|
|
306
|
-
corsHeaders: Record<string, string>,
|
|
307
|
-
sessionManager?: SessionManager
|
|
308
|
-
): Promise<Response> {
|
|
309
|
-
try {
|
|
310
|
-
if (!sessionManager) {
|
|
311
|
-
return createErrorResponse(
|
|
312
|
-
"Session manager is required",
|
|
313
|
-
undefined,
|
|
314
|
-
500,
|
|
315
|
-
corsHeaders
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Get the session name from query params if provided
|
|
320
|
-
const url = new URL(req.url);
|
|
321
|
-
const sessionId = url.searchParams.get('session');
|
|
322
|
-
|
|
323
|
-
let killedCount = 0;
|
|
324
|
-
|
|
325
|
-
if (sessionId) {
|
|
326
|
-
// Kill processes in specific session
|
|
327
|
-
const session = sessionManager.getSession(sessionId);
|
|
328
|
-
if (!session) {
|
|
329
|
-
return createErrorResponse(
|
|
330
|
-
`Session '${sessionId}' not found`,
|
|
331
|
-
undefined,
|
|
332
|
-
404,
|
|
333
|
-
corsHeaders
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
killedCount = await session.killAllProcesses();
|
|
337
|
-
} else {
|
|
338
|
-
// Kill processes in all sessions
|
|
339
|
-
for (const name of sessionManager.listSessions()) {
|
|
340
|
-
const session = sessionManager.getSession(name);
|
|
341
|
-
if (session) {
|
|
342
|
-
killedCount += await session.killAllProcesses();
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return createSuccessResponse({
|
|
348
|
-
success: true,
|
|
349
|
-
killedCount,
|
|
350
|
-
message: `Killed ${killedCount} process${killedCount !== 1 ? 'es' : ''}`,
|
|
351
|
-
timestamp: new Date().toISOString(),
|
|
352
|
-
}, corsHeaders);
|
|
353
|
-
} catch (error) {
|
|
354
|
-
console.error("[Server] Error killing all processes:", error);
|
|
355
|
-
return createErrorResponse(
|
|
356
|
-
"Failed to kill all processes",
|
|
357
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
358
|
-
500,
|
|
359
|
-
corsHeaders
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
export async function handleGetProcessLogsRequest(
|
|
365
|
-
req: Request,
|
|
366
|
-
corsHeaders: Record<string, string>,
|
|
367
|
-
processId: string,
|
|
368
|
-
sessionManager?: SessionManager
|
|
369
|
-
): Promise<Response> {
|
|
370
|
-
try {
|
|
371
|
-
if (!sessionManager) {
|
|
372
|
-
return createErrorResponse(
|
|
373
|
-
"Session manager is required",
|
|
374
|
-
undefined,
|
|
375
|
-
500,
|
|
376
|
-
corsHeaders
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const result = await findProcessAcrossSessions(processId, sessionManager);
|
|
381
|
-
if (!result) {
|
|
382
|
-
return createErrorResponse(
|
|
383
|
-
"Process not found",
|
|
384
|
-
processId,
|
|
385
|
-
404,
|
|
386
|
-
corsHeaders
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Get the session and use its getProcessLogs method to ensure logs are updated from files
|
|
391
|
-
const session = sessionManager.getSession(result.sessionId);
|
|
392
|
-
if (!session) {
|
|
393
|
-
return createErrorResponse(
|
|
394
|
-
"Session not found",
|
|
395
|
-
result.sessionId,
|
|
396
|
-
500,
|
|
397
|
-
corsHeaders
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// This will update logs from temp files before returning
|
|
402
|
-
const logs = await session.getProcessLogs(processId);
|
|
403
|
-
|
|
404
|
-
return createSuccessResponse({
|
|
405
|
-
stdout: logs.stdout,
|
|
406
|
-
stderr: logs.stderr,
|
|
407
|
-
processId,
|
|
408
|
-
sessionId: result.sessionId,
|
|
409
|
-
timestamp: new Date().toISOString(),
|
|
410
|
-
}, corsHeaders);
|
|
411
|
-
} catch (error) {
|
|
412
|
-
console.error("[Server] Error getting process logs:", error);
|
|
413
|
-
return createErrorResponse(
|
|
414
|
-
"Failed to get process logs",
|
|
415
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
416
|
-
500,
|
|
417
|
-
corsHeaders
|
|
418
|
-
);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
export async function handleStreamProcessLogsRequest(
|
|
423
|
-
req: Request,
|
|
424
|
-
corsHeaders: Record<string, string>,
|
|
425
|
-
processId: string,
|
|
426
|
-
sessionManager?: SessionManager
|
|
427
|
-
): Promise<Response> {
|
|
428
|
-
try {
|
|
429
|
-
if (!sessionManager) {
|
|
430
|
-
return createErrorResponse(
|
|
431
|
-
"Session manager is required",
|
|
432
|
-
undefined,
|
|
433
|
-
500,
|
|
434
|
-
corsHeaders
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const result = await findProcessAcrossSessions(processId, sessionManager);
|
|
439
|
-
if (!result) {
|
|
440
|
-
return createErrorResponse(
|
|
441
|
-
"Process not found",
|
|
442
|
-
processId,
|
|
443
|
-
404,
|
|
444
|
-
corsHeaders
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const { process: targetProcess, sessionId } = result;
|
|
449
|
-
|
|
450
|
-
// Get the session to start monitoring
|
|
451
|
-
const session = sessionManager.getSession(sessionId);
|
|
452
|
-
if (!session) {
|
|
453
|
-
return createErrorResponse(
|
|
454
|
-
"Session not found",
|
|
455
|
-
sessionId,
|
|
456
|
-
404,
|
|
457
|
-
corsHeaders
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// Store listeners outside the stream for proper cleanup
|
|
462
|
-
let outputListener: ((stream: 'stdout' | 'stderr', data: string) => void) | null = null;
|
|
463
|
-
let statusListener: ((status: ProcessStatus) => void) | null = null;
|
|
464
|
-
|
|
465
|
-
// Create a stream that sends updates
|
|
466
|
-
const stream = new ReadableStream({
|
|
467
|
-
start(controller) {
|
|
468
|
-
// Send initial logs
|
|
469
|
-
if (targetProcess.stdout) {
|
|
470
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({
|
|
471
|
-
type: 'stdout',
|
|
472
|
-
data: targetProcess.stdout,
|
|
473
|
-
processId,
|
|
474
|
-
sessionId,
|
|
475
|
-
timestamp: new Date().toISOString()
|
|
476
|
-
})}\n\n`));
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (targetProcess.stderr) {
|
|
480
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({
|
|
481
|
-
type: 'stderr',
|
|
482
|
-
data: targetProcess.stderr,
|
|
483
|
-
processId,
|
|
484
|
-
sessionId,
|
|
485
|
-
timestamp: new Date().toISOString()
|
|
486
|
-
})}\n\n`));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// If process is complete, send completion and close
|
|
490
|
-
if (targetProcess.status === 'completed' || targetProcess.status === 'failed' || targetProcess.status === 'killed') {
|
|
491
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({
|
|
492
|
-
type: 'complete',
|
|
493
|
-
status: targetProcess.status,
|
|
494
|
-
exitCode: targetProcess.exitCode,
|
|
495
|
-
processId,
|
|
496
|
-
sessionId,
|
|
497
|
-
timestamp: new Date().toISOString()
|
|
498
|
-
})}\n\n`));
|
|
499
|
-
controller.close();
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Set up listeners for live updates
|
|
504
|
-
outputListener = (stream: 'stdout' | 'stderr', data: string) => {
|
|
505
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({
|
|
506
|
-
type: stream,
|
|
507
|
-
data,
|
|
508
|
-
processId,
|
|
509
|
-
sessionId,
|
|
510
|
-
timestamp: new Date().toISOString()
|
|
511
|
-
})}\n\n`));
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
statusListener = (status: ProcessStatus) => {
|
|
515
|
-
if (status === 'completed' || status === 'failed' || status === 'killed') {
|
|
516
|
-
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({
|
|
517
|
-
type: 'complete',
|
|
518
|
-
status,
|
|
519
|
-
exitCode: targetProcess.exitCode,
|
|
520
|
-
processId,
|
|
521
|
-
sessionId,
|
|
522
|
-
timestamp: new Date().toISOString()
|
|
523
|
-
})}\n\n`));
|
|
524
|
-
controller.close();
|
|
525
|
-
}
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
targetProcess.outputListeners.add(outputListener);
|
|
529
|
-
targetProcess.statusListeners.add(statusListener);
|
|
530
|
-
|
|
531
|
-
// Start monitoring the process for output changes
|
|
532
|
-
session.startProcessMonitoring(targetProcess);
|
|
533
|
-
},
|
|
534
|
-
cancel() {
|
|
535
|
-
// Clean up when stream is closed (client disconnects)
|
|
536
|
-
// Remove only this stream's listeners, not all listeners
|
|
537
|
-
if (outputListener) {
|
|
538
|
-
targetProcess.outputListeners.delete(outputListener);
|
|
539
|
-
}
|
|
540
|
-
if (statusListener) {
|
|
541
|
-
targetProcess.statusListeners.delete(statusListener);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Stop monitoring if no more listeners
|
|
545
|
-
if (targetProcess.outputListeners.size === 0) {
|
|
546
|
-
session.stopProcessMonitoring(targetProcess);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
return new Response(stream, {
|
|
552
|
-
headers: {
|
|
553
|
-
"Content-Type": "text/event-stream",
|
|
554
|
-
"Cache-Control": "no-cache",
|
|
555
|
-
"Connection": "keep-alive",
|
|
556
|
-
...corsHeaders,
|
|
557
|
-
},
|
|
558
|
-
});
|
|
559
|
-
} catch (error) {
|
|
560
|
-
console.error("[Server] Error streaming process logs:", error);
|
|
561
|
-
return createErrorResponse(
|
|
562
|
-
"Failed to stream process logs",
|
|
563
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
564
|
-
500,
|
|
565
|
-
corsHeaders
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
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
|
-
}
|