@blaxel/core 0.2.87 → 0.2.88
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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/sandbox/sandbox.js +62 -2
- package/dist/cjs/types/sandbox/sandbox.d.ts +1 -0
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/sandbox/sandbox.js +62 -2
- package/dist/cjs-browser/types/sandbox/sandbox.d.ts +1 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm/sandbox/sandbox.js +62 -2
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/settings.js +2 -2
- package/dist/esm-browser/sandbox/sandbox.js +62 -2
- package/package.json +1 -1
|
@@ -28,8 +28,8 @@ function missingCredentialsMessage() {
|
|
|
28
28
|
return "No Blaxel credentials found. Set the BL_API_KEY and BL_WORKSPACE environment variables, or run `bl login`.";
|
|
29
29
|
}
|
|
30
30
|
// Build info - these placeholders are replaced at build time by build:replace-imports
|
|
31
|
-
const BUILD_VERSION = "0.2.
|
|
32
|
-
const BUILD_COMMIT = "
|
|
31
|
+
const BUILD_VERSION = "0.2.88";
|
|
32
|
+
const BUILD_COMMIT = "3d4dcbfdf0ac479adef35da9cd8523954af3e7f7";
|
|
33
33
|
const BUILD_SENTRY_DSN = "https://fd5e60e1c9820e1eef5ccebb84a07127@o4508714045276160.ingest.us.sentry.io/4510465864564736";
|
|
34
34
|
const BLAXEL_API_VERSION = "2026-04-16";
|
|
35
35
|
// Cache for config.yaml tracking value
|
|
@@ -54,6 +54,23 @@ const NON_REUSABLE_SANDBOX_STATUSES = new Set([
|
|
|
54
54
|
"DELETING",
|
|
55
55
|
"DEACTIVATING",
|
|
56
56
|
]);
|
|
57
|
+
// Statuses that resolve on their own (a delete or deactivation in flight). The control
|
|
58
|
+
// plane keeps answering 409 to creates while the record is in one of these, so retrying
|
|
59
|
+
// instantly burns the whole attempt budget inside the window. Terminal statuses
|
|
60
|
+
// (FAILED, TERMINATED) accept a create immediately and are not listed here.
|
|
61
|
+
const TRANSIENT_SANDBOX_STATUSES = new Set([
|
|
62
|
+
"TERMINATING",
|
|
63
|
+
"DELETING",
|
|
64
|
+
"DEACTIVATING",
|
|
65
|
+
]);
|
|
66
|
+
const TRANSIENT_STATUS_MAX_WAIT_MS = 30_000;
|
|
67
|
+
const TRANSIENT_STATUS_POLL_MS = 500;
|
|
68
|
+
const isSandboxNotFound = (e) => {
|
|
69
|
+
if (typeof e !== "object" || e === null)
|
|
70
|
+
return false;
|
|
71
|
+
const candidate = e;
|
|
72
|
+
return candidate.code === 404 || candidate.code === "404" || candidate.status === 404;
|
|
73
|
+
};
|
|
57
74
|
class SandboxInstance {
|
|
58
75
|
sandbox;
|
|
59
76
|
fs;
|
|
@@ -322,7 +339,9 @@ class SandboxInstance {
|
|
|
322
339
|
}
|
|
323
340
|
static async createIfNotExists(sandbox) {
|
|
324
341
|
const ATTEMPTS = 3;
|
|
342
|
+
let lastStatus = "unknown";
|
|
325
343
|
for (let i = 0; i < ATTEMPTS; ++i) {
|
|
344
|
+
const finalAttempt = i === ATTEMPTS - 1;
|
|
326
345
|
try {
|
|
327
346
|
return await this.create(sandbox, { createIfNotExist: true });
|
|
328
347
|
}
|
|
@@ -333,18 +352,59 @@ class SandboxInstance {
|
|
|
333
352
|
throw new Error("Sandbox name is required");
|
|
334
353
|
}
|
|
335
354
|
// Get the existing sandbox to check its status
|
|
336
|
-
|
|
355
|
+
let sandboxInstance;
|
|
356
|
+
try {
|
|
357
|
+
sandboxInstance = await this.get(name);
|
|
358
|
+
}
|
|
359
|
+
catch (getError) {
|
|
360
|
+
if (isSandboxNotFound(getError)) {
|
|
361
|
+
// The record vanished between the create conflict and this status check
|
|
362
|
+
// (its deletion just finished); give the control plane a beat and retry.
|
|
363
|
+
lastStatus = "vanished";
|
|
364
|
+
if (!finalAttempt) {
|
|
365
|
+
await new Promise((resolve) => setTimeout(resolve, TRANSIENT_STATUS_POLL_MS));
|
|
366
|
+
}
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
throw getError;
|
|
370
|
+
}
|
|
337
371
|
// Recreate instead of returning sandbox records that cannot be reused.
|
|
338
372
|
if (!NON_REUSABLE_SANDBOX_STATUSES.has(sandboxInstance.status ?? "")) {
|
|
339
373
|
return sandboxInstance;
|
|
340
374
|
}
|
|
375
|
+
// A delete or deactivation in flight rejects creates until it finishes;
|
|
376
|
+
// wait it out instead of burning the remaining attempts inside the window.
|
|
377
|
+
// No point waiting after the last attempt: nothing will use the result.
|
|
378
|
+
lastStatus = sandboxInstance.status ?? "unknown";
|
|
379
|
+
if (TRANSIENT_SANDBOX_STATUSES.has(lastStatus) && !finalAttempt) {
|
|
380
|
+
await this.waitWhileSandboxDying(name);
|
|
381
|
+
}
|
|
341
382
|
// Retry creation. We want the same error handling on the retry as creates can race.
|
|
342
383
|
continue;
|
|
343
384
|
}
|
|
344
385
|
throw e;
|
|
345
386
|
}
|
|
346
387
|
}
|
|
347
|
-
throw new Error(`Unable to create sandbox after ${ATTEMPTS} attempts.`);
|
|
388
|
+
throw new Error(`Unable to create sandbox after ${ATTEMPTS} attempts. Last conflicting status: ${lastStatus}.`);
|
|
389
|
+
}
|
|
390
|
+
// Poll the record until an in-flight delete/deactivation settles (or the record
|
|
391
|
+
// disappears), bounded by TRANSIENT_STATUS_MAX_WAIT_MS. Errors from get (e.g. 404
|
|
392
|
+
// once the record is gone) end the wait: the caller's create retry decides next.
|
|
393
|
+
static async waitWhileSandboxDying(name) {
|
|
394
|
+
const deadline = Date.now() + TRANSIENT_STATUS_MAX_WAIT_MS;
|
|
395
|
+
while (Date.now() < deadline) {
|
|
396
|
+
await new Promise((resolve) => setTimeout(resolve, TRANSIENT_STATUS_POLL_MS));
|
|
397
|
+
try {
|
|
398
|
+
const current = await this.get(name);
|
|
399
|
+
if (!TRANSIENT_SANDBOX_STATUSES.has(current.status ?? "")) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
logger_js_1.logger.debug(`Sandbox ${name} still ${current.status}; waiting for the record to settle before recreating`);
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
348
408
|
}
|
|
349
409
|
/* eslint-disable */
|
|
350
410
|
static async fromSession(session) {
|
|
@@ -59,5 +59,6 @@ export declare class SandboxInstance {
|
|
|
59
59
|
static updateLifecycle(sandboxName: string, lifecycle: SandboxLifecycle): Promise<SandboxInstance>;
|
|
60
60
|
static updateNetwork(sandboxName: string, network: SandboxUpdateNetwork): Promise<SandboxInstance>;
|
|
61
61
|
static createIfNotExists(sandbox: SandboxModel | SandboxCreateConfiguration): Promise<SandboxInstance>;
|
|
62
|
+
private static waitWhileSandboxDying;
|
|
62
63
|
static fromSession(session: SessionWithToken): Promise<SandboxInstance>;
|
|
63
64
|
}
|