@cloudflare/sandbox 0.0.0-603d05f → 0.0.0-66cc85b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @cloudflare/sandbox
2
2
 
3
+ ## 0.3.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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
8
+
9
+ ## 0.3.4
10
+
11
+ ### Patch Changes
12
+
13
+ - [#86](https://github.com/cloudflare/sandbox-sdk/pull/86) [`feafd32`](https://github.com/cloudflare/sandbox-sdk/commit/feafd32a51f50dfaf4994bddcbfb56d46cada622) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Fix session reuse to reuse existing healthy session
14
+
15
+ ## 0.3.3
16
+
17
+ ### Patch Changes
18
+
19
+ - [#83](https://github.com/cloudflare/sandbox-sdk/pull/83) [`eec5bb6`](https://github.com/cloudflare/sandbox-sdk/commit/eec5bb6203dd5d775b4b54e91c26de25eeb767ce) Thanks [@mikenomitch](https://github.com/mikenomitch)! - Bump containers package version
20
+
21
+ ## 0.3.2
22
+
23
+ ### Patch Changes
24
+
25
+ - [#76](https://github.com/cloudflare/sandbox-sdk/pull/76) [`ef9e320`](https://github.com/cloudflare/sandbox-sdk/commit/ef9e320dcef30e57797fef6ebd9a9383fa9720d9) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Replace Jupyter with lightweight interpreters for >90% faster cold starts for `.runCode` calls, while maintaining full code execution capabilities and rich output support.
26
+
27
+ ## 0.3.1
28
+
29
+ ### Patch Changes
30
+
31
+ - [#71](https://github.com/cloudflare/sandbox-sdk/pull/71) [`fb3c9c2`](https://github.com/cloudflare/sandbox-sdk/commit/fb3c9c22242d9d4f157c26f547f1e697ef7875f9) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Bump containers package version
32
+
33
+ - [#70](https://github.com/cloudflare/sandbox-sdk/pull/70) [`e1fa354`](https://github.com/cloudflare/sandbox-sdk/commit/e1fa354ab1bc7b0e89db4901b67028ebf1a93d0a) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Fix escaped quotes in file write operations
34
+
35
+ - [#68](https://github.com/cloudflare/sandbox-sdk/pull/68) [`69b91d1`](https://github.com/cloudflare/sandbox-sdk/commit/69b91d1a8f6afb63262cc381ea93e94a033ed5e8) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Configurable timeouts via environment variables in isolation.ts
36
+
37
+ - [#66](https://github.com/cloudflare/sandbox-sdk/pull/66) [`eca93b9`](https://github.com/cloudflare/sandbox-sdk/commit/eca93b97e40fa0d3bd9dc27af2cc214ec355b696) Thanks [@peterp](https://github.com/peterp)! - Determine if the port is specified in the URL.
38
+
3
39
  ## 0.3.0
4
40
 
5
41
  ### Minor 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 \
@@ -51,24 +52,12 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
51
52
  COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
52
53
  COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx
53
54
 
54
- # Install minimal Jupyter components
55
- RUN pip3 install --no-cache-dir \
56
- jupyter-server \
57
- jupyter-client \
58
- ipykernel \
59
- orjson \
60
- && python3 -m ipykernel install --user --name python3
61
-
62
- # Install scientific packages
55
+ # Install essential Python packages for code execution
63
56
  RUN pip3 install --no-cache-dir \
64
57
  matplotlib \
65
58
  numpy \
66
59
  pandas \
67
- seaborn
68
-
69
- # Install JavaScript kernel (ijavascript) - using E2B's fork
70
- RUN npm install -g git+https://github.com/e2b-dev/ijavascript.git \
71
- && ijsinstall --install=global
60
+ ipython
72
61
 
73
62
  # Set up container server directory
74
63
  WORKDIR /container-server
@@ -77,9 +66,7 @@ WORKDIR /container-server
77
66
  RUN python3 --version && \
78
67
  node --version && \
79
68
  npm --version && \
80
- bun --version && \
81
- jupyter --version && \
82
- jupyter kernelspec list
69
+ bun --version
83
70
 
84
71
  # Copy container source files to server directory
85
72
  COPY container_src/package.json container_src/bun.lock ./
@@ -87,15 +74,27 @@ RUN bun install --frozen-lockfile
87
74
 
88
75
  COPY container_src/ ./
89
76
 
90
- # Compile TypeScript control process
91
- # Use npx -p typescript to ensure we get the right tsc command
92
- RUN npx -p typescript tsc control-process.ts --outDir . --module commonjs --target es2020 --esModuleInterop --skipLibCheck
93
-
94
- # Create clean workspace directory for users
77
+ # Compile TypeScript files using the locally installed TypeScript
78
+ RUN npx tsc control-process.ts --outDir . --module commonjs --target es2020 --esModuleInterop --skipLibCheck
79
+ RUN cd runtime/executors/javascript && npx tsc node_executor.ts --module commonjs --target es2020 --esModuleInterop --skipLibCheck
80
+ RUN cd runtime/executors/typescript && npx tsc ts_executor.ts --module commonjs --target es2020 --esModuleInterop --skipLibCheck
81
+
82
+ # Configure process pool sizes (can be overridden at runtime)
83
+ ENV PYTHON_POOL_MIN_SIZE=3
84
+ ENV PYTHON_POOL_MAX_SIZE=15
85
+ ENV JAVASCRIPT_POOL_MIN_SIZE=3
86
+ ENV JAVASCRIPT_POOL_MAX_SIZE=10
87
+ ENV TYPESCRIPT_POOL_MIN_SIZE=3
88
+ ENV TYPESCRIPT_POOL_MAX_SIZE=10
89
+
90
+ # Create clean workspace directory for user code
91
+ # Architecture:
92
+ # /container-server/ - SDK infrastructure (server, executors, dependencies)
93
+ # /workspace/ - User's clean workspace for their code
95
94
  RUN mkdir -p /workspace
96
95
 
97
- # Expose the application port (3000 for control, 8888 for Jupyter)
98
- EXPOSE 3000 8888
96
+ # Expose the application port (3000 for control)
97
+ EXPOSE 3000
99
98
 
100
99
  # Make startup script executable
101
100
  RUN chmod +x startup.sh
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.0
76
+ FROM docker.io/cloudflare/sandbox:0.3.5
76
77
 
77
78
  # Expose the ports you want to expose
78
79
  EXPOSE 3000
@@ -89,7 +90,7 @@ EXPOSE 3000
89
90
  {
90
91
  "class_name": "Sandbox",
91
92
  "image": "./Dockerfile",
92
- "max_instances": 1
93
+ "max_instances": 20
93
94
  }
94
95
  ],
95
96
  "durable_objects": {
@@ -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:**
@@ -4,119 +4,73 @@
4
4
  "": {
5
5
  "name": "sandbox-server",
6
6
  "dependencies": {
7
- "@jupyterlab/services": "^7.0.0",
7
+ "esbuild": "^0.21.5",
8
8
  "uuid": "^9.0.1",
9
- "ws": "^8.16.0",
10
9
  },
11
10
  "devDependencies": {
11
+ "@types/node": "^20.0.0",
12
12
  "@types/uuid": "^9.0.7",
13
- "@types/ws": "^8.5.10",
13
+ "typescript": "^5.3.0",
14
14
  },
15
15
  },
16
16
  },
17
17
  "packages": {
18
- "@jupyter/ydoc": ["@jupyter/ydoc@3.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-+hLNUBZr8Zz8NiuaoYKINDURDJHbjFGxg8EcSU52y+rBe412TsoFxfPSng4eP7w3cZFrVlm7D+8K0nAMHxj0ZQ=="],
18
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
19
19
 
20
- "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.4.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-fNEefnqTNP/3alrmGYDb0Mu1heS5Cu39FIDiDDoZuuWv4bsdE2xf7VaStuwjljsQm7VZwi5aeholi1+ciBkZKg=="],
20
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
21
21
 
22
- "@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-VOlm1klsb1LUNp47AL8iDZPP71pXu9a+WqukJFuuWVxg//+iR6wdqTYcunE9Lx/7SZDqMsXGD+doBZC36Lzenw=="],
22
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
23
23
 
24
- "@jupyterlab/services": ["@jupyterlab/services@7.4.5", "", { "dependencies": { "@jupyter/ydoc": "^3.0.4", "@jupyterlab/coreutils": "^6.4.5", "@jupyterlab/nbformat": "^4.4.5", "@jupyterlab/settingregistry": "^4.4.5", "@jupyterlab/statedb": "^4.4.5", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/polling": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4", "ws": "^8.11.0" } }, "sha512-JEr8+4VS2MqFyYHh4AnpK/g5V0qRBpmnfXmU0p9YFjcIgGR365D8JF0880IJ0e8+jaW88gSAvZFB8x3o9xRoAQ=="],
24
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
25
25
 
26
- "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.4.5", "", { "dependencies": { "@jupyterlab/nbformat": "^4.4.5", "@jupyterlab/statedb": "^4.4.5", "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-6hEBq3qI99VZwO97W0AM0mP1is57bWC8Vk2xudic1a90wA9G6ovUBzayqSMm/6QhTuIbCY+vruznwhhsLrVMeg=="],
26
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
27
27
 
28
- "@jupyterlab/statedb": ["@jupyterlab/statedb@4.4.5", "", { "dependencies": { "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4" } }, "sha512-UpqhOujKwoWoxtNBL2Qk0nrw+ORLJ3ckwiJg2eA0CI+n5kO3IBYAnPzak0tSS0mQNspayXr3KAm4GQ80op5yuA=="],
28
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
29
29
 
30
- "@lumino/algorithm": ["@lumino/algorithm@2.0.3", "", {}, "sha512-DIcF7cIrGEC1Wh8DNjdwaL7IdcNs4Jj1VjO/90oHefeQPszKgc6DSfCxvbQiRanKR6tl/JL7tq4ZRPZES2oVAA=="],
30
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
31
31
 
32
- "@lumino/commands": ["@lumino/commands@2.3.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/domutils": "^2.0.3", "@lumino/keyboard": "^2.0.3", "@lumino/signaling": "^2.1.4", "@lumino/virtualdom": "^2.0.3" } }, "sha512-aAFEiUpp2hrkQU82Z85w1L80g0iDzsQRncLBa+pqVR/k0k1lz6H9F6xZ1ff+lBumZKKtsxBxNomvd0hfxLLqGw=="],
32
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
33
33
 
34
- "@lumino/coreutils": ["@lumino/coreutils@2.2.1", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-yij4TnxDIum7xfFUsVvZB0oLv4shs2mNbn3juwtEIsruvVBPmurNzKX0Y8z2QetbP2AZ6MSFtBzEKsihf0H0VA=="],
34
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
35
35
 
36
- "@lumino/disposable": ["@lumino/disposable@2.1.4", "", { "dependencies": { "@lumino/signaling": "^2.1.4" } }, "sha512-qTJiDbglPE2QnG4x4gtBcRbcfKQibxyyinNGKcNDrcK2TGTbbhK5PpMQ8d70l2V2Xw2pb/LfksBAg5pxkJ/G4A=="],
36
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
37
37
 
38
- "@lumino/domutils": ["@lumino/domutils@2.0.3", "", {}, "sha512-bXAbZg3mf2ZDNdBBpCGDike3U+osRGHePTh8H2Ud2KwaN4g/5IryFJm/TiO4K5IYs91bWF2Zqhf3FsdbZKHlGw=="],
38
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
39
39
 
40
- "@lumino/keyboard": ["@lumino/keyboard@2.0.3", "", {}, "sha512-bU2OxAR8a9eNBdV0YFjU6/lVVpbOw1gM7yHOuDGDdNu4J0UpKapFoR9gopNGSaHTmTwDtx9RHdFfIAgHwjZ+VQ=="],
40
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
41
41
 
42
- "@lumino/polling": ["@lumino/polling@2.1.4", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4" } }, "sha512-gSkxlIJ/4/esY2G7bsRrY9A4KimDMHTo0shaD+MCbhd09fZMCWJoDMcA447/dykB1rM5NXgugNLjpdGGL/e8cw=="],
42
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
43
43
 
44
- "@lumino/properties": ["@lumino/properties@2.0.3", "", {}, "sha512-zkXIU5uYz/ScHCHGl5Bt4gMYsfPxZEduZd80zqDslBWvTIMro3NnzLe66NMnecbdr5N3hDJagYyA8//Qy3XjiA=="],
44
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
45
45
 
46
- "@lumino/signaling": ["@lumino/signaling@2.1.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1" } }, "sha512-nC5Z6d9om369Jkh1Vp3b7C89hV4cjr1fQDVcxhemyKXwc9r6VW7FpKixC+jElcAknP5KLj1FAa8Np+K06mMkEA=="],
46
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
47
47
 
48
- "@lumino/virtualdom": ["@lumino/virtualdom@2.0.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-q2C8eBxPvvOOQjN3KuxZ+vJi082JH/GF7KwMdaWsy5g+7wjKdnXPuLQFTBLOrVqIzmbxBDlLeFr93CEhdQXcyQ=="],
48
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
49
49
 
50
- "@rjsf/utils": ["@rjsf/utils@5.24.12", "", { "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "react-is": "^18.2.0" }, "peerDependencies": { "react": "^16.14.0 || >=17" } }, "sha512-fDwQB0XkjZjpdFUz5UAnuZj8nnbxDbX5tp+jTOjjJKw2TMQ9gFFYCQ12lSpdhezA2YgEGZfxyYTGW0DKDL5Drg=="],
50
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
51
51
 
52
- "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
52
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
53
53
 
54
- "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
55
-
56
- "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
57
-
58
- "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
59
-
60
- "compute-gcd": ["compute-gcd@1.2.1", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2", "validate.io-integer-array": "^1.0.0" } }, "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg=="],
61
-
62
- "compute-lcm": ["compute-lcm@1.1.2", "", { "dependencies": { "compute-gcd": "^1.2.1", "validate.io-array": "^1.0.3", "validate.io-function": "^1.0.2", "validate.io-integer-array": "^1.0.0" } }, "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ=="],
63
-
64
- "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
65
-
66
- "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="],
67
-
68
- "isomorphic.js": ["isomorphic.js@0.2.5", "", {}, "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw=="],
69
-
70
- "json-schema-compare": ["json-schema-compare@0.2.2", "", { "dependencies": { "lodash": "^4.17.4" } }, "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ=="],
71
-
72
- "json-schema-merge-allof": ["json-schema-merge-allof@0.8.1", "", { "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", "lodash": "^4.17.20" } }, "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w=="],
73
-
74
- "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
75
-
76
- "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
77
-
78
- "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="],
54
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
79
55
 
80
- "lib0": ["lib0@0.2.114", "", { "dependencies": { "isomorphic.js": "^0.2.4" }, "bin": { "0gentesthtml": "bin/gentesthtml.js", "0serve": "bin/0serve.js", "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js" } }, "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ=="],
56
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
81
57
 
82
- "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
58
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
83
59
 
84
- "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
60
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
85
61
 
86
- "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
62
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
87
63
 
88
- "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
64
+ "@types/node": ["@types/node@20.19.16", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-VS6TTONVdgwJwtJr7U+ghEjpfmQdqehLLpg/iMYGOd1+ilaFjdBJwFuPggJ4EAYPDCzWfDUHoIxyVnu+tOWVuQ=="],
89
65
 
90
- "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="],
91
-
92
- "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
93
-
94
- "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
95
-
96
- "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
66
+ "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="],
97
67
 
98
- "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="],
68
+ "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
99
69
 
100
- "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
70
+ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
101
71
 
102
- "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="],
72
+ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
103
73
 
104
74
  "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
105
-
106
- "validate.io-array": ["validate.io-array@1.0.6", "", {}, "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg=="],
107
-
108
- "validate.io-function": ["validate.io-function@1.0.2", "", {}, "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ=="],
109
-
110
- "validate.io-integer": ["validate.io-integer@1.0.5", "", { "dependencies": { "validate.io-number": "^1.0.3" } }, "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ=="],
111
-
112
- "validate.io-integer-array": ["validate.io-integer-array@1.0.0", "", { "dependencies": { "validate.io-array": "^1.0.3", "validate.io-integer": "^1.0.4" } }, "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA=="],
113
-
114
- "validate.io-number": ["validate.io-number@1.0.3", "", {}, "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="],
115
-
116
- "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
117
-
118
- "y-protocols": ["y-protocols@1.0.6", "", { "dependencies": { "lib0": "^0.2.85" }, "peerDependencies": { "yjs": "^13.0.0" } }, "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q=="],
119
-
120
- "yjs": ["yjs@13.6.27", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw=="],
121
75
  }
122
76
  }
@@ -654,7 +654,7 @@ async function handleControlMessage(msg: ControlMessage): Promise<void> {
654
654
  *
655
655
  * Creates either an isolated shell using 'unshare' or a regular bash shell.
656
656
  * Isolation uses Linux namespaces (PID) to prevent the shell from:
657
- * - Seeing control plane processes (Jupyter, Bun server)
657
+ * - Seeing control plane processes (Bun server)
658
658
  * - Accessing platform secrets in /proc/1/environ
659
659
  * - Hijacking control plane ports
660
660
  */
@@ -1,5 +1,5 @@
1
- import { SessionManager } from "../isolation";
2
- import { SessionExecRequest } from "../types";
1
+ import type { SessionManager } from "../isolation";
2
+ import type { SessionExecRequest } from "../types";
3
3
 
4
4
  export async function handleExecuteRequest(
5
5
  req: Request,
@@ -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
  }
@@ -1,5 +1,5 @@
1
- import { SessionManager } from "../isolation";
2
- import { CreateSessionRequest } from "../types";
1
+ import type { SessionManager } from "../isolation";
2
+ import type { CreateSessionRequest } from "../types";
3
3
 
4
4
  export async function handleCreateSession(
5
5
  req: Request,