withruntime 0.18.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +32 -1
- package/README.md +19 -0
- package/README.src.md +19 -0
- package/dist/agents/index.d.ts +11 -0
- package/dist/agents/index.js +12 -0
- package/dist/agents-cli.d.ts +10 -0
- package/dist/agents-cli.js +362 -0
- package/dist/apps-cli.d.ts +2 -0
- package/dist/apps-cli.js +173 -0
- package/dist/builds/index.d.ts +10 -0
- package/dist/builds/index.js +11 -0
- package/dist/cli-words.js +1 -0
- package/dist/cli.js +88 -6
- package/dist/client.d.ts +19 -0
- package/dist/client.js +20 -0
- package/dist/cloudflare/errors.d.ts +23 -0
- package/dist/cloudflare/errors.js +16 -0
- package/dist/cloudflare/index.d.ts +82 -9
- package/dist/cloudflare/index.js +162 -38
- package/dist/cloudflare/interpreter.js +14 -5
- package/dist/cloudflare/mime.d.ts +1 -0
- package/dist/cloudflare/mime.js +66 -0
- package/dist/cloudflare/streams.d.ts +8 -0
- package/dist/cloudflare/streams.js +30 -0
- package/dist/cloudflare/types.d.ts +338 -0
- package/dist/cloudflare/types.js +80 -0
- package/dist/cloudflare/worker.d.ts +297 -10
- package/dist/cloudflare/worker.js +203 -17
- package/dist/containers/index.d.ts +10 -0
- package/dist/containers/index.js +11 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/product-names.js +3 -0
- package/dist/products/agents.d.ts +272 -0
- package/dist/products/agents.js +251 -0
- package/dist/products/containers.d.ts +71 -0
- package/dist/products/containers.js +6 -0
- package/dist/products/index.d.ts +6 -0
- package/dist/products/index.js +7 -0
- package/dist/sandbox.d.ts +4 -0
- package/dist/transport.d.ts +2 -2
- package/dist/transport.js +1 -1
- package/dist/tunnel.js +1 -1
- package/dist/types.d.ts +11 -0
- package/package.json +16 -1
package/dist/apps-cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { RuntimeError } from "./errors.js";
|
|
|
3
3
|
import { logStream } from "./terminal.js";
|
|
4
4
|
import { apps, } from "./products/apps.js";
|
|
5
5
|
import { builds, } from "./products/builds.js";
|
|
6
|
+
import { containers, } from "./products/containers.js";
|
|
6
7
|
export const APP_HELP = `runtime apps <command>
|
|
7
8
|
|
|
8
9
|
An App serves your web server from an image or a build. Copies start when
|
|
@@ -73,6 +74,34 @@ Examples
|
|
|
73
74
|
runtime builds run <repoId> --ref main
|
|
74
75
|
runtime builds ls --commit $(git rev-parse HEAD)
|
|
75
76
|
`;
|
|
77
|
+
export const CONTAINER_HELP = `runtime containers <command>
|
|
78
|
+
|
|
79
|
+
A container runs one OCI image's process on its own microVM, billed at the
|
|
80
|
+
sandbox rates. It never sleeps unless --sleep; on exit it restarts when it
|
|
81
|
+
serves a port, else it stops (--on-exit sets either).
|
|
82
|
+
In a private beta: outside it, each command says how to ask to join.
|
|
83
|
+
|
|
84
|
+
create <image> --vcpu <1-16> --memory <128-65536> [--name <name>] [--label key=value]...
|
|
85
|
+
[--env KEY[=VALUE]]... [--entrypoint <program>] [--workdir /app] [--user <user>]
|
|
86
|
+
[--port <1-65535>] [--on-exit restart|stop] [--sleep] [-- <command...>]
|
|
87
|
+
Run it; prints its id. The image's digest is pinned.
|
|
88
|
+
--env KEY alone sends KEY's value from your environment.
|
|
89
|
+
--entrypoint drops the image's command; after --, the
|
|
90
|
+
command replaces the image's
|
|
91
|
+
ls [--all] [--state <state>]... [--label key=value]...
|
|
92
|
+
Your containers: all but deleting and deleted, unless
|
|
93
|
+
--all or --state
|
|
94
|
+
get <container> One container, in full (an id or its name): its
|
|
95
|
+
environment's names, never their values
|
|
96
|
+
update <container> [--name <name>] [--label key=value]...
|
|
97
|
+
Rename it or replace its labels
|
|
98
|
+
rm <container> [--wait] Delete it and stop its process; --wait: until deleted
|
|
99
|
+
|
|
100
|
+
Examples
|
|
101
|
+
runtime containers create nginx:1.27 --vcpu 1 --memory 512 --name web
|
|
102
|
+
runtime containers create python:3.12 --vcpu 1 --memory 1024 -- python3 -m http.server 8000
|
|
103
|
+
runtime containers rm web --wait
|
|
104
|
+
`;
|
|
76
105
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
77
106
|
const when = (ms) => (ms == null ? "-" : new Date(ms).toISOString());
|
|
78
107
|
const ENDED = ["succeeded", "failed", "cancelled", "superseded"];
|
|
@@ -562,3 +591,147 @@ export async function buildsCommand(argv, out, client, kit) {
|
|
|
562
591
|
"logs",
|
|
563
592
|
]);
|
|
564
593
|
}
|
|
594
|
+
const CONTAINER_STATES = [
|
|
595
|
+
"waiting",
|
|
596
|
+
"running",
|
|
597
|
+
"sleeping",
|
|
598
|
+
"stopped",
|
|
599
|
+
"failed",
|
|
600
|
+
"deleting",
|
|
601
|
+
"deleted",
|
|
602
|
+
];
|
|
603
|
+
const CONTAINER_VALUES = [
|
|
604
|
+
"vcpu",
|
|
605
|
+
"memory",
|
|
606
|
+
"name",
|
|
607
|
+
"label",
|
|
608
|
+
"env",
|
|
609
|
+
"entrypoint",
|
|
610
|
+
"workdir",
|
|
611
|
+
"user",
|
|
612
|
+
"port",
|
|
613
|
+
"on-exit",
|
|
614
|
+
];
|
|
615
|
+
export async function containersCommand(argv, env, out, client, kit) {
|
|
616
|
+
const { parse, flag, has, integer, need, pairs, envPairs, table, usage } = kit;
|
|
617
|
+
const [verb, ...rest] = argv;
|
|
618
|
+
const print = (human, value) => out.write(out.json ? JSON.stringify(value) : human);
|
|
619
|
+
if (!verb || verb === "help" || verb === "--help") {
|
|
620
|
+
print(named(CONTAINER_HELP), { usage: named(CONTAINER_HELP) });
|
|
621
|
+
return 0;
|
|
622
|
+
}
|
|
623
|
+
const api = containers((await client()).transport);
|
|
624
|
+
/** A container's id from its id or its name: a name is looked up among the
|
|
625
|
+
* containers not deleted, and must be one container's alone. */
|
|
626
|
+
const idOf = async (target) => {
|
|
627
|
+
const given = need(target, "a container's id or name");
|
|
628
|
+
if (UUID.test(given))
|
|
629
|
+
return given;
|
|
630
|
+
const found = (await api.list({ name: given })).data;
|
|
631
|
+
if (found.length === 1)
|
|
632
|
+
return found[0].id;
|
|
633
|
+
throw usage(found.length
|
|
634
|
+
? `${found.length} containers are named ${given}; give one's id (${me} containers ls).`
|
|
635
|
+
: `No container is named ${given}. Your containers: ${me} containers ls`);
|
|
636
|
+
};
|
|
637
|
+
switch (verb) {
|
|
638
|
+
case "create": {
|
|
639
|
+
const args = parse(rest, ["sleep"], CONTAINER_VALUES);
|
|
640
|
+
if (args.positional.length !== 1)
|
|
641
|
+
throw usage(`Give one image: ${me} containers create nginx:1.27 --vcpu 1 --memory 512 [-- <command...>]`);
|
|
642
|
+
const image = args.positional[0];
|
|
643
|
+
const vcpu = integer(args, "vcpu");
|
|
644
|
+
const memoryMiB = integer(args, "memory");
|
|
645
|
+
if (vcpu === undefined || memoryMiB === undefined)
|
|
646
|
+
throw usage(`Give --vcpu and --memory: ${me} containers create ${image} --vcpu 1 --memory 512`);
|
|
647
|
+
const onExit = flag(args, "on-exit");
|
|
648
|
+
if (onExit !== undefined && onExit !== "restart" && onExit !== "stop")
|
|
649
|
+
throw usage("--on-exit takes restart or stop.");
|
|
650
|
+
const port = integer(args, "port");
|
|
651
|
+
const entrypoint = flag(args, "entrypoint");
|
|
652
|
+
const workingDir = flag(args, "workdir");
|
|
653
|
+
const user = flag(args, "user");
|
|
654
|
+
const name = flag(args, "name");
|
|
655
|
+
const input = {
|
|
656
|
+
image,
|
|
657
|
+
vcpu,
|
|
658
|
+
memoryMiB,
|
|
659
|
+
...(name === undefined ? {} : { name }),
|
|
660
|
+
...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
|
|
661
|
+
...(args.flags.has("env") ? { env: envPairs(args, env) } : {}),
|
|
662
|
+
...(entrypoint === undefined ? {} : { entrypoint: [entrypoint] }),
|
|
663
|
+
...(args.rest?.length ? { command: args.rest } : {}),
|
|
664
|
+
...(workingDir === undefined ? {} : { workingDir }),
|
|
665
|
+
...(user === undefined ? {} : { user }),
|
|
666
|
+
...(port === undefined ? {} : { port }),
|
|
667
|
+
...(onExit === undefined ? {} : { onExit }),
|
|
668
|
+
...(has(args, "sleep") ? { sleep: true } : {}),
|
|
669
|
+
};
|
|
670
|
+
const made = await api.create(input);
|
|
671
|
+
if (!out.json)
|
|
672
|
+
out.error(`${made.name ?? made.id} is ${made.state}, running ${made.image}. Read it: ${me} containers get ${made.name ?? made.id}`);
|
|
673
|
+
print(made.id, made);
|
|
674
|
+
return 0;
|
|
675
|
+
}
|
|
676
|
+
case "ls":
|
|
677
|
+
case "list": {
|
|
678
|
+
const args = parse(rest, ["all"], ["state", "label"]);
|
|
679
|
+
const states = args.flags.get("state");
|
|
680
|
+
for (const state of states ?? [])
|
|
681
|
+
if (!CONTAINER_STATES.includes(state))
|
|
682
|
+
throw usage(`--state takes ${CONTAINER_STATES.join(", ")}.`);
|
|
683
|
+
// --label key=value, as the list's key:value filters.
|
|
684
|
+
const labels = Object.entries(pairs(args, "label")).map(([k, v]) => `${k}:${v}`);
|
|
685
|
+
const all = await (await api.list({
|
|
686
|
+
limit: 100,
|
|
687
|
+
...(states
|
|
688
|
+
? { state: states }
|
|
689
|
+
: has(args, "all")
|
|
690
|
+
? { state: CONTAINER_STATES }
|
|
691
|
+
: {}),
|
|
692
|
+
...(labels.length ? { label: labels } : {}),
|
|
693
|
+
})).toArray(Infinity);
|
|
694
|
+
print(all.length
|
|
695
|
+
? table([
|
|
696
|
+
["ID", "NAME", "STATE", "IMAGE", "PORT", "SIZE", "CREATED"],
|
|
697
|
+
...all.map((c) => [
|
|
698
|
+
c.id,
|
|
699
|
+
c.name ?? "-",
|
|
700
|
+
c.state,
|
|
701
|
+
c.image,
|
|
702
|
+
c.port === null ? "-" : String(c.port),
|
|
703
|
+
`${c.vcpu} vCPU, ${c.memoryMiB} MiB`,
|
|
704
|
+
when(c.createdAt),
|
|
705
|
+
]),
|
|
706
|
+
])
|
|
707
|
+
: `No containers. Run one: ${me} containers create nginx:1.27 --vcpu 1 --memory 512`, all);
|
|
708
|
+
return 0;
|
|
709
|
+
}
|
|
710
|
+
case "get": {
|
|
711
|
+
const found = await api.get(await idOf(rest[0]));
|
|
712
|
+
print(JSON.stringify(found, null, 2), found);
|
|
713
|
+
return 0;
|
|
714
|
+
}
|
|
715
|
+
case "update": {
|
|
716
|
+
const args = parse(rest, [], ["name", "label"]);
|
|
717
|
+
const id = await idOf(args.positional[0]);
|
|
718
|
+
const change = {
|
|
719
|
+
...(flag(args, "name") === undefined ? {} : { name: flag(args, "name") }),
|
|
720
|
+
...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
|
|
721
|
+
};
|
|
722
|
+
if (!Object.keys(change).length)
|
|
723
|
+
throw usage(`Give --name or --label: ${me} containers update ${args.positional[0]} --name web`);
|
|
724
|
+
const updated = await api.update(id, change);
|
|
725
|
+
print(`${updated.name ?? updated.id} updated.`, updated);
|
|
726
|
+
return 0;
|
|
727
|
+
}
|
|
728
|
+
case "rm":
|
|
729
|
+
case "delete": {
|
|
730
|
+
const args = parse(rest, ["wait"], []);
|
|
731
|
+
const gone = await api.delete(await idOf(args.positional[0]), { wait: has(args, "wait") });
|
|
732
|
+
print(`${gone.name ?? gone.id} ${gone.state}.`, gone);
|
|
733
|
+
return 0;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
throw kit.unknown("containers command", verb, ["create", "ls", "get", "update", "rm"]);
|
|
737
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** `withruntime/builds`: Builds alone. An image built from a repository's
|
|
2
|
+
* commit, at the sandbox rates. In a private beta.
|
|
3
|
+
*
|
|
4
|
+
* import { builds } from "withruntime/builds";
|
|
5
|
+
* const client = builds(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
6
|
+
*/
|
|
7
|
+
import { Runtime, type RuntimeOptions } from "../client.js";
|
|
8
|
+
export type * from "../products/builds.js";
|
|
9
|
+
/** `runtime.builds` of a client made with `options`. */
|
|
10
|
+
export declare function builds(options?: RuntimeOptions): Runtime["builds"];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** `withruntime/builds`: Builds alone. An image built from a repository's
|
|
2
|
+
* commit, at the sandbox rates. In a private beta.
|
|
3
|
+
*
|
|
4
|
+
* import { builds } from "withruntime/builds";
|
|
5
|
+
* const client = builds(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
6
|
+
*/
|
|
7
|
+
import { Runtime } from "../client.js";
|
|
8
|
+
/** `runtime.builds` of a client made with `options`. */
|
|
9
|
+
export function builds(options) {
|
|
10
|
+
return new Runtime(options).builds;
|
|
11
|
+
}
|
package/dist/cli-words.js
CHANGED
|
@@ -26,6 +26,7 @@ const OPTIONS = {
|
|
|
26
26
|
image: { flag: "--image" },
|
|
27
27
|
snapshot: { flag: "--snapshot" },
|
|
28
28
|
spreadFrom: { flag: "--spread-from" },
|
|
29
|
+
secrets: { flag: "--secret / --no-secrets" },
|
|
29
30
|
volumes: { flag: "--volume" },
|
|
30
31
|
network: { flag: "--allow / --deny / --connect / --no-internet" },
|
|
31
32
|
maxCostMicros: { flag: "--max-cost", micros: true },
|
package/dist/cli.js
CHANGED
|
@@ -38,6 +38,7 @@ Account
|
|
|
38
38
|
account switch <name|id> Use another of them for every command after
|
|
39
39
|
account add Connect another account (choose it in the browser)
|
|
40
40
|
account close --confirm "<account name>" Close the account for good (owner, key for every product)
|
|
41
|
+
account emails [<category> on|off] Your optional emails from Runtime: own-deletions, billing, usage, tips
|
|
41
42
|
audit [--action member.] [--limit 50] [--before <next>]
|
|
42
43
|
The account's audit log (owner or admin keys)
|
|
43
44
|
sso Single sign-on and SCIM directory sync (owner or
|
|
@@ -111,6 +112,8 @@ Products
|
|
|
111
112
|
jobs A command in a fresh sandbox, once or on a cron schedule, with its logs
|
|
112
113
|
apps Your web server from an image or a build, copies started by requests (beta)
|
|
113
114
|
builds A connected repository's commits built into what an App deploys (beta)
|
|
115
|
+
containers One OCI image's process on its own microVM, at the sandbox rates (beta)
|
|
116
|
+
agents Claude Code, Codex, Gemini CLI or your own agent in a sandbox per session (beta)
|
|
114
117
|
domains Your own hostname for a sandbox's port, with HTTPS
|
|
115
118
|
ports A public TCP port to a sandbox: databases, game servers
|
|
116
119
|
ips A dedicated outbound address, for allow-lists
|
|
@@ -136,7 +139,7 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
136
139
|
[--timeout <60-86400>] [--on-timeout pause|stop|delete] [--no-wait]
|
|
137
140
|
[--no-internet] [--allow <host>]... [--deny <host>]... [--connect <host:port>]...
|
|
138
141
|
[--image <name:tag|imageId> | --snapshot <snapshotId>] [--volume <volumeId>:/data[:snapshot]]...
|
|
139
|
-
[--spread-from <id>]...
|
|
142
|
+
[--spread-from <id>]... [--secret NAME]... [--no-secrets]
|
|
140
143
|
[--idle-pause <seconds>] [--no-auto-wake] [--persistent] [--get-or-create]
|
|
141
144
|
Create one; prints its id
|
|
142
145
|
ls [--all] [--label k=v]... [--state running] List (live ones unless --all)
|
|
@@ -183,7 +186,7 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
183
186
|
tailscale up <id> --auth-key-secret NAME [--hostname h] [--tag tag:x]...
|
|
184
187
|
Join your tailnet (paid); the key is a secret set with --jobs
|
|
185
188
|
tailscale status <id> | tailscale down <id> Its tailnet address; take it off the tailnet
|
|
186
|
-
fork <id> [--count 3] [--name n] [--keep-snapshot]
|
|
189
|
+
fork <id> [--count 3] [--name n] [--keep-snapshot] [--secret NAME]... [--no-secrets]
|
|
187
190
|
Copies of it as it is now, running; prints their ids
|
|
188
191
|
run-code <id> <file|-> [--lang python|javascript|typescript|r|java|bash|go] [--context c] [--out-dir .]
|
|
189
192
|
Run a notebook-style cell; charts saved as PNG
|
|
@@ -427,6 +430,8 @@ const COMMANDS = [
|
|
|
427
430
|
"jobs",
|
|
428
431
|
"apps",
|
|
429
432
|
"builds",
|
|
433
|
+
"containers",
|
|
434
|
+
"agents",
|
|
430
435
|
"domains",
|
|
431
436
|
"ports",
|
|
432
437
|
"ips",
|
|
@@ -447,6 +452,8 @@ const OWN_HELP = [
|
|
|
447
452
|
"jobs",
|
|
448
453
|
"apps",
|
|
449
454
|
"builds",
|
|
455
|
+
"containers",
|
|
456
|
+
"agents",
|
|
450
457
|
"keys",
|
|
451
458
|
"webhooks",
|
|
452
459
|
"webhook",
|
|
@@ -600,6 +607,20 @@ function describe(info) {
|
|
|
600
607
|
.map(([k, v]) => `${k}=${v}`)
|
|
601
608
|
.join(", ") || "-",
|
|
602
609
|
],
|
|
610
|
+
// Null is every egress secret that reaches it; [] is none. Absent from
|
|
611
|
+
// older servers, so no line then rather than a guess.
|
|
612
|
+
...(info.secrets === undefined
|
|
613
|
+
? []
|
|
614
|
+
: [
|
|
615
|
+
[
|
|
616
|
+
"secrets",
|
|
617
|
+
info.secrets === null
|
|
618
|
+
? "every one that reaches it"
|
|
619
|
+
: info.secrets.length
|
|
620
|
+
? info.secrets.join(", ")
|
|
621
|
+
: "none",
|
|
622
|
+
],
|
|
623
|
+
]),
|
|
603
624
|
["charged", dollars(BigInt(Math.round(Number(info.chargedMicros) || 0)))],
|
|
604
625
|
]);
|
|
605
626
|
}
|
|
@@ -1111,6 +1132,14 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
1111
1132
|
product = "builds";
|
|
1112
1133
|
return (await import("./apps-cli.js")).buildsCommand(rest, out, () => client(env), kit);
|
|
1113
1134
|
}
|
|
1135
|
+
if (command === "container" || command === "containers") {
|
|
1136
|
+
product = "containers";
|
|
1137
|
+
return (await import("./apps-cli.js")).containersCommand(rest, env, out, () => client(env), kit);
|
|
1138
|
+
}
|
|
1139
|
+
if (command === "agent" || command === "agents") {
|
|
1140
|
+
product = "agents";
|
|
1141
|
+
return (await import("./agents-cli.js")).agentsCommand(rest, env, out, () => client(env), kit);
|
|
1142
|
+
}
|
|
1114
1143
|
// WireGuard is inside networks: `runtime networks wireguard peer add`.
|
|
1115
1144
|
const wireguard = command === "networks" && rest[0] === "wireguard";
|
|
1116
1145
|
const networkProduct = wireguard ? "wireguard" : command;
|
|
@@ -1437,6 +1466,11 @@ const DROP_IN = {
|
|
|
1437
1466
|
js: 'import { SandboxInstance } from "withruntime/blaxel"; // was: from "@blaxel/core"',
|
|
1438
1467
|
py: "from withruntime.blaxel import SandboxInstance # was: from blaxel.core import SandboxInstance",
|
|
1439
1468
|
},
|
|
1469
|
+
// A Worker-only SDK: no Python. More than the import changes, so say what.
|
|
1470
|
+
cloudflare: {
|
|
1471
|
+
js: 'import { getSandbox } from "withruntime/cloudflare"; // was: from "@cloudflare/sandbox"',
|
|
1472
|
+
also: "Change the `export { Sandbox }` line the same way, delete the `containers` block from the Wrangler config and run `npx wrangler secret put RUNTIME_API_KEY`.",
|
|
1473
|
+
},
|
|
1440
1474
|
};
|
|
1441
1475
|
/** How to move a rival's code across, when one import does it. */
|
|
1442
1476
|
export function dropInLines(provider) {
|
|
@@ -1444,9 +1478,10 @@ export function dropInLines(provider) {
|
|
|
1444
1478
|
if (!drop)
|
|
1445
1479
|
return [];
|
|
1446
1480
|
return [
|
|
1447
|
-
`Your ${RIVAL_NAMES[provider]} code runs on Runtime with one import changed:`,
|
|
1481
|
+
`Your ${RIVAL_NAMES[provider]} code runs on Runtime with ${drop.also ? "this import changed" : "one import changed"}:`,
|
|
1448
1482
|
` ${drop.js}`,
|
|
1449
|
-
` ${drop.py}
|
|
1483
|
+
...(drop.py ? [` ${drop.py}`] : []),
|
|
1484
|
+
...(drop.also ? [drop.also] : []),
|
|
1450
1485
|
];
|
|
1451
1486
|
}
|
|
1452
1487
|
/** A date as the CLI writes one: 23 September 2026. */
|
|
@@ -1888,12 +1923,14 @@ async function sandbox(argv, env, out) {
|
|
|
1888
1923
|
return 0;
|
|
1889
1924
|
}
|
|
1890
1925
|
case "fork": {
|
|
1891
|
-
const args = parse(rest, ["keep-snapshot", "trial", "paid"], ["count", "name"]);
|
|
1926
|
+
const args = parse(rest, ["keep-snapshot", "trial", "paid", "no-secrets"], ["count", "name", "secret"]);
|
|
1927
|
+
const secrets = secretsInput(args);
|
|
1892
1928
|
const sbx = await get(args.positional[0]);
|
|
1893
1929
|
const forks = await sbx.fork({
|
|
1894
1930
|
count: integer(args, "count") ?? 1,
|
|
1895
1931
|
...(flag(args, "name") ? { name: flag(args, "name") } : {}),
|
|
1896
1932
|
...(has(args, "keep-snapshot") ? { keepSnapshot: true } : {}),
|
|
1933
|
+
...secrets,
|
|
1897
1934
|
...(has(args, "trial")
|
|
1898
1935
|
? { funding: "trial" }
|
|
1899
1936
|
: has(args, "paid")
|
|
@@ -2745,8 +2782,28 @@ async function accountCommand(argv, env, out) {
|
|
|
2745
2782
|
].join("\n"), closed);
|
|
2746
2783
|
return 0;
|
|
2747
2784
|
}
|
|
2785
|
+
if (verb === "emails") {
|
|
2786
|
+
/* 0921: which of Runtime's optional emails this key's person gets.
|
|
2787
|
+
Bare, it lists them; `<category> on|off` changes one, for that person
|
|
2788
|
+
only. */
|
|
2789
|
+
const [category, state, ...extra] = rest;
|
|
2790
|
+
const categories = ["own-deletions", "billing", "usage", "tips"];
|
|
2791
|
+
if (extra.length || (category && !state) || (state && state !== "on" && state !== "off"))
|
|
2792
|
+
throw usage(`${me} account emails [<${categories.join("|")}> on|off]`);
|
|
2793
|
+
if (category && !categories.includes(category))
|
|
2794
|
+
throw unknown("email", category, [...categories]);
|
|
2795
|
+
const runtime = await client(env);
|
|
2796
|
+
const settings = await runtime.account.emails(category
|
|
2797
|
+
? { category: category, enabled: state === "on" }
|
|
2798
|
+
: undefined);
|
|
2799
|
+
print([
|
|
2800
|
+
...settings.map((each) => `${each.category.padEnd(14)}${each.enabled ? "on" : "off"}`),
|
|
2801
|
+
"Always sent: sign-in, invitations, revoked keys, support replies, credit expiry, service credits, and deletions Runtime decides.",
|
|
2802
|
+
].join("\n"), { settings });
|
|
2803
|
+
return 0;
|
|
2804
|
+
}
|
|
2748
2805
|
if (verb !== "ls" && verb !== "list")
|
|
2749
|
-
throw unknown("account command", verb, ["ls", "switch", "add", "close"]);
|
|
2806
|
+
throw unknown("account command", verb, ["ls", "switch", "add", "close", "emails"]);
|
|
2750
2807
|
await nameSaved(env).catch(() => undefined);
|
|
2751
2808
|
const current = await store.read().catch(() => null);
|
|
2752
2809
|
const saved = [
|
|
@@ -3749,6 +3806,7 @@ const CREATE_FLAGS = [
|
|
|
3749
3806
|
"no-auto-wake",
|
|
3750
3807
|
"persistent",
|
|
3751
3808
|
"get-or-create",
|
|
3809
|
+
"no-secrets",
|
|
3752
3810
|
];
|
|
3753
3811
|
/** Says on standard error, once for each reason, why a create has not
|
|
3754
3812
|
* answered yet: it is waiting for a slot without credit or room, which can take up to
|
|
@@ -3818,6 +3876,7 @@ const CREATE_VALUES = [
|
|
|
3818
3876
|
"image",
|
|
3819
3877
|
"snapshot",
|
|
3820
3878
|
"spread-from",
|
|
3879
|
+
"secret",
|
|
3821
3880
|
"volume",
|
|
3822
3881
|
"env",
|
|
3823
3882
|
];
|
|
@@ -3836,6 +3895,28 @@ function usdMicros(args, name) {
|
|
|
3836
3895
|
throw usage(`--${name} is too large to represent exactly in microdollars.`);
|
|
3837
3896
|
return Number(micros);
|
|
3838
3897
|
}
|
|
3898
|
+
/** The egress secrets a new sandbox may use: each --secret adds a name
|
|
3899
|
+
* (commas also separate them), --no-secrets is none, and neither leaves it
|
|
3900
|
+
* out, for every secret that reaches it. Fixed for the sandbox's life. */
|
|
3901
|
+
function secretsInput(args) {
|
|
3902
|
+
const names = args.flags.get("secret");
|
|
3903
|
+
if (has(args, "no-secrets")) {
|
|
3904
|
+
if (names)
|
|
3905
|
+
throw usage("Give --secret or --no-secrets, not both.");
|
|
3906
|
+
return { secrets: [] };
|
|
3907
|
+
}
|
|
3908
|
+
if (!names)
|
|
3909
|
+
return {};
|
|
3910
|
+
return {
|
|
3911
|
+
secrets: names
|
|
3912
|
+
.flatMap((value) => value.split(","))
|
|
3913
|
+
.map((name) => {
|
|
3914
|
+
if (!name.trim())
|
|
3915
|
+
throw usage("--secret needs a non-empty value.");
|
|
3916
|
+
return name.trim();
|
|
3917
|
+
}),
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3839
3920
|
/** A create body from the command line's flags. */
|
|
3840
3921
|
function createInput(args, env) {
|
|
3841
3922
|
for (const name of ["image", "snapshot", "volume", "name", "region", "connect"])
|
|
@@ -3857,6 +3938,7 @@ function createInput(args, env) {
|
|
|
3857
3938
|
}),
|
|
3858
3939
|
}
|
|
3859
3940
|
: {}),
|
|
3941
|
+
...secretsInput(args),
|
|
3860
3942
|
...(args.flags.has("volume") ? { volumes: args.flags.get("volume").map(attachment) } : {}),
|
|
3861
3943
|
...(has(args, "no-internet") || rules.length
|
|
3862
3944
|
? {
|
package/dist/client.d.ts
CHANGED
|
@@ -153,6 +153,16 @@ export type ClosedAccount = {
|
|
|
153
153
|
/** False while what was running is still stopping; it is gone within minutes. */
|
|
154
154
|
released: boolean;
|
|
155
155
|
};
|
|
156
|
+
/** One of Runtime's optional emails: `own-deletions` (warnings for dates
|
|
157
|
+
* the account's own retention set), `billing` (credit low or out), `usage`
|
|
158
|
+
* (the month's free usage at 80% and all) or `tips` (getting started). */
|
|
159
|
+
export type EmailCategory = "own-deletions" | "billing" | "usage" | "tips";
|
|
160
|
+
/** Whether this key's person gets one optional email, in this account. */
|
|
161
|
+
export type EmailSetting = {
|
|
162
|
+
category: EmailCategory;
|
|
163
|
+
enabled: boolean;
|
|
164
|
+
updatedAt: string | null;
|
|
165
|
+
};
|
|
156
166
|
/** The account this key belongs to: `runtime.account`. */
|
|
157
167
|
export declare class AccountApi {
|
|
158
168
|
private readonly t;
|
|
@@ -162,6 +172,15 @@ export declare class AccountApi {
|
|
|
162
172
|
* name exactly (`me().orgName`). Needs a key for every product made by an
|
|
163
173
|
* owner. Moves no money: what is unspent of a purchase made in the last 15
|
|
164
174
|
* days is refunded on request; other credit is forfeited. */
|
|
175
|
+
/** Which of Runtime's optional emails this key's person gets for this
|
|
176
|
+
* account. Without `set`, reads them; with `{ category, enabled }`, turns
|
|
177
|
+
* one on or off for that person only, and answers all of them. Sign-in,
|
|
178
|
+
* support replies, credit expiry and deletions Runtime decides are always
|
|
179
|
+
* sent and are not listed. */
|
|
180
|
+
emails(set?: {
|
|
181
|
+
category: EmailCategory;
|
|
182
|
+
enabled: boolean;
|
|
183
|
+
}, options?: RequestOptions): Promise<EmailSetting[]>;
|
|
165
184
|
close(input: {
|
|
166
185
|
confirm: string;
|
|
167
186
|
}, options?: RequestOptions): Promise<ClosedAccount>;
|
package/dist/client.js
CHANGED
|
@@ -167,6 +167,26 @@ export class AccountApi {
|
|
|
167
167
|
* name exactly (`me().orgName`). Needs a key for every product made by an
|
|
168
168
|
* owner. Moves no money: what is unspent of a purchase made in the last 15
|
|
169
169
|
* days is refunded on request; other credit is forfeited. */
|
|
170
|
+
/** Which of Runtime's optional emails this key's person gets for this
|
|
171
|
+
* account. Without `set`, reads them; with `{ category, enabled }`, turns
|
|
172
|
+
* one on or off for that person only, and answers all of them. Sign-in,
|
|
173
|
+
* support replies, credit expiry and deletions Runtime decides are always
|
|
174
|
+
* sent and are not listed. */
|
|
175
|
+
async emails(set, options = {}) {
|
|
176
|
+
const answer = set
|
|
177
|
+
? await this.t.json({
|
|
178
|
+
method: "POST",
|
|
179
|
+
path: "/v1/account/emails:update",
|
|
180
|
+
body: { category: set.category, enabled: set.enabled },
|
|
181
|
+
...options,
|
|
182
|
+
})
|
|
183
|
+
: await this.t.json({
|
|
184
|
+
method: "GET",
|
|
185
|
+
path: "/v1/account/emails",
|
|
186
|
+
...options,
|
|
187
|
+
});
|
|
188
|
+
return answer.settings;
|
|
189
|
+
}
|
|
170
190
|
close(input, options = {}) {
|
|
171
191
|
return this.t.json({
|
|
172
192
|
method: "POST",
|
|
@@ -119,6 +119,29 @@ export declare class OperationInterruptedError extends SandboxError<{
|
|
|
119
119
|
}> {
|
|
120
120
|
get reason(): string;
|
|
121
121
|
}
|
|
122
|
+
/** Cloudflare's transport error for its WebSocket sessions; Runtime's API
|
|
123
|
+
* has no such session, so the adapter never throws it. */
|
|
124
|
+
export declare class RPCTransportError extends SandboxError<{
|
|
125
|
+
kind: string;
|
|
126
|
+
originalMessage: string;
|
|
127
|
+
}> {
|
|
128
|
+
get kind(): string;
|
|
129
|
+
get originalMessage(): string;
|
|
130
|
+
}
|
|
131
|
+
/** Input the container refuses, such as content that is not base64. */
|
|
132
|
+
export declare class ValidationFailedError extends SandboxError<{
|
|
133
|
+
validationErrors: {
|
|
134
|
+
field: string;
|
|
135
|
+
message: string;
|
|
136
|
+
code: string;
|
|
137
|
+
}[];
|
|
138
|
+
}> {
|
|
139
|
+
get validationErrors(): {
|
|
140
|
+
field: string;
|
|
141
|
+
message: string;
|
|
142
|
+
code: string;
|
|
143
|
+
}[];
|
|
144
|
+
}
|
|
122
145
|
export declare class BucketMountError extends Error {
|
|
123
146
|
readonly code: string;
|
|
124
147
|
constructor(message: string, code?: string);
|
|
@@ -125,6 +125,22 @@ export class OperationInterruptedError extends SandboxError {
|
|
|
125
125
|
return this.context.reason;
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
+
/** Cloudflare's transport error for its WebSocket sessions; Runtime's API
|
|
129
|
+
* has no such session, so the adapter never throws it. */
|
|
130
|
+
export class RPCTransportError extends SandboxError {
|
|
131
|
+
get kind() {
|
|
132
|
+
return this.context.kind;
|
|
133
|
+
}
|
|
134
|
+
get originalMessage() {
|
|
135
|
+
return this.context.originalMessage;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** Input the container refuses, such as content that is not base64. */
|
|
139
|
+
export class ValidationFailedError extends SandboxError {
|
|
140
|
+
get validationErrors() {
|
|
141
|
+
return this.context.validationErrors;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
128
144
|
export class BucketMountError extends Error {
|
|
129
145
|
code;
|
|
130
146
|
constructor(message, code = "BUCKET_MOUNT_ERROR") {
|