withruntime 0.11.2 → 0.14.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/BLAXEL.md +2 -2
- package/COMPATIBILITY.md +74 -9
- package/DAYTONA.md +33 -5
- package/E2B.md +85 -24
- package/README.md +11 -10
- package/README.src.md +10 -9
- package/VERCEL.md +43 -5
- package/compatibility-lock.json +58 -49
- package/dist/billing-cli.d.ts +1 -1
- package/dist/billing-cli.js +85 -5
- package/dist/blaxel/errors.d.ts +14 -0
- package/dist/blaxel/errors.js +29 -0
- package/dist/blaxel/index.d.ts +2 -2
- package/dist/blaxel/index.js +4 -4
- package/dist/blaxel/interpreter.d.ts +2 -5
- package/dist/blaxel/sandbox.d.ts +15 -6
- package/dist/blaxel/sandbox.js +64 -8
- package/dist/blaxel/snapshot.d.ts +4 -0
- package/dist/blaxel/snapshot.js +4 -0
- package/dist/cli/logs.d.ts +21 -0
- package/dist/cli/logs.js +188 -0
- package/dist/cli-extras.d.ts +3 -3
- package/dist/cli-extras.js +17 -12
- package/dist/cli-words.d.ts +10 -1
- package/dist/cli-words.js +18 -0
- package/dist/cli.d.ts +22 -3
- package/dist/cli.js +647 -273
- package/dist/client.d.ts +2 -4
- package/dist/client.js +27 -11
- package/dist/cloudflare/errors.d.ts +143 -0
- package/dist/cloudflare/errors.js +167 -0
- package/dist/cloudflare/index.d.ts +175 -6
- package/dist/cloudflare/index.js +416 -26
- package/dist/cloudflare/interpreter.d.ts +119 -0
- package/dist/cloudflare/interpreter.js +223 -0
- package/dist/cloudflare/streams.d.ts +38 -0
- package/dist/cloudflare/streams.js +145 -0
- package/dist/cloudflare/watch.d.ts +9 -0
- package/dist/cloudflare/watch.js +24 -0
- package/dist/codesandbox/index.d.ts +216 -28
- package/dist/codesandbox/index.js +478 -78
- package/dist/compat/core.d.ts +18 -0
- package/dist/compat/core.js +29 -0
- package/dist/compat/files.d.ts +1 -0
- package/dist/compat/files.js +25 -5
- package/dist/credentials.d.ts +3 -0
- package/dist/credentials.js +15 -0
- package/dist/daytona/computer-use.d.ts +13 -4
- package/dist/daytona/computer-use.js +66 -8
- package/dist/daytona/daytona.d.ts +7 -1
- package/dist/daytona/daytona.js +31 -4
- package/dist/daytona/errors.d.ts +11 -0
- package/dist/daytona/errors.js +18 -0
- package/dist/daytona/filesystem.js +2 -1
- package/dist/daytona/image.d.ts +13 -9
- package/dist/daytona/image.js +18 -12
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +8 -2
- package/dist/daytona/sandbox.js +16 -2
- package/dist/domains/index.d.ts +9 -0
- package/dist/domains/index.js +10 -0
- package/dist/e2b/commands.d.ts +1 -1
- package/dist/e2b/commands.js +6 -7
- package/dist/e2b/errors.d.ts +17 -0
- package/dist/e2b/errors.js +40 -1
- package/dist/e2b/index.d.ts +2 -2
- package/dist/e2b/index.js +1 -1
- package/dist/e2b/sandbox.d.ts +28 -22
- package/dist/e2b/sandbox.js +34 -8
- package/dist/e2b/unsupported.js +1 -1
- package/dist/errors.d.ts +11 -2
- package/dist/errors.js +17 -5
- package/dist/freestyle/index.d.ts +285 -37
- package/dist/freestyle/index.js +656 -144
- package/dist/identity.d.ts +3 -0
- package/dist/identity.js +27 -0
- package/dist/images/index.d.ts +9 -0
- package/dist/images/index.js +10 -0
- package/dist/index.d.ts +3 -1
- package/dist/ips/index.d.ts +9 -0
- package/dist/ips/index.js +10 -0
- package/dist/jobs/index.d.ts +9 -0
- package/dist/jobs/index.js +10 -0
- package/dist/keys.js +2 -0
- package/dist/login.js +2 -0
- package/dist/machine.d.ts +27 -4
- package/dist/machine.js +46 -14
- package/dist/mcp.js +144 -20
- package/dist/modal/index.d.ts +339 -61
- package/dist/modal/index.js +781 -121
- package/dist/network-cli.d.ts +3 -1
- package/dist/network-cli.js +78 -36
- package/dist/networks/index.d.ts +11 -0
- package/dist/networks/index.js +10 -0
- package/dist/ports/index.d.ts +9 -0
- package/dist/ports/index.js +10 -0
- package/dist/product-names.d.ts +52 -0
- package/dist/product-names.js +164 -0
- package/dist/product-renames.d.ts +103 -0
- package/dist/product-renames.js +189 -0
- package/dist/products/api-keys.d.ts +73 -0
- package/dist/products/api-keys.js +23 -0
- package/dist/products/apps.d.ts +157 -0
- package/dist/products/apps.js +60 -0
- package/dist/products/balancers.d.ts +87 -0
- package/dist/products/balancers.js +8 -0
- package/dist/products/billing.d.ts +41 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/browser.d.ts +10 -0
- package/dist/products/browsers.d.ts +71 -0
- package/dist/products/browsers.js +102 -0
- package/dist/products/builds.d.ts +156 -0
- package/dist/products/builds.js +227 -0
- package/dist/products/index.d.ts +5 -16
- package/dist/products/index.js +5 -14
- package/dist/products/jobs.d.ts +19 -2
- package/dist/products/jobs.js +28 -11
- package/dist/products/logs.d.ts +245 -0
- package/dist/products/logs.js +274 -0
- package/dist/products/network-products.d.ts +38 -7
- package/dist/products/network-products.js +31 -18
- package/dist/products/observability.d.ts +8 -2
- package/dist/products/private-network.js +1 -1
- package/dist/products/queues.d.ts +149 -0
- package/dist/products/queues.js +213 -0
- package/dist/products/referrals.d.ts +21 -7
- package/dist/products/referrals.js +3 -3
- package/dist/products/resources.d.ts +54 -0
- package/dist/products/resources.js +122 -0
- package/dist/products/secrets.d.ts +29 -3
- package/dist/products/secrets.js +47 -18
- package/dist/products/signed-requests.d.ts +21 -0
- package/dist/products/signed-requests.js +11 -0
- package/dist/products/streams.d.ts +73 -0
- package/dist/products/streams.js +17 -0
- package/dist/products/switching.d.ts +35 -7
- package/dist/products/switching.js +4 -3
- package/dist/products/volumes.d.ts +6 -0
- package/dist/record-cli.d.ts +23 -0
- package/dist/record-cli.js +308 -0
- package/dist/renamed-products.d.ts +38 -0
- package/dist/renamed-products.js +34 -0
- package/dist/runloop/index.d.ts +9 -2
- package/dist/runloop/index.js +29 -8
- package/dist/runloop/sdk.d.ts +131 -0
- package/dist/runloop/sdk.js +167 -0
- package/dist/sandbox.d.ts +7 -2
- package/dist/sandbox.js +41 -11
- package/dist/sandboxes/index.d.ts +11 -0
- package/dist/sandboxes/index.js +12 -0
- package/dist/snapshots/index.d.ts +9 -0
- package/dist/snapshots/index.js +11 -0
- package/dist/snapshots.d.ts +7 -4
- package/dist/sprites/index.d.ts +343 -31
- package/dist/sprites/index.js +866 -84
- package/dist/ssh.d.ts +1 -1
- package/dist/ssh.js +11 -11
- package/dist/terminal.d.ts +24 -0
- package/dist/terminal.js +32 -0
- package/dist/transport.d.ts +18 -1
- package/dist/transport.js +80 -6
- package/dist/tunnel.js +1 -1
- package/dist/types.d.ts +71 -4
- package/dist/vercel/sandbox.d.ts +1 -1
- package/dist/vercel/sandbox.js +6 -4
- package/dist/vercel/snapshot.js +10 -2
- package/dist/volumes/index.d.ts +9 -0
- package/dist/volumes/index.js +10 -0
- package/dist/wallet.d.ts +4 -1
- package/dist/wallet.js +2 -1
- package/package.json +48 -14
package/dist/identity.d.ts
CHANGED
|
@@ -25,3 +25,6 @@ export declare function identityToken(input: {
|
|
|
25
25
|
lifetimeSeconds?: number;
|
|
26
26
|
signal?: AbortSignal;
|
|
27
27
|
}): Promise<IdentityToken>;
|
|
28
|
+
/** Headers for a call to the website's connect API: this sandbox's identity
|
|
29
|
+
* token, or none outside a sandbox or when one cannot be had. Never throws. */
|
|
30
|
+
export declare function sandboxIdentityHeaders(): Promise<Record<string, string>>;
|
package/dist/identity.js
CHANGED
|
@@ -55,3 +55,30 @@ export async function identityToken(input) {
|
|
|
55
55
|
});
|
|
56
56
|
return body;
|
|
57
57
|
}
|
|
58
|
+
/** The header and audience the website keys its per-network limits on
|
|
59
|
+
* (apps/cloud-web/src/lib/limit-key.ts): inside a sandbox, Runtime's own
|
|
60
|
+
* sign-in calls count against this sandbox, not the address every sandbox on
|
|
61
|
+
* its host shares. */
|
|
62
|
+
const LIMIT_HEADER = "x-runtime-sandbox-identity";
|
|
63
|
+
const LIMIT_AUDIENCE = "https://withruntime.com/limits";
|
|
64
|
+
let limitToken;
|
|
65
|
+
/** Headers for a call to the website's connect API: this sandbox's identity
|
|
66
|
+
* token, or none outside a sandbox or when one cannot be had. Never throws. */
|
|
67
|
+
export async function sandboxIdentityHeaders() {
|
|
68
|
+
if (limitToken && Date.now() < limitToken.until)
|
|
69
|
+
return { [LIMIT_HEADER]: limitToken.token };
|
|
70
|
+
if (!(await requestCredentials()))
|
|
71
|
+
return {};
|
|
72
|
+
try {
|
|
73
|
+
const issued = await identityToken({
|
|
74
|
+
audience: LIMIT_AUDIENCE,
|
|
75
|
+
signal: AbortSignal.timeout(3_000),
|
|
76
|
+
});
|
|
77
|
+
// Renewed a minute before it lapses, so a slow call never carries a dead one.
|
|
78
|
+
limitToken = { token: issued.token, until: Date.parse(issued.expiresAt) - 60_000 };
|
|
79
|
+
return { [LIMIT_HEADER]: issued.token };
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `withruntime/images`: Images alone. Custom images to start sandboxes from.
|
|
2
|
+
*
|
|
3
|
+
* import { images } from "withruntime/images";
|
|
4
|
+
* const client = images(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime, type RuntimeOptions } from "../client.js";
|
|
7
|
+
export type * from "../products/images.js";
|
|
8
|
+
/** `runtime.images` of a client made with `options`. */
|
|
9
|
+
export declare function images(options?: RuntimeOptions): Runtime["images"];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `withruntime/images`: Images alone. Custom images to start sandboxes from.
|
|
2
|
+
*
|
|
3
|
+
* import { images } from "withruntime/images";
|
|
4
|
+
* const client = images(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime } from "../client.js";
|
|
7
|
+
/** `runtime.images` of a client made with `options`. */
|
|
8
|
+
export function images(options) {
|
|
9
|
+
return new Runtime(options).images;
|
|
10
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ 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
18
|
export type { UsageExportQuery, UsageExportRow, UsageExportPage, } from "./products/observability.js";
|
|
19
|
-
export type { JobSecret, Secret, SecretRule, SecretUse, SetSecret } from "./products/secrets.js";
|
|
19
|
+
export type { JobSecret, Secret, SecretRule, SecretScope, SecretUse, SetSecret, } from "./products/secrets.js";
|
|
20
20
|
export { SecretPartlyStoredError } from "./products/secrets.js";
|
|
21
21
|
export type { McpCatalogEntry, McpGateway, McpServerRequest } from "./products/mcp.js";
|
|
22
22
|
export type { FileEvent, FileEventType, WatchHandle, WatchNotice, WatchOptions, } from "./products/watch.js";
|
|
@@ -26,6 +26,7 @@ export type { Volume, VolumeBackup, CreateVolume, VolumeAttachment } from "./pro
|
|
|
26
26
|
export type { CreateJob, Job, JobCompute, JobDefinition, JobLogs, JobRun, JobRunState, JobSchedule, } from "./products/jobs.js";
|
|
27
27
|
export type { CheckedDomain, DnsRecord, Domain, EgressAddress, TcpPort, Tunnel as PrivateTunnel, TunnelPeer, TunnelPeerCreated, SetUpstreamProxy, UpstreamProxy, } from "./products/network-products.js";
|
|
28
28
|
export type { PrivateNetwork } from "./products/private-network.js";
|
|
29
|
+
export type { SignedRequests } from "./products/signed-requests.js";
|
|
29
30
|
export { verifyWebhook, WebhookVerificationError, type MetricPoint, type MetricRange, type OtelExport, type RuntimeEvent, type SandboxMetrics, type Webhook, type WebhookDelivery, type WebhookEventType, } from "./products/observability.js";
|
|
30
31
|
export { Snapshots, type Snapshot, type SnapshotOptions } from "./snapshots.js";
|
|
31
32
|
export { Transport, VERSION, apiOrigin, type RequestOptions, type Call } from "./transport.js";
|
|
@@ -73,4 +74,5 @@ export type { RuntimeOptions as ClientOptions };
|
|
|
73
74
|
export { identityToken, type IdentityToken } from "./identity.js";
|
|
74
75
|
export { openWalletAccount, claimWalletAccount, type OpenedWalletAccount, type WalletClaim, type WalletOptions, } from "./wallet.js";
|
|
75
76
|
export type { Topup } from "./products/billing.js";
|
|
77
|
+
export type { NewKey, MadeKey, ListedKey, KeyUsage } from "./products/api-keys.js";
|
|
76
78
|
export type { SsoConnection, SsoStatus } from "./products/sso.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `withruntime/ips`: IPs alone. Dedicated outbound addresses every sandbox of the account sends from.
|
|
2
|
+
*
|
|
3
|
+
* import { ips } from "withruntime/ips";
|
|
4
|
+
* const client = ips(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime, type RuntimeOptions } from "../client.js";
|
|
7
|
+
export type { EgressAddress, NetworkFunding } from "../products/network-products.js";
|
|
8
|
+
/** `runtime.ips` of a client made with `options`. */
|
|
9
|
+
export declare function ips(options?: RuntimeOptions): Runtime["ips"];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `withruntime/ips`: IPs alone. Dedicated outbound addresses every sandbox of the account sends from.
|
|
2
|
+
*
|
|
3
|
+
* import { ips } from "withruntime/ips";
|
|
4
|
+
* const client = ips(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime } from "../client.js";
|
|
7
|
+
/** `runtime.ips` of a client made with `options`. */
|
|
8
|
+
export function ips(options) {
|
|
9
|
+
return new Runtime(options).ips;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `withruntime/jobs`: Jobs alone. A command in a fresh sandbox, once or on a schedule.
|
|
2
|
+
*
|
|
3
|
+
* import { jobs } from "withruntime/jobs";
|
|
4
|
+
* const client = jobs(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime, type RuntimeOptions } from "../client.js";
|
|
7
|
+
export type * from "../products/jobs.js";
|
|
8
|
+
/** `runtime.jobs` of a client made with `options`. */
|
|
9
|
+
export declare function jobs(options?: RuntimeOptions): Runtime["jobs"];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `withruntime/jobs`: Jobs alone. A command in a fresh sandbox, once or on a schedule.
|
|
2
|
+
*
|
|
3
|
+
* import { jobs } from "withruntime/jobs";
|
|
4
|
+
* const client = jobs(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
5
|
+
*/
|
|
6
|
+
import { Runtime } from "../client.js";
|
|
7
|
+
/** `runtime.jobs` of a client made with `options`. */
|
|
8
|
+
export function jobs(options) {
|
|
9
|
+
return new Runtime(options).jobs;
|
|
10
|
+
}
|
package/dist/keys.js
CHANGED
|
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
|
|
|
5
5
|
import { connectionOrigins } from "./credentials.js";
|
|
6
6
|
import { RuntimeError } from "./errors.js";
|
|
7
7
|
import { named } from "./cli-name.js";
|
|
8
|
+
import { sandboxIdentityHeaders } from "./identity.js";
|
|
8
9
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
9
10
|
const KEY = /^rtcloud_([a-f0-9-]{36})_[A-Za-z0-9_-]{43}$/;
|
|
10
11
|
/** How often to ask whether the owner has answered. The website answers
|
|
@@ -79,6 +80,7 @@ export async function createKey(request, env, options = {}) {
|
|
|
79
80
|
headers: {
|
|
80
81
|
"content-type": "application/json",
|
|
81
82
|
...(key ? { authorization: `Bearer ${key}` } : {}),
|
|
83
|
+
...(await sandboxIdentityHeaders()),
|
|
82
84
|
},
|
|
83
85
|
body: JSON.stringify(body),
|
|
84
86
|
});
|
package/dist/login.js
CHANGED
|
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
|
|
|
5
5
|
import { me, named } from "./cli-name.js";
|
|
6
6
|
import { Runtime } from "./client.js";
|
|
7
7
|
import { checkBrowserOrigins, connectionOrigins, connectionStore } from "./credentials.js";
|
|
8
|
+
import { sandboxIdentityHeaders } from "./identity.js";
|
|
8
9
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
9
10
|
class ConnectionError extends Error {
|
|
10
11
|
status;
|
|
@@ -80,6 +81,7 @@ function connection(env, options) {
|
|
|
80
81
|
headers: {
|
|
81
82
|
"content-type": "application/json",
|
|
82
83
|
...(key ? { authorization: `Bearer ${key}` } : {}),
|
|
84
|
+
...(await sandboxIdentityHeaders()),
|
|
83
85
|
},
|
|
84
86
|
body: JSON.stringify(body ?? {}),
|
|
85
87
|
});
|
package/dist/machine.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export type MachineInfo = {
|
|
|
17
17
|
expiresAt: string;
|
|
18
18
|
endsAt?: string | null;
|
|
19
19
|
start?: unknown;
|
|
20
|
+
/** True when getOrCreate found the name already held. */
|
|
21
|
+
reused?: boolean;
|
|
22
|
+
/** True when the answer repeats an earlier call under the same idempotency key. */
|
|
23
|
+
replayed?: boolean;
|
|
20
24
|
};
|
|
21
25
|
/** Any other call's request options, as the caller gave them: `timeoutMs` is
|
|
22
26
|
* the call's whole deadline, and 0 turns it off. */
|
|
@@ -29,7 +33,6 @@ export declare function machinePage<M, Info>(product: MachineProduct, t: Transpo
|
|
|
29
33
|
data: Info[];
|
|
30
34
|
nextCursor: string | null;
|
|
31
35
|
}, query: Record<string, unknown>, options?: RequestOptions): Page<M>;
|
|
32
|
-
/** One machine: its record and the verbs every machine product shares. */
|
|
33
36
|
export declare abstract class Machine<Info extends MachineInfo, Settings extends object, Deleted> implements AsyncDisposable {
|
|
34
37
|
#private;
|
|
35
38
|
constructor(product: MachineProduct, transport: Transport, info: Info);
|
|
@@ -104,8 +107,9 @@ export declare abstract class Machine<Info extends MachineInfo, Settings extends
|
|
|
104
107
|
* unless autoWake is off); a stopped one ends the loop. It does not keep a
|
|
105
108
|
* Node or Bun process alive by itself. */
|
|
106
109
|
keepAlive(options?: KeepAliveOptions): () => void;
|
|
107
|
-
/** Days a paused sandbox is kept before deletion (1 to 365)
|
|
108
|
-
|
|
110
|
+
/** Days a paused sandbox is kept before deletion (1 to 365), or `null` to
|
|
111
|
+
* keep it while your account has credit. */
|
|
112
|
+
setRetention(days: number | null, options?: RequestOptions): Promise<this>;
|
|
109
113
|
/** Keeps this sandbox's whole machine (files, memory, running processes) as
|
|
110
114
|
* a snapshot to start new sandboxes from. A running sandbox is paused for
|
|
111
115
|
* the moment it takes, then woken; a paused one stays paused. Past
|
|
@@ -116,6 +120,10 @@ export declare abstract class Machine<Info extends MachineInfo, Settings extends
|
|
|
116
120
|
restart(options?: RequestOptions & {
|
|
117
121
|
wait?: boolean;
|
|
118
122
|
}): Promise<this>;
|
|
123
|
+
/** The end of an `await using` block. A sandbox this client created is
|
|
124
|
+
* deleted, as E2B's block kills it, unless it was created with
|
|
125
|
+
* `keep: true`; one it found with `get` or `getOrCreate` is stopped, which
|
|
126
|
+
* keeps its disk. */
|
|
119
127
|
[Symbol.asyncDispose](): Promise<void>;
|
|
120
128
|
toJSON(): Info;
|
|
121
129
|
}
|
|
@@ -133,11 +141,12 @@ export type CreateOptions = RequestOptions & {
|
|
|
133
141
|
export declare abstract class Machines<M extends Machine<Info, object, Deleted>, Info extends MachineInfo, Create extends {
|
|
134
142
|
name?: string;
|
|
135
143
|
getOrCreate?: boolean;
|
|
144
|
+
keep?: boolean;
|
|
136
145
|
}, Deleted> {
|
|
137
146
|
#private;
|
|
138
147
|
constructor(product: MachineProduct, t: Transport, wrap: (t: Transport, info: Info) => M);
|
|
139
148
|
/** Creates a sandbox and waits until it is running. Every field is optional:
|
|
140
|
-
* with none you get the
|
|
149
|
+
* with none you get the free usage while it lasts, the default region and a
|
|
141
150
|
* 2 vCPU / 4 GiB machine from a warm template. `wait: false` returns at once.
|
|
142
151
|
* When every slot without credit or the account's quota is taken, it waits for one to
|
|
143
152
|
* free, up to `waitForCapacityMs` (the client's, two minutes by default). */
|
|
@@ -177,4 +186,18 @@ export declare abstract class Machines<M extends Machine<Info, object, Deleted>,
|
|
|
177
186
|
error: unknown;
|
|
178
187
|
}[];
|
|
179
188
|
}>;
|
|
189
|
+
/** Deletes for good every sandbox whose labels all match, stopped ones
|
|
190
|
+
* included, eight at a time, as `delete()` does one: its disk and paused
|
|
191
|
+
* memory go, its snapshots stay. Says which were deleted and which failed;
|
|
192
|
+
* one failure does not stop the rest. Needs at least one label, so deleting
|
|
193
|
+
* everything is never one call. */
|
|
194
|
+
deleteAll(filter: {
|
|
195
|
+
labels: Record<string, string>;
|
|
196
|
+
}, options?: RequestOptions): Promise<{
|
|
197
|
+
deleted: string[];
|
|
198
|
+
failed: {
|
|
199
|
+
id: string;
|
|
200
|
+
error: unknown;
|
|
201
|
+
}[];
|
|
202
|
+
}>;
|
|
180
203
|
}
|
package/dist/machine.js
CHANGED
|
@@ -40,6 +40,10 @@ export function machinePage(product, t, wrap, body, query, options = {}) {
|
|
|
40
40
|
}), query, options));
|
|
41
41
|
}
|
|
42
42
|
/** One machine: its record and the verbs every machine product shares. */
|
|
43
|
+
/** Machines `create` made, which the end of an `await using` block deletes
|
|
44
|
+
* (Marc, 6 October 2026: deleted unless the code passes `keep: true`). One
|
|
45
|
+
* from `get`, or an existing one `getOrCreate` found, is only stopped. */
|
|
46
|
+
const deleteAtBlockEnd = new WeakSet();
|
|
43
47
|
export class Machine {
|
|
44
48
|
#info;
|
|
45
49
|
#keepAlive;
|
|
@@ -227,7 +231,8 @@ export class Machine {
|
|
|
227
231
|
void tick();
|
|
228
232
|
return end;
|
|
229
233
|
}
|
|
230
|
-
/** Days a paused sandbox is kept before deletion (1 to 365)
|
|
234
|
+
/** Days a paused sandbox is kept before deletion (1 to 365), or `null` to
|
|
235
|
+
* keep it while your account has credit. */
|
|
231
236
|
async setRetention(days, options = {}) {
|
|
232
237
|
return this.#lifecycle("retention", { ...options, wait: false }, { days });
|
|
233
238
|
}
|
|
@@ -374,8 +379,16 @@ export class Machine {
|
|
|
374
379
|
}));
|
|
375
380
|
return this;
|
|
376
381
|
}
|
|
382
|
+
/** The end of an `await using` block. A sandbox this client created is
|
|
383
|
+
* deleted, as E2B's block kills it, unless it was created with
|
|
384
|
+
* `keep: true`; one it found with `get` or `getOrCreate` is stopped, which
|
|
385
|
+
* keeps its disk. */
|
|
377
386
|
async [Symbol.asyncDispose]() {
|
|
378
387
|
this.#keepAlive?.();
|
|
388
|
+
if (deleteAtBlockEnd.has(this)) {
|
|
389
|
+
await this.delete().catch(() => undefined);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
379
392
|
if (this.#info.state === "stopped")
|
|
380
393
|
return;
|
|
381
394
|
await this.stop({ wait: false }).catch(() => undefined);
|
|
@@ -395,12 +408,12 @@ export class Machines {
|
|
|
395
408
|
this.#wrap = wrap;
|
|
396
409
|
}
|
|
397
410
|
/** Creates a sandbox and waits until it is running. Every field is optional:
|
|
398
|
-
* with none you get the
|
|
411
|
+
* with none you get the free usage while it lasts, the default region and a
|
|
399
412
|
* 2 vCPU / 4 GiB machine from a warm template. `wait: false` returns at once.
|
|
400
413
|
* When every slot without credit or the account's quota is taken, it waits for one to
|
|
401
414
|
* free, up to `waitForCapacityMs` (the client's, two minutes by default). */
|
|
402
415
|
async create(input = {}, options = {}) {
|
|
403
|
-
const { wait, ...body } = input;
|
|
416
|
+
const { wait, keep, ...body } = input;
|
|
404
417
|
const began = performance.now();
|
|
405
418
|
// Time spent waiting for capacity is the room's, not the deadline's.
|
|
406
419
|
let waited = 0;
|
|
@@ -417,6 +430,8 @@ export class Machines {
|
|
|
417
430
|
},
|
|
418
431
|
});
|
|
419
432
|
const machine = this.#wrap(this.#t, info);
|
|
433
|
+
if (!keep && !info.reused)
|
|
434
|
+
deleteAtBlockEnd.add(machine);
|
|
420
435
|
if (wait !== false && info.state !== "running") {
|
|
421
436
|
// One deadline for the whole create: the wait for running gets what the
|
|
422
437
|
// create left of it (1 ms when nothing is left, so it ends as a timeout).
|
|
@@ -431,7 +446,9 @@ export class Machines {
|
|
|
431
446
|
});
|
|
432
447
|
if (machine.state !== "running")
|
|
433
448
|
throw new RuntimeError({
|
|
434
|
-
message:
|
|
449
|
+
message: info.replayed
|
|
450
|
+
? `${this.#product.noun} ${info.id} was made by an earlier call with this idempotency key and is ${machine.state} now. It did not fail to start; create again with a new key.`
|
|
451
|
+
: `${this.#product.noun} ${info.id} is ${machine.state}, not running.`,
|
|
435
452
|
code: "start_failed",
|
|
436
453
|
status: 0,
|
|
437
454
|
hint: `Read it with runtime.${this.#product.plural}.get(id); stopReason says why.`,
|
|
@@ -476,29 +493,44 @@ export class Machines {
|
|
|
476
493
|
* says which stopped and which failed; one failure does not stop the rest.
|
|
477
494
|
* Needs at least one label, so stopping everything is never one call. */
|
|
478
495
|
async stopAll(filter, options = {}) {
|
|
496
|
+
const { done, failed } = await this.#eachLabelled("stop", filter, false, options, (m, each) => m.stop(each));
|
|
497
|
+
return { stopped: done, failed };
|
|
498
|
+
}
|
|
499
|
+
/** Deletes for good every sandbox whose labels all match, stopped ones
|
|
500
|
+
* included, eight at a time, as `delete()` does one: its disk and paused
|
|
501
|
+
* memory go, its snapshots stay. Says which were deleted and which failed;
|
|
502
|
+
* one failure does not stop the rest. Needs at least one label, so deleting
|
|
503
|
+
* everything is never one call. */
|
|
504
|
+
async deleteAll(filter, options = {}) {
|
|
505
|
+
const { done, failed } = await this.#eachLabelled("delete", filter, true, options, (m, each) => m.delete(each));
|
|
506
|
+
return { deleted: done, failed };
|
|
507
|
+
}
|
|
508
|
+
/** Lists every match first, then acts on each, eight at a time: acting
|
|
509
|
+
* while paging would move the pages under the cursor. */
|
|
510
|
+
async #eachLabelled(verb, filter, includeStopped, options, act) {
|
|
479
511
|
if (!filter?.labels || Object.keys(filter.labels).length === 0) {
|
|
480
512
|
const one = this.#product.noun.toLowerCase();
|
|
481
513
|
throw new RuntimeError({
|
|
482
|
-
message:
|
|
514
|
+
message: `${verb}All needs at least one label to match.`,
|
|
483
515
|
code: "invalid_request",
|
|
484
516
|
status: 0,
|
|
485
|
-
hint:
|
|
517
|
+
hint: `${verb === "stop" ? "Stop" : "Delete"} one ${one} with ${one}.${verb}(), or label the ones to ${verb} together.`,
|
|
486
518
|
});
|
|
487
519
|
}
|
|
488
|
-
const
|
|
489
|
-
// One key cannot name several
|
|
520
|
+
const matches = await (await this.list({ labels: filter.labels, limit: 100, ...(includeStopped ? { includeStopped } : {}) }, options)).toArray(Infinity);
|
|
521
|
+
// One key cannot name several acts: each makes its own.
|
|
490
522
|
const { idempotencyKey: _one, ...each } = options;
|
|
491
523
|
// In list order, oldest first, whichever finishes first.
|
|
492
|
-
const errors = new Array(
|
|
524
|
+
const errors = new Array(matches.length);
|
|
493
525
|
let next = 0;
|
|
494
526
|
const worker = async () => {
|
|
495
|
-
for (let at = next++; at <
|
|
496
|
-
await
|
|
527
|
+
for (let at = next++; at < matches.length; at = next++)
|
|
528
|
+
await act(matches[at], each).catch((error) => (errors[at] = error ?? "failed"));
|
|
497
529
|
};
|
|
498
|
-
await Promise.all(Array.from({ length: Math.min(8,
|
|
530
|
+
await Promise.all(Array.from({ length: Math.min(8, matches.length) }, worker));
|
|
499
531
|
return {
|
|
500
|
-
|
|
501
|
-
failed:
|
|
532
|
+
done: matches.filter((_, at) => errors[at] === undefined).map((m) => m.id),
|
|
533
|
+
failed: matches.flatMap((m, at) => errors[at] === undefined ? [] : [{ id: m.id, error: errors[at] }]),
|
|
502
534
|
};
|
|
503
535
|
}
|
|
504
536
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -1,16 +1,61 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
|
-
import { resolveCredential } from "./credentials.js";
|
|
2
|
+
import { checkBrowserOrigins, connectionOrigins, resolveCredential, saveWalletConnection, } from "./credentials.js";
|
|
3
3
|
import { RuntimeError } from "./errors.js";
|
|
4
4
|
import { beginDeviceLogin } from "./login.js";
|
|
5
5
|
import { envFetch } from "./proxy.js";
|
|
6
6
|
import { apiOrigin, defaultBaseUrl, VERSION } from "./transport.js";
|
|
7
|
-
const CONNECT_INSTRUCTIONS = "Runtime is not connected on this machine yet. Call runtime_connect: it gives you a link and a code for the person you work for to approve in their browser (no key to copy). Once they have, call runtime_connect again; Runtime's tools then appear (sandboxes, commands, files and the rest).";
|
|
7
|
+
const CONNECT_INSTRUCTIONS = "Runtime is not connected on this machine yet. Call runtime_connect: it gives you a link and a code for the person you work for to approve in their browser (no key to copy). Once they have, call runtime_connect again; Runtime's tools then appear (sandboxes, commands, files and the rest). No person to approve? Open an account paid in USDC: runtime_wallet_open gives an address and the exact amount to send, and runtime_wallet_claim connects once the payment lands (in a shell, `npx withruntime billing topup 20 --new-account --accept-terms --wait` does the same; then call runtime_connect). There is no free trial on that path.";
|
|
8
8
|
const connectTool = {
|
|
9
9
|
name: "runtime_connect",
|
|
10
10
|
title: "Connect this machine to Runtime",
|
|
11
11
|
description: "Connect Runtime on this machine with a browser approval. The first call returns a link and a code: show them to the person you work for and ask them to approve. Call again after they have; it waits up to 45 seconds for the approval, then Runtime's tools appear.",
|
|
12
12
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
13
13
|
};
|
|
14
|
+
const walletOpenTool = {
|
|
15
|
+
name: "runtime_wallet_open",
|
|
16
|
+
title: "Open a Runtime account paid with a wallet",
|
|
17
|
+
description: "For an agent with no person to approve a browser login: opens a Runtime account and its first top-up, paid in USDC on Base, Solana or Tempo. No free trial: the account gets its key only once the payment lands. Answers with an address per network, the exact amount to send and a claim code, shown once: keep it secret, it is the only way to the account. Send exactly that amount from any wallet, then call runtime_wallet_claim with the claim code. Opening an account means agreeing to https://withruntime.com/legal/terms for whoever you act for.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
usd: {
|
|
22
|
+
type: "number",
|
|
23
|
+
description: "Dollars of credit to buy: $10 to $500 in an account's first week.",
|
|
24
|
+
},
|
|
25
|
+
acceptTerms: {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
description: "true agrees to https://withruntime.com/legal/terms. Required.",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["usd", "acceptTerms"],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const walletClaimTool = {
|
|
35
|
+
name: "runtime_wallet_claim",
|
|
36
|
+
title: "Connect to a Runtime account opened with a wallet",
|
|
37
|
+
description: "Once a runtime_wallet_open top-up is paid, saves the account's key on this machine (not shown) and connects; Runtime's tools then appear. Until the payment lands it waits up to 45 seconds and says the top-up's status: call it again.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: {
|
|
41
|
+
claimCode: { type: "string", description: "The claim code runtime_wallet_open gave." },
|
|
42
|
+
},
|
|
43
|
+
required: ["claimCode"],
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/** Where to send a wallet account's first top-up, for an agent to act on. */
|
|
48
|
+
function describeOpened(opened) {
|
|
49
|
+
const lines = [
|
|
50
|
+
`Opened a Runtime account. Send exactly ${opened.amount} USDC (for $${opened.amountUsd} of credit), on one of these networks:`,
|
|
51
|
+
"",
|
|
52
|
+
];
|
|
53
|
+
for (const network of opened.networks)
|
|
54
|
+
for (const token of network.tokens)
|
|
55
|
+
lines.push(`- ${network.network}: ${token.currency.toUpperCase()} to ${network.address} (token contract ${token.contract})`);
|
|
56
|
+
lines.push("", ...(opened.payBy ? [`Send before ${opened.payBy}.`] : []), "A different amount, token or network cannot be matched or returned automatically.", "", `Claim code (shown once, keep it secret): ${opened.claimCode}`, "Once sent, call runtime_wallet_claim with this claim code: it connects when the payment lands, a few minutes after the transfer.", "A person can take the account over at https://withruntime.com/account/claim with the same code.");
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
14
59
|
/** Bridges an MCP client that speaks stdio to Runtime's hosted MCP endpoint.
|
|
15
60
|
* Newline-delimited JSON-RPC in, the same out; every message is forwarded as
|
|
16
61
|
* it is, so the server's tools, schemas and `instructions` reach the client
|
|
@@ -115,7 +160,73 @@ export async function serveMcp(env = process.env, options = {}) {
|
|
|
115
160
|
for (const reply of answer.messages)
|
|
116
161
|
write(reply);
|
|
117
162
|
}
|
|
118
|
-
/**
|
|
163
|
+
/** The website's wallet sign-up, at the same origins a login would use. */
|
|
164
|
+
function wallet() {
|
|
165
|
+
const origins = connectionOrigins(env);
|
|
166
|
+
checkBrowserOrigins(origins);
|
|
167
|
+
const walletOptions = {
|
|
168
|
+
authUrl: origins.auth,
|
|
169
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
open: async (input) => (await import("./wallet.js")).openWalletAccount(input, walletOptions),
|
|
173
|
+
claim: async (code) => (await import("./wallet.js")).claimWalletAccount(code, walletOptions),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** A wallet account's claim: once paid, saves its key and connects. Waits up
|
|
177
|
+
* to 45 seconds for a payment on its way, as runtime_connect does. */
|
|
178
|
+
async function claimWallet(code) {
|
|
179
|
+
const until = Date.now() + 45_000;
|
|
180
|
+
for (;;) {
|
|
181
|
+
const claim = await wallet().claim(code);
|
|
182
|
+
if (claim.status === "paid") {
|
|
183
|
+
// The website issues the key once: a save that fails must not lose it.
|
|
184
|
+
let unsaved;
|
|
185
|
+
try {
|
|
186
|
+
await saveWalletConnection(env, claim.apiKey, claim.orgId);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
unsaved = error.message;
|
|
190
|
+
}
|
|
191
|
+
const said = await connected(claim.apiKey, "a wallet account");
|
|
192
|
+
return [
|
|
193
|
+
unsaved === undefined
|
|
194
|
+
? said
|
|
195
|
+
: `${said}\nThe key could not be saved (${unsaved}), and it is issued only once: keep it secret and safe now, as RUNTIME_API_KEY: ${claim.apiKey}`,
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
if (claim.status === "expired" || claim.status === "failed")
|
|
199
|
+
return [
|
|
200
|
+
`The top-up ${claim.status}: nothing was credited. Open a new one with runtime_wallet_open.`,
|
|
201
|
+
true,
|
|
202
|
+
];
|
|
203
|
+
// A top-up that never reached Stripe has no address: waiting cannot help.
|
|
204
|
+
if (claim.topup?.status === "open" && !claim.topup.networks.length)
|
|
205
|
+
return [claim.next ?? "This top-up has no address to pay to yet.", true];
|
|
206
|
+
if (Date.now() + 10_000 > until)
|
|
207
|
+
return [
|
|
208
|
+
claim.status === "pending"
|
|
209
|
+
? "The payment has arrived and is being confirmed. Call runtime_wallet_claim again in a minute."
|
|
210
|
+
: "Not paid yet. Once the transfer has gone through, call runtime_wallet_claim again with the same claim code.",
|
|
211
|
+
];
|
|
212
|
+
await new Promise((resolve) => setTimeout(resolve, 10_000));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Starts forwarding with this key and tells the client its tools changed. */
|
|
216
|
+
async function connected(found, agentName) {
|
|
217
|
+
key = found;
|
|
218
|
+
pending = undefined;
|
|
219
|
+
await post(JSON.stringify({
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
id: "runtime-connect",
|
|
222
|
+
method: "initialize",
|
|
223
|
+
params: initialize ?? {},
|
|
224
|
+
}));
|
|
225
|
+
await post(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }));
|
|
226
|
+
write({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
|
|
227
|
+
return `Connected${agentName ? ` as ${agentName}` : ""}. Runtime's tools are available now: list the tools again, then start with runtime_sandboxes_create.`;
|
|
228
|
+
}
|
|
229
|
+
/** Not connected yet: answer the client here, with the tools that connect. */
|
|
119
230
|
async function connectMode(message, id) {
|
|
120
231
|
const method = message.method;
|
|
121
232
|
const reply = (result) => write({ jsonrpc: "2.0", id, result });
|
|
@@ -133,38 +244,51 @@ export async function serveMcp(env = process.env, options = {}) {
|
|
|
133
244
|
if (method === "ping")
|
|
134
245
|
return reply({});
|
|
135
246
|
if (method === "tools/list")
|
|
136
|
-
return reply({ tools: [connectTool] });
|
|
247
|
+
return reply({ tools: [connectTool, walletOpenTool, walletClaimTool] });
|
|
248
|
+
const params = (message.params ?? {});
|
|
137
249
|
if (method !== "tools/call" ||
|
|
138
|
-
|
|
250
|
+
!["runtime_connect", "runtime_wallet_open", "runtime_wallet_claim"].includes(params.name))
|
|
139
251
|
return write(failure(id, "Runtime is not connected yet: call runtime_connect first."));
|
|
140
|
-
const text = (words) => reply({ content: [{ type: "text", text: words }] });
|
|
252
|
+
const text = (words, isError = false) => reply({ content: [{ type: "text", text: words }], ...(isError ? { isError } : {}) });
|
|
253
|
+
if (params.name !== "runtime_connect") {
|
|
254
|
+
const input = (params.arguments ?? {});
|
|
255
|
+
try {
|
|
256
|
+
if (params.name === "runtime_wallet_open") {
|
|
257
|
+
if (input.acceptTerms !== true)
|
|
258
|
+
return text("Opening an account means agreeing to https://withruntime.com/legal/terms. Call again with acceptTerms: true to agree to them for whoever you act for.", true);
|
|
259
|
+
if (typeof input.usd !== "number" || !(input.usd > 0))
|
|
260
|
+
return text("Say how many dollars of credit to buy in usd, such as 20.", true);
|
|
261
|
+
return text(describeOpened(await wallet().open({ usd: input.usd, acceptTerms: true })));
|
|
262
|
+
}
|
|
263
|
+
if (typeof input.claimCode !== "string" || !input.claimCode.trim())
|
|
264
|
+
return text("Give the claimCode runtime_wallet_open answered.", true);
|
|
265
|
+
return text(...(await claimWallet(input.claimCode.trim())));
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
return text(error instanceof Error ? error.message : String(error), true);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
141
271
|
try {
|
|
272
|
+
// A key saved since this server started (a wallet account's claim, a
|
|
273
|
+
// login in a shell) connects without a browser.
|
|
274
|
+
const saved = await resolveCredential(env).catch(() => undefined);
|
|
275
|
+
if (saved)
|
|
276
|
+
return text(await connected(saved));
|
|
142
277
|
if (!pending) {
|
|
143
278
|
pending = await beginDeviceLogin(env, env.RUNTIME_AGENT_NAME ?? "MCP agent", {
|
|
144
279
|
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
145
280
|
});
|
|
146
281
|
if (!env.RUNTIME_NO_BROWSER && !pending.resumed)
|
|
147
282
|
await (options.openBrowser ?? openBrowser)(pending.url).catch(() => undefined);
|
|
148
|
-
return text(`Ask the person you work for to open ${pending.url} (it may already be open in their browser), sign in to Runtime, check that it shows the code ${pending.userCode}, and choose Connect agent. Then call runtime_connect again.`);
|
|
283
|
+
return text(`Ask the person you work for to open ${pending.url} (it may already be open in their browser), sign in to Runtime, check that it shows the code ${pending.userCode}, and choose Connect agent. Then call runtime_connect again. No person to approve? See this server's instructions to open an account paid in USDC.`);
|
|
149
284
|
}
|
|
150
285
|
const until = Math.min(pending.expiresAt, Date.now() + 45_000);
|
|
151
286
|
while (Date.now() < until) {
|
|
152
287
|
const outcome = await pending.check();
|
|
153
288
|
if (outcome === "expired")
|
|
154
289
|
break;
|
|
155
|
-
if (outcome !== "pending")
|
|
156
|
-
|
|
157
|
-
pending = undefined;
|
|
158
|
-
await post(JSON.stringify({
|
|
159
|
-
jsonrpc: "2.0",
|
|
160
|
-
id: "runtime-connect",
|
|
161
|
-
method: "initialize",
|
|
162
|
-
params: initialize ?? {},
|
|
163
|
-
}));
|
|
164
|
-
await post(JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" }));
|
|
165
|
-
write({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
|
|
166
|
-
return text(`Connected as ${outcome.agentName}. Runtime's tools are available now: list the tools again, then start with runtime_sandbox_create.`);
|
|
167
|
-
}
|
|
290
|
+
if (outcome !== "pending")
|
|
291
|
+
return text(await connected(await resolveCredential(env), outcome.agentName));
|
|
168
292
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
169
293
|
}
|
|
170
294
|
if (Date.now() >= pending.expiresAt) {
|