@cloudflare/sandbox 0.0.0-153e416 → 0.0.0-1558ec9

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 (80) hide show
  1. package/CHANGELOG.md +98 -16
  2. package/Dockerfile +79 -32
  3. package/LICENSE +176 -0
  4. package/README.md +10 -3
  5. package/dist/index.d.ts +1953 -9
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +3278 -65
  8. package/dist/index.js.map +1 -1
  9. package/package.json +5 -5
  10. package/src/clients/base-client.ts +39 -24
  11. package/src/clients/command-client.ts +8 -8
  12. package/src/clients/file-client.ts +31 -26
  13. package/src/clients/git-client.ts +9 -3
  14. package/src/clients/index.ts +16 -16
  15. package/src/clients/interpreter-client.ts +51 -47
  16. package/src/clients/port-client.ts +10 -10
  17. package/src/clients/process-client.ts +11 -8
  18. package/src/clients/sandbox-client.ts +2 -4
  19. package/src/clients/types.ts +6 -2
  20. package/src/clients/utility-client.ts +43 -6
  21. package/src/errors/adapter.ts +90 -32
  22. package/src/errors/classes.ts +189 -64
  23. package/src/errors/index.ts +9 -5
  24. package/src/file-stream.ts +11 -6
  25. package/src/index.ts +28 -17
  26. package/src/interpreter.ts +50 -41
  27. package/src/request-handler.ts +24 -21
  28. package/src/sandbox.ts +403 -148
  29. package/src/security.ts +21 -6
  30. package/src/sse-parser.ts +4 -3
  31. package/src/version.ts +1 -1
  32. package/startup.sh +1 -1
  33. package/tests/base-client.test.ts +116 -80
  34. package/tests/command-client.test.ts +149 -112
  35. package/tests/file-client.test.ts +309 -197
  36. package/tests/file-stream.test.ts +24 -20
  37. package/tests/get-sandbox.test.ts +45 -6
  38. package/tests/git-client.test.ts +260 -101
  39. package/tests/port-client.test.ts +100 -108
  40. package/tests/process-client.test.ts +204 -179
  41. package/tests/request-handler.test.ts +117 -65
  42. package/tests/sandbox.test.ts +253 -68
  43. package/tests/sse-parser.test.ts +17 -16
  44. package/tests/utility-client.test.ts +79 -72
  45. package/tsdown.config.ts +12 -0
  46. package/vitest.config.ts +6 -6
  47. package/dist/chunk-BFVUNTP4.js +0 -104
  48. package/dist/chunk-BFVUNTP4.js.map +0 -1
  49. package/dist/chunk-EKSWCBCA.js +0 -86
  50. package/dist/chunk-EKSWCBCA.js.map +0 -1
  51. package/dist/chunk-FE4PJSRB.js +0 -7
  52. package/dist/chunk-FE4PJSRB.js.map +0 -1
  53. package/dist/chunk-JXZMAU2C.js +0 -559
  54. package/dist/chunk-JXZMAU2C.js.map +0 -1
  55. package/dist/chunk-SVWLTRHD.js +0 -2456
  56. package/dist/chunk-SVWLTRHD.js.map +0 -1
  57. package/dist/chunk-Z532A7QC.js +0 -78
  58. package/dist/chunk-Z532A7QC.js.map +0 -1
  59. package/dist/file-stream.d.ts +0 -43
  60. package/dist/file-stream.js +0 -9
  61. package/dist/file-stream.js.map +0 -1
  62. package/dist/interpreter.d.ts +0 -33
  63. package/dist/interpreter.js +0 -8
  64. package/dist/interpreter.js.map +0 -1
  65. package/dist/request-handler.d.ts +0 -18
  66. package/dist/request-handler.js +0 -13
  67. package/dist/request-handler.js.map +0 -1
  68. package/dist/sandbox-DWQVgVTY.d.ts +0 -603
  69. package/dist/sandbox.d.ts +0 -4
  70. package/dist/sandbox.js +0 -13
  71. package/dist/sandbox.js.map +0 -1
  72. package/dist/security.d.ts +0 -31
  73. package/dist/security.js +0 -13
  74. package/dist/security.js.map +0 -1
  75. package/dist/sse-parser.d.ts +0 -28
  76. package/dist/sse-parser.js +0 -11
  77. package/dist/sse-parser.js.map +0 -1
  78. package/dist/version.d.ts +0 -8
  79. package/dist/version.js +0 -7
  80. package/dist/version.js.map +0 -1
package/src/sandbox.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { DurableObject } from 'cloudflare:workers';
2
- import { Container, getContainer } from "@cloudflare/containers";
2
+ import { Container, getContainer, switchPort } from '@cloudflare/containers';
3
3
  import type {
4
4
  CodeContext,
5
5
  CreateContextOptions,
@@ -16,27 +16,28 @@ import type {
16
16
  SandboxOptions,
17
17
  SessionOptions,
18
18
  StreamOptions
19
- } from "@repo/shared";
20
- import { createLogger, runWithLogger, TraceContext } from "@repo/shared";
21
- import { type ExecuteResponse, SandboxClient } from "./clients";
19
+ } from '@repo/shared';
20
+ import {
21
+ createLogger,
22
+ runWithLogger,
23
+ type SessionDeleteResult,
24
+ TraceContext
25
+ } from '@repo/shared';
26
+ import { type ExecuteResponse, SandboxClient } from './clients';
22
27
  import type { ErrorResponse } from './errors';
23
28
  import { CustomDomainRequiredError, ErrorCode } from './errors';
24
- import { CodeInterpreter } from "./interpreter";
25
- import { isLocalhostPattern } from "./request-handler";
26
- import {
27
- SecurityError,
28
- sanitizeSandboxId,
29
- validatePort
30
- } from "./security";
31
- import { parseSSEStream } from "./sse-parser";
32
- import { SDK_VERSION } from "./version";
29
+ import { CodeInterpreter } from './interpreter';
30
+ import { isLocalhostPattern } from './request-handler';
31
+ import { SecurityError, sanitizeSandboxId, validatePort } from './security';
32
+ import { parseSSEStream } from './sse-parser';
33
+ import { SDK_VERSION } from './version';
33
34
 
34
35
  export function getSandbox(
35
36
  ns: DurableObjectNamespace<Sandbox>,
36
37
  id: string,
37
38
  options?: SandboxOptions
38
- ) {
39
- const stub = getContainer(ns, id);
39
+ ): Sandbox {
40
+ const stub = getContainer(ns, id) as unknown as Sandbox;
40
41
 
41
42
  // Store the name on first access
42
43
  stub.setSandboxName?.(id);
@@ -49,12 +50,33 @@ export function getSandbox(
49
50
  stub.setSleepAfter(options.sleepAfter);
50
51
  }
51
52
 
52
- return stub;
53
+ if (options?.keepAlive !== undefined) {
54
+ stub.setKeepAlive(options.keepAlive);
55
+ }
56
+
57
+ return Object.assign(stub, {
58
+ wsConnect: connect(stub)
59
+ });
60
+ }
61
+
62
+ export function connect(stub: {
63
+ fetch: (request: Request) => Promise<Response>;
64
+ }) {
65
+ return async (request: Request, port: number) => {
66
+ // Validate port before routing
67
+ if (!validatePort(port)) {
68
+ throw new SecurityError(
69
+ `Invalid or restricted port: ${port}. Ports must be in range 1024-65535 and not reserved.`
70
+ );
71
+ }
72
+ const portSwitchedRequest = switchPort(request, port);
73
+ return await stub.fetch(portSwitchedRequest);
74
+ };
53
75
  }
54
76
 
55
77
  export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
56
78
  defaultPort = 3000; // Default port for the container's Bun server
57
- sleepAfter: string | number = "10m"; // Sleep the sandbox if no requests are made in this timeframe
79
+ sleepAfter: string | number = '10m'; // Sleep the sandbox if no requests are made in this timeframe
58
80
 
59
81
  client: SandboxClient;
60
82
  private codeInterpreter: CodeInterpreter;
@@ -64,14 +86,15 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
64
86
  private defaultSession: string | null = null;
65
87
  envVars: Record<string, string> = {};
66
88
  private logger: ReturnType<typeof createLogger>;
89
+ private keepAliveEnabled: boolean = false;
67
90
 
68
- constructor(ctx: DurableObject['ctx'], env: Env) {
91
+ constructor(ctx: DurableObjectState<{}>, env: Env) {
69
92
  super(ctx, env);
70
93
 
71
94
  const envObj = env as any;
72
95
  // Set sandbox environment variables from env object
73
96
  const sandboxEnvKeys = ['SANDBOX_LOG_LEVEL', 'SANDBOX_LOG_FORMAT'] as const;
74
- sandboxEnvKeys.forEach(key => {
97
+ sandboxEnvKeys.forEach((key) => {
75
98
  if (envObj?.[key]) {
76
99
  this.envVars[key] = envObj[key];
77
100
  }
@@ -85,7 +108,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
85
108
  this.client = new SandboxClient({
86
109
  logger: this.logger,
87
110
  port: 3000, // Control plane port
88
- stub: this,
111
+ stub: this
89
112
  });
90
113
 
91
114
  // Initialize code interpreter - pass 'this' after client is ready
@@ -94,9 +117,13 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
94
117
 
95
118
  // Load the sandbox name, port tokens, and default session from storage on initialization
96
119
  this.ctx.blockConcurrencyWhile(async () => {
97
- this.sandboxName = await this.ctx.storage.get<string>('sandboxName') || null;
98
- this.defaultSession = await this.ctx.storage.get<string>('defaultSession') || null;
99
- const storedTokens = await this.ctx.storage.get<Record<string, string>>('portTokens') || {};
120
+ this.sandboxName =
121
+ (await this.ctx.storage.get<string>('sandboxName')) || null;
122
+ this.defaultSession =
123
+ (await this.ctx.storage.get<string>('defaultSession')) || null;
124
+ const storedTokens =
125
+ (await this.ctx.storage.get<Record<string, string>>('portTokens')) ||
126
+ {};
100
127
 
101
128
  // Convert stored tokens back to Map
102
129
  this.portTokens = new Map();
@@ -120,8 +147,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
120
147
  this.baseUrl = baseUrl;
121
148
  await this.ctx.storage.put('baseUrl', baseUrl);
122
149
  } else {
123
- if(this.baseUrl !== baseUrl) {
124
- throw new Error('Base URL already set and different from one previously provided');
150
+ if (this.baseUrl !== baseUrl) {
151
+ throw new Error(
152
+ 'Base URL already set and different from one previously provided'
153
+ );
125
154
  }
126
155
  }
127
156
  }
@@ -131,6 +160,20 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
131
160
  this.sleepAfter = sleepAfter;
132
161
  }
133
162
 
163
+ // RPC method to enable keepAlive mode
164
+ async setKeepAlive(keepAlive: boolean): Promise<void> {
165
+ this.keepAliveEnabled = keepAlive;
166
+ if (keepAlive) {
167
+ this.logger.info(
168
+ 'KeepAlive mode enabled - container will stay alive until explicitly destroyed'
169
+ );
170
+ } else {
171
+ this.logger.info(
172
+ 'KeepAlive mode disabled - container will timeout normally'
173
+ );
174
+ }
175
+ }
176
+
134
177
  // RPC method to set environment variables
135
178
  async setEnvVars(envVars: Record<string, string>): Promise<void> {
136
179
  // Update local state for new sessions
@@ -143,10 +186,15 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
143
186
  const escapedValue = value.replace(/'/g, "'\\''");
144
187
  const exportCommand = `export ${key}='${escapedValue}'`;
145
188
 
146
- const result = await this.client.commands.execute(exportCommand, this.defaultSession);
189
+ const result = await this.client.commands.execute(
190
+ exportCommand,
191
+ this.defaultSession
192
+ );
147
193
 
148
194
  if (result.exitCode !== 0) {
149
- throw new Error(`Failed to set ${key}: ${result.stderr || 'Unknown error'}`);
195
+ throw new Error(
196
+ `Failed to set ${key}: ${result.stderr || 'Unknown error'}`
197
+ );
150
198
  }
151
199
  }
152
200
  }
@@ -164,8 +212,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
164
212
  this.logger.debug('Sandbox started');
165
213
 
166
214
  // Check version compatibility asynchronously (don't block startup)
167
- this.checkVersionCompatibility().catch(error => {
168
- this.logger.error('Version compatibility check failed', error instanceof Error ? error : new Error(String(error)));
215
+ this.checkVersionCompatibility().catch((error) => {
216
+ this.logger.error(
217
+ 'Version compatibility check failed',
218
+ error instanceof Error ? error : new Error(String(error))
219
+ );
169
220
  });
170
221
  }
171
222
 
@@ -185,8 +236,9 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
185
236
  if (containerVersion === 'unknown') {
186
237
  this.logger.warn(
187
238
  'Container version check: Container version could not be determined. ' +
188
- 'This may indicate an outdated container image. ' +
189
- 'Please update your container to match SDK version ' + sdkVersion
239
+ 'This may indicate an outdated container image. ' +
240
+ 'Please update your container to match SDK version ' +
241
+ sdkVersion
190
242
  );
191
243
  return;
192
244
  }
@@ -202,7 +254,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
202
254
  // so we always use warning level as requested by the user
203
255
  this.logger.warn(message);
204
256
  } else {
205
- this.logger.debug('Version check passed', { sdkVersion, containerVersion });
257
+ this.logger.debug('Version check passed', {
258
+ sdkVersion,
259
+ containerVersion
260
+ });
206
261
  }
207
262
  } catch (error) {
208
263
  // Don't fail the sandbox initialization if version check fails
@@ -217,13 +272,34 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
217
272
  }
218
273
 
219
274
  override onError(error: unknown) {
220
- this.logger.error('Sandbox error', error instanceof Error ? error : new Error(String(error)));
275
+ this.logger.error(
276
+ 'Sandbox error',
277
+ error instanceof Error ? error : new Error(String(error))
278
+ );
279
+ }
280
+
281
+ /**
282
+ * Override onActivityExpired to prevent automatic shutdown when keepAlive is enabled
283
+ * When keepAlive is disabled, calls parent implementation which stops the container
284
+ */
285
+ override async onActivityExpired(): Promise<void> {
286
+ if (this.keepAliveEnabled) {
287
+ this.logger.debug(
288
+ 'Activity expired but keepAlive is enabled - container will stay alive'
289
+ );
290
+ // Do nothing - don't call stop(), container stays alive
291
+ } else {
292
+ // Default behavior: stop the container
293
+ this.logger.debug('Activity expired - stopping container');
294
+ await super.onActivityExpired();
295
+ }
221
296
  }
222
297
 
223
298
  // Override fetch to route internal container requests to appropriate ports
224
299
  override async fetch(request: Request): Promise<Response> {
225
300
  // Extract or generate trace ID from request
226
- const traceId = TraceContext.fromHeaders(request.headers) || TraceContext.generate();
301
+ const traceId =
302
+ TraceContext.fromHeaders(request.headers) || TraceContext.generate();
227
303
 
228
304
  // Create request-specific logger with trace ID
229
305
  const requestLogger = this.logger.child({ traceId, operation: 'fetch' });
@@ -238,14 +314,30 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
238
314
  await this.ctx.storage.put('sandboxName', name);
239
315
  }
240
316
 
241
- // Detect WebSocket upgrade request
317
+ // Detect WebSocket upgrade request (RFC 6455 compliant)
242
318
  const upgradeHeader = request.headers.get('Upgrade');
243
- const isWebSocket = upgradeHeader?.toLowerCase() === 'websocket';
319
+ const connectionHeader = request.headers.get('Connection');
320
+ const isWebSocket =
321
+ upgradeHeader?.toLowerCase() === 'websocket' &&
322
+ connectionHeader?.toLowerCase().includes('upgrade');
244
323
 
245
324
  if (isWebSocket) {
246
325
  // WebSocket path: Let parent Container class handle WebSocket proxying
247
326
  // This bypasses containerFetch() which uses JSRPC and cannot handle WebSocket upgrades
248
- return await super.fetch(request);
327
+ try {
328
+ requestLogger.debug('WebSocket upgrade requested', {
329
+ path: url.pathname,
330
+ port: this.determinePort(url)
331
+ });
332
+ return await super.fetch(request);
333
+ } catch (error) {
334
+ requestLogger.error(
335
+ 'WebSocket connection failed',
336
+ error instanceof Error ? error : new Error(String(error)),
337
+ { path: url.pathname }
338
+ );
339
+ throw error;
340
+ }
249
341
  }
250
342
 
251
343
  // Non-WebSocket: Use existing port determination and HTTP routing logic
@@ -256,6 +348,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
256
348
  });
257
349
  }
258
350
 
351
+ wsConnect(request: Request, port: number): Promise<Response> {
352
+ // Dummy implementation that will be overridden by the stub
353
+ throw new Error('Not implemented here to avoid RPC serialization issues');
354
+ }
355
+
259
356
  private determinePort(url: URL): number {
260
357
  // Extract port from proxy requests (e.g., /proxy/8080/*)
261
358
  const proxyMatch = url.pathname.match(/^\/proxy\/(\d+)/);
@@ -285,7 +382,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
285
382
  await this.client.utils.createSession({
286
383
  id: sessionId,
287
384
  env: this.envVars || {},
288
- cwd: '/workspace',
385
+ cwd: '/workspace'
289
386
  });
290
387
 
291
388
  this.defaultSession = sessionId;
@@ -295,7 +392,9 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
295
392
  } catch (error: any) {
296
393
  // If session already exists (e.g., after hot reload), reuse it
297
394
  if (error?.message?.includes('already exists')) {
298
- this.logger.debug('Reusing existing session after reload', { sessionId });
395
+ this.logger.debug('Reusing existing session after reload', {
396
+ sessionId
397
+ });
299
398
  this.defaultSession = sessionId;
300
399
  // Persist to storage in case it wasn't saved before
301
400
  await this.ctx.storage.put('defaultSession', sessionId);
@@ -327,7 +426,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
327
426
  const startTime = Date.now();
328
427
  const timestamp = new Date().toISOString();
329
428
 
330
- // Handle timeout
331
429
  let timeoutId: NodeJS.Timeout | undefined;
332
430
 
333
431
  try {
@@ -340,13 +438,23 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
340
438
 
341
439
  if (options?.stream && options?.onOutput) {
342
440
  // Streaming with callbacks - we need to collect the final result
343
- result = await this.executeWithStreaming(command, sessionId, options, startTime, timestamp);
441
+ result = await this.executeWithStreaming(
442
+ command,
443
+ sessionId,
444
+ options,
445
+ startTime,
446
+ timestamp
447
+ );
344
448
  } else {
345
449
  // Regular execution with session
346
450
  const response = await this.client.commands.execute(command, sessionId);
347
451
 
348
452
  const duration = Date.now() - startTime;
349
- result = this.mapExecuteResponseToExecResult(response, duration, sessionId);
453
+ result = this.mapExecuteResponseToExecResult(
454
+ response,
455
+ duration,
456
+ sessionId
457
+ );
350
458
  }
351
459
 
352
460
  // Call completion callback if provided
@@ -378,7 +486,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
378
486
  let stderr = '';
379
487
 
380
488
  try {
381
- const stream = await this.client.commands.executeStream(command, sessionId);
489
+ const stream = await this.client.commands.executeStream(
490
+ command,
491
+ sessionId
492
+ );
382
493
 
383
494
  for await (const event of parseSSEStream<ExecEvent>(stream)) {
384
495
  // Check for cancellation
@@ -423,7 +534,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
423
534
 
424
535
  // If we get here without a complete event, something went wrong
425
536
  throw new Error('Stream ended without completion event');
426
-
427
537
  } catch (error) {
428
538
  if (options.signal?.aborted) {
429
539
  throw new Error('Operation was aborted');
@@ -471,8 +581,15 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
471
581
  pid: data.pid,
472
582
  command: data.command,
473
583
  status: data.status,
474
- startTime: typeof data.startTime === 'string' ? new Date(data.startTime) : data.startTime,
475
- endTime: data.endTime ? (typeof data.endTime === 'string' ? new Date(data.endTime) : data.endTime) : undefined,
584
+ startTime:
585
+ typeof data.startTime === 'string'
586
+ ? new Date(data.startTime)
587
+ : data.startTime,
588
+ endTime: data.endTime
589
+ ? typeof data.endTime === 'string'
590
+ ? new Date(data.endTime)
591
+ : data.endTime
592
+ : undefined,
476
593
  exitCode: data.exitCode,
477
594
  sessionId,
478
595
 
@@ -492,25 +609,35 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
492
609
  };
493
610
  }
494
611
 
495
-
496
612
  // Background process management
497
- async startProcess(command: string, options?: ProcessOptions, sessionId?: string): Promise<Process> {
613
+ async startProcess(
614
+ command: string,
615
+ options?: ProcessOptions,
616
+ sessionId?: string
617
+ ): Promise<Process> {
498
618
  // Use the new HttpClient method to start the process
499
619
  try {
500
- const session = sessionId ?? await this.ensureDefaultSession();
501
- const response = await this.client.processes.startProcess(command, session, {
502
- processId: options?.processId
503
- });
504
-
505
- const processObj = this.createProcessFromDTO({
506
- id: response.processId,
507
- pid: response.pid,
508
- command: response.command,
509
- status: 'running' as ProcessStatus,
510
- startTime: new Date(),
511
- endTime: undefined,
512
- exitCode: undefined
513
- }, session);
620
+ const session = sessionId ?? (await this.ensureDefaultSession());
621
+ const response = await this.client.processes.startProcess(
622
+ command,
623
+ session,
624
+ {
625
+ processId: options?.processId
626
+ }
627
+ );
628
+
629
+ const processObj = this.createProcessFromDTO(
630
+ {
631
+ id: response.processId,
632
+ pid: response.pid,
633
+ command: response.command,
634
+ status: 'running' as ProcessStatus,
635
+ startTime: new Date(),
636
+ endTime: undefined,
637
+ exitCode: undefined
638
+ },
639
+ session
640
+ );
514
641
 
515
642
  // Call onStart callback if provided
516
643
  if (options?.onStart) {
@@ -518,7 +645,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
518
645
  }
519
646
 
520
647
  return processObj;
521
-
522
648
  } catch (error) {
523
649
  if (options?.onError && error instanceof Error) {
524
650
  options.onError(error);
@@ -529,42 +655,52 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
529
655
  }
530
656
 
531
657
  async listProcesses(sessionId?: string): Promise<Process[]> {
532
- const session = sessionId ?? await this.ensureDefaultSession();
658
+ const session = sessionId ?? (await this.ensureDefaultSession());
533
659
  const response = await this.client.processes.listProcesses();
534
660
 
535
- return response.processes.map(processData =>
536
- this.createProcessFromDTO({
537
- id: processData.id,
538
- pid: processData.pid,
539
- command: processData.command,
540
- status: processData.status,
541
- startTime: processData.startTime,
542
- endTime: processData.endTime,
543
- exitCode: processData.exitCode
544
- }, session)
661
+ return response.processes.map((processData) =>
662
+ this.createProcessFromDTO(
663
+ {
664
+ id: processData.id,
665
+ pid: processData.pid,
666
+ command: processData.command,
667
+ status: processData.status,
668
+ startTime: processData.startTime,
669
+ endTime: processData.endTime,
670
+ exitCode: processData.exitCode
671
+ },
672
+ session
673
+ )
545
674
  );
546
675
  }
547
676
 
548
677
  async getProcess(id: string, sessionId?: string): Promise<Process | null> {
549
- const session = sessionId ?? await this.ensureDefaultSession();
678
+ const session = sessionId ?? (await this.ensureDefaultSession());
550
679
  const response = await this.client.processes.getProcess(id);
551
680
  if (!response.process) {
552
681
  return null;
553
682
  }
554
683
 
555
684
  const processData = response.process;
556
- return this.createProcessFromDTO({
557
- id: processData.id,
558
- pid: processData.pid,
559
- command: processData.command,
560
- status: processData.status,
561
- startTime: processData.startTime,
562
- endTime: processData.endTime,
563
- exitCode: processData.exitCode
564
- }, session);
565
- }
566
-
567
- async killProcess(id: string, signal?: string, sessionId?: string): Promise<void> {
685
+ return this.createProcessFromDTO(
686
+ {
687
+ id: processData.id,
688
+ pid: processData.pid,
689
+ command: processData.command,
690
+ status: processData.status,
691
+ startTime: processData.startTime,
692
+ endTime: processData.endTime,
693
+ exitCode: processData.exitCode
694
+ },
695
+ session
696
+ );
697
+ }
698
+
699
+ async killProcess(
700
+ id: string,
701
+ signal?: string,
702
+ sessionId?: string
703
+ ): Promise<void> {
568
704
  // Note: signal parameter is not currently supported by the HttpClient implementation
569
705
  // The HTTP client already throws properly typed errors, so we just let them propagate
570
706
  await this.client.processes.killProcess(id);
@@ -582,7 +718,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
582
718
  return 0;
583
719
  }
584
720
 
585
- async getProcessLogs(id: string, sessionId?: string): Promise<{ stdout: string; stderr: string; processId: string }> {
721
+ async getProcessLogs(
722
+ id: string,
723
+ sessionId?: string
724
+ ): Promise<{ stdout: string; stderr: string; processId: string }> {
586
725
  // The HTTP client already throws properly typed errors, so we just let them propagate
587
726
  const response = await this.client.processes.getProcessLogs(id);
588
727
  return {
@@ -592,9 +731,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
592
731
  };
593
732
  }
594
733
 
595
-
596
734
  // Streaming methods - return ReadableStream for RPC compatibility
597
- async execStream(command: string, options?: StreamOptions): Promise<ReadableStream<Uint8Array>> {
735
+ async execStream(
736
+ command: string,
737
+ options?: StreamOptions
738
+ ): Promise<ReadableStream<Uint8Array>> {
598
739
  // Check for cancellation
599
740
  if (options?.signal?.aborted) {
600
741
  throw new Error('Operation was aborted');
@@ -608,7 +749,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
608
749
  /**
609
750
  * Internal session-aware execStream implementation
610
751
  */
611
- private async execStreamWithSession(command: string, sessionId: string, options?: StreamOptions): Promise<ReadableStream<Uint8Array>> {
752
+ private async execStreamWithSession(
753
+ command: string,
754
+ sessionId: string,
755
+ options?: StreamOptions
756
+ ): Promise<ReadableStream<Uint8Array>> {
612
757
  // Check for cancellation
613
758
  if (options?.signal?.aborted) {
614
759
  throw new Error('Operation was aborted');
@@ -617,7 +762,13 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
617
762
  return this.client.commands.executeStream(command, sessionId);
618
763
  }
619
764
 
620
- async streamProcessLogs(processId: string, options?: { signal?: AbortSignal }): Promise<ReadableStream<Uint8Array>> {
765
+ /**
766
+ * Stream logs from a background process as a ReadableStream.
767
+ */
768
+ async streamProcessLogs(
769
+ processId: string,
770
+ options?: { signal?: AbortSignal }
771
+ ): Promise<ReadableStream<Uint8Array>> {
621
772
  // Check for cancellation
622
773
  if (options?.signal?.aborted) {
623
774
  throw new Error('Operation was aborted');
@@ -630,7 +781,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
630
781
  repoUrl: string,
631
782
  options: { branch?: string; targetDir?: string; sessionId?: string }
632
783
  ) {
633
- const session = options.sessionId ?? await this.ensureDefaultSession();
784
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
634
785
  return this.client.git.checkout(repoUrl, session, {
635
786
  branch: options.branch,
636
787
  targetDir: options.targetDir
@@ -641,8 +792,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
641
792
  path: string,
642
793
  options: { recursive?: boolean; sessionId?: string } = {}
643
794
  ) {
644
- const session = options.sessionId ?? await this.ensureDefaultSession();
645
- return this.client.files.mkdir(path, session, { recursive: options.recursive });
795
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
796
+ return this.client.files.mkdir(path, session, {
797
+ recursive: options.recursive
798
+ });
646
799
  }
647
800
 
648
801
  async writeFile(
@@ -650,21 +803,19 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
650
803
  content: string,
651
804
  options: { encoding?: string; sessionId?: string } = {}
652
805
  ) {
653
- const session = options.sessionId ?? await this.ensureDefaultSession();
654
- return this.client.files.writeFile(path, content, session, { encoding: options.encoding });
806
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
807
+ return this.client.files.writeFile(path, content, session, {
808
+ encoding: options.encoding
809
+ });
655
810
  }
656
811
 
657
812
  async deleteFile(path: string, sessionId?: string) {
658
- const session = sessionId ?? await this.ensureDefaultSession();
813
+ const session = sessionId ?? (await this.ensureDefaultSession());
659
814
  return this.client.files.deleteFile(path, session);
660
815
  }
661
816
 
662
- async renameFile(
663
- oldPath: string,
664
- newPath: string,
665
- sessionId?: string
666
- ) {
667
- const session = sessionId ?? await this.ensureDefaultSession();
817
+ async renameFile(oldPath: string, newPath: string, sessionId?: string) {
818
+ const session = sessionId ?? (await this.ensureDefaultSession());
668
819
  return this.client.files.renameFile(oldPath, newPath, session);
669
820
  }
670
821
 
@@ -673,7 +824,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
673
824
  destinationPath: string,
674
825
  sessionId?: string
675
826
  ) {
676
- const session = sessionId ?? await this.ensureDefaultSession();
827
+ const session = sessionId ?? (await this.ensureDefaultSession());
677
828
  return this.client.files.moveFile(sourcePath, destinationPath, session);
678
829
  }
679
830
 
@@ -681,8 +832,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
681
832
  path: string,
682
833
  options: { encoding?: string; sessionId?: string } = {}
683
834
  ) {
684
- const session = options.sessionId ?? await this.ensureDefaultSession();
685
- return this.client.files.readFile(path, session, { encoding: options.encoding });
835
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
836
+ return this.client.files.readFile(path, session, {
837
+ encoding: options.encoding
838
+ });
686
839
  }
687
840
 
688
841
  /**
@@ -695,7 +848,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
695
848
  path: string,
696
849
  options: { sessionId?: string } = {}
697
850
  ): Promise<ReadableStream<Uint8Array>> {
698
- const session = options.sessionId ?? await this.ensureDefaultSession();
851
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
699
852
  return this.client.files.readFileStream(path, session);
700
853
  }
701
854
 
@@ -708,7 +861,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
708
861
  }
709
862
 
710
863
  async exists(path: string, sessionId?: string) {
711
- const session = sessionId ?? await this.ensureDefaultSession();
864
+ const session = sessionId ?? (await this.ensureDefaultSession());
712
865
  return this.client.files.exists(path, session);
713
866
  }
714
867
 
@@ -730,7 +883,9 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
730
883
 
731
884
  // We need the sandbox name to construct preview URLs
732
885
  if (!this.sandboxName) {
733
- throw new Error('Sandbox name not available. Ensure sandbox is accessed through getSandbox()');
886
+ throw new Error(
887
+ 'Sandbox name not available. Ensure sandbox is accessed through getSandbox()'
888
+ );
734
889
  }
735
890
 
736
891
  // Generate and store token for this port
@@ -738,18 +893,25 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
738
893
  this.portTokens.set(port, token);
739
894
  await this.persistPortTokens();
740
895
 
741
- const url = this.constructPreviewUrl(port, this.sandboxName, options.hostname, token);
896
+ const url = this.constructPreviewUrl(
897
+ port,
898
+ this.sandboxName,
899
+ options.hostname,
900
+ token
901
+ );
742
902
 
743
903
  return {
744
904
  url,
745
905
  port,
746
- name: options?.name,
906
+ name: options?.name
747
907
  };
748
908
  }
749
909
 
750
910
  async unexposePort(port: number) {
751
911
  if (!validatePort(port)) {
752
- throw new SecurityError(`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`);
912
+ throw new SecurityError(
913
+ `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
914
+ );
753
915
  }
754
916
 
755
917
  const sessionId = await this.ensureDefaultSession();
@@ -768,32 +930,44 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
768
930
 
769
931
  // We need the sandbox name to construct preview URLs
770
932
  if (!this.sandboxName) {
771
- throw new Error('Sandbox name not available. Ensure sandbox is accessed through getSandbox()');
933
+ throw new Error(
934
+ 'Sandbox name not available. Ensure sandbox is accessed through getSandbox()'
935
+ );
772
936
  }
773
937
 
774
- return response.ports.map(port => {
938
+ return response.ports.map((port) => {
775
939
  // Get token for this port - must exist for all exposed ports
776
940
  const token = this.portTokens.get(port.port);
777
941
  if (!token) {
778
- throw new Error(`Port ${port.port} is exposed but has no token. This should not happen.`);
942
+ throw new Error(
943
+ `Port ${port.port} is exposed but has no token. This should not happen.`
944
+ );
779
945
  }
780
946
 
781
947
  return {
782
- url: this.constructPreviewUrl(port.port, this.sandboxName!, hostname, token),
948
+ url: this.constructPreviewUrl(
949
+ port.port,
950
+ this.sandboxName!,
951
+ hostname,
952
+ token
953
+ ),
783
954
  port: port.port,
784
- status: port.status,
955
+ status: port.status
785
956
  };
786
957
  });
787
958
  }
788
959
 
789
-
790
960
  async isPortExposed(port: number): Promise<boolean> {
791
961
  try {
792
962
  const sessionId = await this.ensureDefaultSession();
793
963
  const response = await this.client.ports.getExposedPorts(sessionId);
794
- return response.ports.some(exposedPort => exposedPort.port === port);
964
+ return response.ports.some((exposedPort) => exposedPort.port === port);
795
965
  } catch (error) {
796
- this.logger.error('Error checking if port is exposed', error instanceof Error ? error : new Error(String(error)), { port });
966
+ this.logger.error(
967
+ 'Error checking if port is exposed',
968
+ error instanceof Error ? error : new Error(String(error)),
969
+ { port }
970
+ );
797
971
  return false;
798
972
  }
799
973
  }
@@ -809,7 +983,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
809
983
  const storedToken = this.portTokens.get(port);
810
984
  if (!storedToken) {
811
985
  // This should not happen - all exposed ports must have tokens
812
- this.logger.error('Port is exposed but has no token - bug detected', undefined, { port });
986
+ this.logger.error(
987
+ 'Port is exposed but has no token - bug detected',
988
+ undefined,
989
+ { port }
990
+ );
813
991
  return false;
814
992
  }
815
993
 
@@ -825,7 +1003,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
825
1003
 
826
1004
  // Convert to base64url format (URL-safe, no padding, lowercase)
827
1005
  const base64 = btoa(String.fromCharCode(...array));
828
- return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '').toLowerCase();
1006
+ return base64
1007
+ .replace(/\+/g, '-')
1008
+ .replace(/\//g, '_')
1009
+ .replace(/=/g, '')
1010
+ .toLowerCase();
829
1011
  }
830
1012
 
831
1013
  private async persistPortTokens(): Promise<void> {
@@ -837,9 +1019,16 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
837
1019
  await this.ctx.storage.put('portTokens', tokensObj);
838
1020
  }
839
1021
 
840
- private constructPreviewUrl(port: number, sandboxId: string, hostname: string, token: string): string {
1022
+ private constructPreviewUrl(
1023
+ port: number,
1024
+ sandboxId: string,
1025
+ hostname: string,
1026
+ token: string
1027
+ ): string {
841
1028
  if (!validatePort(port)) {
842
- throw new SecurityError(`Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`);
1029
+ throw new SecurityError(
1030
+ `Invalid port number: ${port}. Must be between 1024-65535 and not reserved.`
1031
+ );
843
1032
  }
844
1033
 
845
1034
  // Validate sandbox ID (will throw SecurityError if invalid)
@@ -861,14 +1050,18 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
861
1050
 
862
1051
  return baseUrl.toString();
863
1052
  } catch (error) {
864
- throw new SecurityError(`Failed to construct preview URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
1053
+ throw new SecurityError(
1054
+ `Failed to construct preview URL: ${
1055
+ error instanceof Error ? error.message : 'Unknown error'
1056
+ }`
1057
+ );
865
1058
  }
866
1059
  }
867
1060
 
868
1061
  // Production subdomain logic - enforce HTTPS
869
1062
  try {
870
1063
  // Always use HTTPS for production (non-localhost)
871
- const protocol = "https";
1064
+ const protocol = 'https';
872
1065
  const baseUrl = new URL(`${protocol}://${hostname}`);
873
1066
 
874
1067
  // Construct subdomain safely with mandatory token
@@ -877,7 +1070,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
877
1070
 
878
1071
  return baseUrl.toString();
879
1072
  } catch (error) {
880
- throw new SecurityError(`Failed to construct preview URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
1073
+ throw new SecurityError(
1074
+ `Failed to construct preview URL: ${
1075
+ error instanceof Error ? error.message : 'Unknown error'
1076
+ }`
1077
+ );
881
1078
  }
882
1079
  }
883
1080
 
@@ -896,7 +1093,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
896
1093
  await this.client.utils.createSession({
897
1094
  id: sessionId,
898
1095
  env: options?.env,
899
- cwd: options?.cwd,
1096
+ cwd: options?.cwd
900
1097
  });
901
1098
 
902
1099
  // Return wrapper that binds sessionId to all operations
@@ -918,6 +1115,34 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
918
1115
  return this.getSessionWrapper(sessionId);
919
1116
  }
920
1117
 
1118
+ /**
1119
+ * Delete an execution session
1120
+ * Cleans up session resources and removes it from the container
1121
+ * Note: Cannot delete the default session. To reset the default session,
1122
+ * use sandbox.destroy() to terminate the entire sandbox.
1123
+ *
1124
+ * @param sessionId - The ID of the session to delete
1125
+ * @returns Result with success status, sessionId, and timestamp
1126
+ * @throws Error if attempting to delete the default session
1127
+ */
1128
+ async deleteSession(sessionId: string): Promise<SessionDeleteResult> {
1129
+ // Prevent deletion of default session
1130
+ if (this.defaultSession && sessionId === this.defaultSession) {
1131
+ throw new Error(
1132
+ `Cannot delete default session '${sessionId}'. Use sandbox.destroy() to terminate the sandbox.`
1133
+ );
1134
+ }
1135
+
1136
+ const response = await this.client.utils.deleteSession(sessionId);
1137
+
1138
+ // Map HTTP response to result type
1139
+ return {
1140
+ success: response.success,
1141
+ sessionId: response.sessionId,
1142
+ timestamp: response.timestamp
1143
+ };
1144
+ }
1145
+
921
1146
  /**
922
1147
  * Internal helper to create ExecutionSession wrapper for a given sessionId
923
1148
  * Used by both createSession and getSession
@@ -927,32 +1152,42 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
927
1152
  id: sessionId,
928
1153
 
929
1154
  // Command execution - delegate to internal session-aware methods
930
- exec: (command, options) => this.execWithSession(command, sessionId, options),
931
- execStream: (command, options) => this.execStreamWithSession(command, sessionId, options),
1155
+ exec: (command, options) =>
1156
+ this.execWithSession(command, sessionId, options),
1157
+ execStream: (command, options) =>
1158
+ this.execStreamWithSession(command, sessionId, options),
932
1159
 
933
1160
  // Process management
934
- startProcess: (command, options) => this.startProcess(command, options, sessionId),
1161
+ startProcess: (command, options) =>
1162
+ this.startProcess(command, options, sessionId),
935
1163
  listProcesses: () => this.listProcesses(sessionId),
936
1164
  getProcess: (id) => this.getProcess(id, sessionId),
937
1165
  killProcess: (id, signal) => this.killProcess(id, signal),
938
1166
  killAllProcesses: () => this.killAllProcesses(),
939
1167
  cleanupCompletedProcesses: () => this.cleanupCompletedProcesses(),
940
1168
  getProcessLogs: (id) => this.getProcessLogs(id),
941
- streamProcessLogs: (processId, options) => this.streamProcessLogs(processId, options),
1169
+ streamProcessLogs: (processId, options) =>
1170
+ this.streamProcessLogs(processId, options),
942
1171
 
943
1172
  // File operations - pass sessionId via options or parameter
944
- writeFile: (path, content, options) => this.writeFile(path, content, { ...options, sessionId }),
945
- readFile: (path, options) => this.readFile(path, { ...options, sessionId }),
1173
+ writeFile: (path, content, options) =>
1174
+ this.writeFile(path, content, { ...options, sessionId }),
1175
+ readFile: (path, options) =>
1176
+ this.readFile(path, { ...options, sessionId }),
946
1177
  readFileStream: (path) => this.readFileStream(path, { sessionId }),
947
1178
  mkdir: (path, options) => this.mkdir(path, { ...options, sessionId }),
948
1179
  deleteFile: (path) => this.deleteFile(path, sessionId),
949
- renameFile: (oldPath, newPath) => this.renameFile(oldPath, newPath, sessionId),
950
- moveFile: (sourcePath, destPath) => this.moveFile(sourcePath, destPath, sessionId),
951
- listFiles: (path, options) => this.client.files.listFiles(path, sessionId, options),
1180
+ renameFile: (oldPath, newPath) =>
1181
+ this.renameFile(oldPath, newPath, sessionId),
1182
+ moveFile: (sourcePath, destPath) =>
1183
+ this.moveFile(sourcePath, destPath, sessionId),
1184
+ listFiles: (path, options) =>
1185
+ this.client.files.listFiles(path, sessionId, options),
952
1186
  exists: (path) => this.exists(path, sessionId),
953
1187
 
954
1188
  // Git operations
955
- gitCheckout: (repoUrl, options) => this.gitCheckout(repoUrl, { ...options, sessionId }),
1189
+ gitCheckout: (repoUrl, options) =>
1190
+ this.gitCheckout(repoUrl, { ...options, sessionId }),
956
1191
 
957
1192
  // Environment management - needs special handling
958
1193
  setEnvVars: async (envVars: Record<string, string>) => {
@@ -962,27 +1197,39 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
962
1197
  const escapedValue = value.replace(/'/g, "'\\''");
963
1198
  const exportCommand = `export ${key}='${escapedValue}'`;
964
1199
 
965
- const result = await this.client.commands.execute(exportCommand, sessionId);
1200
+ const result = await this.client.commands.execute(
1201
+ exportCommand,
1202
+ sessionId
1203
+ );
966
1204
 
967
1205
  if (result.exitCode !== 0) {
968
- throw new Error(`Failed to set ${key}: ${result.stderr || 'Unknown error'}`);
1206
+ throw new Error(
1207
+ `Failed to set ${key}: ${result.stderr || 'Unknown error'}`
1208
+ );
969
1209
  }
970
1210
  }
971
1211
  } catch (error) {
972
- this.logger.error('Failed to set environment variables', error instanceof Error ? error : new Error(String(error)), { sessionId });
1212
+ this.logger.error(
1213
+ 'Failed to set environment variables',
1214
+ error instanceof Error ? error : new Error(String(error)),
1215
+ { sessionId }
1216
+ );
973
1217
  throw error;
974
1218
  }
975
1219
  },
976
1220
 
977
1221
  // Code interpreter methods - delegate to sandbox's code interpreter
978
- createCodeContext: (options) => this.codeInterpreter.createCodeContext(options),
1222
+ createCodeContext: (options) =>
1223
+ this.codeInterpreter.createCodeContext(options),
979
1224
  runCode: async (code, options) => {
980
1225
  const execution = await this.codeInterpreter.runCode(code, options);
981
1226
  return execution.toJSON();
982
1227
  },
983
- runCodeStream: (code, options) => this.codeInterpreter.runCodeStream(code, options),
1228
+ runCodeStream: (code, options) =>
1229
+ this.codeInterpreter.runCodeStream(code, options),
984
1230
  listCodeContexts: () => this.codeInterpreter.listCodeContexts(),
985
- deleteCodeContext: (contextId) => this.codeInterpreter.deleteCodeContext(contextId),
1231
+ deleteCodeContext: (contextId) =>
1232
+ this.codeInterpreter.deleteCodeContext(contextId)
986
1233
  };
987
1234
  }
988
1235
 
@@ -990,16 +1237,24 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
990
1237
  // Code interpreter methods - delegate to CodeInterpreter wrapper
991
1238
  // ============================================================================
992
1239
 
993
- async createCodeContext(options?: CreateContextOptions): Promise<CodeContext> {
1240
+ async createCodeContext(
1241
+ options?: CreateContextOptions
1242
+ ): Promise<CodeContext> {
994
1243
  return this.codeInterpreter.createCodeContext(options);
995
1244
  }
996
1245
 
997
- async runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult> {
1246
+ async runCode(
1247
+ code: string,
1248
+ options?: RunCodeOptions
1249
+ ): Promise<ExecutionResult> {
998
1250
  const execution = await this.codeInterpreter.runCode(code, options);
999
1251
  return execution.toJSON();
1000
1252
  }
1001
1253
 
1002
- async runCodeStream(code: string, options?: RunCodeOptions): Promise<ReadableStream> {
1254
+ async runCodeStream(
1255
+ code: string,
1256
+ options?: RunCodeOptions
1257
+ ): Promise<ReadableStream> {
1003
1258
  return this.codeInterpreter.runCodeStream(code, options);
1004
1259
  }
1005
1260