@cloudflare/sandbox 0.2.3 → 0.3.0
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 +69 -0
- package/Dockerfile +9 -11
- package/README.md +69 -7
- package/container_src/control-process.ts +784 -0
- package/container_src/handler/exec.ts +99 -254
- package/container_src/handler/file.ts +204 -642
- 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 +74 -129
- package/container_src/isolation.ts +1039 -0
- package/container_src/jupyter-service.ts +8 -5
- package/container_src/shell-escape.ts +42 -0
- package/container_src/types.ts +35 -12
- package/dist/{chunk-VTKZL632.js → chunk-BEQUGUY4.js} +2 -2
- package/dist/{chunk-4KELYYKS.js → chunk-GTGWAEED.js} +239 -265
- package/dist/chunk-GTGWAEED.js.map +1 -0
- package/dist/{chunk-CUHYLCMT.js → chunk-SMUEY5JR.js} +111 -99
- package/dist/chunk-SMUEY5JR.js.map +1 -0
- package/dist/{client-bzEV222a.d.ts → client-Dny_ro_v.d.ts} +48 -84
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -9
- package/dist/interpreter.d.ts +2 -2
- package/dist/jupyter-client.d.ts +2 -2
- package/dist/jupyter-client.js +2 -2
- package/dist/request-handler.d.ts +3 -3
- package/dist/request-handler.js +3 -5
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.js +3 -5
- package/dist/types.d.ts +127 -21
- package/dist/types.js +35 -9
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +175 -187
- package/src/index.ts +23 -13
- package/src/sandbox.ts +297 -332
- package/src/types.ts +125 -24
- package/dist/chunk-4KELYYKS.js.map +0 -1
- package/dist/chunk-CUHYLCMT.js.map +0 -1
- package/dist/chunk-S5FFBU4Y.js +0 -46
- package/dist/chunk-S5FFBU4Y.js.map +0 -1
- /package/dist/{chunk-VTKZL632.js.map → chunk-BEQUGUY4.js.map} +0 -0
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;
|
|
@@ -500,15 +483,13 @@ export class HttpClient {
|
|
|
500
483
|
|
|
501
484
|
async deleteFile(
|
|
502
485
|
path: string,
|
|
503
|
-
sessionId
|
|
486
|
+
sessionId: string
|
|
504
487
|
): Promise<DeleteFileResponse> {
|
|
505
488
|
try {
|
|
506
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
507
|
-
|
|
508
489
|
const response = await this.doFetch(`/api/delete`, {
|
|
509
490
|
body: JSON.stringify({
|
|
510
491
|
path,
|
|
511
|
-
sessionId
|
|
492
|
+
sessionId,
|
|
512
493
|
} as DeleteFileRequest),
|
|
513
494
|
headers: {
|
|
514
495
|
"Content-Type": "application/json",
|
|
@@ -527,7 +508,7 @@ export class HttpClient {
|
|
|
527
508
|
|
|
528
509
|
const data: DeleteFileResponse = await response.json();
|
|
529
510
|
console.log(
|
|
530
|
-
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
511
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
531
512
|
);
|
|
532
513
|
|
|
533
514
|
return data;
|
|
@@ -540,16 +521,14 @@ export class HttpClient {
|
|
|
540
521
|
async renameFile(
|
|
541
522
|
oldPath: string,
|
|
542
523
|
newPath: string,
|
|
543
|
-
sessionId
|
|
524
|
+
sessionId: string
|
|
544
525
|
): Promise<RenameFileResponse> {
|
|
545
526
|
try {
|
|
546
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
547
|
-
|
|
548
527
|
const response = await this.doFetch(`/api/rename`, {
|
|
549
528
|
body: JSON.stringify({
|
|
550
529
|
newPath,
|
|
551
530
|
oldPath,
|
|
552
|
-
sessionId
|
|
531
|
+
sessionId,
|
|
553
532
|
} as RenameFileRequest),
|
|
554
533
|
headers: {
|
|
555
534
|
"Content-Type": "application/json",
|
|
@@ -568,7 +547,7 @@ export class HttpClient {
|
|
|
568
547
|
|
|
569
548
|
const data: RenameFileResponse = await response.json();
|
|
570
549
|
console.log(
|
|
571
|
-
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
550
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
572
551
|
);
|
|
573
552
|
|
|
574
553
|
return data;
|
|
@@ -581,16 +560,14 @@ export class HttpClient {
|
|
|
581
560
|
async moveFile(
|
|
582
561
|
sourcePath: string,
|
|
583
562
|
destinationPath: string,
|
|
584
|
-
sessionId
|
|
563
|
+
sessionId: string
|
|
585
564
|
): Promise<MoveFileResponse> {
|
|
586
565
|
try {
|
|
587
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
588
|
-
|
|
589
566
|
const response = await this.doFetch(`/api/move`, {
|
|
590
567
|
body: JSON.stringify({
|
|
591
568
|
destinationPath,
|
|
592
|
-
sessionId: targetSessionId,
|
|
593
569
|
sourcePath,
|
|
570
|
+
sessionId,
|
|
594
571
|
} as MoveFileRequest),
|
|
595
572
|
headers: {
|
|
596
573
|
"Content-Type": "application/json",
|
|
@@ -609,7 +586,7 @@ export class HttpClient {
|
|
|
609
586
|
|
|
610
587
|
const data: MoveFileResponse = await response.json();
|
|
611
588
|
console.log(
|
|
612
|
-
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
589
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
613
590
|
);
|
|
614
591
|
|
|
615
592
|
return data;
|
|
@@ -619,6 +596,48 @@ export class HttpClient {
|
|
|
619
596
|
}
|
|
620
597
|
}
|
|
621
598
|
|
|
599
|
+
async listFiles(
|
|
600
|
+
path: string,
|
|
601
|
+
sessionId: string,
|
|
602
|
+
options?: {
|
|
603
|
+
recursive?: boolean;
|
|
604
|
+
includeHidden?: boolean;
|
|
605
|
+
}
|
|
606
|
+
): Promise<ListFilesResponse> {
|
|
607
|
+
try {
|
|
608
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
609
|
+
body: JSON.stringify({
|
|
610
|
+
path,
|
|
611
|
+
options,
|
|
612
|
+
sessionId,
|
|
613
|
+
} as ListFilesRequest),
|
|
614
|
+
headers: {
|
|
615
|
+
"Content-Type": "application/json",
|
|
616
|
+
},
|
|
617
|
+
method: "POST",
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
if (!response.ok) {
|
|
621
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
622
|
+
error?: string;
|
|
623
|
+
};
|
|
624
|
+
throw new Error(
|
|
625
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const data: ListFilesResponse = await response.json();
|
|
630
|
+
console.log(
|
|
631
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
return data;
|
|
635
|
+
} catch (error) {
|
|
636
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
637
|
+
throw error;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
622
641
|
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
623
642
|
try {
|
|
624
643
|
const response = await this.doFetch(`/api/expose-port`, {
|
|
@@ -739,48 +758,13 @@ export class HttpClient {
|
|
|
739
758
|
}
|
|
740
759
|
}
|
|
741
760
|
|
|
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
761
|
|
|
778
762
|
// Process management methods
|
|
779
763
|
async startProcess(
|
|
780
764
|
command: string,
|
|
765
|
+
sessionId: string,
|
|
781
766
|
options?: {
|
|
782
767
|
processId?: string;
|
|
783
|
-
sessionId?: string;
|
|
784
768
|
timeout?: number;
|
|
785
769
|
env?: Record<string, string>;
|
|
786
770
|
cwd?: string;
|
|
@@ -789,15 +773,11 @@ export class HttpClient {
|
|
|
789
773
|
}
|
|
790
774
|
): Promise<StartProcessResponse> {
|
|
791
775
|
try {
|
|
792
|
-
const targetSessionId = options?.sessionId || this.sessionId;
|
|
793
|
-
|
|
794
776
|
const response = await this.doFetch("/api/process/start", {
|
|
795
777
|
body: JSON.stringify({
|
|
796
778
|
command,
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
sessionId: targetSessionId,
|
|
800
|
-
},
|
|
779
|
+
sessionId,
|
|
780
|
+
options,
|
|
801
781
|
} as StartProcessRequest),
|
|
802
782
|
headers: {
|
|
803
783
|
"Content-Type": "application/json",
|
|
@@ -826,9 +806,12 @@ export class HttpClient {
|
|
|
826
806
|
}
|
|
827
807
|
}
|
|
828
808
|
|
|
829
|
-
async listProcesses(): Promise<ListProcessesResponse> {
|
|
809
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
830
810
|
try {
|
|
831
|
-
const
|
|
811
|
+
const url = sessionId
|
|
812
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
813
|
+
: "/api/process/list";
|
|
814
|
+
const response = await this.doFetch(url, {
|
|
832
815
|
headers: {
|
|
833
816
|
"Content-Type": "application/json",
|
|
834
817
|
},
|
|
@@ -919,13 +902,16 @@ export class HttpClient {
|
|
|
919
902
|
}
|
|
920
903
|
}
|
|
921
904
|
|
|
922
|
-
async killAllProcesses(): Promise<{
|
|
905
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
923
906
|
success: boolean;
|
|
924
907
|
killedCount: number;
|
|
925
908
|
message: string;
|
|
926
909
|
}> {
|
|
927
910
|
try {
|
|
928
|
-
const
|
|
911
|
+
const url = sessionId
|
|
912
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
913
|
+
: "/api/process/kill-all";
|
|
914
|
+
const response = await this.doFetch(url, {
|
|
929
915
|
headers: {
|
|
930
916
|
"Content-Type": "application/json",
|
|
931
917
|
},
|
|
@@ -984,7 +970,8 @@ export class HttpClient {
|
|
|
984
970
|
}
|
|
985
971
|
|
|
986
972
|
async streamProcessLogs(
|
|
987
|
-
processId: string
|
|
973
|
+
processId: string,
|
|
974
|
+
options?: { signal?: AbortSignal }
|
|
988
975
|
): Promise<ReadableStream<Uint8Array>> {
|
|
989
976
|
try {
|
|
990
977
|
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
@@ -993,6 +980,7 @@ export class HttpClient {
|
|
|
993
980
|
"Cache-Control": "no-cache",
|
|
994
981
|
},
|
|
995
982
|
method: "GET",
|
|
983
|
+
signal: options?.signal,
|
|
996
984
|
});
|
|
997
985
|
|
|
998
986
|
if (!response.ok) {
|