@cloudflare/sandbox 0.0.0-18fac92 → 0.0.0-1ab3f9d

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,7 +15,6 @@ export interface ExecuteResponse {
12
15
  stderr: string;
13
16
  exitCode: number;
14
17
  command: string;
15
- args: string[];
16
18
  timestamp: string;
17
19
  }
18
20
 
@@ -139,37 +141,47 @@ export interface MoveFileResponse {
139
141
  timestamp: string;
140
142
  }
141
143
 
142
- interface PingResponse {
143
- message: string;
144
+ interface PreviewInfo {
145
+ url: string;
146
+ port: number;
147
+ name?: string;
148
+ }
149
+
150
+ interface ExposedPort extends PreviewInfo {
151
+ exposedAt: string;
152
+ timestamp: string;
153
+ }
154
+
155
+ interface ExposePortResponse {
156
+ success: boolean;
157
+ port: number;
158
+ name?: string;
159
+ exposedAt: string;
160
+ timestamp: string;
161
+ }
162
+
163
+ interface UnexposePortResponse {
164
+ success: boolean;
165
+ port: number;
166
+ timestamp: string;
167
+ }
168
+
169
+ interface GetExposedPortsResponse {
170
+ ports: ExposedPort[];
171
+ count: number;
144
172
  timestamp: string;
145
173
  }
146
174
 
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;
175
+ interface PingResponse {
176
+ message: string;
177
+ timestamp: string;
166
178
  }
167
179
 
168
180
  interface HttpClientOptions {
169
181
  stub?: Sandbox;
170
182
  baseUrl?: string;
171
183
  port?: number;
172
- onCommandStart?: (command: string, args: string[]) => void;
184
+ onCommandStart?: (command: string) => void;
173
185
  onOutput?: (
174
186
  stream: "stdout" | "stderr",
175
187
  data: string,
@@ -180,11 +192,9 @@ interface HttpClientOptions {
180
192
  exitCode: number,
181
193
  stdout: string,
182
194
  stderr: string,
183
- command: string,
184
- args: string[]
195
+ command: string
185
196
  ) => void;
186
- onError?: (error: string, command?: string, args?: string[]) => void;
187
- onStreamEvent?: (event: StreamEvent) => void;
197
+ onError?: (error: string, command?: string) => void;
188
198
  }
189
199
 
190
200
  export class HttpClient {
@@ -203,7 +213,9 @@ export class HttpClient {
203
213
  path: string,
204
214
  options?: RequestInit
205
215
  ): Promise<Response> {
206
- const url = this.options.stub ? `stub:${path}` : `${this.baseUrl}${path}`;
216
+ const url = this.options.stub
217
+ ? `http://localhost:${this.options.port}${path}`
218
+ : `${this.baseUrl}${path}`;
207
219
  const method = options?.method || "GET";
208
220
 
209
221
  console.log(`[HTTP Client] Making ${method} request to ${url}`);
@@ -212,15 +224,23 @@ export class HttpClient {
212
224
  let response: Response;
213
225
 
214
226
  if (this.options.stub) {
215
- response = await this.options.stub.containerFetch(this.baseUrl + path, options, this.options.port);
227
+ response = await this.options.stub.containerFetch(
228
+ url,
229
+ options,
230
+ this.options.port
231
+ );
216
232
  } else {
217
- response = await fetch(this.baseUrl + path, options);
233
+ response = await fetch(url, options);
218
234
  }
219
235
 
220
- console.log(`[HTTP Client] Response: ${response.status} ${response.statusText}`);
236
+ console.log(
237
+ `[HTTP Client] Response: ${response.status} ${response.statusText}`
238
+ );
221
239
 
222
240
  if (!response.ok) {
223
- console.error(`[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`);
241
+ console.error(
242
+ `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
243
+ );
224
244
  }
225
245
 
226
246
  return response;
@@ -229,117 +249,22 @@ export class HttpClient {
229
249
  throw error;
230
250
  }
231
251
  }
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
252
 
329
253
  async execute(
330
254
  command: string,
331
- args: string[] = [],
332
- sessionId?: string
255
+ options: Pick<BaseExecOptions, 'sessionId' | 'cwd' | 'env'>
333
256
  ): Promise<ExecuteResponse> {
334
257
  try {
335
- const targetSessionId = sessionId || this.sessionId;
258
+ const targetSessionId = options.sessionId || this.sessionId;
259
+ const executeRequest = {
260
+ command,
261
+ sessionId: targetSessionId,
262
+ cwd: options.cwd,
263
+ env: options.env,
264
+ } satisfies ExecuteRequest;
336
265
 
337
266
  const response = await this.doFetch(`/api/execute`, {
338
- body: JSON.stringify({
339
- args,
340
- command,
341
- sessionId: targetSessionId,
342
- }),
267
+ body: JSON.stringify(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,135 +392,6 @@ export class HttpClient {
551
392
  }
552
393
  }
553
394
 
554
- async gitCheckoutStream(
555
- repoUrl: string,
556
- branch: string = "main",
557
- targetDir?: string,
558
- sessionId?: string
559
- ): Promise<void> {
560
- try {
561
- const targetSessionId = sessionId || this.sessionId;
562
-
563
- const response = await this.doFetch(`/api/git/checkout/stream`, {
564
- body: JSON.stringify({
565
- branch,
566
- repoUrl,
567
- sessionId: targetSessionId,
568
- targetDir,
569
- }),
570
- headers: {
571
- "Content-Type": "application/json",
572
- },
573
- method: "POST",
574
- });
575
-
576
- if (!response.ok) {
577
- const errorData = (await response.json().catch(() => ({}))) as {
578
- error?: string;
579
- };
580
- throw new Error(
581
- errorData.error || `HTTP error! status: ${response.status}`
582
- );
583
- }
584
-
585
- if (!response.body) {
586
- throw new Error("No response body for streaming request");
587
- }
588
-
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
- }
673
- } 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
- );
680
- throw error;
681
- }
682
- }
683
395
 
684
396
  async mkdir(
685
397
  path: string,
@@ -694,7 +406,7 @@ export class HttpClient {
694
406
  path,
695
407
  recursive,
696
408
  sessionId: targetSessionId,
697
- }),
409
+ } as MkdirRequest),
698
410
  headers: {
699
411
  "Content-Type": "application/json",
700
412
  },
@@ -722,20 +434,23 @@ export class HttpClient {
722
434
  }
723
435
  }
724
436
 
725
- async mkdirStream(
437
+
438
+ async writeFile(
726
439
  path: string,
727
- recursive: boolean = false,
440
+ content: string,
441
+ encoding: string = "utf-8",
728
442
  sessionId?: string
729
- ): Promise<void> {
443
+ ): Promise<WriteFileResponse> {
730
444
  try {
731
445
  const targetSessionId = sessionId || this.sessionId;
732
446
 
733
- const response = await this.doFetch(`/api/mkdir/stream`, {
447
+ const response = await this.doFetch(`/api/write`, {
734
448
  body: JSON.stringify({
449
+ content,
450
+ encoding,
735
451
  path,
736
- recursive,
737
452
  sessionId: targetSessionId,
738
- }),
453
+ } as WriteFileRequest),
739
454
  headers: {
740
455
  "Content-Type": "application/json",
741
456
  },
@@ -751,117 +466,33 @@ export class HttpClient {
751
466
  );
752
467
  }
753
468
 
754
- if (!response.body) {
755
- throw new Error("No response body for streaming request");
756
- }
469
+ const data: WriteFileResponse = await response.json();
470
+ console.log(
471
+ `[HTTP Client] File written: ${path}, Success: ${data.success}`
472
+ );
757
473
 
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
- }
474
+ return data;
838
475
  } 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
- );
476
+ console.error("[HTTP Client] Error writing file:", error);
845
477
  throw error;
846
478
  }
847
479
  }
848
480
 
849
- async writeFile(
481
+
482
+ async readFile(
850
483
  path: string,
851
- content: string,
852
484
  encoding: string = "utf-8",
853
485
  sessionId?: string
854
- ): Promise<WriteFileResponse> {
486
+ ): Promise<ReadFileResponse> {
855
487
  try {
856
488
  const targetSessionId = sessionId || this.sessionId;
857
489
 
858
- const response = await this.doFetch(`/api/write`, {
490
+ const response = await this.doFetch(`/api/read`, {
859
491
  body: JSON.stringify({
860
- content,
861
492
  encoding,
862
493
  path,
863
494
  sessionId: targetSessionId,
864
- }),
495
+ } as ReadFileRequest),
865
496
  headers: {
866
497
  "Content-Type": "application/json",
867
498
  },
@@ -877,34 +508,31 @@ export class HttpClient {
877
508
  );
878
509
  }
879
510
 
880
- const data: WriteFileResponse = await response.json();
511
+ const data: ReadFileResponse = await response.json();
881
512
  console.log(
882
- `[HTTP Client] File written: ${path}, Success: ${data.success}`
513
+ `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
883
514
  );
884
515
 
885
516
  return data;
886
517
  } catch (error) {
887
- console.error("[HTTP Client] Error writing file:", error);
518
+ console.error("[HTTP Client] Error reading file:", error);
888
519
  throw error;
889
520
  }
890
521
  }
891
522
 
892
- async writeFileStream(
523
+
524
+ async deleteFile(
893
525
  path: string,
894
- content: string,
895
- encoding: string = "utf-8",
896
526
  sessionId?: string
897
- ): Promise<void> {
527
+ ): Promise<DeleteFileResponse> {
898
528
  try {
899
529
  const targetSessionId = sessionId || this.sessionId;
900
530
 
901
- const response = await this.doFetch(`/api/write/stream`, {
531
+ const response = await this.doFetch(`/api/delete`, {
902
532
  body: JSON.stringify({
903
- content,
904
- encoding,
905
533
  path,
906
534
  sessionId: targetSessionId,
907
- }),
535
+ } as DeleteFileRequest),
908
536
  headers: {
909
537
  "Content-Type": "application/json",
910
538
  },
@@ -920,114 +548,33 @@ export class HttpClient {
920
548
  );
921
549
  }
922
550
 
923
- if (!response.body) {
924
- throw new Error("No response body for streaming request");
925
- }
551
+ const data: DeleteFileResponse = await response.json();
552
+ console.log(
553
+ `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
554
+ );
926
555
 
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
- }
556
+ return data;
1006
557
  } 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
- );
558
+ console.error("[HTTP Client] Error deleting file:", error);
1013
559
  throw error;
1014
560
  }
1015
561
  }
1016
562
 
1017
- async readFile(
1018
- path: string,
1019
- encoding: string = "utf-8",
563
+
564
+ async renameFile(
565
+ oldPath: string,
566
+ newPath: string,
1020
567
  sessionId?: string
1021
- ): Promise<ReadFileResponse> {
568
+ ): Promise<RenameFileResponse> {
1022
569
  try {
1023
570
  const targetSessionId = sessionId || this.sessionId;
1024
571
 
1025
- const response = await this.doFetch(`/api/read`, {
572
+ const response = await this.doFetch(`/api/rename`, {
1026
573
  body: JSON.stringify({
1027
- encoding,
1028
- path,
574
+ newPath,
575
+ oldPath,
1029
576
  sessionId: targetSessionId,
1030
- }),
577
+ } as RenameFileRequest),
1031
578
  headers: {
1032
579
  "Content-Type": "application/json",
1033
580
  },
@@ -1043,32 +590,33 @@ export class HttpClient {
1043
590
  );
1044
591
  }
1045
592
 
1046
- const data: ReadFileResponse = await response.json();
593
+ const data: RenameFileResponse = await response.json();
1047
594
  console.log(
1048
- `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
595
+ `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
1049
596
  );
1050
597
 
1051
598
  return data;
1052
599
  } catch (error) {
1053
- console.error("[HTTP Client] Error reading file:", error);
600
+ console.error("[HTTP Client] Error renaming file:", error);
1054
601
  throw error;
1055
602
  }
1056
603
  }
1057
604
 
1058
- async readFileStream(
1059
- path: string,
1060
- encoding: string = "utf-8",
605
+
606
+ async moveFile(
607
+ sourcePath: string,
608
+ destinationPath: string,
1061
609
  sessionId?: string
1062
- ): Promise<void> {
610
+ ): Promise<MoveFileResponse> {
1063
611
  try {
1064
612
  const targetSessionId = sessionId || this.sessionId;
1065
613
 
1066
- const response = await this.doFetch(`/api/read/stream`, {
614
+ const response = await this.doFetch(`/api/move`, {
1067
615
  body: JSON.stringify({
1068
- encoding,
1069
- path,
616
+ destinationPath,
1070
617
  sessionId: targetSessionId,
1071
- }),
618
+ sourcePath,
619
+ } as MoveFileRequest),
1072
620
  headers: {
1073
621
  "Content-Type": "application/json",
1074
622
  },
@@ -1084,105 +632,25 @@ export class HttpClient {
1084
632
  );
1085
633
  }
1086
634
 
1087
- if (!response.body) {
1088
- throw new Error("No response body for streaming request");
1089
- }
635
+ const data: MoveFileResponse = await response.json();
636
+ console.log(
637
+ `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
638
+ );
1090
639
 
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
- }
640
+ return data;
1164
641
  } 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
- );
642
+ console.error("[HTTP Client] Error moving file:", error);
1171
643
  throw error;
1172
644
  }
1173
645
  }
1174
646
 
1175
- async deleteFile(
1176
- path: string,
1177
- sessionId?: string
1178
- ): Promise<DeleteFileResponse> {
1179
- try {
1180
- const targetSessionId = sessionId || this.sessionId;
1181
647
 
1182
- const response = await this.doFetch(`/api/delete`, {
648
+ async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
649
+ try {
650
+ const response = await this.doFetch(`/api/expose-port`, {
1183
651
  body: JSON.stringify({
1184
- path,
1185
- sessionId: targetSessionId,
652
+ port,
653
+ name,
1186
654
  }),
1187
655
  headers: {
1188
656
  "Content-Type": "application/json",
@@ -1194,36 +662,34 @@ export class HttpClient {
1194
662
  const errorData = (await response.json().catch(() => ({}))) as {
1195
663
  error?: string;
1196
664
  };
665
+ console.log(errorData);
1197
666
  throw new Error(
1198
667
  errorData.error || `HTTP error! status: ${response.status}`
1199
668
  );
1200
669
  }
1201
670
 
1202
- const data: DeleteFileResponse = await response.json();
671
+ const data: ExposePortResponse = await response.json();
1203
672
  console.log(
1204
- `[HTTP Client] File deleted: ${path}, Success: ${data.success}`
673
+ `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : ""}, Success: ${data.success}`
1205
674
  );
1206
675
 
1207
676
  return data;
1208
677
  } catch (error) {
1209
- console.error("[HTTP Client] Error deleting file:", error);
678
+ console.error("[HTTP Client] Error exposing port:", error);
1210
679
  throw error;
1211
680
  }
1212
681
  }
1213
682
 
1214
- async deleteFileStream(path: string, sessionId?: string): Promise<void> {
683
+ async unexposePort(port: number): Promise<UnexposePortResponse> {
1215
684
  try {
1216
- const targetSessionId = sessionId || this.sessionId;
1217
-
1218
- const response = await this.doFetch(`/api/delete/stream`, {
685
+ const response = await this.doFetch(`/api/unexpose-port`, {
1219
686
  body: JSON.stringify({
1220
- path,
1221
- sessionId: targetSessionId,
687
+ port,
1222
688
  }),
1223
689
  headers: {
1224
690
  "Content-Type": "application/json",
1225
691
  },
1226
- method: "POST",
692
+ method: "DELETE",
1227
693
  });
1228
694
 
1229
695
  if (!response.ok) {
@@ -1235,105 +701,25 @@ export class HttpClient {
1235
701
  );
1236
702
  }
1237
703
 
1238
- if (!response.body) {
1239
- throw new Error("No response body for streaming request");
1240
- }
704
+ const data: UnexposePortResponse = await response.json();
705
+ console.log(
706
+ `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
707
+ );
1241
708
 
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();
1307
- }
709
+ return data;
1308
710
  } 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]
1314
- );
711
+ console.error("[HTTP Client] Error unexposing port:", error);
1315
712
  throw error;
1316
713
  }
1317
714
  }
1318
715
 
1319
- async renameFile(
1320
- oldPath: string,
1321
- newPath: string,
1322
- sessionId?: string
1323
- ): Promise<RenameFileResponse> {
716
+ async getExposedPorts(): Promise<GetExposedPortsResponse> {
1324
717
  try {
1325
- const targetSessionId = sessionId || this.sessionId;
1326
-
1327
- const response = await this.doFetch(`/api/rename`, {
1328
- body: JSON.stringify({
1329
- newPath,
1330
- oldPath,
1331
- sessionId: targetSessionId,
1332
- }),
718
+ const response = await this.doFetch(`/api/exposed-ports`, {
1333
719
  headers: {
1334
720
  "Content-Type": "application/json",
1335
721
  },
1336
- method: "POST",
722
+ method: "GET",
1337
723
  });
1338
724
 
1339
725
  if (!response.ok) {
@@ -1345,145 +731,100 @@ export class HttpClient {
1345
731
  );
1346
732
  }
1347
733
 
1348
- const data: RenameFileResponse = await response.json();
734
+ const data: GetExposedPortsResponse = await response.json();
1349
735
  console.log(
1350
- `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
736
+ `[HTTP Client] Got ${data.count} exposed ports`
1351
737
  );
1352
738
 
1353
739
  return data;
1354
740
  } catch (error) {
1355
- console.error("[HTTP Client] Error renaming file:", error);
741
+ console.error("[HTTP Client] Error getting exposed ports:", error);
1356
742
  throw error;
1357
743
  }
1358
744
  }
1359
745
 
1360
- async renameFileStream(
1361
- oldPath: string,
1362
- newPath: string,
1363
- sessionId?: string
1364
- ): Promise<void> {
746
+ async ping(): Promise<string> {
1365
747
  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
- }),
748
+ const response = await this.doFetch(`/api/ping`, {
1374
749
  headers: {
1375
750
  "Content-Type": "application/json",
1376
751
  },
1377
- method: "POST",
752
+ method: "GET",
1378
753
  });
1379
754
 
1380
755
  if (!response.ok) {
1381
- const errorData = (await response.json().catch(() => ({}))) as {
1382
- error?: string;
1383
- };
1384
- throw new Error(
1385
- errorData.error || `HTTP error! status: ${response.status}`
1386
- );
756
+ throw new Error(`HTTP error! status: ${response.status}`);
1387
757
  }
1388
758
 
1389
- if (!response.body) {
1390
- throw new Error("No response body for streaming request");
1391
- }
759
+ const data: PingResponse = await response.json();
760
+ console.log(`[HTTP Client] Ping response: ${data.message}`);
761
+ return data.timestamp;
762
+ } catch (error) {
763
+ console.error("[HTTP Client] Error pinging server:", error);
764
+ throw error;
765
+ }
766
+ }
1392
767
 
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();
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}`);
1461
779
  }
1462
- } 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]
780
+
781
+ const data: CommandsResponse = await response.json();
782
+ console.log(
783
+ `[HTTP Client] Available commands: ${data.availableCommands.length}`
1468
784
  );
785
+ return data.availableCommands;
786
+ } catch (error) {
787
+ console.error("[HTTP Client] Error getting commands:", error);
1469
788
  throw error;
1470
789
  }
1471
790
  }
1472
791
 
1473
- async moveFile(
1474
- sourcePath: string,
1475
- destinationPath: string,
1476
- sessionId?: string
1477
- ): Promise<MoveFileResponse> {
792
+ getSessionId(): string | null {
793
+ return this.sessionId;
794
+ }
795
+
796
+ setSessionId(sessionId: string): void {
797
+ this.sessionId = sessionId;
798
+ }
799
+
800
+ clearSession(): void {
801
+ this.sessionId = null;
802
+ }
803
+
804
+ // Process management methods
805
+ async startProcess(
806
+ command: string,
807
+ options?: {
808
+ processId?: string;
809
+ sessionId?: string;
810
+ timeout?: number;
811
+ env?: Record<string, string>;
812
+ cwd?: string;
813
+ encoding?: string;
814
+ autoCleanup?: boolean;
815
+ }
816
+ ): Promise<StartProcessResponse> {
1478
817
  try {
1479
- const targetSessionId = sessionId || this.sessionId;
818
+ const targetSessionId = options?.sessionId || this.sessionId;
1480
819
 
1481
- const response = await this.doFetch(`/api/move`, {
820
+ const response = await this.doFetch("/api/process/start", {
1482
821
  body: JSON.stringify({
1483
- destinationPath,
1484
- sessionId: targetSessionId,
1485
- sourcePath,
1486
- }),
822
+ command,
823
+ options: {
824
+ ...options,
825
+ sessionId: targetSessionId,
826
+ },
827
+ } as StartProcessRequest),
1487
828
  headers: {
1488
829
  "Content-Type": "application/json",
1489
830
  },
@@ -1499,36 +840,25 @@ export class HttpClient {
1499
840
  );
1500
841
  }
1501
842
 
1502
- const data: MoveFileResponse = await response.json();
843
+ const data: StartProcessResponse = await response.json();
1503
844
  console.log(
1504
- `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
845
+ `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
1505
846
  );
1506
847
 
1507
848
  return data;
1508
849
  } catch (error) {
1509
- console.error("[HTTP Client] Error moving file:", error);
850
+ console.error("[HTTP Client] Error starting process:", error);
1510
851
  throw error;
1511
852
  }
1512
853
  }
1513
854
 
1514
- async moveFileStream(
1515
- sourcePath: string,
1516
- destinationPath: string,
1517
- sessionId?: string
1518
- ): Promise<void> {
855
+ async listProcesses(): Promise<ListProcessesResponse> {
1519
856
  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
- }),
857
+ const response = await this.doFetch("/api/process/list", {
1528
858
  headers: {
1529
859
  "Content-Type": "application/json",
1530
860
  },
1531
- method: "POST",
861
+ method: "GET",
1532
862
  });
1533
863
 
1534
864
  if (!response.ok) {
@@ -1540,96 +870,21 @@ export class HttpClient {
1540
870
  );
1541
871
  }
1542
872
 
1543
- if (!response.body) {
1544
- throw new Error("No response body for streaming request");
1545
- }
873
+ const data: ListProcessesResponse = await response.json();
874
+ console.log(
875
+ `[HTTP Client] Listed ${data.processes.length} processes`
876
+ );
1546
877
 
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
- }
878
+ return data;
1619
879
  } 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
- );
880
+ console.error("[HTTP Client] Error listing processes:", error);
1626
881
  throw error;
1627
882
  }
1628
883
  }
1629
884
 
1630
- async ping(): Promise<string> {
885
+ async getProcess(processId: string): Promise<GetProcessResponse> {
1631
886
  try {
1632
- const response = await this.doFetch(`/api/ping`, {
887
+ const response = await this.doFetch(`/api/process/${processId}`, {
1633
888
  headers: {
1634
889
  "Content-Type": "application/json",
1635
890
  },
@@ -1637,314 +892,147 @@ export class HttpClient {
1637
892
  });
1638
893
 
1639
894
  if (!response.ok) {
1640
- throw new Error(`HTTP error! status: ${response.status}`);
895
+ const errorData = (await response.json().catch(() => ({}))) as {
896
+ error?: string;
897
+ };
898
+ throw new Error(
899
+ errorData.error || `HTTP error! status: ${response.status}`
900
+ );
1641
901
  }
1642
902
 
1643
- const data: PingResponse = await response.json();
1644
- console.log(`[HTTP Client] Ping response: ${data.message}`);
1645
- return data.timestamp;
903
+ const data: GetProcessResponse = await response.json();
904
+ console.log(
905
+ `[HTTP Client] Got process ${processId}: ${data.process?.status || 'not found'}`
906
+ );
907
+
908
+ return data;
1646
909
  } catch (error) {
1647
- console.error("[HTTP Client] Error pinging server:", error);
910
+ console.error("[HTTP Client] Error getting process:", error);
1648
911
  throw error;
1649
912
  }
1650
913
  }
1651
914
 
1652
- async getCommands(): Promise<string[]> {
915
+ async killProcess(processId: string): Promise<{ success: boolean; message: string }> {
1653
916
  try {
1654
- const response = await fetch(`${this.baseUrl}/api/commands`, {
917
+ const response = await this.doFetch(`/api/process/${processId}`, {
1655
918
  headers: {
1656
919
  "Content-Type": "application/json",
1657
920
  },
1658
- method: "GET",
921
+ method: "DELETE",
1659
922
  });
1660
923
 
1661
924
  if (!response.ok) {
1662
- throw new Error(`HTTP error! status: ${response.status}`);
925
+ const errorData = (await response.json().catch(() => ({}))) as {
926
+ error?: string;
927
+ };
928
+ throw new Error(
929
+ errorData.error || `HTTP error! status: ${response.status}`
930
+ );
1663
931
  }
1664
932
 
1665
- const data: CommandsResponse = await response.json();
933
+ const data = await response.json() as { success: boolean; message: string };
1666
934
  console.log(
1667
- `[HTTP Client] Available commands: ${data.availableCommands.length}`
935
+ `[HTTP Client] Killed process ${processId}`
1668
936
  );
1669
- return data.availableCommands;
937
+
938
+ return data;
1670
939
  } catch (error) {
1671
- console.error("[HTTP Client] Error getting commands:", error);
940
+ console.error("[HTTP Client] Error killing process:", error);
1672
941
  throw error;
1673
942
  }
1674
943
  }
1675
944
 
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
- }
945
+ async killAllProcesses(): Promise<{ success: boolean; killedCount: number; message: string }> {
946
+ try {
947
+ const response = await this.doFetch("/api/process/kill-all", {
948
+ headers: {
949
+ "Content-Type": "application/json",
950
+ },
951
+ method: "DELETE",
952
+ });
1758
953
 
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
- }
954
+ if (!response.ok) {
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
+ );
961
+ }
1775
962
 
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
- }
963
+ const data = await response.json() as { success: boolean; killedCount: number; message: string };
964
+ console.log(
965
+ `[HTTP Client] Killed ${data.killedCount} processes`
966
+ );
1791
967
 
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();
968
+ return data;
969
+ } catch (error) {
970
+ console.error("[HTTP Client] Error killing all processes:", error);
971
+ throw error;
972
+ }
1806
973
  }
1807
- }
1808
974
 
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
- }
975
+ async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
976
+ try {
977
+ const response = await this.doFetch(`/api/process/${processId}/logs`, {
978
+ headers: {
979
+ "Content-Type": "application/json",
980
+ },
981
+ method: "GET",
982
+ });
1825
983
 
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
- }
984
+ if (!response.ok) {
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
+ );
991
+ }
1841
992
 
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
- }
993
+ const data: GetProcessLogsResponse = await response.json();
994
+ console.log(
995
+ `[HTTP Client] Got logs for process ${processId}`
996
+ );
1857
997
 
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();
998
+ return data;
999
+ } catch (error) {
1000
+ console.error("[HTTP Client] Error getting process logs:", error);
1001
+ throw error;
1002
+ }
1870
1003
  }
1871
- }
1872
1004
 
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
  }