@cloudflare/sandbox 0.0.0-e1fa354 → 0.0.0-e943505

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.
@@ -0,0 +1,464 @@
1
+ import { type ChildProcess, spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+
4
+ export type InterpreterLanguage = "python" | "javascript" | "typescript";
5
+
6
+ export interface InterpreterProcess {
7
+ id: string;
8
+ language: InterpreterLanguage;
9
+ process: ChildProcess;
10
+ sessionId?: string;
11
+ lastUsed: Date;
12
+ isAvailable: boolean;
13
+ }
14
+
15
+ export interface ExecutionResult {
16
+ stdout: string;
17
+ stderr: string;
18
+ success: boolean;
19
+ executionId: string;
20
+ outputs?: RichOutput[];
21
+ error?: {
22
+ type: string;
23
+ message: string;
24
+ traceback?: string;
25
+ };
26
+ }
27
+
28
+ export interface RichOutput {
29
+ type: "text" | "image" | "jpeg" | "svg" | "html" | "json" | "latex" | "markdown" | "javascript" | "error";
30
+ data: string;
31
+ metadata?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface PoolConfig {
35
+ maxProcesses: number;
36
+ idleTimeout: number; // milliseconds
37
+ minSize: number;
38
+ preWarmScript?: string;
39
+ }
40
+
41
+ export interface ExecutorPoolConfig extends PoolConfig {
42
+ executor: InterpreterLanguage;
43
+ }
44
+
45
+ const DEFAULT_EXECUTOR_CONFIGS: Record<InterpreterLanguage, ExecutorPoolConfig> = {
46
+ python: {
47
+ executor: "python",
48
+ minSize: 3,
49
+ maxProcesses: 15,
50
+ idleTimeout: 5 * 60 * 1000, // 5 minutes
51
+ preWarmScript: `
52
+ import matplotlib.pyplot as plt
53
+ import pandas as pd
54
+ import numpy as np
55
+ import json
56
+ print(json.dumps({"status": "pre-warmed"}))
57
+ `
58
+ },
59
+ javascript: {
60
+ executor: "javascript",
61
+ minSize: 3,
62
+ maxProcesses: 10,
63
+ idleTimeout: 5 * 60 * 1000,
64
+ preWarmScript: `
65
+ const fs = require('fs');
66
+ const path = require('path');
67
+ const util = require('util');
68
+ const crypto = require('crypto');
69
+ for(let i = 0; i < 1000; i++) {
70
+ JSON.stringify({x: i, data: Math.random()});
71
+ }
72
+ console.log(JSON.stringify({"status": "pre-warmed"}));
73
+ `
74
+ },
75
+ typescript: {
76
+ executor: "typescript",
77
+ minSize: 3,
78
+ maxProcesses: 10,
79
+ idleTimeout: 5 * 60 * 1000,
80
+ preWarmScript: `
81
+ const { transformSync } = require('esbuild');
82
+ const warmupCode = 'interface Test { x: number; } const test: Test = { x: 42 }; test.x';
83
+ transformSync(warmupCode, { loader: 'ts', target: 'es2020', format: 'cjs' });
84
+ console.log(JSON.stringify({"status": "pre-warmed"}));
85
+ `
86
+ }
87
+ };
88
+
89
+ export class ProcessPoolManager {
90
+ private pools: Map<InterpreterLanguage, InterpreterProcess[]> = new Map();
91
+ private poolConfigs: Map<InterpreterLanguage, ExecutorPoolConfig> = new Map();
92
+ private cleanupInterval?: NodeJS.Timeout;
93
+
94
+ constructor(customConfigs: Partial<Record<InterpreterLanguage, Partial<ExecutorPoolConfig>>> = {}) {
95
+ const executorEntries = Object.entries(DEFAULT_EXECUTOR_CONFIGS) as [InterpreterLanguage, ExecutorPoolConfig][];
96
+
97
+ for (const [executor, defaultConfig] of executorEntries) {
98
+ const userConfig = customConfigs[executor] || {};
99
+ const envMinSize = process.env[`${executor.toUpperCase()}_POOL_MIN_SIZE`];
100
+ const envMaxSize = process.env[`${executor.toUpperCase()}_POOL_MAX_SIZE`];
101
+
102
+ const config: ExecutorPoolConfig = {
103
+ ...defaultConfig,
104
+ ...userConfig,
105
+ // Environment variables override user config override defaults
106
+ minSize: envMinSize ? parseInt(envMinSize) : (userConfig.minSize || defaultConfig.minSize),
107
+ maxProcesses: envMaxSize ? parseInt(envMaxSize) : (userConfig.maxProcesses || defaultConfig.maxProcesses)
108
+ };
109
+
110
+ this.poolConfigs.set(executor, config);
111
+ this.pools.set(executor, []);
112
+ }
113
+
114
+ const pythonConfig = this.poolConfigs.get("python");
115
+ if (pythonConfig) {
116
+ this.cleanupInterval = setInterval(() => {
117
+ this.cleanupIdleProcesses();
118
+ }, pythonConfig.idleTimeout / 2);
119
+ }
120
+
121
+ // Start pre-warming in background - don't block constructor
122
+ this.startPreWarming().catch((error) => {
123
+ console.error('[ProcessPool] Pre-warming failed:', error);
124
+ });
125
+ }
126
+
127
+ async execute(
128
+ language: InterpreterLanguage,
129
+ code: string,
130
+ sessionId?: string,
131
+ timeout = 30000
132
+ ): Promise<ExecutionResult> {
133
+ const totalStartTime = Date.now();
134
+ const process = await this.getProcess(language, sessionId);
135
+ const processAcquireTime = Date.now() - totalStartTime;
136
+
137
+ const executionId = randomUUID();
138
+
139
+ try {
140
+ const execStartTime = Date.now();
141
+ const result = await this.executeCode(process, code, executionId, timeout);
142
+ const execTime = Date.now() - execStartTime;
143
+ const totalTime = Date.now() - totalStartTime;
144
+
145
+ console.log(`[ProcessPool] Execution complete - Process acquire: ${processAcquireTime}ms, Code exec: ${execTime}ms, Total: ${totalTime}ms`);
146
+ return result;
147
+ } finally {
148
+ this.releaseProcess(process, sessionId);
149
+ }
150
+ }
151
+
152
+ private async getProcess(
153
+ language: InterpreterLanguage,
154
+ sessionId?: string
155
+ ): Promise<InterpreterProcess> {
156
+ const pool = this.pools.get(language)!;
157
+
158
+ if (sessionId) {
159
+ const existingProcess = pool.find(
160
+ (p) => p.sessionId === sessionId && p.isAvailable
161
+ );
162
+ if (existingProcess) {
163
+ existingProcess.isAvailable = false;
164
+ existingProcess.lastUsed = new Date();
165
+ return existingProcess;
166
+ }
167
+ }
168
+
169
+ const availableProcess = pool.find((p) => p.isAvailable && !p.sessionId);
170
+ if (availableProcess) {
171
+ availableProcess.isAvailable = false;
172
+ availableProcess.sessionId = sessionId;
173
+ availableProcess.lastUsed = new Date();
174
+ return availableProcess;
175
+ }
176
+
177
+ const config = this.poolConfigs.get(language)!;
178
+ if (pool.length < config.maxProcesses) {
179
+ const newProcess = await this.createProcess(language, sessionId);
180
+ pool.push(newProcess);
181
+ return newProcess;
182
+ }
183
+
184
+ return new Promise((resolve) => {
185
+ const checkForAvailable = () => {
186
+ const available = pool.find((p) => p.isAvailable);
187
+ if (available) {
188
+ available.isAvailable = false;
189
+ available.sessionId = sessionId;
190
+ available.lastUsed = new Date();
191
+ resolve(available);
192
+ } else {
193
+ setTimeout(checkForAvailable, 100);
194
+ }
195
+ };
196
+ checkForAvailable();
197
+ });
198
+ }
199
+
200
+ private async createProcess(
201
+ language: InterpreterLanguage,
202
+ sessionId?: string
203
+ ): Promise<InterpreterProcess> {
204
+ const startTime = Date.now();
205
+ const id = randomUUID();
206
+ let command: string;
207
+ let args: string[];
208
+
209
+ switch (language) {
210
+ case "python":
211
+ command = "python3";
212
+ args = ["-u", "/container-server/runtime/executors/python/ipython_executor.py"];
213
+ break;
214
+ case "javascript":
215
+ command = "node";
216
+ args = ["/container-server/runtime/executors/javascript/node_executor.js"];
217
+ break;
218
+ case "typescript":
219
+ command = "node";
220
+ args = ["/container-server/runtime/executors/typescript/ts_executor.js"];
221
+ break;
222
+ }
223
+
224
+ console.log(`[ProcessPool] Spawning ${language} process: ${command} ${args.join(' ')}`);
225
+
226
+ const childProcess = spawn(command, args, {
227
+ stdio: ["pipe", "pipe", "pipe"],
228
+ env: {
229
+ ...process.env,
230
+ PYTHONUNBUFFERED: "1",
231
+ NODE_NO_WARNINGS: "1",
232
+ },
233
+ cwd: "/workspace",
234
+ });
235
+
236
+ const interpreterProcess: InterpreterProcess = {
237
+ id,
238
+ language,
239
+ process: childProcess,
240
+ sessionId,
241
+ lastUsed: new Date(),
242
+ isAvailable: false,
243
+ };
244
+
245
+ return new Promise((resolve, reject) => {
246
+ let readyBuffer = "";
247
+ let errorBuffer = "";
248
+
249
+ const timeout = setTimeout(() => {
250
+ childProcess.kill();
251
+ console.error(`[ProcessPool] ${language} executor timeout. stdout: "${readyBuffer}", stderr: "${errorBuffer}"`);
252
+ reject(new Error(`${language} executor failed to start`));
253
+ }, 5000);
254
+
255
+ const readyHandler = (data: Buffer) => {
256
+ readyBuffer += data.toString();
257
+ console.log(`[ProcessPool] ${language} stdout:`, data.toString());
258
+
259
+ if (readyBuffer.includes('"ready"')) {
260
+ clearTimeout(timeout);
261
+ childProcess.stdout?.removeListener("data", readyHandler);
262
+ childProcess.stderr?.removeListener("data", errorHandler);
263
+ const readyTime = Date.now() - startTime;
264
+ console.log(`[ProcessPool] ${language} process ${id} ready in ${readyTime}ms`);
265
+ resolve(interpreterProcess);
266
+ }
267
+ };
268
+
269
+ const errorHandler = (data: Buffer) => {
270
+ errorBuffer += data.toString();
271
+ console.error(`[ProcessPool] ${language} stderr:`, data.toString());
272
+ };
273
+
274
+ childProcess.stdout?.on("data", readyHandler);
275
+ childProcess.stderr?.on("data", errorHandler);
276
+
277
+ childProcess.once("error", (err) => {
278
+ clearTimeout(timeout);
279
+ console.error(`[ProcessPool] ${language} spawn error:`, err);
280
+ reject(err);
281
+ });
282
+
283
+ childProcess.once("exit", (code) => {
284
+ if (code !== 0) {
285
+ clearTimeout(timeout);
286
+ console.error(`[ProcessPool] ${language} exited with code ${code}`);
287
+ reject(new Error(`${language} executor exited with code ${code}`));
288
+ }
289
+ });
290
+ });
291
+ }
292
+
293
+ private async executeCode(
294
+ process: InterpreterProcess,
295
+ code: string,
296
+ executionId: string,
297
+ timeout: number
298
+ ): Promise<ExecutionResult> {
299
+ const request = JSON.stringify({ code, executionId });
300
+
301
+ return new Promise((resolve, reject) => {
302
+ const timer = setTimeout(() => {
303
+ reject(new Error("Execution timeout"));
304
+ }, timeout);
305
+
306
+ let responseBuffer = "";
307
+
308
+ const responseHandler = (data: Buffer) => {
309
+ responseBuffer += data.toString();
310
+
311
+ try {
312
+ const response = JSON.parse(responseBuffer);
313
+ clearTimeout(timer);
314
+ process.process.stdout?.removeListener("data", responseHandler);
315
+
316
+ resolve({
317
+ stdout: response.stdout || "",
318
+ stderr: response.stderr || "",
319
+ success: response.success !== false,
320
+ executionId,
321
+ outputs: response.outputs || [],
322
+ error: response.error || null,
323
+ });
324
+ } catch (e) {
325
+ }
326
+ };
327
+
328
+ process.process.stdout?.on("data", responseHandler);
329
+ process.process.stdin?.write(`${request}\n`);
330
+ });
331
+ }
332
+
333
+ private releaseProcess(
334
+ process: InterpreterProcess,
335
+ sessionId?: string
336
+ ): void {
337
+ if (!sessionId) {
338
+ process.sessionId = undefined;
339
+ process.isAvailable = true;
340
+ } else {
341
+ process.isAvailable = true;
342
+ }
343
+ }
344
+
345
+ private async startPreWarming(): Promise<void> {
346
+ console.log('[ProcessPool] Starting unified pre-warming for all executors...');
347
+ const startTime = Date.now();
348
+
349
+ const warmupPromises = Array.from(this.poolConfigs.entries()).map(
350
+ async ([executor, config]) => {
351
+ if (config.minSize > 0) {
352
+ await this.preWarmExecutor(executor, config);
353
+ }
354
+ }
355
+ );
356
+
357
+ try {
358
+ await Promise.all(warmupPromises);
359
+ const totalTime = Date.now() - startTime;
360
+ console.log(`[ProcessPool] Pre-warming complete for all executors in ${totalTime}ms`);
361
+ } catch (error) {
362
+ console.error('[ProcessPool] Pre-warming failed:', error);
363
+ }
364
+ }
365
+
366
+ private async preWarmExecutor(executor: InterpreterLanguage, config: ExecutorPoolConfig): Promise<void> {
367
+ const startTime = Date.now();
368
+ console.log(`[ProcessPool] Pre-warming ${config.minSize} ${executor} processes...`);
369
+
370
+ const pool = this.pools.get(executor);
371
+ if (!pool) {
372
+ console.error(`[ProcessPool] No pool found for executor: ${executor}`);
373
+ return;
374
+ }
375
+
376
+ for (let i = 0; i < config.minSize; i++) {
377
+ try {
378
+ const sessionId = `pre-warm-${executor}-${i}-${Date.now()}`;
379
+ const process = await this.createProcess(executor, sessionId);
380
+
381
+ if (config.preWarmScript) {
382
+ await this.executePreWarmScript(process, config.preWarmScript, executor);
383
+ }
384
+
385
+ process.isAvailable = true;
386
+ process.sessionId = undefined;
387
+ pool.push(process);
388
+ } catch (error) {
389
+ console.error(`[ProcessPool] Failed to pre-warm ${executor} process ${i}:`, error);
390
+ }
391
+ }
392
+
393
+ const warmupTime = Date.now() - startTime;
394
+ const actualCount = pool.filter(p => p.isAvailable).length;
395
+ console.log(`[ProcessPool] Pre-warmed ${actualCount}/${config.minSize} ${executor} processes in ${warmupTime}ms`);
396
+ }
397
+
398
+ private async executePreWarmScript(
399
+ process: InterpreterProcess,
400
+ script: string,
401
+ executor: InterpreterLanguage
402
+ ): Promise<void> {
403
+ try {
404
+ const executionId = `pre-warm-${Date.now()}`;
405
+ const result = await this.executeCode(process, script, executionId, 10000);
406
+
407
+ if (result.success) {
408
+ console.log(`[ProcessPool] ${executor} pre-warm script executed successfully`);
409
+ } else {
410
+ console.warn(`[ProcessPool] ${executor} pre-warm script failed:`, result.stderr);
411
+ }
412
+ } catch (error) {
413
+ console.warn(`[ProcessPool] ${executor} pre-warm script error:`, error);
414
+ }
415
+ }
416
+
417
+ private cleanupIdleProcesses(): void {
418
+ const now = new Date();
419
+
420
+ const executors = Array.from(this.pools.keys());
421
+ for (const executor of executors) {
422
+ const pool = this.pools.get(executor);
423
+ const config = this.poolConfigs.get(executor);
424
+
425
+ if (!pool || !config) {
426
+ continue;
427
+ }
428
+
429
+ for (let i = pool.length - 1; i >= 0; i--) {
430
+ const process = pool[i];
431
+ const idleTime = now.getTime() - process.lastUsed.getTime();
432
+
433
+ // Only clean up excess processes beyond minimum pool size
434
+ if (process.isAvailable &&
435
+ idleTime > config.idleTimeout &&
436
+ pool.filter(p => p.isAvailable).length > config.minSize) {
437
+ process.process.kill();
438
+ pool.splice(i, 1);
439
+ console.log(`[ProcessPool] Cleaned up idle ${executor} process (${pool.length} remaining)`);
440
+ }
441
+ }
442
+ }
443
+ }
444
+
445
+ async shutdown(): Promise<void> {
446
+ if (this.cleanupInterval) {
447
+ clearInterval(this.cleanupInterval);
448
+ }
449
+
450
+ const executors = Array.from(this.pools.keys());
451
+ for (const executor of executors) {
452
+ const pool = this.pools.get(executor);
453
+ if (pool) {
454
+ for (const process of pool) {
455
+ process.process.kill();
456
+ }
457
+ }
458
+ }
459
+
460
+ this.pools.clear();
461
+ }
462
+ }
463
+
464
+ export const processPool = new ProcessPoolManager();
@@ -1,84 +1,11 @@
1
1
  #!/bin/bash
2
2
 
3
- # Function to check if Jupyter is ready
4
- check_jupyter_ready() {
5
- # Check if API is responsive and kernelspecs are available
6
- curl -s http://localhost:8888/api/kernelspecs > /dev/null 2>&1
7
- }
3
+ echo "[Startup] Starting interpreter server..."
4
+ echo "[Startup] Process pool system initialized"
8
5
 
9
- # Function to notify Bun server that Jupyter is ready
10
- notify_jupyter_ready() {
11
- # Create a marker file that the Bun server can check
12
- touch /tmp/jupyter-ready
13
- echo "[Startup] Jupyter is ready, notified Bun server"
14
- }
6
+ echo "[Startup] Environment configured:"
7
+ echo " - Working directory: $(pwd)"
15
8
 
16
- # Start Jupyter server in background
17
- echo "[Startup] Starting Jupyter server..."
18
- jupyter server \
19
- --config=/container-server/jupyter_config.py \
20
- > /tmp/jupyter.log 2>&1 &
21
-
22
- JUPYTER_PID=$!
23
-
24
- # Start Bun server immediately (parallel startup)
9
+ # Start Bun server - the only process we need now
25
10
  echo "[Startup] Starting Bun server..."
26
- bun index.ts &
27
- BUN_PID=$!
28
-
29
- # Monitor Jupyter readiness in background
30
- (
31
- echo "[Startup] Monitoring Jupyter readiness in background..."
32
- MAX_ATTEMPTS=60
33
- ATTEMPT=0
34
-
35
- # Track start time for reporting
36
- START_TIME=$(date +%s.%N)
37
-
38
- while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
39
- if check_jupyter_ready; then
40
- notify_jupyter_ready
41
- END_TIME=$(date +%s.%N)
42
- ELAPSED=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}")
43
- echo "[Startup] Jupyter server is ready after $ELAPSED seconds ($ATTEMPT attempts)"
44
- break
45
- fi
46
-
47
- # Check if Jupyter process is still running
48
- if ! kill -0 $JUPYTER_PID 2>/dev/null; then
49
- echo "[Startup] WARNING: Jupyter process died. Check /tmp/jupyter.log for details"
50
- cat /tmp/jupyter.log
51
- # Don't exit - let Bun server continue running in degraded mode
52
- break
53
- fi
54
-
55
- ATTEMPT=$((ATTEMPT + 1))
56
-
57
- # Start with faster checks
58
- if [ $ATTEMPT -eq 1 ]; then
59
- DELAY=0.5 # Start at 0.5s
60
- else
61
- # Exponential backoff with 1.3x multiplier (less aggressive than 1.5x)
62
- DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.3}")
63
- # Cap at 2s max (instead of 5s)
64
- if [ $(awk "BEGIN {print ($DELAY > 2)}") -eq 1 ]; then
65
- DELAY=2
66
- fi
67
- fi
68
-
69
- # Log with current delay for transparency
70
- echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, next check in ${DELAY}s)"
71
-
72
- sleep $DELAY
73
- done
74
-
75
- if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
76
- echo "[Startup] WARNING: Jupyter failed to become ready within attempts"
77
- echo "[Startup] Jupyter logs:"
78
- cat /tmp/jupyter.log
79
- # Don't exit - let Bun server continue in degraded mode
80
- fi
81
- ) &
82
-
83
- # Wait for Bun server (main process)
84
- wait $BUN_PID
11
+ exec bun index.ts
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@cloudflare/sandbox",
3
- "version": "0.0.0-e1fa354",
3
+ "version": "0.0.0-e943505",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cloudflare/sandbox-sdk"
7
7
  },
8
8
  "description": "A sandboxed environment for running commands",
9
9
  "dependencies": {
10
- "@cloudflare/containers": "^0.0.25"
10
+ "@cloudflare/containers": "^0.0.28"
11
11
  },
12
12
  "tags": [
13
13
  "sandbox",
package/src/errors.ts CHANGED
@@ -23,17 +23,17 @@ export class SandboxError extends Error {
23
23
  }
24
24
 
25
25
  /**
26
- * Error thrown when Jupyter functionality is requested but the service is still initializing.
26
+ * Error thrown when interpreter functionality is requested but the service is still initializing.
27
27
  *
28
- * Note: With the current implementation, requests wait for Jupyter to be ready.
28
+ * Note: With the current implementation, requests wait for interpreter to be ready.
29
29
  * This error is only thrown when:
30
- * 1. The request times out waiting for Jupyter (default: 30 seconds)
31
- * 2. Jupyter initialization actually fails
30
+ * 1. The request times out waiting for interpreter (default: 30 seconds)
31
+ * 2. interpreter initialization actually fails
32
32
  *
33
33
  * Most requests will succeed after a delay, not throw this error.
34
34
  */
35
- export class JupyterNotReadyError extends SandboxError {
36
- public readonly code = "JUPYTER_NOT_READY";
35
+ export class InterpreterNotReadyError extends SandboxError {
36
+ public readonly code = "INTERPRETER_NOT_READY";
37
37
  public readonly retryAfter: number;
38
38
  public readonly progress?: number;
39
39
 
@@ -42,7 +42,8 @@ export class JupyterNotReadyError extends SandboxError {
42
42
  options?: { retryAfter?: number; progress?: number }
43
43
  ) {
44
44
  super(
45
- message || "Jupyter is still initializing. Please retry in a few seconds."
45
+ message ||
46
+ "Interpreter is still initializing. Please retry in a few seconds."
46
47
  );
47
48
  this.retryAfter = options?.retryAfter || 5;
48
49
  this.progress = options?.progress;
@@ -123,12 +124,12 @@ export class ServiceUnavailableError extends SandboxError {
123
124
  }
124
125
 
125
126
  /**
126
- * Type guard to check if an error is a JupyterNotReadyError
127
+ * Type guard to check if an error is a InterpreterNotReadyError
127
128
  */
128
- export function isJupyterNotReadyError(
129
+ export function isInterpreterNotReadyError(
129
130
  error: unknown
130
- ): error is JupyterNotReadyError {
131
- return error instanceof JupyterNotReadyError;
131
+ ): error is InterpreterNotReadyError {
132
+ return error instanceof InterpreterNotReadyError;
132
133
  }
133
134
 
134
135
  /**
@@ -143,7 +144,7 @@ export function isSandboxError(error: unknown): error is SandboxError {
143
144
  */
144
145
  export function isRetryableError(error: unknown): boolean {
145
146
  if (
146
- error instanceof JupyterNotReadyError ||
147
+ error instanceof InterpreterNotReadyError ||
147
148
  error instanceof ContainerNotReadyError ||
148
149
  error instanceof ServiceUnavailableError
149
150
  ) {
@@ -189,9 +190,9 @@ export async function parseErrorResponse(
189
190
  );
190
191
  }
191
192
 
192
- // Jupyter initialization error
193
+ // Interpreter initialization error
193
194
  if (data.status === "initializing") {
194
- return new JupyterNotReadyError(data.error, {
195
+ return new InterpreterNotReadyError(data.error, {
195
196
  retryAfter: parseInt(response.headers.get("Retry-After") || "5"),
196
197
  progress: data.progress,
197
198
  });
package/src/index.ts CHANGED
@@ -1,20 +1,31 @@
1
- // Export API response types
1
+ // biome-ignore-start assist/source/organizeImports: Need separate exports for deprecation warnings to work properly
2
+ /**
3
+ * @deprecated Use `InterpreterNotReadyError` instead. Will be removed in a future version.
4
+ */
5
+ export { InterpreterNotReadyError as JupyterNotReadyError } from "./errors";
6
+
7
+ /**
8
+ * @deprecated Use `isInterpreterNotReadyError` instead. Will be removed in a future version.
9
+ */
10
+ export { isInterpreterNotReadyError as isJupyterNotReadyError } from "./errors";
11
+ // biome-ignore-end assist/source/organizeImports: Need separate exports for deprecation warnings to work properly
2
12
 
3
- // Export errors
13
+ // Export API response types
4
14
  export {
5
15
  CodeExecutionError,
6
16
  ContainerNotReadyError,
7
17
  ContextNotFoundError,
8
- isJupyterNotReadyError,
18
+ InterpreterNotReadyError,
19
+ isInterpreterNotReadyError,
9
20
  isRetryableError,
10
21
  isSandboxError,
11
- JupyterNotReadyError,
12
22
  parseErrorResponse,
13
23
  SandboxError,
14
24
  type SandboxErrorResponse,
15
25
  SandboxNetworkError,
16
26
  ServiceUnavailableError,
17
27
  } from "./errors";
28
+
18
29
  // Export code interpreter types
19
30
  export type {
20
31
  ChartData,
@@ -60,5 +71,5 @@ export type {
60
71
  ReadFileResponse,
61
72
  RenameFileResponse,
62
73
  StreamOptions,
63
- WriteFileResponse
74
+ WriteFileResponse,
64
75
  } from "./types";