@cloudflare/sandbox 0.0.0-f5fcd52 → 0.0.0-fd5ec7f

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,7 +15,6 @@ export interface ExecuteResponse {
13
15
  stderr: string;
14
16
  exitCode: number;
15
17
  command: string;
16
- args: string[];
17
18
  timestamp: string;
18
19
  }
19
20
 
@@ -176,32 +177,11 @@ interface PingResponse {
176
177
  timestamp: string;
177
178
  }
178
179
 
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
180
  interface HttpClientOptions {
201
181
  stub?: Sandbox;
202
182
  baseUrl?: string;
203
183
  port?: number;
204
- onCommandStart?: (command: string, args: string[]) => void;
184
+ onCommandStart?: (command: string) => void;
205
185
  onOutput?: (
206
186
  stream: "stdout" | "stderr",
207
187
  data: string,
@@ -212,11 +192,9 @@ interface HttpClientOptions {
212
192
  exitCode: number,
213
193
  stdout: string,
214
194
  stderr: string,
215
- command: string,
216
- args: string[]
195
+ command: string
217
196
  ) => void;
218
- onError?: (error: string, command?: string, args?: string[]) => void;
219
- onStreamEvent?: (event: StreamEvent) => void;
197
+ onError?: (error: string, command?: string) => void;
220
198
  }
221
199
 
222
200
  export class HttpClient {
@@ -271,119 +249,22 @@ export class HttpClient {
271
249
  throw error;
272
250
  }
273
251
  }
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
252
 
371
253
  async execute(
372
254
  command: string,
373
- args: string[] = [],
374
- sessionId?: string,
375
- background: boolean = false,
255
+ options: Pick<BaseExecOptions, 'sessionId' | 'cwd' | 'env'>
376
256
  ): Promise<ExecuteResponse> {
377
257
  try {
378
- const targetSessionId = sessionId || this.sessionId;
258
+ const targetSessionId = options.sessionId || this.sessionId;
259
+ const executeRequest = {
260
+ command,
261
+ sessionId: targetSessionId,
262
+ cwd: options.cwd,
263
+ env: options.env,
264
+ } satisfies ExecuteRequest;
379
265
 
380
266
  const response = await this.doFetch(`/api/execute`, {
381
- body: JSON.stringify({
382
- args,
383
- command,
384
- background,
385
- sessionId: targetSessionId,
386
- } as ExecuteRequest),
267
+ body: JSON.stringify(executeRequest),
387
268
  headers: {
388
269
  "Content-Type": "application/json",
389
270
  },
@@ -410,8 +291,7 @@ export class HttpClient {
410
291
  data.exitCode,
411
292
  data.stdout,
412
293
  data.stderr,
413
- data.command,
414
- data.args
294
+ data.command
415
295
  );
416
296
 
417
297
  return data;
@@ -419,31 +299,28 @@ export class HttpClient {
419
299
  console.error("[HTTP Client] Error executing command:", error);
420
300
  this.options.onError?.(
421
301
  error instanceof Error ? error.message : "Unknown error",
422
- command,
423
- args
302
+ command
424
303
  );
425
304
  throw error;
426
305
  }
427
306
  }
428
307
 
429
- async executeStream(
308
+
309
+ async executeCommandStream(
430
310
  command: string,
431
- args: string[] = [],
432
- sessionId?: string,
433
- background: boolean = false
434
- ): Promise<void> {
311
+ sessionId?: string
312
+ ): Promise<ReadableStream<Uint8Array>> {
435
313
  try {
436
314
  const targetSessionId = sessionId || this.sessionId;
437
315
 
438
316
  const response = await this.doFetch(`/api/execute/stream`, {
439
317
  body: JSON.stringify({
440
- args,
441
318
  command,
442
- background,
443
319
  sessionId: targetSessionId,
444
320
  }),
445
321
  headers: {
446
322
  "Content-Type": "application/json",
323
+ "Accept": "text/event-stream",
447
324
  },
448
325
  method: "POST",
449
326
  });
@@ -461,94 +338,13 @@ export class HttpClient {
461
338
  throw new Error("No response body for streaming request");
462
339
  }
463
340
 
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
- }
545
- } 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
341
+ console.log(
342
+ `[HTTP Client] Started command stream: ${command}`
551
343
  );
344
+
345
+ return response.body;
346
+ } catch (error) {
347
+ console.error("[HTTP Client] Error in command stream:", error);
552
348
  throw error;
553
349
  }
554
350
  }
@@ -596,134 +392,6 @@ export class HttpClient {
596
392
  }
597
393
  }
598
394
 
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
395
 
728
396
  async mkdir(
729
397
  path: string,
@@ -766,128 +434,6 @@ export class HttpClient {
766
434
  }
767
435
  }
768
436
 
769
- async mkdirStream(
770
- path: string,
771
- recursive: boolean = false,
772
- sessionId?: string
773
- ): Promise<void> {
774
- try {
775
- const targetSessionId = sessionId || this.sessionId;
776
-
777
- const response = await this.doFetch(`/api/mkdir/stream`, {
778
- body: JSON.stringify({
779
- path,
780
- recursive,
781
- sessionId: targetSessionId,
782
- } as MkdirRequest),
783
- headers: {
784
- "Content-Type": "application/json",
785
- },
786
- method: "POST",
787
- });
788
-
789
- if (!response.ok) {
790
- const errorData = (await response.json().catch(() => ({}))) as {
791
- error?: string;
792
- };
793
- throw new Error(
794
- errorData.error || `HTTP error! status: ${response.status}`
795
- );
796
- }
797
-
798
- if (!response.body) {
799
- throw new Error("No response body for streaming request");
800
- }
801
-
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
- }
881
- } 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
- );
888
- throw error;
889
- }
890
- }
891
437
 
892
438
  async writeFile(
893
439
  path: string,
@@ -932,22 +478,21 @@ export class HttpClient {
932
478
  }
933
479
  }
934
480
 
935
- async writeFileStream(
481
+
482
+ async readFile(
936
483
  path: string,
937
- content: string,
938
484
  encoding: string = "utf-8",
939
485
  sessionId?: string
940
- ): Promise<void> {
486
+ ): Promise<ReadFileResponse> {
941
487
  try {
942
488
  const targetSessionId = sessionId || this.sessionId;
943
489
 
944
- const response = await this.doFetch(`/api/write/stream`, {
490
+ const response = await this.doFetch(`/api/read`, {
945
491
  body: JSON.stringify({
946
- content,
947
492
  encoding,
948
493
  path,
949
494
  sessionId: targetSessionId,
950
- } as WriteFileRequest),
495
+ } as ReadFileRequest),
951
496
  headers: {
952
497
  "Content-Type": "application/json",
953
498
  },
@@ -963,114 +508,31 @@ export class HttpClient {
963
508
  );
964
509
  }
965
510
 
966
- if (!response.body) {
967
- throw new Error("No response body for streaming request");
968
- }
511
+ const data: ReadFileResponse = await response.json();
512
+ console.log(
513
+ `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
514
+ );
969
515
 
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
- }
516
+ return data;
1049
517
  } 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
- );
518
+ console.error("[HTTP Client] Error reading file:", error);
1056
519
  throw error;
1057
520
  }
1058
521
  }
1059
522
 
1060
- async readFile(
523
+
524
+ async deleteFile(
1061
525
  path: string,
1062
- encoding: string = "utf-8",
1063
526
  sessionId?: string
1064
- ): Promise<ReadFileResponse> {
527
+ ): Promise<DeleteFileResponse> {
1065
528
  try {
1066
529
  const targetSessionId = sessionId || this.sessionId;
1067
530
 
1068
- const response = await this.doFetch(`/api/read`, {
531
+ const response = await this.doFetch(`/api/delete`, {
1069
532
  body: JSON.stringify({
1070
- encoding,
1071
533
  path,
1072
534
  sessionId: targetSessionId,
1073
- } as ReadFileRequest),
535
+ } as DeleteFileRequest),
1074
536
  headers: {
1075
537
  "Content-Type": "application/json",
1076
538
  },
@@ -1086,32 +548,33 @@ export class HttpClient {
1086
548
  );
1087
549
  }
1088
550
 
1089
- const data: ReadFileResponse = await response.json();
551
+ const data: DeleteFileResponse = await response.json();
1090
552
  console.log(
1091
- `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
553
+ `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
1092
554
  );
1093
555
 
1094
556
  return data;
1095
557
  } catch (error) {
1096
- console.error("[HTTP Client] Error reading file:", error);
558
+ console.error("[HTTP Client] Error deleting file:", error);
1097
559
  throw error;
1098
560
  }
1099
561
  }
1100
562
 
1101
- async readFileStream(
1102
- path: string,
1103
- encoding: string = "utf-8",
563
+
564
+ async renameFile(
565
+ oldPath: string,
566
+ newPath: string,
1104
567
  sessionId?: string
1105
- ): Promise<void> {
568
+ ): Promise<RenameFileResponse> {
1106
569
  try {
1107
570
  const targetSessionId = sessionId || this.sessionId;
1108
571
 
1109
- const response = await this.doFetch(`/api/read/stream`, {
572
+ const response = await this.doFetch(`/api/rename`, {
1110
573
  body: JSON.stringify({
1111
- encoding,
1112
- path,
574
+ newPath,
575
+ oldPath,
1113
576
  sessionId: targetSessionId,
1114
- } as ReadFileRequest),
577
+ } as RenameFileRequest),
1115
578
  headers: {
1116
579
  "Content-Type": "application/json",
1117
580
  },
@@ -1127,104 +590,33 @@ export class HttpClient {
1127
590
  );
1128
591
  }
1129
592
 
1130
- if (!response.body) {
1131
- throw new Error("No response body for streaming request");
1132
- }
593
+ const data: RenameFileResponse = await response.json();
594
+ console.log(
595
+ `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
596
+ );
1133
597
 
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
- }
598
+ return data;
1205
599
  } 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
- );
600
+ console.error("[HTTP Client] Error renaming file:", error);
1212
601
  throw error;
1213
602
  }
1214
603
  }
1215
604
 
1216
- async deleteFile(
1217
- path: string,
605
+
606
+ async moveFile(
607
+ sourcePath: string,
608
+ destinationPath: string,
1218
609
  sessionId?: string
1219
- ): Promise<DeleteFileResponse> {
610
+ ): Promise<MoveFileResponse> {
1220
611
  try {
1221
612
  const targetSessionId = sessionId || this.sessionId;
1222
613
 
1223
- const response = await this.doFetch(`/api/delete`, {
614
+ const response = await this.doFetch(`/api/move`, {
1224
615
  body: JSON.stringify({
1225
- path,
616
+ destinationPath,
1226
617
  sessionId: targetSessionId,
1227
- } as DeleteFileRequest),
618
+ sourcePath,
619
+ } as MoveFileRequest),
1228
620
  headers: {
1229
621
  "Content-Type": "application/json",
1230
622
  },
@@ -1240,27 +632,26 @@ export class HttpClient {
1240
632
  );
1241
633
  }
1242
634
 
1243
- const data: DeleteFileResponse = await response.json();
635
+ const data: MoveFileResponse = await response.json();
1244
636
  console.log(
1245
- `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
637
+ `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
1246
638
  );
1247
639
 
1248
640
  return data;
1249
641
  } catch (error) {
1250
- console.error("[HTTP Client] Error deleting file:", error);
642
+ console.error("[HTTP Client] Error moving file:", error);
1251
643
  throw error;
1252
644
  }
1253
645
  }
1254
646
 
1255
- async deleteFileStream(path: string, sessionId?: string): Promise<void> {
1256
- try {
1257
- const targetSessionId = sessionId || this.sessionId;
1258
647
 
1259
- const response = await this.doFetch(`/api/delete/stream`, {
648
+ async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
649
+ try {
650
+ const response = await this.doFetch(`/api/expose-port`, {
1260
651
  body: JSON.stringify({
1261
- path,
1262
- sessionId: targetSessionId,
1263
- } as DeleteFileRequest),
652
+ port,
653
+ name,
654
+ }),
1264
655
  headers: {
1265
656
  "Content-Type": "application/json",
1266
657
  },
@@ -1271,110 +662,34 @@ export class HttpClient {
1271
662
  const errorData = (await response.json().catch(() => ({}))) as {
1272
663
  error?: string;
1273
664
  };
665
+ console.log(errorData);
1274
666
  throw new Error(
1275
667
  errorData.error || `HTTP error! status: ${response.status}`
1276
668
  );
1277
669
  }
1278
670
 
1279
- if (!response.body) {
1280
- throw new Error("No response body for streaming request");
1281
- }
671
+ const data: ExposePortResponse = await response.json();
672
+ console.log(
673
+ `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : ""}, Success: ${data.success}`
674
+ );
1282
675
 
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
- }
676
+ return data;
1349
677
  } 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
- );
678
+ console.error("[HTTP Client] Error exposing port:", error);
1356
679
  throw error;
1357
680
  }
1358
681
  }
1359
682
 
1360
- async renameFile(
1361
- oldPath: string,
1362
- newPath: string,
1363
- sessionId?: string
1364
- ): Promise<RenameFileResponse> {
683
+ async unexposePort(port: number): Promise<UnexposePortResponse> {
1365
684
  try {
1366
- const targetSessionId = sessionId || this.sessionId;
1367
-
1368
- const response = await this.doFetch(`/api/rename`, {
685
+ const response = await this.doFetch(`/api/unexpose-port`, {
1369
686
  body: JSON.stringify({
1370
- newPath,
1371
- oldPath,
1372
- sessionId: targetSessionId,
1373
- } as RenameFileRequest),
687
+ port,
688
+ }),
1374
689
  headers: {
1375
690
  "Content-Type": "application/json",
1376
691
  },
1377
- method: "POST",
692
+ method: "DELETE",
1378
693
  });
1379
694
 
1380
695
  if (!response.ok) {
@@ -1386,36 +701,25 @@ export class HttpClient {
1386
701
  );
1387
702
  }
1388
703
 
1389
- const data: RenameFileResponse = await response.json();
704
+ const data: UnexposePortResponse = await response.json();
1390
705
  console.log(
1391
- `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
706
+ `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
1392
707
  );
1393
708
 
1394
709
  return data;
1395
710
  } catch (error) {
1396
- console.error("[HTTP Client] Error renaming file:", error);
711
+ console.error("[HTTP Client] Error unexposing port:", error);
1397
712
  throw error;
1398
713
  }
1399
714
  }
1400
715
 
1401
- async renameFileStream(
1402
- oldPath: string,
1403
- newPath: string,
1404
- sessionId?: string
1405
- ): Promise<void> {
716
+ async getExposedPorts(): Promise<GetExposedPortsResponse> {
1406
717
  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),
718
+ const response = await this.doFetch(`/api/exposed-ports`, {
1415
719
  headers: {
1416
720
  "Content-Type": "application/json",
1417
721
  },
1418
- method: "POST",
722
+ method: "GET",
1419
723
  });
1420
724
 
1421
725
  if (!response.ok) {
@@ -1427,104 +731,100 @@ export class HttpClient {
1427
731
  );
1428
732
  }
1429
733
 
1430
- if (!response.body) {
1431
- throw new Error("No response body for streaming request");
1432
- }
734
+ const data: GetExposedPortsResponse = await response.json();
735
+ console.log(
736
+ `[HTTP Client] Got ${data.count} exposed ports`
737
+ );
738
+
739
+ return data;
740
+ } catch (error) {
741
+ console.error("[HTTP Client] Error getting exposed ports:", error);
742
+ throw error;
743
+ }
744
+ }
1433
745
 
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();
746
+ async ping(): Promise<string> {
747
+ try {
748
+ const response = await this.doFetch(`/api/ping`, {
749
+ headers: {
750
+ "Content-Type": "application/json",
751
+ },
752
+ method: "GET",
753
+ });
754
+
755
+ if (!response.ok) {
756
+ throw new Error(`HTTP error! status: ${response.status}`);
1502
757
  }
758
+
759
+ const data: PingResponse = await response.json();
760
+ console.log(`[HTTP Client] Ping response: ${data.message}`);
761
+ return data.timestamp;
1503
762
  } 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]
763
+ console.error("[HTTP Client] Error pinging server:", error);
764
+ throw error;
765
+ }
766
+ }
767
+
768
+ async getCommands(): Promise<string[]> {
769
+ try {
770
+ const response = await fetch(`${this.baseUrl}/api/commands`, {
771
+ headers: {
772
+ "Content-Type": "application/json",
773
+ },
774
+ method: "GET",
775
+ });
776
+
777
+ if (!response.ok) {
778
+ throw new Error(`HTTP error! status: ${response.status}`);
779
+ }
780
+
781
+ const data: CommandsResponse = await response.json();
782
+ console.log(
783
+ `[HTTP Client] Available commands: ${data.availableCommands.length}`
1509
784
  );
785
+ return data.availableCommands;
786
+ } catch (error) {
787
+ console.error("[HTTP Client] Error getting commands:", error);
1510
788
  throw error;
1511
789
  }
1512
790
  }
1513
791
 
1514
- async moveFile(
1515
- sourcePath: string,
1516
- destinationPath: string,
1517
- sessionId?: string
1518
- ): Promise<MoveFileResponse> {
792
+ getSessionId(): string | null {
793
+ return this.sessionId;
794
+ }
795
+
796
+ setSessionId(sessionId: string): void {
797
+ this.sessionId = sessionId;
798
+ }
799
+
800
+ clearSession(): void {
801
+ this.sessionId = null;
802
+ }
803
+
804
+ // Process management methods
805
+ async startProcess(
806
+ command: string,
807
+ options?: {
808
+ processId?: string;
809
+ sessionId?: string;
810
+ timeout?: number;
811
+ env?: Record<string, string>;
812
+ cwd?: string;
813
+ encoding?: string;
814
+ autoCleanup?: boolean;
815
+ }
816
+ ): Promise<StartProcessResponse> {
1519
817
  try {
1520
- const targetSessionId = sessionId || this.sessionId;
818
+ const targetSessionId = options?.sessionId || this.sessionId;
1521
819
 
1522
- const response = await this.doFetch(`/api/move`, {
820
+ const response = await this.doFetch("/api/process/start", {
1523
821
  body: JSON.stringify({
1524
- destinationPath,
1525
- sessionId: targetSessionId,
1526
- sourcePath,
1527
- } as MoveFileRequest),
822
+ command,
823
+ options: {
824
+ ...options,
825
+ sessionId: targetSessionId,
826
+ },
827
+ } as StartProcessRequest),
1528
828
  headers: {
1529
829
  "Content-Type": "application/json",
1530
830
  },
@@ -1540,36 +840,25 @@ export class HttpClient {
1540
840
  );
1541
841
  }
1542
842
 
1543
- const data: MoveFileResponse = await response.json();
843
+ const data: StartProcessResponse = await response.json();
1544
844
  console.log(
1545
- `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
845
+ `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
1546
846
  );
1547
847
 
1548
848
  return data;
1549
849
  } catch (error) {
1550
- console.error("[HTTP Client] Error moving file:", error);
850
+ console.error("[HTTP Client] Error starting process:", error);
1551
851
  throw error;
1552
852
  }
1553
853
  }
1554
854
 
1555
- async moveFileStream(
1556
- sourcePath: string,
1557
- destinationPath: string,
1558
- sessionId?: string
1559
- ): Promise<void> {
855
+ async listProcesses(): Promise<ListProcessesResponse> {
1560
856
  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),
857
+ const response = await this.doFetch("/api/process/list", {
1569
858
  headers: {
1570
859
  "Content-Type": "application/json",
1571
860
  },
1572
- method: "POST",
861
+ method: "GET",
1573
862
  });
1574
863
 
1575
864
  if (!response.ok) {
@@ -1581,134 +870,51 @@ export class HttpClient {
1581
870
  );
1582
871
  }
1583
872
 
1584
- if (!response.body) {
1585
- throw new Error("No response body for streaming request");
1586
- }
873
+ const data: ListProcessesResponse = await response.json();
874
+ console.log(
875
+ `[HTTP Client] Listed ${data.processes.length} processes`
876
+ );
1587
877
 
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
- }
878
+ return data;
1660
879
  } 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
- );
880
+ console.error("[HTTP Client] Error listing processes:", error);
1667
881
  throw error;
1668
882
  }
1669
883
  }
1670
884
 
1671
- async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
885
+ async getProcess(processId: string): Promise<GetProcessResponse> {
1672
886
  try {
1673
- const response = await this.doFetch(`/api/expose-port`, {
1674
- body: JSON.stringify({
1675
- port,
1676
- name,
1677
- }),
887
+ const response = await this.doFetch(`/api/process/${processId}`, {
1678
888
  headers: {
1679
889
  "Content-Type": "application/json",
1680
890
  },
1681
- method: "POST",
891
+ method: "GET",
1682
892
  });
1683
893
 
1684
894
  if (!response.ok) {
1685
895
  const errorData = (await response.json().catch(() => ({}))) as {
1686
896
  error?: string;
1687
897
  };
1688
- console.log(errorData);
1689
898
  throw new Error(
1690
899
  errorData.error || `HTTP error! status: ${response.status}`
1691
900
  );
1692
901
  }
1693
902
 
1694
- const data: ExposePortResponse = await response.json();
903
+ const data: GetProcessResponse = await response.json();
1695
904
  console.log(
1696
- `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : ""}, Success: ${data.success}`
905
+ `[HTTP Client] Got process ${processId}: ${data.process?.status || 'not found'}`
1697
906
  );
1698
907
 
1699
908
  return data;
1700
909
  } catch (error) {
1701
- console.error("[HTTP Client] Error exposing port:", error);
910
+ console.error("[HTTP Client] Error getting process:", error);
1702
911
  throw error;
1703
912
  }
1704
913
  }
1705
914
 
1706
- async unexposePort(port: number): Promise<UnexposePortResponse> {
915
+ async killProcess(processId: string): Promise<{ success: boolean; message: string }> {
1707
916
  try {
1708
- const response = await this.doFetch(`/api/unexpose-port`, {
1709
- body: JSON.stringify({
1710
- port,
1711
- }),
917
+ const response = await this.doFetch(`/api/process/${processId}`, {
1712
918
  headers: {
1713
919
  "Content-Type": "application/json",
1714
920
  },
@@ -1724,25 +930,25 @@ export class HttpClient {
1724
930
  );
1725
931
  }
1726
932
 
1727
- const data: UnexposePortResponse = await response.json();
933
+ const data = await response.json() as { success: boolean; message: string };
1728
934
  console.log(
1729
- `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
935
+ `[HTTP Client] Killed process ${processId}`
1730
936
  );
1731
937
 
1732
938
  return data;
1733
939
  } catch (error) {
1734
- console.error("[HTTP Client] Error unexposing port:", error);
940
+ console.error("[HTTP Client] Error killing process:", error);
1735
941
  throw error;
1736
942
  }
1737
943
  }
1738
944
 
1739
- async getExposedPorts(): Promise<GetExposedPortsResponse> {
945
+ async killAllProcesses(): Promise<{ success: boolean; killedCount: number; message: string }> {
1740
946
  try {
1741
- const response = await this.doFetch(`/api/exposed-ports`, {
947
+ const response = await this.doFetch("/api/process/kill-all", {
1742
948
  headers: {
1743
949
  "Content-Type": "application/json",
1744
950
  },
1745
- method: "GET",
951
+ method: "DELETE",
1746
952
  });
1747
953
 
1748
954
  if (!response.ok) {
@@ -1754,21 +960,21 @@ export class HttpClient {
1754
960
  );
1755
961
  }
1756
962
 
1757
- const data: GetExposedPortsResponse = await response.json();
963
+ const data = await response.json() as { success: boolean; killedCount: number; message: string };
1758
964
  console.log(
1759
- `[HTTP Client] Got ${data.count} exposed ports`
965
+ `[HTTP Client] Killed ${data.killedCount} processes`
1760
966
  );
1761
967
 
1762
968
  return data;
1763
969
  } catch (error) {
1764
- console.error("[HTTP Client] Error getting exposed ports:", error);
970
+ console.error("[HTTP Client] Error killing all processes:", error);
1765
971
  throw error;
1766
972
  }
1767
973
  }
1768
974
 
1769
- async ping(): Promise<string> {
975
+ async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
1770
976
  try {
1771
- const response = await this.doFetch(`/api/ping`, {
977
+ const response = await this.doFetch(`/api/process/${processId}/logs`, {
1772
978
  headers: {
1773
979
  "Content-Type": "application/json",
1774
980
  },
@@ -1776,314 +982,57 @@ export class HttpClient {
1776
982
  });
1777
983
 
1778
984
  if (!response.ok) {
1779
- throw new Error(`HTTP error! status: ${response.status}`);
985
+ const errorData = (await response.json().catch(() => ({}))) as {
986
+ error?: string;
987
+ };
988
+ throw new Error(
989
+ errorData.error || `HTTP error! status: ${response.status}`
990
+ );
1780
991
  }
1781
992
 
1782
- const data: PingResponse = await response.json();
1783
- console.log(`[HTTP Client] Ping response: ${data.message}`);
1784
- return data.timestamp;
993
+ const data: GetProcessLogsResponse = await response.json();
994
+ console.log(
995
+ `[HTTP Client] Got logs for process ${processId}`
996
+ );
997
+
998
+ return data;
1785
999
  } catch (error) {
1786
- console.error("[HTTP Client] Error pinging server:", error);
1000
+ console.error("[HTTP Client] Error getting process logs:", error);
1787
1001
  throw error;
1788
1002
  }
1789
1003
  }
1790
1004
 
1791
- async getCommands(): Promise<string[]> {
1005
+ async streamProcessLogs(processId: string): Promise<ReadableStream<Uint8Array>> {
1792
1006
  try {
1793
- const response = await fetch(`${this.baseUrl}/api/commands`, {
1007
+ const response = await this.doFetch(`/api/process/${processId}/stream`, {
1794
1008
  headers: {
1795
- "Content-Type": "application/json",
1009
+ "Accept": "text/event-stream",
1010
+ "Cache-Control": "no-cache",
1796
1011
  },
1797
1012
  method: "GET",
1798
1013
  });
1799
1014
 
1800
1015
  if (!response.ok) {
1801
- throw new Error(`HTTP error! status: ${response.status}`);
1016
+ const errorData = (await response.json().catch(() => ({}))) as {
1017
+ error?: string;
1018
+ };
1019
+ throw new Error(
1020
+ errorData.error || `HTTP error! status: ${response.status}`
1021
+ );
1022
+ }
1023
+
1024
+ if (!response.body) {
1025
+ throw new Error("No response body for streaming request");
1802
1026
  }
1803
1027
 
1804
- const data: CommandsResponse = await response.json();
1805
1028
  console.log(
1806
- `[HTTP Client] Available commands: ${data.availableCommands.length}`
1029
+ `[HTTP Client] Started streaming logs for process ${processId}`
1807
1030
  );
1808
- return data.availableCommands;
1031
+
1032
+ return response.body;
1809
1033
  } catch (error) {
1810
- console.error("[HTTP Client] Error getting commands:", error);
1034
+ console.error("[HTTP Client] Error streaming process logs:", error);
1811
1035
  throw error;
1812
1036
  }
1813
1037
  }
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
1038
  }