@cloudflare/sandbox 0.3.4 → 0.3.6

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 (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/Dockerfile +1 -0
  3. package/README.md +58 -5
  4. package/container_src/handler/file.ts +51 -0
  5. package/container_src/index.ts +9 -0
  6. package/container_src/isolation.ts +173 -9
  7. package/dist/{chunk-SMUEY5JR.js → chunk-32UDXUPC.js} +31 -1
  8. package/dist/chunk-32UDXUPC.js.map +1 -0
  9. package/dist/chunk-5DILEXGY.js +85 -0
  10. package/dist/chunk-5DILEXGY.js.map +1 -0
  11. package/dist/{chunk-Z6OZPC6U.js → chunk-D3U63BZP.js} +2 -2
  12. package/dist/{chunk-DEXT4CAF.js → chunk-SQLJNZ3K.js} +9 -2
  13. package/dist/chunk-SQLJNZ3K.js.map +1 -0
  14. package/dist/{client-DRhcuRza.d.ts → client-B3RUab0s.d.ts} +2 -0
  15. package/dist/client.d.ts +1 -1
  16. package/dist/client.js +1 -1
  17. package/dist/file-stream.d.ts +65 -0
  18. package/dist/file-stream.js +10 -0
  19. package/dist/file-stream.js.map +1 -0
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.js +10 -4
  22. package/dist/interpreter-client.d.ts +1 -1
  23. package/dist/interpreter-client.js +2 -2
  24. package/dist/interpreter.d.ts +1 -1
  25. package/dist/request-handler.d.ts +1 -1
  26. package/dist/request-handler.js +3 -3
  27. package/dist/sandbox.d.ts +1 -1
  28. package/dist/sandbox.js +3 -3
  29. package/dist/types.d.ts +56 -1
  30. package/dist/types.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/client.ts +39 -0
  33. package/src/file-stream.ts +162 -0
  34. package/src/index.ts +6 -0
  35. package/src/sandbox.ts +10 -1
  36. package/src/types.ts +69 -0
  37. package/dist/chunk-DEXT4CAF.js.map +0 -1
  38. package/dist/chunk-SMUEY5JR.js.map +0 -1
  39. /package/dist/{chunk-Z6OZPC6U.js.map → chunk-D3U63BZP.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @cloudflare/sandbox
2
2
 
3
+ ## 0.3.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [#90](https://github.com/cloudflare/sandbox-sdk/pull/90) [`66cc85b`](https://github.com/cloudflare/sandbox-sdk/commit/66cc85b679b466b3ffb1f00fbd697670fc186f06) Thanks [@eastlondoner](https://github.com/eastlondoner)! - set bun idletimeout
8
+
9
+ ## 0.3.5
10
+
11
+ ### Patch Changes
12
+
13
+ - [#88](https://github.com/cloudflare/sandbox-sdk/pull/88) [`46eb4e6`](https://github.com/cloudflare/sandbox-sdk/commit/46eb4e6b6c671b682fc74f83563ccf5f316011cb) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Add binary file support with automatic MIME detection and streaming
14
+
3
15
  ## 0.3.4
4
16
 
5
17
  ### Patch Changes
package/Dockerfile CHANGED
@@ -13,6 +13,7 @@ RUN apt-get update && apt-get install -y \
13
13
  git \
14
14
  unzip \
15
15
  zip \
16
+ file \
16
17
  # Process management
17
18
  procps \
18
19
  htop \
package/README.md CHANGED
@@ -50,10 +50,11 @@ The Cloudflare Sandbox SDK enables you to run isolated code environments directl
50
50
  - **🔒 Secure Isolation**: Each sandbox runs in its own container with full process isolation
51
51
  - **⚡ Edge-Native**: Runs on Cloudflare's global network for low latency worldwide
52
52
  - **📁 File System Access**: Read, write, and manage files within the sandbox
53
+ - **🖼️ Binary File Support**: Automatic MIME type detection and base64 encoding for images, PDFs, and other binary files
53
54
  - **🔧 Command Execution**: Run any command or process inside the container
54
55
  - **🌐 Preview URLs**: Expose services running in your sandbox via public URLs
55
56
  - **🔄 Git Integration**: Clone repositories directly into sandboxes
56
- - **🚀 Streaming Support**: Real-time output streaming for long-running commands
57
+ - **🚀 Streaming Support**: Real-time output streaming for long-running commands and file transfers
57
58
  - **🎮 Session Management**: Maintain state across multiple operations
58
59
  - **🧪 Code Interpreter**: Execute Python and JavaScript with rich outputs (charts, tables, formatted data)
59
60
  - **📊 Multi-Language Support**: Persistent execution contexts for Python and JavaScript/TypeScript
@@ -72,7 +73,7 @@ npm install @cloudflare/sandbox
72
73
  1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
73
74
 
74
75
  ```dockerfile
75
- FROM docker.io/cloudflare/sandbox:0.3.4
76
+ FROM docker.io/cloudflare/sandbox:0.3.6
76
77
 
77
78
  # Expose the ports you want to expose
78
79
  EXPOSE 3000
@@ -189,11 +190,57 @@ await sandbox.writeFile("/workspace/app.js", "console.log('Hello!');");
189
190
 
190
191
  #### `readFile(path, options?)`
191
192
 
192
- Read a file from the sandbox.
193
+ Read a file from the sandbox with automatic binary detection.
193
194
 
194
195
  ```typescript
196
+ // Read text files
195
197
  const file = await sandbox.readFile("/package.json");
196
- console.log(file.content);
198
+ console.log(file.content); // UTF-8 text content
199
+
200
+ // Read binary files - automatically detected and base64 encoded
201
+ const image = await sandbox.readFile("/workspace/chart.png");
202
+ console.log(image.mimeType); // "image/png"
203
+ console.log(image.isBinary); // true
204
+ console.log(image.encoding); // "base64"
205
+ console.log(image.size); // File size in bytes
206
+
207
+ // Use the base64 content directly in data URLs
208
+ const dataUrl = `data:${image.mimeType};base64,${image.content}`;
209
+ ```
210
+
211
+ #### `readFileStream(path)`
212
+
213
+ Stream large files efficiently with automatic chunking and encoding.
214
+
215
+ ```typescript
216
+ import { streamFile, collectFile } from '@cloudflare/sandbox';
217
+
218
+ // Stream a large file
219
+ const stream = await sandbox.readFileStream("/large-video.mp4");
220
+
221
+ // Option 1: Process chunks as they arrive
222
+ for await (const chunk of streamFile(stream)) {
223
+ if (chunk instanceof Uint8Array) {
224
+ // Binary chunk - already decoded from base64
225
+ console.log(`Received ${chunk.byteLength} bytes`);
226
+ // Process binary data...
227
+ } else {
228
+ // Text chunk
229
+ console.log('Text:', chunk);
230
+ }
231
+ }
232
+
233
+ // Option 2: Collect entire file into memory
234
+ const { content, metadata } = await collectFile(stream);
235
+ console.log(`MIME: ${metadata.mimeType}, Size: ${metadata.size} bytes`);
236
+
237
+ if (content instanceof Uint8Array) {
238
+ // Binary file - ready to save or process
239
+ await writeToStorage(content);
240
+ } else {
241
+ // Text file
242
+ console.log('Content:', content);
243
+ }
197
244
  ```
198
245
 
199
246
  #### `gitCheckout(repoUrl, options?)`
@@ -241,7 +288,8 @@ console.log(result.stdout); // "production"
241
288
  #### File System Methods
242
289
 
243
290
  - `writeFile(path, content, options?)` - Write content to a file
244
- - `readFile(path, options?)` - Read a file from the sandbox
291
+ - `readFile(path, options?)` - Read a file with automatic binary detection and base64 encoding
292
+ - `readFileStream(path)` - Stream large files efficiently with chunking
245
293
  - `mkdir(path, options?)` - Create a directory
246
294
  - `deleteFile(path)` - Delete a file
247
295
  - `renameFile(oldPath, newPath)` - Rename a file
@@ -665,10 +713,15 @@ for await (const event of parseSSEStream<ExecEvent>(stream)) {
665
713
 
666
714
  The SDK exports utilities for working with Server-Sent Event streams:
667
715
 
716
+ **Command Execution:**
668
717
  - **`parseSSEStream<T>(stream)`** - Convert ReadableStream to typed AsyncIterable
669
718
  - **`responseToAsyncIterable<T>(response)`** - Convert SSE Response to AsyncIterable
670
719
  - **`asyncIterableToSSEStream<T>(iterable)`** - Convert AsyncIterable back to SSE stream
671
720
 
721
+ **File Streaming:**
722
+ - **`streamFile(stream, signal?)`** - Convert file SSE stream to AsyncIterable with automatic base64 decoding
723
+ - **`collectFile(stream, signal?)`** - Collect entire file from stream into memory
724
+
672
725
  #### Advanced Streaming Examples
673
726
 
674
727
  **CI/CD Build System:**
@@ -204,6 +204,11 @@ export async function handleReadFileRequest(
204
204
  path,
205
205
  success: result.success,
206
206
  timestamp: new Date().toISOString(),
207
+ // New metadata fields for binary file support
208
+ encoding: result.encoding,
209
+ isBinary: result.isBinary,
210
+ mimeType: result.mimeType,
211
+ size: result.size,
207
212
  }),
208
213
  {
209
214
  headers: {
@@ -403,4 +408,50 @@ export async function handleListFilesRequest(
403
408
  } catch (error) {
404
409
  return createServerErrorResponse("handleListFilesRequest", error, corsHeaders);
405
410
  }
411
+ }
412
+
413
+ export async function handleReadFileStreamRequest(
414
+ req: Request,
415
+ corsHeaders: Record<string, string>,
416
+ sessionManager: SessionManager
417
+ ): Promise<Response> {
418
+ try {
419
+ const body = (await req.json()) as ReadFileRequest;
420
+ const { path, sessionId } = body;
421
+
422
+ // Validate path
423
+ const pathError = validatePath(path);
424
+ if (pathError) {
425
+ return createPathErrorResponse(pathError, corsHeaders);
426
+ }
427
+
428
+ console.log(`[Server] Streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`);
429
+
430
+ // Get the appropriate session
431
+ const session = sessionId
432
+ ? sessionManager.getSession(sessionId)
433
+ : await sessionManager.getOrCreateDefaultSession();
434
+
435
+ if (!session) {
436
+ return createServerErrorResponse(
437
+ "handleReadFileStreamRequest",
438
+ new Error(`Session '${sessionId}' not found`),
439
+ corsHeaders
440
+ );
441
+ }
442
+
443
+ // Create SSE stream
444
+ const stream = await session.readFileStreamOperation(path);
445
+
446
+ return new Response(stream, {
447
+ headers: {
448
+ "Content-Type": "text/event-stream",
449
+ "Cache-Control": "no-cache",
450
+ "Connection": "keep-alive",
451
+ ...corsHeaders,
452
+ },
453
+ });
454
+ } catch (error) {
455
+ return createServerErrorResponse("handleReadFileStreamRequest", error, corsHeaders);
456
+ }
406
457
  }
@@ -9,6 +9,7 @@ import {
9
9
  handleMkdirRequest,
10
10
  handleMoveFileRequest,
11
11
  handleReadFileRequest,
12
+ handleReadFileStreamRequest,
12
13
  handleRenameFileRequest,
13
14
  handleWriteFileRequest,
14
15
  } from "./handler/file";
@@ -86,6 +87,7 @@ console.log("[Container] Interpreter service ready - no cold start!");
86
87
  console.log("[Container] All API endpoints available immediately");
87
88
 
88
89
  const server = serve({
90
+ idleTimeout: 255,
89
91
  async fetch(req: Request) {
90
92
  const url = new URL(req.url);
91
93
  const pathname = url.pathname;
@@ -190,6 +192,12 @@ const server = serve({
190
192
  }
191
193
  break;
192
194
 
195
+ case "/api/read/stream":
196
+ if (req.method === "POST") {
197
+ return handleReadFileStreamRequest(req, corsHeaders, sessionManager);
198
+ }
199
+ break;
200
+
193
201
  case "/api/delete":
194
202
  if (req.method === "POST") {
195
203
  return handleDeleteFileRequest(req, corsHeaders, sessionManager);
@@ -569,6 +577,7 @@ console.log(` POST /api/git/checkout - Checkout a git repository`);
569
577
  console.log(` POST /api/mkdir - Create a directory`);
570
578
  console.log(` POST /api/write - Write a file`);
571
579
  console.log(` POST /api/read - Read a file`);
580
+ console.log(` POST /api/read/stream - Stream a file (SSE)`);
572
581
  console.log(` POST /api/delete - Delete a file`);
573
582
  console.log(` POST /api/rename - Rename a file`);
574
583
  console.log(` POST /api/move - Move a file`);
@@ -446,16 +446,180 @@ SANDBOX_EOF`;
446
446
  };
447
447
  }
448
448
 
449
- async readFileOperation(path: string, encoding: string = 'utf-8'): Promise<{ success: boolean; exitCode: number; content: string; path: string }> {
450
- const command = `cat "${path}"`;
451
- const result = await this.exec(command);
452
-
449
+ async readFileOperation(path: string, encoding: string = 'utf-8'): Promise<{
450
+ success: boolean;
451
+ exitCode: number;
452
+ content: string;
453
+ path: string;
454
+ encoding?: 'utf-8' | 'base64';
455
+ isBinary?: boolean;
456
+ mimeType?: string;
457
+ size?: number;
458
+ }> {
459
+ // Step 1: Check if file exists and get metadata
460
+ const statCommand = `stat -c '%s' "${path}" 2>/dev/null || echo "FILE_NOT_FOUND"`;
461
+ const statResult = await this.exec(statCommand);
462
+
463
+ if (statResult.stdout.trim() === 'FILE_NOT_FOUND') {
464
+ // File doesn't exist - return error
465
+ return {
466
+ success: false,
467
+ exitCode: 1,
468
+ content: '',
469
+ path
470
+ };
471
+ }
472
+
473
+ const fileSize = parseInt(statResult.stdout.trim(), 10);
474
+
475
+ // Step 2: Detect MIME type using file command
476
+ const mimeCommand = `file --mime-type -b "${path}"`;
477
+ const mimeResult = await this.exec(mimeCommand);
478
+ const mimeType = mimeResult.stdout.trim();
479
+
480
+ // Step 3: Determine if file is binary based on MIME type
481
+ // Text MIME types: text/*, application/json, application/xml, application/javascript, etc.
482
+ const isBinary = !mimeType.startsWith('text/') &&
483
+ !mimeType.includes('json') &&
484
+ !mimeType.includes('xml') &&
485
+ !mimeType.includes('javascript') &&
486
+ !mimeType.includes('x-empty');
487
+
488
+ // Step 4: Read file with appropriate encoding
489
+ let content: string;
490
+ let actualEncoding: 'utf-8' | 'base64';
491
+
492
+ if (isBinary) {
493
+ // Use base64 for binary files
494
+ const base64Command = `base64 -w 0 "${path}"`;
495
+ const base64Result = await this.exec(base64Command);
496
+ content = base64Result.stdout;
497
+ actualEncoding = 'base64';
498
+ } else {
499
+ // Use cat for text files
500
+ const catCommand = `cat "${path}"`;
501
+ const catResult = await this.exec(catCommand);
502
+ content = catResult.stdout;
503
+ actualEncoding = 'utf-8';
504
+ }
505
+
453
506
  return {
454
- success: result.exitCode === 0,
455
- exitCode: result.exitCode,
456
- content: result.stdout,
457
- path
507
+ success: true,
508
+ exitCode: 0,
509
+ content,
510
+ path,
511
+ encoding: actualEncoding,
512
+ isBinary,
513
+ mimeType,
514
+ size: fileSize
515
+ };
516
+ }
517
+
518
+ async readFileStreamOperation(path: string): Promise<ReadableStream<Uint8Array>> {
519
+ const encoder = new TextEncoder();
520
+
521
+ // Helper to send SSE event
522
+ const sseEvent = (event: any): Uint8Array => {
523
+ return encoder.encode(`data: ${JSON.stringify(event)}\n\n`);
458
524
  };
525
+
526
+ // Create streaming response
527
+ return new ReadableStream({
528
+ start: async (controller) => {
529
+ try {
530
+ // Step 1: Get file metadata (same logic as readFileOperation)
531
+ const statCommand = `stat -c '%s' "${path}" 2>/dev/null || echo "FILE_NOT_FOUND"`;
532
+ const statResult = await this.exec(statCommand);
533
+
534
+ if (statResult.stdout.trim() === 'FILE_NOT_FOUND') {
535
+ // File doesn't exist - send error event
536
+ controller.enqueue(sseEvent({
537
+ type: 'error',
538
+ error: `File not found: ${path}`
539
+ }));
540
+ controller.close();
541
+ return;
542
+ }
543
+
544
+ const fileSize = parseInt(statResult.stdout.trim(), 10);
545
+
546
+ // Step 2: Detect MIME type
547
+ const mimeCommand = `file --mime-type -b "${path}"`;
548
+ const mimeResult = await this.exec(mimeCommand);
549
+ const mimeType = mimeResult.stdout.trim();
550
+
551
+ // Step 3: Determine if binary
552
+ const isBinary = !mimeType.startsWith('text/') &&
553
+ !mimeType.includes('json') &&
554
+ !mimeType.includes('xml') &&
555
+ !mimeType.includes('javascript') &&
556
+ !mimeType.includes('x-empty');
557
+
558
+ const encoding: 'utf-8' | 'base64' = isBinary ? 'base64' : 'utf-8';
559
+
560
+ // Step 4: Send metadata event
561
+ controller.enqueue(sseEvent({
562
+ type: 'metadata',
563
+ mimeType,
564
+ size: fileSize,
565
+ isBinary,
566
+ encoding
567
+ }));
568
+
569
+ // Step 5: Stream file in chunks
570
+ // IMPORTANT: Chunk size MUST be divisible by 3 for base64 encoding!
571
+ // Base64 encodes 3 bytes at a time. If chunks aren't aligned,
572
+ // concatenating separately-encoded base64 strings corrupts the data.
573
+ const CHUNK_SIZE = 65535;
574
+ let bytesRead = 0;
575
+
576
+ while (bytesRead < fileSize) {
577
+ const remainingBytes = fileSize - bytesRead;
578
+ const chunkSize = Math.min(CHUNK_SIZE, remainingBytes);
579
+
580
+ // Use dd to read chunk at specific offset
581
+ // bs=1 means 1 byte block size, skip=offset, count=chunkSize
582
+ let chunkCommand: string;
583
+ if (isBinary) {
584
+ // For binary, read and encode as base64
585
+ chunkCommand = `dd if="${path}" bs=1 skip=${bytesRead} count=${chunkSize} 2>/dev/null | base64 -w 0`;
586
+ } else {
587
+ // For text, just read
588
+ chunkCommand = `dd if="${path}" bs=1 skip=${bytesRead} count=${chunkSize} 2>/dev/null`;
589
+ }
590
+
591
+ const chunkResult = await this.exec(chunkCommand);
592
+
593
+ // Send chunk event
594
+ controller.enqueue(sseEvent({
595
+ type: 'chunk',
596
+ data: chunkResult.stdout
597
+ }));
598
+
599
+ bytesRead += chunkSize;
600
+ }
601
+
602
+ // Step 6: Send complete event
603
+ controller.enqueue(sseEvent({
604
+ type: 'complete',
605
+ bytesRead
606
+ }));
607
+
608
+ controller.close();
609
+ } catch (error) {
610
+ // Send error event
611
+ controller.enqueue(sseEvent({
612
+ type: 'error',
613
+ error: error instanceof Error ? error.message : String(error)
614
+ }));
615
+ controller.close();
616
+ }
617
+ },
618
+
619
+ cancel() {
620
+ console.log(`[Session] File stream cancelled for: ${path}`);
621
+ }
622
+ });
459
623
  }
460
624
 
461
625
  async mkdirOperation(path: string, recursive: boolean = false): Promise<{ success: boolean; exitCode: number; path: string; recursive: boolean }> {
@@ -1010,7 +1174,7 @@ export class SessionManager {
1010
1174
  return defaultSession.writeFileOperation(path, content, encoding);
1011
1175
  }
1012
1176
 
1013
- async readFile(path: string, encoding?: string): Promise<{ success: boolean; exitCode: number; content: string; path: string }> {
1177
+ async readFile(path: string, encoding?: string): Promise<{ success: boolean; exitCode: number; content: string; path: string; encoding?: 'utf-8' | 'base64'; isBinary?: boolean; mimeType?: string; size?: number }> {
1014
1178
  const defaultSession = await this.getOrCreateDefaultSession();
1015
1179
  return defaultSession.readFileOperation(path, encoding);
1016
1180
  }
@@ -249,6 +249,36 @@ var HttpClient = class {
249
249
  throw error;
250
250
  }
251
251
  }
252
+ async readFileStream(path, sessionId) {
253
+ try {
254
+ const response = await this.doFetch(`/api/read/stream`, {
255
+ method: "POST",
256
+ headers: {
257
+ "Content-Type": "application/json"
258
+ },
259
+ body: JSON.stringify({
260
+ path,
261
+ sessionId
262
+ })
263
+ });
264
+ if (!response.ok) {
265
+ const errorData = await response.json().catch(() => ({}));
266
+ throw new Error(
267
+ errorData.error || `HTTP error! status: ${response.status}`
268
+ );
269
+ }
270
+ if (!response.body) {
271
+ throw new Error("No response body for file streaming");
272
+ }
273
+ console.log(
274
+ `[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ""}`
275
+ );
276
+ return response.body;
277
+ } catch (error) {
278
+ console.error("[HTTP Client] Error streaming file:", error);
279
+ throw error;
280
+ }
281
+ }
252
282
  async deleteFile(path, sessionId) {
253
283
  try {
254
284
  const response = await this.doFetch(`/api/delete`, {
@@ -638,4 +668,4 @@ var HttpClient = class {
638
668
  export {
639
669
  HttpClient
640
670
  };
641
- //# sourceMappingURL=chunk-SMUEY5JR.js.map
671
+ //# sourceMappingURL=chunk-32UDXUPC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type { ExecuteRequest } from \"../container_src/types\";\nimport type { Sandbox } from \"./index\";\nimport type {\n BaseExecOptions,\n DeleteFileResponse,\n ExecuteResponse,\n GetProcessLogsResponse,\n GetProcessResponse,\n GitCheckoutResponse,\n ListFilesResponse,\n ListProcessesResponse,\n MkdirResponse,\n MoveFileResponse,\n ReadFileResponse,\n RenameFileResponse,\n StartProcessRequest,\n StartProcessResponse,\n WriteFileResponse,\n} from \"./types\";\n\n\ninterface CommandsResponse {\n availableCommands: string[];\n timestamp: string;\n}\n\ninterface GitCheckoutRequest {\n repoUrl: string;\n branch?: string;\n targetDir?: string;\n sessionId: string;\n}\n\n\ninterface MkdirRequest {\n path: string;\n recursive?: boolean;\n sessionId: string;\n}\n\n\ninterface WriteFileRequest {\n path: string;\n content: string;\n encoding?: string;\n sessionId: string;\n}\n\n\ninterface ReadFileRequest {\n path: string;\n encoding?: string;\n sessionId: string;\n}\n\n\ninterface DeleteFileRequest {\n path: string;\n sessionId: string;\n}\n\n\ninterface RenameFileRequest {\n oldPath: string;\n newPath: string;\n sessionId: string;\n}\n\n\ninterface MoveFileRequest {\n sourcePath: string;\n destinationPath: string;\n sessionId: string;\n}\n\n\ninterface ListFilesRequest {\n path: string;\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n };\n sessionId: string;\n}\n\n\ninterface PreviewInfo {\n url: string;\n port: number;\n name?: string;\n}\n\ninterface ExposedPort extends PreviewInfo {\n exposedAt: string;\n timestamp: string;\n}\n\ninterface ExposePortResponse {\n success: boolean;\n port: number;\n name?: string;\n exposedAt: string;\n timestamp: string;\n}\n\ninterface UnexposePortResponse {\n success: boolean;\n port: number;\n timestamp: string;\n}\n\ninterface GetExposedPortsResponse {\n ports: ExposedPort[];\n count: number;\n timestamp: string;\n}\n\ninterface PingResponse {\n message: string;\n timestamp: string;\n}\n\ninterface HttpClientOptions {\n stub?: Sandbox;\n baseUrl?: string;\n port?: number;\n onCommandStart?: (command: string) => void;\n onOutput?: (\n stream: \"stdout\" | \"stderr\",\n data: string,\n command: string\n ) => void;\n onCommandComplete?: (\n success: boolean,\n exitCode: number,\n stdout: string,\n stderr: string,\n command: string\n ) => void;\n onError?: (error: string, command?: string) => void;\n}\n\nexport class HttpClient {\n private baseUrl: string;\n private options: HttpClientOptions;\n\n constructor(options: HttpClientOptions = {}) {\n this.options = {\n ...options,\n };\n this.baseUrl = this.options.baseUrl!;\n }\n\n protected async doFetch(\n path: string,\n options?: RequestInit\n ): Promise<Response> {\n const url = this.options.stub\n ? `http://localhost:${this.options.port}${path}`\n : `${this.baseUrl}${path}`;\n const method = options?.method || \"GET\";\n\n console.log(`[HTTP Client] Making ${method} request to ${url}`);\n\n try {\n let response: Response;\n\n if (this.options.stub) {\n response = await this.options.stub.containerFetch(\n url,\n options,\n this.options.port\n );\n } else {\n response = await fetch(url, options);\n }\n\n console.log(\n `[HTTP Client] Response: ${response.status} ${response.statusText}`\n );\n\n if (!response.ok) {\n console.error(\n `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`\n );\n }\n\n return response;\n } catch (error) {\n console.error(`[HTTP Client] Request error: ${method} ${url}`, error);\n throw error;\n }\n }\n\n async createSession(options: {\n id: string;\n env?: Record<string, string>;\n cwd?: string;\n isolation?: boolean;\n }): Promise<{ success: boolean; id: string; message: string }> {\n try {\n const response = await this.doFetch(`/api/session/create`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(options),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `Failed to create session: ${response.status}`\n );\n }\n\n const data = await response.json() as { success: boolean; id: string; message: string };\n console.log(`[HTTP Client] Session created: ${options.id}`);\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error creating session:\", error);\n throw error;\n }\n }\n\n async exec(\n sessionId: string,\n command: string,\n options?: Pick<BaseExecOptions, \"cwd\" | \"env\">\n ): Promise<ExecuteResponse> {\n try {\n // Always use session-specific endpoint\n const response = await this.doFetch(`/api/execute`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ id: sessionId, command }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `Failed to execute in session: ${response.status}`\n );\n }\n\n const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };\n console.log(\n `[HTTP Client] Command executed in session ${sessionId}: ${command}`\n );\n \n // Convert to ExecuteResponse format for consistency\n const executeResponse: ExecuteResponse = {\n ...data,\n command,\n timestamp: new Date().toISOString()\n };\n\n // Call the callback if provided\n this.options.onCommandComplete?.(\n executeResponse.success,\n executeResponse.exitCode,\n executeResponse.stdout,\n executeResponse.stderr,\n executeResponse.command\n );\n\n return executeResponse;\n } catch (error) {\n console.error(\"[HTTP Client] Error executing in session:\", error);\n this.options.onError?.(\n error instanceof Error ? error.message : \"Unknown error\",\n command\n );\n throw error;\n }\n }\n\n async execStream(\n sessionId: string,\n command: string\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n // Always use session-specific streaming endpoint\n const response = await this.doFetch(`/api/execute/stream`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ \n id: sessionId,\n command\n }),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `Failed to stream execute in session: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming execution\");\n }\n\n console.log(\n `[HTTP Client] Started streaming command in session ${sessionId}: ${command}`\n );\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error streaming execute in session:\", error);\n throw error;\n }\n }\n\n async gitCheckout(\n repoUrl: string,\n sessionId: string,\n branch: string = \"main\",\n targetDir?: string\n ): Promise<GitCheckoutResponse> {\n try {\n const response = await this.doFetch(`/api/git/checkout`, {\n body: JSON.stringify({\n branch,\n repoUrl,\n targetDir,\n sessionId,\n } as GitCheckoutRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GitCheckoutResponse = await response.json();\n console.log(\n `[HTTP Client] Git checkout completed: ${repoUrl}, Success: ${data.success}, Target: ${data.targetDir}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error in git checkout:\", error);\n throw error;\n }\n }\n\n async mkdir(\n path: string,\n recursive: boolean = false,\n sessionId: string\n ): Promise<MkdirResponse> {\n try {\n const response = await this.doFetch(`/api/mkdir`, {\n body: JSON.stringify({\n path,\n recursive,\n sessionId,\n } as MkdirRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MkdirResponse = await response.json();\n console.log(\n `[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error creating directory:\", error);\n throw error;\n }\n }\n\n async writeFile(\n path: string,\n content: string,\n encoding: string = \"utf-8\",\n sessionId: string\n ): Promise<WriteFileResponse> {\n try {\n const response = await this.doFetch(`/api/write`, {\n body: JSON.stringify({\n content,\n encoding,\n path,\n sessionId,\n } as WriteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: WriteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error writing file:\", error);\n throw error;\n }\n }\n\n async readFile(\n path: string,\n encoding: string = \"utf-8\",\n sessionId: string\n ): Promise<ReadFileResponse> {\n try {\n const response = await this.doFetch(`/api/read`, {\n body: JSON.stringify({\n encoding,\n path,\n sessionId,\n } as ReadFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ReadFileResponse = await response.json();\n console.log(\n `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error reading file:\", error);\n throw error;\n }\n }\n\n async readFileStream(\n path: string,\n sessionId: string\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n const response = await this.doFetch(`/api/read/stream`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path,\n sessionId,\n } as ReadFileRequest),\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for file streaming\");\n }\n\n console.log(\n `[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error streaming file:\", error);\n throw error;\n }\n }\n\n async deleteFile(\n path: string,\n sessionId: string\n ): Promise<DeleteFileResponse> {\n try {\n const response = await this.doFetch(`/api/delete`, {\n body: JSON.stringify({\n path,\n sessionId,\n } as DeleteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: DeleteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error deleting file:\", error);\n throw error;\n }\n }\n\n async renameFile(\n oldPath: string,\n newPath: string,\n sessionId: string\n ): Promise<RenameFileResponse> {\n try {\n const response = await this.doFetch(`/api/rename`, {\n body: JSON.stringify({\n newPath,\n oldPath,\n sessionId,\n } as RenameFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: RenameFileResponse = await response.json();\n console.log(\n `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error renaming file:\", error);\n throw error;\n }\n }\n\n async moveFile(\n sourcePath: string,\n destinationPath: string,\n sessionId: string\n ): Promise<MoveFileResponse> {\n try {\n const response = await this.doFetch(`/api/move`, {\n body: JSON.stringify({\n destinationPath,\n sourcePath,\n sessionId,\n } as MoveFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MoveFileResponse = await response.json();\n console.log(\n `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error moving file:\", error);\n throw error;\n }\n }\n\n async listFiles(\n path: string,\n sessionId: string,\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n }\n ): Promise<ListFilesResponse> {\n try {\n const response = await this.doFetch(`/api/list-files`, {\n body: JSON.stringify({\n path,\n options,\n sessionId,\n } as ListFilesRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ListFilesResponse = await response.json();\n console.log(\n `[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error listing files:\", error);\n throw error;\n }\n }\n\n async exposePort(port: number, name?: string): Promise<ExposePortResponse> {\n try {\n const response = await this.doFetch(`/api/expose-port`, {\n body: JSON.stringify({\n port,\n name,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n console.log(errorData);\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ExposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port exposed: ${port}${\n name ? ` (${name})` : \"\"\n }, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error exposing port:\", error);\n throw error;\n }\n }\n\n async unexposePort(port: number): Promise<UnexposePortResponse> {\n try {\n const response = await this.doFetch(`/api/unexpose-port`, {\n body: JSON.stringify({\n port,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: UnexposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error unexposing port:\", error);\n throw error;\n }\n }\n\n async getExposedPorts(): Promise<GetExposedPortsResponse> {\n try {\n const response = await this.doFetch(`/api/exposed-ports`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetExposedPortsResponse = await response.json();\n console.log(`[HTTP Client] Got ${data.count} exposed ports`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting exposed ports:\", error);\n throw error;\n }\n }\n\n async ping(): Promise<string> {\n try {\n const response = await this.doFetch(`/api/ping`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data: PingResponse = await response.json();\n console.log(`[HTTP Client] Ping response: ${data.message}`);\n return data.timestamp;\n } catch (error) {\n console.error(\"[HTTP Client] Error pinging server:\", error);\n throw error;\n }\n }\n\n\n // Process management methods\n async startProcess(\n command: string,\n sessionId: string,\n options?: {\n processId?: string;\n timeout?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n }\n ): Promise<StartProcessResponse> {\n try {\n const response = await this.doFetch(\"/api/process/start\", {\n body: JSON.stringify({\n command,\n sessionId,\n options,\n } as StartProcessRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: StartProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error starting process:\", error);\n throw error;\n }\n }\n\n async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {\n try {\n const url = sessionId \n ? `/api/process/list?session=${encodeURIComponent(sessionId)}`\n : \"/api/process/list\";\n const response = await this.doFetch(url, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ListProcessesResponse = await response.json();\n console.log(`[HTTP Client] Listed ${data.processes.length} processes`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error listing processes:\", error);\n throw error;\n }\n }\n\n async getProcess(processId: string): Promise<GetProcessResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Got process ${processId}: ${\n data.process?.status || \"not found\"\n }`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process:\", error);\n throw error;\n }\n }\n\n async killProcess(\n processId: string\n ): Promise<{ success: boolean; message: string }> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = (await response.json()) as {\n success: boolean;\n message: string;\n };\n console.log(`[HTTP Client] Killed process ${processId}`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing process:\", error);\n throw error;\n }\n }\n\n async killAllProcesses(sessionId?: string): Promise<{\n success: boolean;\n killedCount: number;\n message: string;\n }> {\n try {\n const url = sessionId \n ? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`\n : \"/api/process/kill-all\";\n const response = await this.doFetch(url, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = (await response.json()) as {\n success: boolean;\n killedCount: number;\n message: string;\n };\n console.log(`[HTTP Client] Killed ${data.killedCount} processes`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing all processes:\", error);\n throw error;\n }\n }\n\n async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/logs`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessLogsResponse = await response.json();\n console.log(`[HTTP Client] Got logs for process ${processId}`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process logs:\", error);\n throw error;\n }\n }\n\n async streamProcessLogs(\n processId: string,\n options?: { signal?: AbortSignal }\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/stream`, {\n headers: {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n },\n method: \"GET\",\n signal: options?.signal,\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming request\");\n }\n\n console.log(\n `[HTTP Client] Started streaming logs for process ${processId}`\n );\n\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error streaming process logs:\", error);\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,85 @@
1
+ import {
2
+ parseSSEStream
3
+ } from "./chunk-NNGBXDMY.js";
4
+
5
+ // src/file-stream.ts
6
+ async function* streamFile(stream, signal) {
7
+ let metadata;
8
+ try {
9
+ for await (const event of parseSSEStream(stream, signal)) {
10
+ switch (event.type) {
11
+ case "metadata":
12
+ metadata = {
13
+ mimeType: event.mimeType,
14
+ size: event.size,
15
+ isBinary: event.isBinary,
16
+ encoding: event.encoding
17
+ };
18
+ streamFile.metadata = metadata;
19
+ break;
20
+ case "chunk":
21
+ if (metadata?.isBinary && metadata?.encoding === "base64") {
22
+ const binaryString = atob(event.data);
23
+ const bytes = new Uint8Array(binaryString.length);
24
+ for (let i = 0; i < binaryString.length; i++) {
25
+ bytes[i] = binaryString.charCodeAt(i);
26
+ }
27
+ yield bytes;
28
+ } else {
29
+ yield event.data;
30
+ }
31
+ break;
32
+ case "complete":
33
+ console.log(`[streamFile] File streaming complete: ${event.bytesRead} bytes read`);
34
+ return;
35
+ case "error":
36
+ throw new Error(`File streaming error: ${event.error}`);
37
+ }
38
+ }
39
+ } catch (error) {
40
+ console.error("[streamFile] Error streaming file:", error);
41
+ throw error;
42
+ }
43
+ }
44
+ async function collectFile(stream, signal) {
45
+ let metadata;
46
+ const chunks = [];
47
+ for await (const chunk of streamFile(stream, signal)) {
48
+ chunks.push(chunk);
49
+ if (!metadata && streamFile.metadata) {
50
+ metadata = streamFile.metadata;
51
+ }
52
+ }
53
+ if (!metadata) {
54
+ throw new Error("No metadata received from file stream");
55
+ }
56
+ if (chunks.length === 0) {
57
+ return {
58
+ content: metadata.isBinary ? new Uint8Array(0) : "",
59
+ metadata
60
+ };
61
+ }
62
+ if (chunks[0] instanceof Uint8Array) {
63
+ const totalLength = chunks.reduce((sum, chunk) => {
64
+ return sum + chunk.byteLength;
65
+ }, 0);
66
+ const result = new Uint8Array(totalLength);
67
+ let offset = 0;
68
+ for (const chunk of chunks) {
69
+ result.set(chunk, offset);
70
+ offset += chunk.byteLength;
71
+ }
72
+ return { content: result, metadata };
73
+ } else {
74
+ return {
75
+ content: chunks.join(""),
76
+ metadata
77
+ };
78
+ }
79
+ }
80
+
81
+ export {
82
+ streamFile,
83
+ collectFile
84
+ };
85
+ //# sourceMappingURL=chunk-5DILEXGY.js.map