withruntime 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/README.src.md +2 -2
- package/dist/billing-cli.js +8 -4
- package/dist/blaxel/sandbox.d.ts +1 -1
- package/dist/blaxel/sandbox.js +1 -1
- package/dist/cli.d.ts +17 -2
- package/dist/cli.js +242 -69
- package/dist/credentials.d.ts +3 -0
- package/dist/credentials.js +15 -0
- package/dist/daytona/daytona.d.ts +1 -1
- package/dist/daytona/daytona.js +1 -1
- package/dist/e2b/sandbox.d.ts +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/machine.d.ts +15 -1
- package/dist/machine.js +26 -11
- package/dist/mcp.js +144 -20
- package/dist/products/api-keys.d.ts +73 -0
- package/dist/products/api-keys.js +23 -0
- package/dist/products/apps.d.ts +12 -31
- package/dist/products/apps.js +8 -113
- package/dist/products/balancers.d.ts +87 -0
- package/dist/products/balancers.js +8 -0
- package/dist/products/builds.d.ts +30 -0
- package/dist/products/builds.js +6 -0
- package/dist/products/index.d.ts +2 -0
- package/dist/products/index.js +2 -0
- package/dist/products/jobs.d.ts +2 -2
- package/dist/products/jobs.js +1 -1
- package/dist/products/observability.d.ts +7 -2
- package/dist/products/resources.d.ts +54 -0
- package/dist/products/resources.js +122 -0
- package/dist/products/secrets.d.ts +22 -0
- package/dist/products/secrets.js +8 -0
- package/dist/products/volumes.d.ts +3 -0
- package/dist/sandbox.d.ts +4 -0
- package/dist/sandbox.js +27 -2
- package/dist/transport.d.ts +4 -1
- package/dist/transport.js +25 -4
- package/dist/types.d.ts +9 -5
- package/dist/vercel/sandbox.d.ts +1 -1
- package/dist/vercel/sandbox.js +1 -1
- package/dist/wallet.d.ts +4 -1
- package/dist/wallet.js +2 -1
- package/package.json +2 -14
- package/dist/cli/logs.test.d.ts +0 -1
- package/dist/cli/logs.test.js +0 -272
- package/dist/products/logs.test.d.ts +0 -1
- package/dist/products/logs.test.js +0 -343
package/dist/cli.js
CHANGED
|
@@ -30,6 +30,8 @@ Account
|
|
|
30
30
|
login [--agent-name <name>] [--no-browser] [--wait]
|
|
31
31
|
Connect this machine (no key to copy)
|
|
32
32
|
login --with-key Or paste a key from withruntime.com/account/keys
|
|
33
|
+
billing topup <usd> --new-account --accept-terms [--wait]
|
|
34
|
+
No person? Open an account paid in USDC
|
|
33
35
|
logout Revoke this machine's connection
|
|
34
36
|
whoami The organization and agent you act as
|
|
35
37
|
account [ls] The accounts this machine is connected to
|
|
@@ -40,16 +42,22 @@ Account
|
|
|
40
42
|
The account's audit log (owner or admin keys)
|
|
41
43
|
sso Single sign-on and SCIM directory sync (owner or
|
|
42
44
|
admin keys); an owner changes it on the website
|
|
43
|
-
usage [--csv] Balance,
|
|
45
|
+
usage [--csv] Balance, free usage and charges; --csv, each resource
|
|
44
46
|
usage export --since <UTC time> --until <UTC time>
|
|
45
47
|
Every settlement in the range as CSV; --json, each page
|
|
46
48
|
limits Whether this key is read-only, and its daily spending limit
|
|
47
49
|
secrets set <NAME> --host <host>... [--header <name> [--format '<text {value}>']]
|
|
48
50
|
[--allow '[GET,HEAD] /path/*']... Store a secret sandboxes use without seeing it; the
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
[--only-label key=value[,k=v]]... value is read from standard input, never an argument.
|
|
52
|
+
[--only-sandbox <id>]... --allow (paid): only these methods and paths get it.
|
|
53
|
+
[--all-sandboxes] --only-label: only sandboxes with those labels (each
|
|
54
|
+
flag one selector, any may match); --only-sandbox:
|
|
55
|
+
only that sandbox. A new value keeps the scope;
|
|
56
|
+
--all-sandboxes gives it to every sandbox again
|
|
51
57
|
secrets set <NAME> --jobs [--host ...] A copy scheduled jobs put in their environment
|
|
52
58
|
(job create --secret NAME); with --host, both
|
|
59
|
+
secrets scope <NAME> --only-label k=v... | --only-sandbox <id>... | --all-sandboxes
|
|
60
|
+
Change which sandboxes get it; no value needed
|
|
53
61
|
secrets ls | get <NAME> Names, hosts, placeholders and jobs versions, never values
|
|
54
62
|
secrets rotate <NAME> A new version of the jobs copy, from standard input
|
|
55
63
|
secrets reveal <NAME> [--version N] Print the jobs copy's value. The sandboxes' copy is
|
|
@@ -143,6 +151,8 @@ const SANDBOX_HELP = `runtime sandboxes <command>
|
|
|
143
151
|
stop --label k=v [--label k=v]... Stop every live sandbox with all those labels
|
|
144
152
|
rm <id> Delete it for good: stops it, deletes its disk and
|
|
145
153
|
paused memory, revokes its previews; snapshots stay
|
|
154
|
+
rm --label k=v [--label k=v]... Delete for good every sandbox with all those labels,
|
|
155
|
+
stopped ones too
|
|
146
156
|
switch-image <id> <image> --keep-workspace Move it to another image, keeping /workspace (your home),
|
|
147
157
|
volumes, env and previews; processes restart, the rest of
|
|
148
158
|
its disk is lost (snapshot it first to keep everything)
|
|
@@ -242,7 +252,14 @@ commandAfter) {
|
|
|
242
252
|
break;
|
|
243
253
|
}
|
|
244
254
|
if (arg.startsWith("--") || (arg.startsWith("-") && arg.length === 2 && !/^-\d/.test(arg))) {
|
|
245
|
-
const [
|
|
255
|
+
const [typed, inline] = arg.replace(/^-+/, "").split(/=(.*)/s, 2);
|
|
256
|
+
// A plural of an option is the option once per comma: --hosts a,b is
|
|
257
|
+
// --host a --host b (13 October 2026: Marc's first try at a secret).
|
|
258
|
+
const plural = !known.has(typed) &&
|
|
259
|
+
typed.endsWith("s") &&
|
|
260
|
+
LISTS.has(typed.slice(0, -1)) &&
|
|
261
|
+
known.has(typed.slice(0, -1));
|
|
262
|
+
const name = plural ? typed.slice(0, -1) : typed;
|
|
246
263
|
if (!known.has(name))
|
|
247
264
|
throw commandAfter && !meantOption(name, known)
|
|
248
265
|
? usage(`Unknown option ${arg.split("=")[0]}. If it belongs to the command, put the command after --: ${commandAfter}`)
|
|
@@ -254,13 +271,16 @@ commandAfter) {
|
|
|
254
271
|
throw usage(`--${name} needs a value.`);
|
|
255
272
|
if (boolean && value !== "true" && value !== "false")
|
|
256
273
|
throw usage(`--${name} takes true or false.`);
|
|
257
|
-
|
|
274
|
+
const values = plural ? value.split(",").filter(Boolean) : [value];
|
|
275
|
+
flags.set(name, [...(flags.get(name) ?? []), ...values]);
|
|
258
276
|
}
|
|
259
277
|
else
|
|
260
278
|
positional.push(arg);
|
|
261
279
|
}
|
|
262
280
|
return { positional, flags, rest };
|
|
263
281
|
}
|
|
282
|
+
/** Options given once per item, which a plural takes as a comma list. */
|
|
283
|
+
const LISTS = new Set(["host", "allow", "deny", "connect"]);
|
|
264
284
|
/** The declared option a mistyped one most likely meant. --memory-mib for
|
|
265
285
|
* --memory, --timeout-seconds for --timeout: the SDK's spelling of an option is
|
|
266
286
|
* the CLI's with its unit added. */
|
|
@@ -706,11 +726,11 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
706
726
|
...(spend.pilot ? [pilotFunding(spend.pilot)] : []),
|
|
707
727
|
...(trialLeft > 0
|
|
708
728
|
? [
|
|
709
|
-
`
|
|
729
|
+
`free usage, ${trialLeft.toLocaleString("en-US", { maximumFractionDigits: 1 })} hours left without credit`,
|
|
710
730
|
]
|
|
711
731
|
: []),
|
|
712
732
|
...(credit > 0n ? [`${dollars(credit)} of credit`] : []),
|
|
713
|
-
].join("; ") || "no
|
|
733
|
+
].join("; ") || "no free usage or credit left: runtime billing";
|
|
714
734
|
print(table([
|
|
715
735
|
["organization", who.orgName ? `${who.orgName} (${who.orgId})` : who.orgId],
|
|
716
736
|
...(who.role ? [["role", who.role]] : []),
|
|
@@ -1049,7 +1069,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
1049
1069
|
}
|
|
1050
1070
|
throw unknown("command", command, COMMANDS);
|
|
1051
1071
|
}
|
|
1052
|
-
/** `runtime usage` for a person: the balance, the
|
|
1072
|
+
/** `runtime usage` for a person: the balance, the free usage and what each kind
|
|
1053
1073
|
* of resource was charged, in dollars. --json keeps every figure, and each
|
|
1054
1074
|
* resource with its rates and CPU time. */
|
|
1055
1075
|
/** One row per resource, for a spreadsheet: when it ran, how big it was, how
|
|
@@ -1104,15 +1124,15 @@ export function usageCsv(u) {
|
|
|
1104
1124
|
/** Milliseconds as hours, to a tenth: "8,731.2". */
|
|
1105
1125
|
const hours = (ms) => (ms / 3_600_000).toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
1106
1126
|
/** The machine time left without credit, as `runtime usage` and `runtime
|
|
1107
|
-
* limits` say it: this month's
|
|
1127
|
+
* limits` say it: this month's free usage, as hours at 2 vCPU and 4 GiB,
|
|
1108
1128
|
* or for an account given free hours before 5 October 2026, those hours too. */
|
|
1109
1129
|
function trialLeft(trial) {
|
|
1110
1130
|
const renews = trial.renewsAt ? `, renews ${trial.renewsAt.slice(0, 10)}` : "";
|
|
1111
1131
|
return trial.totalMs > 0
|
|
1112
|
-
? `${hours(trial.availableMs)} hours left (${hours(trial.totalMs)} free hours given before 5 October 2026, and the
|
|
1132
|
+
? `${hours(trial.availableMs)} hours left (${hours(trial.totalMs)} free hours given before 5 October 2026, and the free usage${renews})`
|
|
1113
1133
|
: `${hours(trial.availableMs)} hours at 2 vCPU and 4 GiB left this month${renews}`;
|
|
1114
1134
|
}
|
|
1115
|
-
/** This month's
|
|
1135
|
+
/** This month's free usage, one row per product, as `runtime usage`
|
|
1116
1136
|
* says it. */
|
|
1117
1137
|
function allowanceRows(allowances) {
|
|
1118
1138
|
const amount = (n) => n.toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
@@ -1598,6 +1618,28 @@ async function sandbox(argv, env, out) {
|
|
|
1598
1618
|
const use = async (id) => SANDBOX_UUID.test(id ?? "")
|
|
1599
1619
|
? new SandboxHandle(runtime.transport, { id })
|
|
1600
1620
|
: get(id);
|
|
1621
|
+
/* `stop --label` and `rm --label`: every sandbox with all the labels, each
|
|
1622
|
+
named with what happened to it; exits 1 if any failed. */
|
|
1623
|
+
const byLabel = (words) => words.some((word) => /^--label(=|$)/.test(word));
|
|
1624
|
+
const everyLabelled = async (act, words) => {
|
|
1625
|
+
const args = parse(words, [], ["label"]);
|
|
1626
|
+
if (args.positional.length)
|
|
1627
|
+
throw usage("Give a sandbox id or --label, not both.");
|
|
1628
|
+
const labels = pairs(args, "label");
|
|
1629
|
+
const { done, failed } = act === "stop"
|
|
1630
|
+
? await runtime.sandboxes.stopAll({ labels }).then((r) => ({ ...r, done: r.stopped }))
|
|
1631
|
+
: await runtime.sandboxes.deleteAll({ labels }).then((r) => ({ ...r, done: r.deleted }));
|
|
1632
|
+
const past = act === "stop" ? "stopped" : "deleted";
|
|
1633
|
+
const said = (error) => (error instanceof Error ? error.message : String(error));
|
|
1634
|
+
print([
|
|
1635
|
+
done.length || failed.length
|
|
1636
|
+
? `${done.length} ${past}${failed.length ? `, ${failed.length} failed` : ""}.`
|
|
1637
|
+
: `No ${act === "stop" ? "live " : ""}sandbox has those labels.`,
|
|
1638
|
+
...done.map((id) => `${id} ${past}`),
|
|
1639
|
+
...failed.map((f) => `${f.id} failed: ${said(f.error)}`),
|
|
1640
|
+
].join("\n"), { [past]: done, failed: failed.map((f) => ({ id: f.id, error: said(f.error) })) });
|
|
1641
|
+
return failed.length ? 1 : 0;
|
|
1642
|
+
};
|
|
1601
1643
|
switch (verb) {
|
|
1602
1644
|
case "create": {
|
|
1603
1645
|
const args = parse(rest, CREATE_FLAGS, CREATE_VALUES);
|
|
@@ -1681,6 +1723,8 @@ async function sandbox(argv, env, out) {
|
|
|
1681
1723
|
}
|
|
1682
1724
|
case "rm":
|
|
1683
1725
|
case "delete": {
|
|
1726
|
+
if (byLabel(rest))
|
|
1727
|
+
return everyLabelled("delete", rest);
|
|
1684
1728
|
const sbx = await use(need(rest[0], `a sandbox, e.g. ${me} sandboxes rm <id>`));
|
|
1685
1729
|
const gone = await sbx.delete();
|
|
1686
1730
|
print(`${gone.id} deleted`, gone);
|
|
@@ -1690,23 +1734,8 @@ async function sandbox(argv, env, out) {
|
|
|
1690
1734
|
case "pause":
|
|
1691
1735
|
case "wake":
|
|
1692
1736
|
case "restart": {
|
|
1693
|
-
if (verb === "stop" && rest
|
|
1694
|
-
|
|
1695
|
-
if (args.positional.length)
|
|
1696
|
-
throw usage("Give a sandbox id or --label, not both.");
|
|
1697
|
-
const { stopped, failed } = await runtime.sandboxes.stopAll({
|
|
1698
|
-
labels: pairs(args, "label"),
|
|
1699
|
-
});
|
|
1700
|
-
const said = (error) => (error instanceof Error ? error.message : String(error));
|
|
1701
|
-
print([
|
|
1702
|
-
stopped.length || failed.length
|
|
1703
|
-
? `${stopped.length} stopped${failed.length ? `, ${failed.length} failed` : ""}.`
|
|
1704
|
-
: "No live sandbox has those labels.",
|
|
1705
|
-
...stopped.map((id) => `${id} stopped`),
|
|
1706
|
-
...failed.map((f) => `${f.id} failed: ${said(f.error)}`),
|
|
1707
|
-
].join("\n"), { stopped, failed: failed.map((f) => ({ id: f.id, error: said(f.error) })) });
|
|
1708
|
-
return failed.length ? 1 : 0;
|
|
1709
|
-
}
|
|
1737
|
+
if (verb === "stop" && byLabel(rest))
|
|
1738
|
+
return everyLabelled("stop", rest);
|
|
1710
1739
|
const sbx = await get(rest[0]);
|
|
1711
1740
|
await sbx[verb]();
|
|
1712
1741
|
print(`${sbx.id} ${sbx.state}`, sbx.info);
|
|
@@ -2160,15 +2189,24 @@ function attachment(spec) {
|
|
|
2160
2189
|
/** A secret's value, from standard input only, so it never sits in shell
|
|
2161
2190
|
* history or a process list. Piped: everything, less one trailing newline.
|
|
2162
2191
|
* A terminal: one line, not shown. */
|
|
2192
|
+
/** A pasted secret as the header it becomes: no line breaks (a token copied
|
|
2193
|
+
* from a terminal arrives wrapped), no bracketed-paste markers, no space at
|
|
2194
|
+
* either end. 13 October 2026: Marc's pasted Claude token was refused as
|
|
2195
|
+
* "not visible ASCII". A header cannot hold a line break, so none is lost. */
|
|
2196
|
+
export function cleanSecret(raw) {
|
|
2197
|
+
return raw
|
|
2198
|
+
.replaceAll("\u001b[200~", "")
|
|
2199
|
+
.replaceAll("\u001b[201~", "")
|
|
2200
|
+
.replace(/[\r\n]/g, "")
|
|
2201
|
+
.trim();
|
|
2202
|
+
}
|
|
2163
2203
|
async function readSecret() {
|
|
2164
2204
|
const stdin = process.stdin;
|
|
2165
2205
|
if (!stdin.isTTY) {
|
|
2166
2206
|
const chunks = [];
|
|
2167
2207
|
for await (const chunk of stdin)
|
|
2168
2208
|
chunks.push(chunk);
|
|
2169
|
-
return Buffer.concat(chunks)
|
|
2170
|
-
.toString("utf8")
|
|
2171
|
-
.replace(/\r?\n$/, "");
|
|
2209
|
+
return cleanSecret(Buffer.concat(chunks).toString("utf8"));
|
|
2172
2210
|
}
|
|
2173
2211
|
process.stderr.write("Value (not shown), then Enter: ");
|
|
2174
2212
|
stdin.setRawMode(true);
|
|
@@ -2181,7 +2219,7 @@ async function readSecret() {
|
|
|
2181
2219
|
if (char === "\r" || char === "\n") {
|
|
2182
2220
|
stdin.off("data", onData);
|
|
2183
2221
|
process.stderr.write("\n");
|
|
2184
|
-
return done(value);
|
|
2222
|
+
return done(cleanSecret(value));
|
|
2185
2223
|
}
|
|
2186
2224
|
if (char === "\u0003") {
|
|
2187
2225
|
stdin.off("data", onData);
|
|
@@ -2224,6 +2262,48 @@ export function parseSecretRules(values) {
|
|
|
2224
2262
|
}
|
|
2225
2263
|
return [...byMethods.values()];
|
|
2226
2264
|
}
|
|
2265
|
+
/** `--only-label role=hub,tier=x --only-sandbox <id>`: a sandbox whose labels
|
|
2266
|
+
* hold every pair of any one --only-label gets the secret, and so does each
|
|
2267
|
+
* --only-sandbox. `--all-sandboxes`: every sandbox again. Nothing: undefined,
|
|
2268
|
+
* so a replacement keeps the scope it has. */
|
|
2269
|
+
export function parseSecretScope(labels, sandboxes, all) {
|
|
2270
|
+
if (all && (labels?.length || sandboxes?.length))
|
|
2271
|
+
throw usage("--all-sandboxes gives the secret to every sandbox; leave out --only-label and --only-sandbox.");
|
|
2272
|
+
if (all)
|
|
2273
|
+
return {};
|
|
2274
|
+
if (!labels?.length && !sandboxes?.length)
|
|
2275
|
+
return undefined;
|
|
2276
|
+
const selectors = (labels ?? []).map((text) => {
|
|
2277
|
+
const selector = {};
|
|
2278
|
+
for (const pair of text.split(",").filter(Boolean)) {
|
|
2279
|
+
const at = pair.indexOf("=");
|
|
2280
|
+
if (at < 1)
|
|
2281
|
+
throw usage(`--only-label takes key=value pairs, such as role=hub; got "${text}".`);
|
|
2282
|
+
selector[pair.slice(0, at).trim()] = pair.slice(at + 1).trim();
|
|
2283
|
+
}
|
|
2284
|
+
if (!Object.keys(selector).length)
|
|
2285
|
+
throw usage(`--only-label takes key=value pairs, such as role=hub; got "${text}".`);
|
|
2286
|
+
return selector;
|
|
2287
|
+
});
|
|
2288
|
+
for (const id of sandboxes ?? [])
|
|
2289
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id))
|
|
2290
|
+
throw usage(`--only-sandbox takes a sandbox id; "${id}" is not one.`);
|
|
2291
|
+
return {
|
|
2292
|
+
...(selectors.length ? { labels: selectors } : {}),
|
|
2293
|
+
...(sandboxes?.length
|
|
2294
|
+
? { sandboxes: [...new Set(sandboxes.map((id) => id.toLowerCase()))] }
|
|
2295
|
+
: {}),
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
/** Who gets a secret, in a few words. */
|
|
2299
|
+
export const scopeText = (only) => only && (only.labels?.length || only.sandboxes?.length)
|
|
2300
|
+
? [
|
|
2301
|
+
...(only.labels ?? []).map((selector) => Object.entries(selector)
|
|
2302
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
2303
|
+
.join(",")),
|
|
2304
|
+
...(only.sandboxes ?? []),
|
|
2305
|
+
].join(" | ")
|
|
2306
|
+
: "all";
|
|
2227
2307
|
const ruleText = (rules) => rules?.length
|
|
2228
2308
|
? rules
|
|
2229
2309
|
.map((rule) => `${rule.methods ? `${rule.methods.join(",")} ` : ""}${rule.paths.join(" ")}`)
|
|
@@ -2233,17 +2313,18 @@ export async function secretsCommand(argv, env, out,
|
|
|
2233
2313
|
/** Tests give the value here; the CLI reads standard input. */
|
|
2234
2314
|
read = readSecret) {
|
|
2235
2315
|
const [verb, ...rest] = argv;
|
|
2236
|
-
const args = parse(rest, ["jobs"], ["host", "header", "format", "allow", "value", "version"]);
|
|
2316
|
+
const args = parse(rest, ["jobs", "all-sandboxes"], ["host", "header", "format", "allow", "value", "version", "only-label", "only-sandbox"]);
|
|
2237
2317
|
const print = (human, value) => out.write(out.json ? JSON.stringify(value) : human);
|
|
2238
2318
|
const rows = (list) => list.length
|
|
2239
2319
|
? table([
|
|
2240
|
-
["name", "hosts", "header", "placeholder", "allow", "jobs"],
|
|
2320
|
+
["name", "hosts", "header", "placeholder", "allow", "sandboxes", "jobs"],
|
|
2241
2321
|
...list.map(({ name, sandboxes: secret, jobs }) => [
|
|
2242
2322
|
name,
|
|
2243
2323
|
secret ? secret.hosts.join(",") : "-",
|
|
2244
2324
|
secret?.header ? `${secret.header}: ${secret.format ?? "{value}"}` : "-",
|
|
2245
2325
|
secret ? secret.placeholder : "-",
|
|
2246
2326
|
secret ? ruleText(secret.rules) : "-",
|
|
2327
|
+
secret ? scopeText(secret.only) : "-",
|
|
2247
2328
|
jobs ? `v${jobs.version}` : "-",
|
|
2248
2329
|
]),
|
|
2249
2330
|
])
|
|
@@ -2266,7 +2347,13 @@ read = readSecret) {
|
|
|
2266
2347
|
const name = need(args.positional[0], "the secret's name, such as OPENAI_API_KEY");
|
|
2267
2348
|
const hosts = args.flags.get("host") ?? [];
|
|
2268
2349
|
const jobs = verb === "rotate" || has(args, "jobs");
|
|
2269
|
-
if (verb === "rotate" &&
|
|
2350
|
+
if (verb === "rotate" &&
|
|
2351
|
+
(hosts.length ||
|
|
2352
|
+
args.flags.has("header") ||
|
|
2353
|
+
args.flags.has("allow") ||
|
|
2354
|
+
args.flags.has("only-label") ||
|
|
2355
|
+
args.flags.has("only-sandbox") ||
|
|
2356
|
+
args.flags.has("all-sandboxes")))
|
|
2270
2357
|
throw usage(`rotate replaces the copy for jobs. ${jobsOnly}; to change the sandboxes' copy, set it again with --host.`);
|
|
2271
2358
|
if (!hosts.length && !jobs)
|
|
2272
2359
|
throw usage("Give --host once for each host the value may go to, such as --host api.openai.com, and/or --jobs to let scheduled jobs use it.");
|
|
@@ -2290,16 +2377,20 @@ read = readSecret) {
|
|
|
2290
2377
|
const format = flag(args, "format");
|
|
2291
2378
|
const allow = args.flags.get("allow");
|
|
2292
2379
|
const rules = allow ? parseSecretRules(allow) : undefined;
|
|
2380
|
+
const only = parseSecretScope(args.flags.get("only-label"), args.flags.get("only-sandbox"), has(args, "all-sandboxes"));
|
|
2293
2381
|
const saved = await runtime.secrets.set(name, {
|
|
2294
2382
|
value,
|
|
2295
2383
|
hosts,
|
|
2296
2384
|
...(header ? { header } : {}),
|
|
2297
2385
|
...(format ? { format } : {}),
|
|
2298
2386
|
...(rules ? { rules } : {}),
|
|
2387
|
+
...(only ? { only } : {}),
|
|
2299
2388
|
...(jobs ? { jobs: true } : {}),
|
|
2300
2389
|
});
|
|
2301
2390
|
print([
|
|
2302
|
-
|
|
2391
|
+
saved.only
|
|
2392
|
+
? `Stored ${saved.name}. Only these sandboxes get it and see ${saved.name}=${saved.placeholder}: ${scopeText(saved.only)}.`
|
|
2393
|
+
: `Stored ${saved.name}. Every sandbox sees ${saved.name}=${saved.placeholder}.`,
|
|
2303
2394
|
saved.header
|
|
2304
2395
|
? `Requests to ${saved.hosts.join(", ")} get ${saved.header}: ${saved.format ?? "{value}"} over HTTPS.`
|
|
2305
2396
|
: `Over HTTPS to ${saved.hosts.join(", ")}, the placeholder becomes the value in the Authorization header or another credential header (x-api-key, x-goog-api-key); in the URL it stays a placeholder.`,
|
|
@@ -2315,6 +2406,15 @@ read = readSecret) {
|
|
|
2315
2406
|
].join("\n"), saved);
|
|
2316
2407
|
return 0;
|
|
2317
2408
|
}
|
|
2409
|
+
if (verb === "scope") {
|
|
2410
|
+
const name = need(args.positional[0], "the secret's name");
|
|
2411
|
+
const only = parseSecretScope(args.flags.get("only-label"), args.flags.get("only-sandbox"), has(args, "all-sandboxes"));
|
|
2412
|
+
if (!only)
|
|
2413
|
+
throw usage("Say who gets it: --only-label key=value (once per selector), --only-sandbox <id>, or --all-sandboxes.");
|
|
2414
|
+
const saved = await (await client(env)).secrets.scope(name, only);
|
|
2415
|
+
print(`${saved.name}: ${saved.only ? `only ${scopeText(saved.only)}` : "every sandbox"}.${saved.enforced ? "" : " A host is catching up; running sandboxes have it within a minute."}`, saved);
|
|
2416
|
+
return 0;
|
|
2417
|
+
}
|
|
2318
2418
|
if (verb === "reveal") {
|
|
2319
2419
|
const name = need(args.positional[0], "the secret's name");
|
|
2320
2420
|
const version = flag(args, "version");
|
|
@@ -2330,7 +2430,7 @@ read = readSecret) {
|
|
|
2330
2430
|
print(`Deleted ${name}${removed.from.length > 1 ? " for sandboxes and jobs" : removed.from[0] === "jobs" ? " for jobs" : ""}.`, removed);
|
|
2331
2431
|
return 0;
|
|
2332
2432
|
}
|
|
2333
|
-
throw usage("secrets takes set, ls, get, rotate, reveal or rm. Run `runtime help`.");
|
|
2433
|
+
throw usage("secrets takes set, scope, ls, get, rotate, reveal or rm. Run `runtime help`.");
|
|
2334
2434
|
}
|
|
2335
2435
|
async function readInput(source) {
|
|
2336
2436
|
if (source !== "-")
|
|
@@ -2482,23 +2582,30 @@ async function browser(sbx, argv, out) {
|
|
|
2482
2582
|
}
|
|
2483
2583
|
const KEYS_HELP = `runtime keys <command>
|
|
2484
2584
|
|
|
2485
|
-
create [--name <name>] [--read-only] [--daily-limit <usd>] [--account-wide]
|
|
2486
|
-
A new API key
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
--
|
|
2491
|
-
|
|
2492
|
-
|
|
2585
|
+
create [--name <name>] [--read-only] [--daily-limit <usd>] [--account-wide] [--browser]
|
|
2586
|
+
A new API key. With a key saved on this machine (or RUNTIME_API_KEY), that
|
|
2587
|
+
key makes it at once, with no browser: a key per worker in an agent swarm.
|
|
2588
|
+
It never has more than the key that made it, and when that key has a daily
|
|
2589
|
+
limit, the keys it made share it, so each needs --daily-limit. Without a
|
|
2590
|
+
saved key, or with --browser, it prints a link and a code and an account
|
|
2591
|
+
owner approves the key in the browser. The key is printed once, alone on
|
|
2592
|
+
stdout, and lasts until revoked. --json prints it as JSON, still alone on
|
|
2593
|
+
stdout. --account-wide makes a key that sees and uses every sandbox and
|
|
2594
|
+
other resource in the account; without it, a key sees only what it makes.
|
|
2595
|
+
ls This key and the keys it made, with what each spent today
|
|
2596
|
+
usage <keyId> What one of them has spent and holds
|
|
2597
|
+
revoke <keyId> Revoke one: a worker's key when the worker is done
|
|
2493
2598
|
|
|
2494
|
-
runtime keys create --name
|
|
2599
|
+
runtime keys create --name worker-1 --daily-limit 5
|
|
2600
|
+
runtime keys create --name ci --daily-limit 25 --browser | gh secret set RUNTIME_API_KEY
|
|
2495
2601
|
|
|
2496
|
-
|
|
2602
|
+
Making, listing and revoking keys with a key needs one an owner or admin made.
|
|
2603
|
+
Every key is also at https://withruntime.com/account/keys.
|
|
2497
2604
|
`;
|
|
2498
|
-
/* `runtime keys
|
|
2499
|
-
browser
|
|
2500
|
-
|
|
2501
|
-
|
|
2605
|
+
/* `runtime keys`: a key made from the terminal, by this machine's key with no
|
|
2606
|
+
browser (Marc, 14 October 2026, agent swarms), or approved in the browser
|
|
2607
|
+
(Marc, 23 September 2026; ARCHITECTURE.md section 10, "Keys from the
|
|
2608
|
+
terminal"). Only the options the keys page offers. */
|
|
2502
2609
|
/* `runtime account`: the accounts this machine is connected to, and which one
|
|
2503
2610
|
every command uses (ARCHITECTURE.md section 3.9). Each is its own saved
|
|
2504
2611
|
connection with its own key; the SDK itself stays key-based. */
|
|
@@ -2604,19 +2711,63 @@ async function nameSaved(env) {
|
|
|
2604
2711
|
await store.save({ ...again, orgName: who.orgName });
|
|
2605
2712
|
}
|
|
2606
2713
|
}
|
|
2714
|
+
/** Dollars from integer microdollars, for a person to read: "$5.00". */
|
|
2715
|
+
const usdOf = (micros) => `$${(Number(BigInt(micros) / 10000n) / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
2607
2716
|
async function keys(argv, env, out) {
|
|
2608
2717
|
product = "keys";
|
|
2609
2718
|
const [verb, ...rest] = argv;
|
|
2719
|
+
const print = (human, value) => out.write(out.json ? JSON.stringify(value) : human);
|
|
2610
2720
|
if (!verb || verb === "help" || verb === "--help" || verb === "-h") {
|
|
2611
2721
|
out.write(out.json ? JSON.stringify({ usage: named(KEYS_HELP) }) : named(KEYS_HELP));
|
|
2612
2722
|
return 0;
|
|
2613
2723
|
}
|
|
2614
|
-
if (verb === "ls" || verb === "list"
|
|
2615
|
-
|
|
2724
|
+
if (verb === "ls" || verb === "list") {
|
|
2725
|
+
const args = parse(rest, [], []);
|
|
2726
|
+
if (args.flags.size || args.positional.length)
|
|
2727
|
+
throw usage(`Usage: ${named("runtime keys ls")}`);
|
|
2728
|
+
const all = await (await client(env)).keys.list();
|
|
2729
|
+
print(all.length
|
|
2730
|
+
? table([
|
|
2731
|
+
["KEY", "NAME", "ACCESS", "REACH", "DAILY LIMIT", "LAST 24H", "MADE BY", "STATE"],
|
|
2732
|
+
...all.map((k) => [
|
|
2733
|
+
k.keyId,
|
|
2734
|
+
k.name,
|
|
2735
|
+
k.access,
|
|
2736
|
+
k.reach,
|
|
2737
|
+
k.dailyLimitMicros === null ? "none" : usdOf(k.dailyLimitMicros),
|
|
2738
|
+
usdOf(k.dailyUsedMicros),
|
|
2739
|
+
k.madeBy ?? "website",
|
|
2740
|
+
k.revokedAt ? "revoked" : "live",
|
|
2741
|
+
]),
|
|
2742
|
+
])
|
|
2743
|
+
: "No keys.", all);
|
|
2744
|
+
return 0;
|
|
2745
|
+
}
|
|
2746
|
+
if (verb === "revoke" || verb === "rm" || verb === "usage") {
|
|
2747
|
+
const args = parse(rest, [], []);
|
|
2748
|
+
const id = args.positional[0];
|
|
2749
|
+
if (!id || args.positional.length > 1 || args.flags.size)
|
|
2750
|
+
throw usage(`Usage: ${named(`runtime keys ${verb} <keyId>`)}`);
|
|
2751
|
+
const api = (await client(env)).keys;
|
|
2752
|
+
if (verb === "usage") {
|
|
2753
|
+
const used = await api.usage(id);
|
|
2754
|
+
print([
|
|
2755
|
+
`${used.name} (${used.keyId})${used.revokedAt ? ", revoked" : ""}`,
|
|
2756
|
+
` last 24 hours ${usdOf(used.dailyUsedMicros)}${used.dailyLimitMicros === null ? "" : ` of ${usdOf(used.dailyLimitMicros)}`}`,
|
|
2757
|
+
` spent ${usdOf(used.spentMicros)}`,
|
|
2758
|
+
` held now ${usdOf(used.heldMicros)}`,
|
|
2759
|
+
` sandboxes ${used.runningSandboxes} running, ${used.resources} kept in all`,
|
|
2760
|
+
].join("\n"), used);
|
|
2761
|
+
return 0;
|
|
2762
|
+
}
|
|
2763
|
+
const revoked = await api.revoke(id);
|
|
2764
|
+
print(`Revoked ${revoked.keyId}. It stops working within ten seconds.`, revoked);
|
|
2765
|
+
return 0;
|
|
2766
|
+
}
|
|
2616
2767
|
if (verb !== "create")
|
|
2617
|
-
throw unknown("keys command", verb, ["create"]);
|
|
2618
|
-
const args = parse(rest, ["read-only", "account-wide"], ["name", "daily-limit"]);
|
|
2619
|
-
const known = new Set(["name", "read-only", "daily-limit", "account-wide"]);
|
|
2768
|
+
throw unknown("keys command", verb, ["create", "ls", "usage", "revoke"]);
|
|
2769
|
+
const args = parse(rest, ["read-only", "account-wide", "browser"], ["name", "daily-limit"]);
|
|
2770
|
+
const known = new Set(["name", "read-only", "daily-limit", "account-wide", "browser"]);
|
|
2620
2771
|
for (const name of args.flags.keys())
|
|
2621
2772
|
if (!known.has(name))
|
|
2622
2773
|
throw usage(`Unknown option --${name}.`);
|
|
@@ -2629,26 +2780,48 @@ async function keys(argv, env, out) {
|
|
|
2629
2780
|
if (readOnly && has(args, "account-wide"))
|
|
2630
2781
|
throw usage("A read-only key already sees the whole account, so it takes no --account-wide.");
|
|
2631
2782
|
const { createKey, dailyLimitMicros } = await import("./keys.js");
|
|
2632
|
-
const
|
|
2783
|
+
const request = {
|
|
2633
2784
|
name: flag(args, "name") ?? (readOnly ? "Read-only key" : "My API key"),
|
|
2634
2785
|
access: readOnly ? "read" : "full",
|
|
2635
2786
|
...(has(args, "account-wide") ? { reach: "account" } : {}),
|
|
2636
2787
|
dailyLimitMicros: limit === undefined ? null : dailyLimitMicros(limit),
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
|
|
2788
|
+
};
|
|
2789
|
+
/* A key on this machine makes the new one itself, with no browser: an
|
|
2790
|
+
agent with no person, a swarm's coordinator (Marc, 14 October 2026). */
|
|
2791
|
+
const saved = has(args, "browser")
|
|
2792
|
+
? null
|
|
2793
|
+
: env.RUNTIME_API_KEY || (await connectionStore(env).read())?.key || null;
|
|
2794
|
+
let key;
|
|
2795
|
+
if (saved) {
|
|
2796
|
+
key = await new Runtime({
|
|
2797
|
+
apiKey: saved,
|
|
2798
|
+
baseUrl: env.RUNTIME_API_URL,
|
|
2799
|
+
maxRetries: 4,
|
|
2800
|
+
}).keys.create({
|
|
2801
|
+
name: request.name,
|
|
2802
|
+
access: request.access,
|
|
2803
|
+
reach: request.reach ?? "agent",
|
|
2804
|
+
dailyLimitMicros: request.dailyLimitMicros,
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
else
|
|
2808
|
+
key = await createKey(request, env, { notify: out.error });
|
|
2809
|
+
const { confirm, ...made } = key;
|
|
2810
|
+
out.write(out.json ? JSON.stringify(made) : made.key);
|
|
2640
2811
|
const terms = [
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2812
|
+
made.access === "read" ? "read only" : "full access",
|
|
2813
|
+
made.reach === "account" ? "account-wide" : undefined,
|
|
2814
|
+
made.access === "read"
|
|
2644
2815
|
? undefined
|
|
2645
|
-
:
|
|
2816
|
+
: made.dailyLimitMicros === null
|
|
2646
2817
|
? "no daily limit"
|
|
2647
|
-
: `daily limit
|
|
2648
|
-
"until revoked",
|
|
2818
|
+
: `daily limit ${usdOf(made.dailyLimitMicros)}`,
|
|
2819
|
+
made.expires ? `until ${made.expires}` : "until revoked",
|
|
2649
2820
|
].filter(Boolean);
|
|
2650
|
-
out.error(
|
|
2651
|
-
|
|
2821
|
+
out.error(saved
|
|
2822
|
+
? `Created key "${made.name}" (${terms.join(", ")}), made by this machine's key. Store it now: it is not shown again, and Runtime keeps only a hash of it.\nGive it to one worker as RUNTIME_API_KEY; revoke it with ${named("runtime keys revoke")} ${made.keyId} when that worker is done.`
|
|
2823
|
+
: `Created key "${made.name}" (${terms.join(", ")}). Store it now: it is not shown again, and Runtime keeps only a hash of it.\nIn CI, give it to your jobs as RUNTIME_API_KEY from the secret store.`);
|
|
2824
|
+
await confirm?.();
|
|
2652
2825
|
return 0;
|
|
2653
2826
|
}
|
|
2654
2827
|
const SNAPSHOT_HELP = `runtime snapshots <command>
|
|
@@ -2976,7 +3149,7 @@ read = readSecret) {
|
|
|
2976
3149
|
const JOB_HELP = `runtime jobs <command>
|
|
2977
3150
|
|
|
2978
3151
|
A job runs a command in a fresh sandbox, once or on a schedule. Each run spends
|
|
2979
|
-
the
|
|
3152
|
+
the free usage first, then credit at the sandbox rates.
|
|
2980
3153
|
|
|
2981
3154
|
create <name> (--cron "<min hour day month weekday>" [--timezone Europe/Berlin] | --at <time|now>)
|
|
2982
3155
|
[--vcpu 2] [--memory 4096] [--disk 4096] [--cpu shared|reserved] [--cpu-floor <thousandths>]
|
package/dist/credentials.d.ts
CHANGED
|
@@ -26,6 +26,9 @@ export declare function connectionOrigins(env: NodeJS.ProcessEnv): {
|
|
|
26
26
|
/** Browser-issued production credentials must only reach the production API.
|
|
27
27
|
* CLI and MCP share this boundary, including resumed device requests. */
|
|
28
28
|
export declare function checkBrowserOrigins(origins: ReturnType<typeof connectionOrigins>): void;
|
|
29
|
+
/** A wallet account's key, from its claim, saved as this machine's connection
|
|
30
|
+
* (the MCP bridge's runtime_wallet_claim; `billing claim` saves the same). */
|
|
31
|
+
export declare function saveWalletConnection(env: NodeJS.ProcessEnv, key: string, orgId: string): Promise<void>;
|
|
29
32
|
/** Private user storage, outside project files. Nothing reading this module
|
|
30
33
|
* returns a secret to CLI output. Store names bind credentials to both origins. */
|
|
31
34
|
export declare function connectionStore(env: NodeJS.ProcessEnv, directory?: string): {
|
package/dist/credentials.js
CHANGED
|
@@ -37,6 +37,21 @@ export function checkBrowserOrigins(origins) {
|
|
|
37
37
|
(local(origins.auth) && local(origins.api))))
|
|
38
38
|
throw new Error("Browser login supports Runtime's production endpoints or local test endpoints. Check RUNTIME_AUTH_URL and RUNTIME_API_URL.");
|
|
39
39
|
}
|
|
40
|
+
/** A wallet account's key, from its claim, saved as this machine's connection
|
|
41
|
+
* (the MCP bridge's runtime_wallet_claim; `billing claim` saves the same). */
|
|
42
|
+
export function saveWalletConnection(env, key, orgId) {
|
|
43
|
+
const origins = connectionOrigins(env);
|
|
44
|
+
checkBrowserOrigins(origins);
|
|
45
|
+
return connectionStore(env).save({
|
|
46
|
+
version: 1,
|
|
47
|
+
apiOrigin: origins.api,
|
|
48
|
+
authOrigin: origins.auth,
|
|
49
|
+
key,
|
|
50
|
+
connectionId: "pasted",
|
|
51
|
+
orgId,
|
|
52
|
+
agentName: "Wallet agent",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
40
55
|
function defaultDirectory(env) {
|
|
41
56
|
const root = env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
|
|
42
57
|
if (!isAbsolute(root))
|
|
@@ -183,7 +183,7 @@ export declare class Daytona implements AsyncDisposable {
|
|
|
183
183
|
[Symbol.asyncDispose](): Promise<void>;
|
|
184
184
|
/** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
|
|
185
185
|
* vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
|
|
186
|
-
* is left to Runtime: the
|
|
186
|
+
* is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
|
|
187
187
|
create(params?: CreateSandboxFromSnapshotParams | CreateSandboxFromImageParams, options?: {
|
|
188
188
|
timeout?: number;
|
|
189
189
|
onSnapshotCreateLogs?: (chunk: string) => void;
|
package/dist/daytona/daytona.js
CHANGED
|
@@ -184,7 +184,7 @@ export class Daytona {
|
|
|
184
184
|
async [Symbol.asyncDispose]() { }
|
|
185
185
|
/** Creates a sandbox and waits until it runs, with Daytona's defaults: 1
|
|
186
186
|
* vCPU, 1 GiB, 3 GiB disk, pausing after 15 minutes without calls. Funding
|
|
187
|
-
* is left to Runtime: the
|
|
187
|
+
* is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
|
|
188
188
|
async create(params = {}, options = {}) {
|
|
189
189
|
refuseCreate(params);
|
|
190
190
|
const timeout = options.timeout;
|
package/dist/e2b/sandbox.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export declare class Sandbox {
|
|
|
133
133
|
protected get client(): Runtime;
|
|
134
134
|
/** Creates a sandbox from `template` (default "base", Runtime's stock image)
|
|
135
135
|
* with E2B's default machine, 2 vCPU and 512 MiB, and waits until it runs.
|
|
136
|
-
* Funding is left to Runtime: the
|
|
136
|
+
* Funding is left to Runtime: the free usage first, then prepaid credit, exactly as withruntime's own create. */
|
|
137
137
|
static create<S extends typeof Sandbox>(this: S, opts?: SandboxOpts): Promise<InstanceType<S>>;
|
|
138
138
|
static create<S extends typeof Sandbox>(this: S, template: string, opts?: SandboxOpts): Promise<InstanceType<S>>;
|
|
139
139
|
/** Connects to a sandbox by id, waking it if it is paused, as E2B does. A
|
package/dist/errors.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const DELIBERATE: ReadonlySet<string>;
|
|
|
13
13
|
* minutes when it has none), however many retries it allows otherwise. */
|
|
14
14
|
export declare const NOTHING_RAN: ReadonlySet<string>;
|
|
15
15
|
/** Refusals of a create that clear when a sandbox stops or pauses, or a host
|
|
16
|
-
* frees room: an account without credit's
|
|
16
|
+
* frees room: an account without credit's four at once and its total, an
|
|
17
17
|
* email domain's, the account's quota and the region's capacity. `sandboxes.create` waits them out, retrying
|
|
18
18
|
* with the same key and input, for up to `waitForCapacityMs`. */
|
|
19
19
|
export declare const WAITS_FOR_ROOM: ReadonlySet<string>;
|
package/dist/errors.js
CHANGED
|
@@ -27,7 +27,7 @@ export const NOTHING_RAN = new Set(["guest_busy", "busy", "rate_limited"]);
|
|
|
27
27
|
* account without credit stops. */
|
|
28
28
|
const PASSING = new Set(["no_capacity", "no_credit_running_limit", "no_credit_total_limit"]);
|
|
29
29
|
/** Refusals of a create that clear when a sandbox stops or pauses, or a host
|
|
30
|
-
* frees room: an account without credit's
|
|
30
|
+
* frees room: an account without credit's four at once and its total, an
|
|
31
31
|
* email domain's, the account's quota and the region's capacity. `sandboxes.create` waits them out, retrying
|
|
32
32
|
* with the same key and input, for up to `waitForCapacityMs`. */
|
|
33
33
|
export const WAITS_FOR_ROOM = new Set([
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { Page } from "./page.js";
|
|
|
16
16
|
export type { KeyLimits } from "./products/limits.js";
|
|
17
17
|
export type { AuditEvent, AuditPage } from "./products/audit.js";
|
|
18
18
|
export type { UsageExportQuery, UsageExportRow, UsageExportPage, } from "./products/observability.js";
|
|
19
|
-
export type { JobSecret, Secret, SecretRule, SecretUse, SetSecret } from "./products/secrets.js";
|
|
19
|
+
export type { JobSecret, Secret, SecretRule, SecretScope, SecretUse, SetSecret, } from "./products/secrets.js";
|
|
20
20
|
export { SecretPartlyStoredError } from "./products/secrets.js";
|
|
21
21
|
export type { McpCatalogEntry, McpGateway, McpServerRequest } from "./products/mcp.js";
|
|
22
22
|
export type { FileEvent, FileEventType, WatchHandle, WatchNotice, WatchOptions, } from "./products/watch.js";
|
|
@@ -74,4 +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
78
|
export type { SsoConnection, SsoStatus } from "./products/sso.js";
|