@cloudflare/sandbox 0.0.0-0ac3cfa → 0.0.0-12bbd12

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