@cloudflare/sandbox 0.0.0-6d1b288 → 0.0.0-70dcdae

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
 
@@ -85,6 +90,20 @@ export interface WriteFileResponse {
85
90
  timestamp: string;
86
91
  }
87
92
 
93
+ interface ReadFileRequest {
94
+ path: string;
95
+ encoding?: string;
96
+ sessionId?: string;
97
+ }
98
+
99
+ export interface ReadFileResponse {
100
+ success: boolean;
101
+ exitCode: number;
102
+ path: string;
103
+ content: string;
104
+ timestamp: string;
105
+ }
106
+
88
107
  interface DeleteFileRequest {
89
108
  path: string;
90
109
  sessionId?: string;
@@ -125,36 +144,47 @@ export interface MoveFileResponse {
125
144
  timestamp: string;
126
145
  }
127
146
 
128
- interface PingResponse {
129
- 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;
130
175
  timestamp: string;
131
176
  }
132
177
 
133
- interface StreamEvent {
134
- type: "command_start" | "output" | "command_complete" | "error";
135
- command?: string;
136
- args?: string[];
137
- stream?: "stdout" | "stderr";
138
- data?: string;
139
- message?: string;
140
- path?: string;
141
- oldPath?: string;
142
- newPath?: string;
143
- sourcePath?: string;
144
- destinationPath?: string;
145
- success?: boolean;
146
- exitCode?: number;
147
- stdout?: string;
148
- stderr?: string;
149
- error?: string;
150
- timestamp?: string;
178
+ interface PingResponse {
179
+ message: string;
180
+ timestamp: string;
151
181
  }
152
182
 
153
183
  interface HttpClientOptions {
154
184
  stub?: Sandbox;
155
185
  baseUrl?: string;
156
186
  port?: number;
157
- onCommandStart?: (command: string, args: string[]) => void;
187
+ onCommandStart?: (command: string) => void;
158
188
  onOutput?: (
159
189
  stream: "stdout" | "stderr",
160
190
  data: string,
@@ -165,11 +195,9 @@ interface HttpClientOptions {
165
195
  exitCode: number,
166
196
  stdout: string,
167
197
  stderr: string,
168
- command: string,
169
- args: string[]
198
+ command: string
170
199
  ) => void;
171
- onError?: (error: string, command?: string, args?: string[]) => void;
172
- onStreamEvent?: (event: StreamEvent) => void;
200
+ onError?: (error: string, command?: string) => void;
173
201
  }
174
202
 
175
203
  export class HttpClient {
@@ -188,111 +216,45 @@ export class HttpClient {
188
216
  path: string,
189
217
  options?: RequestInit
190
218
  ): Promise<Response> {
191
- if (this.options.stub) {
192
- return this.options.stub.containerFetch(path, options, this.options.port);
193
- }
194
- return fetch(this.baseUrl + path, options);
195
- }
196
- // Public methods to set event handlers
197
- setOnOutput(
198
- handler: (
199
- stream: "stdout" | "stderr",
200
- data: string,
201
- command: string
202
- ) => void
203
- ): void {
204
- this.options.onOutput = handler;
205
- }
219
+ const url = this.options.stub
220
+ ? `http://localhost:${this.options.port}${path}`
221
+ : `${this.baseUrl}${path}`;
222
+ const method = options?.method || "GET";
206
223
 
207
- setOnCommandComplete(
208
- handler: (
209
- success: boolean,
210
- exitCode: number,
211
- stdout: string,
212
- stderr: string,
213
- command: string,
214
- args: string[]
215
- ) => void
216
- ): void {
217
- this.options.onCommandComplete = handler;
218
- }
219
-
220
- setOnStreamEvent(handler: (event: StreamEvent) => void): void {
221
- this.options.onStreamEvent = handler;
222
- }
223
-
224
- // Public getter methods
225
- getOnOutput():
226
- | ((stream: "stdout" | "stderr", data: string, command: string) => void)
227
- | undefined {
228
- return this.options.onOutput;
229
- }
230
-
231
- getOnCommandComplete():
232
- | ((
233
- success: boolean,
234
- exitCode: number,
235
- stdout: string,
236
- stderr: string,
237
- command: string,
238
- args: string[]
239
- ) => void)
240
- | undefined {
241
- return this.options.onCommandComplete;
242
- }
224
+ console.log(`[HTTP Client] Making ${method} request to ${url}`);
243
225
 
244
- getOnStreamEvent(): ((event: StreamEvent) => void) | undefined {
245
- return this.options.onStreamEvent;
246
- }
247
-
248
- async createSession(): Promise<string> {
249
226
  try {
250
- const response = await this.doFetch(`/api/session/create`, {
251
- headers: {
252
- "Content-Type": "application/json",
253
- },
254
- method: "POST",
255
- });
227
+ let response: Response;
256
228
 
257
- if (!response.ok) {
258
- throw new Error(`HTTP error! status: ${response.status}`);
229
+ if (this.options.stub) {
230
+ response = await this.options.stub.containerFetch(
231
+ url,
232
+ options,
233
+ this.options.port
234
+ );
235
+ } else {
236
+ response = await fetch(url, options);
259
237
  }
260
238
 
261
- const data: SessionResponse = await response.json();
262
- this.sessionId = data.sessionId;
263
- console.log(`[HTTP Client] Created session: ${this.sessionId}`);
264
- return this.sessionId;
265
- } catch (error) {
266
- console.error("[HTTP Client] Error creating session:", error);
267
- throw error;
268
- }
269
- }
270
-
271
- async listSessions(): Promise<SessionListResponse> {
272
- try {
273
- const response = await this.doFetch(`/api/session/list`, {
274
- headers: {
275
- "Content-Type": "application/json",
276
- },
277
- method: "GET",
278
- });
239
+ console.log(
240
+ `[HTTP Client] Response: ${response.status} ${response.statusText}`
241
+ );
279
242
 
280
243
  if (!response.ok) {
281
- throw new Error(`HTTP error! status: ${response.status}`);
244
+ console.error(
245
+ `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
246
+ );
282
247
  }
283
248
 
284
- const data: SessionListResponse = await response.json();
285
- console.log(`[HTTP Client] Listed ${data.count} sessions`);
286
- return data;
249
+ return response;
287
250
  } catch (error) {
288
- console.error("[HTTP Client] Error listing sessions:", error);
251
+ console.error(`[HTTP Client] Request error: ${method} ${url}`, error);
289
252
  throw error;
290
253
  }
291
254
  }
292
255
 
293
256
  async execute(
294
257
  command: string,
295
- args: string[] = [],
296
258
  sessionId?: string
297
259
  ): Promise<ExecuteResponse> {
298
260
  try {
@@ -300,10 +262,9 @@ export class HttpClient {
300
262
 
301
263
  const response = await this.doFetch(`/api/execute`, {
302
264
  body: JSON.stringify({
303
- args,
304
265
  command,
305
266
  sessionId: targetSessionId,
306
- }),
267
+ } as ExecuteRequest),
307
268
  headers: {
308
269
  "Content-Type": "application/json",
309
270
  },
@@ -330,8 +291,7 @@ export class HttpClient {
330
291
  data.exitCode,
331
292
  data.stdout,
332
293
  data.stderr,
333
- data.command,
334
- data.args
294
+ data.command
335
295
  );
336
296
 
337
297
  return data;
@@ -339,29 +299,28 @@ export class HttpClient {
339
299
  console.error("[HTTP Client] Error executing command:", error);
340
300
  this.options.onError?.(
341
301
  error instanceof Error ? error.message : "Unknown error",
342
- command,
343
- args
302
+ command
344
303
  );
345
304
  throw error;
346
305
  }
347
306
  }
348
307
 
349
- async executeStream(
308
+
309
+ async executeCommandStream(
350
310
  command: string,
351
- args: string[] = [],
352
311
  sessionId?: string
353
- ): Promise<void> {
312
+ ): Promise<ReadableStream<Uint8Array>> {
354
313
  try {
355
314
  const targetSessionId = sessionId || this.sessionId;
356
315
 
357
316
  const response = await this.doFetch(`/api/execute/stream`, {
358
317
  body: JSON.stringify({
359
- args,
360
318
  command,
361
319
  sessionId: targetSessionId,
362
320
  }),
363
321
  headers: {
364
322
  "Content-Type": "application/json",
323
+ "Accept": "text/event-stream",
365
324
  },
366
325
  method: "POST",
367
326
  });
@@ -379,95 +338,13 @@ export class HttpClient {
379
338
  throw new Error("No response body for streaming request");
380
339
  }
381
340
 
382
- const reader = response.body.getReader();
383
- const decoder = new TextDecoder();
384
-
385
- try {
386
- while (true) {
387
- const { done, value } = await reader.read();
388
-
389
- if (done) {
390
- break;
391
- }
392
-
393
- const chunk = decoder.decode(value, { stream: true });
394
- const lines = chunk.split("\n");
395
-
396
- for (const line of lines) {
397
- if (line.startsWith("data: ")) {
398
- try {
399
- const eventData = line.slice(6); // Remove 'data: ' prefix
400
- const event: StreamEvent = JSON.parse(eventData);
401
-
402
- console.log(`[HTTP Client] Stream event: ${event.type}`);
403
- this.options.onStreamEvent?.(event);
404
-
405
- switch (event.type) {
406
- case "command_start":
407
- console.log(
408
- `[HTTP Client] Command started: ${
409
- event.command
410
- } ${event.args?.join(" ")}`
411
- );
412
- this.options.onCommandStart?.(
413
- event.command!,
414
- event.args || []
415
- );
416
- break;
417
-
418
- case "output":
419
- console.log(`[${event.stream}] ${event.data}`);
420
- this.options.onOutput?.(
421
- event.stream!,
422
- event.data!,
423
- event.command!
424
- );
425
- break;
426
-
427
- case "command_complete":
428
- console.log(
429
- `[HTTP Client] Command completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
430
- );
431
- this.options.onCommandComplete?.(
432
- event.success!,
433
- event.exitCode!,
434
- event.stdout!,
435
- event.stderr!,
436
- event.command!,
437
- event.args || []
438
- );
439
- break;
440
-
441
- case "error":
442
- console.error(
443
- `[HTTP Client] Command error: ${event.error}`
444
- );
445
- this.options.onError?.(
446
- event.error!,
447
- event.command,
448
- event.args
449
- );
450
- break;
451
- }
452
- } catch (parseError) {
453
- console.warn(
454
- "[HTTP Client] Failed to parse stream event:",
455
- parseError
456
- );
457
- }
458
- }
459
- }
460
- }
461
- } finally {
462
- reader.releaseLock();
463
- }
464
- } catch (error) {
465
- console.error("[HTTP Client] Error in streaming execution:", error);
466
- this.options.onError?.(
467
- error instanceof Error ? error.message : "Unknown error",
468
- command,
469
- args
341
+ console.log(
342
+ `[HTTP Client] Started command stream: ${command}`
470
343
  );
344
+
345
+ return response.body;
346
+ } catch (error) {
347
+ console.error("[HTTP Client] Error in command stream:", error);
471
348
  throw error;
472
349
  }
473
350
  }
@@ -487,7 +364,7 @@ export class HttpClient {
487
364
  repoUrl,
488
365
  sessionId: targetSessionId,
489
366
  targetDir,
490
- }),
367
+ } as GitCheckoutRequest),
491
368
  headers: {
492
369
  "Content-Type": "application/json",
493
370
  },
@@ -515,135 +392,6 @@ export class HttpClient {
515
392
  }
516
393
  }
517
394
 
518
- async gitCheckoutStream(
519
- repoUrl: string,
520
- branch: string = "main",
521
- targetDir?: string,
522
- sessionId?: string
523
- ): Promise<void> {
524
- try {
525
- const targetSessionId = sessionId || this.sessionId;
526
-
527
- const response = await this.doFetch(`/api/git/checkout/stream`, {
528
- body: JSON.stringify({
529
- branch,
530
- repoUrl,
531
- sessionId: targetSessionId,
532
- targetDir,
533
- }),
534
- headers: {
535
- "Content-Type": "application/json",
536
- },
537
- method: "POST",
538
- });
539
-
540
- if (!response.ok) {
541
- const errorData = (await response.json().catch(() => ({}))) as {
542
- error?: string;
543
- };
544
- throw new Error(
545
- errorData.error || `HTTP error! status: ${response.status}`
546
- );
547
- }
548
-
549
- if (!response.body) {
550
- throw new Error("No response body for streaming request");
551
- }
552
-
553
- const reader = response.body.getReader();
554
- const decoder = new TextDecoder();
555
-
556
- try {
557
- while (true) {
558
- const { done, value } = await reader.read();
559
-
560
- if (done) {
561
- break;
562
- }
563
-
564
- const chunk = decoder.decode(value, { stream: true });
565
- const lines = chunk.split("\n");
566
-
567
- for (const line of lines) {
568
- if (line.startsWith("data: ")) {
569
- try {
570
- const eventData = line.slice(6); // Remove 'data: ' prefix
571
- const event: StreamEvent = JSON.parse(eventData);
572
-
573
- console.log(
574
- `[HTTP Client] Git checkout stream event: ${event.type}`
575
- );
576
- this.options.onStreamEvent?.(event);
577
-
578
- switch (event.type) {
579
- case "command_start":
580
- console.log(
581
- `[HTTP Client] Git checkout started: ${
582
- event.command
583
- } ${event.args?.join(" ")}`
584
- );
585
- this.options.onCommandStart?.(
586
- event.command!,
587
- event.args || []
588
- );
589
- break;
590
-
591
- case "output":
592
- console.log(`[${event.stream}] ${event.data}`);
593
- this.options.onOutput?.(
594
- event.stream!,
595
- event.data!,
596
- event.command!
597
- );
598
- break;
599
-
600
- case "command_complete":
601
- console.log(
602
- `[HTTP Client] Git checkout completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
603
- );
604
- this.options.onCommandComplete?.(
605
- event.success!,
606
- event.exitCode!,
607
- event.stdout!,
608
- event.stderr!,
609
- event.command!,
610
- event.args || []
611
- );
612
- break;
613
-
614
- case "error":
615
- console.error(
616
- `[HTTP Client] Git checkout error: ${event.error}`
617
- );
618
- this.options.onError?.(
619
- event.error!,
620
- event.command,
621
- event.args
622
- );
623
- break;
624
- }
625
- } catch (parseError) {
626
- console.warn(
627
- "[HTTP Client] Failed to parse git checkout stream event:",
628
- parseError
629
- );
630
- }
631
- }
632
- }
633
- }
634
- } finally {
635
- reader.releaseLock();
636
- }
637
- } catch (error) {
638
- console.error("[HTTP Client] Error in streaming git checkout:", error);
639
- this.options.onError?.(
640
- error instanceof Error ? error.message : "Unknown error",
641
- "git clone",
642
- [branch, repoUrl, targetDir || ""]
643
- );
644
- throw error;
645
- }
646
- }
647
395
 
648
396
  async mkdir(
649
397
  path: string,
@@ -658,7 +406,7 @@ export class HttpClient {
658
406
  path,
659
407
  recursive,
660
408
  sessionId: targetSessionId,
661
- }),
409
+ } as MkdirRequest),
662
410
  headers: {
663
411
  "Content-Type": "application/json",
664
412
  },
@@ -686,129 +434,6 @@ export class HttpClient {
686
434
  }
687
435
  }
688
436
 
689
- async mkdirStream(
690
- path: string,
691
- recursive: boolean = false,
692
- sessionId?: string
693
- ): Promise<void> {
694
- try {
695
- const targetSessionId = sessionId || this.sessionId;
696
-
697
- const response = await this.doFetch(`/api/mkdir/stream`, {
698
- body: JSON.stringify({
699
- path,
700
- recursive,
701
- sessionId: targetSessionId,
702
- }),
703
- headers: {
704
- "Content-Type": "application/json",
705
- },
706
- method: "POST",
707
- });
708
-
709
- if (!response.ok) {
710
- const errorData = (await response.json().catch(() => ({}))) as {
711
- error?: string;
712
- };
713
- throw new Error(
714
- errorData.error || `HTTP error! status: ${response.status}`
715
- );
716
- }
717
-
718
- if (!response.body) {
719
- throw new Error("No response body for streaming request");
720
- }
721
-
722
- const reader = response.body.getReader();
723
- const decoder = new TextDecoder();
724
-
725
- try {
726
- while (true) {
727
- const { done, value } = await reader.read();
728
-
729
- if (done) {
730
- break;
731
- }
732
-
733
- const chunk = decoder.decode(value, { stream: true });
734
- const lines = chunk.split("\n");
735
-
736
- for (const line of lines) {
737
- if (line.startsWith("data: ")) {
738
- try {
739
- const eventData = line.slice(6); // Remove 'data: ' prefix
740
- const event: StreamEvent = JSON.parse(eventData);
741
-
742
- console.log(`[HTTP Client] Mkdir stream event: ${event.type}`);
743
- this.options.onStreamEvent?.(event);
744
-
745
- switch (event.type) {
746
- case "command_start":
747
- console.log(
748
- `[HTTP Client] Mkdir started: ${
749
- event.command
750
- } ${event.args?.join(" ")}`
751
- );
752
- this.options.onCommandStart?.(
753
- event.command!,
754
- event.args || []
755
- );
756
- break;
757
-
758
- case "output":
759
- console.log(`[${event.stream}] ${event.data}`);
760
- this.options.onOutput?.(
761
- event.stream!,
762
- event.data!,
763
- event.command!
764
- );
765
- break;
766
-
767
- case "command_complete":
768
- console.log(
769
- `[HTTP Client] Mkdir completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
770
- );
771
- this.options.onCommandComplete?.(
772
- event.success!,
773
- event.exitCode!,
774
- event.stdout!,
775
- event.stderr!,
776
- event.command!,
777
- event.args || []
778
- );
779
- break;
780
-
781
- case "error":
782
- console.error(`[HTTP Client] Mkdir error: ${event.error}`);
783
- this.options.onError?.(
784
- event.error!,
785
- event.command,
786
- event.args
787
- );
788
- break;
789
- }
790
- } catch (parseError) {
791
- console.warn(
792
- "[HTTP Client] Failed to parse mkdir stream event:",
793
- parseError
794
- );
795
- }
796
- }
797
- }
798
- }
799
- } finally {
800
- reader.releaseLock();
801
- }
802
- } catch (error) {
803
- console.error("[HTTP Client] Error in streaming mkdir:", error);
804
- this.options.onError?.(
805
- error instanceof Error ? error.message : "Unknown error",
806
- "mkdir",
807
- recursive ? ["-p", path] : [path]
808
- );
809
- throw error;
810
- }
811
- }
812
437
 
813
438
  async writeFile(
814
439
  path: string,
@@ -825,7 +450,7 @@ export class HttpClient {
825
450
  encoding,
826
451
  path,
827
452
  sessionId: targetSessionId,
828
- }),
453
+ } as WriteFileRequest),
829
454
  headers: {
830
455
  "Content-Type": "application/json",
831
456
  },
@@ -853,22 +478,21 @@ export class HttpClient {
853
478
  }
854
479
  }
855
480
 
856
- async writeFileStream(
481
+
482
+ async readFile(
857
483
  path: string,
858
- content: string,
859
484
  encoding: string = "utf-8",
860
485
  sessionId?: string
861
- ): Promise<void> {
486
+ ): Promise<ReadFileResponse> {
862
487
  try {
863
488
  const targetSessionId = sessionId || this.sessionId;
864
489
 
865
- const response = await this.doFetch(`/api/write/stream`, {
490
+ const response = await this.doFetch(`/api/read`, {
866
491
  body: JSON.stringify({
867
- content,
868
492
  encoding,
869
493
  path,
870
494
  sessionId: targetSessionId,
871
- }),
495
+ } as ReadFileRequest),
872
496
  headers: {
873
497
  "Content-Type": "application/json",
874
498
  },
@@ -884,100 +508,19 @@ export class HttpClient {
884
508
  );
885
509
  }
886
510
 
887
- if (!response.body) {
888
- throw new Error("No response body for streaming request");
889
- }
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
+ );
890
515
 
891
- const reader = response.body.getReader();
892
- const decoder = new TextDecoder();
893
-
894
- try {
895
- while (true) {
896
- const { done, value } = await reader.read();
897
-
898
- if (done) {
899
- break;
900
- }
901
-
902
- const chunk = decoder.decode(value, { stream: true });
903
- const lines = chunk.split("\n");
904
-
905
- for (const line of lines) {
906
- if (line.startsWith("data: ")) {
907
- try {
908
- const eventData = line.slice(6); // Remove 'data: ' prefix
909
- const event: StreamEvent = JSON.parse(eventData);
910
-
911
- console.log(
912
- `[HTTP Client] Write file stream event: ${event.type}`
913
- );
914
- this.options.onStreamEvent?.(event);
915
-
916
- switch (event.type) {
917
- case "command_start":
918
- console.log(
919
- `[HTTP Client] Write file started: ${event.path}`
920
- );
921
- this.options.onCommandStart?.("write", [
922
- path,
923
- content,
924
- encoding,
925
- ]);
926
- break;
927
-
928
- case "output":
929
- console.log(`[output] ${event.message}`);
930
- this.options.onOutput?.("stdout", event.message!, "write");
931
- break;
932
-
933
- case "command_complete":
934
- console.log(
935
- `[HTTP Client] Write file completed: ${event.path}, Success: ${event.success}`
936
- );
937
- this.options.onCommandComplete?.(
938
- event.success!,
939
- 0,
940
- "",
941
- "",
942
- "write",
943
- [path, content, encoding]
944
- );
945
- break;
946
-
947
- case "error":
948
- console.error(
949
- `[HTTP Client] Write file error: ${event.error}`
950
- );
951
- this.options.onError?.(event.error!, "write", [
952
- path,
953
- content,
954
- encoding,
955
- ]);
956
- break;
957
- }
958
- } catch (parseError) {
959
- console.warn(
960
- "[HTTP Client] Failed to parse write file stream event:",
961
- parseError
962
- );
963
- }
964
- }
965
- }
966
- }
967
- } finally {
968
- reader.releaseLock();
969
- }
516
+ return data;
970
517
  } catch (error) {
971
- console.error("[HTTP Client] Error in streaming write file:", error);
972
- this.options.onError?.(
973
- error instanceof Error ? error.message : "Unknown error",
974
- "write",
975
- [path, content, encoding]
976
- );
518
+ console.error("[HTTP Client] Error reading file:", error);
977
519
  throw error;
978
520
  }
979
521
  }
980
522
 
523
+
981
524
  async deleteFile(
982
525
  path: string,
983
526
  sessionId?: string
@@ -989,7 +532,7 @@ export class HttpClient {
989
532
  body: JSON.stringify({
990
533
  path,
991
534
  sessionId: targetSessionId,
992
- }),
535
+ } as DeleteFileRequest),
993
536
  headers: {
994
537
  "Content-Type": "application/json",
995
538
  },
@@ -1017,15 +560,21 @@ export class HttpClient {
1017
560
  }
1018
561
  }
1019
562
 
1020
- async deleteFileStream(path: string, sessionId?: string): Promise<void> {
563
+
564
+ async renameFile(
565
+ oldPath: string,
566
+ newPath: string,
567
+ sessionId?: string
568
+ ): Promise<RenameFileResponse> {
1021
569
  try {
1022
570
  const targetSessionId = sessionId || this.sessionId;
1023
571
 
1024
- const response = await this.doFetch(`/api/delete/stream`, {
572
+ const response = await this.doFetch(`/api/rename`, {
1025
573
  body: JSON.stringify({
1026
- path,
574
+ newPath,
575
+ oldPath,
1027
576
  sessionId: targetSessionId,
1028
- }),
577
+ } as RenameFileRequest),
1029
578
  headers: {
1030
579
  "Content-Type": "application/json",
1031
580
  },
@@ -1041,101 +590,33 @@ export class HttpClient {
1041
590
  );
1042
591
  }
1043
592
 
1044
- if (!response.body) {
1045
- throw new Error("No response body for streaming request");
1046
- }
593
+ const data: RenameFileResponse = await response.json();
594
+ console.log(
595
+ `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
596
+ );
1047
597
 
1048
- const reader = response.body.getReader();
1049
- const decoder = new TextDecoder();
1050
-
1051
- try {
1052
- while (true) {
1053
- const { done, value } = await reader.read();
1054
-
1055
- if (done) {
1056
- break;
1057
- }
1058
-
1059
- const chunk = decoder.decode(value, { stream: true });
1060
- const lines = chunk.split("\n");
1061
-
1062
- for (const line of lines) {
1063
- if (line.startsWith("data: ")) {
1064
- try {
1065
- const eventData = line.slice(6); // Remove 'data: ' prefix
1066
- const event: StreamEvent = JSON.parse(eventData);
1067
-
1068
- console.log(
1069
- `[HTTP Client] Delete file stream event: ${event.type}`
1070
- );
1071
- this.options.onStreamEvent?.(event);
1072
-
1073
- switch (event.type) {
1074
- case "command_start":
1075
- console.log(
1076
- `[HTTP Client] Delete file started: ${event.path}`
1077
- );
1078
- this.options.onCommandStart?.("delete", [path]);
1079
- break;
1080
-
1081
- case "command_complete":
1082
- console.log(
1083
- `[HTTP Client] Delete file completed: ${event.path}, Success: ${event.success}`
1084
- );
1085
- this.options.onCommandComplete?.(
1086
- event.success!,
1087
- 0,
1088
- "",
1089
- "",
1090
- "delete",
1091
- [path]
1092
- );
1093
- break;
1094
-
1095
- case "error":
1096
- console.error(
1097
- `[HTTP Client] Delete file error: ${event.error}`
1098
- );
1099
- this.options.onError?.(event.error!, "delete", [path]);
1100
- break;
1101
- }
1102
- } catch (parseError) {
1103
- console.warn(
1104
- "[HTTP Client] Failed to parse delete file stream event:",
1105
- parseError
1106
- );
1107
- }
1108
- }
1109
- }
1110
- }
1111
- } finally {
1112
- reader.releaseLock();
1113
- }
598
+ return data;
1114
599
  } catch (error) {
1115
- console.error("[HTTP Client] Error in streaming delete file:", error);
1116
- this.options.onError?.(
1117
- error instanceof Error ? error.message : "Unknown error",
1118
- "delete",
1119
- [path]
1120
- );
600
+ console.error("[HTTP Client] Error renaming file:", error);
1121
601
  throw error;
1122
602
  }
1123
603
  }
1124
604
 
1125
- async renameFile(
1126
- oldPath: string,
1127
- newPath: string,
605
+
606
+ async moveFile(
607
+ sourcePath: string,
608
+ destinationPath: string,
1128
609
  sessionId?: string
1129
- ): Promise<RenameFileResponse> {
610
+ ): Promise<MoveFileResponse> {
1130
611
  try {
1131
612
  const targetSessionId = sessionId || this.sessionId;
1132
613
 
1133
- const response = await this.doFetch(`/api/rename`, {
614
+ const response = await this.doFetch(`/api/move`, {
1134
615
  body: JSON.stringify({
1135
- newPath,
1136
- oldPath,
616
+ destinationPath,
1137
617
  sessionId: targetSessionId,
1138
- }),
618
+ sourcePath,
619
+ } as MoveFileRequest),
1139
620
  headers: {
1140
621
  "Content-Type": "application/json",
1141
622
  },
@@ -1151,31 +632,25 @@ export class HttpClient {
1151
632
  );
1152
633
  }
1153
634
 
1154
- const data: RenameFileResponse = await response.json();
635
+ const data: MoveFileResponse = await response.json();
1155
636
  console.log(
1156
- `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
637
+ `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
1157
638
  );
1158
639
 
1159
640
  return data;
1160
641
  } catch (error) {
1161
- console.error("[HTTP Client] Error renaming file:", error);
642
+ console.error("[HTTP Client] Error moving file:", error);
1162
643
  throw error;
1163
644
  }
1164
645
  }
1165
646
 
1166
- async renameFileStream(
1167
- oldPath: string,
1168
- newPath: string,
1169
- sessionId?: string
1170
- ): Promise<void> {
1171
- try {
1172
- const targetSessionId = sessionId || this.sessionId;
1173
647
 
1174
- const response = await this.doFetch(`/api/rename/stream`, {
648
+ async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
649
+ try {
650
+ const response = await this.doFetch(`/api/expose-port`, {
1175
651
  body: JSON.stringify({
1176
- newPath,
1177
- oldPath,
1178
- sessionId: targetSessionId,
652
+ port,
653
+ name,
1179
654
  }),
1180
655
  headers: {
1181
656
  "Content-Type": "application/json",
@@ -1187,113 +662,34 @@ export class HttpClient {
1187
662
  const errorData = (await response.json().catch(() => ({}))) as {
1188
663
  error?: string;
1189
664
  };
665
+ console.log(errorData);
1190
666
  throw new Error(
1191
667
  errorData.error || `HTTP error! status: ${response.status}`
1192
668
  );
1193
669
  }
1194
670
 
1195
- if (!response.body) {
1196
- throw new Error("No response body for streaming request");
1197
- }
671
+ const data: ExposePortResponse = await response.json();
672
+ console.log(
673
+ `[HTTP Client] Port exposed: ${port}${name ? ` (${name})` : ""}, Success: ${data.success}`
674
+ );
1198
675
 
1199
- const reader = response.body.getReader();
1200
- const decoder = new TextDecoder();
1201
-
1202
- try {
1203
- while (true) {
1204
- const { done, value } = await reader.read();
1205
-
1206
- if (done) {
1207
- break;
1208
- }
1209
-
1210
- const chunk = decoder.decode(value, { stream: true });
1211
- const lines = chunk.split("\n");
1212
-
1213
- for (const line of lines) {
1214
- if (line.startsWith("data: ")) {
1215
- try {
1216
- const eventData = line.slice(6); // Remove 'data: ' prefix
1217
- const event: StreamEvent = JSON.parse(eventData);
1218
-
1219
- console.log(
1220
- `[HTTP Client] Rename file stream event: ${event.type}`
1221
- );
1222
- this.options.onStreamEvent?.(event);
1223
-
1224
- switch (event.type) {
1225
- case "command_start":
1226
- console.log(
1227
- `[HTTP Client] Rename file started: ${event.oldPath} -> ${event.newPath}`
1228
- );
1229
- this.options.onCommandStart?.("rename", [oldPath, newPath]);
1230
- break;
1231
-
1232
- case "command_complete":
1233
- console.log(
1234
- `[HTTP Client] Rename file completed: ${event.oldPath} -> ${event.newPath}, Success: ${event.success}`
1235
- );
1236
- this.options.onCommandComplete?.(
1237
- event.success!,
1238
- 0,
1239
- "",
1240
- "",
1241
- "rename",
1242
- [oldPath, newPath]
1243
- );
1244
- break;
1245
-
1246
- case "error":
1247
- console.error(
1248
- `[HTTP Client] Rename file error: ${event.error}`
1249
- );
1250
- this.options.onError?.(event.error!, "rename", [
1251
- oldPath,
1252
- newPath,
1253
- ]);
1254
- break;
1255
- }
1256
- } catch (parseError) {
1257
- console.warn(
1258
- "[HTTP Client] Failed to parse rename file stream event:",
1259
- parseError
1260
- );
1261
- }
1262
- }
1263
- }
1264
- }
1265
- } finally {
1266
- reader.releaseLock();
1267
- }
676
+ return data;
1268
677
  } catch (error) {
1269
- console.error("[HTTP Client] Error in streaming rename file:", error);
1270
- this.options.onError?.(
1271
- error instanceof Error ? error.message : "Unknown error",
1272
- "rename",
1273
- [oldPath, newPath]
1274
- );
678
+ console.error("[HTTP Client] Error exposing port:", error);
1275
679
  throw error;
1276
680
  }
1277
681
  }
1278
682
 
1279
- async moveFile(
1280
- sourcePath: string,
1281
- destinationPath: string,
1282
- sessionId?: string
1283
- ): Promise<MoveFileResponse> {
683
+ async unexposePort(port: number): Promise<UnexposePortResponse> {
1284
684
  try {
1285
- const targetSessionId = sessionId || this.sessionId;
1286
-
1287
- const response = await this.doFetch(`/api/move`, {
685
+ const response = await this.doFetch(`/api/unexpose-port`, {
1288
686
  body: JSON.stringify({
1289
- destinationPath,
1290
- sessionId: targetSessionId,
1291
- sourcePath,
687
+ port,
1292
688
  }),
1293
689
  headers: {
1294
690
  "Content-Type": "application/json",
1295
691
  },
1296
- method: "POST",
692
+ method: "DELETE",
1297
693
  });
1298
694
 
1299
695
  if (!response.ok) {
@@ -1305,36 +701,25 @@ export class HttpClient {
1305
701
  );
1306
702
  }
1307
703
 
1308
- const data: MoveFileResponse = await response.json();
704
+ const data: UnexposePortResponse = await response.json();
1309
705
  console.log(
1310
- `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
706
+ `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
1311
707
  );
1312
708
 
1313
709
  return data;
1314
710
  } catch (error) {
1315
- console.error("[HTTP Client] Error moving file:", error);
711
+ console.error("[HTTP Client] Error unexposing port:", error);
1316
712
  throw error;
1317
713
  }
1318
714
  }
1319
715
 
1320
- async moveFileStream(
1321
- sourcePath: string,
1322
- destinationPath: string,
1323
- sessionId?: string
1324
- ): Promise<void> {
716
+ async getExposedPorts(): Promise<GetExposedPortsResponse> {
1325
717
  try {
1326
- const targetSessionId = sessionId || this.sessionId;
1327
-
1328
- const response = await this.doFetch(`/api/move/stream`, {
1329
- body: JSON.stringify({
1330
- destinationPath,
1331
- sessionId: targetSessionId,
1332
- sourcePath,
1333
- }),
718
+ const response = await this.doFetch(`/api/exposed-ports`, {
1334
719
  headers: {
1335
720
  "Content-Type": "application/json",
1336
721
  },
1337
- method: "POST",
722
+ method: "GET",
1338
723
  });
1339
724
 
1340
725
  if (!response.ok) {
@@ -1346,89 +731,14 @@ export class HttpClient {
1346
731
  );
1347
732
  }
1348
733
 
1349
- if (!response.body) {
1350
- throw new Error("No response body for streaming request");
1351
- }
734
+ const data: GetExposedPortsResponse = await response.json();
735
+ console.log(
736
+ `[HTTP Client] Got ${data.count} exposed ports`
737
+ );
1352
738
 
1353
- const reader = response.body.getReader();
1354
- const decoder = new TextDecoder();
1355
-
1356
- try {
1357
- while (true) {
1358
- const { done, value } = await reader.read();
1359
-
1360
- if (done) {
1361
- break;
1362
- }
1363
-
1364
- const chunk = decoder.decode(value, { stream: true });
1365
- const lines = chunk.split("\n");
1366
-
1367
- for (const line of lines) {
1368
- if (line.startsWith("data: ")) {
1369
- try {
1370
- const eventData = line.slice(6); // Remove 'data: ' prefix
1371
- const event: StreamEvent = JSON.parse(eventData);
1372
-
1373
- console.log(
1374
- `[HTTP Client] Move file stream event: ${event.type}`
1375
- );
1376
- this.options.onStreamEvent?.(event);
1377
-
1378
- switch (event.type) {
1379
- case "command_start":
1380
- console.log(
1381
- `[HTTP Client] Move file started: ${event.sourcePath} -> ${event.destinationPath}`
1382
- );
1383
- this.options.onCommandStart?.("move", [
1384
- sourcePath,
1385
- destinationPath,
1386
- ]);
1387
- break;
1388
-
1389
- case "command_complete":
1390
- console.log(
1391
- `[HTTP Client] Move file completed: ${event.sourcePath} -> ${event.destinationPath}, Success: ${event.success}`
1392
- );
1393
- this.options.onCommandComplete?.(
1394
- event.success!,
1395
- 0,
1396
- "",
1397
- "",
1398
- "move",
1399
- [sourcePath, destinationPath]
1400
- );
1401
- break;
1402
-
1403
- case "error":
1404
- console.error(
1405
- `[HTTP Client] Move file error: ${event.error}`
1406
- );
1407
- this.options.onError?.(event.error!, "move", [
1408
- sourcePath,
1409
- destinationPath,
1410
- ]);
1411
- break;
1412
- }
1413
- } catch (parseError) {
1414
- console.warn(
1415
- "[HTTP Client] Failed to parse move file stream event:",
1416
- parseError
1417
- );
1418
- }
1419
- }
1420
- }
1421
- }
1422
- } finally {
1423
- reader.releaseLock();
1424
- }
739
+ return data;
1425
740
  } catch (error) {
1426
- console.error("[HTTP Client] Error in streaming move file:", error);
1427
- this.options.onError?.(
1428
- error instanceof Error ? error.message : "Unknown error",
1429
- "move",
1430
- [sourcePath, destinationPath]
1431
- );
741
+ console.error("[HTTP Client] Error getting exposed ports:", error);
1432
742
  throw error;
1433
743
  }
1434
744
  }
@@ -1490,235 +800,239 @@ export class HttpClient {
1490
800
  clearSession(): void {
1491
801
  this.sessionId = null;
1492
802
  }
1493
- }
1494
803
 
1495
- // Example usage and utility functions
1496
- export function createClient(options?: HttpClientOptions): HttpClient {
1497
- return new HttpClient(options);
1498
- }
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> {
817
+ try {
818
+ const targetSessionId = options?.sessionId || this.sessionId;
1499
819
 
1500
- // Convenience function for quick command execution
1501
- export async function quickExecute(
1502
- command: string,
1503
- args: string[] = [],
1504
- options?: HttpClientOptions
1505
- ): Promise<ExecuteResponse> {
1506
- const client = createClient(options);
1507
- await client.createSession();
1508
-
1509
- try {
1510
- return await client.execute(command, args);
1511
- } finally {
1512
- client.clearSession();
1513
- }
1514
- }
820
+ const response = await this.doFetch("/api/process/start", {
821
+ body: JSON.stringify({
822
+ command,
823
+ options: {
824
+ ...options,
825
+ sessionId: targetSessionId,
826
+ },
827
+ } as StartProcessRequest),
828
+ headers: {
829
+ "Content-Type": "application/json",
830
+ },
831
+ method: "POST",
832
+ });
1515
833
 
1516
- // Convenience function for quick streaming command execution
1517
- export async function quickExecuteStream(
1518
- command: string,
1519
- args: string[] = [],
1520
- options?: HttpClientOptions
1521
- ): Promise<void> {
1522
- const client = createClient(options);
1523
- await client.createSession();
1524
-
1525
- try {
1526
- await client.executeStream(command, args);
1527
- } finally {
1528
- client.clearSession();
1529
- }
1530
- }
834
+ if (!response.ok) {
835
+ const errorData = (await response.json().catch(() => ({}))) as {
836
+ error?: string;
837
+ };
838
+ throw new Error(
839
+ errorData.error || `HTTP error! status: ${response.status}`
840
+ );
841
+ }
1531
842
 
1532
- // Convenience function for quick git checkout
1533
- export async function quickGitCheckout(
1534
- repoUrl: string,
1535
- branch: string = "main",
1536
- targetDir?: string,
1537
- options?: HttpClientOptions
1538
- ): Promise<GitCheckoutResponse> {
1539
- const client = createClient(options);
1540
- await client.createSession();
1541
-
1542
- try {
1543
- return await client.gitCheckout(repoUrl, branch, targetDir);
1544
- } finally {
1545
- client.clearSession();
1546
- }
1547
- }
843
+ const data: StartProcessResponse = await response.json();
844
+ console.log(
845
+ `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
846
+ );
1548
847
 
1549
- // Convenience function for quick directory creation
1550
- export async function quickMkdir(
1551
- path: string,
1552
- recursive: boolean = false,
1553
- options?: HttpClientOptions
1554
- ): Promise<MkdirResponse> {
1555
- const client = createClient(options);
1556
- await client.createSession();
1557
-
1558
- try {
1559
- return await client.mkdir(path, recursive);
1560
- } finally {
1561
- client.clearSession();
848
+ return data;
849
+ } catch (error) {
850
+ console.error("[HTTP Client] Error starting process:", error);
851
+ throw error;
852
+ }
1562
853
  }
1563
- }
1564
854
 
1565
- // Convenience function for quick streaming git checkout
1566
- export async function quickGitCheckoutStream(
1567
- repoUrl: string,
1568
- branch: string = "main",
1569
- targetDir?: string,
1570
- options?: HttpClientOptions
1571
- ): Promise<void> {
1572
- const client = createClient(options);
1573
- await client.createSession();
1574
-
1575
- try {
1576
- await client.gitCheckoutStream(repoUrl, branch, targetDir);
1577
- } finally {
1578
- client.clearSession();
1579
- }
1580
- }
855
+ async listProcesses(): Promise<ListProcessesResponse> {
856
+ try {
857
+ const response = await this.doFetch("/api/process/list", {
858
+ headers: {
859
+ "Content-Type": "application/json",
860
+ },
861
+ method: "GET",
862
+ });
1581
863
 
1582
- // Convenience function for quick streaming directory creation
1583
- export async function quickMkdirStream(
1584
- path: string,
1585
- recursive: boolean = false,
1586
- options?: HttpClientOptions
1587
- ): Promise<void> {
1588
- const client = createClient(options);
1589
- await client.createSession();
1590
-
1591
- try {
1592
- await client.mkdirStream(path, recursive);
1593
- } finally {
1594
- client.clearSession();
1595
- }
1596
- }
864
+ if (!response.ok) {
865
+ const errorData = (await response.json().catch(() => ({}))) as {
866
+ error?: string;
867
+ };
868
+ throw new Error(
869
+ errorData.error || `HTTP error! status: ${response.status}`
870
+ );
871
+ }
1597
872
 
1598
- // Convenience function for quick file writing
1599
- export async function quickWriteFile(
1600
- path: string,
1601
- content: string,
1602
- encoding: string = "utf-8",
1603
- options?: HttpClientOptions
1604
- ): Promise<WriteFileResponse> {
1605
- const client = createClient(options);
1606
- await client.createSession();
1607
-
1608
- try {
1609
- return await client.writeFile(path, content, encoding);
1610
- } finally {
1611
- client.clearSession();
1612
- }
1613
- }
873
+ const data: ListProcessesResponse = await response.json();
874
+ console.log(
875
+ `[HTTP Client] Listed ${data.processes.length} processes`
876
+ );
1614
877
 
1615
- // Convenience function for quick streaming file writing
1616
- export async function quickWriteFileStream(
1617
- path: string,
1618
- content: string,
1619
- encoding: string = "utf-8",
1620
- options?: HttpClientOptions
1621
- ): Promise<void> {
1622
- const client = createClient(options);
1623
- await client.createSession();
1624
-
1625
- try {
1626
- await client.writeFileStream(path, content, encoding);
1627
- } finally {
1628
- client.clearSession();
878
+ return data;
879
+ } catch (error) {
880
+ console.error("[HTTP Client] Error listing processes:", error);
881
+ throw error;
882
+ }
1629
883
  }
1630
- }
1631
884
 
1632
- // Convenience function for quick file deletion
1633
- export async function quickDeleteFile(
1634
- path: string,
1635
- options?: HttpClientOptions
1636
- ): Promise<DeleteFileResponse> {
1637
- const client = createClient(options);
1638
- await client.createSession();
1639
-
1640
- try {
1641
- return await client.deleteFile(path);
1642
- } finally {
1643
- client.clearSession();
1644
- }
1645
- }
885
+ async getProcess(processId: string): Promise<GetProcessResponse> {
886
+ try {
887
+ const response = await this.doFetch(`/api/process/${processId}`, {
888
+ headers: {
889
+ "Content-Type": "application/json",
890
+ },
891
+ method: "GET",
892
+ });
1646
893
 
1647
- // Convenience function for quick streaming file deletion
1648
- export async function quickDeleteFileStream(
1649
- path: string,
1650
- options?: HttpClientOptions
1651
- ): Promise<void> {
1652
- const client = createClient(options);
1653
- await client.createSession();
1654
-
1655
- try {
1656
- await client.deleteFileStream(path);
1657
- } finally {
1658
- client.clearSession();
894
+ if (!response.ok) {
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
+ );
901
+ }
902
+
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;
909
+ } catch (error) {
910
+ console.error("[HTTP Client] Error getting process:", error);
911
+ throw error;
912
+ }
1659
913
  }
1660
- }
1661
914
 
1662
- // Convenience function for quick file renaming
1663
- export async function quickRenameFile(
1664
- oldPath: string,
1665
- newPath: string,
1666
- options?: HttpClientOptions
1667
- ): Promise<RenameFileResponse> {
1668
- const client = createClient(options);
1669
- await client.createSession();
1670
-
1671
- try {
1672
- return await client.renameFile(oldPath, newPath);
1673
- } finally {
1674
- client.clearSession();
915
+ async killProcess(processId: string): Promise<{ success: boolean; message: string }> {
916
+ try {
917
+ const response = await this.doFetch(`/api/process/${processId}`, {
918
+ headers: {
919
+ "Content-Type": "application/json",
920
+ },
921
+ method: "DELETE",
922
+ });
923
+
924
+ if (!response.ok) {
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
+ );
931
+ }
932
+
933
+ const data = await response.json() as { success: boolean; message: string };
934
+ console.log(
935
+ `[HTTP Client] Killed process ${processId}`
936
+ );
937
+
938
+ return data;
939
+ } catch (error) {
940
+ console.error("[HTTP Client] Error killing process:", error);
941
+ throw error;
942
+ }
1675
943
  }
1676
- }
1677
944
 
1678
- // Convenience function for quick streaming file renaming
1679
- export async function quickRenameFileStream(
1680
- oldPath: string,
1681
- newPath: string,
1682
- options?: HttpClientOptions
1683
- ): Promise<void> {
1684
- const client = createClient(options);
1685
- await client.createSession();
1686
-
1687
- try {
1688
- await client.renameFileStream(oldPath, newPath);
1689
- } finally {
1690
- client.clearSession();
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
+ });
953
+
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
+ }
962
+
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;
969
+ } catch (error) {
970
+ console.error("[HTTP Client] Error killing all processes:", error);
971
+ throw error;
972
+ }
1691
973
  }
1692
- }
1693
974
 
1694
- // Convenience function for quick file moving
1695
- export async function quickMoveFile(
1696
- sourcePath: string,
1697
- destinationPath: string,
1698
- options?: HttpClientOptions
1699
- ): Promise<MoveFileResponse> {
1700
- const client = createClient(options);
1701
- await client.createSession();
1702
-
1703
- try {
1704
- return await client.moveFile(sourcePath, destinationPath);
1705
- } finally {
1706
- client.clearSession();
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
+ });
983
+
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
+ }
992
+
993
+ const data: GetProcessLogsResponse = await response.json();
994
+ console.log(
995
+ `[HTTP Client] Got logs for process ${processId}`
996
+ );
997
+
998
+ return data;
999
+ } catch (error) {
1000
+ console.error("[HTTP Client] Error getting process logs:", error);
1001
+ throw error;
1002
+ }
1707
1003
  }
1708
- }
1709
1004
 
1710
- // Convenience function for quick streaming file moving
1711
- export async function quickMoveFileStream(
1712
- sourcePath: string,
1713
- destinationPath: string,
1714
- options?: HttpClientOptions
1715
- ): Promise<void> {
1716
- const client = createClient(options);
1717
- await client.createSession();
1718
-
1719
- try {
1720
- await client.moveFileStream(sourcePath, destinationPath);
1721
- } finally {
1722
- client.clearSession();
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
+ });
1014
+
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
+ }
1023
+
1024
+ if (!response.body) {
1025
+ throw new Error("No response body for streaming request");
1026
+ }
1027
+
1028
+ console.log(
1029
+ `[HTTP Client] Started streaming logs for process ${processId}`
1030
+ );
1031
+
1032
+ return response.body;
1033
+ } catch (error) {
1034
+ console.error("[HTTP Client] Error streaming process logs:", error);
1035
+ throw error;
1036
+ }
1723
1037
  }
1724
1038
  }