@cloudflare/sandbox 0.0.0-cbb7fcd → 0.0.0-cecde0a

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