withruntime 0.6.1 → 0.7.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/E2B.md +32 -32
- package/README.md +3 -3
- package/dist/ai/index.d.ts +21 -5
- package/dist/ai/index.js +14 -21
- package/dist/ai-harness/index.js +15 -2
- package/dist/billing-cli.d.ts +21 -0
- package/dist/billing-cli.js +158 -0
- package/dist/cli-extras.d.ts +15 -0
- package/dist/cli-extras.js +60 -4
- package/dist/cli-name.d.ts +6 -0
- package/dist/cli-name.js +8 -0
- package/dist/cli.d.ts +17 -1
- package/dist/cli.js +543 -132
- package/dist/client.d.ts +35 -6
- package/dist/client.js +23 -0
- package/dist/credentials.js +1 -2
- package/dist/daytona/filesystem.d.ts +2 -0
- package/dist/daytona/filesystem.js +9 -7
- package/dist/e2b/commands.js +11 -2
- package/dist/e2b/errors.d.ts +5 -0
- package/dist/e2b/errors.js +3 -0
- package/dist/e2b/filesystem.js +3 -2
- package/dist/e2b/sandbox.d.ts +9 -6
- package/dist/e2b/sandbox.js +31 -7
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +7 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1 -0
- package/dist/keys.js +1 -1
- package/dist/login.d.ts +2 -6
- package/dist/login.js +3 -8
- package/dist/network-cli.d.ts +1 -1
- package/dist/network-cli.js +64 -2
- package/dist/products/billing.d.ts +35 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/index.d.ts +4 -1
- package/dist/products/index.js +4 -1
- package/dist/products/network-products.d.ts +45 -0
- package/dist/products/network-products.js +27 -0
- package/dist/products/previews.d.ts +1 -2
- package/dist/products/secrets.d.ts +14 -0
- package/dist/sandbox.d.ts +17 -3
- package/dist/sandbox.js +233 -62
- package/dist/transport.d.ts +17 -1
- package/dist/transport.js +124 -7
- package/dist/types.d.ts +0 -2
- package/dist/wallet.d.ts +30 -0
- package/dist/wallet.js +48 -0
- package/package.json +14 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RuntimeError } from "./errors.js";
|
|
1
2
|
import type { Page } from "./page.js";
|
|
2
3
|
import { type ClientExtensions } from "./products/index.js";
|
|
3
4
|
import { Sandbox } from "./sandbox.js";
|
|
@@ -33,6 +34,7 @@ export declare class Runtime {
|
|
|
33
34
|
readonly snapshots: Snapshots;
|
|
34
35
|
readonly feedback: FeedbackApi;
|
|
35
36
|
readonly support: SupportApi;
|
|
37
|
+
readonly account: AccountApi;
|
|
36
38
|
constructor(options?: RuntimeOptions);
|
|
37
39
|
/** Who this key is: organization, agent and credential. */
|
|
38
40
|
me(options?: RequestOptions): Promise<{
|
|
@@ -49,6 +51,16 @@ export declare class Runtime {
|
|
|
49
51
|
/** Credit, holds, trial time and per-resource charges. */
|
|
50
52
|
usage(options?: RequestOptions): Promise<Usage>;
|
|
51
53
|
}
|
|
54
|
+
/** How `sandboxes.create` waits when every trial slot, the account's quota or
|
|
55
|
+
* the region is full. */
|
|
56
|
+
export type CreateOptions = RequestOptions & {
|
|
57
|
+
/** How long to keep retrying a refusal that clears by itself. Default: the
|
|
58
|
+
* client's, two minutes; 0 fails at once. */
|
|
59
|
+
waitForCapacityMs?: number;
|
|
60
|
+
/** Called before each wait, with the refusal and the milliseconds until the
|
|
61
|
+
* next try: to tell a person why nothing has happened yet. */
|
|
62
|
+
onCapacityWait?: (refusal: RuntimeError, waitMs: number) => void;
|
|
63
|
+
};
|
|
52
64
|
export declare class Sandboxes {
|
|
53
65
|
private readonly t;
|
|
54
66
|
constructor(t: Transport);
|
|
@@ -59,18 +71,14 @@ export declare class Sandboxes {
|
|
|
59
71
|
* free, up to `waitForCapacityMs` (the client's, two minutes by default). */
|
|
60
72
|
create(input?: CreateSandbox & {
|
|
61
73
|
wait?: boolean;
|
|
62
|
-
}, options?:
|
|
63
|
-
waitForCapacityMs?: number;
|
|
64
|
-
}): Promise<Sandbox>;
|
|
74
|
+
}, options?: CreateOptions): Promise<Sandbox>;
|
|
65
75
|
/** The sandbox named `name` in this account, ready to use: running as it
|
|
66
76
|
* is, woken if it is paused, restarted if it is stopped and persistent, or
|
|
67
77
|
* created with `input` when no sandbox has the name. `sandbox.info.reused`
|
|
68
78
|
* says which. The other fields apply only when it is created. */
|
|
69
79
|
getOrCreate(name: string, input?: Omit<CreateSandbox, "name" | "getOrCreate"> & {
|
|
70
80
|
wait?: boolean;
|
|
71
|
-
}, options?:
|
|
72
|
-
waitForCapacityMs?: number;
|
|
73
|
-
}): Promise<Sandbox>;
|
|
81
|
+
}, options?: CreateOptions): Promise<Sandbox>;
|
|
74
82
|
get(id: string, options?: RequestOptions): Promise<Sandbox>;
|
|
75
83
|
/** Live sandboxes, oldest first. Await for a page, or `for await` over all. */
|
|
76
84
|
list(filter?: {
|
|
@@ -142,5 +150,26 @@ export declare class SupportApi {
|
|
|
142
150
|
}): Promise<SupportReply>;
|
|
143
151
|
read(conversationId: string): Promise<SupportReply>;
|
|
144
152
|
}
|
|
153
|
+
/** What closing the account answered. */
|
|
154
|
+
export type ClosedAccount = {
|
|
155
|
+
orgId: string;
|
|
156
|
+
name: string;
|
|
157
|
+
closedAt: string;
|
|
158
|
+
/** False while what was running is still stopping; it is gone within minutes. */
|
|
159
|
+
released: boolean;
|
|
160
|
+
};
|
|
161
|
+
/** The account this key belongs to: `runtime.account`. */
|
|
162
|
+
export declare class AccountApi {
|
|
163
|
+
private readonly t;
|
|
164
|
+
constructor(t: Transport);
|
|
165
|
+
/** Close the account for good: stop and delete everything it runs and
|
|
166
|
+
* stores, end every key and dissolve it. `confirm` must be the account's
|
|
167
|
+
* name exactly (`me().orgName`). Needs a key for every product made by an
|
|
168
|
+
* owner. Moves no money: what is unspent of a purchase made in the last 15
|
|
169
|
+
* days is refunded on request; other credit is forfeited. */
|
|
170
|
+
close(input: {
|
|
171
|
+
confirm: string;
|
|
172
|
+
}, options?: RequestOptions): Promise<ClosedAccount>;
|
|
173
|
+
}
|
|
145
174
|
/** The client behind the static helpers, from the environment. */
|
|
146
175
|
export declare function defaultClient(): Runtime;
|
package/dist/client.js
CHANGED
|
@@ -20,6 +20,7 @@ export class Runtime {
|
|
|
20
20
|
snapshots;
|
|
21
21
|
feedback;
|
|
22
22
|
support;
|
|
23
|
+
account;
|
|
23
24
|
constructor(options = {}) {
|
|
24
25
|
this.transport = new Transport({
|
|
25
26
|
// A key given, then RUNTIME_API_KEY, then (in Node and Bun) the key
|
|
@@ -38,6 +39,7 @@ export class Runtime {
|
|
|
38
39
|
this.snapshots = new Snapshots(this.transport);
|
|
39
40
|
this.feedback = new FeedbackApi(this.transport);
|
|
40
41
|
this.support = new SupportApi(this.transport);
|
|
42
|
+
this.account = new AccountApi(this.transport);
|
|
41
43
|
for (const [name, make] of clientFactories())
|
|
42
44
|
Object.defineProperty(this, name, { value: make(this.transport), enumerable: true });
|
|
43
45
|
}
|
|
@@ -165,6 +167,27 @@ export class SupportApi {
|
|
|
165
167
|
});
|
|
166
168
|
}
|
|
167
169
|
}
|
|
170
|
+
/** The account this key belongs to: `runtime.account`. */
|
|
171
|
+
export class AccountApi {
|
|
172
|
+
t;
|
|
173
|
+
constructor(t) {
|
|
174
|
+
this.t = t;
|
|
175
|
+
}
|
|
176
|
+
/** Close the account for good: stop and delete everything it runs and
|
|
177
|
+
* stores, end every key and dissolve it. `confirm` must be the account's
|
|
178
|
+
* name exactly (`me().orgName`). Needs a key for every product made by an
|
|
179
|
+
* owner. Moves no money: what is unspent of a purchase made in the last 15
|
|
180
|
+
* days is refunded on request; other credit is forfeited. */
|
|
181
|
+
close(input, options = {}) {
|
|
182
|
+
return this.t.json({
|
|
183
|
+
method: "POST",
|
|
184
|
+
path: "/v1/account:close",
|
|
185
|
+
body: { confirm: input.confirm },
|
|
186
|
+
retry: false,
|
|
187
|
+
...options,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
168
191
|
let shared;
|
|
169
192
|
/** The client behind the static helpers, from the environment. */
|
|
170
193
|
export function defaultClient() {
|
package/dist/credentials.js
CHANGED
|
@@ -3,8 +3,7 @@ import { constants } from "node:fs";
|
|
|
3
3
|
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { isAbsolute, join } from "node:path";
|
|
6
|
-
import { apiOrigin } from "./
|
|
7
|
-
import { missingKey } from "./transport.js";
|
|
6
|
+
import { apiOrigin, missingKey } from "./transport.js";
|
|
8
7
|
/** How many other accounts' connections one machine keeps beside the one in use. */
|
|
9
8
|
const SAVED_ACCOUNTS = 20;
|
|
10
9
|
export function connectionOrigins(env) {
|
|
@@ -60,6 +60,8 @@ export declare class FileSystem {
|
|
|
60
60
|
/** The file's bytes, or with a local path, the file written there. */
|
|
61
61
|
downloadFile(remotePath: string, timeout?: number): Promise<Buffer>;
|
|
62
62
|
downloadFile(remotePath: string, localPath: string, timeout?: number): Promise<void>;
|
|
63
|
+
/** The file as it arrives, without holding it in memory. It errors with
|
|
64
|
+
* `download_incomplete` if it ends short of the file's length. */
|
|
63
65
|
downloadFileStream(remotePath: string): Promise<Readable>;
|
|
64
66
|
/** Several files; a file that fails carries `error` instead of `result`. */
|
|
65
67
|
downloadFiles(files: FileDownloadRequest[]): Promise<FileDownloadResponse[]>;
|
|
@@ -69,17 +69,19 @@ export class FileSystem {
|
|
|
69
69
|
async downloadFile(remotePath, localOrTimeout) {
|
|
70
70
|
const target = await this.#path(remotePath);
|
|
71
71
|
const files = await this.#files();
|
|
72
|
-
const bytes = Buffer.from(await guard("file", () => files.read(target)));
|
|
73
72
|
if (typeof localOrTimeout !== "string")
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
await mkdir(paths.dirname(localOrTimeout), { recursive: true });
|
|
78
|
-
await writeFile(localOrTimeout, bytes);
|
|
73
|
+
return Buffer.from(await guard("file", () => files.read(target)));
|
|
74
|
+
// Streamed to disk, any size, checked against the file's length.
|
|
75
|
+
await guard("file", () => files.download(target, localOrTimeout));
|
|
79
76
|
}
|
|
77
|
+
/** The file as it arrives, without holding it in memory. It errors with
|
|
78
|
+
* `download_incomplete` if it ends short of the file's length. */
|
|
80
79
|
async downloadFileStream(remotePath) {
|
|
81
80
|
const { Readable } = await import("node:stream");
|
|
82
|
-
|
|
81
|
+
const target = await this.#path(remotePath);
|
|
82
|
+
const files = await this.#files();
|
|
83
|
+
const stream = await guard("file", () => files.readStream(target));
|
|
84
|
+
return Readable.fromWeb(stream);
|
|
83
85
|
}
|
|
84
86
|
/** Several files; a file that fails carries `error` instead of `result`. */
|
|
85
87
|
async downloadFiles(files) {
|
package/dist/e2b/commands.js
CHANGED
|
@@ -28,7 +28,8 @@ function timedOut(timeoutMs) {
|
|
|
28
28
|
"Pass a larger 'timeoutMs', or 0 for no limit.");
|
|
29
29
|
}
|
|
30
30
|
/** A Runtime result as E2B's: a command killed by a signal has no exit code on
|
|
31
|
-
* Runtime; E2B reports -1.
|
|
31
|
+
* Runtime; E2B reports -1. E2B never loses output, so a result that did says
|
|
32
|
+
* so in `truncated` rather than passing as whole. */
|
|
32
33
|
function toResult(result) {
|
|
33
34
|
const exitCode = result.exitCode ?? -1;
|
|
34
35
|
return {
|
|
@@ -40,12 +41,15 @@ function toResult(result) {
|
|
|
40
41
|
: {
|
|
41
42
|
error: result.exitCode === null ? "terminated by a signal" : `exit status ${exitCode}`,
|
|
42
43
|
}),
|
|
44
|
+
...(result.truncated ? { truncated: true } : {}),
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
function settle(result, timeoutMs) {
|
|
46
48
|
if (result.timedOut)
|
|
47
49
|
throw timedOut(timeoutMs);
|
|
48
50
|
const out = toResult(result);
|
|
51
|
+
if (out.truncated && typeof process !== "undefined" && process.emitWarning)
|
|
52
|
+
process.emitWarning("Part of the command's output was dropped before it was read, so stdout and stderr are incomplete (result.truncated). Write large output to a file and read it with sandbox.files.read.", { code: "RUNTIME_E2B_OUTPUT_TRUNCATED" });
|
|
49
53
|
if (out.exitCode !== 0)
|
|
50
54
|
throw new CommandExitError(out);
|
|
51
55
|
return out;
|
|
@@ -80,7 +84,7 @@ export class Commands {
|
|
|
80
84
|
...(onStderr ? { onStderr: (text) => void onStderr(text) } : {}),
|
|
81
85
|
...(opts.signal ? { signal: opts.signal } : {}),
|
|
82
86
|
}));
|
|
83
|
-
return settle(result, timeoutMs);
|
|
87
|
+
return settle({ ...result, truncated: result.stdoutTruncated === true || result.stderrTruncated === true }, timeoutMs);
|
|
84
88
|
}
|
|
85
89
|
async #start(cmd, opts, timeoutMs) {
|
|
86
90
|
const process = await guard("sandbox", () => this.#ctx.runtime.spawn(cmd, {
|
|
@@ -166,6 +170,7 @@ export class CommandHandle {
|
|
|
166
170
|
#stdout = "";
|
|
167
171
|
#stderr = "";
|
|
168
172
|
#exit;
|
|
173
|
+
#truncated = false;
|
|
169
174
|
#failure;
|
|
170
175
|
#disconnected = false;
|
|
171
176
|
constructor(process, options) {
|
|
@@ -189,6 +194,9 @@ export class CommandHandle {
|
|
|
189
194
|
this.#stderr += event.data;
|
|
190
195
|
await options.onStderr?.(event.data);
|
|
191
196
|
}
|
|
197
|
+
else if (event.type === "truncated") {
|
|
198
|
+
this.#truncated = true;
|
|
199
|
+
}
|
|
192
200
|
else if (event.type === "exit") {
|
|
193
201
|
this.#exit = { exitCode: event.exitCode, timedOut: event.timedOut };
|
|
194
202
|
}
|
|
@@ -237,6 +245,7 @@ export class CommandHandle {
|
|
|
237
245
|
stdout: this.#stdout,
|
|
238
246
|
stderr: this.#stderr,
|
|
239
247
|
timedOut: this.#exit.timedOut,
|
|
248
|
+
truncated: this.#truncated,
|
|
240
249
|
}, this.#timeoutMs);
|
|
241
250
|
}
|
|
242
251
|
/** Stops receiving output; the command keeps running. */
|
package/dist/e2b/errors.d.ts
CHANGED
|
@@ -52,6 +52,10 @@ export interface CommandResult {
|
|
|
52
52
|
error?: string;
|
|
53
53
|
stdout: string;
|
|
54
54
|
stderr: string;
|
|
55
|
+
/** Runtime's addition, present only when true: some of the command's
|
|
56
|
+
* output was dropped before it was read, so `stdout` and `stderr` are
|
|
57
|
+
* missing part of it. A warning says so too. */
|
|
58
|
+
truncated?: true;
|
|
55
59
|
}
|
|
56
60
|
/** Thrown by `commands.run` and `CommandHandle.wait` when the command exits
|
|
57
61
|
* with a code other than 0. It carries the result, as in E2B. */
|
|
@@ -62,6 +66,7 @@ export declare class CommandExitError extends SandboxError implements CommandRes
|
|
|
62
66
|
get error(): string | undefined;
|
|
63
67
|
get stdout(): string;
|
|
64
68
|
get stderr(): string;
|
|
69
|
+
get truncated(): true | undefined;
|
|
65
70
|
}
|
|
66
71
|
/** Which object a call was about, to name a 404 the way E2B would. */
|
|
67
72
|
export type Subject = "sandbox" | "file" | "other";
|
package/dist/e2b/errors.js
CHANGED
|
@@ -92,6 +92,9 @@ export class CommandExitError extends SandboxError {
|
|
|
92
92
|
get stderr() {
|
|
93
93
|
return this.#result.stderr;
|
|
94
94
|
}
|
|
95
|
+
get truncated() {
|
|
96
|
+
return this.#result.truncated;
|
|
97
|
+
}
|
|
95
98
|
}
|
|
96
99
|
/** Turns a Runtime SDK error into the E2B error a caller written for E2B
|
|
97
100
|
* expects. Anything that is not a Runtime error passes through unchanged. */
|
package/dist/e2b/filesystem.js
CHANGED
|
@@ -79,14 +79,15 @@ export class Filesystem {
|
|
|
79
79
|
}
|
|
80
80
|
async read(path, opts = {}) {
|
|
81
81
|
const file = await this.#path(path, opts);
|
|
82
|
+
// A real stream, any size, never the whole file held first.
|
|
83
|
+
if (opts.format === "stream")
|
|
84
|
+
return guard("file", () => this.#files.readStream(file, request(opts)));
|
|
82
85
|
const bytes = await guard("file", () => this.#files.read(file, request(opts)));
|
|
83
86
|
switch (opts.format) {
|
|
84
87
|
case "bytes":
|
|
85
88
|
return bytes;
|
|
86
89
|
case "blob":
|
|
87
90
|
return new Blob([bytes]);
|
|
88
|
-
case "stream":
|
|
89
|
-
return new Blob([bytes]).stream();
|
|
90
91
|
default:
|
|
91
92
|
return new TextDecoder().decode(bytes);
|
|
92
93
|
}
|
package/dist/e2b/sandbox.d.ts
CHANGED
|
@@ -86,8 +86,6 @@ export type SandboxListOpts = SandboxApiOpts & {
|
|
|
86
86
|
limit?: number;
|
|
87
87
|
nextToken?: string;
|
|
88
88
|
};
|
|
89
|
-
/** A sandbox, with E2B's methods. `sandbox.runtime` is the Runtime sandbox
|
|
90
|
-
* underneath, for anything E2B has no name for. */
|
|
91
89
|
export declare class Sandbox {
|
|
92
90
|
#private;
|
|
93
91
|
protected static readonly defaultTemplate: string;
|
|
@@ -156,12 +154,17 @@ export declare class Sandbox {
|
|
|
156
154
|
name?: string;
|
|
157
155
|
}): Promise<SnapshotInfo>;
|
|
158
156
|
static deleteSnapshot(snapshotId: string, opts?: SandboxApiOpts): Promise<boolean>;
|
|
159
|
-
/**
|
|
160
|
-
*
|
|
157
|
+
/** E2B's getHost, answered at once as E2B answers it:
|
|
158
|
+
* `<port>-<id>.runtimehost.com`. A Runtime preview's address is made from
|
|
159
|
+
* the sandbox and the port alone, so it is known without a call; the port
|
|
160
|
+
* is shared publicly the first time it is asked for, in about a tenth of a
|
|
161
|
+
* second, beside the caller. A request that lands before the share may be
|
|
162
|
+
* answered 404 for up to two seconds; `await getPublicHost(port)` returns
|
|
163
|
+
* once the share has landed. */
|
|
161
164
|
getHost(port: number): string;
|
|
162
165
|
/** Shares `port` at a public HTTPS address (a Runtime preview) and returns
|
|
163
|
-
* its host, `<port>-<id>.<domain>`,
|
|
164
|
-
* address can reach it; a browser sees a one-time page naming Runtime. */
|
|
166
|
+
* its host, `<port>-<id>.<domain>`, once the share has landed. Anyone with
|
|
167
|
+
* the address can reach it; a browser sees a one-time page naming Runtime. */
|
|
165
168
|
getPublicHost(port: number): Promise<string>;
|
|
166
169
|
get pty(): never;
|
|
167
170
|
get git(): never;
|
package/dist/e2b/sandbox.js
CHANGED
|
@@ -128,6 +128,8 @@ function unsupported(feature, alternative) {
|
|
|
128
128
|
}
|
|
129
129
|
/** A sandbox, with E2B's methods. `sandbox.runtime` is the Runtime sandbox
|
|
130
130
|
* underneath, for anything E2B has no name for. */
|
|
131
|
+
/** Where Runtime serves previews; getHost names a host under it. */
|
|
132
|
+
const PREVIEW_DOMAIN = "runtimehost.com";
|
|
131
133
|
export class Sandbox {
|
|
132
134
|
static defaultTemplate = "base";
|
|
133
135
|
files;
|
|
@@ -136,6 +138,8 @@ export class Sandbox {
|
|
|
136
138
|
#runtime;
|
|
137
139
|
#envs;
|
|
138
140
|
#home;
|
|
141
|
+
/** Ports shared as public previews, by port: the one share each asked for. */
|
|
142
|
+
#shares = new Map();
|
|
139
143
|
/** Use Sandbox.create or Sandbox.connect. */
|
|
140
144
|
constructor(runtime, client, envs = {}) {
|
|
141
145
|
this.#runtime = runtime;
|
|
@@ -332,17 +336,37 @@ export class Sandbox {
|
|
|
332
336
|
}
|
|
333
337
|
}
|
|
334
338
|
// ---- ports ------------------------------------------------------------
|
|
335
|
-
/**
|
|
336
|
-
*
|
|
339
|
+
/** E2B's getHost, answered at once as E2B answers it:
|
|
340
|
+
* `<port>-<id>.runtimehost.com`. A Runtime preview's address is made from
|
|
341
|
+
* the sandbox and the port alone, so it is known without a call; the port
|
|
342
|
+
* is shared publicly the first time it is asked for, in about a tenth of a
|
|
343
|
+
* second, beside the caller. A request that lands before the share may be
|
|
344
|
+
* answered 404 for up to two seconds; `await getPublicHost(port)` returns
|
|
345
|
+
* once the share has landed. */
|
|
337
346
|
getHost(port) {
|
|
338
|
-
|
|
347
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535)
|
|
348
|
+
throw new InvalidArgumentError(`port must be a whole number from 1 to 65535, not ${port}.`);
|
|
349
|
+
void this.#share(port).catch((error) => {
|
|
350
|
+
if (typeof process !== "undefined" && process.emitWarning)
|
|
351
|
+
process.emitWarning(`Sharing port ${port} for getHost failed: ${error instanceof Error ? error.message : String(error)}. getPublicHost(${port}) says why.`, { code: "RUNTIME_E2B_GET_HOST" });
|
|
352
|
+
});
|
|
353
|
+
return `${port}-${this.sandboxId.replaceAll("-", "").toLowerCase()}.${PREVIEW_DOMAIN}`;
|
|
339
354
|
}
|
|
340
355
|
/** Shares `port` at a public HTTPS address (a Runtime preview) and returns
|
|
341
|
-
* its host, `<port>-<id>.<domain>`,
|
|
342
|
-
* address can reach it; a browser sees a one-time page naming Runtime. */
|
|
356
|
+
* its host, `<port>-<id>.<domain>`, once the share has landed. Anyone with
|
|
357
|
+
* the address can reach it; a browser sees a one-time page naming Runtime. */
|
|
343
358
|
async getPublicHost(port) {
|
|
344
|
-
|
|
345
|
-
|
|
359
|
+
return this.#share(port);
|
|
360
|
+
}
|
|
361
|
+
#share(port) {
|
|
362
|
+
let share = this.#shares.get(port);
|
|
363
|
+
if (!share) {
|
|
364
|
+
share = guard("sandbox", () => this.#runtime.previews.create(port, { visibility: "public" })).then((preview) => new URL(preview.url).host);
|
|
365
|
+
// A failed share is asked again next time, not remembered.
|
|
366
|
+
share.catch(() => this.#shares.delete(port));
|
|
367
|
+
this.#shares.set(port, share);
|
|
368
|
+
}
|
|
369
|
+
return share;
|
|
346
370
|
}
|
|
347
371
|
// ---- what Runtime does differently -----------------------------------
|
|
348
372
|
get pty() {
|
package/dist/errors.d.ts
CHANGED
|
@@ -45,6 +45,11 @@ export declare class RateLimitError extends RuntimeError {
|
|
|
45
45
|
}
|
|
46
46
|
export declare class ServiceUnavailableError extends RuntimeError {
|
|
47
47
|
}
|
|
48
|
+
/** The account may not spend: a payment on it is disputed or under review
|
|
49
|
+
* (`account_blocked`, 402). The message says which and what clears it; a
|
|
50
|
+
* retry cannot, and adding credit alone may not. */
|
|
51
|
+
export declare class AccountBlockedError extends RuntimeError {
|
|
52
|
+
}
|
|
48
53
|
/** No answer arrived. A write may or may not have happened; retrying with the
|
|
49
54
|
* same idempotencyKey (the SDK does, automatically) settles it safely. */
|
|
50
55
|
export declare class ConnectionError extends RuntimeError {
|
package/dist/errors.js
CHANGED
|
@@ -74,6 +74,11 @@ export class RateLimitError extends RuntimeError {
|
|
|
74
74
|
}
|
|
75
75
|
export class ServiceUnavailableError extends RuntimeError {
|
|
76
76
|
}
|
|
77
|
+
/** The account may not spend: a payment on it is disputed or under review
|
|
78
|
+
* (`account_blocked`, 402). The message says which and what clears it; a
|
|
79
|
+
* retry cannot, and adding credit alone may not. */
|
|
80
|
+
export class AccountBlockedError extends RuntimeError {
|
|
81
|
+
}
|
|
77
82
|
/** No answer arrived. A write may or may not have happened; retrying with the
|
|
78
83
|
* same idempotencyKey (the SDK does, automatically) settles it safely. */
|
|
79
84
|
export class ConnectionError extends RuntimeError {
|
|
@@ -110,6 +115,8 @@ export function errorFor(status, body, idempotencyKey) {
|
|
|
110
115
|
...(typeof error.retryAfterMs === "number" ? { retryAfterMs: error.retryAfterMs } : {}),
|
|
111
116
|
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
112
117
|
};
|
|
118
|
+
if (init.code === "account_blocked")
|
|
119
|
+
return new AccountBlockedError(init);
|
|
113
120
|
if (status === 401)
|
|
114
121
|
return new AuthenticationError(init);
|
|
115
122
|
if (status === 403)
|
package/dist/index.d.ts
CHANGED
|
@@ -5,22 +5,22 @@
|
|
|
5
5
|
* console.log((await sbx.exec("echo hello")).stdout);
|
|
6
6
|
*/
|
|
7
7
|
import type { Runtime } from "./client.js";
|
|
8
|
-
import { type RuntimeOptions } from "./client.js";
|
|
8
|
+
import { type CreateOptions, type RuntimeOptions } from "./client.js";
|
|
9
9
|
import { Sandbox as SandboxClass } from "./sandbox.js";
|
|
10
10
|
import type { CreateSandbox, KeepAliveOptions } from "./types.js";
|
|
11
11
|
import type { RequestOptions } from "./transport.js";
|
|
12
|
-
export { Runtime, Sandboxes, FeedbackApi, SupportApi, type RuntimeOptions, type SupportReply, } from "./client.js";
|
|
12
|
+
export { Runtime, Sandboxes, FeedbackApi, SupportApi, type CreateOptions, type RuntimeOptions, type SupportReply, } from "./client.js";
|
|
13
13
|
export { Files, Process, Processes, Terminal } from "./sandbox.js";
|
|
14
14
|
export { Tunnel, TunnelStream, type PortForward } from "./tunnel.js";
|
|
15
15
|
export { Page } from "./page.js";
|
|
16
16
|
export type { KeyLimits } from "./products/limits.js";
|
|
17
17
|
export type { AuditEvent, AuditPage } from "./products/audit.js";
|
|
18
|
-
export type { Secret, SetSecret } from "./products/secrets.js";
|
|
18
|
+
export type { Secret, SecretRule, SetSecret } from "./products/secrets.js";
|
|
19
19
|
export type { McpCatalogEntry, McpGateway, McpServerRequest } from "./products/mcp.js";
|
|
20
20
|
export type { FileEvent, FileEventType, WatchHandle, WatchNotice, WatchOptions, } from "./products/watch.js";
|
|
21
21
|
export type { Mount, MountBucket } from "./products/mounts.js";
|
|
22
22
|
export type { Volume, VolumeBackup, CreateVolume } from "./products/volumes.js";
|
|
23
|
-
export type { CheckedDomain, DnsRecord, Domain, EgressAddress, TcpPort, Tunnel as PrivateTunnel, TunnelPeer, TunnelPeerCreated, } from "./products/network-products.js";
|
|
23
|
+
export type { CheckedDomain, DnsRecord, Domain, EgressAddress, TcpPort, Tunnel as PrivateTunnel, TunnelPeer, TunnelPeerCreated, SetUpstreamProxy, UpstreamProxy, } from "./products/network-products.js";
|
|
24
24
|
export { verifyWebhook, WebhookVerificationError, type MetricPoint, type MetricRange, type OtelExport, type RuntimeEvent, type SandboxMetrics, type Webhook, type WebhookDelivery, type WebhookEventType, } from "./products/observability.js";
|
|
25
25
|
export { Snapshots, type Snapshot, type SnapshotOptions } from "./snapshots.js";
|
|
26
26
|
export { Transport, VERSION, apiOrigin, type RequestOptions, type Call } from "./transport.js";
|
|
@@ -35,7 +35,7 @@ export declare class Sandbox extends SandboxClass {
|
|
|
35
35
|
static create(input?: CreateSandbox & {
|
|
36
36
|
wait?: boolean;
|
|
37
37
|
keepAlive?: boolean | KeepAliveOptions;
|
|
38
|
-
}, options?:
|
|
38
|
+
}, options?: CreateOptions & {
|
|
39
39
|
client?: Runtime;
|
|
40
40
|
}): Promise<SandboxClass>;
|
|
41
41
|
/** The sandbox named `name` in this account, ready to use: running as it
|
|
@@ -45,7 +45,7 @@ export declare class Sandbox extends SandboxClass {
|
|
|
45
45
|
static getOrCreate(name: string, input?: Omit<CreateSandbox, "name" | "getOrCreate"> & {
|
|
46
46
|
wait?: boolean;
|
|
47
47
|
keepAlive?: boolean | KeepAliveOptions;
|
|
48
|
-
}, options?:
|
|
48
|
+
}, options?: CreateOptions & {
|
|
49
49
|
client?: Runtime;
|
|
50
50
|
}): Promise<SandboxClass>;
|
|
51
51
|
/** Reconnects to an existing sandbox by id. */
|
|
@@ -65,4 +65,6 @@ export declare class Sandbox extends SandboxClass {
|
|
|
65
65
|
}
|
|
66
66
|
export type { RuntimeOptions as ClientOptions };
|
|
67
67
|
export { identityToken, type IdentityToken } from "./identity.js";
|
|
68
|
+
export { openWalletAccount, claimWalletAccount, type OpenedWalletAccount, type WalletClaim, type WalletOptions, } from "./wallet.js";
|
|
69
|
+
export type { Topup } from "./products/billing.js";
|
|
68
70
|
export type { SsoConnection, SsoStatus } from "./products/sso.js";
|
package/dist/index.js
CHANGED
package/dist/keys.js
CHANGED
|
@@ -4,7 +4,7 @@ import { hostname } from "node:os";
|
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { connectionOrigins } from "./credentials.js";
|
|
6
6
|
import { RuntimeError } from "./errors.js";
|
|
7
|
-
import { named } from "./
|
|
7
|
+
import { named } from "./cli-name.js";
|
|
8
8
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
9
9
|
const KEY = /^rtcloud_([a-f0-9-]{36})_[A-Za-z0-9_-]{43}$/;
|
|
10
10
|
/** How often to ask whether the owner has answered. The website answers
|
package/dist/login.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const me: string;
|
|
4
|
-
/** Text that names a command, with the command spelled the way it was run. */
|
|
5
|
-
export declare function named(text: string, name?: string): string;
|
|
1
|
+
import { me, named } from "./cli-name.js";
|
|
2
|
+
export { me, named };
|
|
6
3
|
type LoginOptions = {
|
|
7
4
|
readKey?: () => Promise<string>;
|
|
8
5
|
fetch?: typeof fetch;
|
|
@@ -116,4 +113,3 @@ export type PendingLogin = {
|
|
|
116
113
|
* waiting: `runtime login` waits on it, and the MCP bridge gives the link to
|
|
117
114
|
* the agent, which shows it to the person it works for. */
|
|
118
115
|
export declare function beginDeviceLogin(env: NodeJS.ProcessEnv, agentName: string, options?: LoginOptions): Promise<PendingLogin>;
|
|
119
|
-
export {};
|
package/dist/login.js
CHANGED
|
@@ -2,7 +2,8 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { generateKeyPair, privateDecrypt } from "node:crypto";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
-
import {
|
|
5
|
+
import { me, named } from "./cli-name.js";
|
|
6
|
+
import { Runtime } from "./client.js";
|
|
6
7
|
import { connectionOrigins, connectionStore } from "./credentials.js";
|
|
7
8
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
8
9
|
class ConnectionError extends Error {
|
|
@@ -12,13 +13,7 @@ class ConnectionError extends Error {
|
|
|
12
13
|
this.status = status;
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
* the command is `npx withruntime`, installed it is `runtime`. */
|
|
17
|
-
export const me = /[\\/]_npx[\\/]/.test(process.argv[1] ?? "") ? "npx withruntime" : "runtime";
|
|
18
|
-
/** Text that names a command, with the command spelled the way it was run. */
|
|
19
|
-
export function named(text, name = me) {
|
|
20
|
-
return name === "runtime" ? text : text.replace(/(^|[\s`(])runtime(?= [a-z<[])/g, `$1${name}`);
|
|
21
|
-
}
|
|
16
|
+
export { me, named };
|
|
22
17
|
/** The connection id recorded for a key pasted with `login --with-key`. */
|
|
23
18
|
const PASTED = "pasted";
|
|
24
19
|
/** A key from standard input: piped, or typed at a prompt that does not echo. */
|
package/dist/network-cli.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ type Args = {
|
|
|
8
8
|
positional: string[];
|
|
9
9
|
flags: Map<string, string[]>;
|
|
10
10
|
};
|
|
11
|
-
export declare const NETWORK_PRODUCTS: readonly ["domain", "port", "address", "tunnel"];
|
|
11
|
+
export declare const NETWORK_PRODUCTS: readonly ["domain", "port", "address", "tunnel", "network"];
|
|
12
12
|
export type NetworkProduct = (typeof NETWORK_PRODUCTS)[number];
|
|
13
13
|
export declare const NETWORK_HELP: Record<NetworkProduct, string>;
|
|
14
14
|
export declare function networkProductCommand(product: NetworkProduct, argv: string[], args: Args, out: Out, client: () => Promise<Runtime>): Promise<number>;
|
package/dist/network-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chmod, writeFile } from "node:fs/promises";
|
|
2
2
|
import { RuntimeError } from "./errors.js";
|
|
3
|
-
import { named } from "./
|
|
3
|
+
import { named } from "./cli-name.js";
|
|
4
4
|
import { generateWireGuardKeyPair } from "./wireguard.js";
|
|
5
5
|
function fundingState(value) {
|
|
6
6
|
if (value.funded === false)
|
|
@@ -11,7 +11,7 @@ function fundingState(value) {
|
|
|
11
11
|
? `Funded until ${value.fundedUntil}.`
|
|
12
12
|
: "Funding active; no deadline reported.";
|
|
13
13
|
}
|
|
14
|
-
export const NETWORK_PRODUCTS = ["domain", "port", "address", "tunnel"];
|
|
14
|
+
export const NETWORK_PRODUCTS = ["domain", "port", "address", "tunnel", "network"];
|
|
15
15
|
export const NETWORK_HELP = {
|
|
16
16
|
domain: `runtime domain <command>
|
|
17
17
|
|
|
@@ -54,6 +54,19 @@ export const NETWORK_HELP = {
|
|
|
54
54
|
A new key; the old one stops working within seconds
|
|
55
55
|
peer rm <peerId> Remove it; its tunnel ends within seconds
|
|
56
56
|
--route: your own ranges behind the peer that sandboxes may reach. Paid accounts only.
|
|
57
|
+
`,
|
|
58
|
+
network: `runtime network <command>
|
|
59
|
+
|
|
60
|
+
upstream-proxy set <http://host:port|https://host:port> [--secret NAME] [--host <host>]...
|
|
61
|
+
Send your sandboxes' outbound connections through your
|
|
62
|
+
own proxy, after Runtime's own rules allow them.
|
|
63
|
+
--secret: one of your secrets, sent as Proxy-Authorization
|
|
64
|
+
(its hosts must name the proxy's host). --host: only
|
|
65
|
+
these destinations (or *.domain) go through it
|
|
66
|
+
upstream-proxy get The proxy in use
|
|
67
|
+
upstream-proxy remove Connections leave directly again
|
|
68
|
+
A proxy that refuses or cannot be reached fails the connection; nothing goes
|
|
69
|
+
around it. Paid accounts only.
|
|
57
70
|
`,
|
|
58
71
|
};
|
|
59
72
|
function usage(product, message) {
|
|
@@ -319,5 +332,54 @@ export async function networkProductCommand(product, argv, args, out, client) {
|
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
}
|
|
335
|
+
if (product === "network") {
|
|
336
|
+
if (verb !== "upstream-proxy")
|
|
337
|
+
throw usage(product, `Unknown network command ${verb}.`);
|
|
338
|
+
const upstream = runtime.network.upstreamProxy;
|
|
339
|
+
const describe = (proxy) => [
|
|
340
|
+
`Outbound connections${proxy.hosts?.length ? ` to ${proxy.hosts.join(", ")}` : ""} go through ${proxy.url}.`,
|
|
341
|
+
...(proxy.secret ? [`${proxy.secret} is sent as Proxy-Authorization.`] : []),
|
|
342
|
+
].join("\n");
|
|
343
|
+
switch (rest[0]) {
|
|
344
|
+
case "set": {
|
|
345
|
+
const hosts = args.flags.get("host");
|
|
346
|
+
const secret = flag("secret");
|
|
347
|
+
const saved = await upstream.set({
|
|
348
|
+
url: need(rest[1], "the proxy's url, such as http://proxy.example.com:3128"),
|
|
349
|
+
...(secret ? { secret } : {}),
|
|
350
|
+
...(hosts ? { hosts } : {}),
|
|
351
|
+
});
|
|
352
|
+
print([
|
|
353
|
+
describe(saved),
|
|
354
|
+
saved.enforced
|
|
355
|
+
? "A connection your proxy refuses fails; nothing goes around it."
|
|
356
|
+
: "A host is catching up; running sandboxes use it within a minute.",
|
|
357
|
+
].join("\n"), saved);
|
|
358
|
+
return 0;
|
|
359
|
+
}
|
|
360
|
+
case "get":
|
|
361
|
+
case undefined: {
|
|
362
|
+
let found;
|
|
363
|
+
try {
|
|
364
|
+
found = await upstream.get();
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
if (!(error instanceof RuntimeError) || error.code !== "not_found")
|
|
368
|
+
throw error;
|
|
369
|
+
print(named("No upstream proxy. Set one: runtime network upstream-proxy set http://proxy.example.com:3128"), null);
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
print(describe(found), found);
|
|
373
|
+
return 0;
|
|
374
|
+
}
|
|
375
|
+
case "remove":
|
|
376
|
+
case "rm": {
|
|
377
|
+
const gone = await upstream.remove();
|
|
378
|
+
print("Connections leave directly again.", gone);
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
throw usage(product, "upstream-proxy takes set, get or remove.");
|
|
383
|
+
}
|
|
322
384
|
throw usage(product, `Unknown ${product} command ${verb}.`);
|
|
323
385
|
}
|