@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/Dockerfile +9 -11
  3. package/README.md +69 -7
  4. package/container_src/control-process.ts +784 -0
  5. package/container_src/handler/exec.ts +99 -254
  6. package/container_src/handler/file.ts +204 -642
  7. package/container_src/handler/git.ts +28 -80
  8. package/container_src/handler/process.ts +443 -515
  9. package/container_src/handler/session.ts +92 -0
  10. package/container_src/index.ts +74 -129
  11. package/container_src/isolation.ts +1039 -0
  12. package/container_src/jupyter-service.ts +8 -5
  13. package/container_src/shell-escape.ts +42 -0
  14. package/container_src/types.ts +35 -12
  15. package/dist/{chunk-VTKZL632.js → chunk-BEQUGUY4.js} +2 -2
  16. package/dist/{chunk-4KELYYKS.js → chunk-GTGWAEED.js} +239 -265
  17. package/dist/chunk-GTGWAEED.js.map +1 -0
  18. package/dist/{chunk-CUHYLCMT.js → chunk-SMUEY5JR.js} +111 -99
  19. package/dist/chunk-SMUEY5JR.js.map +1 -0
  20. package/dist/{client-bzEV222a.d.ts → client-Dny_ro_v.d.ts} +48 -84
  21. package/dist/client.d.ts +1 -1
  22. package/dist/client.js +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +8 -9
  25. package/dist/interpreter.d.ts +2 -2
  26. package/dist/jupyter-client.d.ts +2 -2
  27. package/dist/jupyter-client.js +2 -2
  28. package/dist/request-handler.d.ts +3 -3
  29. package/dist/request-handler.js +3 -5
  30. package/dist/sandbox.d.ts +2 -2
  31. package/dist/sandbox.js +3 -5
  32. package/dist/types.d.ts +127 -21
  33. package/dist/types.js +35 -9
  34. package/dist/types.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/client.ts +175 -187
  37. package/src/index.ts +23 -13
  38. package/src/sandbox.ts +297 -332
  39. package/src/types.ts +125 -24
  40. package/dist/chunk-4KELYYKS.js.map +0 -1
  41. package/dist/chunk-CUHYLCMT.js.map +0 -1
  42. package/dist/chunk-S5FFBU4Y.js +0 -46
  43. package/dist/chunk-S5FFBU4Y.js.map +0 -1
  44. /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?: string;
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?: string;
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?: string;
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?: string;
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?: string;
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?: string;
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?: string;
73
+ sessionId: string;
118
74
  }
119
75
 
120
- export interface MoveFileResponse {
121
- success: boolean;
122
- exitCode: number;
123
- sourcePath: string;
124
- destinationPath: string;
125
- timestamp: string;
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 execute(
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: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
231
+ options?: Pick<BaseExecOptions, "cwd" | "env">
240
232
  ): Promise<ExecuteResponse> {
241
233
  try {
242
- const targetSessionId = options.sessionId || this.sessionId;
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
- body: JSON.stringify(executeRequest),
236
+ method: "POST",
252
237
  headers: {
253
238
  "Content-Type": "application/json",
254
239
  },
255
- method: "POST",
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 || `HTTP error! status: ${response.status}`
248
+ errorData.error || `Failed to execute in session: ${response.status}`
264
249
  );
265
250
  }
266
251
 
267
- const data: ExecuteResponse = await response.json();
252
+ const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };
268
253
  console.log(
269
- `[HTTP Client] Command executed: ${command}, Success: ${data.success}`
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
- data.success,
275
- data.exitCode,
276
- data.stdout,
277
- data.stderr,
278
- data.command
266
+ executeResponse.success,
267
+ executeResponse.exitCode,
268
+ executeResponse.stdout,
269
+ executeResponse.stderr,
270
+ executeResponse.command
279
271
  );
280
272
 
281
- return data;
273
+ return executeResponse;
282
274
  } catch (error) {
283
- console.error("[HTTP Client] Error executing command:", error);
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 executeCommandStream(
293
- command: string,
294
- sessionId?: string
284
+ async execStream(
285
+ sessionId: string,
286
+ command: string
295
287
  ): Promise<ReadableStream<Uint8Array>> {
296
288
  try {
297
- const targetSessionId = sessionId || this.sessionId;
298
-
289
+ // Always use session-specific streaming endpoint
299
290
  const response = await this.doFetch(`/api/execute/stream`, {
300
- body: JSON.stringify({
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
- method: "POST",
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 || `HTTP error! status: ${response.status}`
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 request");
311
+ throw new Error("No response body for streaming execution");
322
312
  }
323
313
 
324
- console.log(`[HTTP Client] Started command stream: ${command}`);
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 command stream:", error);
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?: string
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: targetSessionId,
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?: string
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: targetSessionId,
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?: string
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: targetSessionId,
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?: string
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: targetSessionId,
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?: string
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: targetSessionId,
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?: string
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
- options: {
798
- ...options,
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 response = await this.doFetch("/api/process/list", {
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 response = await this.doFetch("/api/process/kill-all", {
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) {