@cloudflare/sandbox 0.0.0-444d2da → 0.0.0-4aceb32

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