@cloudflare/sandbox 0.0.0-2f85e95 → 0.0.0-3053156

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