@runblackcat/sdk 0.1.0-alpha.100
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 +35 -0
- package/README.md +406 -0
- package/package.json +29 -0
- package/sdk/dist/index.d.ts +1020 -0
- package/sdk/dist/index.js +751 -0
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side TypeScript client for the Blackcat gateway.
|
|
3
|
+
*
|
|
4
|
+
* The SDK intentionally contains transport primitives only. Workload-specific
|
|
5
|
+
* workflows (a consumer's dirty-worktree sync, say) compose file and exec
|
|
6
|
+
* operations in their own repository.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CAPABILITY_OPS: {
|
|
9
|
+
readonly EXEC: 1;
|
|
10
|
+
readonly READ: 2;
|
|
11
|
+
readonly WRITE: 4;
|
|
12
|
+
readonly SNAPSHOT: 8;
|
|
13
|
+
readonly DESTROY: 16;
|
|
14
|
+
readonly INSPECT: 32;
|
|
15
|
+
readonly SANDBOX_CREATE: 64;
|
|
16
|
+
readonly TEMPLATE_RW: 128;
|
|
17
|
+
readonly PUBLISH: 256;
|
|
18
|
+
readonly PROCESS_START: 512;
|
|
19
|
+
readonly PROCESS_INSPECT: 1024;
|
|
20
|
+
readonly PROCESS_CANCEL: 2048;
|
|
21
|
+
readonly RENEW: 4096;
|
|
22
|
+
};
|
|
23
|
+
export type CapabilityOps = number;
|
|
24
|
+
export declare const BLACKCAT_ERROR_CODES: {
|
|
25
|
+
readonly SANDBOX_TRANSITIONING: "sandbox_transitioning";
|
|
26
|
+
readonly FLEET_UPGRADE_QUIESCING: "fleet_upgrade_quiescing";
|
|
27
|
+
};
|
|
28
|
+
export type FetchFunction = (input: RequestInfo | URL, init?: RequestInit & {
|
|
29
|
+
tls?: {
|
|
30
|
+
ca: string;
|
|
31
|
+
};
|
|
32
|
+
timeout?: false;
|
|
33
|
+
}) => Promise<Response>;
|
|
34
|
+
export interface BlackcatClientOptions {
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
/**
|
|
37
|
+
* A tenant API key. The gateway accepts it directly on every resource route
|
|
38
|
+
* for the tenant's own resources (limited to the key's operation set), so a
|
|
39
|
+
* trusted server-side caller needs no `tokens.mint` per call. Keep it
|
|
40
|
+
* server-side; hand less-trusted holders a minted `token` instead.
|
|
41
|
+
*/
|
|
42
|
+
apiKey?: string;
|
|
43
|
+
/** A capability token; preferred over `apiKey` when both are set. */
|
|
44
|
+
token?: string;
|
|
45
|
+
/** Capability identity and expiry, using idiomatic TypeScript names. */
|
|
46
|
+
tokenId?: string;
|
|
47
|
+
tokenExpiresAt?: number;
|
|
48
|
+
/** Backward-compatible aliases retained for pre-publication callers. */
|
|
49
|
+
token_id?: string;
|
|
50
|
+
token_expires_at?: number;
|
|
51
|
+
/** Re-mint a capability when renewal must replace the token secret. */
|
|
52
|
+
renewCapability?: () => Promise<MintTokenResponse>;
|
|
53
|
+
/** Private gateway trust root. Bun forwards this through its fetch TLS option. */
|
|
54
|
+
caPem?: string;
|
|
55
|
+
fetch?: FetchFunction;
|
|
56
|
+
}
|
|
57
|
+
export interface TemplateDefaults {
|
|
58
|
+
vcpus: number;
|
|
59
|
+
memory_mib: number;
|
|
60
|
+
hibernate?: HibernateMode;
|
|
61
|
+
on_interruption?: "evacuate" | "destroy";
|
|
62
|
+
/** MiB of virtio-mem region booted into the checkpoint; a multiple of 128. Defaults to the template's memory. */
|
|
63
|
+
hotplug_max_mib?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The size a stored template version carries: `memory_mib` is the size it was
|
|
67
|
+
* captured at (boot memory plus what was plugged), and a sandbox can later
|
|
68
|
+
* take any size from `base_memory_mib` to `base_memory_mib + hotplug_max_mib`.
|
|
69
|
+
*/
|
|
70
|
+
export interface TemplateShapeDefaults extends TemplateDefaults {
|
|
71
|
+
base_memory_mib: number;
|
|
72
|
+
hotplug_max_mib: number;
|
|
73
|
+
}
|
|
74
|
+
export type HibernateMode = "memory";
|
|
75
|
+
export type TemplateEgressPolicy = {
|
|
76
|
+
mode: "deny_all";
|
|
77
|
+
} | {
|
|
78
|
+
mode: "allow_public";
|
|
79
|
+
} | {
|
|
80
|
+
mode: "rules";
|
|
81
|
+
rules: ReadonlyArray<{
|
|
82
|
+
protocol: "tcp" | "udp";
|
|
83
|
+
port: number;
|
|
84
|
+
target: {
|
|
85
|
+
kind: "cidr";
|
|
86
|
+
value: string;
|
|
87
|
+
} | {
|
|
88
|
+
kind: "dns_name";
|
|
89
|
+
value: string;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "dns_suffix";
|
|
92
|
+
value: string;
|
|
93
|
+
};
|
|
94
|
+
}>;
|
|
95
|
+
};
|
|
96
|
+
export type TemplatePreparationValue = {
|
|
97
|
+
from: "literal";
|
|
98
|
+
value: string;
|
|
99
|
+
} | {
|
|
100
|
+
from: "parameter";
|
|
101
|
+
name: string;
|
|
102
|
+
} | {
|
|
103
|
+
from: "secret";
|
|
104
|
+
name: string;
|
|
105
|
+
};
|
|
106
|
+
export interface TemplateRestoreHealth {
|
|
107
|
+
command: string;
|
|
108
|
+
args: readonly string[];
|
|
109
|
+
cwd: string;
|
|
110
|
+
timeout_ms: number;
|
|
111
|
+
}
|
|
112
|
+
interface TemplateDockerSourceBase {
|
|
113
|
+
/** A prepared filesystem; each sandbox starts a fresh process tree. */
|
|
114
|
+
docker: {
|
|
115
|
+
image: string;
|
|
116
|
+
};
|
|
117
|
+
defaults: TemplateDefaults;
|
|
118
|
+
egress: TemplateEgressPolicy;
|
|
119
|
+
restore_health: TemplateRestoreHealth;
|
|
120
|
+
/** Runs in the guest after it is ready and before the capture, so every
|
|
121
|
+
* clone restores with the app's caches and JIT already hot. Must exit 0. */
|
|
122
|
+
warmup?: TemplateRestoreHealth;
|
|
123
|
+
}
|
|
124
|
+
interface TemplatePreparation {
|
|
125
|
+
parameters: Record<string, {
|
|
126
|
+
min_length: number;
|
|
127
|
+
max_length: number;
|
|
128
|
+
charset: "printable_ascii" | "lower_hex";
|
|
129
|
+
}>;
|
|
130
|
+
prepare: {
|
|
131
|
+
command: string;
|
|
132
|
+
args: TemplatePreparationValue[];
|
|
133
|
+
cwd: string;
|
|
134
|
+
environment: Record<string, TemplatePreparationValue>;
|
|
135
|
+
timeout_ms: number;
|
|
136
|
+
output_limit_bytes: number;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
export type TemplateDockerSource = TemplateDockerSourceBase & (TemplatePreparation | {
|
|
140
|
+
parameters?: never;
|
|
141
|
+
prepare?: never;
|
|
142
|
+
});
|
|
143
|
+
/** A build whose image was uploaded rather than pulled from a registry. */
|
|
144
|
+
export type TemplateUploadedImageSource = Omit<TemplateDockerSourceBase, "docker"> & {
|
|
145
|
+
docker: UploadedImageSource;
|
|
146
|
+
} & (TemplatePreparation | {
|
|
147
|
+
parameters?: never;
|
|
148
|
+
prepare?: never;
|
|
149
|
+
});
|
|
150
|
+
export interface UploadedImageSource {
|
|
151
|
+
/** The `sha256:<hex>` digest returned by `templates.uploadImage`. */
|
|
152
|
+
uploaded_image: string;
|
|
153
|
+
}
|
|
154
|
+
export interface UploadedImage {
|
|
155
|
+
image: string;
|
|
156
|
+
bytes: number;
|
|
157
|
+
/** True when the control plane already held this exact image. */
|
|
158
|
+
unchanged: boolean;
|
|
159
|
+
}
|
|
160
|
+
export interface TemplateSandboxSource {
|
|
161
|
+
/** `warmup` runs in the sandbox just before it is captured; omitted, the
|
|
162
|
+
* sandbox's template's own warm-up (if any) runs. */
|
|
163
|
+
sandbox: {
|
|
164
|
+
id: string;
|
|
165
|
+
warmup?: TemplateRestoreHealth;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* A promoted sandbox keeps the machine dimensions it was captured on, so
|
|
169
|
+
* the gateway rejects `defaults` here. Omit `egress` to inherit the source
|
|
170
|
+
* template's policy.
|
|
171
|
+
*/
|
|
172
|
+
egress?: TemplateEgressPolicy;
|
|
173
|
+
}
|
|
174
|
+
export type TemplateSource = TemplateDockerSource | TemplateUploadedImageSource | TemplateSandboxSource;
|
|
175
|
+
/** What a promotion adds to a template create: how it is warmed and packed. */
|
|
176
|
+
export interface PromotionOptions {
|
|
177
|
+
/** Warm the new version onto this many more nodes in bulk, beyond the node
|
|
178
|
+
* that captured it (at most 99), or onto `"all"` the other healthy nodes.
|
|
179
|
+
* Nodes bound to a capacity reservation that follows the template are warmed
|
|
180
|
+
* regardless. */
|
|
181
|
+
prefetch_copies?: number | "all";
|
|
182
|
+
/** Millicores each clone of the new version reserves, at most 1000 per vCPU.
|
|
183
|
+
* Omitted, the version inherits what the sandbox's own template reserves. */
|
|
184
|
+
cpu_reservation_mcpu?: number;
|
|
185
|
+
}
|
|
186
|
+
export type CreateTemplateInput = {
|
|
187
|
+
name: string;
|
|
188
|
+
source: TemplateDockerSource;
|
|
189
|
+
} | {
|
|
190
|
+
name: string;
|
|
191
|
+
source: TemplateUploadedImageSource;
|
|
192
|
+
} | ({
|
|
193
|
+
name?: string;
|
|
194
|
+
source: TemplateSandboxSource;
|
|
195
|
+
} & PromotionOptions);
|
|
196
|
+
export interface Template {
|
|
197
|
+
id: string;
|
|
198
|
+
name: string;
|
|
199
|
+
version: number;
|
|
200
|
+
/** `local_ready` while a promotion's chunks are still being published. */
|
|
201
|
+
state?: "local_ready" | "published";
|
|
202
|
+
/** The gateway stores the complete source manifest as JSON text. */
|
|
203
|
+
source: string;
|
|
204
|
+
current: boolean;
|
|
205
|
+
}
|
|
206
|
+
/** Public record name retained for consumers that distinguish it from creation input. */
|
|
207
|
+
export type TemplateRecord = Template;
|
|
208
|
+
export interface CreateTemplateBuildInput {
|
|
209
|
+
name: string;
|
|
210
|
+
content_key: string;
|
|
211
|
+
activate_on_ready: boolean;
|
|
212
|
+
source: TemplateDockerSource;
|
|
213
|
+
}
|
|
214
|
+
export type TemplateBuildPhase = "queued" | "building" | "validating" | "ready" | "failed";
|
|
215
|
+
export interface TemplateBuildRecord {
|
|
216
|
+
id: string;
|
|
217
|
+
name: string;
|
|
218
|
+
content_key: string;
|
|
219
|
+
phase: TemplateBuildPhase;
|
|
220
|
+
attempt: number;
|
|
221
|
+
template_version_id: string | null;
|
|
222
|
+
error: string | null;
|
|
223
|
+
}
|
|
224
|
+
export interface PrepareSandboxResponse {
|
|
225
|
+
prepared?: boolean;
|
|
226
|
+
process: Pick<ProcessRecord, "id" | "state" | "exit_code">;
|
|
227
|
+
}
|
|
228
|
+
export interface CreateTemplateResponse {
|
|
229
|
+
id: string;
|
|
230
|
+
version: number;
|
|
231
|
+
/**
|
|
232
|
+
* `local_ready`: a promotion whose capture has been proved but whose chunks
|
|
233
|
+
* are still being published. The version is current and claimable, but only
|
|
234
|
+
* on the node that captured it; clones depend only on that node's files and
|
|
235
|
+
* are destroyed if the publication fails. `published`: every chunk is in the
|
|
236
|
+
* object store.
|
|
237
|
+
*/
|
|
238
|
+
state?: "local_ready" | "published";
|
|
239
|
+
/**
|
|
240
|
+
* Capture/publish phase breakdown for a checkpoint promotion, when this
|
|
241
|
+
* create captured a running sandbox. Absent for a template created without
|
|
242
|
+
* a capture, and for a server that predates this field.
|
|
243
|
+
*/
|
|
244
|
+
capture_timing?: {
|
|
245
|
+
capture_ms: number;
|
|
246
|
+
/** How long the source VM stood paused: the memory/state dump, the disk
|
|
247
|
+
* freeze and the resume. Sealing and upload happen after it. */
|
|
248
|
+
pause_ms: number;
|
|
249
|
+
/** Absent while the version is `local_ready`: publication is not over. */
|
|
250
|
+
publish_ms?: number;
|
|
251
|
+
/** Restoring a disposable clone from the capture and running the
|
|
252
|
+
* template's restore-health command in it; both overlap the upload. */
|
|
253
|
+
validation_restore_ms?: number;
|
|
254
|
+
validation_health_ms?: number;
|
|
255
|
+
/** Whether the capturing node was left holding the new version's memory
|
|
256
|
+
* and disk for fault-free restores. */
|
|
257
|
+
warmed?: {
|
|
258
|
+
memory: boolean;
|
|
259
|
+
disk: boolean;
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
/** Present for a promotion that asked for or achieved warming. */
|
|
263
|
+
warming?: {
|
|
264
|
+
capturing_node_warm: boolean;
|
|
265
|
+
prefetch_copies: number;
|
|
266
|
+
/** Pass to `templates.prefetchStatus` to follow the extra copies. */
|
|
267
|
+
prefetch_id?: string;
|
|
268
|
+
error?: string;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
export type CreateSandboxInput = {
|
|
272
|
+
/**
|
|
273
|
+
* The template name or version id. Omitted, it is the tenant's `default`
|
|
274
|
+
* template, which the gateway builds on the tenant's first use: that first
|
|
275
|
+
* create fails with a 503 `default_template_building` until the build is
|
|
276
|
+
* ready, and repeating the same request is how to wait for it.
|
|
277
|
+
*/
|
|
278
|
+
template?: string;
|
|
279
|
+
/** Must equal the checkpoint's vCPUs, which are fixed at capture; omit it. */
|
|
280
|
+
vcpus?: number;
|
|
281
|
+
/**
|
|
282
|
+
* Guest memory in MiB: the checkpoint's captured size or larger, up to its
|
|
283
|
+
* boot memory plus hotplug region (the extra is plugged right after restore).
|
|
284
|
+
*/
|
|
285
|
+
memory_mib?: number;
|
|
286
|
+
/** CPU entitlement in millicores, at most the checkpoint's vCPUs * 1000. */
|
|
287
|
+
cpu_mcpu?: number;
|
|
288
|
+
/** Stable across retries of one acquisition; never reuse for another request. */
|
|
289
|
+
request_id: string;
|
|
290
|
+
ssh_public_key?: string;
|
|
291
|
+
lease: SandboxLeaseInput;
|
|
292
|
+
/** A capacity reservation this create should draw a bound slot from. */
|
|
293
|
+
reservation?: string;
|
|
294
|
+
/**
|
|
295
|
+
* Ask for a capability scoped to each sandbox this request creates, returned
|
|
296
|
+
* in the response (`capability` on the receipt, or on each batch member), so
|
|
297
|
+
* a caller driving many sandboxes mints none of its own. `ops` must be within
|
|
298
|
+
* the creating token's; `ttl_secs` is capped at 900 and renewable like any
|
|
299
|
+
* token. Only the request that creates a sandbox gets its capability: a
|
|
300
|
+
* repeat of the same `request_id` returns none.
|
|
301
|
+
*/
|
|
302
|
+
capability?: {
|
|
303
|
+
ops: string[] | CapabilityOps;
|
|
304
|
+
ttl_secs: number;
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
/** A capability scoped to one sandbox, as returned by a create. */
|
|
308
|
+
export interface SandboxCapability {
|
|
309
|
+
token: string;
|
|
310
|
+
token_id: string;
|
|
311
|
+
ops: string[];
|
|
312
|
+
/** Epoch milliseconds. */
|
|
313
|
+
expires_at: number;
|
|
314
|
+
renewable_until: number;
|
|
315
|
+
}
|
|
316
|
+
/** A batch create: `request_id` names the batch, member `i` is `<request_id>.<i>`. */
|
|
317
|
+
export type CreateSandboxBatchInput = CreateSandboxInput & {
|
|
318
|
+
/** How many identical sandboxes to create, 1 to 1000. */
|
|
319
|
+
count: number;
|
|
320
|
+
};
|
|
321
|
+
export type SandboxBatchMemberState = "pending" | "placed" | "failed" | "destroyed";
|
|
322
|
+
export interface SandboxBatchMember {
|
|
323
|
+
/** Poll this member alone with `sandboxes.acquisition(request_id)`. */
|
|
324
|
+
request_id: string;
|
|
325
|
+
id: string;
|
|
326
|
+
state: SandboxBatchMemberState;
|
|
327
|
+
/** Present when the create asked for `capability`. */
|
|
328
|
+
capability?: SandboxCapability;
|
|
329
|
+
}
|
|
330
|
+
export interface SandboxBatch {
|
|
331
|
+
/** The batch's request id. */
|
|
332
|
+
request_id: string;
|
|
333
|
+
count: number;
|
|
334
|
+
/** Members in each state; a state with no members is absent. */
|
|
335
|
+
counts: Partial<Record<SandboxBatchMemberState, number>>;
|
|
336
|
+
members: SandboxBatchMember[];
|
|
337
|
+
}
|
|
338
|
+
export type SandboxBatchResponse = SandboxBatch | {
|
|
339
|
+
request_id: string;
|
|
340
|
+
state: "prevented";
|
|
341
|
+
};
|
|
342
|
+
export interface SandboxLeaseInput {
|
|
343
|
+
/** Initial lease duration, between 60 seconds and 24 hours. */
|
|
344
|
+
ttl_secs: number;
|
|
345
|
+
/** Hard lifetime from acquisition, at least ttl_secs and at most 30 days. */
|
|
346
|
+
renewable_for_secs: number;
|
|
347
|
+
}
|
|
348
|
+
export type CreateCapacityReservationInput = ({
|
|
349
|
+
/** One immutable template version, pinned for the reservation's life. */
|
|
350
|
+
template: string;
|
|
351
|
+
template_resource?: never;
|
|
352
|
+
} | {
|
|
353
|
+
/** A template resource name: the reservation follows whichever version
|
|
354
|
+
* is current, even one that does not exist yet, and its bound nodes are
|
|
355
|
+
* warmed with each new version as it is activated. */
|
|
356
|
+
template_resource: string;
|
|
357
|
+
template?: never;
|
|
358
|
+
}) & {
|
|
359
|
+
/** How many sandboxes of this template/shape to reserve. */
|
|
360
|
+
count: number;
|
|
361
|
+
vcpus: number;
|
|
362
|
+
memory_mib: number;
|
|
363
|
+
/** How long to hold the reservation, between 1 second and 4 hours. */
|
|
364
|
+
ttl_secs: number;
|
|
365
|
+
/** Defaults to `consumable`; `reusable` returns each slot after confirmed destruction.
|
|
366
|
+
* `warm` keeps `count` free slots on workers that cache a pinned template
|
|
367
|
+
* version; it takes `template` (not `template_resource`), is never held
|
|
368
|
+
* against other claims, and a sandbox create naming it is a 400. */
|
|
369
|
+
mode?: "consumable" | "reusable" | "warm";
|
|
370
|
+
/** When the capacity must be ready: seconds from now, or an RFC 3339 time
|
|
371
|
+
* within `ttl_secs`. Absent: as soon as it can be, at the lowest cost. */
|
|
372
|
+
needed_by?: number | string;
|
|
373
|
+
/** `shared` (default): any eligible node, free headroom first. `dedicated`:
|
|
374
|
+
* only hosts dedicated reservations hold, launched for them. A held host
|
|
375
|
+
* serves only dedicated reservations (of any tenant: they pack), never
|
|
376
|
+
* ordinary work; a sandbox create naming it waits for one of its slots. */
|
|
377
|
+
placement?: "shared" | "dedicated";
|
|
378
|
+
/** With `placement: "dedicated"`, the provider shape to launch: an exact
|
|
379
|
+
* shape or a family (`c7i`) the fleet can launch (direct EC2: the config's
|
|
380
|
+
* instance_types). Absent: the planner chooses. */
|
|
381
|
+
shape?: string;
|
|
382
|
+
/** Stable retry key, at most 107 ASCII letters, digits, `_` or `-`. */
|
|
383
|
+
idempotency_key?: string;
|
|
384
|
+
};
|
|
385
|
+
export interface CapacityReservation {
|
|
386
|
+
id: string;
|
|
387
|
+
state: "active" | "released" | "expired";
|
|
388
|
+
/** The version nodes are warmed with; null while a reservation following a
|
|
389
|
+
* template resource waits for the resource's first version. */
|
|
390
|
+
template: string | null;
|
|
391
|
+
/** The template resource this reservation follows, or null when pinned. */
|
|
392
|
+
template_resource: string | null;
|
|
393
|
+
count: number;
|
|
394
|
+
mode: "consumable" | "reusable" | "warm";
|
|
395
|
+
placement: "shared" | "dedicated";
|
|
396
|
+
/** The shape a dedicated reservation launches, or null. */
|
|
397
|
+
shape: string | null;
|
|
398
|
+
vcpus: number;
|
|
399
|
+
memory_mib: number;
|
|
400
|
+
/** Slots consumed by an actual sandbox create. */
|
|
401
|
+
claimed: number;
|
|
402
|
+
/** Current slot occupancy for reusable reservations; zero otherwise. */
|
|
403
|
+
occupied: number;
|
|
404
|
+
/** Slots bound to a specific node and not yet claimed. */
|
|
405
|
+
bound: number;
|
|
406
|
+
/** Bound slots whose node already has the template warm. For warm mode this
|
|
407
|
+
* counts member nodes at 1 each. */
|
|
408
|
+
warm: number;
|
|
409
|
+
/** Warm mode: free slots of the shape on member workers whose template is
|
|
410
|
+
* ready, as of the last reconcile; zero for other modes. */
|
|
411
|
+
warm_free: number;
|
|
412
|
+
expires_at: SystemTimeValue;
|
|
413
|
+
/** When the capacity must be ready (epoch milliseconds), or null. */
|
|
414
|
+
needed_by: number | null;
|
|
415
|
+
}
|
|
416
|
+
export interface WaitOptions {
|
|
417
|
+
/**
|
|
418
|
+
* Have the gateway hold a still-pending claim for up to this many seconds
|
|
419
|
+
* and answer as soon as it is placed or fails, with its ports, instead of
|
|
420
|
+
* answering `pending` at once. The gateway holds at most 55 seconds. A claim
|
|
421
|
+
* still pending after the hold is returned as `pending`; ask again.
|
|
422
|
+
*/
|
|
423
|
+
wait_secs?: number;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Response returned by POST /v1/sandboxes (the gateway has no template field).
|
|
427
|
+
* The gateway sends a `failed` receipt with HTTP 409, so `create` and
|
|
428
|
+
* `acquisition` surface it as a `BlackcatApiError` whose `details` this
|
|
429
|
+
* parser accepts: placement was abandoned and its sandbox torn down, which is
|
|
430
|
+
* retryable under a new request id rather than a protocol fault.
|
|
431
|
+
*/
|
|
432
|
+
export type CreateSandboxResponse = {
|
|
433
|
+
request_id: string;
|
|
434
|
+
state: "prevented";
|
|
435
|
+
} | ({
|
|
436
|
+
request_id: string;
|
|
437
|
+
id: string;
|
|
438
|
+
tier: "boot" | "warm_pool" | "restore";
|
|
439
|
+
expires_at: SystemTimeValue;
|
|
440
|
+
renewable_until: SystemTimeValue;
|
|
441
|
+
/**
|
|
442
|
+
* Phase-timing breakdown for the launch that placed this sandbox, once one
|
|
443
|
+
* has committed. Absent while still queued/pending, and for a server that
|
|
444
|
+
* predates this field.
|
|
445
|
+
*/
|
|
446
|
+
restore_timing?: RestoreTiming;
|
|
447
|
+
/** Present when the create asked for `capability` and created this sandbox. */
|
|
448
|
+
capability?: SandboxCapability;
|
|
449
|
+
} & ({
|
|
450
|
+
state: "placed";
|
|
451
|
+
node: string;
|
|
452
|
+
} | {
|
|
453
|
+
state: "pending";
|
|
454
|
+
node: string | null;
|
|
455
|
+
} | {
|
|
456
|
+
state: "failed";
|
|
457
|
+
node: null;
|
|
458
|
+
error: string;
|
|
459
|
+
} | {
|
|
460
|
+
state: "destroyed";
|
|
461
|
+
node: string | null;
|
|
462
|
+
error: string;
|
|
463
|
+
}));
|
|
464
|
+
/**
|
|
465
|
+
* Restore phase breakdown a node reported for a checkpoint launch (Proposal
|
|
466
|
+
* F, docs/restore-performance.md). `phase_ms` mirrors the node's own
|
|
467
|
+
* `checkpoint vm timing` log line; `chunk_source` reports how much of the
|
|
468
|
+
* checkpoint's memory/disk was fetched remotely vs. served locally, how many
|
|
469
|
+
* UFFD faults and NBD reads this restore actually served (both zero on the
|
|
470
|
+
* file-restore path), and the checkpoint's logical sizes. `memory_source` is
|
|
471
|
+
* `"file"` when a node-local materialized copy let this restore skip
|
|
472
|
+
* UFFD/NBD lazy faulting entirely (fault-free warm restore,
|
|
473
|
+
* docs/restore-performance.md), or `"uffd"` for the lazy chunk-source path
|
|
474
|
+
* every checkpoint restore used before it.
|
|
475
|
+
*/
|
|
476
|
+
export interface RestoreTiming {
|
|
477
|
+
proposal_ms: number;
|
|
478
|
+
restore_vm_ms: number;
|
|
479
|
+
commit_ms: number;
|
|
480
|
+
total_ms: number;
|
|
481
|
+
phase_ms: {
|
|
482
|
+
open: number;
|
|
483
|
+
network: number;
|
|
484
|
+
firecracker: number;
|
|
485
|
+
state: number;
|
|
486
|
+
disk: number;
|
|
487
|
+
pager: number;
|
|
488
|
+
load: number;
|
|
489
|
+
activation: number;
|
|
490
|
+
/** Milliseconds from the TAP being raised until the guest's gateway and resolver answered; absent from a node that predates it, 0 from a guest that does. */
|
|
491
|
+
network_ready?: number;
|
|
492
|
+
};
|
|
493
|
+
memory_source?: "file" | "uffd";
|
|
494
|
+
/** The node already held what it restores from as files (a warm restore), so no fetch ran for this claim: the memory, and the disk on a node that clones disks (one that cannot still pages its disk through NBD). Null when the node did not say; absent from a server that predates it. */
|
|
495
|
+
template_cached?: boolean | null;
|
|
496
|
+
chunk_source: {
|
|
497
|
+
remote_requests: number;
|
|
498
|
+
remote_bytes: number;
|
|
499
|
+
local_requests?: number;
|
|
500
|
+
local_bytes?: number;
|
|
501
|
+
uffd_faults?: number;
|
|
502
|
+
uffd_fault_bytes?: number;
|
|
503
|
+
nbd_reads?: number;
|
|
504
|
+
nbd_read_bytes?: number;
|
|
505
|
+
logical_memory_bytes: number;
|
|
506
|
+
logical_disk_bytes: number;
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
export interface Sandbox {
|
|
510
|
+
id: string;
|
|
511
|
+
template: string;
|
|
512
|
+
state: string;
|
|
513
|
+
node?: string;
|
|
514
|
+
/** vCPUs the guest has now: the template's, until a cold resize reboots it onto another count. */
|
|
515
|
+
vcpus: number;
|
|
516
|
+
/** Current guest memory in MiB (boot memory plus what is plugged). */
|
|
517
|
+
memory_mib: number;
|
|
518
|
+
/** Current CPU entitlement in millicores. */
|
|
519
|
+
cpu_mcpu: number;
|
|
520
|
+
expires_at: SystemTimeValue;
|
|
521
|
+
renewable_until: SystemTimeValue;
|
|
522
|
+
}
|
|
523
|
+
/** A short-lived SSH key to authorize on a running sandbox. */
|
|
524
|
+
export interface GrantSshKeyInput {
|
|
525
|
+
/** One `ssh-ed25519` public key line. */
|
|
526
|
+
public_key: string;
|
|
527
|
+
/** Lifetime in seconds, 1 to 86400. The key only matters while connecting. */
|
|
528
|
+
ttl_secs: number;
|
|
529
|
+
}
|
|
530
|
+
export interface SshKeyGrant {
|
|
531
|
+
/** Unix milliseconds at which the key stops authenticating. */
|
|
532
|
+
expires_at: number;
|
|
533
|
+
/** The login account in the sandbox. */
|
|
534
|
+
user: string;
|
|
535
|
+
/** The sandbox's SSH host public key; pin it before connecting. */
|
|
536
|
+
host_key: string;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Where and how to reach a port inside a sandbox through the gateway. Send
|
|
540
|
+
* `headers` with every request and with the WebSocket handshake; the gateway
|
|
541
|
+
* strips them before the application sees the request. A browser `WebSocket`
|
|
542
|
+
* cannot send headers, so the WebSocket form is for server-side clients.
|
|
543
|
+
*/
|
|
544
|
+
export interface PortConnection {
|
|
545
|
+
/** HTTPS URL of the path on the port. */
|
|
546
|
+
url: string;
|
|
547
|
+
/** The same target as a `wss:` URL. */
|
|
548
|
+
websocketUrl: string;
|
|
549
|
+
headers: {
|
|
550
|
+
Authorization: string;
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
export interface RenewSandboxResponse {
|
|
554
|
+
id: string;
|
|
555
|
+
expires_at: SystemTimeValue;
|
|
556
|
+
renewable_until: SystemTimeValue;
|
|
557
|
+
}
|
|
558
|
+
/** The CPU a node runs on, as it reports it. */
|
|
559
|
+
export interface CpuIdentity {
|
|
560
|
+
vendor: string;
|
|
561
|
+
model_name: string;
|
|
562
|
+
family: number;
|
|
563
|
+
model: number;
|
|
564
|
+
stepping: number;
|
|
565
|
+
microcode: string;
|
|
566
|
+
/** Hash of the CPUID feature leaves a guest's CPUID is derived from. */
|
|
567
|
+
features_hash: string;
|
|
568
|
+
/** The host is itself a guest (nested virtualization). */
|
|
569
|
+
nested: boolean;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Tenant-scoped view returned by GET /v1/fleet. Residents and cached snapshots
|
|
573
|
+
* are already filtered by the gateway to the caller's tenant.
|
|
574
|
+
*/
|
|
575
|
+
export interface FleetNode {
|
|
576
|
+
id: string;
|
|
577
|
+
addr: string;
|
|
578
|
+
total_mib: number;
|
|
579
|
+
reserved_mib: number;
|
|
580
|
+
vcpus: number;
|
|
581
|
+
health: string;
|
|
582
|
+
schedulable: boolean;
|
|
583
|
+
lease_until: SystemTimeValue;
|
|
584
|
+
committed_mib: number;
|
|
585
|
+
residents: string[];
|
|
586
|
+
cached_snapshots: string[];
|
|
587
|
+
interruption: FleetInterruption | null;
|
|
588
|
+
/** Charged versus measured memory and the node's pressure state. */
|
|
589
|
+
memory: FleetNodeMemory;
|
|
590
|
+
/** Whether the host runs with SMT active. */
|
|
591
|
+
smt_active: boolean;
|
|
592
|
+
/** Bytes the worker reserves for cached templates; 0 until it reports one. */
|
|
593
|
+
cache_budget_bytes: number;
|
|
594
|
+
/** The node's CPU; absent from nodes older than CPU-aware placement. */
|
|
595
|
+
cpu?: CpuIdentity | null;
|
|
596
|
+
/** 1000 charges file-backed clones in full; 2000 charges them at half. */
|
|
597
|
+
max_memory_ratio_milli: number;
|
|
598
|
+
max_vcpu_ratio_milli: number;
|
|
599
|
+
/** How the fleet decides what a sandbox costs. */
|
|
600
|
+
packing_mode?: "learned" | "manual";
|
|
601
|
+
}
|
|
602
|
+
export type MemoryPressure = "normal" | "high" | "critical";
|
|
603
|
+
export interface FleetNodeMemory {
|
|
604
|
+
/** What admission charged for the node's residents. */
|
|
605
|
+
charged_mib: number;
|
|
606
|
+
/** The residents' sizes before the overcommit discount. */
|
|
607
|
+
plugged_mib: number;
|
|
608
|
+
/** Resident memory of the residents' VMMs, shared pages counted per mapper. */
|
|
609
|
+
measured_rss_mib: number;
|
|
610
|
+
/** Proportional memory the residents cost; compare with `charged_mib`. */
|
|
611
|
+
measured_pss_mib: number;
|
|
612
|
+
/** Memory only one resident maps (Private_Clean + Private_Dirty). */
|
|
613
|
+
measured_uss_mib: number;
|
|
614
|
+
host_available_mib: number;
|
|
615
|
+
psi_some_avg10_milli: number;
|
|
616
|
+
pressure: MemoryPressure;
|
|
617
|
+
}
|
|
618
|
+
export type FleetTrust = "single_trust" | "multi_tenant";
|
|
619
|
+
export interface ResizeSandboxInput {
|
|
620
|
+
/**
|
|
621
|
+
* Target vCPU count. A count different from the sandbox's reboots the guest
|
|
622
|
+
* cold on its own disk: every process ends, template boot services restart,
|
|
623
|
+
* processes started by hand do not.
|
|
624
|
+
*/
|
|
625
|
+
vcpus?: number;
|
|
626
|
+
/** Target guest memory in MiB: boot memory plus a multiple of 128. */
|
|
627
|
+
memory_mib?: number;
|
|
628
|
+
/** Target CPU entitlement in millicores. */
|
|
629
|
+
cpu_mcpu?: number;
|
|
630
|
+
/** Drop the guest's page caches before a shrink. Defaults to true. */
|
|
631
|
+
drop_caches?: boolean;
|
|
632
|
+
/** Stable across retries of one resize; generated when omitted. */
|
|
633
|
+
request_id?: string;
|
|
634
|
+
}
|
|
635
|
+
/** The size a resize reached. A shrink the guest could not fully honour reports what it reached. */
|
|
636
|
+
export interface SandboxResize {
|
|
637
|
+
id: string;
|
|
638
|
+
state: "placed";
|
|
639
|
+
request_id: string;
|
|
640
|
+
/** True when the guest was rebooted to change its vCPU count. */
|
|
641
|
+
cold: boolean;
|
|
642
|
+
vcpus: number;
|
|
643
|
+
memory_mib: number;
|
|
644
|
+
cpu_mcpu: number;
|
|
645
|
+
/** False when the guest stopped short of the requested memory. */
|
|
646
|
+
reached_target: boolean;
|
|
647
|
+
elapsed_ms: number;
|
|
648
|
+
/** Cold resizes only: durable processes the reboot ended. */
|
|
649
|
+
processes_ended?: number;
|
|
650
|
+
/** Cold resizes only: where the reboot spent its time, in milliseconds. */
|
|
651
|
+
phases?: {
|
|
652
|
+
quiesce_ms: number;
|
|
653
|
+
stop_ms: number;
|
|
654
|
+
boot_ms: number;
|
|
655
|
+
plug_ms: number;
|
|
656
|
+
handshake_ms: number;
|
|
657
|
+
health_ms: number;
|
|
658
|
+
};
|
|
659
|
+
/** Cold resizes only: unexpired SSH grants installed again, and ones that failed. */
|
|
660
|
+
ssh_grants_restored?: number;
|
|
661
|
+
ssh_grants_failed?: number;
|
|
662
|
+
}
|
|
663
|
+
export interface FleetInterruption {
|
|
664
|
+
reason: string;
|
|
665
|
+
deadline: SystemTimeValue;
|
|
666
|
+
}
|
|
667
|
+
export interface FleetResponse {
|
|
668
|
+
nodes: FleetNode[];
|
|
669
|
+
fleet_trust: FleetTrust;
|
|
670
|
+
/** `native`, or a CPU template (`t2`, `t2s`, `t2cl`, `t2a`). */
|
|
671
|
+
cpu_baseline?: string;
|
|
672
|
+
}
|
|
673
|
+
export interface TemplateRefreshPolicy {
|
|
674
|
+
interval_seconds: number;
|
|
675
|
+
plugin_installation: string;
|
|
676
|
+
}
|
|
677
|
+
export interface ConfigureTemplateResourceInput {
|
|
678
|
+
keep_versions: number;
|
|
679
|
+
refresh: TemplateRefreshPolicy | null;
|
|
680
|
+
}
|
|
681
|
+
export interface TemplateResource {
|
|
682
|
+
name: string;
|
|
683
|
+
current_version_id: string;
|
|
684
|
+
retention: {
|
|
685
|
+
keep_versions: number;
|
|
686
|
+
};
|
|
687
|
+
refresh: TemplateRefreshPolicy | null;
|
|
688
|
+
next_refresh_at: number | null;
|
|
689
|
+
last_refresh?: unknown;
|
|
690
|
+
}
|
|
691
|
+
export interface ExecInput {
|
|
692
|
+
command: string;
|
|
693
|
+
args: string[];
|
|
694
|
+
cwd: string;
|
|
695
|
+
/** Positive guest execution budget; effects before timeout are not undone. */
|
|
696
|
+
timeout_ms: number;
|
|
697
|
+
/** Optional bounded stdin for the streaming EXEC endpoint. */
|
|
698
|
+
stdin?: number[];
|
|
699
|
+
}
|
|
700
|
+
export interface ExecResult {
|
|
701
|
+
exit_code: number | null;
|
|
702
|
+
timed_out: boolean;
|
|
703
|
+
stdout: Uint8Array;
|
|
704
|
+
stderr: Uint8Array;
|
|
705
|
+
}
|
|
706
|
+
export interface ExecStreamChunk {
|
|
707
|
+
stdout: Uint8Array;
|
|
708
|
+
stderr: Uint8Array;
|
|
709
|
+
done: boolean;
|
|
710
|
+
exit_code: number | null;
|
|
711
|
+
timed_out: boolean;
|
|
712
|
+
}
|
|
713
|
+
export type ProcessState = "starting" | "running" | "succeeded" | "failed" | "timed_out" | "cancelled" | "interrupted";
|
|
714
|
+
export interface ProcessStartInput {
|
|
715
|
+
command: string;
|
|
716
|
+
args: string[];
|
|
717
|
+
cwd: string;
|
|
718
|
+
env: Array<[string, string]>;
|
|
719
|
+
timeout_ms: number;
|
|
720
|
+
output_limit_bytes: number;
|
|
721
|
+
/** Optional payload delivered once and followed by EOF. Maximum 512 KiB. */
|
|
722
|
+
stdin?: Uint8Array;
|
|
723
|
+
}
|
|
724
|
+
export interface ProcessRecord {
|
|
725
|
+
id: string;
|
|
726
|
+
sandbox_id: string;
|
|
727
|
+
state: ProcessState;
|
|
728
|
+
exit_code: number | null;
|
|
729
|
+
stdout_bytes: number;
|
|
730
|
+
stderr_bytes: number;
|
|
731
|
+
stdout_truncated: boolean;
|
|
732
|
+
stderr_truncated: boolean;
|
|
733
|
+
failure: string | null;
|
|
734
|
+
created_at: number;
|
|
735
|
+
updated_at: number;
|
|
736
|
+
terminal_at: number | null;
|
|
737
|
+
}
|
|
738
|
+
export interface ProcessLogs {
|
|
739
|
+
stdout: Uint8Array;
|
|
740
|
+
stderr: Uint8Array;
|
|
741
|
+
next_stdout_offset: number;
|
|
742
|
+
next_stderr_offset: number;
|
|
743
|
+
stdout_truncated: boolean;
|
|
744
|
+
stderr_truncated: boolean;
|
|
745
|
+
state: ProcessState;
|
|
746
|
+
}
|
|
747
|
+
export interface ProcessLogsResponse {
|
|
748
|
+
process: ProcessRecord;
|
|
749
|
+
logs: ProcessLogs;
|
|
750
|
+
}
|
|
751
|
+
export interface FileReadChunk {
|
|
752
|
+
data: Uint8Array;
|
|
753
|
+
offset: number;
|
|
754
|
+
eof: boolean;
|
|
755
|
+
}
|
|
756
|
+
export interface ReadFileInput {
|
|
757
|
+
path: string;
|
|
758
|
+
offset?: number;
|
|
759
|
+
/** Total bytes to read. Omit to read through EOF. */
|
|
760
|
+
length?: number;
|
|
761
|
+
/** Per-request chunk size; defaults to the gateway's maximum. */
|
|
762
|
+
chunk_size?: number;
|
|
763
|
+
}
|
|
764
|
+
export interface WriteFileInput {
|
|
765
|
+
path: string;
|
|
766
|
+
data: Uint8Array;
|
|
767
|
+
offset?: number;
|
|
768
|
+
truncate?: boolean;
|
|
769
|
+
}
|
|
770
|
+
export interface WriteFileStreamInput {
|
|
771
|
+
path: string;
|
|
772
|
+
data: AsyncIterable<Uint8Array>;
|
|
773
|
+
offset?: number;
|
|
774
|
+
truncate?: boolean;
|
|
775
|
+
}
|
|
776
|
+
export interface MintTokenInput {
|
|
777
|
+
sandbox?: string;
|
|
778
|
+
ops: CapabilityOps;
|
|
779
|
+
ttl_secs: number;
|
|
780
|
+
}
|
|
781
|
+
/** JSON representation emitted by serde for Rust's SystemTime. */
|
|
782
|
+
export interface SystemTimeValue {
|
|
783
|
+
secs_since_epoch: number;
|
|
784
|
+
nanos_since_epoch: number;
|
|
785
|
+
}
|
|
786
|
+
export interface MintTokenResponse {
|
|
787
|
+
token: string;
|
|
788
|
+
token_id: string;
|
|
789
|
+
/** Capability timestamps are epoch milliseconds. */
|
|
790
|
+
expires_at: number;
|
|
791
|
+
renewable_until: number;
|
|
792
|
+
}
|
|
793
|
+
export interface RenewTokenResponse {
|
|
794
|
+
expires_at: number;
|
|
795
|
+
}
|
|
796
|
+
export declare class BlackcatApiError extends Error {
|
|
797
|
+
readonly status: number;
|
|
798
|
+
/** Stable gateway error code when one is provided. */
|
|
799
|
+
readonly code?: string;
|
|
800
|
+
readonly retryAfter?: number;
|
|
801
|
+
readonly requestId?: string;
|
|
802
|
+
readonly details: unknown;
|
|
803
|
+
constructor(status: number, message: string, details: unknown, retryAfter?: number, requestId?: string);
|
|
804
|
+
}
|
|
805
|
+
declare const MAX_FILE_CHUNK: number;
|
|
806
|
+
declare const MAX_FILE_READ_CHUNK = 1000000;
|
|
807
|
+
declare const MAX_PROCESS_STDIN: number;
|
|
808
|
+
declare const MAX_EXEC_STDIN: number;
|
|
809
|
+
export declare function epochMillis(value: unknown, field: string): number;
|
|
810
|
+
export declare function parseCreateSandboxResponse(value: unknown, requestId: string): CreateSandboxResponse;
|
|
811
|
+
export declare function parseSandboxBatch(value: unknown, requestId: string): SandboxBatchResponse;
|
|
812
|
+
export declare function assertSecureBlackcatGatewayUrl(baseUrl: string): void;
|
|
813
|
+
declare class GatewayClient {
|
|
814
|
+
readonly baseUrl: string;
|
|
815
|
+
readonly apiKey?: string;
|
|
816
|
+
token?: string;
|
|
817
|
+
tokenId?: string;
|
|
818
|
+
tokenExpiresAt?: number;
|
|
819
|
+
readonly renewCapability?: () => Promise<MintTokenResponse>;
|
|
820
|
+
readonly caPem?: string;
|
|
821
|
+
readonly fetcher: FetchFunction;
|
|
822
|
+
private renewalInFlight?;
|
|
823
|
+
constructor(options: BlackcatClientOptions);
|
|
824
|
+
credential(kind: "apiKey" | "token" | "either"): string;
|
|
825
|
+
json<T>(method: string, path: string, body?: unknown, kind?: "apiKey" | "token" | "either", timeout?: false, signal?: AbortSignal, extraHeaders?: HeadersInit): Promise<T>;
|
|
826
|
+
raw(method: string, path: string, body?: BodyInit, kind?: "apiKey" | "token" | "either", contentType?: string, timeout?: false, signal?: AbortSignal): Promise<Response>;
|
|
827
|
+
renewIfNeeded(signal?: AbortSignal): Promise<void>;
|
|
828
|
+
stream(method: string, path: string, body?: BodyInit, kind?: "apiKey" | "token" | "either", contentType?: string, renew?: boolean, signal?: AbortSignal): AsyncGenerator<Uint8Array>;
|
|
829
|
+
}
|
|
830
|
+
declare class TemplatesResource {
|
|
831
|
+
private readonly client;
|
|
832
|
+
constructor(client: GatewayClient);
|
|
833
|
+
create(input: CreateTemplateInput): Promise<CreateTemplateResponse>;
|
|
834
|
+
list(): Promise<Template[]>;
|
|
835
|
+
/**
|
|
836
|
+
* Upload a `docker save` / OCI archive as a build input. The image is named
|
|
837
|
+
* by its own content, so re-uploading the same bytes is free, and a build
|
|
838
|
+
* that names it needs no registry credentials anywhere.
|
|
839
|
+
*/
|
|
840
|
+
uploadImage(archive: BodyInit, options?: {
|
|
841
|
+
signal?: AbortSignal;
|
|
842
|
+
}): Promise<UploadedImage>;
|
|
843
|
+
activate(id: string): Promise<{
|
|
844
|
+
id: string;
|
|
845
|
+
name: string;
|
|
846
|
+
version: number;
|
|
847
|
+
}>;
|
|
848
|
+
}
|
|
849
|
+
declare class TemplateResourcesResource {
|
|
850
|
+
private readonly client;
|
|
851
|
+
constructor(client: GatewayClient);
|
|
852
|
+
get(name: string): Promise<TemplateResource>;
|
|
853
|
+
configure(name: string, input: ConfigureTemplateResourceInput): Promise<TemplateResource>;
|
|
854
|
+
delete(name: string): Promise<void>;
|
|
855
|
+
}
|
|
856
|
+
declare class TemplateBuildsResource {
|
|
857
|
+
private readonly client;
|
|
858
|
+
constructor(client: GatewayClient);
|
|
859
|
+
create(input: CreateTemplateBuildInput): Promise<{
|
|
860
|
+
build: TemplateBuildRecord;
|
|
861
|
+
}>;
|
|
862
|
+
get(id: string): Promise<{
|
|
863
|
+
build: TemplateBuildRecord;
|
|
864
|
+
}>;
|
|
865
|
+
retry(id: string): Promise<{
|
|
866
|
+
build: TemplateBuildRecord;
|
|
867
|
+
}>;
|
|
868
|
+
}
|
|
869
|
+
declare class TokensResource {
|
|
870
|
+
private readonly client;
|
|
871
|
+
constructor(client: GatewayClient);
|
|
872
|
+
mint(input: MintTokenInput): Promise<MintTokenResponse>;
|
|
873
|
+
renew(id: string): Promise<RenewTokenResponse>;
|
|
874
|
+
revoke(id: string): Promise<void>;
|
|
875
|
+
}
|
|
876
|
+
declare class FleetResource {
|
|
877
|
+
private readonly client;
|
|
878
|
+
constructor(client: GatewayClient);
|
|
879
|
+
/** List the nodes and this tenant's residents/snapshots visible to INSPECT. */
|
|
880
|
+
list(): Promise<FleetResponse>;
|
|
881
|
+
}
|
|
882
|
+
declare class ReservationsResource {
|
|
883
|
+
private readonly client;
|
|
884
|
+
constructor(client: GatewayClient);
|
|
885
|
+
/** Reserve capacity for a burst of sandboxes of one template/shape. */
|
|
886
|
+
create(input: CreateCapacityReservationInput): Promise<CapacityReservation>;
|
|
887
|
+
get(id: string): Promise<CapacityReservation>;
|
|
888
|
+
/** Release early. Unclaimed slots free immediately; bound nodes then fall
|
|
889
|
+
* back to the normal empty-worker scale-in grace period. */
|
|
890
|
+
release(id: string): Promise<CapacityReservation>;
|
|
891
|
+
renew(id: string, input: {
|
|
892
|
+
ttl_secs: number;
|
|
893
|
+
idempotency_key: string;
|
|
894
|
+
}): Promise<CapacityReservation>;
|
|
895
|
+
}
|
|
896
|
+
declare class SandboxesResource {
|
|
897
|
+
private readonly client;
|
|
898
|
+
constructor(client: GatewayClient);
|
|
899
|
+
/** List all of this tenant's non-destroyed sandboxes, including hibernated ones. */
|
|
900
|
+
list(): Promise<Sandbox[]>;
|
|
901
|
+
/**
|
|
902
|
+
* Acquire once under a caller-owned request ID. Repeat the exact request to
|
|
903
|
+
* recover a lost response. A pending receipt is not an executable sandbox;
|
|
904
|
+
* no automatic retry, new ID, or replacement allocation is synthesized here.
|
|
905
|
+
*/
|
|
906
|
+
create(input: CreateSandboxInput, options?: WaitOptions): Promise<CreateSandboxResponse>;
|
|
907
|
+
/**
|
|
908
|
+
* Create `count` identical sandboxes under one request ID. Resolves as soon
|
|
909
|
+
* as the whole batch is durably recorded; nothing waits on a restore.
|
|
910
|
+
* Repeat the exact request to recover a lost response: member IDs are
|
|
911
|
+
* stable. Poll `batch` for counts by state, or a member with `acquisition`.
|
|
912
|
+
*/
|
|
913
|
+
createBatch(input: CreateSandboxBatchInput): Promise<SandboxBatchResponse>;
|
|
914
|
+
/** Counts by state and every member of a batch created with `createBatch`. */
|
|
915
|
+
batch(requestId: string): Promise<SandboxBatchResponse>;
|
|
916
|
+
acquisition(requestId: string, options?: WaitOptions): Promise<CreateSandboxResponse>;
|
|
917
|
+
/**
|
|
918
|
+
* Fence an acquisition request ID before its create is recorded, or destroy
|
|
919
|
+
* the allocation when the create has already won.
|
|
920
|
+
*/
|
|
921
|
+
cancelAcquisition(requestId: string): Promise<CreateSandboxResponse>;
|
|
922
|
+
get(id: string): Promise<Sandbox>;
|
|
923
|
+
/** Extend the soft expiry without moving the immutable hard deadline. */
|
|
924
|
+
renew(id: string, ttl_secs: number): Promise<RenewSandboxResponse>;
|
|
925
|
+
/**
|
|
926
|
+
* Authorize `public_key` on a running sandbox for `ttl_secs`, whether or not
|
|
927
|
+
* it was created with a key. Needs the sandbox-scoped exec capability. Connect
|
|
928
|
+
* with `CONNECT /v1/sandboxes/{id}/ssh`, or let `blackcat sandbox ssh` do it.
|
|
929
|
+
*/
|
|
930
|
+
grantSshKey(id: string, input: GrantSshKeyInput): Promise<SshKeyGrant>;
|
|
931
|
+
/**
|
|
932
|
+
* Describe a tunnel to `port` (any TCP port the sandbox listens on, not only
|
|
933
|
+
* a declared preview port), at `path` on that port. Needs the sandbox-scoped
|
|
934
|
+
* exec capability, which is checked once when a connection opens: an open
|
|
935
|
+
* WebSocket outlives it. Renews a nearly expired capability first, so call
|
|
936
|
+
* this again for each new connection. A `CONNECT` to `url` instead returns
|
|
937
|
+
* a raw byte stream, as `blackcat sandbox port-forward` uses.
|
|
938
|
+
*/
|
|
939
|
+
portConnection(id: string, port: number, path?: string): Promise<PortConnection>;
|
|
940
|
+
exec(id: string, input: ExecInput): Promise<ExecResult>;
|
|
941
|
+
execStream(id: string, input: ExecInput): AsyncGenerator<ExecStreamChunk>;
|
|
942
|
+
startProcess(id: string, requestId: string, input: ProcessStartInput): Promise<ProcessRecord>;
|
|
943
|
+
getProcess(id: string, processId: string): Promise<ProcessRecord>;
|
|
944
|
+
processLogs(id: string, processId: string, input?: number | {
|
|
945
|
+
stdout_offset?: number;
|
|
946
|
+
stderr_offset?: number;
|
|
947
|
+
max_bytes?: number;
|
|
948
|
+
}): Promise<ProcessLogsResponse>;
|
|
949
|
+
cancelProcess(id: string, processId: string): Promise<ProcessRecord>;
|
|
950
|
+
prepare(id: string, input: {
|
|
951
|
+
parameters: Record<string, string>;
|
|
952
|
+
secrets: Record<string, string>;
|
|
953
|
+
}, idempotencyKey: string): Promise<PrepareSandboxResponse>;
|
|
954
|
+
logs(id: string): AsyncGenerator<Uint8Array>;
|
|
955
|
+
readFileChunk(id: string, input: {
|
|
956
|
+
path: string;
|
|
957
|
+
offset: number;
|
|
958
|
+
length: number;
|
|
959
|
+
}): Promise<FileReadChunk>;
|
|
960
|
+
readFileStream(id: string, input: ReadFileInput): AsyncGenerator<FileReadChunk>;
|
|
961
|
+
readFile(id: string, input: ReadFileInput): Promise<Uint8Array>;
|
|
962
|
+
writeFileChunk(id: string, input: {
|
|
963
|
+
path: string;
|
|
964
|
+
offset: number;
|
|
965
|
+
data: Uint8Array;
|
|
966
|
+
truncate: boolean;
|
|
967
|
+
}): Promise<void>;
|
|
968
|
+
writeFileStream(id: string, input: WriteFileStreamInput): Promise<number>;
|
|
969
|
+
writeFile(id: string, input: WriteFileInput): Promise<number>;
|
|
970
|
+
/**
|
|
971
|
+
* Capture a restore point. The sandbox keeps running, and the returned
|
|
972
|
+
* template version is what it restarts from.
|
|
973
|
+
*/
|
|
974
|
+
snapshot(id: string): Promise<{
|
|
975
|
+
template: string;
|
|
976
|
+
}>;
|
|
977
|
+
/**
|
|
978
|
+
* Pause a running sandbox's VM in memory: processes, memory and network
|
|
979
|
+
* state are kept, nothing is captured, and `resume` continues it where it
|
|
980
|
+
* stopped. Memory stays resident and charged (`hibernate` frees it); the CPU
|
|
981
|
+
* entitlement is released. Idempotent. Requests that need the guest are
|
|
982
|
+
* refused while it is paused.
|
|
983
|
+
*/
|
|
984
|
+
pause(id: string): Promise<void>;
|
|
985
|
+
hibernate(id: string, input?: {
|
|
986
|
+
mode: HibernateMode;
|
|
987
|
+
}): Promise<void>;
|
|
988
|
+
/**
|
|
989
|
+
* Change a running sandbox's memory (live, within its checkpoint's hotplug
|
|
990
|
+
* region) and CPU entitlement; processes keep running. A `vcpus` different
|
|
991
|
+
* from the sandbox's is a cold reboot instead (`cold: true`). Repeating the
|
|
992
|
+
* same `request_id` replays the result.
|
|
993
|
+
*/
|
|
994
|
+
resize(id: string, input: ResizeSandboxInput): Promise<SandboxResize>;
|
|
995
|
+
/** Continue a paused sandbox, or restore a hibernated one. */
|
|
996
|
+
resume(id: string): Promise<void>;
|
|
997
|
+
/**
|
|
998
|
+
* Destroy a sandbox. Resolves when its VM is dead and the node has released
|
|
999
|
+
* its capacity, not when the node has finished removing its network device,
|
|
1000
|
+
* jail and cgroup: that follows in the background, usually within a second.
|
|
1001
|
+
* Destroying a sandbox that is already destroyed succeeds.
|
|
1002
|
+
*/
|
|
1003
|
+
destroy(id: string): Promise<void>;
|
|
1004
|
+
}
|
|
1005
|
+
export declare class BlackcatClient extends GatewayClient {
|
|
1006
|
+
readonly templates: TemplatesResource;
|
|
1007
|
+
readonly templateResources: TemplateResourcesResource;
|
|
1008
|
+
readonly templateBuilds: TemplateBuildsResource;
|
|
1009
|
+
readonly sandboxes: SandboxesResource;
|
|
1010
|
+
readonly tokens: TokensResource;
|
|
1011
|
+
readonly fleet: FleetResource;
|
|
1012
|
+
readonly reservations: ReservationsResource;
|
|
1013
|
+
constructor(options: BlackcatClientOptions);
|
|
1014
|
+
/**
|
|
1015
|
+
* Create a client that uses a sandbox capability for resource operations.
|
|
1016
|
+
* Passing the complete mint response enables renewal during long streams.
|
|
1017
|
+
*/
|
|
1018
|
+
withToken(token: string | MintTokenResponse, renewCapability?: () => Promise<MintTokenResponse>): BlackcatClient;
|
|
1019
|
+
}
|
|
1020
|
+
export { MAX_EXEC_STDIN, MAX_FILE_CHUNK, MAX_FILE_CHUNK as MAX_FILE_WRITE_CHUNK, MAX_FILE_READ_CHUNK, MAX_PROCESS_STDIN, };
|