@aerol-ai/aerolvm-sdk 0.8.1 → 0.8.3
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.d.ts +133 -51
- package/dist/MicroVM.js +207 -99
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +89 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/internal/client.d.ts +253 -33
- package/dist/internal/client.js +1249 -91
- package/dist/internal/tar.d.ts +14 -0
- package/dist/internal/tar.js +79 -0
- package/dist/types.d.ts +448 -2
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** One archive member. Names use "/" and are relative to the folder. */
|
|
2
|
+
export interface TarEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
type: "file" | "dir" | "symlink";
|
|
5
|
+
mode: number;
|
|
6
|
+
/** Seconds since the epoch. */
|
|
7
|
+
mtime: number;
|
|
8
|
+
data?: Uint8Array;
|
|
9
|
+
linkname?: string;
|
|
10
|
+
}
|
|
11
|
+
/** A PAX record: "<length> <key>=<value>\n", the length counting itself. */
|
|
12
|
+
export declare function paxRecord(key: string, value: string): Uint8Array;
|
|
13
|
+
/** The entries as one uncompressed tar archive. */
|
|
14
|
+
export declare function tarArchive(entries: TarEntry[]): Uint8Array;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// A minimal tar writer for uploadDir (plans/agent-sandbox-feature-parity.md,
|
|
2
|
+
// F3). Node has no tar module, and the SDK takes no dependencies. It writes
|
|
3
|
+
// ustar headers, with a PAX record for a name or link target longer than
|
|
4
|
+
// ustar's 100 bytes, which Go's archive/tar (toolboxd) reads.
|
|
5
|
+
const BLOCK = 512;
|
|
6
|
+
const encoder = new TextEncoder();
|
|
7
|
+
function putString(block, offset, length, value) {
|
|
8
|
+
const bytes = typeof value === "string" ? encoder.encode(value) : value;
|
|
9
|
+
block.set(bytes.subarray(0, length), offset);
|
|
10
|
+
}
|
|
11
|
+
function putOctal(block, offset, length, value) {
|
|
12
|
+
putString(block, offset, length - 1, Math.floor(value).toString(8).padStart(length - 1, "0"));
|
|
13
|
+
}
|
|
14
|
+
function header(name, typeflag, mode, size, mtime, linkname) {
|
|
15
|
+
const block = new Uint8Array(BLOCK);
|
|
16
|
+
putString(block, 0, 100, name);
|
|
17
|
+
putOctal(block, 100, 8, mode & 0o7777);
|
|
18
|
+
putOctal(block, 108, 8, 0);
|
|
19
|
+
putOctal(block, 116, 8, 0);
|
|
20
|
+
putOctal(block, 124, 12, size);
|
|
21
|
+
putOctal(block, 136, 12, mtime);
|
|
22
|
+
block.fill(0x20, 148, 156);
|
|
23
|
+
putString(block, 156, 1, typeflag);
|
|
24
|
+
putString(block, 157, 100, linkname);
|
|
25
|
+
putString(block, 257, 6, "ustar\0");
|
|
26
|
+
putString(block, 263, 2, "00");
|
|
27
|
+
let sum = 0;
|
|
28
|
+
for (const byte of block)
|
|
29
|
+
sum += byte;
|
|
30
|
+
putString(block, 148, 8, `${sum.toString(8).padStart(6, "0")}\0 `);
|
|
31
|
+
return block;
|
|
32
|
+
}
|
|
33
|
+
/** A PAX record: "<length> <key>=<value>\n", the length counting itself. */
|
|
34
|
+
export function paxRecord(key, value) {
|
|
35
|
+
const body = encoder.encode(` ${key}=${value}\n`);
|
|
36
|
+
let length = body.length + String(body.length).length;
|
|
37
|
+
if (String(length).length !== String(body.length).length) {
|
|
38
|
+
// Adding the digits made the number one digit longer (98 -> 100).
|
|
39
|
+
length = body.length + String(length).length;
|
|
40
|
+
}
|
|
41
|
+
return encoder.encode(`${length} ${key}=${value}\n`);
|
|
42
|
+
}
|
|
43
|
+
function padded(data) {
|
|
44
|
+
const rest = data.length % BLOCK;
|
|
45
|
+
return rest === 0 ? [data] : [data, new Uint8Array(BLOCK - rest)];
|
|
46
|
+
}
|
|
47
|
+
/** The entries as one uncompressed tar archive. */
|
|
48
|
+
export function tarArchive(entries) {
|
|
49
|
+
const chunks = [];
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const name = encoder.encode(entry.type === "dir" && !entry.name.endsWith("/") ? `${entry.name}/` : entry.name);
|
|
52
|
+
const linkname = encoder.encode(entry.linkname ?? "");
|
|
53
|
+
const records = [];
|
|
54
|
+
if (name.length > 100)
|
|
55
|
+
records.push(paxRecord("path", new TextDecoder().decode(name)));
|
|
56
|
+
if (linkname.length > 100)
|
|
57
|
+
records.push(paxRecord("linkpath", entry.linkname ?? ""));
|
|
58
|
+
if (records.length > 0) {
|
|
59
|
+
const pax = concat(records);
|
|
60
|
+
chunks.push(header(encoder.encode("PaxHeader"), "x", 0o644, pax.length, entry.mtime, new Uint8Array()), ...padded(pax));
|
|
61
|
+
}
|
|
62
|
+
const data = entry.type === "file" ? entry.data ?? new Uint8Array() : new Uint8Array();
|
|
63
|
+
const typeflag = entry.type === "file" ? "0" : entry.type === "dir" ? "5" : "2";
|
|
64
|
+
chunks.push(header(name, typeflag, entry.mode, data.length, entry.mtime, linkname));
|
|
65
|
+
if (data.length > 0)
|
|
66
|
+
chunks.push(...padded(data));
|
|
67
|
+
}
|
|
68
|
+
chunks.push(new Uint8Array(BLOCK * 2));
|
|
69
|
+
return concat(chunks);
|
|
70
|
+
}
|
|
71
|
+
function concat(chunks) {
|
|
72
|
+
const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
|
|
73
|
+
let offset = 0;
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
out.set(chunk, offset);
|
|
76
|
+
offset += chunk.length;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export type SandboxStatus = "creating" | "started" | "stopped" | "destroyed" | "error" | "passivated" | "awaiting_runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Why a sandbox is in its current status. A stopped sandbox says what
|
|
4
|
+
* stopped it; a started one may say `"program_exited"` (its main program
|
|
5
|
+
* ended and `lifecycle.destroyAfterExitFor` is counting), `"failover"`
|
|
6
|
+
* (recreated on another node) or `"restarted"` (by `restartOnFailure`).
|
|
7
|
+
*/
|
|
8
|
+
export type StatusReason = "user_stop" | "idle" | "max_age" | "out_of_memory" | "disk_limit" | "crashed" | "reconcile" | "program_exited" | "failover" | "restarted";
|
|
2
9
|
export type Durability = "ephemeral" | "passivatable" | "durable";
|
|
3
10
|
export interface RegistryAuth {
|
|
4
11
|
server: string;
|
|
@@ -88,6 +95,13 @@ export interface PlatformVolumeMount {
|
|
|
88
95
|
path: string;
|
|
89
96
|
/** Mount read-only. Defaults to read-write. */
|
|
90
97
|
readOnly?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Delete the volume, its files and its record, once the last sandbox using
|
|
100
|
+
* it is deleted. Set when the volume is first made; a later sandbox that
|
|
101
|
+
* names the same volume must pass the same value, or the create fails with
|
|
102
|
+
* 409. Defaults to false: the volume outlives its sandboxes.
|
|
103
|
+
*/
|
|
104
|
+
deleteWithSandbox?: boolean;
|
|
91
105
|
}
|
|
92
106
|
export interface Lifecycle {
|
|
93
107
|
/** Duration in integer nanoseconds. */
|
|
@@ -105,6 +119,27 @@ export interface Lifecycle {
|
|
|
105
119
|
* without an idle window. TCP and TLS exposures stay always-on.
|
|
106
120
|
*/
|
|
107
121
|
serverless?: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Start the sandbox again when it crashes or runs out of memory, waiting
|
|
124
|
+
* 5 s, then 10 s, 20 s and so on up to 5 minutes between tries. The wait
|
|
125
|
+
* goes back to 5 s once it has run for 10 minutes. Off by default.
|
|
126
|
+
* Docker, gVisor and containerd sandboxes only: Firecracker, WebAssembly
|
|
127
|
+
* and V8-isolate sandboxes refuse it with a 400.
|
|
128
|
+
*/
|
|
129
|
+
restartOnFailure?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Delete the sandbox this long (integer nanoseconds) after its main
|
|
132
|
+
* program ends. The server checks about once a minute. Not for
|
|
133
|
+
* WebAssembly or V8-isolate sandboxes, which have no main program (400).
|
|
134
|
+
*/
|
|
135
|
+
destroyAfterExitFor?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Delete the sandbox at this clock time: a `Date`, or an RFC 3339 string
|
|
138
|
+
* such as `"2026-11-06T18:00:00Z"`. The server turns it into
|
|
139
|
+
* `destroyAtAge`, so the sandbox you get back shows `destroyAtAge`, not
|
|
140
|
+
* this. A time in the past is refused with a 400, as is setting both.
|
|
141
|
+
*/
|
|
142
|
+
destroyAt?: Date | string;
|
|
108
143
|
}
|
|
109
144
|
export type UpdateLifecycleOptions = Lifecycle;
|
|
110
145
|
export type FailoverPolicy = "none" | "recreate";
|
|
@@ -115,6 +150,17 @@ export interface Failover {
|
|
|
115
150
|
*/
|
|
116
151
|
policy: FailoverPolicy;
|
|
117
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Where a sandbox may run in a cluster. Every field you set must match the
|
|
155
|
+
* node's labels exactly. If no node matches, create fails with 409
|
|
156
|
+
* `placement_unsatisfiable`; it never falls back to another node.
|
|
157
|
+
*/
|
|
158
|
+
export interface PlacementRule {
|
|
159
|
+
/** Run only on nodes in this zone (the node's `SB_NODE_ZONE`). */
|
|
160
|
+
zone?: string;
|
|
161
|
+
/** Run only on nodes that have all of these labels (`SB_NODE_LABELS`). */
|
|
162
|
+
labels?: Record<string, string>;
|
|
163
|
+
}
|
|
118
164
|
export type GPUVendor = "nvidia" | "amd" | "apple";
|
|
119
165
|
export interface GPUOptions {
|
|
120
166
|
/**
|
|
@@ -158,6 +204,12 @@ export interface CreateOptions {
|
|
|
158
204
|
* the whole call safe too, such as after your process restarts.
|
|
159
205
|
*/
|
|
160
206
|
idempotencyKey?: string;
|
|
207
|
+
/**
|
|
208
|
+
* Client-side only, never sent. When true, the returned sandbox deletes
|
|
209
|
+
* itself when an `await using` block around it ends. Off by default, so
|
|
210
|
+
* leaving the block keeps the sandbox.
|
|
211
|
+
*/
|
|
212
|
+
destroyOnExit?: boolean;
|
|
161
213
|
/**
|
|
162
214
|
* Optional name, unique among the caller's sandboxes (another account may
|
|
163
215
|
* use the same name). A repeat create with a name the caller already holds
|
|
@@ -255,6 +307,11 @@ export interface CreateOptions {
|
|
|
255
307
|
platformVolumes?: PlatformVolumeMount[];
|
|
256
308
|
lifecycle?: Lifecycle;
|
|
257
309
|
failover?: Failover;
|
|
310
|
+
/**
|
|
311
|
+
* Limit which nodes may run the sandbox, by zone and node labels. Kept on
|
|
312
|
+
* failover. No matching node gives a 409 `placement_unsatisfiable` error.
|
|
313
|
+
*/
|
|
314
|
+
placement?: PlacementRule;
|
|
258
315
|
/**
|
|
259
316
|
* Container runtime to use for this sandbox. Omit to inherit the host
|
|
260
317
|
* default (`SB_CONTAINER_RUNTIME`). Use `"gvisor"` for runsc-backed
|
|
@@ -300,6 +357,65 @@ export interface CreateOptions {
|
|
|
300
357
|
* rootfs instead of building one from `image` on every create.
|
|
301
358
|
*/
|
|
302
359
|
templateId?: string;
|
|
360
|
+
/**
|
|
361
|
+
* A short-lived cloud identity token for the sandbox (AWS, GCP, Azure).
|
|
362
|
+
* The role tag must be on your team's allow list (`putIdentityRole`).
|
|
363
|
+
*/
|
|
364
|
+
identity?: IdentityOptions;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A cloud identity for a sandbox. The server signs a one-hour token whose
|
|
368
|
+
* subject is your team and `roleTag`, writes it to
|
|
369
|
+
* `/run/aerolvm/identity/token` inside the sandbox and refreshes it.
|
|
370
|
+
*/
|
|
371
|
+
export interface IdentityOptions {
|
|
372
|
+
roleTag: string;
|
|
373
|
+
/** The token's `aud`; default `"sts.amazonaws.com"`. */
|
|
374
|
+
audience?: string;
|
|
375
|
+
/** Also sets `AWS_ROLE_ARN` and `AWS_WEB_IDENTITY_TOKEN_FILE` for the AWS SDKs. */
|
|
376
|
+
awsRoleArn?: string;
|
|
377
|
+
/** Set both to give the Azure SDKs `AZURE_CLIENT_ID`, `AZURE_TENANT_ID` and `AZURE_FEDERATED_TOKEN_FILE`. */
|
|
378
|
+
azureClientId?: string;
|
|
379
|
+
azureTenantId?: string;
|
|
380
|
+
}
|
|
381
|
+
/** A sandbox's cloud identity as `get` shows it. Never the token. */
|
|
382
|
+
export interface SandboxIdentity {
|
|
383
|
+
roleTag: string;
|
|
384
|
+
audience: string;
|
|
385
|
+
/** The token's `sub`: what your cloud's trust policy matches. */
|
|
386
|
+
subject: string;
|
|
387
|
+
awsRoleArn?: string;
|
|
388
|
+
azureClientId?: string;
|
|
389
|
+
azureTenantId?: string;
|
|
390
|
+
tokenFile: string;
|
|
391
|
+
/** When the token now in the sandbox expires; absent until the first one. */
|
|
392
|
+
tokenExpiresAt?: string;
|
|
393
|
+
lastError?: string;
|
|
394
|
+
}
|
|
395
|
+
/** One role tag on a team's identity allow list. */
|
|
396
|
+
export interface IdentityRole {
|
|
397
|
+
tag: string;
|
|
398
|
+
/** The least caller role that may create a sandbox with this tag: `"runner"` or `"admin"`. */
|
|
399
|
+
minRole: string;
|
|
400
|
+
team?: string;
|
|
401
|
+
createdAt: string;
|
|
402
|
+
updatedAt: string;
|
|
403
|
+
}
|
|
404
|
+
/** The body of `putIdentityRole`. */
|
|
405
|
+
export interface IdentityRoleOptions {
|
|
406
|
+
minRole: "runner" | "admin";
|
|
407
|
+
/** The team whose list to change; omit for sandboxes the operator token creates. */
|
|
408
|
+
team?: string;
|
|
409
|
+
}
|
|
410
|
+
export interface ListIdentityRolesOptions {
|
|
411
|
+
team?: string;
|
|
412
|
+
cursor?: string;
|
|
413
|
+
limit?: number;
|
|
414
|
+
}
|
|
415
|
+
export interface IdentityRoleList {
|
|
416
|
+
roles: IdentityRole[];
|
|
417
|
+
/** Pass back as `cursor` for the next page; absent on the last one. */
|
|
418
|
+
nextCursor?: string;
|
|
303
419
|
}
|
|
304
420
|
export interface ResizeOptions {
|
|
305
421
|
/** Number of CPU cores. Fractional values supported (e.g. 0.5). */
|
|
@@ -307,6 +423,30 @@ export interface ResizeOptions {
|
|
|
307
423
|
memoryMB?: number;
|
|
308
424
|
diskGB?: number;
|
|
309
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* The last, optional argument of every `MicroVM` and `Sandbox` call that
|
|
428
|
+
* talks to the server.
|
|
429
|
+
*/
|
|
430
|
+
export interface CallOptions {
|
|
431
|
+
/**
|
|
432
|
+
* Cancels this call: aborting it ends the request in flight and any retry
|
|
433
|
+
* wait, and the call rejects with the signal's reason. Pass
|
|
434
|
+
* `AbortSignal.timeout(ms)` to give one call a time limit. It works with
|
|
435
|
+
* the client-wide `signal`; either one aborting ends the call. Calls that
|
|
436
|
+
* open a stream (`execStream`, `attachSession`, `watchFiles`) take no
|
|
437
|
+
* `CallOptions`, as the client-wide signal does not cover streams either.
|
|
438
|
+
*/
|
|
439
|
+
signal?: AbortSignal;
|
|
440
|
+
}
|
|
441
|
+
/** Options for {@link MicroVM.destroyAll}. */
|
|
442
|
+
export interface DestroyAllOptions {
|
|
443
|
+
/**
|
|
444
|
+
* Delete only sandboxes whose `tags` hold every key/value pair given here.
|
|
445
|
+
* Required and must not be empty: with no filter the call would delete
|
|
446
|
+
* every sandbox you own.
|
|
447
|
+
*/
|
|
448
|
+
tags: Record<string, string>;
|
|
449
|
+
}
|
|
310
450
|
export interface ListOptions {
|
|
311
451
|
/**
|
|
312
452
|
* Filter the result to only sandboxes whose `tags` map contains every
|
|
@@ -367,6 +507,8 @@ export interface ExposedPort {
|
|
|
367
507
|
port: number;
|
|
368
508
|
publicURL: string;
|
|
369
509
|
createdAt: string;
|
|
510
|
+
/** `"token"` when the URL needs a sandbox token; absent for a public port. */
|
|
511
|
+
visibility?: ExposeVisibility;
|
|
370
512
|
}
|
|
371
513
|
/**
|
|
372
514
|
* Lifecycle states for a per-sandbox custom hostname.
|
|
@@ -459,9 +601,22 @@ export interface CustomDomainDNSRecords {
|
|
|
459
601
|
* - "tls": caddy-l4 TLS-SNI route on the shared :443 listener.
|
|
460
602
|
*/
|
|
461
603
|
export type ExposeProtocol = "http" | "tcp" | "tls";
|
|
604
|
+
/**
|
|
605
|
+
* Who can open a port's URL:
|
|
606
|
+
* - "public": anyone with the URL.
|
|
607
|
+
* - "token": only a visitor with a sandbox token for this port (see
|
|
608
|
+
* `sandbox.createToken({ port })`). HTTP ports on a server
|
|
609
|
+
* with a domain only.
|
|
610
|
+
*/
|
|
611
|
+
export type ExposeVisibility = "public" | "token";
|
|
462
612
|
export interface ExposePortOptions {
|
|
463
613
|
/** Defaults to "http" when omitted. */
|
|
464
614
|
protocol?: ExposeProtocol;
|
|
615
|
+
/**
|
|
616
|
+
* Left out, a new port is public and an exposed port keeps what it has.
|
|
617
|
+
* Pass it to change it.
|
|
618
|
+
*/
|
|
619
|
+
visibility?: ExposeVisibility;
|
|
465
620
|
}
|
|
466
621
|
export interface SandboxSnapshot {
|
|
467
622
|
name: string;
|
|
@@ -489,6 +644,52 @@ export interface SnapshotList {
|
|
|
489
644
|
/** True when more snapshots remain after this page. */
|
|
490
645
|
hasMore: boolean;
|
|
491
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Options for `sandbox.createToken`: a signed token for this one sandbox that
|
|
649
|
+
* you can hand to a browser or an end user. Every field but `expiresIn`
|
|
650
|
+
* narrows it; leave one out and that part is not limited.
|
|
651
|
+
*/
|
|
652
|
+
export interface CreateSandboxTokenOptions {
|
|
653
|
+
/** Required lifetime: `"15m"`, `"2h"` or `"1d"`. The server caps it (24 hours by default). */
|
|
654
|
+
expiresIn: string;
|
|
655
|
+
/**
|
|
656
|
+
* A port token opens that port's public URL (a port exposed with
|
|
657
|
+
* `visibility: "token"`). Without a port the token is for the sandbox's
|
|
658
|
+
* toolbox: commands, files and terminal sessions.
|
|
659
|
+
*/
|
|
660
|
+
port?: number;
|
|
661
|
+
/** Only paths under this one, for example `"/files"` (toolbox) or `"/app"` (public URL). */
|
|
662
|
+
pathPrefix?: string;
|
|
663
|
+
/** Only these HTTP methods, for example `["GET"]`. */
|
|
664
|
+
methods?: string[];
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* A sandbox token from `sandbox.createToken`. The server keeps no copy, so
|
|
668
|
+
* `token` is shown only here. Every call makes a new token. It stops working
|
|
669
|
+
* when it expires, when its sandbox is deleted, or when
|
|
670
|
+
* `sandbox.revokeTokens()` is called.
|
|
671
|
+
*/
|
|
672
|
+
export interface SandboxToken {
|
|
673
|
+
/** The token (`avmst_…`). Send it as the bearer credential. */
|
|
674
|
+
token: string;
|
|
675
|
+
/** Public id (`stk_…`): what audit records name. Safe to log. */
|
|
676
|
+
tokenID: string;
|
|
677
|
+
sandboxID: string;
|
|
678
|
+
port?: number;
|
|
679
|
+
pathPrefix?: string;
|
|
680
|
+
methods?: string[];
|
|
681
|
+
/** RFC 3339 time the token stops working. */
|
|
682
|
+
expiresAt: string;
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* The result of `sandbox.revokeTokens()`. Every token issued for the sandbox
|
|
686
|
+
* before `tokensNotBefore` no longer works; a token made after the call works.
|
|
687
|
+
*/
|
|
688
|
+
export interface RevokeSandboxTokensResult {
|
|
689
|
+
sandboxID: string;
|
|
690
|
+
/** RFC 3339 time. Tokens issued before it are refused. */
|
|
691
|
+
tokensNotBefore: string;
|
|
692
|
+
}
|
|
492
693
|
/**
|
|
493
694
|
* Discriminated result from `exposePort`. Branch on `protocol` to access the
|
|
494
695
|
* fields that are meaningful for the chosen wire protocol — only the raw-TCP
|
|
@@ -498,6 +699,7 @@ export interface SnapshotList {
|
|
|
498
699
|
export type ExposeResult = {
|
|
499
700
|
protocol: "http";
|
|
500
701
|
url: string;
|
|
702
|
+
visibility?: "token";
|
|
501
703
|
} | {
|
|
502
704
|
protocol: "tcp";
|
|
503
705
|
url: string;
|
|
@@ -550,6 +752,10 @@ export interface Sandbox {
|
|
|
550
752
|
updatedAt: string;
|
|
551
753
|
lastActiveAt: string;
|
|
552
754
|
lastError?: string;
|
|
755
|
+
/** Why the sandbox is in its status; absent when a call simply created or started it. */
|
|
756
|
+
statusReason?: StatusReason;
|
|
757
|
+
/** Exit code behind `out_of_memory`, `crashed`, `program_exited` and `restarted`. */
|
|
758
|
+
exitCode?: number;
|
|
553
759
|
containerCommand?: string[];
|
|
554
760
|
lifecycle: Lifecycle;
|
|
555
761
|
failover?: Failover;
|
|
@@ -568,6 +774,8 @@ export interface Sandbox {
|
|
|
568
774
|
tenantId?: string;
|
|
569
775
|
/** GPU configuration this sandbox was created with. Absent means no GPU. */
|
|
570
776
|
gpus?: GPUOptions;
|
|
777
|
+
/** The sandbox's cloud identity, when it was created with one. */
|
|
778
|
+
identity?: SandboxIdentity;
|
|
571
779
|
}
|
|
572
780
|
/**
|
|
573
781
|
* Per-sandbox network byte counters and the configured caps that drive the
|
|
@@ -623,6 +831,12 @@ export interface AuditOptions {
|
|
|
623
831
|
limit?: number;
|
|
624
832
|
cursor?: string;
|
|
625
833
|
incarnationID?: string;
|
|
834
|
+
/**
|
|
835
|
+
* Only records by this caller: an id such as `"system:lifecycle"`, or
|
|
836
|
+
* `"system:*"` for everything the server did on its own (automatic
|
|
837
|
+
* stops, deletes, restarts and recreates).
|
|
838
|
+
*/
|
|
839
|
+
caller?: string;
|
|
626
840
|
}
|
|
627
841
|
/**
|
|
628
842
|
* Would a sandbox created with these egress fields reach `destination`?
|
|
@@ -756,6 +970,32 @@ export interface EgressProfile {
|
|
|
756
970
|
createdAt: string;
|
|
757
971
|
updatedAt: string;
|
|
758
972
|
}
|
|
973
|
+
/**
|
|
974
|
+
* A team quota's limits, the body of `putTeamQuota`. Omit a limit (or set
|
|
975
|
+
* 0) for no limit on it. At least one must be set.
|
|
976
|
+
*/
|
|
977
|
+
export interface TeamQuotaOptions {
|
|
978
|
+
/** Most sandboxes the team may hold, running or stopped. */
|
|
979
|
+
maxSandboxes?: number;
|
|
980
|
+
/** Most CPU the team's sandboxes may ask for in total. */
|
|
981
|
+
maxCPU?: number;
|
|
982
|
+
/** Most memory, in MB, the team's sandboxes may ask for in total. */
|
|
983
|
+
maxMemoryMB?: number;
|
|
984
|
+
}
|
|
985
|
+
/** What a team holds now: every sandbox not deleted, at the size it asked for. */
|
|
986
|
+
export interface TeamQuotaUsage {
|
|
987
|
+
sandboxes: number;
|
|
988
|
+
cpu: number;
|
|
989
|
+
memoryMB: number;
|
|
990
|
+
}
|
|
991
|
+
/** A team's quota, and its usage when read back. */
|
|
992
|
+
export interface TeamQuota extends TeamQuotaOptions {
|
|
993
|
+
team: string;
|
|
994
|
+
generation: number;
|
|
995
|
+
createdAt: string;
|
|
996
|
+
updatedAt: string;
|
|
997
|
+
usage?: TeamQuotaUsage;
|
|
998
|
+
}
|
|
759
999
|
/** The body of `putEgressProfile`: a full replace. */
|
|
760
1000
|
export interface EgressProfileOptions {
|
|
761
1001
|
allowOut: string[];
|
|
@@ -770,6 +1010,66 @@ export interface EgressProfileList {
|
|
|
770
1010
|
/** Pass back as `cursor` for the next page; absent on the last one. */
|
|
771
1011
|
nextCursor?: string;
|
|
772
1012
|
}
|
|
1013
|
+
/**
|
|
1014
|
+
* A warm-pool entry: how many ready sandboxes every server with a warm pool
|
|
1015
|
+
* keeps for one image. The server's `SB_DOCKER_POOL_*` / `SB_CONTAINERD_POOL_*`
|
|
1016
|
+
* settings stay the default for every other image. Admin only.
|
|
1017
|
+
*/
|
|
1018
|
+
export interface WarmPool {
|
|
1019
|
+
/** The image, exactly as creates name it (`python:3.12`, `ghcr.io/acme/agent:1`). */
|
|
1020
|
+
image: string;
|
|
1021
|
+
/** Warm slots each server keeps for this image. 0 turns warming off for it. */
|
|
1022
|
+
depth: number;
|
|
1023
|
+
/** With `maxDepth`, each server sets its own target from recent demand, never below this. */
|
|
1024
|
+
minDepth?: number;
|
|
1025
|
+
/** Set to follow demand: each server's target moves with its recent creates, up to this. */
|
|
1026
|
+
maxDepth?: number;
|
|
1027
|
+
/** What happens to warm sandboxes when the image changes. */
|
|
1028
|
+
onImageChange: WarmPoolOnImageChange;
|
|
1029
|
+
/** Goes up by one on every change; the same `putWarmPool` twice leaves it alone. */
|
|
1030
|
+
generation: number;
|
|
1031
|
+
createdAt: string;
|
|
1032
|
+
updatedAt: string;
|
|
1033
|
+
/** How full the pools are. Set by `getWarmPool` and `listWarmPools`. */
|
|
1034
|
+
status?: WarmPoolStatus;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* The body of `putWarmPool`: a full replace. Set `depth` for a fixed size, or
|
|
1038
|
+
* `minDepth` and `maxDepth` to follow demand, not both.
|
|
1039
|
+
*/
|
|
1040
|
+
export interface WarmPoolOptions {
|
|
1041
|
+
/** Warm slots each server keeps for this image (0 to 128). */
|
|
1042
|
+
depth?: number;
|
|
1043
|
+
/** The fewest warm slots a server keeps when demand is low (0 lets it reach zero). */
|
|
1044
|
+
minDepth?: number;
|
|
1045
|
+
/** The most warm slots a server keeps when demand is high (1 to 128). */
|
|
1046
|
+
maxDepth?: number;
|
|
1047
|
+
/** What happens to warm sandboxes when the image changes. Default `"replace_now"`. */
|
|
1048
|
+
onImageChange?: WarmPoolOnImageChange;
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* What a warm pool does when the image its name points to changes on a
|
|
1052
|
+
* server. `"replace_now"` swaps every waiting sandbox for one on the new
|
|
1053
|
+
* image at once; `"replace_as_used"` keeps handing out the old ones and
|
|
1054
|
+
* replaces each as it is used.
|
|
1055
|
+
*/
|
|
1056
|
+
export type WarmPoolOnImageChange = "replace_now" | "replace_as_used";
|
|
1057
|
+
/** Ready and target slots in total, and on each server that runs a warm pool. */
|
|
1058
|
+
export interface WarmPoolStatus {
|
|
1059
|
+
ready: number;
|
|
1060
|
+
target: number;
|
|
1061
|
+
/** Ready sandboxes still on an older image (a `"replace_as_used"` change in progress). */
|
|
1062
|
+
outdated: number;
|
|
1063
|
+
servers: WarmPoolServerStatus[];
|
|
1064
|
+
}
|
|
1065
|
+
export interface WarmPoolServerStatus {
|
|
1066
|
+
nodeId: string;
|
|
1067
|
+
ready: number;
|
|
1068
|
+
target: number;
|
|
1069
|
+
outdated: number;
|
|
1070
|
+
/** The server's last report is too old to trust; the numbers are its last ones. */
|
|
1071
|
+
stale?: boolean;
|
|
1072
|
+
}
|
|
773
1073
|
/** Who the caller is, as `microvm.whoami()` reports it. */
|
|
774
1074
|
export interface WhoAmIPrincipal {
|
|
775
1075
|
/** `"operator"`, `"token"`, `"oidc"`, `"controlplane"`, ... */
|
|
@@ -857,19 +1157,130 @@ export interface SetNetworkLimitsOptions {
|
|
|
857
1157
|
networkBytesOutLimit?: number;
|
|
858
1158
|
}
|
|
859
1159
|
export interface ExecRequest {
|
|
860
|
-
command
|
|
1160
|
+
/** A shell command. Set `command` or `argv`, not both. */
|
|
1161
|
+
command?: string;
|
|
1162
|
+
/**
|
|
1163
|
+
* Run a program with these arguments instead of a shell command: spaces,
|
|
1164
|
+
* quotes, `$` and `;` reach it as written. A sandbox whose agent predates
|
|
1165
|
+
* argv throws MicroVMUnsupportedFeatureError before anything runs.
|
|
1166
|
+
*/
|
|
1167
|
+
argv?: string[];
|
|
861
1168
|
workDir?: string;
|
|
862
1169
|
env?: Record<string, string>;
|
|
863
1170
|
timeoutSeconds?: number;
|
|
1171
|
+
/**
|
|
1172
|
+
* Keep only the first N bytes of stdout. The rest is dropped and the command
|
|
1173
|
+
* is not slowed down. A sandbox whose agent predates the caps throws
|
|
1174
|
+
* MicroVMUnsupportedFeatureError instead of returning everything.
|
|
1175
|
+
*/
|
|
1176
|
+
maxStdoutBytes?: number;
|
|
1177
|
+
/** Keep only the first N bytes of stderr. Same rules as maxStdoutBytes. */
|
|
1178
|
+
maxStderrBytes?: number;
|
|
864
1179
|
}
|
|
865
1180
|
export interface ExecResult {
|
|
866
1181
|
stdout: string;
|
|
867
1182
|
stderr: string;
|
|
868
1183
|
exitCode: number;
|
|
869
1184
|
durationMS: number;
|
|
1185
|
+
/** True when the command ran past its timeout and the sandbox killed its whole process group. Older sandbox agents never set it. */
|
|
1186
|
+
timedOut: boolean;
|
|
1187
|
+
/** True when stdout was longer than its cap (maxStdoutBytes, or the server's own limit) and was cut. */
|
|
1188
|
+
stdoutTruncated: boolean;
|
|
1189
|
+
/** True when stderr was longer than its cap and was cut. */
|
|
1190
|
+
stderrTruncated: boolean;
|
|
1191
|
+
/**
|
|
1192
|
+
* The signal that ended the command, such as "SIGKILL"; undefined when it
|
|
1193
|
+
* exited on its own. exitCode is -1 then; a shell would say 128 + the
|
|
1194
|
+
* signal's number (137 for SIGKILL).
|
|
1195
|
+
*/
|
|
1196
|
+
signal?: string;
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* How an upload writes its file. With none set, missing folders are made and
|
|
1200
|
+
* an existing file is replaced in place.
|
|
1201
|
+
*/
|
|
1202
|
+
export interface UploadFileOptions {
|
|
1203
|
+
/** Add to the end of the file instead of replacing it. */
|
|
1204
|
+
append?: boolean;
|
|
1205
|
+
/** Write only if the file does not exist yet (HTTP 412 otherwise). Of two uploads racing for one new path, exactly one wins. */
|
|
1206
|
+
createOnly?: boolean;
|
|
1207
|
+
/** Write a temp file and rename it into place, so a reader never sees half a file. Cannot be combined with `append`. */
|
|
1208
|
+
atomic?: boolean;
|
|
1209
|
+
/** Permissions such as `0o600`. Without it a new file is 0644 and an existing one keeps its own. */
|
|
1210
|
+
mode?: number;
|
|
1211
|
+
}
|
|
1212
|
+
export interface DownloadFileOptions {
|
|
1213
|
+
/**
|
|
1214
|
+
* Refuse a file larger than this many bytes with `MicroVMFileTooLargeError`:
|
|
1215
|
+
* before reading when the server states the size, otherwise as soon as more
|
|
1216
|
+
* arrives. A guard against a file larger than expected filling memory.
|
|
1217
|
+
*/
|
|
1218
|
+
maxBytes?: number;
|
|
1219
|
+
}
|
|
1220
|
+
/** One file, folder or symlink in a sandbox. */
|
|
1221
|
+
export interface FileInfo {
|
|
1222
|
+
name: string;
|
|
1223
|
+
/** The full path in the sandbox. */
|
|
1224
|
+
path: string;
|
|
1225
|
+
isDir: boolean;
|
|
1226
|
+
/** Bytes. */
|
|
1227
|
+
size: number;
|
|
1228
|
+
/** Type and permission string, such as `-rw-r--r--`. */
|
|
1229
|
+
mode: string;
|
|
1230
|
+
/** Octal permission bits, such as `0644`. */
|
|
1231
|
+
permissions: string;
|
|
1232
|
+
owner: string;
|
|
1233
|
+
group: string;
|
|
1234
|
+
/** Last change time, RFC 3339 in UTC. */
|
|
1235
|
+
modTime: string;
|
|
1236
|
+
/**
|
|
1237
|
+
* Where a symlink points. Only on `listFiles` entries: `getFileInfo`
|
|
1238
|
+
* follows a symlink and describes what it names.
|
|
1239
|
+
*/
|
|
1240
|
+
symlinkTarget?: string;
|
|
1241
|
+
}
|
|
1242
|
+
/** The result of `listFiles`. `truncated` means the listing reached its limit and more entries exist. */
|
|
1243
|
+
export interface FileList {
|
|
1244
|
+
entries: FileInfo[];
|
|
1245
|
+
truncated: boolean;
|
|
1246
|
+
}
|
|
1247
|
+
export interface ListFilesOptions {
|
|
1248
|
+
/** Levels to go down; 1 (the default) is the folder's own entries. Symlinked folders are listed but never entered. */
|
|
1249
|
+
depth?: number;
|
|
1250
|
+
/** The most entries to return. Defaults to 10,000. */
|
|
1251
|
+
limit?: number;
|
|
1252
|
+
}
|
|
1253
|
+
export interface DeleteFileOptions {
|
|
1254
|
+
/** Delete a folder and everything in it. Without it a folder is refused (HTTP 409). */
|
|
1255
|
+
recursive?: boolean;
|
|
1256
|
+
}
|
|
1257
|
+
export interface MakeDirOptions {
|
|
1258
|
+
/** Also make missing parent folders, and accept an existing folder (`mkdir -p`). */
|
|
1259
|
+
parents?: boolean;
|
|
1260
|
+
/** Permissions such as `0o750`. Defaults to 0755. */
|
|
1261
|
+
mode?: number;
|
|
1262
|
+
}
|
|
1263
|
+
export interface MoveFileOptions {
|
|
1264
|
+
/** Replace an existing destination. Without it an existing destination is refused (HTTP 409). */
|
|
1265
|
+
overwrite?: boolean;
|
|
1266
|
+
}
|
|
1267
|
+
export interface CopyFileOptions {
|
|
1268
|
+
/** Replace an existing destination. Without it an existing destination is refused (HTTP 409). */
|
|
1269
|
+
overwrite?: boolean;
|
|
1270
|
+
/** Copy a folder and everything in it, keeping modes and symlinks. Without it a folder is refused (HTTP 409). */
|
|
1271
|
+
recursive?: boolean;
|
|
1272
|
+
}
|
|
1273
|
+
/** One line `findInFiles` found. */
|
|
1274
|
+
export interface FileMatch {
|
|
1275
|
+
file: string;
|
|
1276
|
+
line: number;
|
|
1277
|
+
content: string;
|
|
870
1278
|
}
|
|
871
1279
|
export interface ExecStreamOptions {
|
|
872
|
-
command
|
|
1280
|
+
/** A shell command. Set `command` or `argv`, not both. */
|
|
1281
|
+
command?: string;
|
|
1282
|
+
/** Same as ExecRequest.argv. */
|
|
1283
|
+
argv?: string[];
|
|
873
1284
|
workdir?: string;
|
|
874
1285
|
env?: Record<string, string>;
|
|
875
1286
|
tty?: boolean;
|
|
@@ -915,6 +1326,41 @@ export interface SessionAttachHandle {
|
|
|
915
1326
|
/** Resolves when the session exits, or rejects if the attach stream drops first. */
|
|
916
1327
|
done: Promise<ExecExitInfo>;
|
|
917
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* The kinds of change a file watch reports. `overflow` means some events were
|
|
1331
|
+
* lost (the kernel's queue filled, too many changes in one batch, or a folder
|
|
1332
|
+
* could not be watched): list the folder again to catch up. A rename is
|
|
1333
|
+
* reported as `rename` for the old name and `create` for the new one.
|
|
1334
|
+
*/
|
|
1335
|
+
export type FileEventType = "create" | "write" | "remove" | "rename" | "chmod" | "overflow";
|
|
1336
|
+
/** One change reported by {@link FileWatchHandle}'s watch. */
|
|
1337
|
+
export interface FileEvent {
|
|
1338
|
+
/**
|
|
1339
|
+
* One of {@link FileEventType}. A newer sandbox agent may add kinds; they
|
|
1340
|
+
* are passed through as they come rather than dropped.
|
|
1341
|
+
*/
|
|
1342
|
+
type: FileEventType | (string & {});
|
|
1343
|
+
/** The entry's absolute path in the sandbox. Empty on most overflow events. */
|
|
1344
|
+
path: string;
|
|
1345
|
+
/** True when the entry is a folder. */
|
|
1346
|
+
isDir: boolean;
|
|
1347
|
+
/** Overflow events only: `kernel_queue`, `event_buffer` or `watch_limit`. */
|
|
1348
|
+
reason?: string;
|
|
1349
|
+
}
|
|
1350
|
+
export interface WatchFilesOptions {
|
|
1351
|
+
/** Also watch every subfolder, including ones made after the watch starts. */
|
|
1352
|
+
recursive?: boolean;
|
|
1353
|
+
}
|
|
1354
|
+
export interface FileWatchHandle {
|
|
1355
|
+
/** Stop watching. Safe to call more than once; `wait()` then resolves. */
|
|
1356
|
+
stop(): void;
|
|
1357
|
+
/**
|
|
1358
|
+
* Resolves after `stop()`. Rejects when the sandbox ends the watch (the
|
|
1359
|
+
* watched folder was removed or moved; the error message says which), when
|
|
1360
|
+
* the connection drops, or with the error `onEvent` threw.
|
|
1361
|
+
*/
|
|
1362
|
+
wait(): Promise<void>;
|
|
1363
|
+
}
|
|
918
1364
|
export interface HealthStatus {
|
|
919
1365
|
status: string;
|
|
920
1366
|
sandboxes: number;
|