@cloudflare/sandbox 0.0.0-8af9edd → 0.0.0-8c1f440

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