@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
package/src/client.ts
CHANGED
|
@@ -2,21 +2,22 @@ import type { ExecuteRequest } from "../container_src/types";
|
|
|
2
2
|
import type { Sandbox } from "./index";
|
|
3
3
|
import type {
|
|
4
4
|
BaseExecOptions,
|
|
5
|
+
DeleteFileResponse,
|
|
6
|
+
ExecuteResponse,
|
|
5
7
|
GetProcessLogsResponse,
|
|
6
8
|
GetProcessResponse,
|
|
9
|
+
GitCheckoutResponse,
|
|
10
|
+
ListFilesResponse,
|
|
7
11
|
ListProcessesResponse,
|
|
12
|
+
MkdirResponse,
|
|
13
|
+
MoveFileResponse,
|
|
14
|
+
ReadFileResponse,
|
|
15
|
+
RenameFileResponse,
|
|
8
16
|
StartProcessRequest,
|
|
9
17
|
StartProcessResponse,
|
|
18
|
+
WriteFileResponse,
|
|
10
19
|
} from "./types";
|
|
11
20
|
|
|
12
|
-
export interface ExecuteResponse {
|
|
13
|
-
success: boolean;
|
|
14
|
-
stdout: string;
|
|
15
|
-
stderr: string;
|
|
16
|
-
exitCode: number;
|
|
17
|
-
command: string;
|
|
18
|
-
timestamp: string;
|
|
19
|
-
}
|
|
20
21
|
|
|
21
22
|
interface CommandsResponse {
|
|
22
23
|
availableCommands: string[];
|
|
@@ -27,104 +28,62 @@ interface GitCheckoutRequest {
|
|
|
27
28
|
repoUrl: string;
|
|
28
29
|
branch?: string;
|
|
29
30
|
targetDir?: string;
|
|
30
|
-
sessionId
|
|
31
|
+
sessionId: string;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
export interface GitCheckoutResponse {
|
|
34
|
-
success: boolean;
|
|
35
|
-
stdout: string;
|
|
36
|
-
stderr: string;
|
|
37
|
-
exitCode: number;
|
|
38
|
-
repoUrl: string;
|
|
39
|
-
branch: string;
|
|
40
|
-
targetDir: string;
|
|
41
|
-
timestamp: string;
|
|
42
|
-
}
|
|
43
34
|
|
|
44
35
|
interface MkdirRequest {
|
|
45
36
|
path: string;
|
|
46
37
|
recursive?: boolean;
|
|
47
|
-
sessionId
|
|
38
|
+
sessionId: string;
|
|
48
39
|
}
|
|
49
40
|
|
|
50
|
-
export interface MkdirResponse {
|
|
51
|
-
success: boolean;
|
|
52
|
-
stdout: string;
|
|
53
|
-
stderr: string;
|
|
54
|
-
exitCode: number;
|
|
55
|
-
path: string;
|
|
56
|
-
recursive: boolean;
|
|
57
|
-
timestamp: string;
|
|
58
|
-
}
|
|
59
41
|
|
|
60
42
|
interface WriteFileRequest {
|
|
61
43
|
path: string;
|
|
62
44
|
content: string;
|
|
63
45
|
encoding?: string;
|
|
64
|
-
sessionId
|
|
46
|
+
sessionId: string;
|
|
65
47
|
}
|
|
66
48
|
|
|
67
|
-
export interface WriteFileResponse {
|
|
68
|
-
success: boolean;
|
|
69
|
-
exitCode: number;
|
|
70
|
-
path: string;
|
|
71
|
-
timestamp: string;
|
|
72
|
-
}
|
|
73
49
|
|
|
74
50
|
interface ReadFileRequest {
|
|
75
51
|
path: string;
|
|
76
52
|
encoding?: string;
|
|
77
|
-
sessionId
|
|
53
|
+
sessionId: string;
|
|
78
54
|
}
|
|
79
55
|
|
|
80
|
-
export interface ReadFileResponse {
|
|
81
|
-
success: boolean;
|
|
82
|
-
exitCode: number;
|
|
83
|
-
path: string;
|
|
84
|
-
content: string;
|
|
85
|
-
timestamp: string;
|
|
86
|
-
}
|
|
87
56
|
|
|
88
57
|
interface DeleteFileRequest {
|
|
89
58
|
path: string;
|
|
90
|
-
sessionId
|
|
59
|
+
sessionId: string;
|
|
91
60
|
}
|
|
92
61
|
|
|
93
|
-
export interface DeleteFileResponse {
|
|
94
|
-
success: boolean;
|
|
95
|
-
exitCode: number;
|
|
96
|
-
path: string;
|
|
97
|
-
timestamp: string;
|
|
98
|
-
}
|
|
99
62
|
|
|
100
63
|
interface RenameFileRequest {
|
|
101
64
|
oldPath: string;
|
|
102
65
|
newPath: string;
|
|
103
|
-
sessionId
|
|
66
|
+
sessionId: string;
|
|
104
67
|
}
|
|
105
68
|
|
|
106
|
-
export interface RenameFileResponse {
|
|
107
|
-
success: boolean;
|
|
108
|
-
exitCode: number;
|
|
109
|
-
oldPath: string;
|
|
110
|
-
newPath: string;
|
|
111
|
-
timestamp: string;
|
|
112
|
-
}
|
|
113
69
|
|
|
114
70
|
interface MoveFileRequest {
|
|
115
71
|
sourcePath: string;
|
|
116
72
|
destinationPath: string;
|
|
117
|
-
sessionId
|
|
73
|
+
sessionId: string;
|
|
118
74
|
}
|
|
119
75
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
76
|
+
|
|
77
|
+
interface ListFilesRequest {
|
|
78
|
+
path: string;
|
|
79
|
+
options?: {
|
|
80
|
+
recursive?: boolean;
|
|
81
|
+
includeHidden?: boolean;
|
|
82
|
+
};
|
|
83
|
+
sessionId: string;
|
|
126
84
|
}
|
|
127
85
|
|
|
86
|
+
|
|
128
87
|
interface PreviewInfo {
|
|
129
88
|
url: string;
|
|
130
89
|
port: number;
|
|
@@ -184,7 +143,6 @@ interface HttpClientOptions {
|
|
|
184
143
|
export class HttpClient {
|
|
185
144
|
private baseUrl: string;
|
|
186
145
|
private options: HttpClientOptions;
|
|
187
|
-
private sessionId: string | null = null;
|
|
188
146
|
|
|
189
147
|
constructor(options: HttpClientOptions = {}) {
|
|
190
148
|
this.options = {
|
|
@@ -234,25 +192,52 @@ export class HttpClient {
|
|
|
234
192
|
}
|
|
235
193
|
}
|
|
236
194
|
|
|
237
|
-
async
|
|
195
|
+
async createSession(options: {
|
|
196
|
+
id: string;
|
|
197
|
+
env?: Record<string, string>;
|
|
198
|
+
cwd?: string;
|
|
199
|
+
isolation?: boolean;
|
|
200
|
+
}): Promise<{ success: boolean; id: string; message: string }> {
|
|
201
|
+
try {
|
|
202
|
+
const response = await this.doFetch(`/api/session/create`, {
|
|
203
|
+
method: "POST",
|
|
204
|
+
headers: {
|
|
205
|
+
"Content-Type": "application/json",
|
|
206
|
+
},
|
|
207
|
+
body: JSON.stringify(options),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
212
|
+
error?: string;
|
|
213
|
+
};
|
|
214
|
+
throw new Error(
|
|
215
|
+
errorData.error || `Failed to create session: ${response.status}`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const data = await response.json() as { success: boolean; id: string; message: string };
|
|
220
|
+
console.log(`[HTTP Client] Session created: ${options.id}`);
|
|
221
|
+
return data;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error("[HTTP Client] Error creating session:", error);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async exec(
|
|
229
|
+
sessionId: string,
|
|
238
230
|
command: string,
|
|
239
|
-
options
|
|
231
|
+
options?: Pick<BaseExecOptions, "cwd" | "env">
|
|
240
232
|
): Promise<ExecuteResponse> {
|
|
241
233
|
try {
|
|
242
|
-
|
|
243
|
-
const executeRequest = {
|
|
244
|
-
command,
|
|
245
|
-
sessionId: targetSessionId,
|
|
246
|
-
cwd: options.cwd,
|
|
247
|
-
env: options.env,
|
|
248
|
-
} satisfies ExecuteRequest;
|
|
249
|
-
|
|
234
|
+
// Always use session-specific endpoint
|
|
250
235
|
const response = await this.doFetch(`/api/execute`, {
|
|
251
|
-
|
|
236
|
+
method: "POST",
|
|
252
237
|
headers: {
|
|
253
238
|
"Content-Type": "application/json",
|
|
254
239
|
},
|
|
255
|
-
|
|
240
|
+
body: JSON.stringify({ id: sessionId, command }),
|
|
256
241
|
});
|
|
257
242
|
|
|
258
243
|
if (!response.ok) {
|
|
@@ -260,27 +245,34 @@ export class HttpClient {
|
|
|
260
245
|
error?: string;
|
|
261
246
|
};
|
|
262
247
|
throw new Error(
|
|
263
|
-
errorData.error || `
|
|
248
|
+
errorData.error || `Failed to execute in session: ${response.status}`
|
|
264
249
|
);
|
|
265
250
|
}
|
|
266
251
|
|
|
267
|
-
const data
|
|
252
|
+
const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };
|
|
268
253
|
console.log(
|
|
269
|
-
`[HTTP Client] Command executed
|
|
254
|
+
`[HTTP Client] Command executed in session ${sessionId}: ${command}`
|
|
270
255
|
);
|
|
256
|
+
|
|
257
|
+
// Convert to ExecuteResponse format for consistency
|
|
258
|
+
const executeResponse: ExecuteResponse = {
|
|
259
|
+
...data,
|
|
260
|
+
command,
|
|
261
|
+
timestamp: new Date().toISOString()
|
|
262
|
+
};
|
|
271
263
|
|
|
272
264
|
// Call the callback if provided
|
|
273
265
|
this.options.onCommandComplete?.(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
266
|
+
executeResponse.success,
|
|
267
|
+
executeResponse.exitCode,
|
|
268
|
+
executeResponse.stdout,
|
|
269
|
+
executeResponse.stderr,
|
|
270
|
+
executeResponse.command
|
|
279
271
|
);
|
|
280
272
|
|
|
281
|
-
return
|
|
273
|
+
return executeResponse;
|
|
282
274
|
} catch (error) {
|
|
283
|
-
console.error("[HTTP Client] Error executing
|
|
275
|
+
console.error("[HTTP Client] Error executing in session:", error);
|
|
284
276
|
this.options.onError?.(
|
|
285
277
|
error instanceof Error ? error.message : "Unknown error",
|
|
286
278
|
command
|
|
@@ -289,23 +281,21 @@ export class HttpClient {
|
|
|
289
281
|
}
|
|
290
282
|
}
|
|
291
283
|
|
|
292
|
-
async
|
|
293
|
-
|
|
294
|
-
|
|
284
|
+
async execStream(
|
|
285
|
+
sessionId: string,
|
|
286
|
+
command: string
|
|
295
287
|
): Promise<ReadableStream<Uint8Array>> {
|
|
296
288
|
try {
|
|
297
|
-
|
|
298
|
-
|
|
289
|
+
// Always use session-specific streaming endpoint
|
|
299
290
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
300
|
-
|
|
301
|
-
command,
|
|
302
|
-
sessionId: targetSessionId,
|
|
303
|
-
}),
|
|
291
|
+
method: "POST",
|
|
304
292
|
headers: {
|
|
305
293
|
"Content-Type": "application/json",
|
|
306
|
-
Accept: "text/event-stream",
|
|
307
294
|
},
|
|
308
|
-
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
id: sessionId,
|
|
297
|
+
command
|
|
298
|
+
}),
|
|
309
299
|
});
|
|
310
300
|
|
|
311
301
|
if (!response.ok) {
|
|
@@ -313,38 +303,37 @@ export class HttpClient {
|
|
|
313
303
|
error?: string;
|
|
314
304
|
};
|
|
315
305
|
throw new Error(
|
|
316
|
-
errorData.error || `
|
|
306
|
+
errorData.error || `Failed to stream execute in session: ${response.status}`
|
|
317
307
|
);
|
|
318
308
|
}
|
|
319
309
|
|
|
320
310
|
if (!response.body) {
|
|
321
|
-
throw new Error("No response body for streaming
|
|
311
|
+
throw new Error("No response body for streaming execution");
|
|
322
312
|
}
|
|
323
313
|
|
|
324
|
-
console.log(
|
|
325
|
-
|
|
314
|
+
console.log(
|
|
315
|
+
`[HTTP Client] Started streaming command in session ${sessionId}: ${command}`
|
|
316
|
+
);
|
|
326
317
|
return response.body;
|
|
327
318
|
} catch (error) {
|
|
328
|
-
console.error("[HTTP Client] Error in
|
|
319
|
+
console.error("[HTTP Client] Error streaming execute in session:", error);
|
|
329
320
|
throw error;
|
|
330
321
|
}
|
|
331
322
|
}
|
|
332
323
|
|
|
333
324
|
async gitCheckout(
|
|
334
325
|
repoUrl: string,
|
|
326
|
+
sessionId: string,
|
|
335
327
|
branch: string = "main",
|
|
336
|
-
targetDir?: string
|
|
337
|
-
sessionId?: string
|
|
328
|
+
targetDir?: string
|
|
338
329
|
): Promise<GitCheckoutResponse> {
|
|
339
330
|
try {
|
|
340
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
341
|
-
|
|
342
331
|
const response = await this.doFetch(`/api/git/checkout`, {
|
|
343
332
|
body: JSON.stringify({
|
|
344
333
|
branch,
|
|
345
334
|
repoUrl,
|
|
346
|
-
sessionId: targetSessionId,
|
|
347
335
|
targetDir,
|
|
336
|
+
sessionId,
|
|
348
337
|
} as GitCheckoutRequest),
|
|
349
338
|
headers: {
|
|
350
339
|
"Content-Type": "application/json",
|
|
@@ -376,16 +365,14 @@ export class HttpClient {
|
|
|
376
365
|
async mkdir(
|
|
377
366
|
path: string,
|
|
378
367
|
recursive: boolean = false,
|
|
379
|
-
sessionId
|
|
368
|
+
sessionId: string
|
|
380
369
|
): Promise<MkdirResponse> {
|
|
381
370
|
try {
|
|
382
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
383
|
-
|
|
384
371
|
const response = await this.doFetch(`/api/mkdir`, {
|
|
385
372
|
body: JSON.stringify({
|
|
386
373
|
path,
|
|
387
374
|
recursive,
|
|
388
|
-
sessionId
|
|
375
|
+
sessionId,
|
|
389
376
|
} as MkdirRequest),
|
|
390
377
|
headers: {
|
|
391
378
|
"Content-Type": "application/json",
|
|
@@ -404,7 +391,7 @@ export class HttpClient {
|
|
|
404
391
|
|
|
405
392
|
const data: MkdirResponse = await response.json();
|
|
406
393
|
console.log(
|
|
407
|
-
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
394
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
408
395
|
);
|
|
409
396
|
|
|
410
397
|
return data;
|
|
@@ -418,17 +405,15 @@ export class HttpClient {
|
|
|
418
405
|
path: string,
|
|
419
406
|
content: string,
|
|
420
407
|
encoding: string = "utf-8",
|
|
421
|
-
sessionId
|
|
408
|
+
sessionId: string
|
|
422
409
|
): Promise<WriteFileResponse> {
|
|
423
410
|
try {
|
|
424
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
425
|
-
|
|
426
411
|
const response = await this.doFetch(`/api/write`, {
|
|
427
412
|
body: JSON.stringify({
|
|
428
413
|
content,
|
|
429
414
|
encoding,
|
|
430
415
|
path,
|
|
431
|
-
sessionId
|
|
416
|
+
sessionId,
|
|
432
417
|
} as WriteFileRequest),
|
|
433
418
|
headers: {
|
|
434
419
|
"Content-Type": "application/json",
|
|
@@ -447,7 +432,7 @@ export class HttpClient {
|
|
|
447
432
|
|
|
448
433
|
const data: WriteFileResponse = await response.json();
|
|
449
434
|
console.log(
|
|
450
|
-
`[HTTP Client] File written: ${path}, Success: ${data.success}`
|
|
435
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
451
436
|
);
|
|
452
437
|
|
|
453
438
|
return data;
|
|
@@ -460,16 +445,14 @@ export class HttpClient {
|
|
|
460
445
|
async readFile(
|
|
461
446
|
path: string,
|
|
462
447
|
encoding: string = "utf-8",
|
|
463
|
-
sessionId
|
|
448
|
+
sessionId: string
|
|
464
449
|
): Promise<ReadFileResponse> {
|
|
465
450
|
try {
|
|
466
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
467
|
-
|
|
468
451
|
const response = await this.doFetch(`/api/read`, {
|
|
469
452
|
body: JSON.stringify({
|
|
470
453
|
encoding,
|
|
471
454
|
path,
|
|
472
|
-
sessionId
|
|
455
|
+
sessionId,
|
|
473
456
|
} as ReadFileRequest),
|
|
474
457
|
headers: {
|
|
475
458
|
"Content-Type": "application/json",
|
|
@@ -488,7 +471,7 @@ export class HttpClient {
|
|
|
488
471
|
|
|
489
472
|
const data: ReadFileResponse = await response.json();
|
|
490
473
|
console.log(
|
|
491
|
-
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
|
|
474
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
492
475
|
);
|
|
493
476
|
|
|
494
477
|
return data;
|
|
@@ -498,17 +481,54 @@ export class HttpClient {
|
|
|
498
481
|
}
|
|
499
482
|
}
|
|
500
483
|
|
|
484
|
+
async readFileStream(
|
|
485
|
+
path: string,
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
488
|
+
try {
|
|
489
|
+
const response = await this.doFetch(`/api/read/stream`, {
|
|
490
|
+
method: "POST",
|
|
491
|
+
headers: {
|
|
492
|
+
"Content-Type": "application/json",
|
|
493
|
+
},
|
|
494
|
+
body: JSON.stringify({
|
|
495
|
+
path,
|
|
496
|
+
sessionId,
|
|
497
|
+
} as ReadFileRequest),
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
502
|
+
error?: string;
|
|
503
|
+
};
|
|
504
|
+
throw new Error(
|
|
505
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!response.body) {
|
|
510
|
+
throw new Error("No response body for file streaming");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
console.log(
|
|
514
|
+
`[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
515
|
+
);
|
|
516
|
+
return response.body;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error("[HTTP Client] Error streaming file:", error);
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
501
523
|
async deleteFile(
|
|
502
524
|
path: string,
|
|
503
|
-
sessionId
|
|
525
|
+
sessionId: string
|
|
504
526
|
): Promise<DeleteFileResponse> {
|
|
505
527
|
try {
|
|
506
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
507
|
-
|
|
508
528
|
const response = await this.doFetch(`/api/delete`, {
|
|
509
529
|
body: JSON.stringify({
|
|
510
530
|
path,
|
|
511
|
-
sessionId
|
|
531
|
+
sessionId,
|
|
512
532
|
} as DeleteFileRequest),
|
|
513
533
|
headers: {
|
|
514
534
|
"Content-Type": "application/json",
|
|
@@ -527,7 +547,7 @@ export class HttpClient {
|
|
|
527
547
|
|
|
528
548
|
const data: DeleteFileResponse = await response.json();
|
|
529
549
|
console.log(
|
|
530
|
-
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
550
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
531
551
|
);
|
|
532
552
|
|
|
533
553
|
return data;
|
|
@@ -540,16 +560,14 @@ export class HttpClient {
|
|
|
540
560
|
async renameFile(
|
|
541
561
|
oldPath: string,
|
|
542
562
|
newPath: string,
|
|
543
|
-
sessionId
|
|
563
|
+
sessionId: string
|
|
544
564
|
): Promise<RenameFileResponse> {
|
|
545
565
|
try {
|
|
546
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
547
|
-
|
|
548
566
|
const response = await this.doFetch(`/api/rename`, {
|
|
549
567
|
body: JSON.stringify({
|
|
550
568
|
newPath,
|
|
551
569
|
oldPath,
|
|
552
|
-
sessionId
|
|
570
|
+
sessionId,
|
|
553
571
|
} as RenameFileRequest),
|
|
554
572
|
headers: {
|
|
555
573
|
"Content-Type": "application/json",
|
|
@@ -568,7 +586,7 @@ export class HttpClient {
|
|
|
568
586
|
|
|
569
587
|
const data: RenameFileResponse = await response.json();
|
|
570
588
|
console.log(
|
|
571
|
-
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
589
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
572
590
|
);
|
|
573
591
|
|
|
574
592
|
return data;
|
|
@@ -581,16 +599,14 @@ export class HttpClient {
|
|
|
581
599
|
async moveFile(
|
|
582
600
|
sourcePath: string,
|
|
583
601
|
destinationPath: string,
|
|
584
|
-
sessionId
|
|
602
|
+
sessionId: string
|
|
585
603
|
): Promise<MoveFileResponse> {
|
|
586
604
|
try {
|
|
587
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
588
|
-
|
|
589
605
|
const response = await this.doFetch(`/api/move`, {
|
|
590
606
|
body: JSON.stringify({
|
|
591
607
|
destinationPath,
|
|
592
|
-
sessionId: targetSessionId,
|
|
593
608
|
sourcePath,
|
|
609
|
+
sessionId,
|
|
594
610
|
} as MoveFileRequest),
|
|
595
611
|
headers: {
|
|
596
612
|
"Content-Type": "application/json",
|
|
@@ -609,7 +625,7 @@ export class HttpClient {
|
|
|
609
625
|
|
|
610
626
|
const data: MoveFileResponse = await response.json();
|
|
611
627
|
console.log(
|
|
612
|
-
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
628
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
613
629
|
);
|
|
614
630
|
|
|
615
631
|
return data;
|
|
@@ -619,6 +635,48 @@ export class HttpClient {
|
|
|
619
635
|
}
|
|
620
636
|
}
|
|
621
637
|
|
|
638
|
+
async listFiles(
|
|
639
|
+
path: string,
|
|
640
|
+
sessionId: string,
|
|
641
|
+
options?: {
|
|
642
|
+
recursive?: boolean;
|
|
643
|
+
includeHidden?: boolean;
|
|
644
|
+
}
|
|
645
|
+
): Promise<ListFilesResponse> {
|
|
646
|
+
try {
|
|
647
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
648
|
+
body: JSON.stringify({
|
|
649
|
+
path,
|
|
650
|
+
options,
|
|
651
|
+
sessionId,
|
|
652
|
+
} as ListFilesRequest),
|
|
653
|
+
headers: {
|
|
654
|
+
"Content-Type": "application/json",
|
|
655
|
+
},
|
|
656
|
+
method: "POST",
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
if (!response.ok) {
|
|
660
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
661
|
+
error?: string;
|
|
662
|
+
};
|
|
663
|
+
throw new Error(
|
|
664
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const data: ListFilesResponse = await response.json();
|
|
669
|
+
console.log(
|
|
670
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
return data;
|
|
674
|
+
} catch (error) {
|
|
675
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
622
680
|
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
623
681
|
try {
|
|
624
682
|
const response = await this.doFetch(`/api/expose-port`, {
|
|
@@ -739,48 +797,13 @@ export class HttpClient {
|
|
|
739
797
|
}
|
|
740
798
|
}
|
|
741
799
|
|
|
742
|
-
async getCommands(): Promise<string[]> {
|
|
743
|
-
try {
|
|
744
|
-
const response = await fetch(`${this.baseUrl}/api/commands`, {
|
|
745
|
-
headers: {
|
|
746
|
-
"Content-Type": "application/json",
|
|
747
|
-
},
|
|
748
|
-
method: "GET",
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
if (!response.ok) {
|
|
752
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
const data: CommandsResponse = await response.json();
|
|
756
|
-
console.log(
|
|
757
|
-
`[HTTP Client] Available commands: ${data.availableCommands.length}`
|
|
758
|
-
);
|
|
759
|
-
return data.availableCommands;
|
|
760
|
-
} catch (error) {
|
|
761
|
-
console.error("[HTTP Client] Error getting commands:", error);
|
|
762
|
-
throw error;
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
getSessionId(): string | null {
|
|
767
|
-
return this.sessionId;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
setSessionId(sessionId: string): void {
|
|
771
|
-
this.sessionId = sessionId;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
clearSession(): void {
|
|
775
|
-
this.sessionId = null;
|
|
776
|
-
}
|
|
777
800
|
|
|
778
801
|
// Process management methods
|
|
779
802
|
async startProcess(
|
|
780
803
|
command: string,
|
|
804
|
+
sessionId: string,
|
|
781
805
|
options?: {
|
|
782
806
|
processId?: string;
|
|
783
|
-
sessionId?: string;
|
|
784
807
|
timeout?: number;
|
|
785
808
|
env?: Record<string, string>;
|
|
786
809
|
cwd?: string;
|
|
@@ -789,15 +812,11 @@ export class HttpClient {
|
|
|
789
812
|
}
|
|
790
813
|
): Promise<StartProcessResponse> {
|
|
791
814
|
try {
|
|
792
|
-
const targetSessionId = options?.sessionId || this.sessionId;
|
|
793
|
-
|
|
794
815
|
const response = await this.doFetch("/api/process/start", {
|
|
795
816
|
body: JSON.stringify({
|
|
796
817
|
command,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
sessionId: targetSessionId,
|
|
800
|
-
},
|
|
818
|
+
sessionId,
|
|
819
|
+
options,
|
|
801
820
|
} as StartProcessRequest),
|
|
802
821
|
headers: {
|
|
803
822
|
"Content-Type": "application/json",
|
|
@@ -826,9 +845,12 @@ export class HttpClient {
|
|
|
826
845
|
}
|
|
827
846
|
}
|
|
828
847
|
|
|
829
|
-
async listProcesses(): Promise<ListProcessesResponse> {
|
|
848
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
830
849
|
try {
|
|
831
|
-
const
|
|
850
|
+
const url = sessionId
|
|
851
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
852
|
+
: "/api/process/list";
|
|
853
|
+
const response = await this.doFetch(url, {
|
|
832
854
|
headers: {
|
|
833
855
|
"Content-Type": "application/json",
|
|
834
856
|
},
|
|
@@ -919,13 +941,16 @@ export class HttpClient {
|
|
|
919
941
|
}
|
|
920
942
|
}
|
|
921
943
|
|
|
922
|
-
async killAllProcesses(): Promise<{
|
|
944
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
923
945
|
success: boolean;
|
|
924
946
|
killedCount: number;
|
|
925
947
|
message: string;
|
|
926
948
|
}> {
|
|
927
949
|
try {
|
|
928
|
-
const
|
|
950
|
+
const url = sessionId
|
|
951
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
952
|
+
: "/api/process/kill-all";
|
|
953
|
+
const response = await this.doFetch(url, {
|
|
929
954
|
headers: {
|
|
930
955
|
"Content-Type": "application/json",
|
|
931
956
|
},
|
|
@@ -984,7 +1009,8 @@ export class HttpClient {
|
|
|
984
1009
|
}
|
|
985
1010
|
|
|
986
1011
|
async streamProcessLogs(
|
|
987
|
-
processId: string
|
|
1012
|
+
processId: string,
|
|
1013
|
+
options?: { signal?: AbortSignal }
|
|
988
1014
|
): Promise<ReadableStream<Uint8Array>> {
|
|
989
1015
|
try {
|
|
990
1016
|
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
@@ -993,6 +1019,7 @@ export class HttpClient {
|
|
|
993
1019
|
"Cache-Control": "no-cache",
|
|
994
1020
|
},
|
|
995
1021
|
method: "GET",
|
|
1022
|
+
signal: options?.signal,
|
|
996
1023
|
});
|
|
997
1024
|
|
|
998
1025
|
if (!response.ok) {
|