@spheron/sandbox 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/http.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { SpheronApiError } from './errors.js';
2
+ export declare const DEFAULT_BASE_URL = "https://app.spheron.ai";
3
+ export declare const SDK_VERSION = "0.1.1";
4
+ export interface ClientOptions {
5
+ /** A GPU VM API key (`sai_pk_...`). Defaults to the SPHERON_API_KEY environment variable. */
6
+ apiKey?: string;
7
+ /** Defaults to SPHERON_BASE_URL, then https://app.spheron.ai. */
8
+ baseUrl?: string;
9
+ /** A fetch implementation. Defaults to the global fetch. */
10
+ fetch?: typeof fetch;
11
+ /** Retries for a safe request that failed to connect or got a 502, 503 or 504. Default 3. (429s retry for 90 s.) */
12
+ maxRetries?: number;
13
+ /** Timeout for one HTTP request. Default 90 s; long polls add their wait on top. */
14
+ requestTimeoutMs?: number;
15
+ }
16
+ export interface RequestOptions {
17
+ query?: Record<string, string | number | boolean | undefined>;
18
+ body?: unknown;
19
+ timeoutMs?: number;
20
+ /** Safe to send again after a dropped connection or a 502/503/504. GET requests always are. */
21
+ idempotent?: boolean;
22
+ headers?: Record<string, string>;
23
+ signal?: AbortSignal;
24
+ }
25
+ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
26
+ /** The typed error for a refused response. */
27
+ export declare function apiError(res: Response, body: unknown): SpheronApiError;
28
+ /** The HTTP layer every SDK call goes through: auth, JSON, typed errors and retries. */
29
+ export declare class HttpClient {
30
+ readonly baseUrl: string;
31
+ private readonly apiKey;
32
+ private readonly fetchImpl;
33
+ private readonly maxRetries;
34
+ private readonly timeoutMs;
35
+ constructor(opts?: ClientOptions);
36
+ request<T = unknown>(method: string, path: string, opts?: RequestOptions): Promise<T>;
37
+ }
package/dist/http.js ADDED
@@ -0,0 +1,198 @@
1
+ import { AuthenticationError, InstanceNotRunningError, NotFoundError, RateLimitError, SandboxNotReadyError, SandboxUnreachableError, SpheronApiError, SpheronError, } from './errors.js';
2
+ export const DEFAULT_BASE_URL = 'https://app.spheron.ai';
3
+ export const SDK_VERSION = '0.1.1';
4
+ const CLIENT_NAME = `spheron-sandbox-js/${SDK_VERSION}`;
5
+ /** The API answers these two 409s with exactly this text (shipped API text never changes), so they map to types. */
6
+ const NOT_RUNNING = 'This instance is not running.';
7
+ const INSTALLING = 'The GPU sandbox is still being set up on this instance. Try again in a minute.';
8
+ /** A 429 is retried until this much time has passed. The file transfer cap frees a slot within seconds to a minute. */
9
+ const RATE_LIMIT_BUDGET_MS = 90_000;
10
+ /** The longest single wait for a 429, whatever Retry-After says (the general limit can say 15 minutes). */
11
+ const RATE_LIMIT_MAX_WAIT_MS = 30_000;
12
+ /** An environment variable, or undefined where there is none or reading it is not allowed (Deno without --allow-env). */
13
+ function env(name) {
14
+ try {
15
+ return globalThis.process?.env?.[name];
16
+ }
17
+ catch {
18
+ return undefined;
19
+ }
20
+ }
21
+ export function sleep(ms, signal) {
22
+ return new Promise((resolve, reject) => {
23
+ if (signal?.aborted)
24
+ return reject(signal.reason);
25
+ const onAbort = () => {
26
+ clearTimeout(timer);
27
+ reject(signal.reason);
28
+ };
29
+ const timer = setTimeout(() => {
30
+ signal?.removeEventListener('abort', onAbort);
31
+ resolve();
32
+ }, ms);
33
+ signal?.addEventListener('abort', onAbort, { once: true });
34
+ });
35
+ }
36
+ function retryAfterSec(res, body) {
37
+ const header = Number(res.headers.get('retry-after'));
38
+ if (Number.isFinite(header) && header > 0)
39
+ return header;
40
+ const b = body;
41
+ const fromBody = Number(b?.details?.retryAfter ?? b?.retryAfter);
42
+ return Number.isFinite(fromBody) && fromBody > 0 ? fromBody : undefined;
43
+ }
44
+ /** The typed error for a refused response. */
45
+ export function apiError(res, body) {
46
+ const b = (body && typeof body === 'object' ? body : {});
47
+ // Auth refusals put the useful sentence in `message` and a bare "Unauthorized" in `error`.
48
+ const generic = b.error === 'Unauthorized' || b.error === 'Forbidden';
49
+ const message = (typeof b.error === 'string' && !generic ? b.error : undefined) ??
50
+ (typeof b.message === 'string' ? b.message : undefined) ??
51
+ (typeof b.error === 'string' ? b.error : undefined) ??
52
+ `HTTP ${res.status}`;
53
+ const opts = { code: typeof b.code === 'string' ? b.code : undefined, requestId: res.headers.get('x-request-id') ?? undefined, body };
54
+ switch (res.status) {
55
+ case 401:
56
+ case 403:
57
+ return new AuthenticationError(res.status, message, opts);
58
+ case 404:
59
+ return new NotFoundError(res.status, message, opts);
60
+ case 409:
61
+ if (message === NOT_RUNNING)
62
+ return new InstanceNotRunningError(res.status, message, opts);
63
+ if (message === INSTALLING)
64
+ return new SandboxNotReadyError(res.status, message, opts);
65
+ return new SpheronApiError(res.status, message, opts);
66
+ case 429:
67
+ return new RateLimitError(res.status, message, { ...opts, retryAfterSec: retryAfterSec(res, body) });
68
+ case 503:
69
+ if (b.recovery)
70
+ return new SandboxUnreachableError(res.status, message, { ...opts, recovery: b.recovery });
71
+ return new SpheronApiError(res.status, message, opts);
72
+ default:
73
+ return new SpheronApiError(res.status, message, opts);
74
+ }
75
+ }
76
+ /** The HTTP layer every SDK call goes through: auth, JSON, typed errors and retries. */
77
+ export class HttpClient {
78
+ baseUrl;
79
+ apiKey;
80
+ fetchImpl;
81
+ maxRetries;
82
+ timeoutMs;
83
+ constructor(opts = {}) {
84
+ const apiKey = opts.apiKey ?? env('SPHERON_API_KEY');
85
+ if (!apiKey)
86
+ throw new SpheronError('No API key. Pass apiKey, or set SPHERON_API_KEY to a GPU VM API key (sai_pk_...).');
87
+ this.apiKey = apiKey;
88
+ this.baseUrl = (opts.baseUrl ?? env('SPHERON_BASE_URL') ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
89
+ const f = opts.fetch ?? globalThis.fetch;
90
+ if (!f)
91
+ throw new SpheronError('No fetch available. Use Node 18 or later, or pass a fetch implementation.');
92
+ this.fetchImpl = f.bind(globalThis);
93
+ this.maxRetries = opts.maxRetries ?? 3;
94
+ this.timeoutMs = opts.requestTimeoutMs ?? 90_000;
95
+ }
96
+ async request(method, path, opts = {}) {
97
+ const url = new URL(this.baseUrl + path);
98
+ for (const [k, v] of Object.entries(opts.query ?? {}))
99
+ if (v !== undefined)
100
+ url.searchParams.set(k, String(v));
101
+ const idempotent = opts.idempotent ?? method === 'GET';
102
+ const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
103
+ const headers = {
104
+ Authorization: `Bearer ${this.apiKey}`,
105
+ Accept: 'application/json',
106
+ 'User-Agent': CLIENT_NAME,
107
+ // Names the SDK and its version to Spheron, so a release that breaks shows on our side (a browser may drop User-Agent).
108
+ 'X-Spheron-Client': CLIENT_NAME,
109
+ ...opts.headers,
110
+ };
111
+ let payload;
112
+ if (opts.body !== undefined) {
113
+ headers['Content-Type'] = 'application/json';
114
+ payload = JSON.stringify(opts.body);
115
+ }
116
+ const started = Date.now();
117
+ for (let attempt = 0;; attempt++) {
118
+ if (opts.signal?.aborted)
119
+ throw opts.signal.reason;
120
+ const call = withTimeout(timeoutMs, opts.signal);
121
+ let res;
122
+ let text;
123
+ try {
124
+ res = await this.fetchImpl(url, { method, headers, body: payload, signal: call.signal });
125
+ text = await res.text();
126
+ }
127
+ catch (error) {
128
+ if (opts.signal?.aborted)
129
+ throw opts.signal.reason;
130
+ // A dropped connection or a timeout: the request may have done its work, so only a safe one is sent again.
131
+ if (idempotent && attempt < this.maxRetries) {
132
+ await sleep(backoffMs(attempt), opts.signal);
133
+ continue;
134
+ }
135
+ const why = call.timedOut() ? `timed out after ${timeoutMs} ms` : String(error?.message ?? error);
136
+ throw new SpheronError(`${method} ${path} failed: ${why}`);
137
+ }
138
+ finally {
139
+ call.done();
140
+ }
141
+ const body = parseBody(text);
142
+ if (res.ok)
143
+ return body;
144
+ const err = apiError(res, body);
145
+ // A 429 did nothing, so any request can be sent again: wait as asked, for up to the budget.
146
+ if (err instanceof RateLimitError) {
147
+ const wait = Math.min((err.retryAfterSec ?? backoffMs(attempt) / 1000) * 1000, RATE_LIMIT_MAX_WAIT_MS);
148
+ if (Date.now() - started + wait > RATE_LIMIT_BUDGET_MS)
149
+ throw err;
150
+ await sleep(wait, opts.signal);
151
+ continue;
152
+ }
153
+ const transient = res.status === 502 || res.status === 504 || (res.status === 503 && !(err instanceof SandboxUnreachableError));
154
+ if (!(idempotent && transient && attempt < this.maxRetries))
155
+ throw err;
156
+ await sleep(backoffMs(attempt), opts.signal);
157
+ }
158
+ }
159
+ }
160
+ function parseBody(text) {
161
+ if (!text)
162
+ return null;
163
+ try {
164
+ return JSON.parse(text);
165
+ }
166
+ catch {
167
+ return text;
168
+ }
169
+ }
170
+ /**
171
+ * A signal that aborts after `ms` or when `outer` does (AbortSignal.any and AbortSignal.timeout are not on Node 18).
172
+ * Call done() when the request ends, so the timer and the listener on `outer` do not outlive it.
173
+ */
174
+ function withTimeout(ms, outer) {
175
+ const ac = new AbortController();
176
+ let timedOut = false;
177
+ const timer = setTimeout(() => {
178
+ timedOut = true;
179
+ ac.abort(new SpheronError(`timed out after ${ms} ms`));
180
+ }, ms);
181
+ const onAbort = () => ac.abort(outer.reason);
182
+ if (outer?.aborted)
183
+ ac.abort(outer.reason);
184
+ else
185
+ outer?.addEventListener('abort', onAbort, { once: true });
186
+ return {
187
+ signal: ac.signal,
188
+ timedOut: () => timedOut,
189
+ done: () => {
190
+ clearTimeout(timer);
191
+ outer?.removeEventListener('abort', onAbort);
192
+ },
193
+ };
194
+ }
195
+ function backoffMs(attempt) {
196
+ return Math.min(500 * 2 ** attempt, 8_000) + Math.floor(Math.random() * 250);
197
+ }
198
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export { Sandbox } from './sandbox.js';
2
+ export type { CreateOptions, OfferChoice, ReadyPhase, SandboxOptions, WaitReadyOptions } from './sandbox.js';
3
+ export { CommandHandle, Commands } from './commands.js';
4
+ export type { RunOptions, StartOptions, WaitOptions } from './commands.js';
5
+ export { Files, MAX_FILE_BYTES } from './files.js';
6
+ export { hourlyPrice } from './offers.js';
7
+ export type { OfferFilter } from './offers.js';
8
+ export type { ClientOptions } from './http.js';
9
+ export { DEFAULT_BASE_URL, SDK_VERSION } from './http.js';
10
+ export * from './errors.js';
11
+ export type { CommandResult, CommandStatus, Deployment, FileEntry, GpuOffer, SandboxStatus } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { Sandbox } from './sandbox.js';
2
+ export { CommandHandle, Commands } from './commands.js';
3
+ export { Files, MAX_FILE_BYTES } from './files.js';
4
+ export { hourlyPrice } from './offers.js';
5
+ export { DEFAULT_BASE_URL, SDK_VERSION } from './http.js';
6
+ export * from './errors.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,cAAc,aAAa,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { HttpClient } from './http.js';
2
+ import type { GpuOffer } from './types.js';
3
+ export interface OfferFilter {
4
+ /** GPU model, one word: h100, a100, l40s, rtx4090. Leave out for any GPU. */
5
+ gpu?: string;
6
+ /** GPUs per instance. */
7
+ gpuCount?: number;
8
+ /** Spot offers only (cheaper, and can be reclaimed at any time). Default: dedicated only. */
9
+ spot?: boolean;
10
+ /** Regions to include. */
11
+ regions?: string[];
12
+ /** Providers to include, as the API names them (spheron-ai, spheron-es, data-crunch, ...). */
13
+ providers?: string[];
14
+ /** Most you are willing to pay per hour for the whole instance, in USD. */
15
+ maxHourlyPrice?: number;
16
+ }
17
+ /** What an offer bills per hour: the spot price on a spot offer when there is one. */
18
+ export declare function hourlyPrice(offer: GpuOffer): number;
19
+ /** Offers a sandbox can be deployed on, matching the filter, cheapest first. Virtual machines only. */
20
+ export declare function findOffers(http: HttpClient, filter?: OfferFilter): Promise<GpuOffer[]>;
21
+ /**
22
+ * The operating system to deploy: the one asked for when the offer has it (exact, then by substring), else an
23
+ * Ubuntu image with CUDA, else any Ubuntu, else the first listed.
24
+ */
25
+ export declare function pickOperatingSystem(offer: GpuOffer, wanted?: string): string | undefined;
package/dist/offers.js ADDED
@@ -0,0 +1,55 @@
1
+ /** What an offer bills per hour: the spot price on a spot offer when there is one. */
2
+ export function hourlyPrice(offer) {
3
+ return String(offer.instanceType).toUpperCase() === 'SPOT' && typeof offer.spot_price === 'number' ? offer.spot_price : offer.price;
4
+ }
5
+ /** Pages of the listing read at most: 50 GPU types each, more than the catalogue holds today. */
6
+ const MAX_PAGES = 5;
7
+ /** Offers a sandbox can be deployed on, matching the filter, cheapest first. Virtual machines only. */
8
+ export async function findOffers(http, filter = {}) {
9
+ const search = filter.gpu?.replace(/\s+/g, '').toLowerCase() || undefined;
10
+ const offers = [];
11
+ // GPU count and price are filtered here, so read every page: a match can be on any of them.
12
+ for (let page = 1; page <= MAX_PAGES; page++) {
13
+ const res = await http.request('GET', '/api/gpu-offers', {
14
+ query: {
15
+ search,
16
+ computeType: 'gpu',
17
+ instanceType: filter.spot ? 'SPOT' : 'DEDICATED',
18
+ regions: filter.regions?.join(','),
19
+ providers: filter.providers?.join(','),
20
+ sortBy: 'price',
21
+ sortOrder: 'asc',
22
+ limit: 50,
23
+ page,
24
+ },
25
+ });
26
+ for (const group of res.data ?? []) {
27
+ for (const offer of group.offers ?? [])
28
+ offers.push({ ...offer, gpuType: group.gpuType });
29
+ }
30
+ if (!res.totalPages || page >= res.totalPages)
31
+ break;
32
+ }
33
+ const isSpot = (o) => String(o.instanceType).toUpperCase() === 'SPOT';
34
+ return offers
35
+ // The listing's instanceType filter lets some providers' spot offers through: a dedicated ask must never land
36
+ // on a VM that can be reclaimed (seen live on Spheron ES).
37
+ .filter((o) => isSpot(o) === !!filter.spot)
38
+ .filter((o) => o.available !== false && o.maintenance !== true)
39
+ .filter((o) => filter.gpuCount === undefined || o.gpuCount === filter.gpuCount)
40
+ .filter((o) => filter.maxHourlyPrice === undefined || hourlyPrice(o) <= filter.maxHourlyPrice)
41
+ .sort((a, b) => hourlyPrice(a) - hourlyPrice(b));
42
+ }
43
+ /**
44
+ * The operating system to deploy: the one asked for when the offer has it (exact, then by substring), else an
45
+ * Ubuntu image with CUDA, else any Ubuntu, else the first listed.
46
+ */
47
+ export function pickOperatingSystem(offer, wanted) {
48
+ const options = offer.os_options ?? [];
49
+ if (wanted) {
50
+ const w = wanted.toLowerCase();
51
+ return options.find((o) => o.toLowerCase() === w) ?? options.find((o) => o.toLowerCase().includes(w)) ?? undefined;
52
+ }
53
+ return options.find((o) => /ubuntu/i.test(o) && /cuda/i.test(o)) ?? options.find((o) => /ubuntu/i.test(o)) ?? options[0];
54
+ }
55
+ //# sourceMappingURL=offers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offers.js","sourceRoot":"","sources":["../src/offers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ import { Commands } from './commands.js';
2
+ import { Files } from './files.js';
3
+ import { type ClientOptions } from './http.js';
4
+ import { type OfferFilter } from './offers.js';
5
+ import type { Deployment, GpuOffer, SandboxStatus } from './types.js';
6
+ export interface SandboxOptions extends ClientOptions {
7
+ /** How often a running command's output is read, at most. Default 2000 ms. */
8
+ pollIntervalMs?: number;
9
+ }
10
+ export interface WaitReadyOptions {
11
+ /** Give up after this long. Default 20 minutes. */
12
+ timeoutMs?: number;
13
+ /** Called when the phase changes: deploying, installing, then ready. */
14
+ onStatus?: (phase: ReadyPhase, status: SandboxStatus | null) => void;
15
+ /** How often the status is checked. Default 5000 ms. */
16
+ checkEveryMs?: number;
17
+ signal?: AbortSignal;
18
+ }
19
+ export type ReadyPhase = 'deploying' | 'installing' | 'ready';
20
+ /** An offer picked by hand, for example from Sandbox.findOffers(). */
21
+ export interface OfferChoice {
22
+ provider: string;
23
+ offerId: string;
24
+ gpuType: string;
25
+ gpuCount: number;
26
+ region?: string;
27
+ clusters?: string[];
28
+ instanceType?: string;
29
+ os_options?: string[];
30
+ }
31
+ export interface CreateOptions extends SandboxOptions, OfferFilter {
32
+ /** Deploy this exact offer instead of picking the cheapest match. */
33
+ offer?: OfferChoice | GpuOffer;
34
+ /** Operating system, matched against the offer's options. Default: Ubuntu with CUDA when listed. */
35
+ operatingSystem?: string;
36
+ /** Instance name. */
37
+ name?: string;
38
+ /** Team to deploy in. Default: your only team (required when you belong to several). */
39
+ teamId?: string;
40
+ /** A saved SSH key's id. Default: the team's first saved key. */
41
+ sshKeyId?: string;
42
+ /** A public key for this instance only (not saved). Use instead of sshKeyId. */
43
+ sshPublicKey?: string;
44
+ /** Volumes to attach at launch (same provider and region). */
45
+ volumeIds?: string[];
46
+ /** How many matching offers to try when one is sold out or cannot take the sandbox. Default 3. */
47
+ maxOfferAttempts?: number;
48
+ /** Wait until commands can run. Default true. */
49
+ waitUntilReady?: boolean;
50
+ /** How long to wait for it. Default 20 minutes. */
51
+ readyTimeoutMs?: number;
52
+ /** Called when the phase changes while waiting: deploying, installing, then ready. */
53
+ onStatus?: WaitReadyOptions['onStatus'];
54
+ /** How often the status is checked while waiting. Default 5000 ms. */
55
+ checkEveryMs?: number;
56
+ signal?: AbortSignal;
57
+ }
58
+ /**
59
+ * A Spheron Agent GPU Sandbox: a GPU VM you run commands on and move files to and from, over the Spheron API.
60
+ *
61
+ * ```ts
62
+ * const sandbox = await Sandbox.create({ gpu: 'h100', maxHourlyPrice: 3 });
63
+ * const r = await sandbox.commands.run('nvidia-smi');
64
+ * console.log(r.output);
65
+ * await sandbox.terminate();
66
+ * ```
67
+ *
68
+ * The instance bills at its normal hourly price from the moment it runs until it is stopped or terminated.
69
+ */
70
+ export declare class Sandbox {
71
+ #private;
72
+ /** The deployment id. */
73
+ readonly id: string;
74
+ readonly commands: Commands;
75
+ readonly files: Files;
76
+ private constructor();
77
+ private get path();
78
+ /** Offers a sandbox can be deployed on, cheapest first. Pass one to Sandbox.create({ offer }). */
79
+ static findOffers(filter?: OfferFilter & ClientOptions): Promise<GpuOffer[]>;
80
+ /**
81
+ * Connect to an instance that already has a sandbox (deployed with it on, by this SDK, the API or an AI agent).
82
+ * Only the person who deployed it can use its sandbox.
83
+ */
84
+ static connect(deploymentId: string, opts?: SandboxOptions): Promise<Sandbox>;
85
+ /**
86
+ * Deploy a GPU VM with the sandbox on and, by default, wait until commands can run.
87
+ *
88
+ * Picks the cheapest offer matching the filter (or deploys `offer`), and moves on to the next one when an offer
89
+ * is sold out or cannot take the sandbox. Set maxHourlyPrice so a scarce GPU never lands on a pricey offer.
90
+ * Once an instance exists, any error names it in `deploymentId`: it bills until it is terminated.
91
+ */
92
+ static create(opts?: CreateOptions): Promise<Sandbox>;
93
+ /** The sandbox's state: installing, ready, failed or removed, and whether the instance is reachable now. */
94
+ status(signal?: AbortSignal): Promise<SandboxStatus>;
95
+ /** The instance: status, IP, SSH command, GPU, region, hourly rate and so on. */
96
+ info(signal?: AbortSignal): Promise<Deployment>;
97
+ /**
98
+ * Wait until the instance runs and the sandbox is reachable. Throws SandboxFailedError when the install fails or
99
+ * the instance dies, and TimeoutError (the instance keeps running and billing) when it takes too long.
100
+ */
101
+ waitUntilReady(opts?: WaitReadyOptions): Promise<SandboxStatus>;
102
+ /** waitUntilReady, also for the moment after a Start when the instance still reads stopped. */
103
+ private waitReady;
104
+ /**
105
+ * Stop the instance: it keeps its disk and bills only storage. Commands fail until it is started again. By default
106
+ * this waits until the provider has finished stopping it, which can take minutes on a large machine.
107
+ */
108
+ stop(opts?: {
109
+ wait?: boolean;
110
+ timeoutMs?: number;
111
+ checkEveryMs?: number;
112
+ signal?: AbortSignal;
113
+ }): Promise<void>;
114
+ /**
115
+ * Start a stopped instance, and by default wait until the sandbox is reachable again (timeoutMs covers both).
116
+ * While the provider is still stopping it, has no capacity yet or is busy, nothing changes and the start is tried
117
+ * again every retryEveryMs (default 30 s) for up to retryForMs (default 10 minutes).
118
+ */
119
+ start(opts?: {
120
+ waitUntilReady?: boolean;
121
+ retryEveryMs?: number;
122
+ retryForMs?: number;
123
+ } & WaitReadyOptions): Promise<void>;
124
+ /**
125
+ * Terminate the instance. It is deleted with its disk and stops billing. An instance must run for its offer's
126
+ * minimum runtime (usually 20 minutes) first: before then the API refuses with minimum_runtime_not_met.
127
+ */
128
+ terminate(): Promise<void>;
129
+ /** Uninstall the sandbox program. The instance keeps running and your SSH access is unaffected. */
130
+ removeSandbox(): Promise<void>;
131
+ /**
132
+ * POST pause or resume. A slow one answers 202 (or, from a server that ignores Prefer, 504) with the operation
133
+ * still running: both are handed back to follow, not thrown.
134
+ */
135
+ private lifecycleCall;
136
+ /** Wait for a Stop or Start to finish. Throws the operation's own error when it fails. */
137
+ private follow;
138
+ /** Runs fn and names this instance on any error it throws, wrapping errors that are not ours. */
139
+ private tagged;
140
+ }