@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.
@@ -1,3 +1,4 @@
1
+ import type { InterpreterClient } from "./interpreter-client.js";
1
2
  import {
2
3
  type CodeContext,
3
4
  type CreateContextOptions,
@@ -5,15 +6,14 @@ import {
5
6
  ResultImpl,
6
7
  type RunCodeOptions,
7
8
  } from "./interpreter-types.js";
8
- import type { JupyterClient } from "./jupyter-client.js";
9
9
  import type { Sandbox } from "./sandbox.js";
10
10
 
11
11
  export class CodeInterpreter {
12
- private jupyterClient: JupyterClient;
12
+ private interpreterClient: InterpreterClient;
13
13
  private contexts = new Map<string, CodeContext>();
14
14
 
15
15
  constructor(sandbox: Sandbox) {
16
- this.jupyterClient = sandbox.client as JupyterClient;
16
+ this.interpreterClient = sandbox.client as InterpreterClient;
17
17
  }
18
18
 
19
19
  /**
@@ -22,7 +22,7 @@ export class CodeInterpreter {
22
22
  async createCodeContext(
23
23
  options: CreateContextOptions = {}
24
24
  ): Promise<CodeContext> {
25
- const context = await this.jupyterClient.createCodeContext(options);
25
+ const context = await this.interpreterClient.createCodeContext(options);
26
26
  this.contexts.set(context.id, context);
27
27
  return context;
28
28
  }
@@ -46,7 +46,7 @@ export class CodeInterpreter {
46
46
  const execution = new Execution(code, context);
47
47
 
48
48
  // Stream execution
49
- await this.jupyterClient.runCodeStream(context.id, code, options.language, {
49
+ await this.interpreterClient.runCodeStream(context.id, code, options.language, {
50
50
  onStdout: (output) => {
51
51
  execution.logs.stdout.push(output.text);
52
52
  if (options.onStdout) return options.onStdout(output);
@@ -83,7 +83,7 @@ export class CodeInterpreter {
83
83
  }
84
84
 
85
85
  // Create streaming response
86
- const response = await this.jupyterClient.doFetch("/api/execute/code", {
86
+ const response = await this.interpreterClient.doFetch("/api/execute/code", {
87
87
  method: "POST",
88
88
  headers: {
89
89
  "Content-Type": "application/json",
@@ -116,7 +116,7 @@ export class CodeInterpreter {
116
116
  * List all code contexts
117
117
  */
118
118
  async listCodeContexts(): Promise<CodeContext[]> {
119
- const contexts = await this.jupyterClient.listCodeContexts();
119
+ const contexts = await this.interpreterClient.listCodeContexts();
120
120
 
121
121
  // Update local cache
122
122
  for (const context of contexts) {
@@ -130,7 +130,7 @@ export class CodeInterpreter {
130
130
  * Delete a code context
131
131
  */
132
132
  async deleteCodeContext(contextId: string): Promise<void> {
133
- await this.jupyterClient.deleteCodeContext(contextId);
133
+ await this.interpreterClient.deleteCodeContext(contextId);
134
134
  this.contexts.delete(contextId);
135
135
  }
136
136
 
package/src/sandbox.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { Container, getContainer } from "@cloudflare/containers";
2
2
  import { CodeInterpreter } from "./interpreter";
3
+ import { InterpreterClient } from "./interpreter-client";
3
4
  import type {
4
5
  CodeContext,
5
6
  CreateContextOptions,
6
7
  ExecutionResult,
7
8
  RunCodeOptions,
8
9
  } from "./interpreter-types";
9
- import { JupyterClient } from "./jupyter-client";
10
10
  import { isLocalhostPattern } from "./request-handler";
11
11
  import {
12
12
  logSecurityEvent,
@@ -41,14 +41,14 @@ export function getSandbox(ns: DurableObjectNamespace<Sandbox>, id: string) {
41
41
  export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
42
42
  defaultPort = 3000; // Default port for the container's Bun server
43
43
  sleepAfter = "20m"; // Keep container warm for 20 minutes to avoid cold starts
44
- client: JupyterClient;
44
+ client: InterpreterClient;
45
45
  private sandboxName: string | null = null;
46
46
  private codeInterpreter: CodeInterpreter;
47
47
  private defaultSession: ExecutionSession | null = null;
48
48
 
49
- constructor(ctx: DurableObjectState, env: Env) {
49
+ constructor(ctx: DurableObjectState<{}>, env: Env) {
50
50
  super(ctx, env);
51
- this.client = new JupyterClient({
51
+ this.client = new InterpreterClient({
52
52
  onCommandComplete: (success, exitCode, _stdout, _stderr, command) => {
53
53
  console.log(
54
54
  `[Container] Command completed: ${command}, Success: ${success}, Exit code: ${exitCode}`
@@ -135,6 +135,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
135
135
  return parseInt(proxyMatch[1]);
136
136
  }
137
137
 
138
+ if (url.port) {
139
+ return parseInt(url.port);
140
+ }
141
+
138
142
  // All other requests go to control plane on port 3000
139
143
  // This includes /api/* endpoints and any other control requests
140
144
  return 3000;
@@ -260,6 +264,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
260
264
  return session.readFile(path, options);
261
265
  }
262
266
 
267
+ async readFileStream(path: string): Promise<ReadableStream<Uint8Array>> {
268
+ const session = await this.ensureDefaultSession();
269
+ return session.readFileStream(path);
270
+ }
271
+
263
272
  async listFiles(
264
273
  path: string,
265
274
  options: {
@@ -674,7 +683,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
674
683
  readFile: async (path: string, options?: { encoding?: string }) => {
675
684
  return await this.client.readFile(path, options?.encoding, sessionId);
676
685
  },
677
-
686
+
687
+ readFileStream: async (path: string) => {
688
+ return await this.client.readFileStream(path, sessionId);
689
+ },
690
+
678
691
  mkdir: async (path: string, options?: { recursive?: boolean }) => {
679
692
  return await this.client.mkdir(path, options?.recursive, sessionId);
680
693
  },
package/src/types.ts CHANGED
@@ -215,6 +215,54 @@ export interface StreamOptions extends BaseExecOptions {
215
215
  signal?: AbortSignal;
216
216
  }
217
217
 
218
+ // File Streaming Types
219
+
220
+ /**
221
+ * SSE events for file streaming
222
+ */
223
+ export type FileStreamEvent =
224
+ | {
225
+ type: 'metadata';
226
+ mimeType: string;
227
+ size: number;
228
+ isBinary: boolean;
229
+ encoding: 'utf-8' | 'base64';
230
+ }
231
+ | {
232
+ type: 'chunk';
233
+ data: string; // base64 for binary, UTF-8 for text
234
+ }
235
+ | {
236
+ type: 'complete';
237
+ bytesRead: number;
238
+ }
239
+ | {
240
+ type: 'error';
241
+ error: string;
242
+ };
243
+
244
+ /**
245
+ * File metadata from streaming
246
+ */
247
+ export interface FileMetadata {
248
+ mimeType: string;
249
+ size: number;
250
+ isBinary: boolean;
251
+ encoding: 'utf-8' | 'base64';
252
+ }
253
+
254
+ /**
255
+ * File stream chunk - either string (text) or Uint8Array (binary, auto-decoded)
256
+ */
257
+ export type FileChunk = string | Uint8Array;
258
+
259
+ /**
260
+ * AsyncIterable of file chunks with metadata
261
+ */
262
+ export interface FileStream extends AsyncIterable<FileChunk> {
263
+ metadata?: FileMetadata;
264
+ }
265
+
218
266
  // Error Types
219
267
 
220
268
  export class SandboxError extends Error {
@@ -359,6 +407,7 @@ export interface ISandbox {
359
407
  renameFile(oldPath: string, newPath: string): Promise<RenameFileResponse>;
360
408
  moveFile(sourcePath: string, destinationPath: string): Promise<MoveFileResponse>;
361
409
  readFile(path: string, options?: { encoding?: string }): Promise<ReadFileResponse>;
410
+ readFileStream(path: string): Promise<ReadableStream<Uint8Array>>;
362
411
  listFiles(path: string, options?: { recursive?: boolean; includeHidden?: boolean }): Promise<ListFilesResponse>;
363
412
 
364
413
  // Port management
@@ -434,6 +483,26 @@ export interface ReadFileResponse {
434
483
  path: string;
435
484
  content: string;
436
485
  timestamp: string;
486
+
487
+ /**
488
+ * Encoding used for content (utf-8 for text, base64 for binary)
489
+ */
490
+ encoding?: 'utf-8' | 'base64';
491
+
492
+ /**
493
+ * Whether the file is detected as binary
494
+ */
495
+ isBinary?: boolean;
496
+
497
+ /**
498
+ * MIME type of the file (e.g., 'image/png', 'text/plain')
499
+ */
500
+ mimeType?: string;
501
+
502
+ /**
503
+ * File size in bytes
504
+ */
505
+ size?: number;
437
506
  }
438
507
 
439
508
  export interface DeleteFileResponse {