withruntime 0.16.1 → 0.17.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/DAYTONA.md CHANGED
@@ -17,8 +17,8 @@ from withruntime.daytona import AsyncDaytona # was: from daytona import AsyncDa
17
17
  ```
18
18
 
19
19
  The mapping was written against `@daytona/sdk` 0.220.0 and `daytona` 0.220.0
20
- on PyPI, and checked against `@daytona/sdk` and `daytona` 0.224.1 on
21
- 15 October 2026 (below, "0.224.1"). The exact releases and artifact hashes
20
+ on PyPI, and checked against `@daytona/sdk` and `daytona` 0.225.0 on
21
+ 18 October 2026 (below, "0.225.0"). The exact releases and artifact hashes
22
22
  are kept in `compatibility-lock.json`.
23
23
 
24
24
  ## Keys
@@ -126,6 +126,37 @@ Some differences are not refusals:
126
126
  - `autoArchiveInterval` is recorded only: Runtime has no archive tier.
127
127
  - Runtime's stock image is Ubuntu 24.04 with Python 3.12, Node.js 24 and Bun.
128
128
 
129
+ ## 0.225.0, 18 October 2026
130
+
131
+ Reviewed the published npm tarballs for `@daytona/sdk`, `@daytona/api-client`,
132
+ `@daytona/toolbox-api-client` and `@daytona/analytics-api-client` 0.224.1 →
133
+ 0.225.0, and the PyPI wheel and sdist for `daytona` 0.224.1 → 0.225.0, each its
134
+ registry's latest when checked. Every pinned file matched the hash already in
135
+ `compatibility-lock.json`, every new file its registry's SHA-512 integrity or
136
+ SHA-256 digest, and the wheel's Python source is byte for byte its sdist's.
137
+ Nothing was installed, imported or run. No declaration of `@daytona/sdk` and no
138
+ Python source of `daytona` changed: only versions and the exact pins on the
139
+ three API clients. The one API change is in `@daytona/api-client` (and its
140
+ PyPI twins): `GpuType` gains `B200`.
141
+
142
+ | Upstream change | Drop-in disposition |
143
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
144
+ | `GpuType.B200` | Exported, with every other `GpuType`. A create naming any GPU type is refused (`NotSupportedError`, "Runtime runs sandboxes on CPUs") before anything is made. |
145
+
146
+ Two gaps near it are closed with the review. `@daytona/sdk` re-exports
147
+ `GpuType`, `SnapshotState`, `SandboxListSortField`, `SandboxListSortDirection`,
148
+ `SandboxClass`, `ListSecretsPaginatedSortEnum` and
149
+ `ListSecretsPaginatedOrderEnum`, and `daytona` exports `GpuType`,
150
+ `SandboxClass`, `SandboxListSortField` and `SandboxListSortDirection`; the
151
+ drop-ins exported none of them, so code importing one failed. They are exported
152
+ now with upstream's values, and the sort enums drive `list`. Python refused
153
+ `Resources(gpu=...)` but made a CPU sandbox for `Resources(gpu_type=...)`
154
+ alone, which Daytona sends as a GPU request; it is refused now, as in
155
+ TypeScript. `tests/daytona/daytona-0-225-0.test.ts` and
156
+ `sdks/python/tests/test_daytona_0_225_0.py` fail without this change. The
157
+ oracle was not run against the new release, since it executes Daytona's code;
158
+ the provider remains marked `partial`.
159
+
129
160
  ## 0.224.1, 15 October 2026
130
161
 
131
162
  Reviewed the published npm tarballs for `@daytona/sdk`, `@daytona/api-client`,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "checkedAt": "2026-10-11",
3
+ "checkedAt": "2026-10-18",
4
4
  "providers": [
5
5
  {
6
6
  "id": "e2b",
@@ -49,42 +49,42 @@
49
49
  {
50
50
  "registry": "npm",
51
51
  "name": "@daytona/sdk",
52
- "version": "0.224.1",
52
+ "version": "0.225.0",
53
53
  "artifacts": {
54
- "0.224.1": "sha512-ZQCY9qOXnAXpYD8KhMzn27VNk6KYfBYCpHhij5Th36+fMXfG8qpxvnb3xau/H7ZWN1HPBhlzjaHHn/J+ZhH/OQ=="
54
+ "0.225.0": "sha512-PeE4INyipIP6HzSNAZTfR+As2u+rwddCAniCk9a/gEjEh4JpvyCyTNASjikpsTcZSn1XvksmxhxKFodv6x0YYQ=="
55
55
  }
56
56
  },
57
57
  {
58
58
  "registry": "pypi",
59
59
  "name": "daytona",
60
- "version": "0.224.1",
60
+ "version": "0.225.0",
61
61
  "artifacts": {
62
- "daytona-0.224.1-py3-none-any.whl": "sha256-2dedf1ef0c5f98dd0c3a70ab130284abdd7414a3b8a6f6452842c4f5dcc2a583",
63
- "daytona-0.224.1.tar.gz": "sha256-02d5a2fa8099f18503310a80b6b807c5fb18863738c13026ea4dad21a454a831"
62
+ "daytona-0.225.0-py3-none-any.whl": "sha256-8cdeb78e5d34b08c4818bd9dfa67fb693152c37eb0907067980fc1bd031256e5",
63
+ "daytona-0.225.0.tar.gz": "sha256-7fd2e9422360ca2d3b393af137231f4fd9d9a128c9a4070d231f8c776e446dc8"
64
64
  }
65
65
  },
66
66
  {
67
67
  "registry": "npm",
68
68
  "name": "@daytona/api-client",
69
- "version": "0.224.1",
69
+ "version": "0.225.0",
70
70
  "artifacts": {
71
- "0.224.1": "sha512-RFsDcGuifpyKnUHlfTLC9jDXXg7ZwciKlzmy0MBGcG5VKSRdwlRyqCaM9g6/k7m5eQ+N1ieQR2JAjZnVWn23YQ=="
71
+ "0.225.0": "sha512-P0HJOfLc3LGzeRdRhIogaA2q4+Of84UBCMWCFfnDeJ91pxQ4FExJ2KY5uF631MoNx1F+u2tc7fl0z0JuBNa/Uw=="
72
72
  }
73
73
  },
74
74
  {
75
75
  "registry": "npm",
76
76
  "name": "@daytona/toolbox-api-client",
77
- "version": "0.224.1",
77
+ "version": "0.225.0",
78
78
  "artifacts": {
79
- "0.224.1": "sha512-fiuBJedfubBMoLh1Z7VdAWsgnQaYGsQMZoAGV12IZ3R5D/ELFATd4tq/jW1mhSl7X27lxqSmtkHvaQt3ELRNPA=="
79
+ "0.225.0": "sha512-+LGQtqZTY0SBGMut3bhCPNU/cvfwKQcU1tAoVStZD9mM7EHWPey2TkMF+ZW0Fq2waWPEH2g7JhWWz9YFBUBQeQ=="
80
80
  }
81
81
  },
82
82
  {
83
83
  "registry": "npm",
84
84
  "name": "@daytona/analytics-api-client",
85
- "version": "0.224.1",
85
+ "version": "0.225.0",
86
86
  "artifacts": {
87
- "0.224.1": "sha512-CDW5CWbf5Ql0h31uZDD0vcOjJ8T0KimARqgni6gc9YICAoN0tx3getO4CDySTgjJpiYwH1qw4zlJs+h36L0T8w=="
87
+ "0.225.0": "sha512-zsj9YjBpXmQ2owhJF7+LEpKnekYy9D4wA52U6P7jsB7ng5KeSQ+3+0MFR3Bj3Cy3HIcKNW96e30F6olbiK/q+A=="
88
88
  }
89
89
  }
90
90
  ]
@@ -248,10 +248,10 @@
248
248
  {
249
249
  "registry": "pypi",
250
250
  "name": "prime-sandboxes",
251
- "version": "0.4.5",
251
+ "version": "0.4.6",
252
252
  "artifacts": {
253
- "prime_sandboxes-0.4.5-py3-none-any.whl": "sha256-3f24ed4830d2d823169fdb2df75de3b8355224bf867c0208d47eab0d3b33bb33",
254
- "prime_sandboxes-0.4.5.tar.gz": "sha256-4f7b7e50d3d60d3b459fb3ace5713e7d9eb136ac0aa79cf0e7ee7c75c63b0d82"
253
+ "prime_sandboxes-0.4.6-py3-none-any.whl": "sha256-e97c6e70c032e0b6d7defdb26d6192a02643ad279af1a4726ebceb35ee89d70c",
254
+ "prime_sandboxes-0.4.6.tar.gz": "sha256-23204a465500e9c20639e3043e92d703712d8c80b94d6434bfeb82c787102915"
255
255
  }
256
256
  }
257
257
  ]
@@ -1,5 +1,5 @@
1
1
  import type { Runtime } from "./client.js";
2
- import type { claimWalletAccount, openWalletAccount } from "./wallet.js";
2
+ import type { claimWalletAccount, openWalletAccount, rekeyWalletAccount } from "./wallet.js";
3
3
  type Out = {
4
4
  json: boolean;
5
5
  write: (text: string) => void;
@@ -9,11 +9,13 @@ type Args = {
9
9
  positional: string[];
10
10
  flags: Map<string, string[]>;
11
11
  };
12
- export declare const BILLING_HELP = "runtime billing <command>\n\n topup <usd> [--wait] Buy credit with stablecoins (USDC on Base, Solana or Tempo).\n Prints an address per network and the exact amount to send.\n Send exactly that, from any wallet, before the time shown.\n --wait keeps checking until the credit lands.\n status <id> [--wait] Whether a top-up's payment has arrived\n\n auto-top-up Show auto top-up: the card is charged when credit runs low\n auto-top-up on [--below <usd>] [--amount <usd>] [--monthly <usd>]\n Charge <amount> plus tax when credit falls below <below>,\n at most <monthly> a month: $10, $20 and $500 unless given,\n or as last set. At least $10, $20 and $100. 5% more as\n credit each time, and 200 sandboxes at once while on.\n A person saves the card at\n https://withruntime.com/account/billing\n auto-top-up off Turn it off. Running sandboxes are never stopped.\n\n No account yet? An agent can open one with a wallet alone:\n topup <usd> --new-account --accept-terms [--wait]\n Opens an account and its first top-up. Prints a claim code,\n shown once. --wait saves the key here once the money lands.\n claim <code> [--wait] Exchange a paid account's claim code for its key, saved here\n\n $10 to $10,000 a top-up; $500 in an account's first week. By card:\n https://withruntime.com/account/billing\n";
12
+ export declare const BILLING_HELP = "runtime billing <command>\n\n topup <usd> [--wait] Buy credit with stablecoins (USDC on Base, Solana or Tempo).\n Prints an address per network and the exact amount to send.\n Send exactly that, from any wallet, before the time shown.\n --wait keeps checking until the credit lands.\n status <id> [--wait] Whether a top-up's payment has arrived\n\n auto-top-up Show auto top-up: the card is charged when credit runs low\n auto-top-up on [--below <usd>] [--amount <usd>] [--monthly <usd>]\n Charge <amount> plus tax when credit falls below <below>,\n at most <monthly> a month: $10, $20 and $500 unless given,\n or as last set. At least $10, $20 and $100. 5% more as\n credit each time, and 200 sandboxes at once while on.\n A person saves the card at\n https://withruntime.com/account/billing\n auto-top-up off Turn it off. Running sandboxes are never stopped.\n\n No account yet? An agent can open one with a wallet alone:\n topup <usd> --new-account --accept-terms [--wait]\n Opens an account and its first top-up. Prints a claim code,\n shown once. --wait saves the key here once the money lands.\n claim <code> [--wait] Exchange a paid account's claim code for its key, saved here\n claim <code> --new-key Lost the key? Replace it with the claim code. The new key is\n saved here; every other key stops working.\n\n $10 to $10,000 a top-up; $500 in an account's first week. By card:\n https://withruntime.com/account/billing\n";
13
13
  /** Wallet-account commands need no key, and the claim saves the one they get. */
14
14
  export type WalletDeps = {
15
15
  open: typeof openWalletAccount;
16
16
  claim: typeof claimWalletAccount;
17
+ /** Replaces a lost key with the claim code. */
18
+ rekey: typeof rekeyWalletAccount;
17
19
  /** Saves a claimed key as this machine's connection, as `login --with-key` does. */
18
20
  save: (key: string, orgId: string) => Promise<void>;
19
21
  };
@@ -23,6 +23,8 @@ export const BILLING_HELP = `runtime billing <command>
23
23
  Opens an account and its first top-up. Prints a claim code,
24
24
  shown once. --wait saves the key here once the money lands.
25
25
  claim <code> [--wait] Exchange a paid account's claim code for its key, saved here
26
+ claim <code> --new-key Lost the key? Replace it with the claim code. The new key is
27
+ saved here; every other key stops working.
26
28
 
27
29
  $10 to $10,000 a top-up; $500 in an account's first week. By card:
28
30
  https://withruntime.com/account/billing
@@ -53,7 +55,15 @@ function describe(topup) {
53
55
  lines.push("", ...(topup.payBy ? [`Send before ${topup.payBy}.`] : []), "A different amount, token or network cannot be matched or returned automatically.", `The credit lands a few minutes after the transfer: \`${me} billing status ${topup.purchaseId}\`.`);
54
56
  return lines.join("\n");
55
57
  }
56
- const FLAGS = new Set(["wait", "new-account", "accept-terms", "below", "amount", "monthly"]);
58
+ const FLAGS = new Set([
59
+ "wait",
60
+ "new-account",
61
+ "accept-terms",
62
+ "below",
63
+ "amount",
64
+ "monthly",
65
+ "new-key",
66
+ ]);
57
67
  export async function billingCommand(argv, args, out, client, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), wallet) {
58
68
  const [verb, value] = args.positional;
59
69
  const has = (name) => args.flags.get(name)?.at(-1) === "true";
@@ -68,6 +78,11 @@ export async function billingCommand(argv, args, out, client, sleep = (ms) => ne
68
78
  if ((verb === "topup" && has("new-account")) || verb === "claim") {
69
79
  if (!wallet)
70
80
  throw usage("Wallet accounts are not available here.");
81
+ if (has("new-key")) {
82
+ if (verb !== "claim")
83
+ throw usage("--new-key is for `billing claim <code> --new-key`.");
84
+ return rekeyCommand(value, out, wallet);
85
+ }
71
86
  return walletCommand(verb, value, has("accept-terms"), wait, out, sleep, wallet);
72
87
  }
73
88
  if (verb === "auto-top-up")
@@ -177,6 +192,17 @@ function dollarsOf(value) {
177
192
  throw usage("Say how many dollars of credit to buy, such as `billing topup 20`.");
178
193
  return usd;
179
194
  }
195
+ /** `claim <code> --new-key`: the key was lost; the claim code makes a new one. */
196
+ async function rekeyCommand(code, out, wallet) {
197
+ if (!code)
198
+ throw usage("Give the claim code.");
199
+ const replaced = await wallet.rekey(code);
200
+ await wallet.save(replaced.apiKey, replaced.orgId);
201
+ out.write(out.json
202
+ ? JSON.stringify({ status: "replaced", orgId: replaced.orgId, connected: true })
203
+ : "Replaced. This machine is connected with the new key, saved and not shown. Every other key of this account no longer works.");
204
+ return 0;
205
+ }
180
206
  /** `topup <usd> --new-account` and `claim <code>`: an account with no key. */
181
207
  async function walletCommand(verb, value, acceptTerms, wait, out, sleep, wallet) {
182
208
  const me = named("runtime");
package/dist/cli.js CHANGED
@@ -124,12 +124,12 @@ const SANDBOX_HELP = `runtime sandboxes <command>
124
124
 
125
125
  Every <id> below can also be the sandbox's name.
126
126
 
127
- run [--keep] [create options] -- <command> A fresh sandbox runs one command, then is deleted
127
+ run [--keep] [--root] [create options] -- <command> A fresh sandbox runs one command, then is deleted
128
128
  ssh <id|name> [-- <command...>] | ssh config [--install] Log in over SSH, also from VS Code and JetBrains
129
129
  code <id|name> [--editor vscode|cursor|windsurf] [--path /workspace] [--print]
130
130
  Open it in your editor over SSH, in one step
131
131
  port-forward <id|name> <port|local:remote>... Forward local ports to ports in it
132
- create [--name n] [--label k=v]... [--env K=V]... [--vcpu 2] [--memory 4096] [--disk 4096]
132
+ create [--name n] [--label k=v]... [--env K[=V]]... [--vcpu 2] [--memory 4096] [--disk 4096]
133
133
  [--cpu shared|reserved] [--cpu-floor <thousandths>] [--max-cost <usd>] [--max-total-cost <usd>]
134
134
  [--timeout <60-86400>] [--on-timeout pause|stop|delete] [--no-wait]
135
135
  [--no-internet] [--allow <host>]... [--deny <host>]... [--connect <host:port>]...
@@ -139,15 +139,16 @@ const SANDBOX_HELP = `runtime sandboxes <command>
139
139
  Create one; prints its id
140
140
  ls [--all] [--label k=v]... [--state running] List (live ones unless --all)
141
141
  get <id> One sandbox, in full
142
- exec <id> [--cwd /workspace] [--env K=V]... [--timeout <s>] -- <command...>
143
- Run and stream output; exits with its code
144
- spawn <id> [--pty] -- <command...> Start in the background; prints a process id
142
+ exec <id> [--cwd /workspace] [--env K[=V]]... [--timeout <s>] [--root] -- <command...>
143
+ Run and stream output; exits with its code.
144
+ --root runs it as root: no sudo needed
145
+ spawn <id> [--pty] [--root] -- <command...> Start in the background; prints a process id
145
146
  ps <id> Running and recent processes
146
147
  logs <id> <processId> [-f] [--raw] A process's output so far; -f follows it to the end;
147
148
  on a terminal control characters show as \\uXXXX
148
149
  unless --raw
149
150
  kill <id> <processId> [--signal SIGTERM]
150
- shell <id> [--command "bash -l"] An interactive terminal
151
+ shell <id> [--command "bash -l"] [--root] An interactive terminal
151
152
  cp <src> <dst> Copy files or directories; name a sandbox
152
153
  path as <id>:/path, e.g. cp ./app sbx:/workspace/app
153
154
  cat <id> <path> Print a file
@@ -166,10 +167,12 @@ const SANDBOX_HELP = `runtime sandboxes <command>
166
167
  --disk only grows it; programs stop (snapshot it first
167
168
  to keep memory)
168
169
  extend <id> <seconds> Move a time limit on (one with none needs nothing)
169
- update <id> [--name n] [--label k=v]... [--env K=V]... [--unset-env K]... [--idle-pause <seconds>] [--auto-wake on|off]
170
+ update <id> [--name n] [--label k=v]... [--env K[=V]]... [--unset-env K]... [--idle-pause <seconds>] [--auto-wake on|off]
170
171
  [--persistent on|off] [--max-total-cost <usd>|none]
171
172
  [--timeout <60-86400 from now>|0] [--on-timeout pause|stop|delete]
172
- Change its settings; persistent keeps it running until stopped
173
+ Change its settings; persistent keeps it running until stopped;
174
+ --env K alone sends K's value from your own environment,
175
+ so it is in no command line or shell history
173
176
  snapshot <id> [--name n] [--retention days|credit] Keep its whole machine; prints the snapshot id
174
177
  mount <id> <s3|r2|gcs>://<bucket>[/prefix] <path> [--secret NAME] [--region r]
175
178
  [--endpoint https://...] [--account-id id] [--read-only]
@@ -333,6 +336,20 @@ const pairs = (args, name) => Object.fromEntries((args.flags.get(name) ?? []).ma
333
336
  throw usage(`--${name} takes KEY=VALUE.`);
334
337
  return [pair.slice(0, at), pair.slice(at + 1)];
335
338
  }));
339
+ /** --env KEY=VALUE, or --env KEY alone to take KEY's value from the CLI's own
340
+ * environment, as docker run -e does: the value stays out of the process's
341
+ * arguments and the shell's history. */
342
+ const envPairs = (args, from) => Object.fromEntries((args.flags.get("env") ?? []).map((pair) => {
343
+ const at = pair.indexOf("=");
344
+ if (at > 0)
345
+ return [pair.slice(0, at), pair.slice(at + 1)];
346
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(pair))
347
+ throw usage(`--env takes KEY=VALUE, or KEY alone to send KEY's value from your environment.`);
348
+ const value = from[pair];
349
+ if (value === undefined)
350
+ throw usage(`--env ${pair} sends ${pair} from your environment, and it is not set there.`);
351
+ return [pair, value];
352
+ }));
336
353
  /** What the commands in cli-extras.ts use of this file. */
337
354
  const kit = {
338
355
  parse: (argv, booleans, values) => parse(argv, booleans, values),
@@ -648,13 +665,13 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
648
665
  return 0;
649
666
  }
650
667
  if (command === "run") {
651
- const args = parse(rest, [...CREATE_FLAGS, "keep"], [...CREATE_VALUES, "cwd", "env"], `${me} sandboxes run -- python3 < invoice.py`);
668
+ const args = parse(rest, [...CREATE_FLAGS, "keep", "root"], [...CREATE_VALUES, "cwd", "env"], `${me} sandboxes run -- python3 < invoice.py`);
652
669
  const commandLine = args.rest ?? args.positional;
653
670
  if (!commandLine.length)
654
671
  throw usage(`Give the command after --: ${me} sandboxes run -- python3 < invoice.py`);
655
672
  const runtime = await client(env);
656
673
  const timeout = integer(args, "timeout");
657
- const { timeoutSeconds: _lease, ...input } = createInput(args);
674
+ const { timeoutSeconds: _lease, ...input } = createInput(args, env);
658
675
  const onCapacityWait = waiting(out);
659
676
  const sbx = await withVolumeRelease((options) => runtime.sandboxes.create(input, { ...options, onCapacityWait }), out);
660
677
  let cleanup;
@@ -685,7 +702,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
685
702
  process.once("SIGINT", interrupted);
686
703
  let code = 1;
687
704
  try {
688
- code = await execute(sbx, commandLine, args, out, timeout, has(args, "keep"));
705
+ code = await execute(sbx, commandLine, args, out, timeout, env, has(args, "keep"));
689
706
  }
690
707
  finally {
691
708
  try {
@@ -820,6 +837,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
820
837
  ],
821
838
  ["used, last 24 hours", usd(l.daily.usedMicros)],
822
839
  ...(l.daily.remainingMicros === null ? [] : [["left", usd(l.daily.remainingMicros)]]),
840
+ ...(l.credit ? [["credit", creditLine(l.credit, usd)]] : []),
823
841
  ...(l.trial ? [["free time", trialLeft(l.trial)]] : []),
824
842
  ...(spend?.pilot
825
843
  ? [
@@ -828,6 +846,8 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
828
846
  ]
829
847
  : []),
830
848
  ]), l);
849
+ if (l.credit?.addedBy === "agent" && !out.json)
850
+ out.write(`No person pays for this account, so add credit yourself: \`${me} billing topup <usd>\`.`);
831
851
  return 0;
832
852
  }
833
853
  if (command === "secrets")
@@ -912,8 +932,8 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
912
932
  if (command === "billing") {
913
933
  product = "billing";
914
934
  const origins = connectionOrigins(env);
915
- const { claimWalletAccount, openWalletAccount } = await import("./wallet.js");
916
- return billingCommand(rest, parse(rest, ["wait", "new-account", "accept-terms"], ["below", "amount", "monthly"]), out, () => client(env), undefined, {
935
+ const { claimWalletAccount, openWalletAccount, rekeyWalletAccount } = await import("./wallet.js");
936
+ return billingCommand(rest, parse(rest, ["wait", "new-account", "accept-terms", "new-key"], ["below", "amount", "monthly"]), out, () => client(env), undefined, {
917
937
  open: (input) => {
918
938
  checkBrowserOrigins(origins);
919
939
  return openWalletAccount(input, { authUrl: origins.auth });
@@ -922,6 +942,10 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
922
942
  checkBrowserOrigins(origins);
923
943
  return claimWalletAccount(code, { authUrl: origins.auth });
924
944
  },
945
+ rekey: (code) => {
946
+ checkBrowserOrigins(origins);
947
+ return rekeyWalletAccount(code, { authUrl: origins.auth });
948
+ },
925
949
  save: (key, orgId) => {
926
950
  checkBrowserOrigins(origins);
927
951
  return connectionStore(env).save({
@@ -1339,6 +1363,14 @@ export function outOfMemoryLines(memory) {
1339
1363
  /** Dollars from integer microdollars: cents from a dollar up, four places
1340
1364
  * below one, and every place below a hundredth of a cent, so a short trial
1341
1365
  * test never reads as $0.0000. */
1366
+ function creditLine(c, usd) {
1367
+ if (c.state === "empty")
1368
+ return "none left: add credit to keep going";
1369
+ const days = c.runwayDays === null
1370
+ ? "nothing is being spent"
1371
+ : `lasts about ${Math.max(1, Math.round(c.runwayDays))} ${Math.round(c.runwayDays) <= 1 ? "day" : "days"} at this month's pace`;
1372
+ return `${usd(c.availableMicros)} left, ${days}${c.state === "low" ? " (low)" : ""}`;
1373
+ }
1342
1374
  function dollars(micros) {
1343
1375
  const value = BigInt(micros);
1344
1376
  const sign = value < 0n ? "-" : "";
@@ -1655,7 +1687,7 @@ async function sandbox(argv, env, out) {
1655
1687
  switch (verb) {
1656
1688
  case "create": {
1657
1689
  const args = parse(rest, CREATE_FLAGS, CREATE_VALUES);
1658
- const input = createInput(args);
1690
+ const input = createInput(args, env);
1659
1691
  const onCapacityWait = waiting(out);
1660
1692
  const sbx = await withVolumeRelease((options) => runtime.sandboxes.create(input, { ...options, onCapacityWait }), out);
1661
1693
  print(sbx.id, sbx.info);
@@ -1883,14 +1915,14 @@ async function sandbox(argv, env, out) {
1883
1915
  const cost = flag(args, "max-total-cost");
1884
1916
  if (cost !== undefined && cost !== "none" && !/^\d+(\.\d{1,6})?$/.test(cost))
1885
1917
  throw usage("--max-total-cost takes dollars, such as 25 or 2.50, or none.");
1886
- const env = {
1887
- ...(args.flags.has("env") ? pairs(args, "env") : {}),
1918
+ const variables = {
1919
+ ...(args.flags.has("env") ? envPairs(args, env) : {}),
1888
1920
  ...Object.fromEntries((args.flags.get("unset-env") ?? []).map((name) => [name, null])),
1889
1921
  };
1890
1922
  const settings = {
1891
1923
  ...(flag(args, "name") ? { name: flag(args, "name") } : {}),
1892
1924
  ...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
1893
- ...(Object.keys(env).length ? { env } : {}),
1925
+ ...(Object.keys(variables).length ? { env: variables } : {}),
1894
1926
  ...(integer(args, "idle-pause") !== undefined
1895
1927
  ? { idlePauseSeconds: integer(args, "idle-pause") }
1896
1928
  : {}),
@@ -1922,23 +1954,24 @@ async function sandbox(argv, env, out) {
1922
1954
  return 0;
1923
1955
  }
1924
1956
  case "exec": {
1925
- const args = parse(rest, [], ["cwd", "env", "timeout"], `${me} sandboxes exec <id> -- ls -la`);
1957
+ const args = parse(rest, ["root"], ["cwd", "env", "timeout"], `${me} sandboxes exec <id> -- ls -la`);
1926
1958
  const command = args.rest ?? args.positional.slice(1);
1927
1959
  if (!command.length)
1928
1960
  throw usage("Give the command after --: runtime sandboxes exec <id> -- ls -la");
1929
1961
  const sbx = await use(args.positional[0]);
1930
- return execute(sbx, command, args, out, integer(args, "timeout"));
1962
+ return execute(sbx, command, args, out, integer(args, "timeout"), env);
1931
1963
  }
1932
1964
  case "spawn": {
1933
- const args = parse(rest, ["pty"], ["cwd", "env"], `${me} sandboxes spawn <id> -- npm run dev`);
1965
+ const args = parse(rest, ["pty", "root"], ["cwd", "env"], `${me} sandboxes spawn <id> -- npm run dev`);
1934
1966
  const command = args.rest ?? [];
1935
1967
  if (!command.length)
1936
1968
  throw usage("Give the command after --: runtime sandboxes spawn <id> -- npm run dev");
1937
1969
  const sbx = await use(args.positional[0]);
1938
1970
  const proc = await sbx.spawn(command.length === 1 ? command[0] : command, {
1939
1971
  ...(has(args, "pty") ? { pty: { cols: 120, rows: 40 } } : {}),
1972
+ ...(has(args, "root") ? { user: "root" } : {}),
1940
1973
  ...(flag(args, "cwd") ? { cwd: flag(args, "cwd") } : {}),
1941
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
1974
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
1942
1975
  });
1943
1976
  print(proc.id, proc.info);
1944
1977
  return 0;
@@ -1948,12 +1981,13 @@ async function sandbox(argv, env, out) {
1948
1981
  const list = await sbx.processes.list();
1949
1982
  print(list.length
1950
1983
  ? table([
1951
- ["PROCESS", "STATE", "EXIT", "COMMAND"],
1984
+ ["PROCESS", "STATE", "EXIT", "USER", "COMMAND"],
1952
1985
  // The command line is the guest's report: escaped, as a log is.
1953
1986
  ...list.map((p) => [
1954
1987
  terminalSafe(String(p.id)),
1955
1988
  terminalSafe(String(p.state)),
1956
1989
  `${typeof p.exitCode === "number" ? p.exitCode : "-"}${p.outOfMemory ? " (out of memory)" : ""}`,
1990
+ p.user === "root" ? "root" : "sandbox",
1957
1991
  terminalSafe(String(p.command ?? "")),
1958
1992
  ]),
1959
1993
  ])
@@ -2021,9 +2055,9 @@ async function sandbox(argv, env, out) {
2021
2055
  return 0;
2022
2056
  }
2023
2057
  case "shell": {
2024
- const args = parse(rest, [], ["command"]);
2058
+ const args = parse(rest, ["root"], ["command"]);
2025
2059
  const sbx = await use(args.positional[0]);
2026
- return shell(sbx, flag(args, "command"));
2060
+ return shell(sbx, flag(args, "command"), has(args, "root"));
2027
2061
  }
2028
2062
  case "cat": {
2029
2063
  const sbx = await use(rest[0]);
@@ -2614,12 +2648,20 @@ const KEYS_HELP = `runtime keys <command>
2614
2648
  stdout, and lasts until revoked. --json prints it as JSON, still alone on
2615
2649
  stdout. --account-wide makes a key that sees and uses every sandbox and
2616
2650
  other resource in the account; without it, a key sees only what it makes.
2651
+ create --sandbox <id,...> --actions <action,...> [--name <name>] [--daily-limit <usd>]
2652
+ A key limited to those sandboxes (up to 20, by id) and those actions on
2653
+ them, made by the saved key: get reads them; create starts, wakes and
2654
+ restarts them, and needs --daily-limit when the making key has one; stop
2655
+ stops and pauses them; exec runs commands; read_file and write_file reach
2656
+ their files. It is served nothing else: no list, no other sandbox, no
2657
+ account route, no MCP.
2617
2658
  ls This key and the keys it made, with what each spent today
2618
2659
  usage <keyId> What one of them has spent and holds
2619
2660
  revoke <keyId> Revoke one: a worker's key when the worker is done
2620
2661
 
2621
2662
  runtime keys create --name worker-1 --daily-limit 5
2622
2663
  runtime keys create --name ci --daily-limit 25 --browser | gh secret set RUNTIME_API_KEY
2664
+ runtime keys create --name watcher --sandbox sbx_id --actions get,create --daily-limit 2
2623
2665
 
2624
2666
  Making, listing and revoking keys with a key needs one an owner or admin made.
2625
2667
  Every key is also at https://withruntime.com/account/keys.
@@ -2788,8 +2830,16 @@ async function keys(argv, env, out) {
2788
2830
  }
2789
2831
  if (verb !== "create")
2790
2832
  throw unknown("keys command", verb, ["create", "ls", "usage", "revoke"]);
2791
- const args = parse(rest, ["read-only", "account-wide", "browser"], ["name", "daily-limit"]);
2792
- const known = new Set(["name", "read-only", "daily-limit", "account-wide", "browser"]);
2833
+ const args = parse(rest, ["read-only", "account-wide", "browser"], ["name", "daily-limit", "sandbox", "actions"]);
2834
+ const known = new Set([
2835
+ "name",
2836
+ "read-only",
2837
+ "daily-limit",
2838
+ "account-wide",
2839
+ "browser",
2840
+ "sandbox",
2841
+ "actions",
2842
+ ]);
2793
2843
  for (const name of args.flags.keys())
2794
2844
  if (!known.has(name))
2795
2845
  throw usage(`Unknown option --${name}.`);
@@ -2797,6 +2847,8 @@ async function keys(argv, env, out) {
2797
2847
  throw usage(`Unexpected ${args.positional[0] ?? "--"}.`);
2798
2848
  const readOnly = has(args, "read-only");
2799
2849
  const limit = flag(args, "daily-limit");
2850
+ if (flag(args, "sandbox") !== undefined || flag(args, "actions") !== undefined)
2851
+ return limitedKey(args, env, out, limit === undefined ? null : limit);
2800
2852
  if (readOnly && limit !== undefined)
2801
2853
  throw usage("A read-only key cannot spend, so it takes no --daily-limit.");
2802
2854
  if (readOnly && has(args, "account-wide"))
@@ -2846,6 +2898,32 @@ async function keys(argv, env, out) {
2846
2898
  await confirm?.();
2847
2899
  return 0;
2848
2900
  }
2901
+ /** `runtime keys create --sandbox ... --actions ...`: a key limited to
2902
+ * sandboxes, made by the saved key (never in the browser: the keys page
2903
+ * makes none). */
2904
+ async function limitedKey(args, env, out, limit) {
2905
+ for (const refused of ["read-only", "account-wide", "browser"])
2906
+ if (has(args, refused))
2907
+ throw usage(`A key limited to sandboxes reaches only them and is made by this machine's key, so it takes no --${refused}.`);
2908
+ const list = (name) => (flag(args, name) ?? "")
2909
+ .split(",")
2910
+ .map((each) => each.trim())
2911
+ .filter(Boolean);
2912
+ const sandboxes = list("sandbox");
2913
+ const actions = list("actions");
2914
+ if (!sandboxes.length || !actions.length)
2915
+ throw usage(`Usage: ${named("runtime keys create --sandbox <id,...> --actions <get,create,stop,exec,read_file,write_file> [--daily-limit <usd>]")}`);
2916
+ const { dailyLimitMicros } = await import("./keys.js");
2917
+ const made = await (await client(env)).keys.create({
2918
+ name: flag(args, "name") ?? "Sandbox key",
2919
+ sandboxes,
2920
+ actions: actions,
2921
+ dailyLimitMicros: limit === null ? null : dailyLimitMicros(limit),
2922
+ });
2923
+ out.write(out.json ? JSON.stringify(made) : made.key);
2924
+ out.error(`Created key "${made.name}", limited to ${made.sandboxes?.length === 1 ? "sandbox" : "sandboxes"} ${(made.sandboxes ?? []).join(", ")} for ${(made.actions ?? []).join(", ")}${made.dailyLimitMicros === null ? "" : `, daily limit ${usdOf(made.dailyLimitMicros)}`}, made by this machine's key. Store it now: it is not shown again.\nIt calls those sandboxes by id and nothing else; revoke it with ${named("runtime keys revoke")} ${made.keyId}.`);
2925
+ return 0;
2926
+ }
2849
2927
  const SNAPSHOT_HELP = `runtime snapshots <command>
2850
2928
 
2851
2929
  ls [--sandbox <id>] [--name n] Your snapshots
@@ -3743,7 +3821,7 @@ function usdMicros(args, name) {
3743
3821
  return Number(micros);
3744
3822
  }
3745
3823
  /** A create body from the command line's flags. */
3746
- function createInput(args) {
3824
+ function createInput(args, env) {
3747
3825
  for (const name of ["image", "snapshot", "volume", "name", "region", "connect"])
3748
3826
  if (args.flags.get(name)?.some((value) => !value.trim()))
3749
3827
  throw usage(`--${name} needs a non-empty value.`);
@@ -3774,7 +3852,7 @@ function createInput(args) {
3774
3852
  : {}),
3775
3853
  ...(flag(args, "name") ? { name: flag(args, "name") } : {}),
3776
3854
  ...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
3777
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
3855
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
3778
3856
  ...(integer(args, "vcpu") !== undefined ? { vcpu: integer(args, "vcpu") } : {}),
3779
3857
  ...(integer(args, "memory") !== undefined ? { memoryMiB: integer(args, "memory") } : {}),
3780
3858
  ...(integer(args, "disk") !== undefined ? { diskMiB: integer(args, "disk") } : {}),
@@ -3811,7 +3889,7 @@ function createInput(args) {
3811
3889
  }
3812
3890
  /** Runs a command in a sandbox, streaming its output, and answers its exit code
3813
3891
  * (124 on a timeout), so a script's `set -e` behaves. */
3814
- async function execute(sbx, command, args, out, timeout,
3892
+ async function execute(sbx, command, args, out, timeout, env,
3815
3893
  /** `run` stops its whole sandbox on Ctrl-C, so it handles the signal itself. */
3816
3894
  ownInterrupt = true) {
3817
3895
  const target = command.length === 1 ? command[0] : command;
@@ -3820,7 +3898,8 @@ ownInterrupt = true) {
3820
3898
  const options = {
3821
3899
  ...(stdin ? { stdin } : {}),
3822
3900
  ...(flag(args, "cwd") ? { cwd: flag(args, "cwd") } : {}),
3823
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
3901
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
3902
+ ...(has(args, "root") ? { user: "root" } : {}),
3824
3903
  ...(timeout !== undefined ? { timeoutMs: timeout * 1000 } : {}),
3825
3904
  };
3826
3905
  // Ctrl-C stops the command in the sandbox too, and says so; without this
@@ -3869,12 +3948,13 @@ ownInterrupt = true) {
3869
3948
  const code = result.timedOut ? 124 : Math.min(255, exit < 0 ? 128 - exit : exit);
3870
3949
  return lost && code === 0 ? 1 : code;
3871
3950
  }
3872
- async function shell(sbx, command) {
3951
+ async function shell(sbx, command, root = false) {
3873
3952
  const stdin = process.stdin;
3874
3953
  const size = () => ({ cols: process.stdout.columns || 80, rows: process.stdout.rows || 24 });
3875
3954
  const terminal = await sbx.terminal({
3876
3955
  ...size(),
3877
3956
  ...(command ? { command } : {}),
3957
+ ...(root ? { user: "root" } : {}),
3878
3958
  onData: (data) => process.stdout.write(data),
3879
3959
  });
3880
3960
  if (stdin.isTTY)
@@ -19,6 +19,67 @@ export declare const SandboxState: {
19
19
  readonly DESTROYED: "destroyed";
20
20
  };
21
21
  export type SandboxState = (typeof SandboxState)[keyof typeof SandboxState];
22
+ export declare const GpuType: {
23
+ readonly H100: "H100";
24
+ readonly H200: "H200";
25
+ readonly B200: "B200";
26
+ readonly B300: "B300";
27
+ readonly RTX_PRO_6000: "RTX-PRO-6000";
28
+ readonly RTX_4090: "RTX-4090";
29
+ readonly RTX_5090: "RTX-5090";
30
+ readonly MI355X: "MI355X";
31
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
32
+ };
33
+ export type GpuType = (typeof GpuType)[keyof typeof GpuType];
34
+ export declare const SnapshotState: {
35
+ readonly BUILDING: "building";
36
+ readonly PENDING: "pending";
37
+ readonly PULLING: "pulling";
38
+ readonly SNAPSHOTTING: "snapshotting";
39
+ readonly ACTIVE: "active";
40
+ readonly INACTIVE: "inactive";
41
+ readonly ERROR: "error";
42
+ readonly BUILD_FAILED: "build_failed";
43
+ readonly REMOVING: "removing";
44
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
45
+ };
46
+ export type SnapshotState = (typeof SnapshotState)[keyof typeof SnapshotState];
47
+ export declare const SandboxListSortField: {
48
+ readonly NAME: "name";
49
+ readonly CPU: "cpu";
50
+ readonly MEMORY_GIB: "memoryGib";
51
+ readonly DISK_GIB: "diskGib";
52
+ readonly LAST_ACTIVITY_AT: "lastActivityAt";
53
+ readonly CREATED_AT: "createdAt";
54
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
55
+ };
56
+ export type SandboxListSortField = (typeof SandboxListSortField)[keyof typeof SandboxListSortField];
57
+ export declare const SandboxListSortDirection: {
58
+ readonly ASC: "asc";
59
+ readonly DESC: "desc";
60
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
61
+ };
62
+ export type SandboxListSortDirection = (typeof SandboxListSortDirection)[keyof typeof SandboxListSortDirection];
63
+ export declare const SandboxClass: {
64
+ readonly LINUX_VM: "linux-vm";
65
+ readonly CONTAINER: "container";
66
+ readonly WINDOWS: "windows";
67
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
68
+ };
69
+ export type SandboxClass = (typeof SandboxClass)[keyof typeof SandboxClass];
70
+ export declare const ListSecretsPaginatedSortEnum: {
71
+ readonly NAME: "name";
72
+ readonly CREATED_AT: "createdAt";
73
+ readonly UPDATED_AT: "updatedAt";
74
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
75
+ };
76
+ export type ListSecretsPaginatedSortEnum = (typeof ListSecretsPaginatedSortEnum)[keyof typeof ListSecretsPaginatedSortEnum];
77
+ export declare const ListSecretsPaginatedOrderEnum: {
78
+ readonly ASC: "asc";
79
+ readonly DESC: "desc";
80
+ readonly UNKNOWN_DEFAULT_OPEN_API: "11184809";
81
+ };
82
+ export type ListSecretsPaginatedOrderEnum = (typeof ListSecretsPaginatedOrderEnum)[keyof typeof ListSecretsPaginatedOrderEnum];
22
83
  export declare const LspLanguageId: {
23
84
  (...args: unknown[]): never;
24
85
  new (...args: unknown[]): never;
@@ -26,6 +26,63 @@ export const SandboxState = {
26
26
  DESTROYING: "destroying",
27
27
  DESTROYED: "destroyed",
28
28
  };
29
+ /* Daytona's other enums, re-exported by @daytona/sdk from its API client, so
30
+ code importing them loads. A GPU type on create is refused (Runtime runs
31
+ sandboxes on CPUs); the sort field and direction drive daytona.list. */
32
+ export const GpuType = {
33
+ H100: "H100",
34
+ H200: "H200",
35
+ B200: "B200",
36
+ B300: "B300",
37
+ RTX_PRO_6000: "RTX-PRO-6000",
38
+ RTX_4090: "RTX-4090",
39
+ RTX_5090: "RTX-5090",
40
+ MI355X: "MI355X",
41
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
42
+ };
43
+ export const SnapshotState = {
44
+ BUILDING: "building",
45
+ PENDING: "pending",
46
+ PULLING: "pulling",
47
+ SNAPSHOTTING: "snapshotting",
48
+ ACTIVE: "active",
49
+ INACTIVE: "inactive",
50
+ ERROR: "error",
51
+ BUILD_FAILED: "build_failed",
52
+ REMOVING: "removing",
53
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
54
+ };
55
+ export const SandboxListSortField = {
56
+ NAME: "name",
57
+ CPU: "cpu",
58
+ MEMORY_GIB: "memoryGib",
59
+ DISK_GIB: "diskGib",
60
+ LAST_ACTIVITY_AT: "lastActivityAt",
61
+ CREATED_AT: "createdAt",
62
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
63
+ };
64
+ export const SandboxListSortDirection = {
65
+ ASC: "asc",
66
+ DESC: "desc",
67
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
68
+ };
69
+ export const SandboxClass = {
70
+ LINUX_VM: "linux-vm",
71
+ CONTAINER: "container",
72
+ WINDOWS: "windows",
73
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
74
+ };
75
+ export const ListSecretsPaginatedSortEnum = {
76
+ NAME: "name",
77
+ CREATED_AT: "createdAt",
78
+ UPDATED_AT: "updatedAt",
79
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
80
+ };
81
+ export const ListSecretsPaginatedOrderEnum = {
82
+ ASC: "asc",
83
+ DESC: "desc",
84
+ UNKNOWN_DEFAULT_OPEN_API: "11184809",
85
+ };
29
86
  /** A stand-in for a Daytona export Runtime has no counterpart for. Importing
30
87
  * it works; using it throws NotSupportedError naming the alternative. */
31
88
  function unsupportedExport(name, alternative) {
package/dist/index.d.ts CHANGED
@@ -72,7 +72,7 @@ export declare class Sandbox extends SandboxClass {
72
72
  }
73
73
  export type { RuntimeOptions as ClientOptions };
74
74
  export { identityToken, type IdentityToken } from "./identity.js";
75
- export { openWalletAccount, claimWalletAccount, type OpenedWalletAccount, type WalletClaim, type WalletOptions, } from "./wallet.js";
75
+ export { openWalletAccount, claimWalletAccount, rekeyWalletAccount, type OpenedWalletAccount, type WalletClaim, type WalletRekey, type WalletOptions, } from "./wallet.js";
76
76
  export type { Topup } from "./products/billing.js";
77
- export type { NewKey, MadeKey, ListedKey, KeyUsage } from "./products/api-keys.js";
77
+ export type { NewKey, LimitedKey, LimitedKeyAction, MadeKey, ListedKey, KeyUsage, } from "./products/api-keys.js";
78
78
  export type { SsoConnection, SsoStatus } from "./products/sso.js";
package/dist/index.js CHANGED
@@ -47,4 +47,4 @@ export class Sandbox extends SandboxClass {
47
47
  }
48
48
  }
49
49
  export { identityToken } from "./identity.js";
50
- export { openWalletAccount, claimWalletAccount, } from "./wallet.js";
50
+ export { openWalletAccount, claimWalletAccount, rekeyWalletAccount, } from "./wallet.js";
@@ -1,5 +1,11 @@
1
1
  import type { RequestOptions, Transport } from "../transport.js";
2
- export type NewKey = {
2
+ /** What a key limited to sandboxes may do to them. get reads them; create
3
+ * starts, wakes and restarts them (never makes one), and is the one that
4
+ * spends; stop stops and pauses them; exec runs commands and processes;
5
+ * read_file and write_file reach their files. */
6
+ export type LimitedKeyAction = "get" | "create" | "stop" | "exec" | "read_file" | "write_file";
7
+ export type NewKey = FullOrReadKey | LimitedKey;
8
+ type FullOrReadKey = {
3
9
  /** The key's name, and its agent's: up to 80 characters. */
4
10
  name: string;
5
11
  /** full (the default): every action. read: every read, nothing else. */
@@ -11,6 +17,19 @@ export type NewKey = {
11
17
  * when the making key has a limit: the limits of the keys a key made add
12
18
  * up to at most its own. */
13
19
  dailyLimitMicros?: number | null;
20
+ sandboxes?: never;
21
+ actions?: never;
22
+ };
23
+ /** A key limited to these sandboxes and these actions on them. */
24
+ export type LimitedKey = {
25
+ name: string;
26
+ /** 1 to 20 sandbox ids this key acts on, outside every compartment. */
27
+ sandboxes: string[];
28
+ actions: LimitedKeyAction[];
29
+ /** Only with create, the one action that spends. */
30
+ dailyLimitMicros?: number | null;
31
+ access?: never;
32
+ reach?: never;
14
33
  };
15
34
  export type MadeKey = {
16
35
  /** The key itself. Shown once: store it now. */
@@ -19,9 +38,15 @@ export type MadeKey = {
19
38
  agentId: string;
20
39
  orgId: string;
21
40
  name: string;
22
- access: "full" | "read";
23
- reach: "agent" | "account";
41
+ /** selected: a key limited to sandboxes. */
42
+ access: "full" | "read" | "selected";
43
+ /** sandboxes: only the sandboxes it names. */
44
+ reach: "agent" | "account" | "sandboxes";
24
45
  dailyLimitMicros: number | null;
46
+ /** The sandboxes it is limited to, or null. */
47
+ sandboxes: string[] | null;
48
+ /** Its actions, when it has named ones, or null. */
49
+ actions: string[] | null;
25
50
  /** The making key's expiry, or null for until revoked. */
26
51
  expires: string | null;
27
52
  /** The id of the key that made it. */
@@ -32,8 +57,10 @@ export type ListedKey = {
32
57
  agentId: string;
33
58
  name: string;
34
59
  access: "full" | "read" | "selected";
35
- reach: "agent" | "account";
60
+ reach: "agent" | "account" | "sandboxes";
36
61
  dailyLimitMicros: number | null;
62
+ sandboxes: string[] | null;
63
+ actions: string[] | null;
37
64
  /** Committed in the last 24 hours: settled plus held, as the limit counts. */
38
65
  dailyUsedMicros: number;
39
66
  /** The key that made it, or null for one made on the website. */
@@ -71,3 +98,4 @@ export declare function keys(t: Transport): {
71
98
  /** What one key spent: itself, or a key it made. */
72
99
  usage: (keyId: string, options?: RequestOptions) => Promise<KeyUsage>;
73
100
  };
101
+ export {};
@@ -82,6 +82,8 @@ export type AppDeploy = {
82
82
  lastOutput: string;
83
83
  };
84
84
  });
85
+ /** `image`: an image of this account (id, name, name:tag or name@version),
86
+ * ready; the version records its id. */
85
87
  export type DeployApp = ({
86
88
  image: string;
87
89
  build?: never;
@@ -90,6 +92,14 @@ export type DeployApp = ({
90
92
  image?: never;
91
93
  }) & {
92
94
  env?: Record<string, string>;
95
+ /** Carry the last deploy's environment, still sealed. With env, that
96
+ * environment with env's names set: change one value without sending the rest
97
+ * (needs a key that may reveal the App's secrets, secrets_reveal; else 403).
98
+ * For a build: only one a push to the production branch made, from the
99
+ * repository the environment came with. For an image: only from a key that
100
+ * may reveal the App's secrets, and only an environment named with an image
101
+ * deploy. Otherwise refused with keep_env_refused (409). */
102
+ keepEnv?: boolean;
93
103
  port?: number;
94
104
  healthPath?: string;
95
105
  /** Return live, failed or superseded, including its URL/version or failure details. */
@@ -100,6 +110,10 @@ export type AppVersion = {
100
110
  live: boolean;
101
111
  previous: boolean;
102
112
  saved: boolean;
113
+ /** Its deploy's id, for GET /v1/apps/{id}/deploys/{deploy}. */
114
+ deploy: string;
115
+ /** The build it was deployed from, or null for an image. */
116
+ build: string | null;
103
117
  };
104
118
  export type AppCopy = {
105
119
  id: string;
@@ -37,8 +37,10 @@ export type BrowserStartOptions = {
37
37
  *
38
38
  * Inside the sandbox the same browser is at http://127.0.0.1:9222. */
39
39
  export declare function sandboxBrowser(t: Transport, sandbox: Sandbox): {
40
- /** Starts Chromium and returns its CDP address. The first start in a
41
- * sandbox installs Chromium, about a minute; this waits for it (up to 10
40
+ /** Starts Chromium and returns its CDP address. A start in a new
41
+ * sandbox takes about 1 to 2 seconds: Chromium comes from the host. A
42
+ * sandbox from a custom image that is not Ubuntu 24.04 x86_64 installs it
43
+ * on the first start, about a minute; this waits for it (up to 10
42
44
  * minutes, or the call's timeoutMs). Starting a running browser returns it
43
45
  * as it is. */
44
46
  start(input?: BrowserStartOptions, options?: RequestOptions): Promise<{
@@ -8,8 +8,10 @@ import { whileInstalling } from "../wait.js";
8
8
  export function sandboxBrowser(t, sandbox) {
9
9
  const base = () => `/v1/sandboxes/${encodeURIComponent(sandbox.id)}/browser`;
10
10
  return {
11
- /** Starts Chromium and returns its CDP address. The first start in a
12
- * sandbox installs Chromium, about a minute; this waits for it (up to 10
11
+ /** Starts Chromium and returns its CDP address. A start in a new
12
+ * sandbox takes about 1 to 2 seconds: Chromium comes from the host. A
13
+ * sandbox from a custom image that is not Ubuntu 24.04 x86_64 installs it
14
+ * on the first start, about a minute; this waits for it (up to 10
13
15
  * minutes, or the call's timeoutMs). Starting a running browser returns it
14
16
  * as it is. */
15
17
  async start(input = {}, options) {
@@ -22,6 +22,24 @@ export type KeyLimits = {
22
22
  * numbers a create past one is refused `quota_exceeded` by (its
23
23
  * `details.limit` names the row). Null when they can't be read. */
24
24
  account: AccountLimit[] | null;
25
+ /** The account's prepaid credit and how long it lasts. */
26
+ credit: {
27
+ /** Credit left to spend now, in microdollars. */
28
+ availableMicros: string;
29
+ /** What the account spends in a day at this month's pace, or null when
30
+ * nothing has been spent yet. */
31
+ dailyPaceMicros: string | null;
32
+ /** Days the credit lasts at that pace, or null when nothing is being spent. */
33
+ runwayDays: number | null;
34
+ /** `state` is "low" when the runway is under this many days. */
35
+ lowBelowDays: number;
36
+ /** ok: enough. low: under `lowBelowDays` of runway. empty: none left. */
37
+ state: "ok" | "low" | "empty";
38
+ /** owner: a person pays for this account. agent: it was opened with a
39
+ * wallet and no person pays, so its own agent adds credit with
40
+ * `runtime billing topup <usd>`. */
41
+ addedBy: "owner" | "agent";
42
+ };
25
43
  };
26
44
  /** One of the account's limits, as https://withruntime.com/account/limits shows it. */
27
45
  export type AccountLimit = {
@@ -35,7 +35,7 @@ export type SandboxMetrics = {
35
35
  latest: MetricPoint | null;
36
36
  points: MetricPoint[];
37
37
  };
38
- export type WebhookEventType = "sandbox.created" | "sandbox.running" | "sandbox.paused" | "sandbox.woken" | "sandbox.stopped" | "sandbox.start_failed" | "sandbox.wake_failed" | "sandbox.host_unreachable" | "sandbox.host_reachable" | "sandbox.recovered" | "sandbox.recovery_failed" | "sandbox.files.changed" | "sandbox.out_of_memory" | "sandbox.secret_not_applied" | "snapshot.ready" | "snapshot.failed" | "snapshot.deleted" | "volume.ready" | "volume.failed" | "volume.deleted" | "volume.recovered" | "egress_secret.created" | "egress_secret.updated" | "egress_secret.deleted" | "upstream_proxy.set" | "upstream_proxy.removed";
38
+ export type WebhookEventType = "sandbox.created" | "sandbox.running" | "sandbox.paused" | "sandbox.woken" | "sandbox.stopped" | "sandbox.start_failed" | "sandbox.wake_failed" | "sandbox.host_unreachable" | "sandbox.host_reachable" | "sandbox.recovered" | "sandbox.recovery_failed" | "sandbox.files.changed" | "sandbox.out_of_memory" | "sandbox.secret_not_applied" | "snapshot.ready" | "snapshot.failed" | "snapshot.deleted" | "volume.ready" | "volume.failed" | "volume.deleted" | "volume.recovered" | "egress_secret.created" | "egress_secret.updated" | "egress_secret.deleted" | "upstream_proxy.set" | "upstream_proxy.removed" | "credit.low" | "credit.empty";
39
39
  /** An event as /v1/events lists it and a webhook delivers it. */
40
40
  export type RuntimeEvent = {
41
41
  id: string;
package/dist/sandbox.d.ts CHANGED
@@ -73,6 +73,8 @@ export declare class Sandbox extends Machine<SandboxInfo, SandboxSettings, Delet
73
73
  rows?: number;
74
74
  command?: string;
75
75
  cwd?: string;
76
+ /** "root" opens the shell (or runs `command`) as root. */
77
+ user?: "sandbox" | "root";
76
78
  /** Attach to a process started with a pty instead of a new shell. */
77
79
  processId?: string;
78
80
  onData?: (data: Uint8Array) => void;
package/dist/sandbox.js CHANGED
@@ -79,6 +79,7 @@ function commandBody(command, options) {
79
79
  ...(typeof command === "string" ? { command } : { argv: [...command] }),
80
80
  ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
81
81
  ...(options.env === undefined ? {} : { env: options.env }),
82
+ ...(options.user === undefined ? {} : { user: options.user }),
82
83
  ...(options.stdin === undefined
83
84
  ? {}
84
85
  : typeof options.stdin === "string"
@@ -314,6 +315,7 @@ export class Sandbox extends Machine {
314
315
  rows: options.rows,
315
316
  command: options.command,
316
317
  cwd: options.cwd,
318
+ user: options.user,
317
319
  processId: options.processId,
318
320
  });
319
321
  return Terminal.open(socket, options.onData);
@@ -1,5 +1,5 @@
1
1
  import { RuntimeError } from "./errors.js";
2
- export declare const VERSION = "0.16.1";
2
+ export declare const VERSION = "0.17.2";
3
3
  export declare const DEFAULT_BASE_URL = "https://api.withruntime.com";
4
4
  /** Runtime's API as code inside a Runtime sandbox reaches it: the sandbox's
5
5
  * own host sends each request on to DEFAULT_BASE_URL over HTTPS. The API runs
package/dist/transport.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ConnectionError, DELIBERATE, errorFor, NOTHING_RAN, RuntimeError, WAITS_FOR_ROOM, } from "./errors.js";
2
2
  import { describeRoute, envFetch, openWebSocket } from "./proxy.js";
3
3
  import { warningText } from "./product-renames.js";
4
- export const VERSION = "0.16.1";
4
+ export const VERSION = "0.17.2";
5
5
  export const DEFAULT_BASE_URL = "https://api.withruntime.com";
6
6
  /** Runtime's API as code inside a Runtime sandbox reaches it: the sandbox's
7
7
  * own host sends each request on to DEFAULT_BASE_URL over HTTPS. The API runs
package/dist/types.d.ts CHANGED
@@ -200,6 +200,10 @@ export type CommandResult = {
200
200
  };
201
201
  export type ExecOptions = {
202
202
  cwd?: string;
203
+ /** Who runs it: "sandbox" (the default, uid 1000, HOME=/workspace) or
204
+ * "root", through the sandbox's passwordless sudo with the same
205
+ * environment and PATH and HOME=/root: no need to prefix it with sudo. */
206
+ user?: "sandbox" | "root";
203
207
  /** Merged over the sandbox's environment. Put secrets here, never in the command. */
204
208
  env?: Record<string, string>;
205
209
  /** Given to standard input, then closed. */
@@ -220,6 +224,8 @@ export type ProcessInfo = {
220
224
  state: "running" | "exited" | "killed" | "timed_out" | "unknown";
221
225
  exitCode: number | null;
222
226
  command: string;
227
+ /** Who it runs as; absent from APIs before 17 October 2026. */
228
+ user?: "sandbox" | "root";
223
229
  cwd: string;
224
230
  pty: boolean;
225
231
  stdinOpen: boolean;
package/dist/wallet.d.ts CHANGED
@@ -20,6 +20,14 @@ export type WalletClaim = {
20
20
  /** What to do now, such as sending the top-up again when it has no address. */
21
21
  next?: string;
22
22
  };
23
+ export type WalletRekey = {
24
+ status: "replaced";
25
+ orgId: string;
26
+ /** The new key, shown once. The old one no longer works. */
27
+ apiKey: string;
28
+ /** The id of the key this replaced, or null when there was none to revoke. */
29
+ revokedKeyId: string | null;
30
+ };
23
31
  /** Opens an account and a stablecoin top-up of `usd` dollars ($10 to $500,
24
32
  * a new account's first week).
25
33
  * `acceptTerms` must be true: opening one means agreeing to
@@ -31,3 +39,7 @@ export declare function openWalletAccount(input: {
31
39
  /** Exchanges a paid account's claim code for its API key, once. Until the
32
40
  * payment arrives it answers the top-up's status instead; call it again. */
33
41
  export declare function claimWalletAccount(claimCode: string, options?: WalletOptions): Promise<WalletClaim>;
42
+ /** Replaces a lost API key with a new one, using the account's claim code. The
43
+ * account's every other key stops working. Refused (409) when the first key was never claimed or
44
+ * a person took the account over, and (429) past 3 times in 24 hours. */
45
+ export declare function rekeyWalletAccount(claimCode: string, options?: WalletOptions): Promise<WalletRekey>;
package/dist/wallet.js CHANGED
@@ -47,3 +47,10 @@ export async function claimWalletAccount(claimCode, options = {}) {
47
47
  const [, claim] = await post(options, "/api/wallet/claim", { claimCode });
48
48
  return claim;
49
49
  }
50
+ /** Replaces a lost API key with a new one, using the account's claim code. The
51
+ * account's every other key stops working. Refused (409) when the first key was never claimed or
52
+ * a person took the account over, and (429) past 3 times in 24 hours. */
53
+ export async function rekeyWalletAccount(claimCode, options = {}) {
54
+ const [, rekey] = await post(options, "/api/wallet/rekey", { claimCode });
55
+ return rekey;
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.16.1",
3
+ "version": "0.17.2",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",