withruntime 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -4
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +1262 -0
- package/dist/client.d.ts +118 -0
- package/dist/client.js +160 -0
- package/dist/credentials.d.ts +44 -0
- package/dist/credentials.js +180 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.js +106 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +25 -0
- package/dist/login.d.ts +119 -0
- package/dist/login.js +367 -0
- package/dist/mcp.d.ts +15 -0
- package/dist/mcp.js +221 -0
- package/dist/page.d.ts +13 -0
- package/dist/page.js +39 -0
- package/dist/products/desktop.d.ts +103 -0
- package/dist/products/desktop.js +38 -0
- package/dist/products/images.d.ts +121 -0
- package/dist/products/images.js +64 -0
- package/dist/products/index.d.ts +29 -0
- package/dist/products/index.js +36 -0
- package/dist/products/interpreter.d.ts +88 -0
- package/dist/products/interpreter.js +81 -0
- package/dist/products/network.d.ts +47 -0
- package/dist/products/network.js +19 -0
- package/dist/products/previews.d.ts +51 -0
- package/dist/products/previews.js +30 -0
- package/dist/products/referrals.d.ts +36 -0
- package/dist/products/referrals.js +8 -0
- package/dist/products/volumes.d.ts +47 -0
- package/dist/products/volumes.js +27 -0
- package/dist/sandbox.d.ts +192 -0
- package/dist/sandbox.js +628 -0
- package/dist/snapshots.d.ts +50 -0
- package/dist/snapshots.js +55 -0
- package/dist/tar.d.ts +3 -0
- package/dist/tar.js +116 -0
- package/dist/transport.d.ts +65 -0
- package/dist/transport.js +261 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.js +1 -0
- package/package.json +53 -8
- package/cli.js +0 -8
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
export type InterpreterLanguage = "python" | "javascript";
|
|
4
|
+
export type ResultRef = {
|
|
5
|
+
path: string;
|
|
6
|
+
bytes: number;
|
|
7
|
+
sha256: string;
|
|
8
|
+
};
|
|
9
|
+
/** One rich result: a MIME bundle (text/plain, text/html, image/png as base64,
|
|
10
|
+
* application/json, application/vnd.runtime.table+json for a DataFrame). */
|
|
11
|
+
export type ExecutionResult = {
|
|
12
|
+
main: boolean;
|
|
13
|
+
data: Record<string, unknown>;
|
|
14
|
+
refs: Record<string, ResultRef>;
|
|
15
|
+
};
|
|
16
|
+
export type Execution = {
|
|
17
|
+
id: string;
|
|
18
|
+
contextId: string;
|
|
19
|
+
language: InterpreterLanguage;
|
|
20
|
+
executionCount: number | null;
|
|
21
|
+
status: "ok" | "error" | "interrupted" | "timeout" | "lost";
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
results: ExecutionResult[];
|
|
25
|
+
error: {
|
|
26
|
+
name: string;
|
|
27
|
+
value: string;
|
|
28
|
+
traceback: string;
|
|
29
|
+
} | null;
|
|
30
|
+
overflow: {
|
|
31
|
+
stream: "stdout" | "stderr";
|
|
32
|
+
path: string | null;
|
|
33
|
+
bytes: number;
|
|
34
|
+
dropped: number;
|
|
35
|
+
}[];
|
|
36
|
+
durationMs: number;
|
|
37
|
+
contextStarted: boolean;
|
|
38
|
+
lostBytes: number;
|
|
39
|
+
};
|
|
40
|
+
export type InterpreterContext = {
|
|
41
|
+
id: string;
|
|
42
|
+
language: InterpreterLanguage;
|
|
43
|
+
processId: string;
|
|
44
|
+
cwd: string;
|
|
45
|
+
state: string;
|
|
46
|
+
startedAt: number;
|
|
47
|
+
};
|
|
48
|
+
export type RunOptions = RequestOptions & {
|
|
49
|
+
language?: InterpreterLanguage;
|
|
50
|
+
/** A context id; "python" and "javascript" are the defaults, started on first use. */
|
|
51
|
+
context?: string;
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
onStdout?: (text: string) => void;
|
|
54
|
+
onStderr?: (text: string) => void;
|
|
55
|
+
onResult?: (result: ExecutionResult) => void;
|
|
56
|
+
onError?: (error: {
|
|
57
|
+
name: string;
|
|
58
|
+
value: string;
|
|
59
|
+
traceback: string;
|
|
60
|
+
}) => void;
|
|
61
|
+
};
|
|
62
|
+
/** A stateful Python and JavaScript interpreter in the sandbox:
|
|
63
|
+
* `await sbx.interpreter.run("import pandas as pd; pd.DataFrame({'a': [1]})")`.
|
|
64
|
+
* Variables persist between runs of the same context, like a notebook. */
|
|
65
|
+
export declare function sandboxInterpreter(t: Transport, sandbox: Sandbox): {
|
|
66
|
+
/** Run a cell. With any of the on* callbacks, output streams as it happens. */
|
|
67
|
+
run(code: string, options?: RunOptions): Promise<Execution>;
|
|
68
|
+
contexts: {
|
|
69
|
+
list: (options?: RequestOptions) => Promise<InterpreterContext[]>;
|
|
70
|
+
create: (input?: {
|
|
71
|
+
id?: string;
|
|
72
|
+
language?: InterpreterLanguage;
|
|
73
|
+
cwd?: string;
|
|
74
|
+
env?: Record<string, string>;
|
|
75
|
+
}, options?: RequestOptions) => Promise<InterpreterContext>;
|
|
76
|
+
restart: (id: string, options?: RequestOptions) => Promise<InterpreterContext>;
|
|
77
|
+
interrupt: (id: string, options?: RequestOptions) => Promise<{
|
|
78
|
+
interrupted: boolean;
|
|
79
|
+
}>;
|
|
80
|
+
remove: (id: string, options?: RequestOptions) => Promise<{
|
|
81
|
+
deleted: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
};
|
|
84
|
+
/** The bytes of a result too large to travel inline (a `refs` entry). */
|
|
85
|
+
result(ref: ResultRef | {
|
|
86
|
+
path: string;
|
|
87
|
+
}, options?: RequestOptions): Promise<Uint8Array>;
|
|
88
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** A stateful Python and JavaScript interpreter in the sandbox:
|
|
2
|
+
* `await sbx.interpreter.run("import pandas as pd; pd.DataFrame({'a': [1]})")`.
|
|
3
|
+
* Variables persist between runs of the same context, like a notebook. */
|
|
4
|
+
export function sandboxInterpreter(t, sandbox) {
|
|
5
|
+
const base = () => `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/interpreter`;
|
|
6
|
+
return {
|
|
7
|
+
/** Run a cell. With any of the on* callbacks, output streams as it happens. */
|
|
8
|
+
async run(code, options = {}) {
|
|
9
|
+
const { language, context, timeoutMs, onStdout, onStderr, onResult, onError, ...request } = options;
|
|
10
|
+
const body = {
|
|
11
|
+
code,
|
|
12
|
+
...(language ? { language } : {}),
|
|
13
|
+
...(context ? { context } : {}),
|
|
14
|
+
...(timeoutMs ? { timeoutMs } : {}),
|
|
15
|
+
};
|
|
16
|
+
if (!onStdout && !onStderr && !onResult && !onError)
|
|
17
|
+
return t.json({ method: "POST", path: `${base()}:run`, body, ...request });
|
|
18
|
+
for await (const event of t.events({
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: `${base()}:run`,
|
|
21
|
+
body: { ...body, stream: true },
|
|
22
|
+
...request,
|
|
23
|
+
})) {
|
|
24
|
+
if (event.k === "stdout")
|
|
25
|
+
onStdout?.(event.text);
|
|
26
|
+
else if (event.k === "stderr")
|
|
27
|
+
onStderr?.(event.text);
|
|
28
|
+
else if (event.k === "result")
|
|
29
|
+
onResult?.({ main: event.main, data: event.data, refs: event.refs });
|
|
30
|
+
else if (event.k === "error")
|
|
31
|
+
onError?.(event);
|
|
32
|
+
else if (event.k === "execution")
|
|
33
|
+
return event.execution;
|
|
34
|
+
else if (event.k === "failure")
|
|
35
|
+
throw new Error(`${event.code}: ${event.message}`);
|
|
36
|
+
}
|
|
37
|
+
throw new Error("The interpreter stream ended without a result");
|
|
38
|
+
},
|
|
39
|
+
contexts: {
|
|
40
|
+
list: async (options) => (await t.json({
|
|
41
|
+
method: "GET",
|
|
42
|
+
path: `${base()}/contexts`,
|
|
43
|
+
...options,
|
|
44
|
+
})).data,
|
|
45
|
+
create: (input = {}, options) => t.json({
|
|
46
|
+
method: "POST",
|
|
47
|
+
path: `${base()}/contexts`,
|
|
48
|
+
body: input,
|
|
49
|
+
...options,
|
|
50
|
+
}),
|
|
51
|
+
restart: (id, options) => t.json({
|
|
52
|
+
method: "POST",
|
|
53
|
+
path: `${base()}/contexts/${encodeURIComponent(id)}:restart`,
|
|
54
|
+
body: {},
|
|
55
|
+
...options,
|
|
56
|
+
}),
|
|
57
|
+
interrupt: (id, options) => t.json({
|
|
58
|
+
method: "POST",
|
|
59
|
+
path: `${base()}/contexts/${encodeURIComponent(id)}:interrupt`,
|
|
60
|
+
body: {},
|
|
61
|
+
...options,
|
|
62
|
+
}),
|
|
63
|
+
remove: (id, options) => t.json({
|
|
64
|
+
method: "DELETE",
|
|
65
|
+
path: `${base()}/contexts/${encodeURIComponent(id)}`,
|
|
66
|
+
...options,
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
/** The bytes of a result too large to travel inline (a `refs` entry). */
|
|
70
|
+
result(ref, options) {
|
|
71
|
+
const match = /^\/workspace\/\.runtime\/interpreter\/([a-z0-9][a-z0-9-]*)\/out\/([A-Za-z0-9][A-Za-z0-9_.-]*)$/.exec(ref.path);
|
|
72
|
+
if (!match)
|
|
73
|
+
throw new Error("Not an interpreter result path");
|
|
74
|
+
return t.bytes({
|
|
75
|
+
method: "GET",
|
|
76
|
+
path: `${base()}/contexts/${match[1]}/results/${match[2]}`,
|
|
77
|
+
...options,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
/** A sandbox's own network rules. */
|
|
4
|
+
export type NetworkRules = {
|
|
5
|
+
/** false refuses every outbound connection. */
|
|
6
|
+
internet: boolean;
|
|
7
|
+
/** When not empty, only these web destinations are reachable: `example.com`,
|
|
8
|
+
* `*.example.com` (subdomains, not the apex), an address or a CIDR range. */
|
|
9
|
+
allow?: string[];
|
|
10
|
+
/** Never reachable. Wins over everything. */
|
|
11
|
+
deny?: string[];
|
|
12
|
+
/** host:port pairs beyond ports 80 and 443, such as `db.example.com:5432` or
|
|
13
|
+
* `github.com:22`. Paid sandboxes of organizations with a paid purchase
|
|
14
|
+
* only. In the sandbox, `python3 /usr/local/lib/runtime/guest-egress.py
|
|
15
|
+
* forward 5432 db.example.com:5432` gives psql a local port. */
|
|
16
|
+
connect?: string[];
|
|
17
|
+
};
|
|
18
|
+
export type NetworkPolicy = Required<NetworkRules> & {
|
|
19
|
+
sandboxId: string;
|
|
20
|
+
version: number;
|
|
21
|
+
/** The sandbox's host confirmed it enforces this version. */
|
|
22
|
+
enforced: boolean;
|
|
23
|
+
enforcedVersion: number;
|
|
24
|
+
/** Ports reachable without a connect entry. */
|
|
25
|
+
ports: number[];
|
|
26
|
+
connectAllowed: boolean;
|
|
27
|
+
/** paid, granted, not_paid, revoked or suspended. */
|
|
28
|
+
connectReason: string;
|
|
29
|
+
forbiddenPorts: number[];
|
|
30
|
+
updatedAt: string | null;
|
|
31
|
+
};
|
|
32
|
+
/** `sbx.network`: turn the sandbox's internet off or on, narrow it to a list,
|
|
33
|
+
* refuse destinations, or open host:port pairs. Changes apply at once, to open
|
|
34
|
+
* connections too.
|
|
35
|
+
*
|
|
36
|
+
* await sbx.network.set({ internet: true, allow: ["registry.npmjs.org"] });
|
|
37
|
+
* await sbx.network.set({ internet: false });
|
|
38
|
+
*/
|
|
39
|
+
export declare function sandboxNetwork(t: Transport, sandbox: Sandbox): {
|
|
40
|
+
get: (options?: RequestOptions) => Promise<NetworkPolicy>;
|
|
41
|
+
/** Replaces the rules. */
|
|
42
|
+
set: (rules: NetworkRules, options?: RequestOptions) => Promise<NetworkPolicy>;
|
|
43
|
+
/** No outbound connections at all. */
|
|
44
|
+
off: (options?: RequestOptions) => Promise<NetworkPolicy>;
|
|
45
|
+
/** The host's public-web policy and nothing narrower. */
|
|
46
|
+
on: (options?: RequestOptions) => Promise<NetworkPolicy>;
|
|
47
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** `sbx.network`: turn the sandbox's internet off or on, narrow it to a list,
|
|
2
|
+
* refuse destinations, or open host:port pairs. Changes apply at once, to open
|
|
3
|
+
* connections too.
|
|
4
|
+
*
|
|
5
|
+
* await sbx.network.set({ internet: true, allow: ["registry.npmjs.org"] });
|
|
6
|
+
* await sbx.network.set({ internet: false });
|
|
7
|
+
*/
|
|
8
|
+
export function sandboxNetwork(t, sandbox) {
|
|
9
|
+
const path = () => `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/network`;
|
|
10
|
+
return {
|
|
11
|
+
get: (options) => t.json({ method: "GET", path: path(), ...options }),
|
|
12
|
+
/** Replaces the rules. */
|
|
13
|
+
set: (rules, options) => t.json({ method: "PUT", path: path(), body: rules, ...options }),
|
|
14
|
+
/** No outbound connections at all. */
|
|
15
|
+
off: (options) => t.json({ method: "PUT", path: path(), body: { internet: false }, ...options }),
|
|
16
|
+
/** The host's public-web policy and nothing narrower. */
|
|
17
|
+
on: (options) => t.json({ method: "PUT", path: path(), body: { internet: true }, ...options }),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Sandbox } from "../sandbox.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
/** A public HTTPS address for a port inside a sandbox. */
|
|
4
|
+
export type Preview = {
|
|
5
|
+
id: string;
|
|
6
|
+
sandboxId: string;
|
|
7
|
+
port: number;
|
|
8
|
+
visibility: "private" | "public";
|
|
9
|
+
/** `https://<port>-<sandbox>.<preview domain>/`. A private preview also
|
|
10
|
+
* needs the token. */
|
|
11
|
+
url: string;
|
|
12
|
+
/** Private previews: a fresh signed token. Send it as the
|
|
13
|
+
* `x-runtime-preview-token` header. */
|
|
14
|
+
token: string | null;
|
|
15
|
+
tokenExpiresAt: string | null;
|
|
16
|
+
/** Private previews: a link that carries the token once, for a browser. */
|
|
17
|
+
urlWithToken: string | null;
|
|
18
|
+
/** Turned off by Runtime after a report. */
|
|
19
|
+
disabled: boolean;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
};
|
|
22
|
+
export type CreatePreview = {
|
|
23
|
+
/** private (default): a token is needed. public: anyone with the address;
|
|
24
|
+
* a browser sees a one-time warning page naming Runtime. */
|
|
25
|
+
visibility?: "private" | "public";
|
|
26
|
+
/** How long the returned token lasts, 60 s to 7 days (default 1 day). */
|
|
27
|
+
ttlSeconds?: number;
|
|
28
|
+
};
|
|
29
|
+
/** `sbx.previews`: share ports of this sandbox at public HTTPS addresses.
|
|
30
|
+
*
|
|
31
|
+
* const { url, token } = await sbx.previews.create(3000);
|
|
32
|
+
* await fetch(url, { headers: { "x-runtime-preview-token": token! } });
|
|
33
|
+
*
|
|
34
|
+
* WebSockets work. The server must listen on 0.0.0.0 or localhost. */
|
|
35
|
+
export declare function sandboxPreviews(t: Transport, sandbox: Sandbox): {
|
|
36
|
+
/** Shares `port`, or changes its visibility if it is shared already. */
|
|
37
|
+
create: (port: number, input?: CreatePreview, options?: RequestOptions) => Promise<Preview>;
|
|
38
|
+
/** Every shared port, each private one with a fresh token. */
|
|
39
|
+
list: (options?: RequestOptions) => Promise<Preview[]>;
|
|
40
|
+
/** One preview, with a fresh token of `ttlSeconds` if it is private. */
|
|
41
|
+
get: (port: number, ttlSeconds?: number, options?: RequestOptions) => Promise<Preview>;
|
|
42
|
+
/** Refuses every token issued for this port so far and returns a new one. */
|
|
43
|
+
rotate: (port: number, options?: RequestOptions) => Promise<Preview>;
|
|
44
|
+
/** Stops sharing `port`. Open connections close within seconds. */
|
|
45
|
+
delete: (port: number, options?: RequestOptions) => Promise<{
|
|
46
|
+
id: string;
|
|
47
|
+
sandboxId: string;
|
|
48
|
+
port: number;
|
|
49
|
+
deleted: true;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** `sbx.previews`: share ports of this sandbox at public HTTPS addresses.
|
|
2
|
+
*
|
|
3
|
+
* const { url, token } = await sbx.previews.create(3000);
|
|
4
|
+
* await fetch(url, { headers: { "x-runtime-preview-token": token! } });
|
|
5
|
+
*
|
|
6
|
+
* WebSockets work. The server must listen on 0.0.0.0 or localhost. */
|
|
7
|
+
export function sandboxPreviews(t, sandbox) {
|
|
8
|
+
const base = () => `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/previews`;
|
|
9
|
+
return {
|
|
10
|
+
/** Shares `port`, or changes its visibility if it is shared already. */
|
|
11
|
+
create: (port, input = {}, options) => t.json({ method: "POST", path: base(), body: { port, ...input }, ...options }),
|
|
12
|
+
/** Every shared port, each private one with a fresh token. */
|
|
13
|
+
list: async (options) => (await t.json({ method: "GET", path: base(), ...options })).data,
|
|
14
|
+
/** One preview, with a fresh token of `ttlSeconds` if it is private. */
|
|
15
|
+
get: (port, ttlSeconds, options) => t.json({
|
|
16
|
+
method: "GET",
|
|
17
|
+
path: `${base()}/${port}`,
|
|
18
|
+
...(ttlSeconds ? { query: { ttlSeconds } } : {}),
|
|
19
|
+
...options,
|
|
20
|
+
}),
|
|
21
|
+
/** Refuses every token issued for this port so far and returns a new one. */
|
|
22
|
+
rotate: (port, options) => t.json({ method: "POST", path: `${base()}/${port}:rotate`, ...options }),
|
|
23
|
+
/** Stops sharing `port`. Open connections close within seconds. */
|
|
24
|
+
delete: (port, options) => t.json({
|
|
25
|
+
method: "DELETE",
|
|
26
|
+
path: `${base()}/${port}`,
|
|
27
|
+
...options,
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
2
|
+
/** Your organization's referral link and what it has earned. Money is integer
|
|
3
|
+
* microdollars in strings (1,000,000 = $1). */
|
|
4
|
+
export type ReferralSummary = {
|
|
5
|
+
code: string;
|
|
6
|
+
/** Share this: https://withruntime.com/r/<code>. */
|
|
7
|
+
link: string;
|
|
8
|
+
/** The least each side gets: a first top-up is matched, never below this. */
|
|
9
|
+
rewardMicros: string;
|
|
10
|
+
/** The most each side gets: a first top-up is matched up to this. */
|
|
11
|
+
maxRewardMicros: string;
|
|
12
|
+
/** The smallest first top-up that pays a referral. */
|
|
13
|
+
minPurchaseMicros: string;
|
|
14
|
+
/** The most referral credit you can earn in one calendar year (UTC). */
|
|
15
|
+
yearlyCapMicros: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
year: number;
|
|
18
|
+
signedUp: number;
|
|
19
|
+
pending: number;
|
|
20
|
+
paid: number;
|
|
21
|
+
capped: number;
|
|
22
|
+
reversed: number;
|
|
23
|
+
earnedMicros: string;
|
|
24
|
+
earnedThisYearMicros: string;
|
|
25
|
+
capRemainingMicros: string;
|
|
26
|
+
referredBy: {
|
|
27
|
+
status: "pending" | "paid" | "refused" | "reversed";
|
|
28
|
+
creditMicros: string;
|
|
29
|
+
} | null;
|
|
30
|
+
};
|
|
31
|
+
/** `runtime.referrals.get()`: when a company that signs up with your link makes
|
|
32
|
+
* its first top-up of at least $10, you and it each get credit equal to that
|
|
33
|
+
* top-up, at least $25 and at most $500. */
|
|
34
|
+
export declare function referrals(t: Transport): {
|
|
35
|
+
get: (options?: RequestOptions) => Promise<ReferralSummary>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** `runtime.referrals.get()`: when a company that signs up with your link makes
|
|
2
|
+
* its first top-up of at least $10, you and it each get credit equal to that
|
|
3
|
+
* top-up, at least $25 and at most $500. */
|
|
4
|
+
export function referrals(t) {
|
|
5
|
+
return {
|
|
6
|
+
get: (options) => t.json({ method: "GET", path: "/v1/referrals", ...options }),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Page } from "../page.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
export type VolumeState = "creating" | "ready" | "failed" | "deleting" | "deleted";
|
|
4
|
+
export type Volume = {
|
|
5
|
+
id: string;
|
|
6
|
+
kind: "volume";
|
|
7
|
+
status: string;
|
|
8
|
+
state: VolumeState;
|
|
9
|
+
name: string | null;
|
|
10
|
+
labels: Record<string, string>;
|
|
11
|
+
region: string;
|
|
12
|
+
sizeMiB: number;
|
|
13
|
+
filesystem: "ext4";
|
|
14
|
+
usedMiB: number | null;
|
|
15
|
+
measuredAt: string | null;
|
|
16
|
+
/** Always false: a volume lives on one host and is not copied off it. */
|
|
17
|
+
backedUp: false;
|
|
18
|
+
attachments: {
|
|
19
|
+
sandboxId: string;
|
|
20
|
+
mode: "rw" | "snapshot";
|
|
21
|
+
path: string;
|
|
22
|
+
attachedAt: string;
|
|
23
|
+
}[];
|
|
24
|
+
error: string | null;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
readyAt: string | null;
|
|
27
|
+
};
|
|
28
|
+
export type CreateVolume = {
|
|
29
|
+
sizeMiB: number;
|
|
30
|
+
name?: string;
|
|
31
|
+
labels?: Record<string, string>;
|
|
32
|
+
region?: string;
|
|
33
|
+
};
|
|
34
|
+
/** Persistent disks: create once, attach when creating a sandbox with
|
|
35
|
+
* `volumes: [{ volumeId: v.id, path: "/data" }]`. Not backed up off its host. */
|
|
36
|
+
export declare function volumes(t: Transport): {
|
|
37
|
+
/** Create and wait (up to 10 s) until it is ready. */
|
|
38
|
+
create: (input: CreateVolume, options?: RequestOptions) => Promise<Volume>;
|
|
39
|
+
get: (id: string, options?: RequestOptions) => Promise<Volume>;
|
|
40
|
+
list: (query?: {
|
|
41
|
+
state?: VolumeState;
|
|
42
|
+
name?: string;
|
|
43
|
+
limit?: number;
|
|
44
|
+
cursor?: string;
|
|
45
|
+
}, options?: RequestOptions) => Promise<Page<Volume>>;
|
|
46
|
+
delete: (id: string, options?: RequestOptions) => Promise<Volume>;
|
|
47
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Page } from "../page.js";
|
|
2
|
+
/** Persistent disks: create once, attach when creating a sandbox with
|
|
3
|
+
* `volumes: [{ volumeId: v.id, path: "/data" }]`. Not backed up off its host. */
|
|
4
|
+
export function volumes(t) {
|
|
5
|
+
const get = (id, options) => t.json({ method: "GET", path: `/v1/volumes/${encodeURIComponent(id)}`, ...options });
|
|
6
|
+
const list = async (query = {}, options) => {
|
|
7
|
+
const page = await t.json({
|
|
8
|
+
method: "GET",
|
|
9
|
+
path: "/v1/volumes",
|
|
10
|
+
query,
|
|
11
|
+
...options,
|
|
12
|
+
});
|
|
13
|
+
return new Page(page.data, page.nextCursor, (cursor) => list({ ...query, cursor }, options));
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
/** Create and wait (up to 10 s) until it is ready. */
|
|
17
|
+
create: (input, options) => t.json({ method: "POST", path: "/v1/volumes", body: input, wait: 10, ...options }),
|
|
18
|
+
get,
|
|
19
|
+
list,
|
|
20
|
+
delete: (id, options) => t.json({
|
|
21
|
+
method: "POST",
|
|
22
|
+
path: `/v1/volumes/${encodeURIComponent(id)}:delete`,
|
|
23
|
+
body: {},
|
|
24
|
+
...options,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Page } from "./page.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "./transport.js";
|
|
3
|
+
import type { CommandResult, ExecOptions, FileEntry, OutputEvent, ProcessInfo, SandboxInfo } from "./types.js";
|
|
4
|
+
import { type SandboxExtensions } from "./products/index.js";
|
|
5
|
+
import type { Snapshot, SnapshotOptions } from "./snapshots.js";
|
|
6
|
+
/** A running (or stopped) sandbox. Every method is one API call or a short
|
|
7
|
+
* sequence of them; `await using sbx = await Sandbox.create()` stops it for
|
|
8
|
+
* you at the end of the block. */
|
|
9
|
+
export interface Sandbox extends SandboxExtensions {
|
|
10
|
+
}
|
|
11
|
+
export declare class Sandbox implements AsyncDisposable {
|
|
12
|
+
#private;
|
|
13
|
+
readonly files: Files;
|
|
14
|
+
readonly processes: Processes;
|
|
15
|
+
constructor(transport: Transport, info: SandboxInfo);
|
|
16
|
+
get id(): string;
|
|
17
|
+
/** What the API last said about this sandbox. `refresh()` asks again. */
|
|
18
|
+
get info(): SandboxInfo;
|
|
19
|
+
get state(): SandboxInfo["state"];
|
|
20
|
+
refresh(options?: RequestOptions): Promise<this>;
|
|
21
|
+
/** Waits (server-side, no polling) until the sandbox reaches `state`. */
|
|
22
|
+
waitFor(state: "running" | "paused" | "stopped", options?: {
|
|
23
|
+
timeoutSeconds?: number;
|
|
24
|
+
} & RequestOptions): Promise<this>;
|
|
25
|
+
/** Runs a command and returns its exit code and output. A string runs under
|
|
26
|
+
* `bash -c`; an array runs without a shell. With onStdout/onStderr the output
|
|
27
|
+
* streams as it happens. A timeout is a result (timedOut: true, the output so
|
|
28
|
+
* far), never an error; `check: true` throws CommandError on a non-zero exit. */
|
|
29
|
+
exec(command: string | readonly string[], options?: ExecOptions): Promise<CommandResult>;
|
|
30
|
+
/** The command's events as they happen: start, stdout, stderr, exit. Resumes
|
|
31
|
+
* by itself when the server ends a long stream, so it never drops output. */
|
|
32
|
+
execStream(command: string | readonly string[], options?: ExecOptions): AsyncGenerator<OutputEvent>;
|
|
33
|
+
/** Starts a background process (a server, a watcher, a REPL) and returns at
|
|
34
|
+
* once. `stdin: "pipe"` keeps its input open for write(); `pty` gives it a
|
|
35
|
+
* terminal. */
|
|
36
|
+
spawn(command: string | readonly string[], options?: Omit<ExecOptions, "onStdout" | "onStderr" | "check" | "stdin"> & {
|
|
37
|
+
/** "pipe" keeps input open for write(); any other text is given once, then closed. */
|
|
38
|
+
stdin?: string;
|
|
39
|
+
pty?: {
|
|
40
|
+
cols?: number;
|
|
41
|
+
rows?: number;
|
|
42
|
+
};
|
|
43
|
+
}): Promise<Process>;
|
|
44
|
+
/** An interactive terminal over a WebSocket. Bytes you write are typed;
|
|
45
|
+
* onData receives what the terminal prints. */
|
|
46
|
+
terminal(options?: {
|
|
47
|
+
cols?: number;
|
|
48
|
+
rows?: number;
|
|
49
|
+
command?: string;
|
|
50
|
+
cwd?: string;
|
|
51
|
+
/** Attach to a process started with a pty instead of a new shell. */
|
|
52
|
+
processId?: string;
|
|
53
|
+
onData?: (data: Uint8Array) => void;
|
|
54
|
+
}): Promise<Terminal>;
|
|
55
|
+
stop(options?: RequestOptions & {
|
|
56
|
+
wait?: boolean;
|
|
57
|
+
}): Promise<this>;
|
|
58
|
+
pause(options?: RequestOptions & {
|
|
59
|
+
wait?: boolean;
|
|
60
|
+
}): Promise<this>;
|
|
61
|
+
/** Carries on a paused sandbox; `timeoutSeconds` is its new lease. */
|
|
62
|
+
wake(options?: RequestOptions & {
|
|
63
|
+
wait?: boolean;
|
|
64
|
+
timeoutSeconds?: number;
|
|
65
|
+
}): Promise<this>;
|
|
66
|
+
/** More time before the lease ends (at most an hour ahead of now). */
|
|
67
|
+
extend(seconds: number, options?: RequestOptions): Promise<this>;
|
|
68
|
+
/** Days a paused sandbox is kept before deletion (1 to 365). */
|
|
69
|
+
setRetention(days: number, options?: RequestOptions): Promise<this>;
|
|
70
|
+
/** Keeps this sandbox's whole machine (files, memory, running processes) as
|
|
71
|
+
* a snapshot to start new sandboxes from. A running sandbox is paused for
|
|
72
|
+
* the moment it takes, then woken; a paused one stays paused. */
|
|
73
|
+
snapshot(options?: SnapshotOptions & RequestOptions): Promise<Snapshot>;
|
|
74
|
+
/** Copies of this sandbox as it is now (files, memory, running processes),
|
|
75
|
+
* each its own sandbox, answered once they run. A running sandbox is paused
|
|
76
|
+
* for the moment its snapshot takes, then woken. One without `count`; an
|
|
77
|
+
* array with it. */
|
|
78
|
+
fork(options?: {
|
|
79
|
+
name?: string;
|
|
80
|
+
labels?: Record<string, string>;
|
|
81
|
+
keepSnapshot?: boolean;
|
|
82
|
+
} & RequestOptions): Promise<Sandbox>;
|
|
83
|
+
fork(options: {
|
|
84
|
+
count: number;
|
|
85
|
+
name?: string;
|
|
86
|
+
labels?: Record<string, string>;
|
|
87
|
+
keepSnapshot?: boolean;
|
|
88
|
+
} & RequestOptions): Promise<Sandbox[]>;
|
|
89
|
+
/** Starts a stopped persistent sandbox again from its disk (memory is not kept). */
|
|
90
|
+
restart(options?: RequestOptions & {
|
|
91
|
+
wait?: boolean;
|
|
92
|
+
}): Promise<this>;
|
|
93
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
94
|
+
toJSON(): SandboxInfo;
|
|
95
|
+
}
|
|
96
|
+
export declare class Processes {
|
|
97
|
+
private readonly t;
|
|
98
|
+
private readonly sandboxId;
|
|
99
|
+
constructor(t: Transport, sandboxId: string);
|
|
100
|
+
list(): Promise<ProcessInfo[]>;
|
|
101
|
+
get(processId: string): Promise<Process>;
|
|
102
|
+
/** Output events from `cursor` until the process exits, across stream slices. */
|
|
103
|
+
follow(processId: string, options?: {
|
|
104
|
+
cursor?: number;
|
|
105
|
+
signal?: AbortSignal;
|
|
106
|
+
}): AsyncGenerator<OutputEvent>;
|
|
107
|
+
}
|
|
108
|
+
/** A background process: its output, its input, its end. */
|
|
109
|
+
export declare class Process {
|
|
110
|
+
#private;
|
|
111
|
+
private readonly t;
|
|
112
|
+
readonly sandboxId: string;
|
|
113
|
+
info: ProcessInfo;
|
|
114
|
+
constructor(t: Transport, sandboxId: string, info: ProcessInfo);
|
|
115
|
+
get id(): string;
|
|
116
|
+
/** Every output event from the start (or `cursor`) until exit. */
|
|
117
|
+
output(options?: {
|
|
118
|
+
cursor?: number;
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
}): AsyncGenerator<OutputEvent>;
|
|
121
|
+
/** Waits for the process to end and returns its result. */
|
|
122
|
+
wait(options?: {
|
|
123
|
+
signal?: AbortSignal;
|
|
124
|
+
}): Promise<CommandResult>;
|
|
125
|
+
/** Sends input. Offsets are tracked for you, so a retried write is never typed twice. */
|
|
126
|
+
write(data: string | Uint8Array, options?: {
|
|
127
|
+
eof?: boolean;
|
|
128
|
+
}): Promise<void>;
|
|
129
|
+
kill(signal?: "SIGTERM" | "SIGKILL" | "SIGINT" | "SIGHUP" | "SIGQUIT" | "SIGUSR1" | "SIGUSR2"): Promise<void>;
|
|
130
|
+
resize(cols: number, rows: number): Promise<void>;
|
|
131
|
+
refresh(): Promise<ProcessInfo>;
|
|
132
|
+
}
|
|
133
|
+
export declare class Terminal {
|
|
134
|
+
#private;
|
|
135
|
+
private readonly socket;
|
|
136
|
+
readonly exited: Promise<number | null>;
|
|
137
|
+
private constructor();
|
|
138
|
+
get processId(): string | undefined;
|
|
139
|
+
static open(socket: WebSocket, onData?: (data: Uint8Array) => void): Promise<Terminal>;
|
|
140
|
+
write(data: string | Uint8Array): void;
|
|
141
|
+
resize(cols: number, rows: number): void;
|
|
142
|
+
close(): void;
|
|
143
|
+
}
|
|
144
|
+
/** Files in the sandbox. Paths are absolute; any path the sandbox user may use. */
|
|
145
|
+
export declare class Files {
|
|
146
|
+
#private;
|
|
147
|
+
private readonly t;
|
|
148
|
+
private readonly sandboxId;
|
|
149
|
+
constructor(t: Transport, sandboxId: string);
|
|
150
|
+
/** A file's bytes, any size. */
|
|
151
|
+
read(path: string, options?: RequestOptions): Promise<Uint8Array>;
|
|
152
|
+
readText(path: string, options?: RequestOptions): Promise<string>;
|
|
153
|
+
/** Writes a file of any size, atomically, making parent directories. Large
|
|
154
|
+
* ones go in parallel 1 MiB chunks checked against their SHA-256. */
|
|
155
|
+
write(path: string, data: string | Uint8Array, options?: RequestOptions): Promise<{
|
|
156
|
+
path: string;
|
|
157
|
+
size: number;
|
|
158
|
+
}>;
|
|
159
|
+
/** Directory entries; `depth` goes deeper, `glob` filters (e.g. "**\/*.py"). */
|
|
160
|
+
list(path?: string, options?: {
|
|
161
|
+
depth?: number;
|
|
162
|
+
glob?: string;
|
|
163
|
+
hidden?: boolean;
|
|
164
|
+
limit?: number;
|
|
165
|
+
}): Promise<FileEntry[]>;
|
|
166
|
+
glob(pattern: string, root?: string): Promise<FileEntry[]>;
|
|
167
|
+
stat(path: string): Promise<(FileEntry & {
|
|
168
|
+
exists: true;
|
|
169
|
+
}) | {
|
|
170
|
+
exists: false;
|
|
171
|
+
path: string;
|
|
172
|
+
}>;
|
|
173
|
+
exists(path: string): Promise<boolean>;
|
|
174
|
+
mkdir(path: string, options?: {
|
|
175
|
+
parents?: boolean;
|
|
176
|
+
}): Promise<void>;
|
|
177
|
+
remove(path: string, options?: {
|
|
178
|
+
recursive?: boolean;
|
|
179
|
+
}): Promise<boolean>;
|
|
180
|
+
rename(from: string, to: string, options?: {
|
|
181
|
+
overwrite?: boolean;
|
|
182
|
+
}): Promise<void>;
|
|
183
|
+
/** Copies a local file or directory into the sandbox. A directory travels as
|
|
184
|
+
* one gzipped tar and is unpacked in place. */
|
|
185
|
+
upload(localPath: string, remotePath: string): Promise<void>;
|
|
186
|
+
/** Copies a file or directory out of the sandbox. */
|
|
187
|
+
download(remotePath: string, localPath: string): Promise<void>;
|
|
188
|
+
}
|
|
189
|
+
export declare function sandboxPage(t: Transport, body: {
|
|
190
|
+
data: SandboxInfo[];
|
|
191
|
+
nextCursor: string | null;
|
|
192
|
+
}, query: Record<string, unknown>): Page<Sandbox>;
|