@cloudflare/sandbox 0.0.0-c39674b → 0.0.0-c484232

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 +130 -15
  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 +6 -6
  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 +51 -20
  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 +34 -21
  28. package/src/sandbox.ts +417 -146
  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 +373 -185
  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 +292 -0
  42. package/tests/sandbox.test.ts +336 -62
  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-JXZMAU2C.js +0 -559
  52. package/dist/chunk-JXZMAU2C.js.map +0 -1
  53. package/dist/chunk-UZQBJBJF.js +0 -7
  54. package/dist/chunk-UZQBJBJF.js.map +0 -1
  55. package/dist/chunk-YEZBBFK7.js +0 -2420
  56. package/dist/chunk-YEZBBFK7.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-DMlNr93l.d.ts +0 -596
  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,7 +314,33 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
238
314
  await this.ctx.storage.put('sandboxName', name);
239
315
  }
240
316
 
241
- // Determine which port to route to
317
+ // Detect WebSocket upgrade request (RFC 6455 compliant)
318
+ const upgradeHeader = request.headers.get('Upgrade');
319
+ const connectionHeader = request.headers.get('Connection');
320
+ const isWebSocket =
321
+ upgradeHeader?.toLowerCase() === 'websocket' &&
322
+ connectionHeader?.toLowerCase().includes('upgrade');
323
+
324
+ if (isWebSocket) {
325
+ // WebSocket path: Let parent Container class handle WebSocket proxying
326
+ // This bypasses containerFetch() which uses JSRPC and cannot handle WebSocket upgrades
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
+ }
341
+ }
342
+
343
+ // Non-WebSocket: Use existing port determination and HTTP routing logic
242
344
  const port = this.determinePort(url);
243
345
 
244
346
  // Route to the appropriate port
@@ -246,6 +348,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
246
348
  });
247
349
  }
248
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
+
249
356
  private determinePort(url: URL): number {
250
357
  // Extract port from proxy requests (e.g., /proxy/8080/*)
251
358
  const proxyMatch = url.pathname.match(/^\/proxy\/(\d+)/);
@@ -275,7 +382,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
275
382
  await this.client.utils.createSession({
276
383
  id: sessionId,
277
384
  env: this.envVars || {},
278
- cwd: '/workspace',
385
+ cwd: '/workspace'
279
386
  });
280
387
 
281
388
  this.defaultSession = sessionId;
@@ -284,8 +391,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
284
391
  this.logger.debug('Default session initialized', { sessionId });
285
392
  } catch (error: any) {
286
393
  // If session already exists (e.g., after hot reload), reuse it
287
- if (error?.message?.includes('already exists') || error?.message?.includes('Session')) {
288
- this.logger.debug('Reusing existing session after reload', { sessionId });
394
+ if (error?.message?.includes('already exists')) {
395
+ this.logger.debug('Reusing existing session after reload', {
396
+ sessionId
397
+ });
289
398
  this.defaultSession = sessionId;
290
399
  // Persist to storage in case it wasn't saved before
291
400
  await this.ctx.storage.put('defaultSession', sessionId);
@@ -317,7 +426,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
317
426
  const startTime = Date.now();
318
427
  const timestamp = new Date().toISOString();
319
428
 
320
- // Handle timeout
321
429
  let timeoutId: NodeJS.Timeout | undefined;
322
430
 
323
431
  try {
@@ -330,13 +438,23 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
330
438
 
331
439
  if (options?.stream && options?.onOutput) {
332
440
  // Streaming with callbacks - we need to collect the final result
333
- 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
+ );
334
448
  } else {
335
449
  // Regular execution with session
336
450
  const response = await this.client.commands.execute(command, sessionId);
337
451
 
338
452
  const duration = Date.now() - startTime;
339
- result = this.mapExecuteResponseToExecResult(response, duration, sessionId);
453
+ result = this.mapExecuteResponseToExecResult(
454
+ response,
455
+ duration,
456
+ sessionId
457
+ );
340
458
  }
341
459
 
342
460
  // Call completion callback if provided
@@ -368,7 +486,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
368
486
  let stderr = '';
369
487
 
370
488
  try {
371
- const stream = await this.client.commands.executeStream(command, sessionId);
489
+ const stream = await this.client.commands.executeStream(
490
+ command,
491
+ sessionId
492
+ );
372
493
 
373
494
  for await (const event of parseSSEStream<ExecEvent>(stream)) {
374
495
  // Check for cancellation
@@ -413,7 +534,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
413
534
 
414
535
  // If we get here without a complete event, something went wrong
415
536
  throw new Error('Stream ended without completion event');
416
-
417
537
  } catch (error) {
418
538
  if (options.signal?.aborted) {
419
539
  throw new Error('Operation was aborted');
@@ -461,8 +581,15 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
461
581
  pid: data.pid,
462
582
  command: data.command,
463
583
  status: data.status,
464
- startTime: typeof data.startTime === 'string' ? new Date(data.startTime) : data.startTime,
465
- 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,
466
593
  exitCode: data.exitCode,
467
594
  sessionId,
468
595
 
@@ -482,25 +609,35 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
482
609
  };
483
610
  }
484
611
 
485
-
486
612
  // Background process management
487
- 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> {
488
618
  // Use the new HttpClient method to start the process
489
619
  try {
490
- const session = sessionId ?? await this.ensureDefaultSession();
491
- const response = await this.client.processes.startProcess(command, session, {
492
- processId: options?.processId
493
- });
494
-
495
- const processObj = this.createProcessFromDTO({
496
- id: response.processId,
497
- pid: response.pid,
498
- command: response.command,
499
- status: 'running' as ProcessStatus,
500
- startTime: new Date(),
501
- endTime: undefined,
502
- exitCode: undefined
503
- }, 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
+ );
504
641
 
505
642
  // Call onStart callback if provided
506
643
  if (options?.onStart) {
@@ -508,7 +645,6 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
508
645
  }
509
646
 
510
647
  return processObj;
511
-
512
648
  } catch (error) {
513
649
  if (options?.onError && error instanceof Error) {
514
650
  options.onError(error);
@@ -519,42 +655,52 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
519
655
  }
520
656
 
521
657
  async listProcesses(sessionId?: string): Promise<Process[]> {
522
- const session = sessionId ?? await this.ensureDefaultSession();
658
+ const session = sessionId ?? (await this.ensureDefaultSession());
523
659
  const response = await this.client.processes.listProcesses();
524
660
 
525
- return response.processes.map(processData =>
526
- this.createProcessFromDTO({
527
- id: processData.id,
528
- pid: processData.pid,
529
- command: processData.command,
530
- status: processData.status,
531
- startTime: processData.startTime,
532
- endTime: processData.endTime,
533
- exitCode: processData.exitCode
534
- }, 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
+ )
535
674
  );
536
675
  }
537
676
 
538
677
  async getProcess(id: string, sessionId?: string): Promise<Process | null> {
539
- const session = sessionId ?? await this.ensureDefaultSession();
678
+ const session = sessionId ?? (await this.ensureDefaultSession());
540
679
  const response = await this.client.processes.getProcess(id);
541
680
  if (!response.process) {
542
681
  return null;
543
682
  }
544
683
 
545
684
  const processData = response.process;
546
- return this.createProcessFromDTO({
547
- id: processData.id,
548
- pid: processData.pid,
549
- command: processData.command,
550
- status: processData.status,
551
- startTime: processData.startTime,
552
- endTime: processData.endTime,
553
- exitCode: processData.exitCode
554
- }, session);
555
- }
556
-
557
- 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> {
558
704
  // Note: signal parameter is not currently supported by the HttpClient implementation
559
705
  // The HTTP client already throws properly typed errors, so we just let them propagate
560
706
  await this.client.processes.killProcess(id);
@@ -572,7 +718,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
572
718
  return 0;
573
719
  }
574
720
 
575
- 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 }> {
576
725
  // The HTTP client already throws properly typed errors, so we just let them propagate
577
726
  const response = await this.client.processes.getProcessLogs(id);
578
727
  return {
@@ -582,9 +731,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
582
731
  };
583
732
  }
584
733
 
585
-
586
734
  // Streaming methods - return ReadableStream for RPC compatibility
587
- async execStream(command: string, options?: StreamOptions): Promise<ReadableStream<Uint8Array>> {
735
+ async execStream(
736
+ command: string,
737
+ options?: StreamOptions
738
+ ): Promise<ReadableStream<Uint8Array>> {
588
739
  // Check for cancellation
589
740
  if (options?.signal?.aborted) {
590
741
  throw new Error('Operation was aborted');
@@ -598,7 +749,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
598
749
  /**
599
750
  * Internal session-aware execStream implementation
600
751
  */
601
- 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>> {
602
757
  // Check for cancellation
603
758
  if (options?.signal?.aborted) {
604
759
  throw new Error('Operation was aborted');
@@ -607,7 +762,13 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
607
762
  return this.client.commands.executeStream(command, sessionId);
608
763
  }
609
764
 
610
- 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>> {
611
772
  // Check for cancellation
612
773
  if (options?.signal?.aborted) {
613
774
  throw new Error('Operation was aborted');
@@ -620,7 +781,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
620
781
  repoUrl: string,
621
782
  options: { branch?: string; targetDir?: string; sessionId?: string }
622
783
  ) {
623
- const session = options.sessionId ?? await this.ensureDefaultSession();
784
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
624
785
  return this.client.git.checkout(repoUrl, session, {
625
786
  branch: options.branch,
626
787
  targetDir: options.targetDir
@@ -631,8 +792,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
631
792
  path: string,
632
793
  options: { recursive?: boolean; sessionId?: string } = {}
633
794
  ) {
634
- const session = options.sessionId ?? await this.ensureDefaultSession();
635
- 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
+ });
636
799
  }
637
800
 
638
801
  async writeFile(
@@ -640,21 +803,19 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
640
803
  content: string,
641
804
  options: { encoding?: string; sessionId?: string } = {}
642
805
  ) {
643
- const session = options.sessionId ?? await this.ensureDefaultSession();
644
- 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
+ });
645
810
  }
646
811
 
647
812
  async deleteFile(path: string, sessionId?: string) {
648
- const session = sessionId ?? await this.ensureDefaultSession();
813
+ const session = sessionId ?? (await this.ensureDefaultSession());
649
814
  return this.client.files.deleteFile(path, session);
650
815
  }
651
816
 
652
- async renameFile(
653
- oldPath: string,
654
- newPath: string,
655
- sessionId?: string
656
- ) {
657
- const session = sessionId ?? await this.ensureDefaultSession();
817
+ async renameFile(oldPath: string, newPath: string, sessionId?: string) {
818
+ const session = sessionId ?? (await this.ensureDefaultSession());
658
819
  return this.client.files.renameFile(oldPath, newPath, session);
659
820
  }
660
821
 
@@ -663,7 +824,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
663
824
  destinationPath: string,
664
825
  sessionId?: string
665
826
  ) {
666
- const session = sessionId ?? await this.ensureDefaultSession();
827
+ const session = sessionId ?? (await this.ensureDefaultSession());
667
828
  return this.client.files.moveFile(sourcePath, destinationPath, session);
668
829
  }
669
830
 
@@ -671,8 +832,10 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
671
832
  path: string,
672
833
  options: { encoding?: string; sessionId?: string } = {}
673
834
  ) {
674
- const session = options.sessionId ?? await this.ensureDefaultSession();
675
- 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
+ });
676
839
  }
677
840
 
678
841
  /**
@@ -685,7 +848,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
685
848
  path: string,
686
849
  options: { sessionId?: string } = {}
687
850
  ): Promise<ReadableStream<Uint8Array>> {
688
- const session = options.sessionId ?? await this.ensureDefaultSession();
851
+ const session = options.sessionId ?? (await this.ensureDefaultSession());
689
852
  return this.client.files.readFileStream(path, session);
690
853
  }
691
854
 
@@ -697,6 +860,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
697
860
  return this.client.files.listFiles(path, session, options);
698
861
  }
699
862
 
863
+ async exists(path: string, sessionId?: string) {
864
+ const session = sessionId ?? (await this.ensureDefaultSession());
865
+ return this.client.files.exists(path, session);
866
+ }
867
+
700
868
  async exposePort(port: number, options: { name?: string; hostname: string }) {
701
869
  // Check if hostname is workers.dev domain (doesn't support wildcard subdomains)
702
870
  if (options.hostname.endsWith('.workers.dev')) {
@@ -715,7 +883,9 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
715
883
 
716
884
  // We need the sandbox name to construct preview URLs
717
885
  if (!this.sandboxName) {
718
- 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
+ );
719
889
  }
720
890
 
721
891
  // Generate and store token for this port
@@ -723,18 +893,25 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
723
893
  this.portTokens.set(port, token);
724
894
  await this.persistPortTokens();
725
895
 
726
- 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
+ );
727
902
 
728
903
  return {
729
904
  url,
730
905
  port,
731
- name: options?.name,
906
+ name: options?.name
732
907
  };
733
908
  }
734
909
 
735
910
  async unexposePort(port: number) {
736
911
  if (!validatePort(port)) {
737
- 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
+ );
738
915
  }
739
916
 
740
917
  const sessionId = await this.ensureDefaultSession();
@@ -753,32 +930,44 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
753
930
 
754
931
  // We need the sandbox name to construct preview URLs
755
932
  if (!this.sandboxName) {
756
- 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
+ );
757
936
  }
758
937
 
759
- return response.ports.map(port => {
938
+ return response.ports.map((port) => {
760
939
  // Get token for this port - must exist for all exposed ports
761
940
  const token = this.portTokens.get(port.port);
762
941
  if (!token) {
763
- 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
+ );
764
945
  }
765
946
 
766
947
  return {
767
- url: this.constructPreviewUrl(port.port, this.sandboxName!, hostname, token),
948
+ url: this.constructPreviewUrl(
949
+ port.port,
950
+ this.sandboxName!,
951
+ hostname,
952
+ token
953
+ ),
768
954
  port: port.port,
769
- status: port.status,
955
+ status: port.status
770
956
  };
771
957
  });
772
958
  }
773
959
 
774
-
775
960
  async isPortExposed(port: number): Promise<boolean> {
776
961
  try {
777
962
  const sessionId = await this.ensureDefaultSession();
778
963
  const response = await this.client.ports.getExposedPorts(sessionId);
779
- return response.ports.some(exposedPort => exposedPort.port === port);
964
+ return response.ports.some((exposedPort) => exposedPort.port === port);
780
965
  } catch (error) {
781
- 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
+ );
782
971
  return false;
783
972
  }
784
973
  }
@@ -794,7 +983,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
794
983
  const storedToken = this.portTokens.get(port);
795
984
  if (!storedToken) {
796
985
  // This should not happen - all exposed ports must have tokens
797
- 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
+ );
798
991
  return false;
799
992
  }
800
993
 
@@ -810,7 +1003,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
810
1003
 
811
1004
  // Convert to base64url format (URL-safe, no padding, lowercase)
812
1005
  const base64 = btoa(String.fromCharCode(...array));
813
- return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '').toLowerCase();
1006
+ return base64
1007
+ .replace(/\+/g, '-')
1008
+ .replace(/\//g, '_')
1009
+ .replace(/=/g, '')
1010
+ .toLowerCase();
814
1011
  }
815
1012
 
816
1013
  private async persistPortTokens(): Promise<void> {
@@ -822,9 +1019,16 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
822
1019
  await this.ctx.storage.put('portTokens', tokensObj);
823
1020
  }
824
1021
 
825
- 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 {
826
1028
  if (!validatePort(port)) {
827
- 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
+ );
828
1032
  }
829
1033
 
830
1034
  // Validate sandbox ID (will throw SecurityError if invalid)
@@ -846,14 +1050,18 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
846
1050
 
847
1051
  return baseUrl.toString();
848
1052
  } catch (error) {
849
- 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
+ );
850
1058
  }
851
1059
  }
852
1060
 
853
1061
  // Production subdomain logic - enforce HTTPS
854
1062
  try {
855
1063
  // Always use HTTPS for production (non-localhost)
856
- const protocol = "https";
1064
+ const protocol = 'https';
857
1065
  const baseUrl = new URL(`${protocol}://${hostname}`);
858
1066
 
859
1067
  // Construct subdomain safely with mandatory token
@@ -862,7 +1070,11 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
862
1070
 
863
1071
  return baseUrl.toString();
864
1072
  } catch (error) {
865
- 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
+ );
866
1078
  }
867
1079
  }
868
1080
 
@@ -881,7 +1093,7 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
881
1093
  await this.client.utils.createSession({
882
1094
  id: sessionId,
883
1095
  env: options?.env,
884
- cwd: options?.cwd,
1096
+ cwd: options?.cwd
885
1097
  });
886
1098
 
887
1099
  // Return wrapper that binds sessionId to all operations
@@ -903,6 +1115,34 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
903
1115
  return this.getSessionWrapper(sessionId);
904
1116
  }
905
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
+
906
1146
  /**
907
1147
  * Internal helper to create ExecutionSession wrapper for a given sessionId
908
1148
  * Used by both createSession and getSession
@@ -912,31 +1152,42 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
912
1152
  id: sessionId,
913
1153
 
914
1154
  // Command execution - delegate to internal session-aware methods
915
- exec: (command, options) => this.execWithSession(command, sessionId, options),
916
- 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),
917
1159
 
918
1160
  // Process management
919
- startProcess: (command, options) => this.startProcess(command, options, sessionId),
1161
+ startProcess: (command, options) =>
1162
+ this.startProcess(command, options, sessionId),
920
1163
  listProcesses: () => this.listProcesses(sessionId),
921
1164
  getProcess: (id) => this.getProcess(id, sessionId),
922
1165
  killProcess: (id, signal) => this.killProcess(id, signal),
923
1166
  killAllProcesses: () => this.killAllProcesses(),
924
1167
  cleanupCompletedProcesses: () => this.cleanupCompletedProcesses(),
925
1168
  getProcessLogs: (id) => this.getProcessLogs(id),
926
- streamProcessLogs: (processId, options) => this.streamProcessLogs(processId, options),
1169
+ streamProcessLogs: (processId, options) =>
1170
+ this.streamProcessLogs(processId, options),
927
1171
 
928
1172
  // File operations - pass sessionId via options or parameter
929
- writeFile: (path, content, options) => this.writeFile(path, content, { ...options, sessionId }),
930
- 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 }),
931
1177
  readFileStream: (path) => this.readFileStream(path, { sessionId }),
932
1178
  mkdir: (path, options) => this.mkdir(path, { ...options, sessionId }),
933
1179
  deleteFile: (path) => this.deleteFile(path, sessionId),
934
- renameFile: (oldPath, newPath) => this.renameFile(oldPath, newPath, sessionId),
935
- moveFile: (sourcePath, destPath) => this.moveFile(sourcePath, destPath, sessionId),
936
- 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),
1186
+ exists: (path) => this.exists(path, sessionId),
937
1187
 
938
1188
  // Git operations
939
- gitCheckout: (repoUrl, options) => this.gitCheckout(repoUrl, { ...options, sessionId }),
1189
+ gitCheckout: (repoUrl, options) =>
1190
+ this.gitCheckout(repoUrl, { ...options, sessionId }),
940
1191
 
941
1192
  // Environment management - needs special handling
942
1193
  setEnvVars: async (envVars: Record<string, string>) => {
@@ -946,27 +1197,39 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
946
1197
  const escapedValue = value.replace(/'/g, "'\\''");
947
1198
  const exportCommand = `export ${key}='${escapedValue}'`;
948
1199
 
949
- const result = await this.client.commands.execute(exportCommand, sessionId);
1200
+ const result = await this.client.commands.execute(
1201
+ exportCommand,
1202
+ sessionId
1203
+ );
950
1204
 
951
1205
  if (result.exitCode !== 0) {
952
- 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
+ );
953
1209
  }
954
1210
  }
955
1211
  } catch (error) {
956
- 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
+ );
957
1217
  throw error;
958
1218
  }
959
1219
  },
960
1220
 
961
1221
  // Code interpreter methods - delegate to sandbox's code interpreter
962
- createCodeContext: (options) => this.codeInterpreter.createCodeContext(options),
1222
+ createCodeContext: (options) =>
1223
+ this.codeInterpreter.createCodeContext(options),
963
1224
  runCode: async (code, options) => {
964
1225
  const execution = await this.codeInterpreter.runCode(code, options);
965
1226
  return execution.toJSON();
966
1227
  },
967
- runCodeStream: (code, options) => this.codeInterpreter.runCodeStream(code, options),
1228
+ runCodeStream: (code, options) =>
1229
+ this.codeInterpreter.runCodeStream(code, options),
968
1230
  listCodeContexts: () => this.codeInterpreter.listCodeContexts(),
969
- deleteCodeContext: (contextId) => this.codeInterpreter.deleteCodeContext(contextId),
1231
+ deleteCodeContext: (contextId) =>
1232
+ this.codeInterpreter.deleteCodeContext(contextId)
970
1233
  };
971
1234
  }
972
1235
 
@@ -974,16 +1237,24 @@ export class Sandbox<Env = unknown> extends Container<Env> implements ISandbox {
974
1237
  // Code interpreter methods - delegate to CodeInterpreter wrapper
975
1238
  // ============================================================================
976
1239
 
977
- async createCodeContext(options?: CreateContextOptions): Promise<CodeContext> {
1240
+ async createCodeContext(
1241
+ options?: CreateContextOptions
1242
+ ): Promise<CodeContext> {
978
1243
  return this.codeInterpreter.createCodeContext(options);
979
1244
  }
980
1245
 
981
- async runCode(code: string, options?: RunCodeOptions): Promise<ExecutionResult> {
1246
+ async runCode(
1247
+ code: string,
1248
+ options?: RunCodeOptions
1249
+ ): Promise<ExecutionResult> {
982
1250
  const execution = await this.codeInterpreter.runCode(code, options);
983
1251
  return execution.toJSON();
984
1252
  }
985
1253
 
986
- async runCodeStream(code: string, options?: RunCodeOptions): Promise<ReadableStream> {
1254
+ async runCodeStream(
1255
+ code: string,
1256
+ options?: RunCodeOptions
1257
+ ): Promise<ReadableStream> {
987
1258
  return this.codeInterpreter.runCodeStream(code, options);
988
1259
  }
989
1260