withruntime 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,10 @@ The sandbox object does the rest:
45
45
  and persistence, and `keepAlive` to extend its lease while your process runs;
46
46
  - `interpreter`, `network`, `previews` and `desktop` for the other products.
47
47
 
48
+ A sandbox pauses itself after 60 seconds with nothing happening in it, keeping
49
+ its memory and processes, and the next call wakes it, so an idle sandbox costs
50
+ no compute; `idlePauseSeconds` sets 10 to 86,400, or 0 for never.
51
+
48
52
  `Sandbox.getOrCreate(name)` returns the sandbox with that name, woken if it is
49
53
  paused, or creates it.
50
54
 
package/README.src.md ADDED
@@ -0,0 +1,173 @@
1
+ <!-- The source of README.md. Figures are {{facts}} from packages/cloud-guide/src/facts.ts; run `bun run generate` in packages/cloud-guide after editing, which writes README.md. -->
2
+
3
+ # Runtime Cloud SDK and CLI
4
+
5
+ One client for every Runtime Cloud product, and the `runtime` CLI. Node.js
6
+ 22.12 or later, or Bun. Its one dependency, undici (Node's own HTTP client), is
7
+ loaded only behind a proxy.
8
+
9
+ ```bash no-run
10
+ npm install withruntime
11
+ ```
12
+
13
+ The client uses `RUNTIME_API_KEY` when it is set, and otherwise the connection
14
+ this machine saved when the CLI connected it (one browser approval, no key to
15
+ copy). On a server or a CI runner, set `RUNTIME_API_KEY` from your secret
16
+ manager: `npx withruntime keys create` prints a new key once after an account
17
+ owner approves it in the browser, or create one at
18
+ https://withruntime.com/account/keys. Never put a key in browser code, a URL or
19
+ a command-line argument.
20
+
21
+ ```ts
22
+ import { Sandbox } from "withruntime";
23
+
24
+ await using sbx = await Sandbox.create();
25
+ const result = await sbx.exec("python3 -c 'print(6 * 7)'");
26
+ console.log(result.exitCode, result.stdout);
27
+ ```
28
+
29
+ `Sandbox.create()` needs no arguments and returns once the sandbox is running;
30
+ `await using` stops it when the block ends (Node 24, Bun or TypeScript; on
31
+ Node 22 call `await sbx.stop()`). With no arguments you get the free
32
+ trial while it lasts ({{trial-hours}} free hours, up to {{trial-sandboxes}} sandboxes running at once), 2 vCPU, 4 GiB of
33
+ memory and a 4 GiB disk. The current default image includes NumPy, pandas and
34
+ matplotlib; see the [sandbox environment](https://withruntime.com/docs/sandbox-environment).
35
+
36
+ The sandbox object does the rest:
37
+
38
+ - `exec`, `execStream`, `spawn` and `processes` for commands and background
39
+ work, with `cwd`, `env` (the place for secrets), `stdin` and `timeoutMs`;
40
+ - `terminal()` for an interactive terminal over a WebSocket;
41
+ - `forwardPort(5432)` to reach any TCP port in the sandbox from this machine, and
42
+ `tunnel()` for single connections and SSH logins (`runtime sandbox ssh` uses it);
43
+ - `files` to read, write, list, glob, stat, move and remove, and to copy whole
44
+ directories with `upload` and `download`;
45
+ - `pause`, `wake`, `extend`, `fork` and `snapshot` (a paused sandbox also wakes
46
+ by itself on the next call), `update` for its name, automatic wake, idle pause
47
+ and persistence, and `keepAlive` to extend its lease while your process runs;
48
+ - `interpreter`, `network`, `previews` and `desktop` for the other products.
49
+
50
+ A sandbox pauses itself after {{idle-pause}} with nothing happening in it, keeping
51
+ its memory and processes, and the next call wakes it, so an idle sandbox costs
52
+ no compute; `idlePauseSeconds` sets {{idle-pause-min}} to {{idle-pause-max}}, or 0 for never.
53
+
54
+ `Sandbox.getOrCreate(name)` returns the sandbox with that name, woken if it is
55
+ paused, or creates it.
56
+
57
+ The client has `sandboxes`, `images`, `volumes`, `snapshots`, `limits`,
58
+ `feedback` and `support`. `runtime.limits.get()` (0.3.1 and later) says whether
59
+ the key is read-only and what its agent may still spend today. Every write carries an idempotency key, made for you, so the SDK's
60
+ own retries (timeouts, 429, 503) never do anything twice. Errors are typed and
61
+ carry a `code`, a `hint` and a `requestId`.
62
+
63
+ ## Behind a proxy
64
+
65
+ The SDK, the CLI and the MCP bridge read `HTTPS_PROXY`, `HTTP_PROXY` and
66
+ `NO_PROXY` (upper or lower case; lower wins when both are set) on Node and Bun
67
+ alike, with no `NODE_USE_ENV_PROXY` needed. Calls to the API and terminal
68
+ WebSockets go through `HTTPS_PROXY` as a CONNECT tunnel, with any
69
+ `user:password@` in its address sent to the proxy. `HTTP_PROXY` is used only
70
+ for `http://` addresses, never for the HTTPS API. `NO_PROXY` lists hosts to
71
+ reach directly, split by commas or spaces: a name covers its subdomains, a
72
+ leading dot is allowed, `host:port` limits it to one port, and `*` means every
73
+ host. A proxy address without a scheme is `http://`; `http://` and `https://`
74
+ proxies work, and anything else fails at once with `invalid_proxy`. When a call
75
+ cannot get through, the error names the proxy it tried (without the password),
76
+ for example `No answer from Runtime at https://api.withruntime.com through the
77
+ proxy http://proxy.internal:3128 (HTTPS_PROXY).` A `fetch` you pass to
78
+ `new Runtime({ fetch })` is used as it is.
79
+
80
+ Runtime improves fast because agents tell us what they run into: reports go straight into what we build next, and whoever asked is told when it ships. Whenever something gets in your way, report it with `runtime.feedback.submit()` (or `npx withruntime feedback "..."`) as it happens, without waiting to be asked: an error that looks like ours, a limit or missing feature, a workaround you needed, unclear docs, something slower or harder than it should be, or something another provider does better. Small things count, and duplicates are merged on our side. Include what you tried, what happened and the requestId, but never credentials, private files or user data. Then let the user know in one line what you reported.
81
+
82
+ ## Code written for E2B
83
+
84
+ `withruntime/e2b` runs code written for E2B's SDK on Runtime. Change the
85
+ import and set `RUNTIME_API_KEY`. The
86
+ [switch guide](https://withruntime.com/docs/migrate) covers the other providers
87
+ and how to work out what you save:
88
+
89
+ ```ts no-run
90
+ import { Sandbox } from "withruntime/e2b"; // was: from "e2b"
91
+ ```
92
+
93
+ `withruntime/e2b/code-interpreter` replaces `@e2b/code-interpreter`.
94
+ Sandboxes get E2B's defaults: 2 vCPU, 512 MiB and a 300-second timeout. What
95
+ Runtime does not do the way E2B does throws `NotSupportedError` before
96
+ anything happens, naming what to use instead. `E2B.md` in this package lists
97
+ every mapping and gap. Importing `withruntime` alone does not load it.
98
+
99
+ ## Code written for Daytona or Vercel Sandbox
100
+
101
+ `withruntime/daytona` and `withruntime/vercel` do the same for Daytona's SDK
102
+ and Vercel Sandbox's. Change the import:
103
+
104
+ ```ts no-run
105
+ import { Daytona } from "withruntime/daytona"; // was: from "@daytona/sdk"
106
+ import { Sandbox } from "withruntime/vercel"; // was: from "@vercel/sandbox"
107
+ ```
108
+
109
+ Sandboxes get the rival's defaults: Daytona's 1 vCPU, 1 GiB and 3 GiB disk,
110
+ pausing after 15 minutes without calls; Vercel's 2 vCPUs with 2048 MiB each, 5
111
+ minutes, persistent. A Daytona or Vercel key is never sent anywhere. What
112
+ Runtime does not do the same way throws `NotSupportedError` before anything
113
+ happens, naming what to use instead. `DAYTONA.md` and `VERCEL.md` in this
114
+ package list every mapping and gap.
115
+
116
+ ## Code written for Blaxel
117
+
118
+ `withruntime/blaxel` runs code written for Blaxel's sandbox SDK. Change the
119
+ import:
120
+
121
+ ```ts no-run
122
+ import { SandboxInstance } from "withruntime/blaxel"; // was: from "@blaxel/core"
123
+ ```
124
+
125
+ Sandboxes get Blaxel's default of 4096 MB, with one vCPU for every 2048 MB.
126
+ Standby becomes Runtime's pause: after 60 seconds without a call a sandbox
127
+ pauses with its memory and processes, and the next call wakes it. Envs,
128
+ processes by name, files, previews, snapshots, forks and the code interpreter
129
+ work as they do on Blaxel. A Blaxel key is never sent anywhere. Sessions,
130
+ drives, codegen, schedules and Blaxel's agent, model and MCP hosting throw
131
+ `NotSupportedError` before anything happens, naming what to use instead.
132
+ `BLAXEL.md` in this package lists every mapping and gap.
133
+
134
+ ## Agent frameworks
135
+
136
+ `withruntime/openai-agents` is a sandbox client for the OpenAI Agents SDK's
137
+ `SandboxAgent`. `withruntime/ai-harness` is a sandbox provider for the Vercel
138
+ AI SDK's `HarnessAgent` (Claude Code, Codex, OpenCode, Pi and the rest).
139
+ `withruntime/ai` gives Vercel AI SDK and Mastra agents four
140
+ sandbox tools, `withruntime/claude-agent-sdk` gives them to the Claude Agent
141
+ SDK as an MCP server, and `withruntime/tools` gives them in no framework's
142
+ format. Each needs its framework installed beside this package. See
143
+ https://withruntime.com/docs/frameworks.
144
+
145
+ ## CLI
146
+
147
+ ```bash no-run
148
+ npx withruntime sandbox run -- python3 -c 'print(6 * 7)' # connects on first use
149
+ npx withruntime sandbox create
150
+ npx withruntime help
151
+ ```
152
+
153
+ Installed with `npm i -g withruntime`, the command is `runtime`:
154
+ `runtime sandbox exec <id> -- ls`, `runtime sandbox shell <id>`,
155
+ `runtime image build --pip pandas`, and `--json` on every command.
156
+
157
+ ## MCP
158
+
159
+ ```bash no-run
160
+ claude mcp add --scope user runtime -- npx -y withruntime mcp
161
+ codex mcp add runtime -- npx -y withruntime mcp
162
+ ```
163
+
164
+ The bridge serves Runtime's MCP tools on stdio over the saved connection; not
165
+ connected yet, it offers `runtime_connect`, which walks you through the browser
166
+ approval. Remote clients add `https://api.withruntime.com/mcp` and sign in through
167
+ the browser, or send a bearer key.
168
+
169
+ Docs: https://withruntime.com/docs/javascript and https://withruntime.com/docs/cli.
170
+
171
+ The package was called `@withruntime/cloud` until 0.3.0. That name, and
172
+ `runtime-cloud` and `withruntime-cloud`, stopped at 0.5.1 and get no new
173
+ releases.
package/dist/cli.js CHANGED
@@ -42,7 +42,7 @@ Account
42
42
  --allow (paid): only these methods and paths get it
43
43
  secrets ls Names, hosts and placeholders, never values
44
44
  secrets rm <NAME> Delete a secret
45
- keys create [--name <name>] [--read-only] [--daily-limit <usd>]
45
+ keys create [--name <name>] [--read-only] [--daily-limit <usd>] [--account-wide]
46
46
  A new API key for CI, approved by an owner in the browser
47
47
  referrals Your referral link: you both get up to $500
48
48
  compare --from <provider> [--days 30] What your usage would cost at e2b, daytona, vercel,
@@ -1930,11 +1930,14 @@ async function desktop(sbx, argv, out) {
1930
1930
  }
1931
1931
  const KEYS_HELP = `runtime keys <command>
1932
1932
 
1933
- create [--name <name>] [--read-only] [--daily-limit <usd>]
1933
+ create [--name <name>] [--read-only] [--daily-limit <usd>] [--account-wide]
1934
1934
  A new API key, for a CI runner or any place with no browser. Prints a link
1935
1935
  and a code; an account owner approves the key in the browser, seeing its
1936
1936
  name, access and limit. The key is printed once, alone on stdout, and
1937
1937
  lasts until revoked. --json prints it as JSON, still alone on stdout.
1938
+ --account-wide makes a key that sees and uses every sandbox and other
1939
+ resource in the account, whichever key made it; only an owner or admin
1940
+ can approve one. Without it, a key sees only what it makes.
1938
1941
 
1939
1942
  runtime keys create --name ci --daily-limit 25 | gh secret set RUNTIME_API_KEY
1940
1943
 
@@ -2060,8 +2063,8 @@ async function keys(argv, env, out) {
2060
2063
  throw usage("List and revoke keys at https://withruntime.com/account/keys. No key can list or revoke keys.");
2061
2064
  if (verb !== "create")
2062
2065
  throw unknown("keys command", verb, ["create"]);
2063
- const args = parse(rest, ["read-only"], ["name", "daily-limit"]);
2064
- const known = new Set(["name", "read-only", "daily-limit"]);
2066
+ const args = parse(rest, ["read-only", "account-wide"], ["name", "daily-limit"]);
2067
+ const known = new Set(["name", "read-only", "daily-limit", "account-wide"]);
2065
2068
  for (const name of args.flags.keys())
2066
2069
  if (!known.has(name))
2067
2070
  throw usage(`Unknown option --${name}.`);
@@ -2071,16 +2074,20 @@ async function keys(argv, env, out) {
2071
2074
  const limit = flag(args, "daily-limit");
2072
2075
  if (readOnly && limit !== undefined)
2073
2076
  throw usage("A read-only key cannot spend, so it takes no --daily-limit.");
2077
+ if (readOnly && has(args, "account-wide"))
2078
+ throw usage("A read-only key already sees the whole account, so it takes no --account-wide.");
2074
2079
  const { createKey, dailyLimitMicros } = await import("./keys.js");
2075
2080
  const created = await createKey({
2076
2081
  name: flag(args, "name") ?? (readOnly ? "Read-only key" : "My API key"),
2077
2082
  access: readOnly ? "read" : "full",
2083
+ ...(has(args, "account-wide") ? { reach: "account" } : {}),
2078
2084
  dailyLimitMicros: limit === undefined ? null : dailyLimitMicros(limit),
2079
2085
  }, env, { notify: out.error });
2080
2086
  const { confirm, ...key } = created;
2081
2087
  out.write(out.json ? JSON.stringify(key) : key.key);
2082
2088
  const terms = [
2083
2089
  key.access === "read" ? "read only" : "full access",
2090
+ key.reach === "account" ? "account-wide" : undefined,
2084
2091
  key.access === "read"
2085
2092
  ? undefined
2086
2093
  : key.dailyLimitMicros === null
package/dist/keys.d.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  export type KeyAccess = "full" | "read";
2
+ /** What a key acts on: what its own agent makes, or everything in the
3
+ * account, which only an owner or admin can approve. */
4
+ export type KeyReach = "agent" | "account";
2
5
  export type KeyRequest = {
3
6
  /** The key's name, and its agent's. */
4
7
  name: string;
5
8
  access: KeyAccess;
9
+ /** `account` for a key that sees and uses every sandbox and other resource
10
+ * in the account, whichever key made it. Absent is `agent`. */
11
+ reach?: KeyReach;
6
12
  /** The most its agent may spend in any 24 hours, or null for none. */
7
13
  dailyLimitMicros: number | null;
8
14
  };
package/dist/keys.js CHANGED
@@ -57,6 +57,8 @@ export async function createKey(request, env, options = {}) {
57
57
  throw failure("usage", "Give the key a name of up to 80 characters.");
58
58
  if (request.access === "read" && request.dailyLimitMicros !== null)
59
59
  throw failure("usage", "A read-only key cannot spend, so it takes no --daily-limit.");
60
+ if (request.access === "read" && request.reach === "account")
61
+ throw failure("usage", "A read-only key already sees the whole account, so it takes no --account-wide.");
60
62
  const origins = connectionOrigins(env);
61
63
  const fetcher = options.fetch ?? envFetch;
62
64
  const notify = options.notify ?? ((message) => process.stderr.write(`${message}\n`));
@@ -99,6 +101,7 @@ export async function createKey(request, env, options = {}) {
99
101
  purpose: "key",
100
102
  agentName: name,
101
103
  access: request.access,
104
+ ...(request.reach === "account" ? { reach: "account" } : {}),
102
105
  dailyLimitMicros: request.dailyLimitMicros,
103
106
  machine: options.machine ?? hostname(),
104
107
  publicKey: pair.publicKey,
@@ -177,6 +180,7 @@ export async function createKey(request, env, options = {}) {
177
180
  return {
178
181
  name,
179
182
  access: request.access,
183
+ reach: request.reach ?? "agent",
180
184
  dailyLimitMicros: request.dailyLimitMicros,
181
185
  key,
182
186
  keyId: match[1],
package/dist/sandbox.js CHANGED
@@ -4,7 +4,12 @@ import { sandboxWatches, watchDirectory, } from "./products/watch.js";
4
4
  import { sandboxFactories } from "./products/index.js";
5
5
  import { Tunnel } from "./tunnel.js";
6
6
  const CHUNK = 1_048_576;
7
- const PARALLEL = 4;
7
+ /** Chunks of a large write in flight at once. Each chunk's reply waits on the
8
+ * API and the guest, and the link idles while every chunk in flight waits:
9
+ * from a home link (12.6 MB/s up, 27 September 2026) 100 MB took 17-18 s at
10
+ * four and 11 s at eight, and sixteen was no faster. The guest takes 128
11
+ * connections at once. */
12
+ const PARALLEL = 8;
8
13
  const enc = (id) => encodeURIComponent(id);
9
14
  const toBase64 = (data) => (typeof data === "string" ? Buffer.from(data) : Buffer.from(data)).toString("base64");
10
15
  function commandBody(command, options) {
@@ -1,5 +1,5 @@
1
1
  import { RuntimeError } from "./errors.js";
2
- export declare const VERSION = "0.8.1";
2
+ export declare const VERSION = "0.8.2";
3
3
  export declare const DEFAULT_BASE_URL = "https://api.withruntime.com";
4
4
  export type RequestOptions = {
5
5
  /** Sent as Idempotency-Key. Omit it: the SDK makes one per call and keeps
package/dist/transport.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ConnectionError, errorFor, RuntimeError, WAITS_FOR_ROOM } from "./errors.js";
2
2
  import { describeRoute, envFetch, openWebSocket } from "./proxy.js";
3
- export const VERSION = "0.8.1";
3
+ export const VERSION = "0.8.2";
4
4
  export const DEFAULT_BASE_URL = "https://api.withruntime.com";
5
5
  export function apiOrigin(value) {
6
6
  const url = new URL(value);
package/dist/types.d.ts CHANGED
@@ -18,7 +18,8 @@ export type SandboxInfo = {
18
18
  timeoutSeconds: number;
19
19
  /** Pauses after this many idle seconds; 0 is never. */
20
20
  idlePauseSeconds?: number;
21
- /** True for the default idle pause, which pauses only a sandbox nothing has used yet. */
21
+ /** True only for a sandbox created before 27 September 2026 with the old
22
+ * default idle pause, which paused it only while nothing had used it. */
22
23
  idlePauseUnusedOnly?: boolean;
23
24
  /** A request (exec, files, terminal, a visit to a shared port) wakes it when paused. */
24
25
  autoWake?: boolean;
@@ -66,8 +67,10 @@ export type CreateSandbox = {
66
67
  pausable?: boolean;
67
68
  /** What happens when timeoutSeconds runs out: "pause" (default) or "stop". */
68
69
  onLeaseEnd?: "pause" | "stop";
69
- /** Pause after this many seconds with no exec, file, terminal, desktop or
70
- * preview request (60 to 86400; 0 never). A request wakes it again. */
70
+ /** Pause after this many seconds in which nothing happens in it: no
71
+ * request, no command or terminal running, no open connection, no network
72
+ * traffic and no CPU use. Default 60 for a pausable sandbox; 0 never;
73
+ * otherwise 10 to 86400. A request wakes it again. */
71
74
  idlePauseSeconds?: number;
72
75
  /** A request to a paused sandbox wakes it. Default true. */
73
76
  autoWake?: boolean;
@@ -202,8 +205,9 @@ export type Usage = {
202
205
  reservedMs: number;
203
206
  availableMs: number;
204
207
  } | null;
205
- /** Outbound traffic this calendar month, UTC: the first `allowanceBytes`
206
- * (100 GiB) an account sends are free, the rest $0.02 per decimal GB. */
208
+ /** Outbound traffic this calendar month, UTC: the first `allowanceBytes` an
209
+ * account sends are free, and the rest is charged at the rate in the pricing
210
+ * guide (https://withruntime.com/docs/pricing#network-products). */
207
211
  outbound?: {
208
212
  month: string;
209
213
  sentBytes: number;
@@ -224,7 +228,8 @@ export type SandboxSettings = {
224
228
  labels?: Record<string, string>;
225
229
  /** A request to a paused sandbox wakes it. */
226
230
  autoWake?: boolean;
227
- /** Pause after this many seconds with no activity, counted from now; 0 never. */
231
+ /** Pause after this many seconds with no activity, counted from now; 0
232
+ * never, otherwise 10 to 86400. */
228
233
  idlePauseSeconds?: number;
229
234
  /** Keep it running while credit lasts and keep its disk after a stop. Paid only. */
230
235
  persistent?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",