@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.js CHANGED
@@ -4,10 +4,8 @@ const defaultAPIURL = "http://127.0.0.1:21212";
4
4
  const authRequiredErrorMessage = "PAT token is required. Set patToken, SB_PAT_TOKEN or SB_API_TOKEN.";
5
5
  export class MicroVM {
6
6
  apiUrl;
7
- patToken;
8
7
  /** The team sent as `X-AerolVM-Team`, or `""` when none is. */
9
8
  team;
10
- client;
11
9
  constructor(config = {}) {
12
10
  const patToken = config.patToken ?? readEnv("SB_PAT_TOKEN") ?? readEnv("SB_API_TOKEN") ?? "";
13
11
  const apiUrl = normalizeURL(config.apiUrl ?? readEnv("SB_API_URL") ?? defaultAPIURL);
@@ -16,9 +14,9 @@ export class MicroVM {
16
14
  throw new Error(authRequiredErrorMessage);
17
15
  }
18
16
  this.apiUrl = apiUrl;
19
- this.patToken = patToken;
17
+ Object.defineProperty(this, "patToken", { value: patToken, enumerable: false });
20
18
  this.team = team;
21
- this.client = new APIClient({
19
+ const client = new APIClient({
22
20
  baseURL: apiUrl,
23
21
  patToken,
24
22
  team,
@@ -27,26 +25,29 @@ export class MicroVM {
27
25
  retry: config.retry,
28
26
  signal: config.signal,
29
27
  });
28
+ Object.defineProperty(this, "client", { value: client, enumerable: false });
30
29
  }
31
- async create(options) {
32
- const sandbox = await this.client.create(options);
33
- return this.wrap(sandbox.toJSON());
30
+ async create(options, call) {
31
+ const sandbox = await this.clientFor(call).create(options);
32
+ const wrapped = this.wrap(sandbox.toJSON());
33
+ wrapped.destroyOnExit = options.destroyOnExit ?? false;
34
+ return wrapped;
34
35
  }
35
- async list(options) {
36
- const sandboxes = await this.client.list(options);
36
+ async list(options, call) {
37
+ const sandboxes = await this.clientFor(call).list(options);
37
38
  return sandboxes.map((sandbox) => this.wrap(sandbox.toJSON()));
38
39
  }
39
40
  /**
40
41
  * Iterates one server page at a time. Use this for large fleet inventories so
41
42
  * the SDK does not retain every sandbox in memory.
42
43
  */
43
- async *listPages(options) {
44
- for await (const page of this.client.listPages(options)) {
44
+ async *listPages(options, call) {
45
+ for await (const page of this.clientFor(call).listPages(options)) {
45
46
  yield page.map((sandbox) => this.wrap(sandbox.toJSON()));
46
47
  }
47
48
  }
48
- async get(id, options) {
49
- const sandbox = await this.client.get(id, options);
49
+ async get(id, options, call) {
50
+ const sandbox = await this.clientFor(call).get(id, options);
50
51
  return this.wrap(sandbox.toJSON());
51
52
  }
52
53
  /**
@@ -54,52 +55,81 @@ export class MicroVM {
54
55
  * Names are unique per owner. Throws when the server predates name lookup
55
56
  * (it ignores `?name=`), rather than guessing from an unfiltered list.
56
57
  */
57
- async getByName(name, options) {
58
- const sandbox = await this.client.getByName(name, options);
58
+ async getByName(name, options, call) {
59
+ const sandbox = await this.clientFor(call).getByName(name, options);
59
60
  return sandbox === null ? null : this.wrap(sandbox.toJSON());
60
61
  }
61
- async start(id) {
62
- const sandbox = await this.client.start(id);
62
+ /**
63
+ * Returns the caller's sandbox called `name`, creating it from `options`
64
+ * when there is none. `created` is true only when this call made the
65
+ * sandbox. Safe to retry and to run at the same time from many processes:
66
+ * names are unique per owner, so when two callers race to create the same
67
+ * name, one gets a 409 and reads the winner's sandbox instead. An existing
68
+ * sandbox is returned as it is; its image and settings are not compared
69
+ * with `options`.
70
+ */
71
+ async getOrCreate(name, options, call) {
72
+ const result = await this.clientFor(call).getOrCreate(name, options);
73
+ const sandbox = this.wrap(result.sandbox.toJSON());
74
+ // destroyOnExit applies only to a sandbox this call created. One it found
75
+ // may be in use by another program that shares the name.
76
+ sandbox.destroyOnExit = result.created ? (options.destroyOnExit ?? false) : false;
77
+ return { sandbox, created: result.created };
78
+ }
79
+ async start(id, call) {
80
+ const sandbox = await this.clientFor(call).start(id);
63
81
  return this.wrap(sandbox.toJSON());
64
82
  }
65
- async stop(id) {
66
- const sandbox = await this.client.stop(id);
83
+ async stop(id, call) {
84
+ const sandbox = await this.clientFor(call).stop(id);
67
85
  return this.wrap(sandbox.toJSON());
68
86
  }
69
- async createSnapshot(id, name, options = {}) {
70
- return this.client.createSnapshot(id, name, options);
87
+ async createSnapshot(id, name, options = {}, call) {
88
+ return this.clientFor(call).createSnapshot(id, name, options);
71
89
  }
72
- async registerSnapshot(options) {
73
- return this.client.registerSnapshot(options);
90
+ async registerSnapshot(options, call) {
91
+ return this.clientFor(call).registerSnapshot(options);
74
92
  }
75
- async registerSnapshotFromImage(name, image, options = {}) {
76
- return this.client.registerSnapshotFromImage(name, image, options);
93
+ async registerSnapshotFromImage(name, image, options = {}, call) {
94
+ return this.clientFor(call).registerSnapshotFromImage(name, image, options);
77
95
  }
78
96
  /** Look a snapshot up by name. In a cluster the request is routed to the node holding the image. */
79
- async getSnapshot(name) {
80
- return this.client.getSnapshot(name);
97
+ async getSnapshot(name, call) {
98
+ return this.clientFor(call).getSnapshot(name);
81
99
  }
82
100
  /** List the caller's snapshots, one name-ordered page at a time. */
83
- async listSnapshots(options = {}) {
84
- return this.client.listSnapshots(options);
101
+ async listSnapshots(options = {}, call) {
102
+ return this.clientFor(call).listSnapshots(options);
85
103
  }
86
104
  /** Delete a snapshot by name (idempotent 404 if it does not exist). */
87
- async deleteSnapshot(name) {
88
- return this.client.deleteSnapshot(name);
105
+ async deleteSnapshot(name, call) {
106
+ return this.clientFor(call).deleteSnapshot(name);
107
+ }
108
+ async destroy(id, call) {
109
+ await this.clientFor(call).destroy(id);
89
110
  }
90
- async destroy(id) {
91
- await this.client.destroy(id);
111
+ /**
112
+ * Deletes every sandbox of yours that carries all of `options.tags` and
113
+ * returns the IDs of the ones now gone. It lists every page first, then
114
+ * deletes them one at a time. A sandbox that is already gone (404) counts
115
+ * as deleted, so a second call, or two calls at once, is safe. Any other
116
+ * failed delete stops the call and is thrown; the sandboxes already
117
+ * deleted stay deleted, and calling it again finishes the job. `tags`
118
+ * must not be empty.
119
+ */
120
+ async destroyAll(options, call) {
121
+ return this.clientFor(call).destroyAll(options.tags);
92
122
  }
93
- async resize(id, options) {
94
- const sandbox = await this.client.resize(id, options);
123
+ async resize(id, options, call) {
124
+ const sandbox = await this.clientFor(call).resize(id, options);
95
125
  return this.wrap(sandbox.toJSON());
96
126
  }
97
- async updateLifecycle(id, lifecycle) {
98
- const sandbox = await this.client.updateLifecycle(id, lifecycle);
127
+ async updateLifecycle(id, lifecycle, call) {
128
+ const sandbox = await this.clientFor(call).updateLifecycle(id, lifecycle);
99
129
  return this.wrap(sandbox.toJSON());
100
130
  }
101
- async reconcile() {
102
- await this.client.reconcile();
131
+ async reconcile(call) {
132
+ await this.clientFor(call).reconcile();
103
133
  }
104
134
  /**
105
135
  * Build an {@link Image} on the daemon and optionally push the result to a
@@ -107,40 +137,103 @@ export class MicroVM {
107
137
  * persisted by the daemon. Returns the local content-addressed tag and,
108
138
  * when push was requested, the pushed reference.
109
139
  */
110
- async buildImage(image, options) {
111
- return this.client.buildImage(image, options);
140
+ async buildImage(image, options, call) {
141
+ return this.clientFor(call).buildImage(image, options);
112
142
  }
113
- async health() {
114
- return this.client.health();
143
+ async health(call) {
144
+ return this.clientFor(call).health();
115
145
  }
116
146
  /**
117
147
  * Asks whether a sandbox created with these egress fields would reach a
118
148
  * destination, using the same matcher the filter enforces. No sandbox is
119
149
  * needed.
120
150
  */
121
- async checkNetworkPolicy(options) {
122
- return this.client.checkNetworkPolicy(options);
151
+ async checkNetworkPolicy(options, call) {
152
+ return this.clientFor(call).checkNetworkPolicy(options);
123
153
  }
124
154
  /**
125
155
  * Creates or replaces a named egress profile (a full replace: the same body
126
156
  * twice is a no-op). Sandboxes reference it through `egressProfiles`, and a
127
157
  * change reaches every one of them.
128
158
  */
129
- async putEgressProfile(name, options) {
130
- return this.client.putEgressProfile(name, options);
159
+ async putEgressProfile(name, options, call) {
160
+ return this.clientFor(call).putEgressProfile(name, options);
131
161
  }
132
- async getEgressProfile(name) {
133
- return this.client.getEgressProfile(name);
162
+ async getEgressProfile(name, call) {
163
+ return this.clientFor(call).getEgressProfile(name);
134
164
  }
135
- async listEgressProfiles(options) {
136
- return this.client.listEgressProfiles(options);
165
+ async listEgressProfiles(options, call) {
166
+ return this.clientFor(call).listEgressProfiles(options);
137
167
  }
138
168
  /** Deletes a profile; one that sandboxes still reference is refused (409). */
139
- async deleteEgressProfile(name) {
140
- return this.client.deleteEgressProfile(name);
169
+ async deleteEgressProfile(name, call) {
170
+ return this.clientFor(call).deleteEgressProfile(name);
171
+ }
172
+ /**
173
+ * Sets how many ready sandboxes every server with a warm pool keeps for an
174
+ * image (a full replace: the same call twice is a no-op). A create that names
175
+ * the image then starts from a warm sandbox. Admin only.
176
+ */
177
+ async putWarmPool(image, options, call) {
178
+ return this.clientFor(call).putWarmPool(image, options);
179
+ }
180
+ /** Returns an image's warm-pool entry with its status (ready and target, per server). */
181
+ async getWarmPool(image, call) {
182
+ return this.clientFor(call).getWarmPool(image);
183
+ }
184
+ /** Returns every warm-pool entry, by image, with its status. */
185
+ async listWarmPools(call) {
186
+ return this.clientFor(call).listWarmPools();
187
+ }
188
+ /** Deletes an image's warm-pool entry; its warm sandboxes are removed. Deleting a missing entry succeeds. */
189
+ async deleteWarmPool(image, call) {
190
+ return this.clientFor(call).deleteWarmPool(image);
141
191
  }
142
- async mounts(sandboxID) {
143
- return this.client.mounts(sandboxID);
192
+ /**
193
+ * Puts a role tag on a team's cloud identity allow list, or changes its
194
+ * minimum caller role (`"runner"` or `"admin"`). Admin only. The same call
195
+ * twice changes nothing, so it is safe to retry.
196
+ */
197
+ async putIdentityRole(tag, options, call) {
198
+ return this.clientFor(call).putIdentityRole(tag, options);
199
+ }
200
+ /** Lists a team's cloud identity allow list, one page at a time. Admin only. */
201
+ async listIdentityRoles(options, call) {
202
+ return this.clientFor(call).listIdentityRoles(options);
203
+ }
204
+ /**
205
+ * Takes a role tag off a team's allow list. Admin only. Sandboxes that use
206
+ * it keep their current token (at most one hour) and get no new one.
207
+ * Deleting a tag that is already gone succeeds.
208
+ */
209
+ async deleteIdentityRole(tag, options, call) {
210
+ return this.clientFor(call).deleteIdentityRole(tag, options);
211
+ }
212
+ /**
213
+ * Sets a team's quota: the most sandboxes, CPU and memory its sandboxes
214
+ * may hold. Admin only. The same limits twice change nothing. A create,
215
+ * resize or start past the quota fails with 429 `team_quota_exceeded`.
216
+ */
217
+ async putTeamQuota(team, options, call) {
218
+ return this.clientFor(call).putTeamQuota(team, options);
219
+ }
220
+ /** Reads a team's quota and what the team holds now. 404 if it has none. */
221
+ async getTeamQuota(team, call) {
222
+ return this.clientFor(call).getTeamQuota(team);
223
+ }
224
+ /** Removes a team's quota. Removing one that is already gone succeeds. */
225
+ async deleteTeamQuota(team, call) {
226
+ return this.clientFor(call).deleteTeamQuota(team);
227
+ }
228
+ async mounts(sandboxID, call) {
229
+ return this.clientFor(call).mounts(sandboxID);
230
+ }
231
+ /**
232
+ * Make every token issued for a sandbox so far stop working (see
233
+ * `sandbox.revokeTokens()`). A token made after the call works.
234
+ */
235
+ async revokeSandboxTokens(sandboxID, call) {
236
+ return this.clientFor(call).revokeSandboxTokens(sandboxID);
144
237
  }
145
238
  /**
146
239
  * Read a sandbox's clone-generation token. The token changes whenever the
@@ -148,35 +241,46 @@ export class MicroVM {
148
241
  * Read-only — the SDK cannot reseed a process inside the guest; see the
149
242
  * "Randomness in cloned sandboxes" docs page for the in-guest reseed pattern.
150
243
  */
151
- async cloneGeneration(sandboxID) {
152
- return this.client.cloneGeneration(sandboxID);
244
+ async cloneGeneration(sandboxID, call) {
245
+ return this.clientFor(call).cloneGeneration(sandboxID);
153
246
  }
154
247
  execStream(sandboxID, options) {
155
248
  return this.client.execStream(sandboxID, options);
156
249
  }
157
- async createSession(sandboxID, options) {
158
- return this.client.createSession(sandboxID, options);
250
+ /**
251
+ * Watch a folder in a sandbox for changes: `onEvent` gets each create,
252
+ * write, remove, rename and chmod, in batches every 250 ms. Resolves once
253
+ * the watch is in place. gVisor and Firecracker sandboxes only; Docker,
254
+ * containerd, WASM and isolate sandboxes reject with a 501
255
+ * `MicroVMApiError`. A sandbox whose agent predates file watching rejects
256
+ * with `MicroVMUnsupportedFeatureError`.
257
+ */
258
+ async watchFiles(sandboxID, path, onEvent, options = {}) {
259
+ return this.client.watchFiles(sandboxID, path, onEvent, options);
260
+ }
261
+ async createSession(sandboxID, options, call) {
262
+ return this.clientFor(call).createSession(sandboxID, options);
159
263
  }
160
- async listSessions(sandboxID) {
161
- return this.client.listSessions(sandboxID);
264
+ async listSessions(sandboxID, call) {
265
+ return this.clientFor(call).listSessions(sandboxID);
162
266
  }
163
- async getSession(sandboxID, sessionID) {
164
- return this.client.getSession(sandboxID, sessionID);
267
+ async getSession(sandboxID, sessionID, call) {
268
+ return this.clientFor(call).getSession(sandboxID, sessionID);
165
269
  }
166
- async deleteSession(sandboxID, sessionID) {
167
- await this.client.deleteSession(sandboxID, sessionID);
270
+ async deleteSession(sandboxID, sessionID, call) {
271
+ await this.clientFor(call).deleteSession(sandboxID, sessionID);
168
272
  }
169
- async signalSession(sandboxID, sessionID, signal) {
170
- await this.client.signalSession(sandboxID, sessionID, signal);
273
+ async signalSession(sandboxID, sessionID, signal, call) {
274
+ await this.clientFor(call).signalSession(sandboxID, sessionID, signal);
171
275
  }
172
- async resizeSession(sandboxID, sessionID, cols, rows) {
173
- await this.client.resizeSession(sandboxID, sessionID, cols, rows);
276
+ async resizeSession(sandboxID, sessionID, cols, rows, call) {
277
+ await this.clientFor(call).resizeSession(sandboxID, sessionID, cols, rows);
174
278
  }
175
- async sessionLog(sandboxID, sessionID) {
176
- return this.client.sessionLog(sandboxID, sessionID);
279
+ async sessionLog(sandboxID, sessionID, call) {
280
+ return this.clientFor(call).sessionLog(sandboxID, sessionID);
177
281
  }
178
- async sessionRecording(sandboxID, sessionID) {
179
- return this.client.sessionRecording(sandboxID, sessionID);
282
+ async sessionRecording(sandboxID, sessionID, call) {
283
+ return this.clientFor(call).sessionRecording(sandboxID, sessionID);
180
284
  }
181
285
  attachSession(sandboxID, sessionID, options = {}) {
182
286
  return this.client.attachSession(sandboxID, sessionID, options);
@@ -191,42 +295,42 @@ export class MicroVM {
191
295
  * duplicate ID returns 409 so a retried CI step does not create two
192
296
  * rows for the same logical template.
193
297
  */
194
- async createTemplate(options) {
195
- return this.client.createTemplate(options);
298
+ async createTemplate(options, call) {
299
+ return this.clientFor(call).createTemplate(options);
196
300
  }
197
- async listTemplates() {
198
- return this.client.listTemplates();
301
+ async listTemplates(call) {
302
+ return this.clientFor(call).listTemplates();
199
303
  }
200
- async getTemplate(id) {
201
- return this.client.getTemplate(id);
304
+ async getTemplate(id, call) {
305
+ return this.clientFor(call).getTemplate(id);
202
306
  }
203
- async deleteTemplate(id) {
204
- await this.client.deleteTemplate(id);
307
+ async deleteTemplate(id, call) {
308
+ await this.clientFor(call).deleteTemplate(id);
205
309
  }
206
310
  /**
207
311
  * Register a WASM module in the host catalogue. Resolution is synchronous —
208
312
  * the returned row is typically already `ready`. Use the row's `id` or
209
313
  * `moduleRef` on subsequent `create` calls with `runtime: "wasm"`.
210
314
  */
211
- async createWasmModule(options) {
212
- return this.client.createWasmModule(options);
315
+ async createWasmModule(options, call) {
316
+ return this.clientFor(call).createWasmModule(options);
213
317
  }
214
- async listWasmModules() {
215
- return this.client.listWasmModules();
318
+ async listWasmModules(call) {
319
+ return this.clientFor(call).listWasmModules();
216
320
  }
217
- async getWasmModule(id) {
218
- return this.client.getWasmModule(id);
321
+ async getWasmModule(id, call) {
322
+ return this.clientFor(call).getWasmModule(id);
219
323
  }
220
- async deleteWasmModule(id) {
221
- await this.client.deleteWasmModule(id);
324
+ async deleteWasmModule(id, call) {
325
+ await this.clientFor(call).deleteWasmModule(id);
222
326
  }
223
327
  /**
224
328
  * Upload a compiled core-wasip1 module to the registry under your own
225
329
  * credentials and get back the `oci://` ref to use as `moduleRef` on create.
226
330
  * The daemon validates and forwards the bytes; it never stores them.
227
331
  */
228
- async pushWasmModule(options) {
229
- return this.client.pushWasmModule(options);
332
+ async pushWasmModule(options, call) {
333
+ return this.clientFor(call).pushWasmModule(options);
230
334
  }
231
335
  /**
232
336
  * Re-run the snapshot phase against an existing template. Idempotent
@@ -239,8 +343,8 @@ export class MicroVM {
239
343
  * where rebuild is not safe (build in flight) or not supported
240
344
  * (`ready_no_snapshot` / `failed` — those need delete+recreate today).
241
345
  */
242
- async rebuildTemplate(id) {
243
- return this.client.rebuildTemplate(id);
346
+ async rebuildTemplate(id, call) {
347
+ return this.clientFor(call).rebuildTemplate(id);
244
348
  }
245
349
  /**
246
350
  * Cluster-level DNS helpers. Exposed as a namespaced accessor (rather
@@ -252,15 +356,15 @@ export class MicroVM {
252
356
  get dns() {
253
357
  const client = this.client;
254
358
  return {
255
- target: () => client.ingressDNS(),
359
+ target: (call) => client.withSignal(call?.signal).ingressDNS(),
256
360
  };
257
361
  }
258
362
  /**
259
363
  * Who the server says the caller is: principal, role, team and when the
260
364
  * credential expires. Every valid credential may ask.
261
365
  */
262
- async whoami() {
263
- return this.client.whoami();
366
+ async whoami(call) {
367
+ return this.clientFor(call).whoami();
264
368
  }
265
369
  /**
266
370
  * Scoped API tokens (`avm_…`) for services and CI, as
@@ -270,11 +374,15 @@ export class MicroVM {
270
374
  get tokens() {
271
375
  const client = this.client;
272
376
  return {
273
- create: (options) => client.createAPIToken(options),
274
- list: () => client.listAPITokens(),
275
- revoke: (id) => client.revokeAPIToken(id),
377
+ create: (options, call) => client.withSignal(call?.signal).createAPIToken(options),
378
+ list: (call) => client.withSignal(call?.signal).listAPITokens(),
379
+ revoke: (id, call) => client.withSignal(call?.signal).revokeAPIToken(id),
276
380
  };
277
381
  }
382
+ /** The client for one call: the real one, or a view carrying the call's signal. */
383
+ clientFor(call) {
384
+ return this.client.withSignal(call?.signal);
385
+ }
278
386
  wrap(sandbox) {
279
387
  return new Sandbox(this.client, sandbox);
280
388
  }
package/dist/errors.d.ts CHANGED
@@ -26,3 +26,55 @@ export declare class MicroVMApiError extends Error {
26
26
  retryAfterMs?: number;
27
27
  });
28
28
  }
29
+ /** HTTP 404: the sandbox, file, snapshot or other thing the call named does not exist (or is not yours). */
30
+ export declare class MicroVMNotFoundError extends MicroVMApiError {
31
+ }
32
+ /** HTTP 409: the call clashes with what is there now, such as a sandbox name you already use. */
33
+ export declare class MicroVMConflictError extends MicroVMApiError {
34
+ }
35
+ /**
36
+ * HTTP 503: the server has no room for the call right now or is shedding
37
+ * load. The SDK has already retried it; try again later.
38
+ */
39
+ export declare class MicroVMCapacityError extends MicroVMApiError {
40
+ }
41
+ /** HTTP 408 or 504: the server, or a proxy in front of it, gave up waiting. */
42
+ export declare class MicroVMTimeoutError extends MicroVMApiError {
43
+ }
44
+ /** HTTP 401 or 403: the token is missing, wrong or expired (401), or it may not do this (403). */
45
+ export declare class MicroVMAuthError extends MicroVMApiError {
46
+ }
47
+ /** The most specific {@link MicroVMApiError} subclass for a status. */
48
+ export declare function apiErrorFor(status: number, message: string, options?: {
49
+ code?: string;
50
+ retryAfterMs?: number;
51
+ }): MicroVMApiError;
52
+ /**
53
+ * The sandbox's agent (toolboxd) is older than an option this call needs. An
54
+ * older agent would silently ignore the option (an overwrite guard, an output
55
+ * cap), so the SDK refuses instead of sending it. Restart the sandbox, or
56
+ * re-create its Firecracker template, to get the current agent.
57
+ */
58
+ export declare class MicroVMUnsupportedFeatureError extends Error {
59
+ /** The sandbox whose agent is too old. */
60
+ readonly sandboxID: string;
61
+ /** The features the agent did not list. */
62
+ readonly features: string[];
63
+ /** The agent's version when it reported one. */
64
+ readonly agentVersion: string;
65
+ /**
66
+ * Set (for example "wasm") when the sandbox's runtime cannot offer the
67
+ * features at all; a restart does not help there.
68
+ */
69
+ readonly runtime: string;
70
+ constructor(sandboxID: string, features: string[], agentVersion?: string, runtime?: string);
71
+ }
72
+ /**
73
+ * A download was larger than its `maxBytes`. Thrown before reading when the
74
+ * server states the size, otherwise as soon as more arrives.
75
+ */
76
+ export declare class MicroVMFileTooLargeError extends Error {
77
+ readonly path: string;
78
+ readonly maxBytes: number;
79
+ constructor(path: string, maxBytes: number);
80
+ }
package/dist/errors.js CHANGED
@@ -29,3 +29,92 @@ export class MicroVMApiError extends Error {
29
29
  this.retryAfterMs = options.retryAfterMs;
30
30
  }
31
31
  }
32
+ // The typed errors below extend MicroVMApiError, so code that catches
33
+ // MicroVMApiError and reads `status` keeps working unchanged; new code can
34
+ // test `instanceof` for the one case it handles instead of comparing numbers.
35
+ // They keep `name` "MicroVMApiError": code that checks the name (often done
36
+ // where two copies of the SDK make instanceof unreliable) keeps working too.
37
+ /** HTTP 404: the sandbox, file, snapshot or other thing the call named does not exist (or is not yours). */
38
+ export class MicroVMNotFoundError extends MicroVMApiError {
39
+ }
40
+ /** HTTP 409: the call clashes with what is there now, such as a sandbox name you already use. */
41
+ export class MicroVMConflictError extends MicroVMApiError {
42
+ }
43
+ /**
44
+ * HTTP 503: the server has no room for the call right now or is shedding
45
+ * load. The SDK has already retried it; try again later.
46
+ */
47
+ export class MicroVMCapacityError extends MicroVMApiError {
48
+ }
49
+ /** HTTP 408 or 504: the server, or a proxy in front of it, gave up waiting. */
50
+ export class MicroVMTimeoutError extends MicroVMApiError {
51
+ }
52
+ /** HTTP 401 or 403: the token is missing, wrong or expired (401), or it may not do this (403). */
53
+ export class MicroVMAuthError extends MicroVMApiError {
54
+ }
55
+ const API_ERROR_CLASSES = {
56
+ 401: MicroVMAuthError,
57
+ 403: MicroVMAuthError,
58
+ 404: MicroVMNotFoundError,
59
+ 408: MicroVMTimeoutError,
60
+ 409: MicroVMConflictError,
61
+ 503: MicroVMCapacityError,
62
+ 504: MicroVMTimeoutError,
63
+ };
64
+ /** The most specific {@link MicroVMApiError} subclass for a status. */
65
+ export function apiErrorFor(status, message, options = {}) {
66
+ const ErrorClass = API_ERROR_CLASSES[status] ?? MicroVMApiError;
67
+ return new ErrorClass(status, message, options);
68
+ }
69
+ /**
70
+ * The sandbox's agent (toolboxd) is older than an option this call needs. An
71
+ * older agent would silently ignore the option (an overwrite guard, an output
72
+ * cap), so the SDK refuses instead of sending it. Restart the sandbox, or
73
+ * re-create its Firecracker template, to get the current agent.
74
+ */
75
+ export class MicroVMUnsupportedFeatureError extends Error {
76
+ /** The sandbox whose agent is too old. */
77
+ sandboxID;
78
+ /** The features the agent did not list. */
79
+ features;
80
+ /** The agent's version when it reported one. */
81
+ agentVersion;
82
+ /**
83
+ * Set (for example "wasm") when the sandbox's runtime cannot offer the
84
+ * features at all; a restart does not help there.
85
+ */
86
+ runtime;
87
+ constructor(sandboxID, features, agentVersion = "", runtime = "") {
88
+ const agent = agentVersion ? `toolboxd ${agentVersion}` : "an older toolboxd";
89
+ super(runtime
90
+ ? `sandbox ${sandboxID} is a ${runtimeDisplayName(runtime)} sandbox, which does not support ${features.join(", ")}`
91
+ : `sandbox ${sandboxID} runs ${agent}, which does not support ${features.join(", ")}; ` +
92
+ "restart the sandbox, or re-create its Firecracker template, to get the current agent");
93
+ this.name = "MicroVMUnsupportedFeatureError";
94
+ this.sandboxID = sandboxID;
95
+ this.features = features;
96
+ this.agentVersion = agentVersion;
97
+ this.runtime = runtime;
98
+ }
99
+ }
100
+ function runtimeDisplayName(runtime) {
101
+ if (runtime === "wasm")
102
+ return "WebAssembly";
103
+ if (runtime === "isolate")
104
+ return "V8 isolate";
105
+ return runtime;
106
+ }
107
+ /**
108
+ * A download was larger than its `maxBytes`. Thrown before reading when the
109
+ * server states the size, otherwise as soon as more arrives.
110
+ */
111
+ export class MicroVMFileTooLargeError extends Error {
112
+ path;
113
+ maxBytes;
114
+ constructor(path, maxBytes) {
115
+ super(`${path} is larger than maxBytes (${maxBytes} bytes)`);
116
+ this.name = "MicroVMFileTooLargeError";
117
+ this.path = path;
118
+ this.maxBytes = maxBytes;
119
+ }
120
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { MicroVM } from "./MicroVM.js";
2
2
  export { Sandbox } from "./Sandbox.js";
3
3
  export { Image } from "./Image.js";
4
- export { MicroVMApiError } from "./errors.js";
5
- export type { MicroVMConfig, Tokens } from "./MicroVM.js";
4
+ export { MicroVMApiError, MicroVMAuthError, MicroVMCapacityError, MicroVMConflictError, MicroVMFileTooLargeError, MicroVMNotFoundError, MicroVMTimeoutError, MicroVMUnsupportedFeatureError, } from "./errors.js";
5
+ export { DEFAULT_LIST_LIMIT } from "./internal/client.js";
6
+ export type { GetOrCreateResult, MicroVMConfig, Tokens } from "./MicroVM.js";
6
7
  export type { RetryConfig } from "./internal/client.js";
7
- export type { AddCustomDomainOptions, APIToken, APITokenList, CreateAPITokenOptions, CreatedAPIToken, WhoAmI, WhoAmIPrincipal, AuditEvent, AuditOptions, AuditPage, EgressProfile, EgressProfileList, EgressProfileOptions, EgressProfileRef, EgressInject, EgressRule, ListEgressProfilesOptions, NetworkLearned, NetworkLearnedEntry, NetworkPolicy, NetworkPolicyCheckOptions, NetworkPolicyCheckResult, NetworkPolicyOptions, BinaryLike, BuildImageOptions, BuildImagePushOptions, BuildImageResult, CloneGeneration, CreateOptions, CreateSessionOptions, CreateTemplateOptions, CreateWasmModuleOptions, CustomDomain, CustomDomainDNSRecords, CustomDomainStatus, DNSRecord, DNSRecordType, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, Failover, FailoverPolicy, GetOptions, HealthStatus, IngressTarget, IngressTargetSource, Lifecycle, ListOptions, MountSpec, MountSpecRedacted, MountType, PlatformVolumeMount, CreateSnapshotOptions, RegisterSnapshotOptions, ListSnapshotsOptions, SnapshotList, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, Template, TemplatePushState, TemplateStatus, WasmModule, WasmModuleStatus, PushWasmModuleOptions, PushWasmModuleResult, UpdateLifecycleOptions, } from "./types.js";
8
+ export type { AddCustomDomainOptions, APIToken, APITokenList, CreateAPITokenOptions, CreatedAPIToken, CreateSandboxTokenOptions, RevokeSandboxTokensResult, SandboxToken, WhoAmI, WhoAmIPrincipal, AuditEvent, AuditOptions, AuditPage, EgressProfile, EgressProfileList, EgressProfileOptions, EgressProfileRef, IdentityOptions, IdentityRole, IdentityRoleList, IdentityRoleOptions, ListIdentityRolesOptions, SandboxIdentity, EgressInject, EgressRule, ListEgressProfilesOptions, WarmPool, WarmPoolOnImageChange, WarmPoolOptions, WarmPoolServerStatus, WarmPoolStatus, NetworkLearned, NetworkLearnedEntry, NetworkPolicy, NetworkPolicyCheckOptions, NetworkPolicyCheckResult, NetworkPolicyOptions, BinaryLike, BuildImageOptions, BuildImagePushOptions, CallOptions, BuildImageResult, CloneGeneration, CreateOptions, CreateSessionOptions, CreateTemplateOptions, CreateWasmModuleOptions, DestroyAllOptions, CustomDomain, CustomDomainDNSRecords, CustomDomainStatus, DNSRecord, DNSRecordType, ExecRequest, ExecResult, ExecExitInfo, ExposedPort, ExposePortOptions, ExposeProtocol, ExposeResult, ExposeVisibility, Failover, FailoverPolicy, FileEvent, FileEventType, FileWatchHandle, FileInfo, FileList, UploadFileOptions, DownloadFileOptions, FileMatch, ListFilesOptions, DeleteFileOptions, MakeDirOptions, MoveFileOptions, CopyFileOptions, PlacementRule, TeamQuota, TeamQuotaOptions, TeamQuotaUsage, GetOptions, HealthStatus, IngressTarget, IngressTargetSource, Lifecycle, ListOptions, MountSpec, MountSpecRedacted, MountType, PlatformVolumeMount, CreateSnapshotOptions, RegisterSnapshotOptions, ListSnapshotsOptions, SnapshotList, RegistryAuth, ResizeOptions, Sandbox as SandboxData, SandboxSnapshot, SandboxStatus, StatusReason, Session, SessionAttachHandle, SessionAttachOptions, SessionStatus, Template, TemplatePushState, TemplateStatus, WasmModule, WasmModuleStatus, PushWasmModuleOptions, PushWasmModuleResult, UpdateLifecycleOptions, WatchFilesOptions, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { MicroVM } from "./MicroVM.js";
2
2
  export { Sandbox } from "./Sandbox.js";
3
3
  export { Image } from "./Image.js";
4
- export { MicroVMApiError } from "./errors.js";
4
+ export { MicroVMApiError, MicroVMAuthError, MicroVMCapacityError, MicroVMConflictError, MicroVMFileTooLargeError, MicroVMNotFoundError, MicroVMTimeoutError, MicroVMUnsupportedFeatureError, } from "./errors.js";
5
+ export { DEFAULT_LIST_LIMIT } from "./internal/client.js";