@blaxel/core 0.2.84-preview.154 → 0.2.84

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.
@@ -5,8 +5,8 @@ import { authentication } from "../authentication/index.js";
5
5
  import { env } from "../common/env.js";
6
6
  import { fs, os, path } from "../common/node.js";
7
7
  // Build info - these placeholders are replaced at build time by build:replace-imports
8
- const BUILD_VERSION = "0.2.84-preview.154";
9
- const BUILD_COMMIT = "44e0848f215027fe6f3981e7decdc1ec192544cf";
8
+ const BUILD_VERSION = "0.2.84";
9
+ const BUILD_COMMIT = "b37c7bd0ef1257de732b54a2f784c6c91a8d450d";
10
10
  const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
11
11
  const BLAXEL_API_VERSION = "2026-04-16";
12
12
  // Cache for config.yaml tracking value
@@ -30,7 +30,7 @@ export class CodeInterpreter extends SandboxInstance {
30
30
  };
31
31
  return new CodeInterpreter(config);
32
32
  }
33
- static async create(sandbox, { safe = true } = {}) {
33
+ static async create(sandbox, { safe = true, createIfNotExist = false } = {}) {
34
34
  // Build a SandboxCreateConfiguration with CodeInterpreter defaults
35
35
  const defaults = {
36
36
  image: CodeInterpreter.DEFAULT_IMAGE,
@@ -63,7 +63,7 @@ export class CodeInterpreter extends SandboxInstance {
63
63
  else {
64
64
  merged = defaults;
65
65
  }
66
- const baseInstance = await super.create(merged, { safe });
66
+ const baseInstance = await super.create(merged, { safe, createIfNotExist });
67
67
  // Create config from the instance
68
68
  const config = {
69
69
  metadata: baseInstance.metadata,
@@ -11,6 +11,13 @@ import { SandboxProcess } from "./process/index.js";
11
11
  import { SandboxSessions } from "./session.js";
12
12
  import { SandboxSystem } from "./system.js";
13
13
  import { normalizeEnvs, normalizePorts, normalizeVolumes } from "./types.js";
14
+ const NON_REUSABLE_SANDBOX_STATUSES = new Set([
15
+ "FAILED",
16
+ "TERMINATED",
17
+ "TERMINATING",
18
+ "DELETING",
19
+ "DEACTIVATING",
20
+ ]);
14
21
  export class SandboxInstance {
15
22
  sandbox;
16
23
  fs;
@@ -96,7 +103,7 @@ export class SandboxInstance {
96
103
  logger.warn("⚠️ Warning: sandbox.wait() is deprecated. You don't need to wait for the sandbox to be deployed anymore.");
97
104
  return this;
98
105
  }
99
- static async create(sandbox, { safe = false } = {}) {
106
+ static async create(sandbox, { safe = false, createIfNotExist = false } = {}) {
100
107
  const defaultName = `sandbox-${uuidv4().replace(/-/g, '').substring(0, 8)}`;
101
108
  const defaultImage = `blaxel/base-image:latest`;
102
109
  const defaultMemory = 4096;
@@ -180,6 +187,7 @@ export class SandboxInstance {
180
187
  const [{ data }, h2Session] = await Promise.all([
181
188
  createSandbox({
182
189
  body: sandbox,
190
+ query: createIfNotExist ? { createIfNotExist } : undefined,
183
191
  throwOnError: true,
184
192
  }),
185
193
  edgeDomain && !settings.disableH2 ? import("../common/h2pool.js").then(({ h2Pool }) => h2Pool.get(edgeDomain)).catch(() => null) : Promise.resolve(null),
@@ -266,27 +274,30 @@ export class SandboxInstance {
266
274
  return SandboxInstance.attachH2Session(instance);
267
275
  }
268
276
  static async createIfNotExists(sandbox) {
269
- try {
270
- return await this.create(sandbox);
271
- }
272
- catch (e) {
273
- if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
274
- const name = 'name' in sandbox ? sandbox.name : sandbox.metadata.name;
275
- if (!name) {
276
- throw new Error("Sandbox name is required");
277
- }
278
- // Get the existing sandbox to check its status
279
- const sandboxInstance = await this.get(name);
280
- // If the sandbox is TERMINATED, treat it as not existing
281
- if (sandboxInstance.status === "TERMINATED") {
282
- // Create a new sandbox - backend will handle cleanup of the terminated one
283
- return await this.create(sandbox);
277
+ const ATTEMPTS = 3;
278
+ for (let i = 0; i < ATTEMPTS; ++i) {
279
+ try {
280
+ return await this.create(sandbox, { createIfNotExist: true });
281
+ }
282
+ catch (e) {
283
+ if (typeof e === "object" && e !== null && "code" in e && (e.code === 409 || e.code === 'SANDBOX_ALREADY_EXISTS')) {
284
+ const name = 'name' in sandbox ? sandbox.name : sandbox.metadata.name;
285
+ if (!name) {
286
+ throw new Error("Sandbox name is required");
287
+ }
288
+ // Get the existing sandbox to check its status
289
+ const sandboxInstance = await this.get(name);
290
+ // Recreate instead of returning sandbox records that cannot be reused.
291
+ if (!NON_REUSABLE_SANDBOX_STATUSES.has(sandboxInstance.status ?? "")) {
292
+ return sandboxInstance;
293
+ }
294
+ // Retry creation. We want the same error handling on the retry as creates can race.
295
+ continue;
284
296
  }
285
- // Otherwise return the existing running sandbox
286
- return sandboxInstance;
297
+ throw e;
287
298
  }
288
- throw e;
289
299
  }
300
+ throw new Error(`Unable to create sandbox after ${ATTEMPTS} attempts.`);
290
301
  }
291
302
  /* eslint-disable */
292
303
  static async fromSession(session) {