withruntime 0.16.1 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 {
@@ -1655,7 +1672,7 @@ async function sandbox(argv, env, out) {
1655
1672
  switch (verb) {
1656
1673
  case "create": {
1657
1674
  const args = parse(rest, CREATE_FLAGS, CREATE_VALUES);
1658
- const input = createInput(args);
1675
+ const input = createInput(args, env);
1659
1676
  const onCapacityWait = waiting(out);
1660
1677
  const sbx = await withVolumeRelease((options) => runtime.sandboxes.create(input, { ...options, onCapacityWait }), out);
1661
1678
  print(sbx.id, sbx.info);
@@ -1883,14 +1900,14 @@ async function sandbox(argv, env, out) {
1883
1900
  const cost = flag(args, "max-total-cost");
1884
1901
  if (cost !== undefined && cost !== "none" && !/^\d+(\.\d{1,6})?$/.test(cost))
1885
1902
  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") : {}),
1903
+ const variables = {
1904
+ ...(args.flags.has("env") ? envPairs(args, env) : {}),
1888
1905
  ...Object.fromEntries((args.flags.get("unset-env") ?? []).map((name) => [name, null])),
1889
1906
  };
1890
1907
  const settings = {
1891
1908
  ...(flag(args, "name") ? { name: flag(args, "name") } : {}),
1892
1909
  ...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
1893
- ...(Object.keys(env).length ? { env } : {}),
1910
+ ...(Object.keys(variables).length ? { env: variables } : {}),
1894
1911
  ...(integer(args, "idle-pause") !== undefined
1895
1912
  ? { idlePauseSeconds: integer(args, "idle-pause") }
1896
1913
  : {}),
@@ -1922,23 +1939,24 @@ async function sandbox(argv, env, out) {
1922
1939
  return 0;
1923
1940
  }
1924
1941
  case "exec": {
1925
- const args = parse(rest, [], ["cwd", "env", "timeout"], `${me} sandboxes exec <id> -- ls -la`);
1942
+ const args = parse(rest, ["root"], ["cwd", "env", "timeout"], `${me} sandboxes exec <id> -- ls -la`);
1926
1943
  const command = args.rest ?? args.positional.slice(1);
1927
1944
  if (!command.length)
1928
1945
  throw usage("Give the command after --: runtime sandboxes exec <id> -- ls -la");
1929
1946
  const sbx = await use(args.positional[0]);
1930
- return execute(sbx, command, args, out, integer(args, "timeout"));
1947
+ return execute(sbx, command, args, out, integer(args, "timeout"), env);
1931
1948
  }
1932
1949
  case "spawn": {
1933
- const args = parse(rest, ["pty"], ["cwd", "env"], `${me} sandboxes spawn <id> -- npm run dev`);
1950
+ const args = parse(rest, ["pty", "root"], ["cwd", "env"], `${me} sandboxes spawn <id> -- npm run dev`);
1934
1951
  const command = args.rest ?? [];
1935
1952
  if (!command.length)
1936
1953
  throw usage("Give the command after --: runtime sandboxes spawn <id> -- npm run dev");
1937
1954
  const sbx = await use(args.positional[0]);
1938
1955
  const proc = await sbx.spawn(command.length === 1 ? command[0] : command, {
1939
1956
  ...(has(args, "pty") ? { pty: { cols: 120, rows: 40 } } : {}),
1957
+ ...(has(args, "root") ? { user: "root" } : {}),
1940
1958
  ...(flag(args, "cwd") ? { cwd: flag(args, "cwd") } : {}),
1941
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
1959
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
1942
1960
  });
1943
1961
  print(proc.id, proc.info);
1944
1962
  return 0;
@@ -1948,12 +1966,13 @@ async function sandbox(argv, env, out) {
1948
1966
  const list = await sbx.processes.list();
1949
1967
  print(list.length
1950
1968
  ? table([
1951
- ["PROCESS", "STATE", "EXIT", "COMMAND"],
1969
+ ["PROCESS", "STATE", "EXIT", "USER", "COMMAND"],
1952
1970
  // The command line is the guest's report: escaped, as a log is.
1953
1971
  ...list.map((p) => [
1954
1972
  terminalSafe(String(p.id)),
1955
1973
  terminalSafe(String(p.state)),
1956
1974
  `${typeof p.exitCode === "number" ? p.exitCode : "-"}${p.outOfMemory ? " (out of memory)" : ""}`,
1975
+ p.user === "root" ? "root" : "sandbox",
1957
1976
  terminalSafe(String(p.command ?? "")),
1958
1977
  ]),
1959
1978
  ])
@@ -2021,9 +2040,9 @@ async function sandbox(argv, env, out) {
2021
2040
  return 0;
2022
2041
  }
2023
2042
  case "shell": {
2024
- const args = parse(rest, [], ["command"]);
2043
+ const args = parse(rest, ["root"], ["command"]);
2025
2044
  const sbx = await use(args.positional[0]);
2026
- return shell(sbx, flag(args, "command"));
2045
+ return shell(sbx, flag(args, "command"), has(args, "root"));
2027
2046
  }
2028
2047
  case "cat": {
2029
2048
  const sbx = await use(rest[0]);
@@ -2614,12 +2633,20 @@ const KEYS_HELP = `runtime keys <command>
2614
2633
  stdout, and lasts until revoked. --json prints it as JSON, still alone on
2615
2634
  stdout. --account-wide makes a key that sees and uses every sandbox and
2616
2635
  other resource in the account; without it, a key sees only what it makes.
2636
+ create --sandbox <id,...> --actions <action,...> [--name <name>] [--daily-limit <usd>]
2637
+ A key limited to those sandboxes (up to 20, by id) and those actions on
2638
+ them, made by the saved key: get reads them; create starts, wakes and
2639
+ restarts them, and needs --daily-limit when the making key has one; stop
2640
+ stops and pauses them; exec runs commands; read_file and write_file reach
2641
+ their files. It is served nothing else: no list, no other sandbox, no
2642
+ account route, no MCP.
2617
2643
  ls This key and the keys it made, with what each spent today
2618
2644
  usage <keyId> What one of them has spent and holds
2619
2645
  revoke <keyId> Revoke one: a worker's key when the worker is done
2620
2646
 
2621
2647
  runtime keys create --name worker-1 --daily-limit 5
2622
2648
  runtime keys create --name ci --daily-limit 25 --browser | gh secret set RUNTIME_API_KEY
2649
+ runtime keys create --name watcher --sandbox sbx_id --actions get,create --daily-limit 2
2623
2650
 
2624
2651
  Making, listing and revoking keys with a key needs one an owner or admin made.
2625
2652
  Every key is also at https://withruntime.com/account/keys.
@@ -2788,8 +2815,16 @@ async function keys(argv, env, out) {
2788
2815
  }
2789
2816
  if (verb !== "create")
2790
2817
  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"]);
2818
+ const args = parse(rest, ["read-only", "account-wide", "browser"], ["name", "daily-limit", "sandbox", "actions"]);
2819
+ const known = new Set([
2820
+ "name",
2821
+ "read-only",
2822
+ "daily-limit",
2823
+ "account-wide",
2824
+ "browser",
2825
+ "sandbox",
2826
+ "actions",
2827
+ ]);
2793
2828
  for (const name of args.flags.keys())
2794
2829
  if (!known.has(name))
2795
2830
  throw usage(`Unknown option --${name}.`);
@@ -2797,6 +2832,8 @@ async function keys(argv, env, out) {
2797
2832
  throw usage(`Unexpected ${args.positional[0] ?? "--"}.`);
2798
2833
  const readOnly = has(args, "read-only");
2799
2834
  const limit = flag(args, "daily-limit");
2835
+ if (flag(args, "sandbox") !== undefined || flag(args, "actions") !== undefined)
2836
+ return limitedKey(args, env, out, limit === undefined ? null : limit);
2800
2837
  if (readOnly && limit !== undefined)
2801
2838
  throw usage("A read-only key cannot spend, so it takes no --daily-limit.");
2802
2839
  if (readOnly && has(args, "account-wide"))
@@ -2846,6 +2883,32 @@ async function keys(argv, env, out) {
2846
2883
  await confirm?.();
2847
2884
  return 0;
2848
2885
  }
2886
+ /** `runtime keys create --sandbox ... --actions ...`: a key limited to
2887
+ * sandboxes, made by the saved key (never in the browser: the keys page
2888
+ * makes none). */
2889
+ async function limitedKey(args, env, out, limit) {
2890
+ for (const refused of ["read-only", "account-wide", "browser"])
2891
+ if (has(args, refused))
2892
+ throw usage(`A key limited to sandboxes reaches only them and is made by this machine's key, so it takes no --${refused}.`);
2893
+ const list = (name) => (flag(args, name) ?? "")
2894
+ .split(",")
2895
+ .map((each) => each.trim())
2896
+ .filter(Boolean);
2897
+ const sandboxes = list("sandbox");
2898
+ const actions = list("actions");
2899
+ if (!sandboxes.length || !actions.length)
2900
+ throw usage(`Usage: ${named("runtime keys create --sandbox <id,...> --actions <get,create,stop,exec,read_file,write_file> [--daily-limit <usd>]")}`);
2901
+ const { dailyLimitMicros } = await import("./keys.js");
2902
+ const made = await (await client(env)).keys.create({
2903
+ name: flag(args, "name") ?? "Sandbox key",
2904
+ sandboxes,
2905
+ actions: actions,
2906
+ dailyLimitMicros: limit === null ? null : dailyLimitMicros(limit),
2907
+ });
2908
+ out.write(out.json ? JSON.stringify(made) : made.key);
2909
+ 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}.`);
2910
+ return 0;
2911
+ }
2849
2912
  const SNAPSHOT_HELP = `runtime snapshots <command>
2850
2913
 
2851
2914
  ls [--sandbox <id>] [--name n] Your snapshots
@@ -3743,7 +3806,7 @@ function usdMicros(args, name) {
3743
3806
  return Number(micros);
3744
3807
  }
3745
3808
  /** A create body from the command line's flags. */
3746
- function createInput(args) {
3809
+ function createInput(args, env) {
3747
3810
  for (const name of ["image", "snapshot", "volume", "name", "region", "connect"])
3748
3811
  if (args.flags.get(name)?.some((value) => !value.trim()))
3749
3812
  throw usage(`--${name} needs a non-empty value.`);
@@ -3774,7 +3837,7 @@ function createInput(args) {
3774
3837
  : {}),
3775
3838
  ...(flag(args, "name") ? { name: flag(args, "name") } : {}),
3776
3839
  ...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
3777
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
3840
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
3778
3841
  ...(integer(args, "vcpu") !== undefined ? { vcpu: integer(args, "vcpu") } : {}),
3779
3842
  ...(integer(args, "memory") !== undefined ? { memoryMiB: integer(args, "memory") } : {}),
3780
3843
  ...(integer(args, "disk") !== undefined ? { diskMiB: integer(args, "disk") } : {}),
@@ -3811,7 +3874,7 @@ function createInput(args) {
3811
3874
  }
3812
3875
  /** Runs a command in a sandbox, streaming its output, and answers its exit code
3813
3876
  * (124 on a timeout), so a script's `set -e` behaves. */
3814
- async function execute(sbx, command, args, out, timeout,
3877
+ async function execute(sbx, command, args, out, timeout, env,
3815
3878
  /** `run` stops its whole sandbox on Ctrl-C, so it handles the signal itself. */
3816
3879
  ownInterrupt = true) {
3817
3880
  const target = command.length === 1 ? command[0] : command;
@@ -3820,7 +3883,8 @@ ownInterrupt = true) {
3820
3883
  const options = {
3821
3884
  ...(stdin ? { stdin } : {}),
3822
3885
  ...(flag(args, "cwd") ? { cwd: flag(args, "cwd") } : {}),
3823
- ...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
3886
+ ...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
3887
+ ...(has(args, "root") ? { user: "root" } : {}),
3824
3888
  ...(timeout !== undefined ? { timeoutMs: timeout * 1000 } : {}),
3825
3889
  };
3826
3890
  // Ctrl-C stops the command in the sandbox too, and says so; without this
@@ -3869,12 +3933,13 @@ ownInterrupt = true) {
3869
3933
  const code = result.timedOut ? 124 : Math.min(255, exit < 0 ? 128 - exit : exit);
3870
3934
  return lost && code === 0 ? 1 : code;
3871
3935
  }
3872
- async function shell(sbx, command) {
3936
+ async function shell(sbx, command, root = false) {
3873
3937
  const stdin = process.stdin;
3874
3938
  const size = () => ({ cols: process.stdout.columns || 80, rows: process.stdout.rows || 24 });
3875
3939
  const terminal = await sbx.terminal({
3876
3940
  ...size(),
3877
3941
  ...(command ? { command } : {}),
3942
+ ...(root ? { user: "root" } : {}),
3878
3943
  onData: (data) => process.stdout.write(data),
3879
3944
  });
3880
3945
  if (stdin.isTTY)
package/dist/index.d.ts CHANGED
@@ -74,5 +74,5 @@ export type { RuntimeOptions as ClientOptions };
74
74
  export { identityToken, type IdentityToken } from "./identity.js";
75
75
  export { openWalletAccount, claimWalletAccount, type OpenedWalletAccount, type WalletClaim, 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";
@@ -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 {};
@@ -90,6 +90,12 @@ export type DeployApp = ({
90
90
  image?: never;
91
91
  }) & {
92
92
  env?: Record<string, string>;
93
+ /** Carry the last deploy's environment, still sealed. With env, that
94
+ * environment with env's names set: change one value without sending the rest
95
+ * (needs a key that may reveal the App's secrets, secrets_reveal; else 403).
96
+ * Only for a build a push to the production branch made, from the repository the
97
+ * environment came with; otherwise refused with keep_env_refused (409). */
98
+ keepEnv?: boolean;
93
99
  port?: number;
94
100
  healthPath?: string;
95
101
  /** Return live, failed or superseded, including its URL/version or failure details. */
@@ -100,6 +106,10 @@ export type AppVersion = {
100
106
  live: boolean;
101
107
  previous: boolean;
102
108
  saved: boolean;
109
+ /** Its deploy's id, for GET /v1/apps/{id}/deploys/{deploy}. */
110
+ deploy: string;
111
+ /** The build it was deployed from, or null for an image. */
112
+ build: string | null;
103
113
  };
104
114
  export type AppCopy = {
105
115
  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.1";
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.1";
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "withruntime",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "license": "Apache-2.0",
5
5
  "mcpName": "com.withruntime/runtime",
6
6
  "type": "module",