@cloudflare/sandbox 0.3.6 → 0.4.1

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 (120) hide show
  1. package/.turbo/turbo-build.log +44 -0
  2. package/CHANGELOG.md +6 -8
  3. package/Dockerfile +88 -18
  4. package/README.md +89 -824
  5. package/dist/{chunk-JTKON2SH.js → chunk-BCJ7SF3Q.js} +9 -5
  6. package/dist/chunk-BCJ7SF3Q.js.map +1 -0
  7. package/dist/chunk-BFVUNTP4.js +104 -0
  8. package/dist/chunk-BFVUNTP4.js.map +1 -0
  9. package/dist/{chunk-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
  10. package/dist/chunk-EKSWCBCA.js.map +1 -0
  11. package/dist/chunk-HGF554LH.js +2236 -0
  12. package/dist/chunk-HGF554LH.js.map +1 -0
  13. package/dist/{chunk-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
  14. package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
  15. package/dist/file-stream.d.ts +16 -38
  16. package/dist/file-stream.js +1 -2
  17. package/dist/index.d.ts +6 -5
  18. package/dist/index.js +35 -39
  19. package/dist/index.js.map +1 -1
  20. package/dist/interpreter.d.ts +3 -3
  21. package/dist/interpreter.js +2 -2
  22. package/dist/request-handler.d.ts +4 -3
  23. package/dist/request-handler.js +4 -7
  24. package/dist/sandbox-D9K2ypln.d.ts +583 -0
  25. package/dist/sandbox.d.ts +3 -3
  26. package/dist/sandbox.js +4 -7
  27. package/dist/security.d.ts +4 -3
  28. package/dist/security.js +3 -3
  29. package/dist/sse-parser.js +1 -1
  30. package/package.json +11 -5
  31. package/src/clients/base-client.ts +280 -0
  32. package/src/clients/command-client.ts +115 -0
  33. package/src/clients/file-client.ts +269 -0
  34. package/src/clients/git-client.ts +92 -0
  35. package/src/clients/index.ts +63 -0
  36. package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
  37. package/src/clients/port-client.ts +105 -0
  38. package/src/clients/process-client.ts +177 -0
  39. package/src/clients/sandbox-client.ts +41 -0
  40. package/src/clients/types.ts +84 -0
  41. package/src/clients/utility-client.ts +94 -0
  42. package/src/errors/adapter.ts +180 -0
  43. package/src/errors/classes.ts +469 -0
  44. package/src/errors/index.ts +105 -0
  45. package/src/file-stream.ts +119 -117
  46. package/src/index.ts +81 -69
  47. package/src/interpreter.ts +17 -8
  48. package/src/request-handler.ts +69 -43
  49. package/src/sandbox.ts +694 -533
  50. package/src/security.ts +14 -23
  51. package/src/sse-parser.ts +4 -8
  52. package/startup.sh +3 -0
  53. package/tests/base-client.test.ts +328 -0
  54. package/tests/command-client.test.ts +407 -0
  55. package/tests/file-client.test.ts +643 -0
  56. package/tests/file-stream.test.ts +306 -0
  57. package/tests/git-client.test.ts +328 -0
  58. package/tests/port-client.test.ts +301 -0
  59. package/tests/process-client.test.ts +658 -0
  60. package/tests/sandbox.test.ts +465 -0
  61. package/tests/sse-parser.test.ts +290 -0
  62. package/tests/utility-client.test.ts +266 -0
  63. package/tests/wrangler.jsonc +35 -0
  64. package/tsconfig.json +9 -1
  65. package/vitest.config.ts +31 -0
  66. package/container_src/bun.lock +0 -76
  67. package/container_src/circuit-breaker.ts +0 -121
  68. package/container_src/control-process.ts +0 -784
  69. package/container_src/handler/exec.ts +0 -185
  70. package/container_src/handler/file.ts +0 -457
  71. package/container_src/handler/git.ts +0 -130
  72. package/container_src/handler/ports.ts +0 -314
  73. package/container_src/handler/process.ts +0 -568
  74. package/container_src/handler/session.ts +0 -92
  75. package/container_src/index.ts +0 -601
  76. package/container_src/interpreter-service.ts +0 -276
  77. package/container_src/isolation.ts +0 -1213
  78. package/container_src/mime-processor.ts +0 -255
  79. package/container_src/package.json +0 -18
  80. package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
  81. package/container_src/runtime/executors/python/ipython_executor.py +0 -338
  82. package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
  83. package/container_src/runtime/process-pool.ts +0 -464
  84. package/container_src/shell-escape.ts +0 -42
  85. package/container_src/startup.sh +0 -11
  86. package/container_src/types.ts +0 -131
  87. package/dist/chunk-32UDXUPC.js +0 -671
  88. package/dist/chunk-32UDXUPC.js.map +0 -1
  89. package/dist/chunk-5DILEXGY.js +0 -85
  90. package/dist/chunk-5DILEXGY.js.map +0 -1
  91. package/dist/chunk-D3U63BZP.js +0 -240
  92. package/dist/chunk-D3U63BZP.js.map +0 -1
  93. package/dist/chunk-FXYPFGOZ.js +0 -129
  94. package/dist/chunk-FXYPFGOZ.js.map +0 -1
  95. package/dist/chunk-JTKON2SH.js.map +0 -1
  96. package/dist/chunk-NNGBXDMY.js.map +0 -1
  97. package/dist/chunk-SQLJNZ3K.js +0 -674
  98. package/dist/chunk-SQLJNZ3K.js.map +0 -1
  99. package/dist/chunk-W7TVRPBG.js +0 -108
  100. package/dist/chunk-W7TVRPBG.js.map +0 -1
  101. package/dist/client-B3RUab0s.d.ts +0 -225
  102. package/dist/client.d.ts +0 -4
  103. package/dist/client.js +0 -7
  104. package/dist/client.js.map +0 -1
  105. package/dist/errors.d.ts +0 -95
  106. package/dist/errors.js +0 -27
  107. package/dist/errors.js.map +0 -1
  108. package/dist/interpreter-client.d.ts +0 -4
  109. package/dist/interpreter-client.js +0 -9
  110. package/dist/interpreter-client.js.map +0 -1
  111. package/dist/interpreter-types.d.ts +0 -259
  112. package/dist/interpreter-types.js +0 -9
  113. package/dist/interpreter-types.js.map +0 -1
  114. package/dist/types.d.ts +0 -453
  115. package/dist/types.js +0 -45
  116. package/dist/types.js.map +0 -1
  117. package/src/client.ts +0 -1048
  118. package/src/errors.ts +0 -219
  119. package/src/interpreter-types.ts +0 -390
  120. package/src/types.ts +0 -571
package/src/client.ts DELETED
@@ -1,1048 +0,0 @@
1
- import type { ExecuteRequest } from "../container_src/types";
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";
20
-
21
-
22
- interface CommandsResponse {
23
- availableCommands: string[];
24
- timestamp: string;
25
- }
26
-
27
- interface GitCheckoutRequest {
28
- repoUrl: string;
29
- branch?: string;
30
- targetDir?: string;
31
- sessionId: string;
32
- }
33
-
34
-
35
- interface MkdirRequest {
36
- path: string;
37
- recursive?: boolean;
38
- sessionId: string;
39
- }
40
-
41
-
42
- interface WriteFileRequest {
43
- path: string;
44
- content: string;
45
- encoding?: string;
46
- sessionId: string;
47
- }
48
-
49
-
50
- interface ReadFileRequest {
51
- path: string;
52
- encoding?: string;
53
- sessionId: string;
54
- }
55
-
56
-
57
- interface DeleteFileRequest {
58
- path: string;
59
- sessionId: string;
60
- }
61
-
62
-
63
- interface RenameFileRequest {
64
- oldPath: string;
65
- newPath: string;
66
- sessionId: string;
67
- }
68
-
69
-
70
- interface MoveFileRequest {
71
- sourcePath: string;
72
- destinationPath: string;
73
- sessionId: string;
74
- }
75
-
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 {
99
- success: boolean;
100
- port: number;
101
- name?: string;
102
- exposedAt: string;
103
- timestamp: string;
104
- }
105
-
106
- interface UnexposePortResponse {
107
- success: boolean;
108
- port: number;
109
- timestamp: string;
110
- }
111
-
112
- interface GetExposedPortsResponse {
113
- ports: ExposedPort[];
114
- count: number;
115
- timestamp: string;
116
- }
117
-
118
- interface PingResponse {
119
- message: string;
120
- timestamp: string;
121
- }
122
-
123
- interface HttpClientOptions {
124
- stub?: Sandbox;
125
- baseUrl?: string;
126
- port?: number;
127
- onCommandStart?: (command: string) => void;
128
- onOutput?: (
129
- stream: "stdout" | "stderr",
130
- data: string,
131
- command: string
132
- ) => void;
133
- onCommandComplete?: (
134
- success: boolean,
135
- exitCode: number,
136
- stdout: string,
137
- stderr: string,
138
- command: string
139
- ) => void;
140
- onError?: (error: string, command?: string) => void;
141
- }
142
-
143
- export class HttpClient {
144
- private baseUrl: string;
145
- private options: HttpClientOptions;
146
-
147
- constructor(options: HttpClientOptions = {}) {
148
- this.options = {
149
- ...options,
150
- };
151
- this.baseUrl = this.options.baseUrl!;
152
- }
153
-
154
- protected async doFetch(
155
- path: string,
156
- options?: RequestInit
157
- ): Promise<Response> {
158
- const url = this.options.stub
159
- ? `http://localhost:${this.options.port}${path}`
160
- : `${this.baseUrl}${path}`;
161
- const method = options?.method || "GET";
162
-
163
- console.log(`[HTTP Client] Making ${method} request to ${url}`);
164
-
165
- try {
166
- let response: Response;
167
-
168
- if (this.options.stub) {
169
- response = await this.options.stub.containerFetch(
170
- url,
171
- options,
172
- this.options.port
173
- );
174
- } else {
175
- response = await fetch(url, options);
176
- }
177
-
178
- console.log(
179
- `[HTTP Client] Response: ${response.status} ${response.statusText}`
180
- );
181
-
182
- if (!response.ok) {
183
- console.error(
184
- `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
185
- );
186
- }
187
-
188
- return response;
189
- } catch (error) {
190
- console.error(`[HTTP Client] Request error: ${method} ${url}`, error);
191
- throw error;
192
- }
193
- }
194
-
195
- async createSession(options: {
196
- id: string;
197
- env?: Record<string, string>;
198
- cwd?: string;
199
- isolation?: boolean;
200
- }): Promise<{ success: boolean; id: string; message: string }> {
201
- try {
202
- const response = await this.doFetch(`/api/session/create`, {
203
- method: "POST",
204
- headers: {
205
- "Content-Type": "application/json",
206
- },
207
- body: JSON.stringify(options),
208
- });
209
-
210
- if (!response.ok) {
211
- const errorData = (await response.json().catch(() => ({}))) as {
212
- error?: string;
213
- };
214
- throw new Error(
215
- errorData.error || `Failed to create session: ${response.status}`
216
- );
217
- }
218
-
219
- const data = await response.json() as { success: boolean; id: string; message: string };
220
- console.log(`[HTTP Client] Session created: ${options.id}`);
221
- return data;
222
- } catch (error) {
223
- console.error("[HTTP Client] Error creating session:", error);
224
- throw error;
225
- }
226
- }
227
-
228
- async exec(
229
- sessionId: string,
230
- command: string,
231
- options?: Pick<BaseExecOptions, "cwd" | "env">
232
- ): Promise<ExecuteResponse> {
233
- try {
234
- // Always use session-specific endpoint
235
- const response = await this.doFetch(`/api/execute`, {
236
- method: "POST",
237
- headers: {
238
- "Content-Type": "application/json",
239
- },
240
- body: JSON.stringify({ id: sessionId, command }),
241
- });
242
-
243
- if (!response.ok) {
244
- const errorData = (await response.json().catch(() => ({}))) as {
245
- error?: string;
246
- };
247
- throw new Error(
248
- errorData.error || `Failed to execute in session: ${response.status}`
249
- );
250
- }
251
-
252
- const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };
253
- console.log(
254
- `[HTTP Client] Command executed in session ${sessionId}: ${command}`
255
- );
256
-
257
- // Convert to ExecuteResponse format for consistency
258
- const executeResponse: ExecuteResponse = {
259
- ...data,
260
- command,
261
- timestamp: new Date().toISOString()
262
- };
263
-
264
- // Call the callback if provided
265
- this.options.onCommandComplete?.(
266
- executeResponse.success,
267
- executeResponse.exitCode,
268
- executeResponse.stdout,
269
- executeResponse.stderr,
270
- executeResponse.command
271
- );
272
-
273
- return executeResponse;
274
- } catch (error) {
275
- console.error("[HTTP Client] Error executing in session:", error);
276
- this.options.onError?.(
277
- error instanceof Error ? error.message : "Unknown error",
278
- command
279
- );
280
- throw error;
281
- }
282
- }
283
-
284
- async execStream(
285
- sessionId: string,
286
- command: string
287
- ): Promise<ReadableStream<Uint8Array>> {
288
- try {
289
- // Always use session-specific streaming endpoint
290
- const response = await this.doFetch(`/api/execute/stream`, {
291
- method: "POST",
292
- headers: {
293
- "Content-Type": "application/json",
294
- },
295
- body: JSON.stringify({
296
- id: sessionId,
297
- command
298
- }),
299
- });
300
-
301
- if (!response.ok) {
302
- const errorData = (await response.json().catch(() => ({}))) as {
303
- error?: string;
304
- };
305
- throw new Error(
306
- errorData.error || `Failed to stream execute in session: ${response.status}`
307
- );
308
- }
309
-
310
- if (!response.body) {
311
- throw new Error("No response body for streaming execution");
312
- }
313
-
314
- console.log(
315
- `[HTTP Client] Started streaming command in session ${sessionId}: ${command}`
316
- );
317
- return response.body;
318
- } catch (error) {
319
- console.error("[HTTP Client] Error streaming execute in session:", error);
320
- throw error;
321
- }
322
- }
323
-
324
- async gitCheckout(
325
- repoUrl: string,
326
- sessionId: string,
327
- branch: string = "main",
328
- targetDir?: string
329
- ): Promise<GitCheckoutResponse> {
330
- try {
331
- const response = await this.doFetch(`/api/git/checkout`, {
332
- body: JSON.stringify({
333
- branch,
334
- repoUrl,
335
- targetDir,
336
- sessionId,
337
- } as GitCheckoutRequest),
338
- headers: {
339
- "Content-Type": "application/json",
340
- },
341
- method: "POST",
342
- });
343
-
344
- if (!response.ok) {
345
- const errorData = (await response.json().catch(() => ({}))) as {
346
- error?: string;
347
- };
348
- throw new Error(
349
- errorData.error || `HTTP error! status: ${response.status}`
350
- );
351
- }
352
-
353
- const data: GitCheckoutResponse = await response.json();
354
- console.log(
355
- `[HTTP Client] Git checkout completed: ${repoUrl}, Success: ${data.success}, Target: ${data.targetDir}`
356
- );
357
-
358
- return data;
359
- } catch (error) {
360
- console.error("[HTTP Client] Error in git checkout:", error);
361
- throw error;
362
- }
363
- }
364
-
365
- async mkdir(
366
- path: string,
367
- recursive: boolean = false,
368
- sessionId: string
369
- ): Promise<MkdirResponse> {
370
- try {
371
- const response = await this.doFetch(`/api/mkdir`, {
372
- body: JSON.stringify({
373
- path,
374
- recursive,
375
- sessionId,
376
- } as MkdirRequest),
377
- headers: {
378
- "Content-Type": "application/json",
379
- },
380
- method: "POST",
381
- });
382
-
383
- if (!response.ok) {
384
- const errorData = (await response.json().catch(() => ({}))) as {
385
- error?: string;
386
- };
387
- throw new Error(
388
- errorData.error || `HTTP error! status: ${response.status}`
389
- );
390
- }
391
-
392
- const data: MkdirResponse = await response.json();
393
- console.log(
394
- `[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
395
- );
396
-
397
- return data;
398
- } catch (error) {
399
- console.error("[HTTP Client] Error creating directory:", error);
400
- throw error;
401
- }
402
- }
403
-
404
- async writeFile(
405
- path: string,
406
- content: string,
407
- encoding: string = "utf-8",
408
- sessionId: string
409
- ): Promise<WriteFileResponse> {
410
- try {
411
- const response = await this.doFetch(`/api/write`, {
412
- body: JSON.stringify({
413
- content,
414
- encoding,
415
- path,
416
- sessionId,
417
- } as WriteFileRequest),
418
- headers: {
419
- "Content-Type": "application/json",
420
- },
421
- method: "POST",
422
- });
423
-
424
- if (!response.ok) {
425
- const errorData = (await response.json().catch(() => ({}))) as {
426
- error?: string;
427
- };
428
- throw new Error(
429
- errorData.error || `HTTP error! status: ${response.status}`
430
- );
431
- }
432
-
433
- const data: WriteFileResponse = await response.json();
434
- console.log(
435
- `[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
436
- );
437
-
438
- return data;
439
- } catch (error) {
440
- console.error("[HTTP Client] Error writing file:", error);
441
- throw error;
442
- }
443
- }
444
-
445
- async readFile(
446
- path: string,
447
- encoding: string = "utf-8",
448
- sessionId: string
449
- ): Promise<ReadFileResponse> {
450
- try {
451
- const response = await this.doFetch(`/api/read`, {
452
- body: JSON.stringify({
453
- encoding,
454
- path,
455
- sessionId,
456
- } as ReadFileRequest),
457
- headers: {
458
- "Content-Type": "application/json",
459
- },
460
- method: "POST",
461
- });
462
-
463
- if (!response.ok) {
464
- const errorData = (await response.json().catch(() => ({}))) as {
465
- error?: string;
466
- };
467
- throw new Error(
468
- errorData.error || `HTTP error! status: ${response.status}`
469
- );
470
- }
471
-
472
- const data: ReadFileResponse = await response.json();
473
- console.log(
474
- `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
475
- );
476
-
477
- return data;
478
- } catch (error) {
479
- console.error("[HTTP Client] Error reading file:", error);
480
- throw error;
481
- }
482
- }
483
-
484
- async readFileStream(
485
- path: string,
486
- sessionId: string
487
- ): Promise<ReadableStream<Uint8Array>> {
488
- try {
489
- const response = await this.doFetch(`/api/read/stream`, {
490
- method: "POST",
491
- headers: {
492
- "Content-Type": "application/json",
493
- },
494
- body: JSON.stringify({
495
- path,
496
- sessionId,
497
- } as ReadFileRequest),
498
- });
499
-
500
- if (!response.ok) {
501
- const errorData = (await response.json().catch(() => ({}))) as {
502
- error?: string;
503
- };
504
- throw new Error(
505
- errorData.error || `HTTP error! status: ${response.status}`
506
- );
507
- }
508
-
509
- if (!response.body) {
510
- throw new Error("No response body for file streaming");
511
- }
512
-
513
- console.log(
514
- `[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`
515
- );
516
- return response.body;
517
- } catch (error) {
518
- console.error("[HTTP Client] Error streaming file:", error);
519
- throw error;
520
- }
521
- }
522
-
523
- async deleteFile(
524
- path: string,
525
- sessionId: string
526
- ): Promise<DeleteFileResponse> {
527
- try {
528
- const response = await this.doFetch(`/api/delete`, {
529
- body: JSON.stringify({
530
- path,
531
- sessionId,
532
- } as DeleteFileRequest),
533
- headers: {
534
- "Content-Type": "application/json",
535
- },
536
- method: "POST",
537
- });
538
-
539
- if (!response.ok) {
540
- const errorData = (await response.json().catch(() => ({}))) as {
541
- error?: string;
542
- };
543
- throw new Error(
544
- errorData.error || `HTTP error! status: ${response.status}`
545
- );
546
- }
547
-
548
- const data: DeleteFileResponse = await response.json();
549
- console.log(
550
- `[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
551
- );
552
-
553
- return data;
554
- } catch (error) {
555
- console.error("[HTTP Client] Error deleting file:", error);
556
- throw error;
557
- }
558
- }
559
-
560
- async renameFile(
561
- oldPath: string,
562
- newPath: string,
563
- sessionId: string
564
- ): Promise<RenameFileResponse> {
565
- try {
566
- const response = await this.doFetch(`/api/rename`, {
567
- body: JSON.stringify({
568
- newPath,
569
- oldPath,
570
- sessionId,
571
- } as RenameFileRequest),
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: RenameFileResponse = await response.json();
588
- console.log(
589
- `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
590
- );
591
-
592
- return data;
593
- } catch (error) {
594
- console.error("[HTTP Client] Error renaming file:", error);
595
- throw error;
596
- }
597
- }
598
-
599
- async moveFile(
600
- sourcePath: string,
601
- destinationPath: string,
602
- sessionId: string
603
- ): Promise<MoveFileResponse> {
604
- try {
605
- const response = await this.doFetch(`/api/move`, {
606
- body: JSON.stringify({
607
- destinationPath,
608
- sourcePath,
609
- sessionId,
610
- } as MoveFileRequest),
611
- headers: {
612
- "Content-Type": "application/json",
613
- },
614
- method: "POST",
615
- });
616
-
617
- if (!response.ok) {
618
- const errorData = (await response.json().catch(() => ({}))) as {
619
- error?: string;
620
- };
621
- throw new Error(
622
- errorData.error || `HTTP error! status: ${response.status}`
623
- );
624
- }
625
-
626
- const data: MoveFileResponse = await response.json();
627
- console.log(
628
- `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
629
- );
630
-
631
- return data;
632
- } catch (error) {
633
- console.error("[HTTP Client] Error moving file:", error);
634
- throw error;
635
- }
636
- }
637
-
638
- async listFiles(
639
- path: string,
640
- sessionId: string,
641
- options?: {
642
- recursive?: boolean;
643
- includeHidden?: boolean;
644
- }
645
- ): Promise<ListFilesResponse> {
646
- try {
647
- const response = await this.doFetch(`/api/list-files`, {
648
- body: JSON.stringify({
649
- path,
650
- options,
651
- sessionId,
652
- } as ListFilesRequest),
653
- headers: {
654
- "Content-Type": "application/json",
655
- },
656
- method: "POST",
657
- });
658
-
659
- if (!response.ok) {
660
- const errorData = (await response.json().catch(() => ({}))) as {
661
- error?: string;
662
- };
663
- throw new Error(
664
- errorData.error || `HTTP error! status: ${response.status}`
665
- );
666
- }
667
-
668
- const data: ListFilesResponse = await response.json();
669
- console.log(
670
- `[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
671
- );
672
-
673
- return data;
674
- } catch (error) {
675
- console.error("[HTTP Client] Error listing files:", error);
676
- throw error;
677
- }
678
- }
679
-
680
- async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
681
- try {
682
- const response = await this.doFetch(`/api/expose-port`, {
683
- body: JSON.stringify({
684
- port,
685
- name,
686
- }),
687
- headers: {
688
- "Content-Type": "application/json",
689
- },
690
- method: "POST",
691
- });
692
-
693
- if (!response.ok) {
694
- const errorData = (await response.json().catch(() => ({}))) as {
695
- error?: string;
696
- };
697
- console.log(errorData);
698
- throw new Error(
699
- errorData.error || `HTTP error! status: ${response.status}`
700
- );
701
- }
702
-
703
- const data: ExposePortResponse = await response.json();
704
- console.log(
705
- `[HTTP Client] Port exposed: ${port}${
706
- name ? ` (${name})` : ""
707
- }, Success: ${data.success}`
708
- );
709
-
710
- return data;
711
- } catch (error) {
712
- console.error("[HTTP Client] Error exposing port:", error);
713
- throw error;
714
- }
715
- }
716
-
717
- async unexposePort(port: number): Promise<UnexposePortResponse> {
718
- try {
719
- const response = await this.doFetch(`/api/unexpose-port`, {
720
- body: JSON.stringify({
721
- port,
722
- }),
723
- headers: {
724
- "Content-Type": "application/json",
725
- },
726
- method: "DELETE",
727
- });
728
-
729
- if (!response.ok) {
730
- const errorData = (await response.json().catch(() => ({}))) as {
731
- error?: string;
732
- };
733
- throw new Error(
734
- errorData.error || `HTTP error! status: ${response.status}`
735
- );
736
- }
737
-
738
- const data: UnexposePortResponse = await response.json();
739
- console.log(
740
- `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
741
- );
742
-
743
- return data;
744
- } catch (error) {
745
- console.error("[HTTP Client] Error unexposing port:", error);
746
- throw error;
747
- }
748
- }
749
-
750
- async getExposedPorts(): Promise<GetExposedPortsResponse> {
751
- try {
752
- const response = await this.doFetch(`/api/exposed-ports`, {
753
- headers: {
754
- "Content-Type": "application/json",
755
- },
756
- method: "GET",
757
- });
758
-
759
- if (!response.ok) {
760
- const errorData = (await response.json().catch(() => ({}))) as {
761
- error?: string;
762
- };
763
- throw new Error(
764
- errorData.error || `HTTP error! status: ${response.status}`
765
- );
766
- }
767
-
768
- const data: GetExposedPortsResponse = await response.json();
769
- console.log(`[HTTP Client] Got ${data.count} exposed ports`);
770
-
771
- return data;
772
- } catch (error) {
773
- console.error("[HTTP Client] Error getting exposed ports:", error);
774
- throw error;
775
- }
776
- }
777
-
778
- async ping(): Promise<string> {
779
- try {
780
- const response = await this.doFetch(`/api/ping`, {
781
- headers: {
782
- "Content-Type": "application/json",
783
- },
784
- method: "GET",
785
- });
786
-
787
- if (!response.ok) {
788
- throw new Error(`HTTP error! status: ${response.status}`);
789
- }
790
-
791
- const data: PingResponse = await response.json();
792
- console.log(`[HTTP Client] Ping response: ${data.message}`);
793
- return data.timestamp;
794
- } catch (error) {
795
- console.error("[HTTP Client] Error pinging server:", error);
796
- throw error;
797
- }
798
- }
799
-
800
-
801
- // Process management methods
802
- async startProcess(
803
- command: string,
804
- sessionId: string,
805
- options?: {
806
- processId?: string;
807
- timeout?: number;
808
- env?: Record<string, string>;
809
- cwd?: string;
810
- encoding?: string;
811
- autoCleanup?: boolean;
812
- }
813
- ): Promise<StartProcessResponse> {
814
- try {
815
- const response = await this.doFetch("/api/process/start", {
816
- body: JSON.stringify({
817
- command,
818
- sessionId,
819
- options,
820
- } as StartProcessRequest),
821
- headers: {
822
- "Content-Type": "application/json",
823
- },
824
- method: "POST",
825
- });
826
-
827
- if (!response.ok) {
828
- const errorData = (await response.json().catch(() => ({}))) as {
829
- error?: string;
830
- };
831
- throw new Error(
832
- errorData.error || `HTTP error! status: ${response.status}`
833
- );
834
- }
835
-
836
- const data: StartProcessResponse = await response.json();
837
- console.log(
838
- `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
839
- );
840
-
841
- return data;
842
- } catch (error) {
843
- console.error("[HTTP Client] Error starting process:", error);
844
- throw error;
845
- }
846
- }
847
-
848
- async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
849
- try {
850
- const url = sessionId
851
- ? `/api/process/list?session=${encodeURIComponent(sessionId)}`
852
- : "/api/process/list";
853
- const response = await this.doFetch(url, {
854
- headers: {
855
- "Content-Type": "application/json",
856
- },
857
- method: "GET",
858
- });
859
-
860
- if (!response.ok) {
861
- const errorData = (await response.json().catch(() => ({}))) as {
862
- error?: string;
863
- };
864
- throw new Error(
865
- errorData.error || `HTTP error! status: ${response.status}`
866
- );
867
- }
868
-
869
- const data: ListProcessesResponse = await response.json();
870
- console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
871
-
872
- return data;
873
- } catch (error) {
874
- console.error("[HTTP Client] Error listing processes:", error);
875
- throw error;
876
- }
877
- }
878
-
879
- async getProcess(processId: string): Promise<GetProcessResponse> {
880
- try {
881
- const response = await this.doFetch(`/api/process/${processId}`, {
882
- headers: {
883
- "Content-Type": "application/json",
884
- },
885
- method: "GET",
886
- });
887
-
888
- if (!response.ok) {
889
- const errorData = (await response.json().catch(() => ({}))) as {
890
- error?: string;
891
- };
892
- throw new Error(
893
- errorData.error || `HTTP error! status: ${response.status}`
894
- );
895
- }
896
-
897
- const data: GetProcessResponse = await response.json();
898
- console.log(
899
- `[HTTP Client] Got process ${processId}: ${
900
- data.process?.status || "not found"
901
- }`
902
- );
903
-
904
- return data;
905
- } catch (error) {
906
- console.error("[HTTP Client] Error getting process:", error);
907
- throw error;
908
- }
909
- }
910
-
911
- async killProcess(
912
- processId: string
913
- ): Promise<{ success: boolean; message: string }> {
914
- try {
915
- const response = await this.doFetch(`/api/process/${processId}`, {
916
- headers: {
917
- "Content-Type": "application/json",
918
- },
919
- method: "DELETE",
920
- });
921
-
922
- if (!response.ok) {
923
- const errorData = (await response.json().catch(() => ({}))) as {
924
- error?: string;
925
- };
926
- throw new Error(
927
- errorData.error || `HTTP error! status: ${response.status}`
928
- );
929
- }
930
-
931
- const data = (await response.json()) as {
932
- success: boolean;
933
- message: string;
934
- };
935
- console.log(`[HTTP Client] Killed process ${processId}`);
936
-
937
- return data;
938
- } catch (error) {
939
- console.error("[HTTP Client] Error killing process:", error);
940
- throw error;
941
- }
942
- }
943
-
944
- async killAllProcesses(sessionId?: string): Promise<{
945
- success: boolean;
946
- killedCount: number;
947
- message: string;
948
- }> {
949
- try {
950
- const url = sessionId
951
- ? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
952
- : "/api/process/kill-all";
953
- const response = await this.doFetch(url, {
954
- headers: {
955
- "Content-Type": "application/json",
956
- },
957
- method: "DELETE",
958
- });
959
-
960
- if (!response.ok) {
961
- const errorData = (await response.json().catch(() => ({}))) as {
962
- error?: string;
963
- };
964
- throw new Error(
965
- errorData.error || `HTTP error! status: ${response.status}`
966
- );
967
- }
968
-
969
- const data = (await response.json()) as {
970
- success: boolean;
971
- killedCount: number;
972
- message: string;
973
- };
974
- console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
975
-
976
- return data;
977
- } catch (error) {
978
- console.error("[HTTP Client] Error killing all processes:", error);
979
- throw error;
980
- }
981
- }
982
-
983
- async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
984
- try {
985
- const response = await this.doFetch(`/api/process/${processId}/logs`, {
986
- headers: {
987
- "Content-Type": "application/json",
988
- },
989
- method: "GET",
990
- });
991
-
992
- if (!response.ok) {
993
- const errorData = (await response.json().catch(() => ({}))) as {
994
- error?: string;
995
- };
996
- throw new Error(
997
- errorData.error || `HTTP error! status: ${response.status}`
998
- );
999
- }
1000
-
1001
- const data: GetProcessLogsResponse = await response.json();
1002
- console.log(`[HTTP Client] Got logs for process ${processId}`);
1003
-
1004
- return data;
1005
- } catch (error) {
1006
- console.error("[HTTP Client] Error getting process logs:", error);
1007
- throw error;
1008
- }
1009
- }
1010
-
1011
- async streamProcessLogs(
1012
- processId: string,
1013
- options?: { signal?: AbortSignal }
1014
- ): Promise<ReadableStream<Uint8Array>> {
1015
- try {
1016
- const response = await this.doFetch(`/api/process/${processId}/stream`, {
1017
- headers: {
1018
- Accept: "text/event-stream",
1019
- "Cache-Control": "no-cache",
1020
- },
1021
- method: "GET",
1022
- signal: options?.signal,
1023
- });
1024
-
1025
- if (!response.ok) {
1026
- const errorData = (await response.json().catch(() => ({}))) as {
1027
- error?: string;
1028
- };
1029
- throw new Error(
1030
- errorData.error || `HTTP error! status: ${response.status}`
1031
- );
1032
- }
1033
-
1034
- if (!response.body) {
1035
- throw new Error("No response body for streaming request");
1036
- }
1037
-
1038
- console.log(
1039
- `[HTTP Client] Started streaming logs for process ${processId}`
1040
- );
1041
-
1042
- return response.body;
1043
- } catch (error) {
1044
- console.error("[HTTP Client] Error streaming process logs:", error);
1045
- throw error;
1046
- }
1047
- }
1048
- }