@cloudflare/sandbox 0.0.0-6704961 → 0.0.0-68d9bc5

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/src/client.ts CHANGED
@@ -1,11 +1,13 @@
1
+ import type { ExecuteRequest } from "../container_src/types";
1
2
  import type { Sandbox } from "./index";
2
-
3
- interface ExecuteRequest {
4
- command: string;
5
- args?: string[];
6
- sessionId?: string;
7
- background?: boolean;
8
- }
3
+ import type {
4
+ BaseExecOptions,
5
+ GetProcessLogsResponse,
6
+ GetProcessResponse,
7
+ ListProcessesResponse,
8
+ StartProcessRequest,
9
+ StartProcessResponse,
10
+ } from "./types";
9
11
 
10
12
  export interface ExecuteResponse {
11
13
  success: boolean;
@@ -13,23 +15,6 @@ export interface ExecuteResponse {
13
15
  stderr: string;
14
16
  exitCode: number;
15
17
  command: string;
16
- args: string[];
17
- timestamp: string;
18
- }
19
-
20
- interface SessionResponse {
21
- sessionId: string;
22
- message: string;
23
- timestamp: string;
24
- }
25
-
26
- interface SessionListResponse {
27
- sessions: Array<{
28
- sessionId: string;
29
- hasActiveProcess: boolean;
30
- createdAt: string;
31
- }>;
32
- count: number;
33
18
  timestamp: string;
34
19
  }
35
20
 
@@ -176,32 +161,11 @@ interface PingResponse {
176
161
  timestamp: string;
177
162
  }
178
163
 
179
- interface StreamEvent {
180
- type: "command_start" | "output" | "command_complete" | "error";
181
- command?: string;
182
- args?: string[];
183
- stream?: "stdout" | "stderr";
184
- data?: string;
185
- message?: string;
186
- path?: string;
187
- oldPath?: string;
188
- newPath?: string;
189
- sourcePath?: string;
190
- destinationPath?: string;
191
- content?: string;
192
- success?: boolean;
193
- exitCode?: number;
194
- stdout?: string;
195
- stderr?: string;
196
- error?: string;
197
- timestamp?: string;
198
- }
199
-
200
164
  interface HttpClientOptions {
201
165
  stub?: Sandbox;
202
166
  baseUrl?: string;
203
167
  port?: number;
204
- onCommandStart?: (command: string, args: string[]) => void;
168
+ onCommandStart?: (command: string) => void;
205
169
  onOutput?: (
206
170
  stream: "stdout" | "stderr",
207
171
  data: string,
@@ -212,11 +176,9 @@ interface HttpClientOptions {
212
176
  exitCode: number,
213
177
  stdout: string,
214
178
  stderr: string,
215
- command: string,
216
- args: string[]
179
+ command: string
217
180
  ) => void;
218
- onError?: (error: string, command?: string, args?: string[]) => void;
219
- onStreamEvent?: (event: StreamEvent) => void;
181
+ onError?: (error: string, command?: string) => void;
220
182
  }
221
183
 
222
184
  export class HttpClient {
@@ -231,7 +193,7 @@ export class HttpClient {
231
193
  this.baseUrl = this.options.baseUrl!;
232
194
  }
233
195
 
234
- private async doFetch(
196
+ protected async doFetch(
235
197
  path: string,
236
198
  options?: RequestInit
237
199
  ): Promise<Response> {
@@ -271,119 +233,22 @@ export class HttpClient {
271
233
  throw error;
272
234
  }
273
235
  }
274
- // Public methods to set event handlers
275
- setOnOutput(
276
- handler: (
277
- stream: "stdout" | "stderr",
278
- data: string,
279
- command: string
280
- ) => void
281
- ): void {
282
- this.options.onOutput = handler;
283
- }
284
-
285
- setOnCommandComplete(
286
- handler: (
287
- success: boolean,
288
- exitCode: number,
289
- stdout: string,
290
- stderr: string,
291
- command: string,
292
- args: string[]
293
- ) => void
294
- ): void {
295
- this.options.onCommandComplete = handler;
296
- }
297
-
298
- setOnStreamEvent(handler: (event: StreamEvent) => void): void {
299
- this.options.onStreamEvent = handler;
300
- }
301
-
302
- // Public getter methods
303
- getOnOutput():
304
- | ((stream: "stdout" | "stderr", data: string, command: string) => void)
305
- | undefined {
306
- return this.options.onOutput;
307
- }
308
-
309
- getOnCommandComplete():
310
- | ((
311
- success: boolean,
312
- exitCode: number,
313
- stdout: string,
314
- stderr: string,
315
- command: string,
316
- args: string[]
317
- ) => void)
318
- | undefined {
319
- return this.options.onCommandComplete;
320
- }
321
-
322
- getOnStreamEvent(): ((event: StreamEvent) => void) | undefined {
323
- return this.options.onStreamEvent;
324
- }
325
-
326
- async createSession(): Promise<string> {
327
- try {
328
- const response = await this.doFetch(`/api/session/create`, {
329
- headers: {
330
- "Content-Type": "application/json",
331
- },
332
- method: "POST",
333
- });
334
-
335
- if (!response.ok) {
336
- throw new Error(`HTTP error! status: ${response.status}`);
337
- }
338
-
339
- const data: SessionResponse = await response.json();
340
- this.sessionId = data.sessionId;
341
- console.log(`[HTTP Client] Created session: ${this.sessionId}`);
342
- return this.sessionId;
343
- } catch (error) {
344
- console.error("[HTTP Client] Error creating session:", error);
345
- throw error;
346
- }
347
- }
348
-
349
- async listSessions(): Promise<SessionListResponse> {
350
- try {
351
- const response = await this.doFetch(`/api/session/list`, {
352
- headers: {
353
- "Content-Type": "application/json",
354
- },
355
- method: "GET",
356
- });
357
-
358
- if (!response.ok) {
359
- throw new Error(`HTTP error! status: ${response.status}`);
360
- }
361
-
362
- const data: SessionListResponse = await response.json();
363
- console.log(`[HTTP Client] Listed ${data.count} sessions`);
364
- return data;
365
- } catch (error) {
366
- console.error("[HTTP Client] Error listing sessions:", error);
367
- throw error;
368
- }
369
- }
370
236
 
371
237
  async execute(
372
238
  command: string,
373
- args: string[] = [],
374
- sessionId?: string,
375
- background: boolean = false,
239
+ options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
376
240
  ): Promise<ExecuteResponse> {
377
241
  try {
378
- const targetSessionId = sessionId || this.sessionId;
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;
379
249
 
380
250
  const response = await this.doFetch(`/api/execute`, {
381
- body: JSON.stringify({
382
- args,
383
- command,
384
- background,
385
- sessionId: targetSessionId,
386
- } as ExecuteRequest),
251
+ body: JSON.stringify(executeRequest),
387
252
  headers: {
388
253
  "Content-Type": "application/json",
389
254
  },
@@ -410,8 +275,7 @@ export class HttpClient {
410
275
  data.exitCode,
411
276
  data.stdout,
412
277
  data.stderr,
413
- data.command,
414
- data.args
278
+ data.command
415
279
  );
416
280
 
417
281
  return data;
@@ -419,31 +283,27 @@ export class HttpClient {
419
283
  console.error("[HTTP Client] Error executing command:", error);
420
284
  this.options.onError?.(
421
285
  error instanceof Error ? error.message : "Unknown error",
422
- command,
423
- args
286
+ command
424
287
  );
425
288
  throw error;
426
289
  }
427
290
  }
428
291
 
429
- async executeStream(
292
+ async executeCommandStream(
430
293
  command: string,
431
- args: string[] = [],
432
- sessionId?: string,
433
- background: boolean = false
434
- ): Promise<void> {
294
+ sessionId?: string
295
+ ): Promise<ReadableStream<Uint8Array>> {
435
296
  try {
436
297
  const targetSessionId = sessionId || this.sessionId;
437
298
 
438
299
  const response = await this.doFetch(`/api/execute/stream`, {
439
300
  body: JSON.stringify({
440
- args,
441
301
  command,
442
- background,
443
302
  sessionId: targetSessionId,
444
303
  }),
445
304
  headers: {
446
305
  "Content-Type": "application/json",
306
+ Accept: "text/event-stream",
447
307
  },
448
308
  method: "POST",
449
309
  });
@@ -461,94 +321,11 @@ export class HttpClient {
461
321
  throw new Error("No response body for streaming request");
462
322
  }
463
323
 
464
- const reader = response.body.getReader();
465
- const decoder = new TextDecoder();
466
-
467
- try {
468
- while (true) {
469
- const { done, value } = await reader.read();
470
-
471
- if (done) {
472
- break;
473
- }
474
-
475
- const chunk = decoder.decode(value, { stream: true });
476
- const lines = chunk.split("\n");
477
-
478
- for (const line of lines) {
479
- if (line.startsWith("data: ")) {
480
- try {
481
- const eventData = line.slice(6); // Remove 'data: ' prefix
482
- const event: StreamEvent = JSON.parse(eventData);
483
-
484
- console.log(`[HTTP Client] Stream event: ${event.type}`);
485
- this.options.onStreamEvent?.(event);
486
-
487
- switch (event.type) {
488
- case "command_start":
489
- console.log(
490
- `[HTTP Client] Command started: ${event.command
491
- } ${event.args?.join(" ")}`
492
- );
493
- this.options.onCommandStart?.(
494
- event.command!,
495
- event.args || []
496
- );
497
- break;
498
-
499
- case "output":
500
- console.log(`[${event.stream}] ${event.data}`);
501
- this.options.onOutput?.(
502
- event.stream!,
503
- event.data!,
504
- event.command!
505
- );
506
- break;
507
-
508
- case "command_complete":
509
- console.log(
510
- `[HTTP Client] Command completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
511
- );
512
- this.options.onCommandComplete?.(
513
- event.success!,
514
- event.exitCode!,
515
- event.stdout!,
516
- event.stderr!,
517
- event.command!,
518
- event.args || []
519
- );
520
- break;
521
-
522
- case "error":
523
- console.error(
524
- `[HTTP Client] Command error: ${event.error}`
525
- );
526
- this.options.onError?.(
527
- event.error!,
528
- event.command,
529
- event.args
530
- );
531
- break;
532
- }
533
- } catch (parseError) {
534
- console.warn(
535
- "[HTTP Client] Failed to parse stream event:",
536
- parseError
537
- );
538
- }
539
- }
540
- }
541
- }
542
- } finally {
543
- reader.releaseLock();
544
- }
324
+ console.log(`[HTTP Client] Started command stream: ${command}`);
325
+
326
+ return response.body;
545
327
  } catch (error) {
546
- console.error("[HTTP Client] Error in streaming execution:", error);
547
- this.options.onError?.(
548
- error instanceof Error ? error.message : "Unknown error",
549
- command,
550
- args
551
- );
328
+ console.error("[HTTP Client] Error in command stream:", error);
552
329
  throw error;
553
330
  }
554
331
  }
@@ -596,135 +373,6 @@ export class HttpClient {
596
373
  }
597
374
  }
598
375
 
599
- async gitCheckoutStream(
600
- repoUrl: string,
601
- branch: string = "main",
602
- targetDir?: string,
603
- sessionId?: string
604
- ): Promise<void> {
605
- try {
606
- const targetSessionId = sessionId || this.sessionId;
607
-
608
- const response = await this.doFetch(`/api/git/checkout/stream`, {
609
- body: JSON.stringify({
610
- branch,
611
- repoUrl,
612
- sessionId: targetSessionId,
613
- targetDir,
614
- }),
615
- headers: {
616
- "Content-Type": "application/json",
617
- },
618
- method: "POST",
619
- });
620
-
621
- if (!response.ok) {
622
- const errorData = (await response.json().catch(() => ({}))) as {
623
- error?: string;
624
- };
625
- throw new Error(
626
- errorData.error || `HTTP error! status: ${response.status}`
627
- );
628
- }
629
-
630
- if (!response.body) {
631
- throw new Error("No response body for streaming request");
632
- }
633
-
634
- const reader = response.body.getReader();
635
- const decoder = new TextDecoder();
636
-
637
- try {
638
- while (true) {
639
- const { done, value } = await reader.read();
640
-
641
- if (done) {
642
- break;
643
- }
644
-
645
- const chunk = decoder.decode(value, { stream: true });
646
- const lines = chunk.split("\n");
647
-
648
- for (const line of lines) {
649
- if (line.startsWith("data: ")) {
650
- try {
651
- const eventData = line.slice(6); // Remove 'data: ' prefix
652
- const event: StreamEvent = JSON.parse(eventData);
653
-
654
- console.log(
655
- `[HTTP Client] Git checkout stream event: ${event.type}`
656
- );
657
- this.options.onStreamEvent?.(event);
658
-
659
- switch (event.type) {
660
- case "command_start":
661
- console.log(
662
- `[HTTP Client] Git checkout started: ${event.command
663
- } ${event.args?.join(" ")}`
664
- );
665
- this.options.onCommandStart?.(
666
- event.command!,
667
- event.args || []
668
- );
669
- break;
670
-
671
- case "output":
672
- console.log(`[${event.stream}] ${event.data}`);
673
- this.options.onOutput?.(
674
- event.stream!,
675
- event.data!,
676
- event.command!
677
- );
678
- break;
679
-
680
- case "command_complete":
681
- console.log(
682
- `[HTTP Client] Git checkout completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
683
- );
684
- this.options.onCommandComplete?.(
685
- event.success!,
686
- event.exitCode!,
687
- event.stdout!,
688
- event.stderr!,
689
- event.command!,
690
- event.args || []
691
- );
692
- break;
693
-
694
- case "error":
695
- console.error(
696
- `[HTTP Client] Git checkout error: ${event.error}`
697
- );
698
- this.options.onError?.(
699
- event.error!,
700
- event.command,
701
- event.args
702
- );
703
- break;
704
- }
705
- } catch (parseError) {
706
- console.warn(
707
- "[HTTP Client] Failed to parse git checkout stream event:",
708
- parseError
709
- );
710
- }
711
- }
712
- }
713
- }
714
- } finally {
715
- reader.releaseLock();
716
- }
717
- } catch (error) {
718
- console.error("[HTTP Client] Error in streaming git checkout:", error);
719
- this.options.onError?.(
720
- error instanceof Error ? error.message : "Unknown error",
721
- "git clone",
722
- [branch, repoUrl, targetDir || ""]
723
- );
724
- throw error;
725
- }
726
- }
727
-
728
376
  async mkdir(
729
377
  path: string,
730
378
  recursive: boolean = false,
@@ -766,20 +414,22 @@ export class HttpClient {
766
414
  }
767
415
  }
768
416
 
769
- async mkdirStream(
417
+ async writeFile(
770
418
  path: string,
771
- recursive: boolean = false,
419
+ content: string,
420
+ encoding: string = "utf-8",
772
421
  sessionId?: string
773
- ): Promise<void> {
422
+ ): Promise<WriteFileResponse> {
774
423
  try {
775
424
  const targetSessionId = sessionId || this.sessionId;
776
425
 
777
- const response = await this.doFetch(`/api/mkdir/stream`, {
426
+ const response = await this.doFetch(`/api/write`, {
778
427
  body: JSON.stringify({
428
+ content,
429
+ encoding,
779
430
  path,
780
- recursive,
781
431
  sessionId: targetSessionId,
782
- } as MkdirRequest),
432
+ } as WriteFileRequest),
783
433
  headers: {
784
434
  "Content-Type": "application/json",
785
435
  },
@@ -795,116 +445,32 @@ export class HttpClient {
795
445
  );
796
446
  }
797
447
 
798
- if (!response.body) {
799
- throw new Error("No response body for streaming request");
800
- }
448
+ const data: WriteFileResponse = await response.json();
449
+ console.log(
450
+ `[HTTP Client] File written: ${path}, Success: ${data.success}`
451
+ );
801
452
 
802
- const reader = response.body.getReader();
803
- const decoder = new TextDecoder();
804
-
805
- try {
806
- while (true) {
807
- const { done, value } = await reader.read();
808
-
809
- if (done) {
810
- break;
811
- }
812
-
813
- const chunk = decoder.decode(value, { stream: true });
814
- const lines = chunk.split("\n");
815
-
816
- for (const line of lines) {
817
- if (line.startsWith("data: ")) {
818
- try {
819
- const eventData = line.slice(6); // Remove 'data: ' prefix
820
- const event: StreamEvent = JSON.parse(eventData);
821
-
822
- console.log(`[HTTP Client] Mkdir stream event: ${event.type}`);
823
- this.options.onStreamEvent?.(event);
824
-
825
- switch (event.type) {
826
- case "command_start":
827
- console.log(
828
- `[HTTP Client] Mkdir started: ${event.command
829
- } ${event.args?.join(" ")}`
830
- );
831
- this.options.onCommandStart?.(
832
- event.command!,
833
- event.args || []
834
- );
835
- break;
836
-
837
- case "output":
838
- console.log(`[${event.stream}] ${event.data}`);
839
- this.options.onOutput?.(
840
- event.stream!,
841
- event.data!,
842
- event.command!
843
- );
844
- break;
845
-
846
- case "command_complete":
847
- console.log(
848
- `[HTTP Client] Mkdir completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
849
- );
850
- this.options.onCommandComplete?.(
851
- event.success!,
852
- event.exitCode!,
853
- event.stdout!,
854
- event.stderr!,
855
- event.command!,
856
- event.args || []
857
- );
858
- break;
859
-
860
- case "error":
861
- console.error(`[HTTP Client] Mkdir error: ${event.error}`);
862
- this.options.onError?.(
863
- event.error!,
864
- event.command,
865
- event.args
866
- );
867
- break;
868
- }
869
- } catch (parseError) {
870
- console.warn(
871
- "[HTTP Client] Failed to parse mkdir stream event:",
872
- parseError
873
- );
874
- }
875
- }
876
- }
877
- }
878
- } finally {
879
- reader.releaseLock();
880
- }
453
+ return data;
881
454
  } catch (error) {
882
- console.error("[HTTP Client] Error in streaming mkdir:", error);
883
- this.options.onError?.(
884
- error instanceof Error ? error.message : "Unknown error",
885
- "mkdir",
886
- recursive ? ["-p", path] : [path]
887
- );
455
+ console.error("[HTTP Client] Error writing file:", error);
888
456
  throw error;
889
457
  }
890
458
  }
891
459
 
892
- async writeFile(
460
+ async readFile(
893
461
  path: string,
894
- content: string,
895
462
  encoding: string = "utf-8",
896
463
  sessionId?: string
897
- ): Promise<WriteFileResponse> {
464
+ ): Promise<ReadFileResponse> {
898
465
  try {
899
466
  const targetSessionId = sessionId || this.sessionId;
900
467
 
901
- const response = await this.doFetch(`/api/write`, {
468
+ const response = await this.doFetch(`/api/read`, {
902
469
  body: JSON.stringify({
903
- content,
904
470
  encoding,
905
471
  path,
906
472
  sessionId: targetSessionId,
907
- } as WriteFileRequest),
473
+ } as ReadFileRequest),
908
474
  headers: {
909
475
  "Content-Type": "application/json",
910
476
  },
@@ -920,34 +486,30 @@ export class HttpClient {
920
486
  );
921
487
  }
922
488
 
923
- const data: WriteFileResponse = await response.json();
489
+ const data: ReadFileResponse = await response.json();
924
490
  console.log(
925
- `[HTTP Client] File written: ${path}, Success: ${data.success}`
491
+ `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
926
492
  );
927
493
 
928
494
  return data;
929
495
  } catch (error) {
930
- console.error("[HTTP Client] Error writing file:", error);
496
+ console.error("[HTTP Client] Error reading file:", error);
931
497
  throw error;
932
498
  }
933
499
  }
934
500
 
935
- async writeFileStream(
501
+ async deleteFile(
936
502
  path: string,
937
- content: string,
938
- encoding: string = "utf-8",
939
503
  sessionId?: string
940
- ): Promise<void> {
504
+ ): Promise<DeleteFileResponse> {
941
505
  try {
942
506
  const targetSessionId = sessionId || this.sessionId;
943
507
 
944
- const response = await this.doFetch(`/api/write/stream`, {
508
+ const response = await this.doFetch(`/api/delete`, {
945
509
  body: JSON.stringify({
946
- content,
947
- encoding,
948
510
  path,
949
511
  sessionId: targetSessionId,
950
- } as WriteFileRequest),
512
+ } as DeleteFileRequest),
951
513
  headers: {
952
514
  "Content-Type": "application/json",
953
515
  },
@@ -963,114 +525,32 @@ export class HttpClient {
963
525
  );
964
526
  }
965
527
 
966
- if (!response.body) {
967
- throw new Error("No response body for streaming request");
968
- }
528
+ const data: DeleteFileResponse = await response.json();
529
+ console.log(
530
+ `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
531
+ );
969
532
 
970
- const reader = response.body.getReader();
971
- const decoder = new TextDecoder();
972
-
973
- try {
974
- while (true) {
975
- const { done, value } = await reader.read();
976
-
977
- if (done) {
978
- break;
979
- }
980
-
981
- const chunk = decoder.decode(value, { stream: true });
982
- const lines = chunk.split("\n");
983
-
984
- for (const line of lines) {
985
- if (line.startsWith("data: ")) {
986
- try {
987
- const eventData = line.slice(6); // Remove 'data: ' prefix
988
- const event: StreamEvent = JSON.parse(eventData);
989
-
990
- console.log(
991
- `[HTTP Client] Write file stream event: ${event.type}`
992
- );
993
- this.options.onStreamEvent?.(event);
994
-
995
- switch (event.type) {
996
- case "command_start":
997
- console.log(
998
- `[HTTP Client] Write file started: ${event.path}`
999
- );
1000
- this.options.onCommandStart?.("write", [
1001
- path,
1002
- content,
1003
- encoding,
1004
- ]);
1005
- break;
1006
-
1007
- case "output":
1008
- console.log(`[output] ${event.message}`);
1009
- this.options.onOutput?.("stdout", event.message!, "write");
1010
- break;
1011
-
1012
- case "command_complete":
1013
- console.log(
1014
- `[HTTP Client] Write file completed: ${event.path}, Success: ${event.success}`
1015
- );
1016
- this.options.onCommandComplete?.(
1017
- event.success!,
1018
- 0,
1019
- "",
1020
- "",
1021
- "write",
1022
- [path, content, encoding]
1023
- );
1024
- break;
1025
-
1026
- case "error":
1027
- console.error(
1028
- `[HTTP Client] Write file error: ${event.error}`
1029
- );
1030
- this.options.onError?.(event.error!, "write", [
1031
- path,
1032
- content,
1033
- encoding,
1034
- ]);
1035
- break;
1036
- }
1037
- } catch (parseError) {
1038
- console.warn(
1039
- "[HTTP Client] Failed to parse write file stream event:",
1040
- parseError
1041
- );
1042
- }
1043
- }
1044
- }
1045
- }
1046
- } finally {
1047
- reader.releaseLock();
1048
- }
533
+ return data;
1049
534
  } catch (error) {
1050
- console.error("[HTTP Client] Error in streaming write file:", error);
1051
- this.options.onError?.(
1052
- error instanceof Error ? error.message : "Unknown error",
1053
- "write",
1054
- [path, content, encoding]
1055
- );
535
+ console.error("[HTTP Client] Error deleting file:", error);
1056
536
  throw error;
1057
537
  }
1058
538
  }
1059
539
 
1060
- async readFile(
1061
- path: string,
1062
- encoding: string = "utf-8",
540
+ async renameFile(
541
+ oldPath: string,
542
+ newPath: string,
1063
543
  sessionId?: string
1064
- ): Promise<ReadFileResponse> {
544
+ ): Promise<RenameFileResponse> {
1065
545
  try {
1066
546
  const targetSessionId = sessionId || this.sessionId;
1067
547
 
1068
- const response = await this.doFetch(`/api/read`, {
548
+ const response = await this.doFetch(`/api/rename`, {
1069
549
  body: JSON.stringify({
1070
- encoding,
1071
- path,
550
+ newPath,
551
+ oldPath,
1072
552
  sessionId: targetSessionId,
1073
- } as ReadFileRequest),
553
+ } as RenameFileRequest),
1074
554
  headers: {
1075
555
  "Content-Type": "application/json",
1076
556
  },
@@ -1086,32 +566,32 @@ export class HttpClient {
1086
566
  );
1087
567
  }
1088
568
 
1089
- const data: ReadFileResponse = await response.json();
569
+ const data: RenameFileResponse = await response.json();
1090
570
  console.log(
1091
- `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
571
+ `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
1092
572
  );
1093
573
 
1094
574
  return data;
1095
575
  } catch (error) {
1096
- console.error("[HTTP Client] Error reading file:", error);
576
+ console.error("[HTTP Client] Error renaming file:", error);
1097
577
  throw error;
1098
578
  }
1099
579
  }
1100
580
 
1101
- async readFileStream(
1102
- path: string,
1103
- encoding: string = "utf-8",
581
+ async moveFile(
582
+ sourcePath: string,
583
+ destinationPath: string,
1104
584
  sessionId?: string
1105
- ): Promise<void> {
585
+ ): Promise<MoveFileResponse> {
1106
586
  try {
1107
587
  const targetSessionId = sessionId || this.sessionId;
1108
588
 
1109
- const response = await this.doFetch(`/api/read/stream`, {
589
+ const response = await this.doFetch(`/api/move`, {
1110
590
  body: JSON.stringify({
1111
- encoding,
1112
- path,
591
+ destinationPath,
1113
592
  sessionId: targetSessionId,
1114
- } as ReadFileRequest),
593
+ sourcePath,
594
+ } as MoveFileRequest),
1115
595
  headers: {
1116
596
  "Content-Type": "application/json",
1117
597
  },
@@ -1127,104 +607,25 @@ export class HttpClient {
1127
607
  );
1128
608
  }
1129
609
 
1130
- if (!response.body) {
1131
- throw new Error("No response body for streaming request");
1132
- }
610
+ const data: MoveFileResponse = await response.json();
611
+ console.log(
612
+ `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
613
+ );
1133
614
 
1134
- const reader = response.body.getReader();
1135
- const decoder = new TextDecoder();
1136
-
1137
- try {
1138
- while (true) {
1139
- const { done, value } = await reader.read();
1140
-
1141
- if (done) {
1142
- break;
1143
- }
1144
-
1145
- const chunk = decoder.decode(value, { stream: true });
1146
- const lines = chunk.split("\n");
1147
-
1148
- for (const line of lines) {
1149
- if (line.startsWith("data: ")) {
1150
- try {
1151
- const eventData = line.slice(6); // Remove 'data: ' prefix
1152
- const event: StreamEvent = JSON.parse(eventData);
1153
-
1154
- console.log(
1155
- `[HTTP Client] Read file stream event: ${event.type}`
1156
- );
1157
- this.options.onStreamEvent?.(event);
1158
-
1159
- switch (event.type) {
1160
- case "command_start":
1161
- console.log(
1162
- `[HTTP Client] Read file started: ${event.path}`
1163
- );
1164
- this.options.onCommandStart?.("read", [path, encoding]);
1165
- break;
1166
-
1167
- case "command_complete":
1168
- console.log(
1169
- `[HTTP Client] Read file completed: ${event.path
1170
- }, Success: ${event.success}, Content length: ${event.content?.length || 0
1171
- }`
1172
- );
1173
- this.options.onCommandComplete?.(
1174
- event.success!,
1175
- 0,
1176
- event.content || "",
1177
- "",
1178
- "read",
1179
- [path, encoding]
1180
- );
1181
- break;
1182
-
1183
- case "error":
1184
- console.error(
1185
- `[HTTP Client] Read file error: ${event.error}`
1186
- );
1187
- this.options.onError?.(event.error!, "read", [
1188
- path,
1189
- encoding,
1190
- ]);
1191
- break;
1192
- }
1193
- } catch (parseError) {
1194
- console.warn(
1195
- "[HTTP Client] Failed to parse read file stream event:",
1196
- parseError
1197
- );
1198
- }
1199
- }
1200
- }
1201
- }
1202
- } finally {
1203
- reader.releaseLock();
1204
- }
615
+ return data;
1205
616
  } catch (error) {
1206
- console.error("[HTTP Client] Error in streaming read file:", error);
1207
- this.options.onError?.(
1208
- error instanceof Error ? error.message : "Unknown error",
1209
- "read",
1210
- [path, encoding]
1211
- );
617
+ console.error("[HTTP Client] Error moving file:", error);
1212
618
  throw error;
1213
619
  }
1214
620
  }
1215
621
 
1216
- async deleteFile(
1217
- path: string,
1218
- sessionId?: string
1219
- ): Promise<DeleteFileResponse> {
622
+ async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
1220
623
  try {
1221
- const targetSessionId = sessionId || this.sessionId;
1222
-
1223
- const response = await this.doFetch(`/api/delete`, {
624
+ const response = await this.doFetch(`/api/expose-port`, {
1224
625
  body: JSON.stringify({
1225
- path,
1226
- sessionId: targetSessionId,
1227
- } as DeleteFileRequest),
626
+ port,
627
+ name,
628
+ }),
1228
629
  headers: {
1229
630
  "Content-Type": "application/json",
1230
631
  },
@@ -1235,36 +636,36 @@ export class HttpClient {
1235
636
  const errorData = (await response.json().catch(() => ({}))) as {
1236
637
  error?: string;
1237
638
  };
639
+ console.log(errorData);
1238
640
  throw new Error(
1239
641
  errorData.error || `HTTP error! status: ${response.status}`
1240
642
  );
1241
643
  }
1242
644
 
1243
- const data: DeleteFileResponse = await response.json();
645
+ const data: ExposePortResponse = await response.json();
1244
646
  console.log(
1245
- `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
647
+ `[HTTP Client] Port exposed: ${port}${
648
+ name ? ` (${name})` : ""
649
+ }, Success: ${data.success}`
1246
650
  );
1247
651
 
1248
652
  return data;
1249
653
  } catch (error) {
1250
- console.error("[HTTP Client] Error deleting file:", error);
654
+ console.error("[HTTP Client] Error exposing port:", error);
1251
655
  throw error;
1252
656
  }
1253
657
  }
1254
658
 
1255
- async deleteFileStream(path: string, sessionId?: string): Promise<void> {
659
+ async unexposePort(port: number): Promise<UnexposePortResponse> {
1256
660
  try {
1257
- const targetSessionId = sessionId || this.sessionId;
1258
-
1259
- const response = await this.doFetch(`/api/delete/stream`, {
661
+ const response = await this.doFetch(`/api/unexpose-port`, {
1260
662
  body: JSON.stringify({
1261
- path,
1262
- sessionId: targetSessionId,
1263
- } as DeleteFileRequest),
663
+ port,
664
+ }),
1264
665
  headers: {
1265
666
  "Content-Type": "application/json",
1266
667
  },
1267
- method: "POST",
668
+ method: "DELETE",
1268
669
  });
1269
670
 
1270
671
  if (!response.ok) {
@@ -1276,105 +677,25 @@ export class HttpClient {
1276
677
  );
1277
678
  }
1278
679
 
1279
- if (!response.body) {
1280
- throw new Error("No response body for streaming request");
1281
- }
680
+ const data: UnexposePortResponse = await response.json();
681
+ console.log(
682
+ `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
683
+ );
1282
684
 
1283
- const reader = response.body.getReader();
1284
- const decoder = new TextDecoder();
1285
-
1286
- try {
1287
- while (true) {
1288
- const { done, value } = await reader.read();
1289
-
1290
- if (done) {
1291
- break;
1292
- }
1293
-
1294
- const chunk = decoder.decode(value, { stream: true });
1295
- const lines = chunk.split("\n");
1296
-
1297
- for (const line of lines) {
1298
- if (line.startsWith("data: ")) {
1299
- try {
1300
- const eventData = line.slice(6); // Remove 'data: ' prefix
1301
- const event: StreamEvent = JSON.parse(eventData);
1302
-
1303
- console.log(
1304
- `[HTTP Client] Delete file stream event: ${event.type}`
1305
- );
1306
- this.options.onStreamEvent?.(event);
1307
-
1308
- switch (event.type) {
1309
- case "command_start":
1310
- console.log(
1311
- `[HTTP Client] Delete file started: ${event.path}`
1312
- );
1313
- this.options.onCommandStart?.("delete", [path]);
1314
- break;
1315
-
1316
- case "command_complete":
1317
- console.log(
1318
- `[HTTP Client] Delete file completed: ${event.path}, Success: ${event.success}`
1319
- );
1320
- this.options.onCommandComplete?.(
1321
- event.success!,
1322
- 0,
1323
- "",
1324
- "",
1325
- "delete",
1326
- [path]
1327
- );
1328
- break;
1329
-
1330
- case "error":
1331
- console.error(
1332
- `[HTTP Client] Delete file error: ${event.error}`
1333
- );
1334
- this.options.onError?.(event.error!, "delete", [path]);
1335
- break;
1336
- }
1337
- } catch (parseError) {
1338
- console.warn(
1339
- "[HTTP Client] Failed to parse delete file stream event:",
1340
- parseError
1341
- );
1342
- }
1343
- }
1344
- }
1345
- }
1346
- } finally {
1347
- reader.releaseLock();
1348
- }
685
+ return data;
1349
686
  } catch (error) {
1350
- console.error("[HTTP Client] Error in streaming delete file:", error);
1351
- this.options.onError?.(
1352
- error instanceof Error ? error.message : "Unknown error",
1353
- "delete",
1354
- [path]
1355
- );
687
+ console.error("[HTTP Client] Error unexposing port:", error);
1356
688
  throw error;
1357
689
  }
1358
690
  }
1359
691
 
1360
- async renameFile(
1361
- oldPath: string,
1362
- newPath: string,
1363
- sessionId?: string
1364
- ): Promise<RenameFileResponse> {
692
+ async getExposedPorts(): Promise<GetExposedPortsResponse> {
1365
693
  try {
1366
- const targetSessionId = sessionId || this.sessionId;
1367
-
1368
- const response = await this.doFetch(`/api/rename`, {
1369
- body: JSON.stringify({
1370
- newPath,
1371
- oldPath,
1372
- sessionId: targetSessionId,
1373
- } as RenameFileRequest),
694
+ const response = await this.doFetch(`/api/exposed-ports`, {
1374
695
  headers: {
1375
696
  "Content-Type": "application/json",
1376
697
  },
1377
- method: "POST",
698
+ method: "GET",
1378
699
  });
1379
700
 
1380
701
  if (!response.ok) {
@@ -1386,145 +707,98 @@ export class HttpClient {
1386
707
  );
1387
708
  }
1388
709
 
1389
- const data: RenameFileResponse = await response.json();
1390
- console.log(
1391
- `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
1392
- );
710
+ const data: GetExposedPortsResponse = await response.json();
711
+ console.log(`[HTTP Client] Got ${data.count} exposed ports`);
1393
712
 
1394
713
  return data;
1395
714
  } catch (error) {
1396
- console.error("[HTTP Client] Error renaming file:", error);
715
+ console.error("[HTTP Client] Error getting exposed ports:", error);
1397
716
  throw error;
1398
717
  }
1399
718
  }
1400
719
 
1401
- async renameFileStream(
1402
- oldPath: string,
1403
- newPath: string,
1404
- sessionId?: string
1405
- ): Promise<void> {
720
+ async ping(): Promise<string> {
1406
721
  try {
1407
- const targetSessionId = sessionId || this.sessionId;
1408
-
1409
- const response = await this.doFetch(`/api/rename/stream`, {
1410
- body: JSON.stringify({
1411
- newPath,
1412
- oldPath,
1413
- sessionId: targetSessionId,
1414
- } as RenameFileRequest),
722
+ const response = await this.doFetch(`/api/ping`, {
1415
723
  headers: {
1416
724
  "Content-Type": "application/json",
1417
725
  },
1418
- method: "POST",
726
+ method: "GET",
1419
727
  });
1420
728
 
1421
729
  if (!response.ok) {
1422
- const errorData = (await response.json().catch(() => ({}))) as {
1423
- error?: string;
1424
- };
1425
- throw new Error(
1426
- errorData.error || `HTTP error! status: ${response.status}`
1427
- );
730
+ throw new Error(`HTTP error! status: ${response.status}`);
1428
731
  }
1429
732
 
1430
- if (!response.body) {
1431
- throw new Error("No response body for streaming request");
1432
- }
733
+ const data: PingResponse = await response.json();
734
+ console.log(`[HTTP Client] Ping response: ${data.message}`);
735
+ return data.timestamp;
736
+ } catch (error) {
737
+ console.error("[HTTP Client] Error pinging server:", error);
738
+ throw error;
739
+ }
740
+ }
1433
741
 
1434
- const reader = response.body.getReader();
1435
- const decoder = new TextDecoder();
1436
-
1437
- try {
1438
- while (true) {
1439
- const { done, value } = await reader.read();
1440
-
1441
- if (done) {
1442
- break;
1443
- }
1444
-
1445
- const chunk = decoder.decode(value, { stream: true });
1446
- const lines = chunk.split("\n");
1447
-
1448
- for (const line of lines) {
1449
- if (line.startsWith("data: ")) {
1450
- try {
1451
- const eventData = line.slice(6); // Remove 'data: ' prefix
1452
- const event: StreamEvent = JSON.parse(eventData);
1453
-
1454
- console.log(
1455
- `[HTTP Client] Rename file stream event: ${event.type}`
1456
- );
1457
- this.options.onStreamEvent?.(event);
1458
-
1459
- switch (event.type) {
1460
- case "command_start":
1461
- console.log(
1462
- `[HTTP Client] Rename file started: ${event.oldPath} -> ${event.newPath}`
1463
- );
1464
- this.options.onCommandStart?.("rename", [oldPath, newPath]);
1465
- break;
1466
-
1467
- case "command_complete":
1468
- console.log(
1469
- `[HTTP Client] Rename file completed: ${event.oldPath} -> ${event.newPath}, Success: ${event.success}`
1470
- );
1471
- this.options.onCommandComplete?.(
1472
- event.success!,
1473
- 0,
1474
- "",
1475
- "",
1476
- "rename",
1477
- [oldPath, newPath]
1478
- );
1479
- break;
1480
-
1481
- case "error":
1482
- console.error(
1483
- `[HTTP Client] Rename file error: ${event.error}`
1484
- );
1485
- this.options.onError?.(event.error!, "rename", [
1486
- oldPath,
1487
- newPath,
1488
- ]);
1489
- break;
1490
- }
1491
- } catch (parseError) {
1492
- console.warn(
1493
- "[HTTP Client] Failed to parse rename file stream event:",
1494
- parseError
1495
- );
1496
- }
1497
- }
1498
- }
1499
- }
1500
- } finally {
1501
- reader.releaseLock();
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}`);
1502
753
  }
1503
- } catch (error) {
1504
- console.error("[HTTP Client] Error in streaming rename file:", error);
1505
- this.options.onError?.(
1506
- error instanceof Error ? error.message : "Unknown error",
1507
- "rename",
1508
- [oldPath, newPath]
754
+
755
+ const data: CommandsResponse = await response.json();
756
+ console.log(
757
+ `[HTTP Client] Available commands: ${data.availableCommands.length}`
1509
758
  );
759
+ return data.availableCommands;
760
+ } catch (error) {
761
+ console.error("[HTTP Client] Error getting commands:", error);
1510
762
  throw error;
1511
763
  }
1512
764
  }
1513
765
 
1514
- async moveFile(
1515
- sourcePath: string,
1516
- destinationPath: string,
1517
- sessionId?: string
1518
- ): Promise<MoveFileResponse> {
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
+
778
+ // Process management methods
779
+ async startProcess(
780
+ command: string,
781
+ options?: {
782
+ processId?: string;
783
+ sessionId?: string;
784
+ timeout?: number;
785
+ env?: Record<string, string>;
786
+ cwd?: string;
787
+ encoding?: string;
788
+ autoCleanup?: boolean;
789
+ }
790
+ ): Promise<StartProcessResponse> {
1519
791
  try {
1520
- const targetSessionId = sessionId || this.sessionId;
792
+ const targetSessionId = options?.sessionId || this.sessionId;
1521
793
 
1522
- const response = await this.doFetch(`/api/move`, {
794
+ const response = await this.doFetch("/api/process/start", {
1523
795
  body: JSON.stringify({
1524
- destinationPath,
1525
- sessionId: targetSessionId,
1526
- sourcePath,
1527
- } as MoveFileRequest),
796
+ command,
797
+ options: {
798
+ ...options,
799
+ sessionId: targetSessionId,
800
+ },
801
+ } as StartProcessRequest),
1528
802
  headers: {
1529
803
  "Content-Type": "application/json",
1530
804
  },
@@ -1540,36 +814,25 @@ export class HttpClient {
1540
814
  );
1541
815
  }
1542
816
 
1543
- const data: MoveFileResponse = await response.json();
817
+ const data: StartProcessResponse = await response.json();
1544
818
  console.log(
1545
- `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
819
+ `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
1546
820
  );
1547
821
 
1548
822
  return data;
1549
823
  } catch (error) {
1550
- console.error("[HTTP Client] Error moving file:", error);
824
+ console.error("[HTTP Client] Error starting process:", error);
1551
825
  throw error;
1552
826
  }
1553
827
  }
1554
828
 
1555
- async moveFileStream(
1556
- sourcePath: string,
1557
- destinationPath: string,
1558
- sessionId?: string
1559
- ): Promise<void> {
829
+ async listProcesses(): Promise<ListProcessesResponse> {
1560
830
  try {
1561
- const targetSessionId = sessionId || this.sessionId;
1562
-
1563
- const response = await this.doFetch(`/api/move/stream`, {
1564
- body: JSON.stringify({
1565
- destinationPath,
1566
- sessionId: targetSessionId,
1567
- sourcePath,
1568
- } as MoveFileRequest),
831
+ const response = await this.doFetch("/api/process/list", {
1569
832
  headers: {
1570
833
  "Content-Type": "application/json",
1571
834
  },
1572
- method: "POST",
835
+ method: "GET",
1573
836
  });
1574
837
 
1575
838
  if (!response.ok) {
@@ -1581,134 +844,53 @@ export class HttpClient {
1581
844
  );
1582
845
  }
1583
846
 
1584
- if (!response.body) {
1585
- throw new Error("No response body for streaming request");
1586
- }
847
+ const data: ListProcessesResponse = await response.json();
848
+ console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
1587
849
 
1588
- const reader = response.body.getReader();
1589
- const decoder = new TextDecoder();
1590
-
1591
- try {
1592
- while (true) {
1593
- const { done, value } = await reader.read();
1594
-
1595
- if (done) {
1596
- break;
1597
- }
1598
-
1599
- const chunk = decoder.decode(value, { stream: true });
1600
- const lines = chunk.split("\n");
1601
-
1602
- for (const line of lines) {
1603
- if (line.startsWith("data: ")) {
1604
- try {
1605
- const eventData = line.slice(6); // Remove 'data: ' prefix
1606
- const event: StreamEvent = JSON.parse(eventData);
1607
-
1608
- console.log(
1609
- `[HTTP Client] Move file stream event: ${event.type}`
1610
- );
1611
- this.options.onStreamEvent?.(event);
1612
-
1613
- switch (event.type) {
1614
- case "command_start":
1615
- console.log(
1616
- `[HTTP Client] Move file started: ${event.sourcePath} -> ${event.destinationPath}`
1617
- );
1618
- this.options.onCommandStart?.("move", [
1619
- sourcePath,
1620
- destinationPath,
1621
- ]);
1622
- break;
1623
-
1624
- case "command_complete":
1625
- console.log(
1626
- `[HTTP Client] Move file completed: ${event.sourcePath} -> ${event.destinationPath}, Success: ${event.success}`
1627
- );
1628
- this.options.onCommandComplete?.(
1629
- event.success!,
1630
- 0,
1631
- "",
1632
- "",
1633
- "move",
1634
- [sourcePath, destinationPath]
1635
- );
1636
- break;
1637
-
1638
- case "error":
1639
- console.error(
1640
- `[HTTP Client] Move file error: ${event.error}`
1641
- );
1642
- this.options.onError?.(event.error!, "move", [
1643
- sourcePath,
1644
- destinationPath,
1645
- ]);
1646
- break;
1647
- }
1648
- } catch (parseError) {
1649
- console.warn(
1650
- "[HTTP Client] Failed to parse move file stream event:",
1651
- parseError
1652
- );
1653
- }
1654
- }
1655
- }
1656
- }
1657
- } finally {
1658
- reader.releaseLock();
1659
- }
850
+ return data;
1660
851
  } catch (error) {
1661
- console.error("[HTTP Client] Error in streaming move file:", error);
1662
- this.options.onError?.(
1663
- error instanceof Error ? error.message : "Unknown error",
1664
- "move",
1665
- [sourcePath, destinationPath]
1666
- );
852
+ console.error("[HTTP Client] Error listing processes:", error);
1667
853
  throw error;
1668
854
  }
1669
855
  }
1670
856
 
1671
- async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
857
+ async getProcess(processId: string): Promise<GetProcessResponse> {
1672
858
  try {
1673
- const response = await this.doFetch(`/api/expose-port`, {
1674
- body: JSON.stringify({
1675
- port,
1676
- name,
1677
- }),
859
+ const response = await this.doFetch(`/api/process/${processId}`, {
1678
860
  headers: {
1679
861
  "Content-Type": "application/json",
1680
862
  },
1681
- method: "POST",
863
+ method: "GET",
1682
864
  });
1683
865
 
1684
866
  if (!response.ok) {
1685
867
  const errorData = (await response.json().catch(() => ({}))) as {
1686
868
  error?: string;
1687
869
  };
1688
- console.log(errorData);
1689
870
  throw new Error(
1690
871
  errorData.error || `HTTP error! status: ${response.status}`
1691
872
  );
1692
873
  }
1693
874
 
1694
- const data: ExposePortResponse = await response.json();
875
+ const data: GetProcessResponse = await response.json();
1695
876
  console.log(
1696
- `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : ""}, Success: ${data.success}`
877
+ `[HTTP Client] Got process ${processId}: ${
878
+ data.process?.status || "not found"
879
+ }`
1697
880
  );
1698
881
 
1699
882
  return data;
1700
883
  } catch (error) {
1701
- console.error("[HTTP Client] Error exposing port:", error);
884
+ console.error("[HTTP Client] Error getting process:", error);
1702
885
  throw error;
1703
886
  }
1704
887
  }
1705
888
 
1706
- async unexposePort(port: number): Promise<UnexposePortResponse> {
889
+ async killProcess(
890
+ processId: string
891
+ ): Promise<{ success: boolean; message: string }> {
1707
892
  try {
1708
- const response = await this.doFetch(`/api/unexpose-port`, {
1709
- body: JSON.stringify({
1710
- port,
1711
- }),
893
+ const response = await this.doFetch(`/api/process/${processId}`, {
1712
894
  headers: {
1713
895
  "Content-Type": "application/json",
1714
896
  },
@@ -1724,25 +906,30 @@ export class HttpClient {
1724
906
  );
1725
907
  }
1726
908
 
1727
- const data: UnexposePortResponse = await response.json();
1728
- console.log(
1729
- `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
1730
- );
909
+ const data = (await response.json()) as {
910
+ success: boolean;
911
+ message: string;
912
+ };
913
+ console.log(`[HTTP Client] Killed process ${processId}`);
1731
914
 
1732
915
  return data;
1733
916
  } catch (error) {
1734
- console.error("[HTTP Client] Error unexposing port:", error);
917
+ console.error("[HTTP Client] Error killing process:", error);
1735
918
  throw error;
1736
919
  }
1737
920
  }
1738
921
 
1739
- async getExposedPorts(): Promise<GetExposedPortsResponse> {
922
+ async killAllProcesses(): Promise<{
923
+ success: boolean;
924
+ killedCount: number;
925
+ message: string;
926
+ }> {
1740
927
  try {
1741
- const response = await this.doFetch(`/api/exposed-ports`, {
928
+ const response = await this.doFetch("/api/process/kill-all", {
1742
929
  headers: {
1743
930
  "Content-Type": "application/json",
1744
931
  },
1745
- method: "GET",
932
+ method: "DELETE",
1746
933
  });
1747
934
 
1748
935
  if (!response.ok) {
@@ -1754,21 +941,23 @@ export class HttpClient {
1754
941
  );
1755
942
  }
1756
943
 
1757
- const data: GetExposedPortsResponse = await response.json();
1758
- console.log(
1759
- `[HTTP Client] Got ${data.count} exposed ports`
1760
- );
944
+ const data = (await response.json()) as {
945
+ success: boolean;
946
+ killedCount: number;
947
+ message: string;
948
+ };
949
+ console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
1761
950
 
1762
951
  return data;
1763
952
  } catch (error) {
1764
- console.error("[HTTP Client] Error getting exposed ports:", error);
953
+ console.error("[HTTP Client] Error killing all processes:", error);
1765
954
  throw error;
1766
955
  }
1767
956
  }
1768
957
 
1769
- async ping(): Promise<string> {
958
+ async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
1770
959
  try {
1771
- const response = await this.doFetch(`/api/ping`, {
960
+ const response = await this.doFetch(`/api/process/${processId}/logs`, {
1772
961
  headers: {
1773
962
  "Content-Type": "application/json",
1774
963
  },
@@ -1776,314 +965,57 @@ export class HttpClient {
1776
965
  });
1777
966
 
1778
967
  if (!response.ok) {
1779
- throw new Error(`HTTP error! status: ${response.status}`);
968
+ const errorData = (await response.json().catch(() => ({}))) as {
969
+ error?: string;
970
+ };
971
+ throw new Error(
972
+ errorData.error || `HTTP error! status: ${response.status}`
973
+ );
1780
974
  }
1781
975
 
1782
- const data: PingResponse = await response.json();
1783
- console.log(`[HTTP Client] Ping response: ${data.message}`);
1784
- return data.timestamp;
976
+ const data: GetProcessLogsResponse = await response.json();
977
+ console.log(`[HTTP Client] Got logs for process ${processId}`);
978
+
979
+ return data;
1785
980
  } catch (error) {
1786
- console.error("[HTTP Client] Error pinging server:", error);
981
+ console.error("[HTTP Client] Error getting process logs:", error);
1787
982
  throw error;
1788
983
  }
1789
984
  }
1790
985
 
1791
- async getCommands(): Promise<string[]> {
986
+ async streamProcessLogs(
987
+ processId: string
988
+ ): Promise<ReadableStream<Uint8Array>> {
1792
989
  try {
1793
- const response = await fetch(`${this.baseUrl}/api/commands`, {
990
+ const response = await this.doFetch(`/api/process/${processId}/stream`, {
1794
991
  headers: {
1795
- "Content-Type": "application/json",
992
+ Accept: "text/event-stream",
993
+ "Cache-Control": "no-cache",
1796
994
  },
1797
995
  method: "GET",
1798
996
  });
1799
997
 
1800
998
  if (!response.ok) {
1801
- throw new Error(`HTTP error! status: ${response.status}`);
999
+ const errorData = (await response.json().catch(() => ({}))) as {
1000
+ error?: string;
1001
+ };
1002
+ throw new Error(
1003
+ errorData.error || `HTTP error! status: ${response.status}`
1004
+ );
1005
+ }
1006
+
1007
+ if (!response.body) {
1008
+ throw new Error("No response body for streaming request");
1802
1009
  }
1803
1010
 
1804
- const data: CommandsResponse = await response.json();
1805
1011
  console.log(
1806
- `[HTTP Client] Available commands: ${data.availableCommands.length}`
1012
+ `[HTTP Client] Started streaming logs for process ${processId}`
1807
1013
  );
1808
- return data.availableCommands;
1014
+
1015
+ return response.body;
1809
1016
  } catch (error) {
1810
- console.error("[HTTP Client] Error getting commands:", error);
1017
+ console.error("[HTTP Client] Error streaming process logs:", error);
1811
1018
  throw error;
1812
1019
  }
1813
1020
  }
1814
-
1815
- getSessionId(): string | null {
1816
- return this.sessionId;
1817
- }
1818
-
1819
- setSessionId(sessionId: string): void {
1820
- this.sessionId = sessionId;
1821
- }
1822
-
1823
- clearSession(): void {
1824
- this.sessionId = null;
1825
- }
1826
- }
1827
-
1828
- // Example usage and utility functions
1829
- export function createClient(options?: HttpClientOptions): HttpClient {
1830
- return new HttpClient(options);
1831
- }
1832
-
1833
- // Convenience function for quick command execution
1834
- export async function quickExecute(
1835
- command: string,
1836
- args: string[] = [],
1837
- options?: HttpClientOptions
1838
- ): Promise<ExecuteResponse> {
1839
- const client = createClient(options);
1840
- await client.createSession();
1841
-
1842
- try {
1843
- return await client.execute(command, args);
1844
- } finally {
1845
- client.clearSession();
1846
- }
1847
- }
1848
-
1849
- // Convenience function for quick streaming command execution
1850
- export async function quickExecuteStream(
1851
- command: string,
1852
- args: string[] = [],
1853
- options?: HttpClientOptions
1854
- ): Promise<void> {
1855
- const client = createClient(options);
1856
- await client.createSession();
1857
-
1858
- try {
1859
- await client.executeStream(command, args);
1860
- } finally {
1861
- client.clearSession();
1862
- }
1863
- }
1864
-
1865
- // Convenience function for quick git checkout
1866
- export async function quickGitCheckout(
1867
- repoUrl: string,
1868
- branch: string = "main",
1869
- targetDir?: string,
1870
- options?: HttpClientOptions
1871
- ): Promise<GitCheckoutResponse> {
1872
- const client = createClient(options);
1873
- await client.createSession();
1874
-
1875
- try {
1876
- return await client.gitCheckout(repoUrl, branch, targetDir);
1877
- } finally {
1878
- client.clearSession();
1879
- }
1880
- }
1881
-
1882
- // Convenience function for quick directory creation
1883
- export async function quickMkdir(
1884
- path: string,
1885
- recursive: boolean = false,
1886
- options?: HttpClientOptions
1887
- ): Promise<MkdirResponse> {
1888
- const client = createClient(options);
1889
- await client.createSession();
1890
-
1891
- try {
1892
- return await client.mkdir(path, recursive);
1893
- } finally {
1894
- client.clearSession();
1895
- }
1896
- }
1897
-
1898
- // Convenience function for quick streaming git checkout
1899
- export async function quickGitCheckoutStream(
1900
- repoUrl: string,
1901
- branch: string = "main",
1902
- targetDir?: string,
1903
- options?: HttpClientOptions
1904
- ): Promise<void> {
1905
- const client = createClient(options);
1906
- await client.createSession();
1907
-
1908
- try {
1909
- await client.gitCheckoutStream(repoUrl, branch, targetDir);
1910
- } finally {
1911
- client.clearSession();
1912
- }
1913
- }
1914
-
1915
- // Convenience function for quick streaming directory creation
1916
- export async function quickMkdirStream(
1917
- path: string,
1918
- recursive: boolean = false,
1919
- options?: HttpClientOptions
1920
- ): Promise<void> {
1921
- const client = createClient(options);
1922
- await client.createSession();
1923
-
1924
- try {
1925
- await client.mkdirStream(path, recursive);
1926
- } finally {
1927
- client.clearSession();
1928
- }
1929
- }
1930
-
1931
- // Convenience function for quick file writing
1932
- export async function quickWriteFile(
1933
- path: string,
1934
- content: string,
1935
- encoding: string = "utf-8",
1936
- options?: HttpClientOptions
1937
- ): Promise<WriteFileResponse> {
1938
- const client = createClient(options);
1939
- await client.createSession();
1940
-
1941
- try {
1942
- return await client.writeFile(path, content, encoding);
1943
- } finally {
1944
- client.clearSession();
1945
- }
1946
- }
1947
-
1948
- // Convenience function for quick streaming file writing
1949
- export async function quickWriteFileStream(
1950
- path: string,
1951
- content: string,
1952
- encoding: string = "utf-8",
1953
- options?: HttpClientOptions
1954
- ): Promise<void> {
1955
- const client = createClient(options);
1956
- await client.createSession();
1957
-
1958
- try {
1959
- await client.writeFileStream(path, content, encoding);
1960
- } finally {
1961
- client.clearSession();
1962
- }
1963
- }
1964
-
1965
- // Convenience function for quick file reading
1966
- export async function quickReadFile(
1967
- path: string,
1968
- encoding: string = "utf-8",
1969
- options?: HttpClientOptions
1970
- ): Promise<ReadFileResponse> {
1971
- const client = createClient(options);
1972
- await client.createSession();
1973
-
1974
- try {
1975
- return await client.readFile(path, encoding);
1976
- } finally {
1977
- client.clearSession();
1978
- }
1979
- }
1980
-
1981
- // Convenience function for quick streaming file reading
1982
- export async function quickReadFileStream(
1983
- path: string,
1984
- encoding: string = "utf-8",
1985
- options?: HttpClientOptions
1986
- ): Promise<void> {
1987
- const client = createClient(options);
1988
- await client.createSession();
1989
-
1990
- try {
1991
- await client.readFileStream(path, encoding);
1992
- } finally {
1993
- client.clearSession();
1994
- }
1995
- }
1996
-
1997
- // Convenience function for quick file deletion
1998
- export async function quickDeleteFile(
1999
- path: string,
2000
- options?: HttpClientOptions
2001
- ): Promise<DeleteFileResponse> {
2002
- const client = createClient(options);
2003
- await client.createSession();
2004
-
2005
- try {
2006
- return await client.deleteFile(path);
2007
- } finally {
2008
- client.clearSession();
2009
- }
2010
- }
2011
-
2012
- // Convenience function for quick streaming file deletion
2013
- export async function quickDeleteFileStream(
2014
- path: string,
2015
- options?: HttpClientOptions
2016
- ): Promise<void> {
2017
- const client = createClient(options);
2018
- await client.createSession();
2019
-
2020
- try {
2021
- await client.deleteFileStream(path);
2022
- } finally {
2023
- client.clearSession();
2024
- }
2025
- }
2026
-
2027
- // Convenience function for quick file renaming
2028
- export async function quickRenameFile(
2029
- oldPath: string,
2030
- newPath: string,
2031
- options?: HttpClientOptions
2032
- ): Promise<RenameFileResponse> {
2033
- const client = createClient(options);
2034
- await client.createSession();
2035
-
2036
- try {
2037
- return await client.renameFile(oldPath, newPath);
2038
- } finally {
2039
- client.clearSession();
2040
- }
2041
- }
2042
-
2043
- // Convenience function for quick streaming file renaming
2044
- export async function quickRenameFileStream(
2045
- oldPath: string,
2046
- newPath: string,
2047
- options?: HttpClientOptions
2048
- ): Promise<void> {
2049
- const client = createClient(options);
2050
- await client.createSession();
2051
-
2052
- try {
2053
- await client.renameFileStream(oldPath, newPath);
2054
- } finally {
2055
- client.clearSession();
2056
- }
2057
- }
2058
-
2059
- // Convenience function for quick file moving
2060
- export async function quickMoveFile(
2061
- sourcePath: string,
2062
- destinationPath: string,
2063
- options?: HttpClientOptions
2064
- ): Promise<MoveFileResponse> {
2065
- const client = createClient(options);
2066
- await client.createSession();
2067
-
2068
- try {
2069
- return await client.moveFile(sourcePath, destinationPath);
2070
- } finally {
2071
- client.clearSession();
2072
- }
2073
- }
2074
-
2075
- // Convenience function for quick streaming file moving
2076
- export async function quickMoveFileStream(
2077
- sourcePath: string,
2078
- destinationPath: string,
2079
- options?: HttpClientOptions
2080
- ): Promise<void> {
2081
- const client = createClient(options);
2082
- await client.createSession();
2083
-
2084
- try {
2085
- await client.moveFileStream(sourcePath, destinationPath);
2086
- } finally {
2087
- client.clearSession();
2088
- }
2089
1021
  }