@miosa/sdk 3.1.0 → 3.2.1
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/index.d.ts +22 -2
- package/dist/index.js +93 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7432,14 +7432,34 @@ declare class Sandbox {
|
|
|
7432
7432
|
* Returns `true` once the sandbox is ready, `false` on `event: timeout`
|
|
7433
7433
|
* or when the local timeout elapses before ready.
|
|
7434
7434
|
*
|
|
7435
|
+
* Throws {@link MiosaError} with code `SANDBOX_BOOT_FAILED` as soon as the
|
|
7436
|
+
* sandbox is observed in a terminal failure state (`error` or `destroyed`)
|
|
7437
|
+
* while waiting, instead of silently exhausting the timeout and returning
|
|
7438
|
+
* `false` — a permanent boot failure and "still booting, keep waiting" must
|
|
7439
|
+
* never look the same to the caller. The readiness SSE stream itself has no
|
|
7440
|
+
* dedicated failure event today (the server only ever emits
|
|
7441
|
+
* `ready`/`status`/`timeout`), so on `event: timeout` this makes one
|
|
7442
|
+
* authoritative {@link readiness} check before giving up, to catch a
|
|
7443
|
+
* failure that landed right at the boundary.
|
|
7444
|
+
*
|
|
7435
7445
|
* If the SSE endpoint returns 404 (server pre-dates the streaming
|
|
7436
|
-
* endpoint) this transparently falls back to polling
|
|
7437
|
-
*
|
|
7446
|
+
* endpoint) this transparently falls back to polling {@link readiness}
|
|
7447
|
+
* every 10 ms until ready, timeout, or terminal failure.
|
|
7438
7448
|
*/
|
|
7439
7449
|
waitUntilReady(options?: {
|
|
7440
7450
|
timeout?: number;
|
|
7441
7451
|
stream?: boolean;
|
|
7452
|
+
/**
|
|
7453
|
+
* Require the sandbox to be *command-ready* (exec will succeed) before
|
|
7454
|
+
* resolving `true`, not merely aggregate-`ready`. Defaults to `true` so a
|
|
7455
|
+
* resolved wait guarantees exec won't 409 SANDBOX_NOT_COMMAND_READY. Set
|
|
7456
|
+
* `false` for the legacy aggregate-ready semantics.
|
|
7457
|
+
*/
|
|
7458
|
+
requireCommandReady?: boolean;
|
|
7442
7459
|
}): Promise<boolean>;
|
|
7460
|
+
private bootFailedError;
|
|
7461
|
+
/** Best-effort final check so an SSE `event: timeout` does not mask a boot failure that just landed. */
|
|
7462
|
+
private throwIfTerminallyFailed;
|
|
7443
7463
|
/**
|
|
7444
7464
|
* Readiness answers from the server; `assertRunning` reads the local
|
|
7445
7465
|
* snapshot. Leaving that snapshot behind meant a caller could await
|
package/dist/index.js
CHANGED
|
@@ -168,7 +168,7 @@ var TokenRefreshFailedError = class extends MiosaError {
|
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
// src/version.ts
|
|
171
|
-
var SDK_VERSION = "3.1
|
|
171
|
+
var SDK_VERSION = "3.2.1";
|
|
172
172
|
var SDK_USER_AGENT = `@miosa/sdk/${SDK_VERSION}`;
|
|
173
173
|
|
|
174
174
|
// src/http.ts
|
|
@@ -373,7 +373,7 @@ var HttpClient = class {
|
|
|
373
373
|
async *streamFrames(path, options = {}) {
|
|
374
374
|
const method = options.method ?? "GET";
|
|
375
375
|
let headers = this.baseHeaders({
|
|
376
|
-
Accept: "text/event-stream",
|
|
376
|
+
Accept: "text/event-stream, application/json, */*",
|
|
377
377
|
...options.headers
|
|
378
378
|
});
|
|
379
379
|
let body5 = null;
|
|
@@ -8451,6 +8451,36 @@ function encodeContent(content) {
|
|
|
8451
8451
|
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
8452
8452
|
return btoa(bin);
|
|
8453
8453
|
}
|
|
8454
|
+
function generateIdempotencyKey() {
|
|
8455
|
+
const c = globalThis.crypto;
|
|
8456
|
+
if (c && typeof c.randomUUID === "function") return c.randomUUID();
|
|
8457
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
|
|
8458
|
+
const r = Math.random() * 16 | 0;
|
|
8459
|
+
const v = ch === "x" ? r : r & 3 | 8;
|
|
8460
|
+
return v.toString(16);
|
|
8461
|
+
});
|
|
8462
|
+
}
|
|
8463
|
+
var PLATFORM_READINESS_COMPONENTS = [
|
|
8464
|
+
"command_agent",
|
|
8465
|
+
"process_control",
|
|
8466
|
+
"resource_contract",
|
|
8467
|
+
"clock",
|
|
8468
|
+
"identity",
|
|
8469
|
+
"durability",
|
|
8470
|
+
"filesystem",
|
|
8471
|
+
"internal_probe"
|
|
8472
|
+
];
|
|
8473
|
+
function isCommandReady(payload) {
|
|
8474
|
+
if (payload["ready"] === true) return true;
|
|
8475
|
+
const readiness = payload["readiness"];
|
|
8476
|
+
const components = readiness?.["components"] ?? payload["components"] ?? payload["readiness_components"];
|
|
8477
|
+
if (!components || typeof components !== "object") return false;
|
|
8478
|
+
return PLATFORM_READINESS_COMPONENTS.every((name) => {
|
|
8479
|
+
const comp = components[name];
|
|
8480
|
+
const status = typeof comp === "string" ? comp : comp?.status;
|
|
8481
|
+
return status === "ready";
|
|
8482
|
+
});
|
|
8483
|
+
}
|
|
8454
8484
|
var SANDBOX_TEMPLATE = "miosa-sandbox";
|
|
8455
8485
|
var SANDBOX_TIER_BY_CPU = {
|
|
8456
8486
|
1: { size: "xs", memoryMb: 2048 },
|
|
@@ -8459,6 +8489,9 @@ var SANDBOX_TIER_BY_CPU = {
|
|
|
8459
8489
|
8: { size: "large", memoryMb: 16384 },
|
|
8460
8490
|
16: { size: "xl", memoryMb: 32768 }
|
|
8461
8491
|
};
|
|
8492
|
+
function isTerminalSandboxFailure(state) {
|
|
8493
|
+
return state === "error" || state === "destroyed";
|
|
8494
|
+
}
|
|
8462
8495
|
var AGENT_WORKSPACE_TIMEOUT_SEC = 86400;
|
|
8463
8496
|
var AGENT_WORKSPACE_IDLE_TIMEOUT_SEC = 1800;
|
|
8464
8497
|
var AGENT_WORKSPACE_SNAPSHOT_EXPIRATION_DAYS = 30;
|
|
@@ -9432,34 +9465,77 @@ var Sandbox = class _Sandbox {
|
|
|
9432
9465
|
* Returns `true` once the sandbox is ready, `false` on `event: timeout`
|
|
9433
9466
|
* or when the local timeout elapses before ready.
|
|
9434
9467
|
*
|
|
9468
|
+
* Throws {@link MiosaError} with code `SANDBOX_BOOT_FAILED` as soon as the
|
|
9469
|
+
* sandbox is observed in a terminal failure state (`error` or `destroyed`)
|
|
9470
|
+
* while waiting, instead of silently exhausting the timeout and returning
|
|
9471
|
+
* `false` — a permanent boot failure and "still booting, keep waiting" must
|
|
9472
|
+
* never look the same to the caller. The readiness SSE stream itself has no
|
|
9473
|
+
* dedicated failure event today (the server only ever emits
|
|
9474
|
+
* `ready`/`status`/`timeout`), so on `event: timeout` this makes one
|
|
9475
|
+
* authoritative {@link readiness} check before giving up, to catch a
|
|
9476
|
+
* failure that landed right at the boundary.
|
|
9477
|
+
*
|
|
9435
9478
|
* If the SSE endpoint returns 404 (server pre-dates the streaming
|
|
9436
|
-
* endpoint) this transparently falls back to polling
|
|
9437
|
-
*
|
|
9479
|
+
* endpoint) this transparently falls back to polling {@link readiness}
|
|
9480
|
+
* every 10 ms until ready, timeout, or terminal failure.
|
|
9438
9481
|
*/
|
|
9439
9482
|
async waitUntilReady(options = {}) {
|
|
9440
9483
|
const timeout = options.timeout ?? 30;
|
|
9441
9484
|
const stream = options.stream ?? true;
|
|
9485
|
+
const requireCommandReady = options.requireCommandReady ?? true;
|
|
9442
9486
|
if (stream) {
|
|
9443
9487
|
const sseResult = await this.tryReadinessStream(timeout);
|
|
9444
|
-
if (sseResult
|
|
9445
|
-
|
|
9446
|
-
return
|
|
9488
|
+
if (sseResult === true) {
|
|
9489
|
+
await this.adoptReadyState();
|
|
9490
|
+
return true;
|
|
9491
|
+
}
|
|
9492
|
+
if (sseResult === false) {
|
|
9493
|
+
await this.throwIfTerminallyFailed();
|
|
9494
|
+
return false;
|
|
9447
9495
|
}
|
|
9448
9496
|
}
|
|
9449
9497
|
const deadlineMs = Date.now() + timeout * 1e3;
|
|
9450
9498
|
while (Date.now() < deadlineMs) {
|
|
9451
9499
|
try {
|
|
9452
9500
|
const data = await this.readiness();
|
|
9453
|
-
|
|
9501
|
+
const ready = requireCommandReady ? isCommandReady(data) : data.ready === true || data.status === "ready";
|
|
9502
|
+
if (ready) {
|
|
9454
9503
|
await this.adoptReadyState();
|
|
9455
9504
|
return true;
|
|
9456
9505
|
}
|
|
9457
|
-
|
|
9506
|
+
if (isTerminalSandboxFailure(data.state)) {
|
|
9507
|
+
throw this.bootFailedError(data.state);
|
|
9508
|
+
}
|
|
9509
|
+
} catch (err) {
|
|
9510
|
+
if (err instanceof MiosaError && err.code === "SANDBOX_BOOT_FAILED") {
|
|
9511
|
+
throw err;
|
|
9512
|
+
}
|
|
9458
9513
|
}
|
|
9459
9514
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
9460
9515
|
}
|
|
9461
9516
|
return false;
|
|
9462
9517
|
}
|
|
9518
|
+
bootFailedError(state) {
|
|
9519
|
+
return new MiosaError(
|
|
9520
|
+
`Sandbox ${this.id} entered terminal state '${state}' while waiting for readiness`,
|
|
9521
|
+
502,
|
|
9522
|
+
"SANDBOX_BOOT_FAILED",
|
|
9523
|
+
{ sandbox_id: this.id, state }
|
|
9524
|
+
);
|
|
9525
|
+
}
|
|
9526
|
+
/** Best-effort final check so an SSE `event: timeout` does not mask a boot failure that just landed. */
|
|
9527
|
+
async throwIfTerminallyFailed() {
|
|
9528
|
+
try {
|
|
9529
|
+
const data = await this.readiness();
|
|
9530
|
+
if (isTerminalSandboxFailure(data?.state)) {
|
|
9531
|
+
throw this.bootFailedError(data.state);
|
|
9532
|
+
}
|
|
9533
|
+
} catch (err) {
|
|
9534
|
+
if (err instanceof MiosaError && err.code === "SANDBOX_BOOT_FAILED") {
|
|
9535
|
+
throw err;
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9463
9539
|
/**
|
|
9464
9540
|
* Readiness answers from the server; `assertRunning` reads the local
|
|
9465
9541
|
* snapshot. Leaving that snapshot behind meant a caller could await
|
|
@@ -9486,7 +9562,11 @@ var Sandbox = class _Sandbox {
|
|
|
9486
9562
|
try {
|
|
9487
9563
|
const headers = {
|
|
9488
9564
|
Authorization: `Bearer ${this.http.apiKey}`,
|
|
9489
|
-
|
|
9565
|
+
// Combined defensively: a bare `text/event-stream` Accept header 406s
|
|
9566
|
+
// on some `:accepts`-guarded routes (seen on the databases stream
|
|
9567
|
+
// route). Offering json/`*/*` as fallbacks is free when SSE is
|
|
9568
|
+
// actually returned.
|
|
9569
|
+
Accept: "text/event-stream, application/json, */*",
|
|
9490
9570
|
"User-Agent": SDK_USER_AGENT
|
|
9491
9571
|
};
|
|
9492
9572
|
const response = await fetch(
|
|
@@ -9595,14 +9675,12 @@ var Sandboxes = class {
|
|
|
9595
9675
|
});
|
|
9596
9676
|
}
|
|
9597
9677
|
async create(params = {}) {
|
|
9598
|
-
const idempotencyKey11 = params.idempotencyKey ?? params.idempotency_key;
|
|
9678
|
+
const idempotencyKey11 = params.idempotencyKey ?? params.idempotency_key ?? generateIdempotencyKey();
|
|
9599
9679
|
const requestOptions = {
|
|
9600
9680
|
method: "POST",
|
|
9601
|
-
body: createBody(params)
|
|
9681
|
+
body: createBody(params),
|
|
9682
|
+
headers: { "Idempotency-Key": idempotencyKey11 }
|
|
9602
9683
|
};
|
|
9603
|
-
if (idempotencyKey11) {
|
|
9604
|
-
requestOptions.headers = { "Idempotency-Key": idempotencyKey11 };
|
|
9605
|
-
}
|
|
9606
9684
|
const data = unwrap48(
|
|
9607
9685
|
await this.http.request(
|
|
9608
9686
|
"/sandboxes",
|