smolmachines 1.25.1 → 1.25.2
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/README.md +11 -0
- package/dist/generated/smolfleet.d.ts +227 -3
- package/dist/index.d.ts +1 -1
- package/dist/machine.d.ts +15 -2
- package/dist/machine.js +34 -2
- package/dist/transport.d.ts +13 -2
- package/dist/transport.js +86 -27
- package/dist/types.d.ts +32 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -321,6 +321,17 @@ await m.publishCacheDisk(); // v1
|
|
|
321
321
|
// Every machine created with cacheDisk: { cache: "deps" } now starts from v1.
|
|
322
322
|
```
|
|
323
323
|
|
|
324
|
+
One checkpoint can resume with many caches. Create the base machine with an
|
|
325
|
+
empty cache as a slot, which attaches it unmounted, and checkpoint it warm.
|
|
326
|
+
Each restore then mounts its own cache there, no larger than the slot and at
|
|
327
|
+
the slot's mount path:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
const base = await Machine.create({ image: "node:22", cacheDisk: { cache: "slot-20g", slot: true } });
|
|
331
|
+
const { id } = await base.checkpoint();
|
|
332
|
+
const a = await Machine.restoreCheckpoint(id, "project-a", undefined, { cacheDisk: { cache: "project-a" } });
|
|
333
|
+
```
|
|
334
|
+
|
|
324
335
|
## Building from source
|
|
325
336
|
|
|
326
337
|
This package's native core lives alongside it (Rust, `src/*.rs`) and links the
|
|
@@ -143,6 +143,26 @@ export interface paths {
|
|
|
143
143
|
patch?: never;
|
|
144
144
|
trace?: never;
|
|
145
145
|
};
|
|
146
|
+
"/v1/account/plan/checkout": {
|
|
147
|
+
parameters: {
|
|
148
|
+
query?: never;
|
|
149
|
+
header?: never;
|
|
150
|
+
path?: never;
|
|
151
|
+
cookie?: never;
|
|
152
|
+
};
|
|
153
|
+
get?: never;
|
|
154
|
+
put?: never;
|
|
155
|
+
/**
|
|
156
|
+
* Subscribe to a plan through Stripe Checkout.
|
|
157
|
+
* @description Returns a Stripe-hosted page that shows the plan and its monthly price, takes a card and collects the first month. Once Stripe confirms the payment the account moves onto the plan and keeps the card for later charges. For an account with no card on file; with one, change plan directly. Requires the `billing:write` scope.
|
|
158
|
+
*/
|
|
159
|
+
post: operations["account_plan_checkout"];
|
|
160
|
+
delete?: never;
|
|
161
|
+
options?: never;
|
|
162
|
+
head?: never;
|
|
163
|
+
patch?: never;
|
|
164
|
+
trace?: never;
|
|
165
|
+
};
|
|
146
166
|
"/v1/account/plans": {
|
|
147
167
|
parameters: {
|
|
148
168
|
query?: never;
|
|
@@ -439,6 +459,26 @@ export interface paths {
|
|
|
439
459
|
patch?: never;
|
|
440
460
|
trace?: never;
|
|
441
461
|
};
|
|
462
|
+
"/v1/billing/card": {
|
|
463
|
+
parameters: {
|
|
464
|
+
query?: never;
|
|
465
|
+
header?: never;
|
|
466
|
+
path?: never;
|
|
467
|
+
cookie?: never;
|
|
468
|
+
};
|
|
469
|
+
/**
|
|
470
|
+
* Get the card on file.
|
|
471
|
+
* @description Returns the brand, last four digits and expiry of the card the account's plan fee and auto top-ups are charged to. Requires the `usage:read` or `machine:read` scope.
|
|
472
|
+
*/
|
|
473
|
+
get: operations["billing_card_get"];
|
|
474
|
+
put?: never;
|
|
475
|
+
post?: never;
|
|
476
|
+
delete?: never;
|
|
477
|
+
options?: never;
|
|
478
|
+
head?: never;
|
|
479
|
+
patch?: never;
|
|
480
|
+
trace?: never;
|
|
481
|
+
};
|
|
442
482
|
"/v1/billing/checkout-session": {
|
|
443
483
|
parameters: {
|
|
444
484
|
query?: never;
|
|
@@ -2323,8 +2363,7 @@ export interface components {
|
|
|
2323
2363
|
scopes: string[];
|
|
2324
2364
|
};
|
|
2325
2365
|
/**
|
|
2326
|
-
* @description
|
|
2327
|
-
* Threshold/amount are the RESOLVED effective values (per-tenant override, else
|
|
2366
|
+
* @description Threshold/amount are the RESOLVED effective values (per-tenant override, else
|
|
2328
2367
|
* fleet default), so the UI can render exactly what will happen.
|
|
2329
2368
|
*/
|
|
2330
2369
|
AutoRechargeSettings: {
|
|
@@ -2388,6 +2427,21 @@ export interface components {
|
|
|
2388
2427
|
*/
|
|
2389
2428
|
version: number;
|
|
2390
2429
|
};
|
|
2430
|
+
/**
|
|
2431
|
+
* @description Opt-in saved-card auto-recharge settings (`GET`/`POST /v1/billing/auto-recharge`).
|
|
2432
|
+
* The card on file: what the account's plan fee and auto top-ups are charged
|
|
2433
|
+
* to, as Stripe describes it. Never the number itself.
|
|
2434
|
+
*/
|
|
2435
|
+
CardOnFile: {
|
|
2436
|
+
/** @description Card network, e.g. `visa`, `mastercard`, `amex`. */
|
|
2437
|
+
brand: string;
|
|
2438
|
+
/** Format: int32 */
|
|
2439
|
+
expMonth: number;
|
|
2440
|
+
/** Format: int32 */
|
|
2441
|
+
expYear: number;
|
|
2442
|
+
/** @description Last four digits. */
|
|
2443
|
+
last4: string;
|
|
2444
|
+
};
|
|
2391
2445
|
/**
|
|
2392
2446
|
* @description Ask to move this tenant onto a different plan. Only plans whose
|
|
2393
2447
|
* `self_serve` flag is set may be chosen; operator tiers are assigned by an
|
|
@@ -2982,7 +3036,11 @@ export interface components {
|
|
|
2982
3036
|
/** @description The cache disk a machine runs with. */
|
|
2983
3037
|
MachineCacheDisk: {
|
|
2984
3038
|
cacheDiskId: string;
|
|
3039
|
+
/** @description Put in a cache slot by a restore, which needs an engine that serves slots. */
|
|
3040
|
+
intoSlot?: boolean;
|
|
2985
3041
|
mountPath: string;
|
|
3042
|
+
/** @description Attached unmounted, as a slot a restore can put another cache in. */
|
|
3043
|
+
slot?: boolean;
|
|
2986
3044
|
/** Format: int32 */
|
|
2987
3045
|
version: number;
|
|
2988
3046
|
};
|
|
@@ -2998,6 +3056,12 @@ export interface components {
|
|
|
2998
3056
|
* left out.
|
|
2999
3057
|
*/
|
|
3000
3058
|
mountPath?: string | null;
|
|
3059
|
+
/**
|
|
3060
|
+
* @description Attach it as a slot: left unmounted, so a checkpoint of the machine can
|
|
3061
|
+
* be restored with a different cache mounted here (`cacheDisk` on restore).
|
|
3062
|
+
* The slot's size is this cache disk's size; its contents are never read.
|
|
3063
|
+
*/
|
|
3064
|
+
slot?: boolean;
|
|
3001
3065
|
/**
|
|
3002
3066
|
* Format: int32
|
|
3003
3067
|
* @description Version to start from; the latest when left out.
|
|
@@ -3729,6 +3793,7 @@ export interface components {
|
|
|
3729
3793
|
};
|
|
3730
3794
|
/** @description Create a stopped machine from a durable portable checkpoint. */
|
|
3731
3795
|
RestorePortableCheckpointRequest: {
|
|
3796
|
+
cacheDisk?: null | components["schemas"]["MachineCacheDiskSpec"];
|
|
3732
3797
|
name: string;
|
|
3733
3798
|
};
|
|
3734
3799
|
ScaleRequest: {
|
|
@@ -4268,7 +4333,7 @@ export interface operations {
|
|
|
4268
4333
|
"application/json": components["schemas"]["ErrorResponse"];
|
|
4269
4334
|
};
|
|
4270
4335
|
};
|
|
4271
|
-
/** @description The plan's monthly fee has no saved card to bill, or the card was declined; the plan does not move. */
|
|
4336
|
+
/** @description The plan's monthly fee has no saved card to bill (subscribe through /v1/account/plan/checkout instead), or the card was declined; the plan does not move. */
|
|
4272
4337
|
402: {
|
|
4273
4338
|
headers: {
|
|
4274
4339
|
[name: string]: unknown;
|
|
@@ -4333,6 +4398,91 @@ export interface operations {
|
|
|
4333
4398
|
};
|
|
4334
4399
|
};
|
|
4335
4400
|
};
|
|
4401
|
+
account_plan_checkout: {
|
|
4402
|
+
parameters: {
|
|
4403
|
+
query?: never;
|
|
4404
|
+
header?: never;
|
|
4405
|
+
path?: never;
|
|
4406
|
+
cookie?: never;
|
|
4407
|
+
};
|
|
4408
|
+
requestBody: {
|
|
4409
|
+
content: {
|
|
4410
|
+
"application/json": components["schemas"]["ChangePlanRequest"];
|
|
4411
|
+
};
|
|
4412
|
+
};
|
|
4413
|
+
responses: {
|
|
4414
|
+
/** @description Returns {"url": <stripe-hosted url>} for a Stripe Checkout in subscription mode; the browser comes back to the console's billing page. */
|
|
4415
|
+
200: {
|
|
4416
|
+
headers: {
|
|
4417
|
+
[name: string]: unknown;
|
|
4418
|
+
};
|
|
4419
|
+
content?: never;
|
|
4420
|
+
};
|
|
4421
|
+
/** @description Missing or invalid API key */
|
|
4422
|
+
401: {
|
|
4423
|
+
headers: {
|
|
4424
|
+
[name: string]: unknown;
|
|
4425
|
+
};
|
|
4426
|
+
content: {
|
|
4427
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4428
|
+
};
|
|
4429
|
+
};
|
|
4430
|
+
/** @description Key lacks the required scope */
|
|
4431
|
+
403: {
|
|
4432
|
+
headers: {
|
|
4433
|
+
[name: string]: unknown;
|
|
4434
|
+
};
|
|
4435
|
+
content: {
|
|
4436
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4437
|
+
};
|
|
4438
|
+
};
|
|
4439
|
+
/** @description The account is already on that plan, or already pays a plan fee */
|
|
4440
|
+
409: {
|
|
4441
|
+
headers: {
|
|
4442
|
+
[name: string]: unknown;
|
|
4443
|
+
};
|
|
4444
|
+
content: {
|
|
4445
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4446
|
+
};
|
|
4447
|
+
};
|
|
4448
|
+
/** @description That plan is not self-serve, or has no monthly fee */
|
|
4449
|
+
422: {
|
|
4450
|
+
headers: {
|
|
4451
|
+
[name: string]: unknown;
|
|
4452
|
+
};
|
|
4453
|
+
content: {
|
|
4454
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4455
|
+
};
|
|
4456
|
+
};
|
|
4457
|
+
/** @description Rate limited; retry after the Retry-After header */
|
|
4458
|
+
429: {
|
|
4459
|
+
headers: {
|
|
4460
|
+
[name: string]: unknown;
|
|
4461
|
+
};
|
|
4462
|
+
content: {
|
|
4463
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4464
|
+
};
|
|
4465
|
+
};
|
|
4466
|
+
/** @description Internal error */
|
|
4467
|
+
500: {
|
|
4468
|
+
headers: {
|
|
4469
|
+
[name: string]: unknown;
|
|
4470
|
+
};
|
|
4471
|
+
content: {
|
|
4472
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4473
|
+
};
|
|
4474
|
+
};
|
|
4475
|
+
/** @description Paid plans are not enabled on this deployment */
|
|
4476
|
+
503: {
|
|
4477
|
+
headers: {
|
|
4478
|
+
[name: string]: unknown;
|
|
4479
|
+
};
|
|
4480
|
+
content: {
|
|
4481
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
4482
|
+
};
|
|
4483
|
+
};
|
|
4484
|
+
};
|
|
4485
|
+
};
|
|
4336
4486
|
account_plans_list: {
|
|
4337
4487
|
parameters: {
|
|
4338
4488
|
query?: never;
|
|
@@ -5549,6 +5699,80 @@ export interface operations {
|
|
|
5549
5699
|
};
|
|
5550
5700
|
};
|
|
5551
5701
|
};
|
|
5702
|
+
billing_card_get: {
|
|
5703
|
+
parameters: {
|
|
5704
|
+
query?: never;
|
|
5705
|
+
header?: never;
|
|
5706
|
+
path?: never;
|
|
5707
|
+
cookie?: never;
|
|
5708
|
+
};
|
|
5709
|
+
requestBody?: never;
|
|
5710
|
+
responses: {
|
|
5711
|
+
/** @description The card on file */
|
|
5712
|
+
200: {
|
|
5713
|
+
headers: {
|
|
5714
|
+
[name: string]: unknown;
|
|
5715
|
+
};
|
|
5716
|
+
content: {
|
|
5717
|
+
"application/json": components["schemas"]["CardOnFile"];
|
|
5718
|
+
};
|
|
5719
|
+
};
|
|
5720
|
+
/** @description Missing or invalid API key */
|
|
5721
|
+
401: {
|
|
5722
|
+
headers: {
|
|
5723
|
+
[name: string]: unknown;
|
|
5724
|
+
};
|
|
5725
|
+
content: {
|
|
5726
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5727
|
+
};
|
|
5728
|
+
};
|
|
5729
|
+
/** @description Key lacks the required scope */
|
|
5730
|
+
403: {
|
|
5731
|
+
headers: {
|
|
5732
|
+
[name: string]: unknown;
|
|
5733
|
+
};
|
|
5734
|
+
content: {
|
|
5735
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5736
|
+
};
|
|
5737
|
+
};
|
|
5738
|
+
/** @description No card on file, or card payments are not enabled */
|
|
5739
|
+
404: {
|
|
5740
|
+
headers: {
|
|
5741
|
+
[name: string]: unknown;
|
|
5742
|
+
};
|
|
5743
|
+
content: {
|
|
5744
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5745
|
+
};
|
|
5746
|
+
};
|
|
5747
|
+
/** @description Rate limited; retry after the Retry-After header */
|
|
5748
|
+
429: {
|
|
5749
|
+
headers: {
|
|
5750
|
+
[name: string]: unknown;
|
|
5751
|
+
};
|
|
5752
|
+
content: {
|
|
5753
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5754
|
+
};
|
|
5755
|
+
};
|
|
5756
|
+
/** @description Internal error */
|
|
5757
|
+
500: {
|
|
5758
|
+
headers: {
|
|
5759
|
+
[name: string]: unknown;
|
|
5760
|
+
};
|
|
5761
|
+
content: {
|
|
5762
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5763
|
+
};
|
|
5764
|
+
};
|
|
5765
|
+
/** @description The payment provider did not answer */
|
|
5766
|
+
502: {
|
|
5767
|
+
headers: {
|
|
5768
|
+
[name: string]: unknown;
|
|
5769
|
+
};
|
|
5770
|
+
content: {
|
|
5771
|
+
"application/json": components["schemas"]["ErrorResponse"];
|
|
5772
|
+
};
|
|
5773
|
+
};
|
|
5774
|
+
};
|
|
5775
|
+
};
|
|
5552
5776
|
billing_checkout_session: {
|
|
5553
5777
|
parameters: {
|
|
5554
5778
|
query?: never;
|
package/dist/index.d.ts
CHANGED
|
@@ -22,4 +22,4 @@ export type { BuildRolloutJobOptions, EnsureRolloutExecutorOptions, RolloutBatch
|
|
|
22
22
|
export { SmolError, NotSupportedError, InvalidConfigError, ExecutionError } from './errors';
|
|
23
23
|
export type { CloudCheckpointInfo } from './transport';
|
|
24
24
|
export type { LocalAvailability, LocalUnavailableCode } from './availability';
|
|
25
|
-
export type { MachineConfig, CredentialSpec, CacheDiskRef, CacheDiskInfo, CacheDiskVersion, CreateCacheDiskOptions, PublishedCacheDisk, NetworkPolicy, RestoreCheckpointOptions, MachineState, ResourceSpec, MountSpec, PortSpec, BranchOptions, BranchBatchOptions, CheckpointOptions, ResizeOptions, MachineResources, ForkOptions, ForkBatchOptions, AssignOptions, ExecOptions, ExecResult, MachineUsageReport, ShareLink, PortableCheckpointInfo, MachineUsageTotals, MachineCostBreakdown, ImageInfo, MachineSummary, ListOptions, ExecEvent, ConnectOptions, EgressInterceptor, StartOptions, WaitReadyOptions, PortEndpoint, } from './types';
|
|
25
|
+
export type { MachineConfig, CredentialSpec, CacheDiskRef, CacheDiskInfo, CacheDiskVersion, CreateCacheDiskOptions, PublishedCacheDisk, NetworkPolicy, RestoreCheckpointOptions, MachineState, ResourceSpec, MountSpec, PortSpec, BranchOptions, BranchBatchOptions, CheckpointOptions, ResizeOptions, MachineResources, ForkOptions, ForkBatchOptions, AssignOptions, ExecOptions, ExecResult, MachineUsageReport, ShareLink, PortableCheckpointInfo, MachineUsageTotals, MachineCostBreakdown, ImageInfo, MachineSummary, ListOptions, ExecEvent, ConnectOptions, EgressInterceptor, StartOptions, ResumeOptions, WaitReadyOptions, PortEndpoint, } from './types';
|
package/dist/machine.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { type LocalAvailability } from "./availability";
|
|
11
11
|
import { type CloudCheckpointInfo, type Transport } from "./transport";
|
|
12
|
-
import type { CacheDiskInfo, CreateCacheDiskOptions, PublishedCacheDisk, MachineResources, ResizeOptions, AssignOptions, BranchBatchOptions, BranchOptions, CheckpointOptions, ConnectOptions, ExecEvent, ExecOptions, ExecResult, ForkBatchOptions, ForkOptions, ImageInfo, ListOptions, MachineConfig, MachineSummary, NetworkPolicy, RestoreCheckpointOptions, StartOptions, MachineUsageReport, ShareLink, PortableCheckpointInfo, PortEndpoint, PortSpec, WaitReadyOptions } from "./types";
|
|
12
|
+
import type { CacheDiskInfo, CreateCacheDiskOptions, PublishedCacheDisk, MachineResources, ResizeOptions, AssignOptions, BranchBatchOptions, BranchOptions, CheckpointOptions, ConnectOptions, ExecEvent, ExecOptions, ExecResult, ForkBatchOptions, ForkOptions, ImageInfo, ListOptions, MachineConfig, MachineSummary, NetworkPolicy, RestoreCheckpointOptions, StartOptions, ResumeOptions, MachineUsageReport, ShareLink, PortableCheckpointInfo, PortEndpoint, PortSpec, WaitReadyOptions } from "./types";
|
|
13
13
|
export declare class Machine {
|
|
14
14
|
private readonly transport;
|
|
15
15
|
private constructor();
|
|
@@ -50,6 +50,10 @@ export declare class Machine {
|
|
|
50
50
|
* @param options `labels`: keep machines carrying every one of these
|
|
51
51
|
*/
|
|
52
52
|
static list(conn?: ConnectOptions, options?: ListOptions): Promise<MachineSummary[]>;
|
|
53
|
+
/** Check that a cloud connection works: the key is accepted and the base URL
|
|
54
|
+
* answers with a machine list. Throws otherwise, including when a proxy or
|
|
55
|
+
* a wrong URL answers with something that is not a machine list. */
|
|
56
|
+
static probe(conn?: ConnectOptions): Promise<void>;
|
|
53
57
|
/** Upload a `.checkpoint` file taken on this computer to the cloud, so it
|
|
54
58
|
* can be restored there by the returned id. Restoring the file with a cloud
|
|
55
59
|
* target uploads it for you. */
|
|
@@ -85,6 +89,15 @@ export declare class Machine {
|
|
|
85
89
|
* process launched; it does not mean the guest or workload is ready. Use
|
|
86
90
|
* `ready()` or `waitUntilReady()` before doing work. */
|
|
87
91
|
state(): Promise<string>;
|
|
92
|
+
/** The machine's full cloud record, every field the control plane returns
|
|
93
|
+
* (env, readiness, ports, …). Cloud only. */
|
|
94
|
+
info(): Promise<Record<string, unknown>>;
|
|
95
|
+
/** The last `tail` lines of the machine's console log. Cloud only. */
|
|
96
|
+
logs(tail?: number): Promise<string>;
|
|
97
|
+
/** Export this stopped machine as a `.smolmachine` in your registry
|
|
98
|
+
* namespace, re-deployable anywhere. Distinct from a portable checkpoint,
|
|
99
|
+
* which also keeps live memory. Cloud only. */
|
|
100
|
+
exportArtifact(): Promise<Record<string, unknown>>;
|
|
88
101
|
/** Whether the machine is READY to do work. `state()` becoming "started"
|
|
89
102
|
* means only that the VM process launched — the guest is still booting and
|
|
90
103
|
* is NOT yet usable. `ready` becomes true once the in-VM agent is reachable
|
|
@@ -140,7 +153,7 @@ export declare class Machine {
|
|
|
140
153
|
/** Save RAM and disk durably, then stop at that execution boundary. */
|
|
141
154
|
pause(): Promise<void>;
|
|
142
155
|
/** Resume saved execution in this machine. */
|
|
143
|
-
resume(): Promise<void>;
|
|
156
|
+
resume(options?: ResumeOptions): Promise<void>;
|
|
144
157
|
/** Boot a stopped machine with its disk state, not its previous RAM.
|
|
145
158
|
* The local handle reuses its interceptor binding or accepts a fresh one. */
|
|
146
159
|
start(options?: StartOptions): Promise<void>;
|
package/dist/machine.js
CHANGED
|
@@ -31,6 +31,7 @@ const transport_1 = require("./transport");
|
|
|
31
31
|
function makeExecResult(r) {
|
|
32
32
|
const success = r.exitCode === 0;
|
|
33
33
|
return {
|
|
34
|
+
...(r.pid !== undefined ? { pid: r.pid } : {}),
|
|
34
35
|
exitCode: r.exitCode,
|
|
35
36
|
stdout: r.stdout,
|
|
36
37
|
stderr: r.stderr,
|
|
@@ -102,6 +103,14 @@ class Machine {
|
|
|
102
103
|
static async list(conn = {}, options = {}) {
|
|
103
104
|
return (0, transport_1.listMachines)(conn, options);
|
|
104
105
|
}
|
|
106
|
+
/** Check that a cloud connection works: the key is accepted and the base URL
|
|
107
|
+
* answers with a machine list. Throws otherwise, including when a proxy or
|
|
108
|
+
* a wrong URL answers with something that is not a machine list. */
|
|
109
|
+
static async probe(conn = {}) {
|
|
110
|
+
if (conn.target === "local")
|
|
111
|
+
throw new errors_1.NotSupportedError("probe is cloud-only.");
|
|
112
|
+
await (0, transport_1.listMachines)({ ...conn, target: "cloud" });
|
|
113
|
+
}
|
|
105
114
|
/** Upload a `.checkpoint` file taken on this computer to the cloud, so it
|
|
106
115
|
* can be restored there by the returned id. Restoring the file with a cloud
|
|
107
116
|
* target uploads it for you. */
|
|
@@ -167,6 +176,27 @@ class Machine {
|
|
|
167
176
|
state() {
|
|
168
177
|
return this.transport.state();
|
|
169
178
|
}
|
|
179
|
+
/** The machine's full cloud record, every field the control plane returns
|
|
180
|
+
* (env, readiness, ports, …). Cloud only. */
|
|
181
|
+
info() {
|
|
182
|
+
if (!this.transport.info)
|
|
183
|
+
throw new errors_1.NotSupportedError("info is cloud-only.");
|
|
184
|
+
return this.transport.info();
|
|
185
|
+
}
|
|
186
|
+
/** The last `tail` lines of the machine's console log. Cloud only. */
|
|
187
|
+
logs(tail = 100) {
|
|
188
|
+
if (!this.transport.logs)
|
|
189
|
+
throw new errors_1.NotSupportedError("logs is cloud-only.");
|
|
190
|
+
return this.transport.logs(tail);
|
|
191
|
+
}
|
|
192
|
+
/** Export this stopped machine as a `.smolmachine` in your registry
|
|
193
|
+
* namespace, re-deployable anywhere. Distinct from a portable checkpoint,
|
|
194
|
+
* which also keeps live memory. Cloud only. */
|
|
195
|
+
exportArtifact() {
|
|
196
|
+
if (!this.transport.exportArtifact)
|
|
197
|
+
throw new errors_1.NotSupportedError("exportArtifact is cloud-only.");
|
|
198
|
+
return this.transport.exportArtifact();
|
|
199
|
+
}
|
|
170
200
|
/** Whether the machine is READY to do work. `state()` becoming "started"
|
|
171
201
|
* means only that the VM process launched — the guest is still booting and
|
|
172
202
|
* is NOT yet usable. `ready` becomes true once the in-VM agent is reachable
|
|
@@ -262,11 +292,13 @@ class Machine {
|
|
|
262
292
|
/** Save RAM and disk durably, then stop at that execution boundary. */
|
|
263
293
|
pause() { return this.transport.pause(); }
|
|
264
294
|
/** Resume saved execution in this machine. */
|
|
265
|
-
resume(
|
|
295
|
+
resume(options = {}) {
|
|
296
|
+
return this.transport.resume(options.waitUntilReady ?? true);
|
|
297
|
+
}
|
|
266
298
|
/** Boot a stopped machine with its disk state, not its previous RAM.
|
|
267
299
|
* The local handle reuses its interceptor binding or accepts a fresh one. */
|
|
268
300
|
start(options = {}) {
|
|
269
|
-
return this.transport.start(options.egressInterceptor);
|
|
301
|
+
return this.transport.start(options.egressInterceptor, options.waitUntilReady ?? true);
|
|
270
302
|
}
|
|
271
303
|
delete(opts) {
|
|
272
304
|
if (opts?.includeUsage)
|
package/dist/transport.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { type NativeMachineConfig } from "./native";
|
|
|
10
10
|
import type { CheckpointOptions, CacheDiskInfo, CreateCacheDiskOptions, PublishedCacheDisk, MachineResources, ResizeOptions, ConnectOptions, NetworkPolicy, RestoreCheckpointOptions, ExecEvent, ExecOptions, EgressInterceptor, AssignOptions, ForkBatchOptions, ForkOptions, ImageInfo, ListOptions, MachineConfig, MachineSummary, MachineUsageReport, ShareLink, PortableCheckpointInfo, PortEndpoint, PortSpec, WaitReadyOptions } from "./types";
|
|
11
11
|
/** Raw exec result (the ergonomic wrapper is added in machine.ts). */
|
|
12
12
|
export interface RawExec {
|
|
13
|
+
/** The detached process's pid, for a background exec. */
|
|
14
|
+
pid?: number;
|
|
13
15
|
exitCode: number;
|
|
14
16
|
stdout: string;
|
|
15
17
|
stderr: string;
|
|
@@ -23,6 +25,12 @@ export interface RawExec {
|
|
|
23
25
|
}
|
|
24
26
|
export interface Transport {
|
|
25
27
|
readonly name: string;
|
|
28
|
+
/** Cloud only: the machine's full record as the control plane returns it. */
|
|
29
|
+
info?(): Promise<Record<string, unknown>>;
|
|
30
|
+
/** Cloud only: the last `tail` lines of the machine's console log. */
|
|
31
|
+
logs?(tail: number): Promise<string>;
|
|
32
|
+
/** Cloud only: export the stopped machine as a `.smolmachine` in the registry. */
|
|
33
|
+
exportArtifact?(): Promise<Record<string, unknown>>;
|
|
26
34
|
state(): Promise<string>;
|
|
27
35
|
ready(): Promise<boolean>;
|
|
28
36
|
readyAt(): Promise<string | null>;
|
|
@@ -41,8 +49,8 @@ export interface Transport {
|
|
|
41
49
|
setNetworkPolicy(policy: NetworkPolicy): Promise<void>;
|
|
42
50
|
stop(): Promise<void>;
|
|
43
51
|
pause(): Promise<void>;
|
|
44
|
-
resume(): Promise<void>;
|
|
45
|
-
start(interceptor?: EgressInterceptor): Promise<void>;
|
|
52
|
+
resume(waitUntilReady?: boolean): Promise<void>;
|
|
53
|
+
start(interceptor?: EgressInterceptor, waitUntilReady?: boolean): Promise<void>;
|
|
46
54
|
delete(): Promise<void>;
|
|
47
55
|
/** Cloud only: delete and return the settled usage + cost in one call. */
|
|
48
56
|
deleteWithUsage(): Promise<MachineUsageReport>;
|
|
@@ -110,6 +118,9 @@ export declare function toNativeConfig(name: string, config: MachineConfig): Nat
|
|
|
110
118
|
interface CloudConn {
|
|
111
119
|
baseUrl: string;
|
|
112
120
|
apiKey: string;
|
|
121
|
+
/** The caller's `fetch` (ConnectOptions.fetch), when it supplied one. */
|
|
122
|
+
fetch?: typeof fetch;
|
|
123
|
+
readResponseBytes?: (response: Response) => Promise<Uint8Array>;
|
|
113
124
|
}
|
|
114
125
|
/** The sentence a person should read from a failed response's body. The
|
|
115
126
|
* control plane answers errors as RFC 9457 problem+json, whose `detail` is the
|
package/dist/transport.js
CHANGED
|
@@ -526,7 +526,7 @@ class LocalTransport {
|
|
|
526
526
|
}
|
|
527
527
|
liveLocal.delete(this);
|
|
528
528
|
}
|
|
529
|
-
async resume() {
|
|
529
|
+
async resume(waitUntilReady = true) {
|
|
530
530
|
try {
|
|
531
531
|
await this.inner.resume();
|
|
532
532
|
}
|
|
@@ -535,9 +535,10 @@ class LocalTransport {
|
|
|
535
535
|
}
|
|
536
536
|
if (this.cleanupOnExit)
|
|
537
537
|
liveLocal.add(this);
|
|
538
|
-
|
|
538
|
+
if (waitUntilReady)
|
|
539
|
+
await this.waitUntilReady();
|
|
539
540
|
}
|
|
540
|
-
async start(interceptor) {
|
|
541
|
+
async start(interceptor, waitUntilReady = true) {
|
|
541
542
|
try {
|
|
542
543
|
const binding = interceptor ?? this.interceptor;
|
|
543
544
|
if (binding)
|
|
@@ -552,7 +553,8 @@ class LocalTransport {
|
|
|
552
553
|
}
|
|
553
554
|
if (this.cleanupOnExit)
|
|
554
555
|
liveLocal.add(this);
|
|
555
|
-
|
|
556
|
+
if (waitUntilReady)
|
|
557
|
+
await this.waitUntilReady();
|
|
556
558
|
}
|
|
557
559
|
async delete() {
|
|
558
560
|
liveLocal.delete(this);
|
|
@@ -685,6 +687,15 @@ class LocalTransport {
|
|
|
685
687
|
throw new errors_1.NotSupportedError("getLease() is a cloud-only lease feature.");
|
|
686
688
|
}
|
|
687
689
|
}
|
|
690
|
+
/** A cloud connection carrying the caller's transport hooks. */
|
|
691
|
+
function cloudConnOf(conn, baseUrl, apiKey) {
|
|
692
|
+
return {
|
|
693
|
+
baseUrl,
|
|
694
|
+
apiKey,
|
|
695
|
+
...(conn.fetch ? { fetch: conn.fetch } : {}),
|
|
696
|
+
...(conn.readResponseBytes ? { readResponseBytes: conn.readResponseBytes } : {}),
|
|
697
|
+
};
|
|
698
|
+
}
|
|
688
699
|
const DEFAULT_CLOUD_URL = "https://api.smolmachines.com";
|
|
689
700
|
/** Default per-request timeout for cloud calls (ms). Override via opts.timeoutMs. */
|
|
690
701
|
const CLOUD_TIMEOUT_MS = 30_000;
|
|
@@ -750,7 +761,7 @@ async function cloudFetch(conn, method, path, opts = {}) {
|
|
|
750
761
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
751
762
|
let res;
|
|
752
763
|
try {
|
|
753
|
-
res = await fetch(`${conn.baseUrl}${path}`, {
|
|
764
|
+
res = await (conn.fetch ?? fetch)(`${conn.baseUrl}${path}`, {
|
|
754
765
|
method,
|
|
755
766
|
headers,
|
|
756
767
|
body: body ?? null,
|
|
@@ -763,6 +774,9 @@ async function cloudFetch(conn, method, path, opts = {}) {
|
|
|
763
774
|
if (e.name === "AbortError") {
|
|
764
775
|
throw new errors_1.SmolError("TIMEOUT", `cloud ${method} ${path} timed out after ${opts.timeoutMs ?? CLOUD_TIMEOUT_MS}ms`);
|
|
765
776
|
}
|
|
777
|
+
// A caller's own fetch owns its failure modes (retries, typed errors).
|
|
778
|
+
if (conn.fetch)
|
|
779
|
+
throw e;
|
|
766
780
|
throw new errors_1.SmolError("CONNECTION", `cloud request failed: ${e.message}`);
|
|
767
781
|
}
|
|
768
782
|
finally {
|
|
@@ -781,8 +795,15 @@ async function cloudFetch(conn, method, path, opts = {}) {
|
|
|
781
795
|
? "UNAUTHORIZED"
|
|
782
796
|
: "SMOLVM_ERROR", `cloud ${method} ${path} → ${res.status}${text ? `: ${text}` : ""}${rid ? ` [request id: ${rid}]` : ""}`);
|
|
783
797
|
}
|
|
784
|
-
if (opts.accept === "
|
|
798
|
+
if (opts.accept === "text")
|
|
799
|
+
return (await res.text());
|
|
800
|
+
if (opts.accept === "bytes") {
|
|
801
|
+
if (conn.readResponseBytes) {
|
|
802
|
+
const bytes = await conn.readResponseBytes(res);
|
|
803
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
804
|
+
}
|
|
785
805
|
return Buffer.from(await res.arrayBuffer());
|
|
806
|
+
}
|
|
786
807
|
if (res.status === 204)
|
|
787
808
|
return undefined;
|
|
788
809
|
const ct = res.headers.get("content-type") ?? "";
|
|
@@ -947,6 +968,7 @@ class CloudTransport {
|
|
|
947
968
|
cwd: opts?.workdir ?? null,
|
|
948
969
|
timeoutSeconds: opts?.timeout ?? null,
|
|
949
970
|
...(opts?.user !== undefined ? { user: opts.user } : {}),
|
|
971
|
+
...(opts?.background ? { background: true } : {}),
|
|
950
972
|
};
|
|
951
973
|
// The command may legitimately run far longer than the default cloud
|
|
952
974
|
// timeout, so size the request abort timeout off the request's own timeout
|
|
@@ -971,7 +993,10 @@ class CloudTransport {
|
|
|
971
993
|
// `stdoutB64`/`stderrB64` are byte-exact and untruncated; the generated
|
|
972
994
|
// schema type may lag the server, so read them off the raw object.
|
|
973
995
|
const raw = r;
|
|
996
|
+
// A detached command reports its pid as `pid=<n>` on stdout.
|
|
997
|
+
const pidMatch = opts?.background ? /^pid=(\d+)/m.exec(stdout) : null;
|
|
974
998
|
return {
|
|
999
|
+
...(pidMatch ? { pid: Number(pidMatch[1]) } : {}),
|
|
975
1000
|
exitCode: r.exitCode ?? 0,
|
|
976
1001
|
stdout,
|
|
977
1002
|
stderr,
|
|
@@ -1002,7 +1027,7 @@ class CloudTransport {
|
|
|
1002
1027
|
};
|
|
1003
1028
|
let res;
|
|
1004
1029
|
try {
|
|
1005
|
-
res = await fetch(`${this.conn.baseUrl}/v1/machines/${this.id}/exec/stream`, {
|
|
1030
|
+
res = await (this.conn.fetch ?? fetch)(`${this.conn.baseUrl}/v1/machines/${this.id}/exec/stream`, {
|
|
1006
1031
|
method: "POST",
|
|
1007
1032
|
headers: {
|
|
1008
1033
|
authorization: `Bearer ${this.conn.apiKey}`,
|
|
@@ -1137,18 +1162,34 @@ class CloudTransport {
|
|
|
1137
1162
|
async pause() {
|
|
1138
1163
|
await cloudFetch(this.conn, "POST", `/v1/machines/${this.id}/pause`, { timeoutMs: CLOUD_START_TIMEOUT_MS });
|
|
1139
1164
|
}
|
|
1140
|
-
async resume() {
|
|
1165
|
+
async resume(waitUntilReady = true) {
|
|
1141
1166
|
await cloudFetch(this.conn, "POST", `/v1/machines/${this.id}/resume`, { timeoutMs: CLOUD_START_TIMEOUT_MS });
|
|
1142
|
-
|
|
1167
|
+
if (waitUntilReady)
|
|
1168
|
+
await waitForReady(this.conn, this.id);
|
|
1143
1169
|
}
|
|
1144
|
-
async start(interceptor) {
|
|
1170
|
+
async start(interceptor, waitUntilReady = true) {
|
|
1145
1171
|
if (interceptor)
|
|
1146
1172
|
throw new errors_1.NotSupportedError("egressInterceptor is local-only.");
|
|
1147
1173
|
// Resume a stopped machine, then wait for its agent so the handle is usable.
|
|
1148
1174
|
await cloudFetch(this.conn, "POST", `/v1/machines/${this.id}/start`, {
|
|
1149
1175
|
timeoutMs: CLOUD_START_TIMEOUT_MS,
|
|
1150
1176
|
});
|
|
1151
|
-
|
|
1177
|
+
if (waitUntilReady)
|
|
1178
|
+
await waitForReady(this.conn, this.id);
|
|
1179
|
+
}
|
|
1180
|
+
async info() {
|
|
1181
|
+
return cloudFetch(this.conn, "GET", `/v1/machines/${this.id}`);
|
|
1182
|
+
}
|
|
1183
|
+
async logs(tail) {
|
|
1184
|
+
return cloudFetch(this.conn, "GET", `/v1/machines/${this.id}/logs?tail=${tail}`, {
|
|
1185
|
+
accept: "text",
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
async exportArtifact() {
|
|
1189
|
+
return cloudFetch(this.conn, "POST", `/v1/machines/${this.id}/export`, {
|
|
1190
|
+
json: {},
|
|
1191
|
+
timeoutMs: CLOUD_START_TIMEOUT_MS,
|
|
1192
|
+
});
|
|
1152
1193
|
}
|
|
1153
1194
|
async delete() {
|
|
1154
1195
|
await cloudFetch(this.conn, "DELETE", `/v1/machines/${this.id}`);
|
|
@@ -1443,7 +1484,7 @@ async function makeTransport(config, conn) {
|
|
|
1443
1484
|
process.env.SMOL_CLOUD_URL ??
|
|
1444
1485
|
cliUrl ??
|
|
1445
1486
|
DEFAULT_CLOUD_URL).replace(/\/+$/, "");
|
|
1446
|
-
const cloudConn =
|
|
1487
|
+
const cloudConn = cloudConnOf(conn, baseUrl, key);
|
|
1447
1488
|
await storeCloudCredentials(cloudConn, config.credentials);
|
|
1448
1489
|
// smolfleet CreateMachineRequest (camelCase): source (tagged), nested
|
|
1449
1490
|
// resources, network {mode}, autoStopSeconds, ttlSeconds. Optional numeric
|
|
@@ -1451,6 +1492,7 @@ async function makeTransport(config, conn) {
|
|
|
1451
1492
|
// (exactOptionalPropertyTypes forbids passing `undefined` explicitly).
|
|
1452
1493
|
const createBody = {
|
|
1453
1494
|
name: config.name ?? null,
|
|
1495
|
+
...(config.labels && Object.keys(config.labels).length ? { labels: config.labels } : {}),
|
|
1454
1496
|
source: { type: "image", reference: config.image },
|
|
1455
1497
|
resources: {
|
|
1456
1498
|
...(config.resources?.cpus !== undefined
|
|
@@ -1487,15 +1529,7 @@ async function makeTransport(config, conn) {
|
|
|
1487
1529
|
...(config.credentials?.length
|
|
1488
1530
|
? { credentials: config.credentials.map((c) => c.name) }
|
|
1489
1531
|
: {}),
|
|
1490
|
-
...(config.cacheDisk
|
|
1491
|
-
? {
|
|
1492
|
-
cacheDisk: {
|
|
1493
|
-
cache: config.cacheDisk.cache,
|
|
1494
|
-
...(config.cacheDisk.version !== undefined ? { version: config.cacheDisk.version } : {}),
|
|
1495
|
-
...(config.cacheDisk.mountPath !== undefined ? { mountPath: config.cacheDisk.mountPath } : {}),
|
|
1496
|
-
},
|
|
1497
|
-
}
|
|
1498
|
-
: {}),
|
|
1532
|
+
...(config.cacheDisk ? { cacheDisk: cacheDiskBody(config.cacheDisk) } : {}),
|
|
1499
1533
|
autoStopSeconds: config.autoStopSeconds ?? null,
|
|
1500
1534
|
ttlSeconds: config.ttlSeconds ?? null,
|
|
1501
1535
|
// Forkable is a CREATE-time property: the control plane persists it and the
|
|
@@ -1624,15 +1658,28 @@ async function listMachines(conn, options = {}) {
|
|
|
1624
1658
|
process.env.SMOL_CLOUD_URL ??
|
|
1625
1659
|
cliUrl ??
|
|
1626
1660
|
DEFAULT_CLOUD_URL).replace(/\/+$/, "");
|
|
1627
|
-
|
|
1628
|
-
|
|
1661
|
+
// The control plane filters by label itself; the client-side filter below
|
|
1662
|
+
// stays as the backstop for an older control plane that ignores it.
|
|
1663
|
+
const labelQuery = Object.entries(options.labels ?? {})
|
|
1664
|
+
.map(([k, v]) => `label=${encodeURIComponent(`${k}=${v}`)}`)
|
|
1665
|
+
.join("&");
|
|
1666
|
+
const listed = await cloudFetch(cloudConnOf(conn, baseUrl, key), "GET", `/v1/machines${labelQuery ? `?${labelQuery}` : ""}`);
|
|
1667
|
+
// Anything but a machine list (a proxy's page, a wrong base URL) is an
|
|
1668
|
+
// error, not an account with no machines.
|
|
1669
|
+
const rows = Array.isArray(listed)
|
|
1670
|
+
? listed
|
|
1671
|
+
: Array.isArray(listed?.machines)
|
|
1672
|
+
? listed.machines
|
|
1673
|
+
: undefined;
|
|
1674
|
+
if (!rows) {
|
|
1675
|
+
throw new errors_1.SmolError("SMOLVM_ERROR", `cloud GET /v1/machines at ${baseUrl} did not return a machine list`);
|
|
1676
|
+
}
|
|
1629
1677
|
all = rows.map((m) => ({
|
|
1630
1678
|
name: m.name ?? m.id,
|
|
1631
1679
|
id: m.id,
|
|
1632
1680
|
state: m.state,
|
|
1633
1681
|
...(m.source?.type === "image" && { image: m.source.reference }),
|
|
1634
|
-
|
|
1635
|
-
labels: {},
|
|
1682
|
+
labels: m.labels ?? {},
|
|
1636
1683
|
persistent: !m.ephemeral,
|
|
1637
1684
|
// Remote machines never depend on a client process.
|
|
1638
1685
|
detached: true,
|
|
@@ -1694,7 +1741,7 @@ async function connectTransport(id, conn) {
|
|
|
1694
1741
|
process.env.SMOL_CLOUD_URL ??
|
|
1695
1742
|
cliUrl ??
|
|
1696
1743
|
DEFAULT_CLOUD_URL).replace(/\/+$/, "");
|
|
1697
|
-
const cloudConn =
|
|
1744
|
+
const cloudConn = cloudConnOf(conn, baseUrl, key);
|
|
1698
1745
|
// Resolve like the CLI does: try the id path first, and when that 404s,
|
|
1699
1746
|
// list machines and match by NAME. `machine.name` returns the human name,
|
|
1700
1747
|
// so `Machine.connect(other.name)` — the natural composition of this API —
|
|
@@ -1743,6 +1790,9 @@ async function restoreCheckpointTransport(checkpointId, name, conn, options) {
|
|
|
1743
1790
|
}
|
|
1744
1791
|
conn ??= { target: looksLikeLocalCheckpoint(checkpointId) ? "local" : "cloud" };
|
|
1745
1792
|
if (!selectsCloud(conn)) {
|
|
1793
|
+
if (options?.cacheDisk) {
|
|
1794
|
+
throw new errors_1.InvalidConfigError("cacheDisk is cloud-only: a local restore has no cache slot.");
|
|
1795
|
+
}
|
|
1746
1796
|
const path = (0, node_path_1.resolve)(checkpointId);
|
|
1747
1797
|
let restored;
|
|
1748
1798
|
try {
|
|
@@ -1775,6 +1825,7 @@ async function restoreCheckpointTransport(checkpointId, name, conn, options) {
|
|
|
1775
1825
|
...(options?.networkPolicy !== undefined
|
|
1776
1826
|
? { network: cloudNetwork(options.networkPolicy) }
|
|
1777
1827
|
: {}),
|
|
1828
|
+
...(options?.cacheDisk ? { cacheDisk: cacheDiskBody(options.cacheDisk) } : {}),
|
|
1778
1829
|
},
|
|
1779
1830
|
});
|
|
1780
1831
|
const id = created.id;
|
|
@@ -1790,6 +1841,14 @@ async function restoreCheckpointTransport(checkpointId, name, conn, options) {
|
|
|
1790
1841
|
}
|
|
1791
1842
|
return new CloudTransport(cloudConn, created.name ?? name, id);
|
|
1792
1843
|
}
|
|
1844
|
+
function cacheDiskBody(ref) {
|
|
1845
|
+
return {
|
|
1846
|
+
cache: ref.cache,
|
|
1847
|
+
...(ref.version !== undefined ? { version: ref.version } : {}),
|
|
1848
|
+
...(ref.mountPath !== undefined ? { mountPath: ref.mountPath } : {}),
|
|
1849
|
+
...(ref.slot ? { slot: true } : {}),
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1793
1852
|
/** The control plane and key a cloud operation uses: explicit options, then
|
|
1794
1853
|
* the environment, then the CLI session. */
|
|
1795
1854
|
function cloudConnFor(conn, operation) {
|
|
@@ -1803,7 +1862,7 @@ function cloudConnFor(conn, operation) {
|
|
|
1803
1862
|
process.env.SMOL_CLOUD_URL ??
|
|
1804
1863
|
cliUrl ??
|
|
1805
1864
|
DEFAULT_CLOUD_URL).replace(/\/+$/, "");
|
|
1806
|
-
return
|
|
1865
|
+
return cloudConnOf(conn, baseUrl, key);
|
|
1807
1866
|
}
|
|
1808
1867
|
/** Create a cache disk; its version 0 is an empty filesystem. Cloud only. */
|
|
1809
1868
|
function createCacheDisk(options, conn = {}) {
|
package/dist/types.d.ts
CHANGED
|
@@ -324,6 +324,12 @@ export interface RestoreCheckpointOptions {
|
|
|
324
324
|
* an earlier save point; do not run two machines from one checkpoint with
|
|
325
325
|
* it. Local target only. */
|
|
326
326
|
keepIdentity?: boolean;
|
|
327
|
+
/** Mount this cache in the slot the checkpoint's machine was created with
|
|
328
|
+
* (`cacheDisk: { cache, slot: true }`), in place of the cache it was
|
|
329
|
+
* captured with. It must be no larger than the slot and mount at the
|
|
330
|
+
* slot's path, so one base checkpoint resumes with each project's own
|
|
331
|
+
* cache. Cloud only. */
|
|
332
|
+
cacheDisk?: CacheDiskRef;
|
|
327
333
|
}
|
|
328
334
|
/** Per-call execution options. */
|
|
329
335
|
export interface ExecOptions {
|
|
@@ -354,6 +360,9 @@ export interface ExecOptions {
|
|
|
354
360
|
* Ignored (both families, as before) on older control planes and on the
|
|
355
361
|
* local target. */
|
|
356
362
|
output?: "text" | "b64" | "both";
|
|
363
|
+
/** Cloud only: start the command detached and return at once with its
|
|
364
|
+
* `pid`, leaving it running (a dev server, an agent) after the call ends. */
|
|
365
|
+
background?: boolean;
|
|
357
366
|
}
|
|
358
367
|
/** Result of a command execution. */
|
|
359
368
|
export interface ExecResult {
|
|
@@ -377,6 +386,8 @@ export interface ExecResult {
|
|
|
377
386
|
* the lossy `stdout`); on the local target it is the UTF-8 encoding of `stdout`.
|
|
378
387
|
* Prefer this over `stdout` for binary or >1 MiB output. */
|
|
379
388
|
stdoutBytes: Uint8Array;
|
|
389
|
+
/** The detached process's pid, for an exec run with `background: true`. */
|
|
390
|
+
pid?: number;
|
|
380
391
|
/** Byte-exact, untruncated stderr; see `stdoutBytes`. */
|
|
381
392
|
stderrBytes: Uint8Array;
|
|
382
393
|
/** True when exitCode === 0. */
|
|
@@ -509,6 +520,14 @@ export interface ConnectOptions {
|
|
|
509
520
|
baseUrl?: string;
|
|
510
521
|
/** Cloud API key, `smk_…` (cloud target only). */
|
|
511
522
|
apiKey?: string;
|
|
523
|
+
/** Cloud only: the `fetch` every cloud request goes through, for a proxy,
|
|
524
|
+
* retries, tracing, or a runtime without a global `fetch`. Its own errors
|
|
525
|
+
* reach the caller unchanged; timeouts and aborts are still reported as
|
|
526
|
+
* `TIMEOUT` and the abort reason. Default: the global `fetch`. */
|
|
527
|
+
fetch?: typeof fetch;
|
|
528
|
+
/** Cloud only: read a binary response body, for a `fetch` whose responses
|
|
529
|
+
* lack a working `arrayBuffer()`. */
|
|
530
|
+
readResponseBytes?: (response: Response) => Promise<Uint8Array>;
|
|
512
531
|
}
|
|
513
532
|
/** Per-launch trusted host service. Keep the token out of logs and persistent config. */
|
|
514
533
|
export interface EgressInterceptor {
|
|
@@ -519,6 +538,15 @@ export interface EgressInterceptor {
|
|
|
519
538
|
}
|
|
520
539
|
export interface StartOptions {
|
|
521
540
|
egressInterceptor?: EgressInterceptor;
|
|
541
|
+
/** Wait for the guest to be ready before returning (default true). Pass
|
|
542
|
+
* false to return once the start is accepted, e.g. when starting many
|
|
543
|
+
* machines and polling `ready()` yourself. */
|
|
544
|
+
waitUntilReady?: boolean;
|
|
545
|
+
}
|
|
546
|
+
/** Options for `Machine.resume`. */
|
|
547
|
+
export interface ResumeOptions {
|
|
548
|
+
/** Wait for the guest to be ready before returning (default true). */
|
|
549
|
+
waitUntilReady?: boolean;
|
|
522
550
|
}
|
|
523
551
|
/** Which cache disk a machine starts from. */
|
|
524
552
|
export interface CacheDiskRef {
|
|
@@ -528,6 +556,10 @@ export interface CacheDiskRef {
|
|
|
528
556
|
version?: number;
|
|
529
557
|
/** Absolute guest path to mount it at; the cache disk's own when omitted. */
|
|
530
558
|
mountPath?: string;
|
|
559
|
+
/** On create, attach the cache unmounted as a slot: restoring a checkpoint
|
|
560
|
+
* of the machine with `restoreCheckpoint(..., { cacheDisk })` mounts any
|
|
561
|
+
* cache no larger than this one there, at this one's mount path. */
|
|
562
|
+
slot?: boolean;
|
|
531
563
|
}
|
|
532
564
|
/** Options for creating a cache disk. */
|
|
533
565
|
export interface CreateCacheDiskOptions {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smolmachines",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.2",
|
|
4
4
|
"description": "Embed isolated microVM sandboxes directly in your code — no server to run.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "smol-machines",
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"typescript": "^5.6.0"
|
|
64
64
|
},
|
|
65
65
|
"optionalDependencies": {
|
|
66
|
-
"smolmachines-darwin-arm64": "1.25.
|
|
67
|
-
"smolmachines-linux-x64-gnu": "1.25.
|
|
68
|
-
"smolmachines-linux-arm64-gnu": "1.25.
|
|
66
|
+
"smolmachines-darwin-arm64": "1.25.2",
|
|
67
|
+
"smolmachines-linux-x64-gnu": "1.25.2",
|
|
68
|
+
"smolmachines-linux-arm64-gnu": "1.25.2"
|
|
69
69
|
}
|
|
70
70
|
}
|