@pablofdezr/microvm 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # microvm
2
+
3
+ TypeScript client for the microvm daemon: run untrusted code in Firecracker
4
+ microVMs. ESM, Node ≥ 18, zero runtime dependencies (it uses the built-in
5
+ `fetch`). Types are generated from the same OpenAPI spec as the server.
6
+
7
+ ```
8
+ npm install @pablofdezr/microvm
9
+ ```
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { Client } from "@pablofdezr/microvm";
15
+
16
+ const client = new Client("http://127.0.0.1:8080", { token });
17
+
18
+ const sb = await client.sandboxes.create({ image: "python" });
19
+ try {
20
+ const exe = await client.run(sb.id, "python3", ["-c", "print('hi')"]);
21
+ console.log(exe.stdout);
22
+ } finally {
23
+ await client.sandboxes.delete(sb.id);
24
+ }
25
+ ```
26
+
27
+ ## Sandboxes vs tasks
28
+
29
+ - **Sandbox** — you hold a VM and run commands in it. `create` throws a capacity
30
+ error when the node is full (`err.isCapacity`), so backpressure is yours.
31
+ - **Task** — you hand work to the fleet; it never fails for capacity and waits
32
+ for a slot on any node, sized to the CPU and memory you request.
33
+
34
+ ```ts
35
+ const task = await client.tasks.create({
36
+ image: "python",
37
+ cmd: "python3",
38
+ args: ["-c", "print(2 + 2)"],
39
+ vcpus: 2,
40
+ mem_mib: 1024,
41
+ priority: 7, // 0-10, higher first
42
+ });
43
+ const done = await client.tasks.wait(task.id);
44
+ console.log(done.stdout);
45
+ ```
46
+
47
+ Optional fields are plain optional properties — no pointer wrappers.
48
+
49
+ A task has no live sandbox to upload to first, so its files travel inside
50
+ `create`, keyed by path and written before `cmd` runs. Pass the content as text
51
+ or bytes — it is base64-encoded for you, exactly as `files.write` does:
52
+
53
+ ```ts
54
+ await client.tasks.create({
55
+ image: "python",
56
+ cmd: "python3",
57
+ args: ["/app/main.py"],
58
+ files: { "/app/main.py": 'print("hi")' },
59
+ });
60
+ ```
61
+
62
+ ## Streaming output
63
+
64
+ ```ts
65
+ for await (const frame of client.executions.stream(sb.id, exe.id)) {
66
+ process.stdout.write(frame.data);
67
+ }
68
+ ```
69
+
70
+ ## Pagination
71
+
72
+ `all` is an async iterator that follows `has_more` to the end:
73
+
74
+ ```ts
75
+ for await (const sb of client.sandboxes.all({})) {
76
+ console.log(sb.id, sb.state);
77
+ }
78
+ ```
79
+
80
+ `client.executions.all(sandboxId, {})` works the same way.
81
+
82
+ ## Retries
83
+
84
+ Transient failures — a network error, or a 429/500/502/503/504 — are retried
85
+ with exponential backoff, full jitter, and any `Retry-After` honoured. Only
86
+ idempotent requests are retried: GET/PUT/DELETE always, and POST only when it
87
+ carries an idempotency key.
88
+
89
+ ```ts
90
+ const client = new Client(baseURL, { token, maxRetries: 4 }); // default 2, 0 disables
91
+
92
+ await client.tasks.create(params, { idempotencyKey: crypto.randomUUID() });
93
+ ```
94
+
95
+ ## Errors
96
+
97
+ Failures are `APIError` with the API's `type`, `code`, `message`, `param` and
98
+ `requestId`. Branch on the guards:
99
+
100
+ ```ts
101
+ try {
102
+ await client.sandboxes.retrieve(id);
103
+ } catch (e) {
104
+ if (e instanceof APIError) {
105
+ if (e.isNotFound) { /* 404 */ }
106
+ if (e.isCapacity) { /* node full — consider a task */ }
107
+ if (e.isConflict) { /* e.g. executing in a stopped sandbox */ }
108
+ if (e.isForbidden) { /* key lacks permission (admin-only route) */ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Observability
114
+
115
+ `onResponse` is called once per HTTP attempt — retries included:
116
+
117
+ ```ts
118
+ new Client(baseURL, {
119
+ token,
120
+ onResponse: (info) =>
121
+ console.log(`${info.method} ${info.path} attempt=${info.attempt} status=${info.status} ${info.durationMs}ms`),
122
+ });
123
+ ```
124
+
125
+ ## Tenants (admin)
126
+
127
+ Setting a tenant's storage policy needs an admin token; an ordinary key gets a
128
+ 403 (`err.isForbidden`).
129
+
130
+ ```ts
131
+ await admin.tenants.setLimit(tenantId, 500 * 1024 * 1024, "evict"); // or "preserve"
132
+ const t = await admin.tenants.retrieve(tenantId); // policy + live usage
133
+ ```
@@ -0,0 +1,335 @@
1
+ /**
2
+ * The TypeScript client for the microvm API.
3
+ *
4
+ * The resource types in `types.gen.ts` are generated from `api/openapi.yaml`,
5
+ * the same file the server and the Go SDK are generated from. What is written
6
+ * here by hand is only what a generator does badly: the transport, typed
7
+ * errors, streaming, auto-pagination, and the few helpers that turn three calls
8
+ * into one.
9
+ *
10
+ * ```ts
11
+ * const client = new Client("http://127.0.0.1:8080", { token });
12
+ *
13
+ * const sb = await client.sandboxes.create({ image: "python" });
14
+ * try {
15
+ * await client.files.write(sb.id, "main.py", 'print("hello")');
16
+ * const exe = await client.run(sb.id, "python3", ["main.py"]);
17
+ * console.log(exe.stdout);
18
+ * } finally {
19
+ * await client.sandboxes.delete(sb.id);
20
+ * }
21
+ * ```
22
+ */
23
+ import type { components } from "./types.gen.js";
24
+ type Schemas = components["schemas"];
25
+ export type Sandbox = Schemas["Sandbox"];
26
+ export type SandboxList = Schemas["SandboxList"];
27
+ export type SandboxState = Schemas["SandboxState"];
28
+ export type SandboxCreateParams = Schemas["SandboxCreateParams"];
29
+ export type Execution = Schemas["Execution"];
30
+ export type ExecutionList = Schemas["ExecutionList"];
31
+ export type ExecutionStatus = Schemas["ExecutionStatus"];
32
+ export type ExecutionCreateParams = Schemas["ExecutionCreateParams"];
33
+ export type ExecutionCancelParams = Schemas["ExecutionCancelParams"];
34
+ export type File = Schemas["File"];
35
+ export type Task = Schemas["Task"];
36
+ export type TaskStatus = Schemas["TaskStatus"];
37
+ export type TaskCreateParams = Omit<Schemas["TaskCreateParams"], "files"> & {
38
+ /**
39
+ * Files written into the sandbox before `cmd` runs, keyed by path. Pass the
40
+ * content as text or raw bytes; it is base64-encoded for you, exactly as
41
+ * `files.write` does — the wire form is base64, but that is not the caller's
42
+ * job to produce here any more than it is there.
43
+ */
44
+ files?: Record<string, string | Uint8Array>;
45
+ };
46
+ export type Queue = Schemas["Queue"];
47
+ export type Image = Schemas["Image"];
48
+ export type ImageList = Schemas["ImageList"];
49
+ export type Tenant = Schemas["Tenant"];
50
+ export type TenantList = Schemas["TenantList"];
51
+ export type TenantUpdateParams = Schemas["TenantUpdateParams"];
52
+ export type TenantFullPolicy = Schemas["TenantFullPolicy"];
53
+ export type Health = Schemas["Health"];
54
+ export type Frame = Schemas["Frame"];
55
+ export type ErrorType = Schemas["ErrorType"];
56
+ /** How long an ordinary request may take. Streams and waits opt out. */
57
+ export declare const DEFAULT_TIMEOUT_MS = 30000;
58
+ /** Where a daemon listens unless told otherwise. */
59
+ export declare const DEFAULT_BASE_URL = "http://127.0.0.1:8080";
60
+ /** This SDK's version, sent in the User-Agent. */
61
+ export declare const SDK_VERSION = "0.1.0";
62
+ /** How many times a transient failure is retried unless told otherwise. */
63
+ export declare const DEFAULT_MAX_RETRIES = 2;
64
+ /** What an onResponse observer is told about one HTTP attempt. */
65
+ export interface RequestInfo {
66
+ method: string;
67
+ path: string;
68
+ /** 1 for the first try, 2 for the first retry, ... */
69
+ attempt: number;
70
+ /** 0 when the request never got a response. */
71
+ status: number;
72
+ error?: unknown;
73
+ durationMs: number;
74
+ }
75
+ export interface ClientOptions {
76
+ token?: string;
77
+ /** Overrides the global fetch, for tests or a custom agent. */
78
+ fetch?: typeof globalThis.fetch;
79
+ timeoutMs?: number;
80
+ /**
81
+ * How many times a transient failure -- a network error, or a
82
+ * 429/500/502/503/504 -- is retried before it is thrown, with exponential
83
+ * backoff and jitter and any Retry-After honoured. Only idempotent requests
84
+ * are retried: GET/PUT/DELETE always, POST only with an idempotency key.
85
+ * Defaults to DEFAULT_MAX_RETRIES; 0 disables.
86
+ */
87
+ maxRetries?: number;
88
+ /** Called once per HTTP attempt -- retries included -- for logging or metrics. */
89
+ onResponse?: (info: RequestInfo) => void;
90
+ }
91
+ /** Per-call options. */
92
+ export interface RequestOptions {
93
+ signal?: AbortSignal;
94
+ /**
95
+ * Makes a create safe to retry.
96
+ *
97
+ * A request whose reply never arrived cannot be known to have failed, so a
98
+ * bare retry may run the work twice. With a key, the retry returns the
99
+ * original answer instead.
100
+ */
101
+ idempotencyKey?: string;
102
+ }
103
+ /**
104
+ * An error the API reported.
105
+ *
106
+ * `type` is what to branch on: it says what class of thing went wrong and so
107
+ * what to do about it. `code` says exactly which thing, for when that matters.
108
+ */
109
+ export declare class APIError extends Error {
110
+ readonly status: number;
111
+ readonly type: ErrorType;
112
+ readonly code: string;
113
+ readonly param?: string | undefined;
114
+ readonly requestId?: string | undefined;
115
+ constructor(status: number, type: ErrorType, code: string, message: string, param?: string | undefined, requestId?: string | undefined);
116
+ /** The object does not exist. */
117
+ get isNotFound(): boolean;
118
+ /**
119
+ * The node has no room.
120
+ *
121
+ * The one error worth retrying unchanged — and the signal to consider a task
122
+ * instead, since tasks wait for a slot anywhere in the fleet rather than
123
+ * failing.
124
+ */
125
+ get isCapacity(): boolean;
126
+ /** The object is in a state that forbids the call. */
127
+ get isConflict(): boolean;
128
+ /**
129
+ * The key lacks permission — an ordinary token calling an admin-only endpoint,
130
+ * such as setting a tenant's policy. Distinct from a missing token: the
131
+ * request was authenticated, and refused.
132
+ */
133
+ get isForbidden(): boolean;
134
+ }
135
+ export declare class Client {
136
+ #private;
137
+ readonly sandboxes: SandboxResource;
138
+ readonly executions: ExecutionResource;
139
+ readonly files: FileResource;
140
+ readonly tasks: TaskResource;
141
+ readonly queue: QueueResource;
142
+ readonly images: ImageResource;
143
+ readonly tenants: TenantResource;
144
+ constructor(baseURL?: string, opts?: ClientOptions);
145
+ /** @internal */
146
+ request(method: string, path: string, init?: {
147
+ body?: unknown;
148
+ query?: Record<string, string | number | undefined>;
149
+ opts?: RequestOptions;
150
+ /** Streams manage their own lifetime, so no timeout is imposed. */
151
+ noTimeout?: boolean;
152
+ }): Promise<Response>;
153
+ /** @internal */
154
+ json<T>(method: string, path: string, init?: Parameters<Client["request"]>[2]): Promise<T>;
155
+ /** Whether the daemon is up. Needs no token. */
156
+ health(opts?: RequestOptions): Promise<Health>;
157
+ /**
158
+ * Start a command and wait for it to finish.
159
+ *
160
+ * Check `err(exe)` afterwards: a non-zero exit is the code's own verdict,
161
+ * whereas a timeout or a vanished sandbox is not.
162
+ */
163
+ run(sandboxID: string, cmd: string, args?: string[], opts?: RequestOptions): Promise<Execution>;
164
+ }
165
+ export type ListParams = {
166
+ limit?: number;
167
+ starting_after?: string;
168
+ ending_before?: string;
169
+ };
170
+ declare class SandboxResource {
171
+ private readonly c;
172
+ constructor(c: Client);
173
+ /**
174
+ * Boot a sandbox and wait for it to be ready.
175
+ *
176
+ * Throws a capacity error when the node is full — see `APIError.isCapacity`.
177
+ * That is by design: a sandbox is a reservation, so you are told at once
178
+ * rather than left waiting. Submit a task if you would rather wait.
179
+ */
180
+ create(params: SandboxCreateParams, opts?: RequestOptions): Promise<Sandbox>;
181
+ retrieve(id: string, opts?: RequestOptions): Promise<Sandbox>;
182
+ /**
183
+ * Kill the sandbox and get back its final cost.
184
+ *
185
+ * Those numbers are sampled just before the kill and cannot be had after: the
186
+ * accounting dies with the VM. This reply is the only record of what the
187
+ * sandbox consumed.
188
+ */
189
+ delete(id: string, opts?: RequestOptions): Promise<Sandbox>;
190
+ list(params?: ListParams & {
191
+ state?: SandboxState;
192
+ }, opts?: RequestOptions): Promise<SandboxList>;
193
+ /**
194
+ * Every sandbox, fetching pages as needed.
195
+ *
196
+ * Paging is mechanical and easy to get subtly wrong — forgetting `has_more`,
197
+ * or taking the cursor from the wrong end — and it is the same loop every
198
+ * time, so it lives here rather than in every caller.
199
+ */
200
+ all(params?: ListParams & {
201
+ state?: SandboxState;
202
+ }, opts?: RequestOptions): AsyncGenerator<Sandbox>;
203
+ }
204
+ declare class ExecutionResource {
205
+ private readonly c;
206
+ constructor(c: Client);
207
+ /**
208
+ * Start a command and return at once, without waiting for it.
209
+ *
210
+ * The command belongs to the sandbox, not to this call: dropping the
211
+ * connection does not kill it. Follow it with `stream`, or collect it later
212
+ * with `retrieve`.
213
+ */
214
+ create(sandboxID: string, params: ExecutionCreateParams, opts?: RequestOptions): Promise<Execution>;
215
+ /**
216
+ * An execution and everything it printed.
217
+ *
218
+ * Works after the sandbox is gone, which is the point: the output you most
219
+ * want is from the run that was killed.
220
+ */
221
+ retrieve(sandboxID: string, executionID: string, opts?: RequestOptions): Promise<Execution>;
222
+ list(sandboxID: string, params?: ListParams, opts?: RequestOptions): Promise<ExecutionList>;
223
+ /**
224
+ * Signal a running execution.
225
+ *
226
+ * The signal reaches the whole process group, so a program that spawned
227
+ * children does not leave them behind. Defaults to SIGKILL. Cancelling
228
+ * something that already finished is not an error.
229
+ */
230
+ cancel(sandboxID: string, executionID: string, params?: ExecutionCancelParams, opts?: RequestOptions): Promise<Execution>;
231
+ /**
232
+ * Follow an execution's output as it is produced.
233
+ *
234
+ * The stream replays from the beginning before it follows, so connecting late
235
+ * — or reconnecting after a dropped connection — loses nothing. Aborting the
236
+ * signal stops watching; the execution keeps running, because it belongs to
237
+ * its sandbox. To stop the execution itself, use `cancel`.
238
+ *
239
+ * ```ts
240
+ * for await (const frame of client.executions.stream(sbID, exeID)) {
241
+ * if (frame.type === "stdout") process.stdout.write(frameText(frame));
242
+ * }
243
+ * ```
244
+ */
245
+ stream(sandboxID: string, executionID: string, opts?: RequestOptions): AsyncGenerator<Frame>;
246
+ /**
247
+ * Wait for an execution to finish.
248
+ *
249
+ * Polls rather than streams: streaming is for showing output as it appears,
250
+ * waiting is for knowing the result, and polling survives a dropped
251
+ * connection without any work from the caller.
252
+ */
253
+ wait(sandboxID: string, executionID: string, opts?: RequestOptions): Promise<Execution>;
254
+ }
255
+ declare class FileResource {
256
+ private readonly c;
257
+ constructor(c: Client);
258
+ /** Write a file into the sandbox, making parent directories. */
259
+ write(sandboxID: string, path: string, content: string | Uint8Array, opts?: RequestOptions): Promise<File>;
260
+ /** Download a file's bytes. */
261
+ retrieve(sandboxID: string, path: string, opts?: RequestOptions): Promise<Uint8Array>;
262
+ /** Download a file as text. */
263
+ readText(sandboxID: string, path: string, opts?: RequestOptions): Promise<string>;
264
+ }
265
+ declare class TaskResource {
266
+ private readonly c;
267
+ constructor(c: Client);
268
+ /**
269
+ * Queue work for the fleet.
270
+ *
271
+ * Unlike creating a sandbox this never fails for capacity: the task waits for
272
+ * a slot on any node. Use it for throughput, and a sandbox for several
273
+ * commands that share state.
274
+ */
275
+ create(params: TaskCreateParams, opts?: RequestOptions): Promise<Task>;
276
+ retrieve(taskID: string, opts?: RequestOptions): Promise<Task>;
277
+ /** Wait for a task to have a result. */
278
+ wait(taskID: string, opts?: RequestOptions): Promise<Task>;
279
+ }
280
+ declare class QueueResource {
281
+ private readonly c;
282
+ constructor(c: Client);
283
+ /**
284
+ * The queue's depth and this node's slots.
285
+ *
286
+ * The depth is the fleet's; the slots are this node's alone. No node knows
287
+ * the fleet's capacity, which is what lets one be added without telling
288
+ * anything else.
289
+ */
290
+ retrieve(opts?: RequestOptions): Promise<Queue>;
291
+ }
292
+ declare class ImageResource {
293
+ private readonly c;
294
+ constructor(c: Client);
295
+ list(opts?: RequestOptions): Promise<ImageList>;
296
+ }
297
+ /**
298
+ * The `/v1/tenants` resource, and it is administrative: a tenant's storage cap
299
+ * is set by an operator, never by the code that runs under it. Updating needs an
300
+ * admin token; an ordinary key is refused with a 403 (see `APIError.isForbidden`).
301
+ */
302
+ declare class TenantResource {
303
+ private readonly c;
304
+ constructor(c: Client);
305
+ /** Set a tenant's byte cap and full policy, replacing any previous one. */
306
+ update(tenantID: string, params: TenantUpdateParams, opts?: RequestOptions): Promise<Tenant>;
307
+ /**
308
+ * `update` for the common case: a byte cap and a policy. Pass `"preserve"` to
309
+ * reject writes when full, or `"evict"` to delete the oldest objects to make
310
+ * room. A `maxBytes` of 0 means unlimited.
311
+ */
312
+ setLimit(tenantID: string, maxBytes: number, policy: TenantFullPolicy, opts?: RequestOptions): Promise<Tenant>;
313
+ /**
314
+ * A tenant's policy and its current usage, the usage read live from the bucket
315
+ * at call time (so it costs a listing — see `Tenant.usage_bytes`).
316
+ */
317
+ retrieve(tenantID: string, opts?: RequestOptions): Promise<Tenant>;
318
+ /** Every configured tenant. A tenant with no policy is absent: it is unlimited. */
319
+ list(opts?: RequestOptions): Promise<TenantList>;
320
+ }
321
+ /**
322
+ * Why an execution did not simply run to completion, or null.
323
+ *
324
+ * A non-zero exit returns null: the process ran, and that is its own verdict
325
+ * rather than a failure of ours. The endings that are *not* the code's doing —
326
+ * a timeout, a cancel, a VM taken away, a command that never started — return
327
+ * an error, because those are the ones that must not be mistaken for a program
328
+ * choosing to fail.
329
+ */
330
+ export declare function err(exe: Execution): Error | null;
331
+ /** A frame's bytes. */
332
+ export declare function frameBytes(frame: Frame): Uint8Array;
333
+ /** A frame's bytes as text. */
334
+ export declare function frameText(frame: Frame): string;
335
+ export {};