@aerol-ai/aerolvm-sdk 0.1.2 → 0.1.4
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/MicroVM.js +1 -1
- package/dist/internal/client.js +58 -8
- package/dist/types.d.ts +37 -0
- package/package.json +1 -1
package/dist/MicroVM.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { APIClient } from "./internal/client.js";
|
|
2
2
|
import { Sandbox } from "./Sandbox.js";
|
|
3
|
-
const defaultAPIURL = "http://127.0.0.1:
|
|
3
|
+
const defaultAPIURL = "http://127.0.0.1:21212";
|
|
4
4
|
const authRequiredErrorMessage = "PAT token is required. Set patToken or SB_PAT_TOKEN.";
|
|
5
5
|
export class MicroVM {
|
|
6
6
|
apiUrl;
|
package/dist/internal/client.js
CHANGED
|
@@ -442,6 +442,35 @@ async function decodeError(response) {
|
|
|
442
442
|
}
|
|
443
443
|
const STREAM_PREFIX_STDOUT = 0x01;
|
|
444
444
|
const STREAM_PREFIX_STDERR = 0x02;
|
|
445
|
+
// Pull whatever diagnostic detail the runtime gave us off a WebSocket "error"
|
|
446
|
+
// event. Node 22's native WebSocket fires an ErrorEvent with `.error` /
|
|
447
|
+
// `.message`; browsers' base WebSocket fires a bare Event. Returns "" when
|
|
448
|
+
// nothing useful is there so callers can fall back to a generic label.
|
|
449
|
+
function describeWSError(event) {
|
|
450
|
+
if (!event || typeof event !== "object")
|
|
451
|
+
return "";
|
|
452
|
+
const e = event;
|
|
453
|
+
if (e.error instanceof Error && e.error.message)
|
|
454
|
+
return e.error.message;
|
|
455
|
+
if (typeof e.error === "string" && e.error !== "")
|
|
456
|
+
return e.error;
|
|
457
|
+
if (typeof e.message === "string" && e.message !== "")
|
|
458
|
+
return e.message;
|
|
459
|
+
return "";
|
|
460
|
+
}
|
|
461
|
+
function describeWSClose(event) {
|
|
462
|
+
if (!event || typeof event !== "object")
|
|
463
|
+
return "";
|
|
464
|
+
const e = event;
|
|
465
|
+
const parts = [];
|
|
466
|
+
if (typeof e.code === "number")
|
|
467
|
+
parts.push(`code=${e.code}`);
|
|
468
|
+
if (typeof e.reason === "string" && e.reason !== "")
|
|
469
|
+
parts.push(`reason=${JSON.stringify(e.reason)}`);
|
|
470
|
+
if (typeof e.wasClean === "boolean")
|
|
471
|
+
parts.push(`wasClean=${e.wasClean}`);
|
|
472
|
+
return parts.join(" ");
|
|
473
|
+
}
|
|
445
474
|
function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
446
475
|
const wsURL = baseURL.replace(/^http/, "ws") + `/v1/sandboxes/${encodeURIComponent(sandboxID)}/toolbox/process/exec/stream`;
|
|
447
476
|
const WS = globalThis.WebSocket;
|
|
@@ -453,6 +482,10 @@ function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
|
453
482
|
// `sandbox.bearer, <token>`.
|
|
454
483
|
const ws = new WS(wsURL, ["sandbox.bearer", patToken]);
|
|
455
484
|
ws.binaryType = "arraybuffer";
|
|
485
|
+
// Captured by the "error" listener and consumed by "close" so we report the
|
|
486
|
+
// root cause (e.g. "Unexpected server response: 502") rather than the
|
|
487
|
+
// generic "stream closed before exit" that would otherwise win the race.
|
|
488
|
+
let lastErrorDetail = "";
|
|
456
489
|
let exitResolve;
|
|
457
490
|
let exitReject;
|
|
458
491
|
const done = new Promise((resolve, reject) => {
|
|
@@ -513,11 +546,19 @@ function openExecStream(baseURL, patToken, sandboxID, options) {
|
|
|
513
546
|
options.onStderr?.(payload);
|
|
514
547
|
}
|
|
515
548
|
});
|
|
516
|
-
ws.addEventListener("close", () => {
|
|
517
|
-
|
|
549
|
+
ws.addEventListener("close", (event) => {
|
|
550
|
+
const closeDetail = describeWSClose(event);
|
|
551
|
+
const parts = [lastErrorDetail, closeDetail].filter((s) => s !== "");
|
|
552
|
+
const suffix = parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
553
|
+
failWith(`stream closed before exit${suffix}`);
|
|
518
554
|
});
|
|
519
|
-
ws.addEventListener("error", () => {
|
|
520
|
-
|
|
555
|
+
ws.addEventListener("error", (event) => {
|
|
556
|
+
const detail = describeWSError(event);
|
|
557
|
+
if (detail !== "") {
|
|
558
|
+
lastErrorDetail = detail;
|
|
559
|
+
}
|
|
560
|
+
options.onError?.(detail !== "" ? detail : "websocket error");
|
|
561
|
+
failWith(detail !== "" ? `websocket error: ${detail}` : "websocket error");
|
|
521
562
|
});
|
|
522
563
|
const sendBinary = (data) => {
|
|
523
564
|
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
@@ -552,6 +593,7 @@ function openSessionAttach(baseURL, patToken, sandboxID, sessionID, options) {
|
|
|
552
593
|
exitReject = reject;
|
|
553
594
|
});
|
|
554
595
|
let settled = false;
|
|
596
|
+
let lastErrorDetail = "";
|
|
555
597
|
const finishWith = (info) => {
|
|
556
598
|
if (settled)
|
|
557
599
|
return;
|
|
@@ -602,13 +644,21 @@ function openSessionAttach(baseURL, patToken, sandboxID, sessionID, options) {
|
|
|
602
644
|
options.onStderr?.(payload);
|
|
603
645
|
}
|
|
604
646
|
});
|
|
605
|
-
ws.addEventListener("close", () => {
|
|
647
|
+
ws.addEventListener("close", (event) => {
|
|
606
648
|
if (!settled) {
|
|
607
|
-
|
|
649
|
+
const closeDetail = describeWSClose(event);
|
|
650
|
+
const parts = [lastErrorDetail, closeDetail].filter((s) => s !== "");
|
|
651
|
+
const suffix = parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
|
652
|
+
failWith(`session stream closed before exit${suffix}`);
|
|
608
653
|
}
|
|
609
654
|
});
|
|
610
|
-
ws.addEventListener("error", () => {
|
|
611
|
-
|
|
655
|
+
ws.addEventListener("error", (event) => {
|
|
656
|
+
const detail = describeWSError(event);
|
|
657
|
+
if (detail !== "") {
|
|
658
|
+
lastErrorDetail = detail;
|
|
659
|
+
}
|
|
660
|
+
options.onError?.(detail !== "" ? detail : "websocket error");
|
|
661
|
+
failWith(detail !== "" ? `websocket error: ${detail}` : "websocket error");
|
|
612
662
|
});
|
|
613
663
|
const sendBinary = (data) => {
|
|
614
664
|
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
package/dist/types.d.ts
CHANGED
|
@@ -32,6 +32,34 @@ export interface Lifecycle {
|
|
|
32
32
|
destroyAtAge?: number;
|
|
33
33
|
}
|
|
34
34
|
export type UpdateLifecycleOptions = Lifecycle;
|
|
35
|
+
export type GPUVendor = "nvidia" | "amd" | "apple";
|
|
36
|
+
export interface GPUOptions {
|
|
37
|
+
/**
|
|
38
|
+
* GPU hardware vendor. Required when gpus is set.
|
|
39
|
+
* - "nvidia": NVIDIA GPUs via nvidia-container-runtime. Requires
|
|
40
|
+
* nvidia-container-toolkit on the host.
|
|
41
|
+
* - "amd": AMD GPUs via ROCm (/dev/kfd + /dev/dri). Requires ROCm
|
|
42
|
+
* drivers on the host.
|
|
43
|
+
* - "apple": Apple Silicon GPU via Docker Desktop's experimental Metal
|
|
44
|
+
* support. Only functional on macOS with Docker Desktop.
|
|
45
|
+
*
|
|
46
|
+
* GPU access is not supported with runtime="gvisor" — the API returns
|
|
47
|
+
* an error if both gpus and runtime="gvisor" are set.
|
|
48
|
+
*/
|
|
49
|
+
vendor: GPUVendor;
|
|
50
|
+
/**
|
|
51
|
+
* Number of GPUs to allocate. Use -1 to request all GPUs on the host.
|
|
52
|
+
* Defaults to 1 when omitted. Ignored for AMD (all AMD GPUs are exposed
|
|
53
|
+
* via /dev/kfd and /dev/dri).
|
|
54
|
+
*/
|
|
55
|
+
count?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Pin the sandbox to specific GPU device indices or UUIDs.
|
|
58
|
+
* For NVIDIA: indices ("0", "1") or UUIDs ("GPU-abc123...").
|
|
59
|
+
* For AMD and Apple: ignored.
|
|
60
|
+
*/
|
|
61
|
+
deviceIDs?: string[];
|
|
62
|
+
}
|
|
35
63
|
export interface CreateOptions {
|
|
36
64
|
image: string;
|
|
37
65
|
/** Number of CPU cores to allocate. Fractional values are supported (e.g. 0.5 = half a core). */
|
|
@@ -52,8 +80,15 @@ export interface CreateOptions {
|
|
|
52
80
|
* privileged containers and ignores per-sandbox disk quotas. `"kata"` is
|
|
53
81
|
* reserved for future Kata Containers support and is rejected by the API
|
|
54
82
|
* today.
|
|
83
|
+
*
|
|
84
|
+
* GPU access is not supported with `"gvisor"`.
|
|
55
85
|
*/
|
|
56
86
|
runtime?: "docker" | "gvisor" | "kata";
|
|
87
|
+
/**
|
|
88
|
+
* Attach GPU resources to the sandbox. Omit for CPU-only workloads.
|
|
89
|
+
* Not compatible with runtime="gvisor".
|
|
90
|
+
*/
|
|
91
|
+
gpus?: GPUOptions;
|
|
57
92
|
}
|
|
58
93
|
export interface ResizeOptions {
|
|
59
94
|
/** Number of CPU cores. Fractional values supported (e.g. 0.5). */
|
|
@@ -122,6 +157,8 @@ export interface Sandbox {
|
|
|
122
157
|
* a pre-migration row that resolves to the host default at start time.
|
|
123
158
|
*/
|
|
124
159
|
runtime: "" | "docker" | "gvisor" | "kata";
|
|
160
|
+
/** GPU configuration this sandbox was created with. Absent means no GPU. */
|
|
161
|
+
gpus?: GPUOptions;
|
|
125
162
|
}
|
|
126
163
|
export interface ExecRequest {
|
|
127
164
|
command: string;
|