withruntime 0.9.0 → 0.10.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/BLAXEL.md +4 -4
- package/DAYTONA.md +13 -7
- package/E2B.md +12 -8
- package/README.md +15 -7
- package/README.src.md +15 -7
- package/VERCEL.md +13 -3
- package/compatibility-lock.json +84 -8
- package/dist/blaxel/errors.js +1 -1
- package/dist/blaxel/lookup.d.ts +3 -2
- package/dist/blaxel/lookup.js +3 -2
- package/dist/blaxel/pagination.d.ts +2 -2
- package/dist/blaxel/pagination.js +45 -17
- package/dist/blaxel/preview.d.ts +2 -1
- package/dist/blaxel/preview.js +15 -13
- package/dist/blaxel/process.js +56 -10
- package/dist/blaxel/sandbox.d.ts +2 -0
- package/dist/blaxel/sandbox.js +2 -0
- package/dist/blaxel/snapshot.js +3 -5
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +189 -54
- package/dist/client.d.ts +29 -3
- package/dist/client.js +40 -11
- package/dist/cloudflare/identity.d.ts +7 -0
- package/dist/cloudflare/identity.js +13 -0
- package/dist/cloudflare/index.d.ts +9 -0
- package/dist/cloudflare/index.js +47 -9
- package/dist/cloudflare/worker.d.ts +4 -0
- package/dist/cloudflare/worker.js +166 -16
- package/dist/codesandbox/guest-service.js +57 -20
- package/dist/codesandbox/index.js +42 -8
- package/dist/compat/core.d.ts +9 -8
- package/dist/compat/core.js +60 -21
- package/dist/compat/files.js +22 -4
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +107 -38
- package/dist/daytona/daytona.js +11 -4
- package/dist/daytona/filesystem.d.ts +17 -4
- package/dist/daytona/filesystem.js +121 -38
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +1 -1
- package/dist/daytona/sandbox.js +16 -7
- package/dist/e2b/client.js +1 -1
- package/dist/e2b/commands.d.ts +30 -8
- package/dist/e2b/commands.js +142 -54
- package/dist/e2b/index.d.ts +1 -0
- package/dist/e2b/index.js +1 -0
- package/dist/e2b/pty.d.ts +24 -0
- package/dist/e2b/pty.js +80 -0
- package/dist/e2b/sandbox.d.ts +2 -1
- package/dist/e2b/sandbox.js +49 -15
- package/dist/file-source.d.ts +16 -0
- package/dist/file-source.js +162 -0
- package/dist/freestyle/index.d.ts +9 -1
- package/dist/freestyle/index.js +38 -5
- package/dist/index.d.ts +2 -1
- package/dist/login.js +16 -10
- package/dist/modal/index.js +2 -1
- package/dist/network-cli.js +111 -43
- package/dist/openai-agents/index.js +9 -45
- package/dist/page.d.ts +1 -1
- package/dist/page.js +2 -2
- package/dist/products/index.d.ts +12 -0
- package/dist/products/index.js +10 -0
- package/dist/products/observability.d.ts +44 -0
- package/dist/products/observability.js +6 -0
- package/dist/products/previews.d.ts +8 -2
- package/dist/products/previews.js +11 -3
- package/dist/products/volumes.d.ts +42 -0
- package/dist/products/volumes.js +36 -0
- package/dist/products/watch.js +61 -8
- package/dist/proxy.js +3 -1
- package/dist/runloop/index.d.ts +13 -27
- package/dist/runloop/index.js +149 -52
- package/dist/sandbox.d.ts +37 -8
- package/dist/sandbox.js +432 -68
- package/dist/snapshots.d.ts +9 -4
- package/dist/sprites/index.js +69 -33
- package/dist/tar.d.ts +3 -1
- package/dist/tar.js +140 -14
- package/dist/transport.d.ts +4 -7
- package/dist/transport.js +205 -122
- package/dist/vercel/command.d.ts +1 -1
- package/dist/vercel/command.js +3 -2
- package/dist/vercel/errors.d.ts +1 -1
- package/dist/vercel/errors.js +7 -1
- package/dist/vercel/sandbox.d.ts +8 -8
- package/dist/vercel/sandbox.js +154 -59
- package/dist/vercel/snapshot.d.ts +3 -2
- package/dist/vercel/snapshot.js +9 -5
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
|
+
import { once } from "node:events";
|
|
3
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
5
|
import { dirname, join, resolve } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { connectionOrigins, connectionStore, resolveCredential } from "./credentials.js";
|
|
7
|
+
import { checkBrowserOrigins, connectionOrigins, connectionStore, resolveCredential, } from "./credentials.js";
|
|
7
8
|
import { me, named } from "./cli-name.js";
|
|
8
9
|
import { Runtime } from "./client.js";
|
|
9
10
|
import { Sandbox as SandboxHandle } from "./sandbox.js";
|
|
@@ -36,6 +37,8 @@ Account
|
|
|
36
37
|
sso Single sign-on and SCIM directory sync (owner or
|
|
37
38
|
admin keys); an owner changes it on the website
|
|
38
39
|
usage [--csv] Balance, trial time and charges; --csv, each resource
|
|
40
|
+
usage export --since <UTC time> --until <UTC time>
|
|
41
|
+
Every settlement in the range as CSV; --json, each page
|
|
39
42
|
limits Whether this key is read-only, and its daily spending limit
|
|
40
43
|
secrets set <NAME> --host <host>... [--header <name> [--format '<text {value}>']]
|
|
41
44
|
[--allow '[GET,HEAD] /path/*']... Store a secret sandboxes use without seeing it; the
|
|
@@ -180,8 +183,8 @@ const SANDBOX_HELP = `runtime sandbox <command>
|
|
|
180
183
|
|
|
181
184
|
Examples
|
|
182
185
|
id=$(runtime sandbox create)
|
|
183
|
-
runtime sandbox
|
|
184
|
-
runtime sandbox
|
|
186
|
+
runtime sandbox cp ./invoice.py $id:/workspace/invoice.py
|
|
187
|
+
runtime sandbox exec $id -- python3 /workspace/invoice.py
|
|
185
188
|
runtime sandbox stop $id
|
|
186
189
|
`;
|
|
187
190
|
/** A command's options. Reading one its parse() did not declare is a bug in
|
|
@@ -229,9 +232,13 @@ commandAfter) {
|
|
|
229
232
|
throw commandAfter && !meantOption(name, known)
|
|
230
233
|
? usage(`Unknown option ${arg.split("=")[0]}. If it belongs to the command, put the command after --: ${commandAfter}`)
|
|
231
234
|
: unknownOption(name, known);
|
|
232
|
-
const
|
|
233
|
-
|
|
235
|
+
const boolean = booleans.includes(name);
|
|
236
|
+
const value = inline ?? (boolean ? "true" : argv[++i]);
|
|
237
|
+
if (value === undefined ||
|
|
238
|
+
(!boolean && inline === undefined && (value === "--" || /^--?[^\d]/.test(value))))
|
|
234
239
|
throw usage(`--${name} needs a value.`);
|
|
240
|
+
if (boolean && value !== "true" && value !== "false")
|
|
241
|
+
throw usage(`--${name} takes true or false.`);
|
|
235
242
|
flags.set(name, [...(flags.get(name) ?? []), value]);
|
|
236
243
|
}
|
|
237
244
|
else
|
|
@@ -263,7 +270,10 @@ const integer = (args, name) => {
|
|
|
263
270
|
return undefined;
|
|
264
271
|
if (!/^\d+$/.test(value))
|
|
265
272
|
throw usage(`--${name} must be a whole number.`);
|
|
266
|
-
|
|
273
|
+
const parsed = Number(value);
|
|
274
|
+
if (!Number.isSafeInteger(parsed))
|
|
275
|
+
throw usage(`--${name} is too large to represent exactly.`);
|
|
276
|
+
return parsed;
|
|
267
277
|
};
|
|
268
278
|
/** A flag that takes one of a few words, checked before anything is sent. */
|
|
269
279
|
function oneOf(args, name, choices) {
|
|
@@ -575,8 +585,8 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
575
585
|
? JSON.stringify({ connected, usage: HELP })
|
|
576
586
|
: `Runtime Cloud ${VERSION}. ${start}
|
|
577
587
|
|
|
578
|
-
${me} sandbox run -- python3
|
|
579
|
-
|
|
588
|
+
${me} sandbox run -- python3 < invoice.py
|
|
589
|
+
Run a local invoice script, then stop the sandbox
|
|
580
590
|
${me} sandbox create A sandbox that stays; prints its id
|
|
581
591
|
${me} sandbox exec <id> -- <command> Run in it
|
|
582
592
|
${me} help Everything else
|
|
@@ -588,10 +598,10 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
588
598
|
return 0;
|
|
589
599
|
}
|
|
590
600
|
if (command === "run") {
|
|
591
|
-
const args = parse(rest, [...CREATE_FLAGS, "keep"], [...CREATE_VALUES, "cwd", "env"], `${me} sandbox run -- python3
|
|
601
|
+
const args = parse(rest, [...CREATE_FLAGS, "keep"], [...CREATE_VALUES, "cwd", "env"], `${me} sandbox run -- python3 < invoice.py`);
|
|
592
602
|
const commandLine = args.rest ?? args.positional;
|
|
593
603
|
if (!commandLine.length)
|
|
594
|
-
throw usage(`Give the command after --: ${me} sandbox run -- python3
|
|
604
|
+
throw usage(`Give the command after --: ${me} sandbox run -- python3 < invoice.py`);
|
|
595
605
|
const runtime = await client(env);
|
|
596
606
|
const timeout = integer(args, "timeout");
|
|
597
607
|
const { timeoutSeconds: _lease, ...input } = createInput(args);
|
|
@@ -688,6 +698,40 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
688
698
|
return 0;
|
|
689
699
|
}
|
|
690
700
|
if (command === "usage") {
|
|
701
|
+
if (rest[0] === "export") {
|
|
702
|
+
const args = parse(rest.slice(1), [], ["since", "until"]);
|
|
703
|
+
if (args.positional.length || args.rest)
|
|
704
|
+
throw usage(`Use ${me} usage export --since <UTC time> --until <UTC time>.`);
|
|
705
|
+
const since = need(flag(args, "since"), "--since <UTC time>, such as 2026-09-01T00:00:00Z");
|
|
706
|
+
const until = need(flag(args, "until"), "--until <UTC time>, such as 2026-10-01T00:00:00Z");
|
|
707
|
+
const rt = await client(env);
|
|
708
|
+
const cursors = new Set();
|
|
709
|
+
let cursor;
|
|
710
|
+
for (;;) {
|
|
711
|
+
const page = await rt.usageExport({
|
|
712
|
+
since,
|
|
713
|
+
until,
|
|
714
|
+
...(cursor ? { cursor } : {}),
|
|
715
|
+
limit: 500,
|
|
716
|
+
});
|
|
717
|
+
if (page.nextCursor !== null && cursors.has(page.nextCursor))
|
|
718
|
+
throw new RuntimeError({
|
|
719
|
+
code: "usage_export_incomplete",
|
|
720
|
+
status: 0,
|
|
721
|
+
message: "The usage export repeated a page cursor and could not finish.",
|
|
722
|
+
hint: "Run the export again; do not treat the partial output as complete.",
|
|
723
|
+
});
|
|
724
|
+
if (out.json)
|
|
725
|
+
out.write(JSON.stringify(page));
|
|
726
|
+
else if (page.csv)
|
|
727
|
+
out.write(page.csv);
|
|
728
|
+
await out.drain?.();
|
|
729
|
+
if (page.nextCursor === null)
|
|
730
|
+
return 0;
|
|
731
|
+
cursors.add(page.nextCursor);
|
|
732
|
+
cursor = page.nextCursor;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
691
735
|
const csv = rest.includes("--csv");
|
|
692
736
|
const unknown = rest.find((arg) => arg !== "--csv");
|
|
693
737
|
if (unknown)
|
|
@@ -738,7 +782,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
738
782
|
const page = await rt.events.list({
|
|
739
783
|
...(sandbox ? { resourceId: await sandboxIdOf(rt, sandbox) } : {}),
|
|
740
784
|
...(flag(args, "type") ? { type: flag(args, "type") } : {}),
|
|
741
|
-
...(integer(args, "limit") ? { limit: integer(args, "limit") } : {}),
|
|
785
|
+
...(integer(args, "limit") !== undefined ? { limit: integer(args, "limit") } : {}),
|
|
742
786
|
});
|
|
743
787
|
print(page.data.length
|
|
744
788
|
? table(page.data.map((e) => {
|
|
@@ -783,7 +827,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
783
827
|
throw usage(`Unknown option --${name}.`);
|
|
784
828
|
const page = await (await client(env)).audit.list({
|
|
785
829
|
...(flag(args, "action") ? { action: flag(args, "action") } : {}),
|
|
786
|
-
...(integer(args, "limit") ? { limit: integer(args, "limit") } : {}),
|
|
830
|
+
...(integer(args, "limit") !== undefined ? { limit: integer(args, "limit") } : {}),
|
|
787
831
|
...(flag(args, "before") ? { before: flag(args, "before") } : {}),
|
|
788
832
|
});
|
|
789
833
|
print(page.events.length
|
|
@@ -809,17 +853,26 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
809
853
|
const origins = connectionOrigins(env);
|
|
810
854
|
const { claimWalletAccount, openWalletAccount } = await import("./wallet.js");
|
|
811
855
|
return billingCommand(rest, parse(rest, ["wait", "new-account", "accept-terms"], []), out, () => client(env), undefined, {
|
|
812
|
-
open: (input) =>
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
856
|
+
open: (input) => {
|
|
857
|
+
checkBrowserOrigins(origins);
|
|
858
|
+
return openWalletAccount(input, { authUrl: origins.auth });
|
|
859
|
+
},
|
|
860
|
+
claim: (code) => {
|
|
861
|
+
checkBrowserOrigins(origins);
|
|
862
|
+
return claimWalletAccount(code, { authUrl: origins.auth });
|
|
863
|
+
},
|
|
864
|
+
save: (key, orgId) => {
|
|
865
|
+
checkBrowserOrigins(origins);
|
|
866
|
+
return connectionStore(env).save({
|
|
867
|
+
version: 1,
|
|
868
|
+
apiOrigin: origins.api,
|
|
869
|
+
authOrigin: origins.auth,
|
|
870
|
+
key,
|
|
871
|
+
connectionId: "pasted",
|
|
872
|
+
orgId,
|
|
873
|
+
agentName: "Wallet agent",
|
|
874
|
+
});
|
|
875
|
+
},
|
|
823
876
|
});
|
|
824
877
|
}
|
|
825
878
|
if (command === "referrals") {
|
|
@@ -1536,7 +1589,9 @@ async function sandbox(argv, env, out) {
|
|
|
1536
1589
|
const sbx = await get(args.positional[0]);
|
|
1537
1590
|
const snapshot = await sbx.snapshot({
|
|
1538
1591
|
...(flag(args, "name") ? { name: flag(args, "name") } : {}),
|
|
1539
|
-
...(integer(args, "retention")
|
|
1592
|
+
...(integer(args, "retention") !== undefined
|
|
1593
|
+
? { retentionDays: integer(args, "retention") }
|
|
1594
|
+
: {}),
|
|
1540
1595
|
});
|
|
1541
1596
|
print(snapshot.id, snapshot);
|
|
1542
1597
|
return 0;
|
|
@@ -1596,7 +1651,7 @@ async function sandbox(argv, env, out) {
|
|
|
1596
1651
|
...(cost === undefined
|
|
1597
1652
|
? {}
|
|
1598
1653
|
: {
|
|
1599
|
-
maxTotalCostMicros: cost === "none" ? null :
|
|
1654
|
+
maxTotalCostMicros: cost === "none" ? null : usdMicros(args, "max-total-cost"),
|
|
1600
1655
|
}),
|
|
1601
1656
|
};
|
|
1602
1657
|
if (!Object.keys(settings).length)
|
|
@@ -1709,7 +1764,7 @@ async function sandbox(argv, env, out) {
|
|
|
1709
1764
|
const args = parse(rest, [], ["depth", "glob"]);
|
|
1710
1765
|
const sbx = await use(args.positional[0]);
|
|
1711
1766
|
const entries = await sbx.files.list(args.positional[1] ?? "/workspace", {
|
|
1712
|
-
...(integer(args, "depth") ? { depth: integer(args, "depth") } : {}),
|
|
1767
|
+
...(integer(args, "depth") !== undefined ? { depth: integer(args, "depth") } : {}),
|
|
1713
1768
|
...(flag(args, "glob") ? { glob: flag(args, "glob") } : {}),
|
|
1714
1769
|
});
|
|
1715
1770
|
print(entries
|
|
@@ -1752,7 +1807,7 @@ async function sandbox(argv, env, out) {
|
|
|
1752
1807
|
const sbx = await use(args.positional[0]);
|
|
1753
1808
|
const preview = await sbx.previews.create(port, {
|
|
1754
1809
|
...(has(args, "public") ? { visibility: "public" } : {}),
|
|
1755
|
-
...(integer(args, "ttl") ? { ttlSeconds: integer(args, "ttl") } : {}),
|
|
1810
|
+
...(integer(args, "ttl") !== undefined ? { ttlSeconds: integer(args, "ttl") } : {}),
|
|
1756
1811
|
});
|
|
1757
1812
|
// The link and how to use it, nothing more: the API's `hint` (which
|
|
1758
1813
|
// carries a referral line) stays in --json, not in the command's words.
|
|
@@ -2084,7 +2139,7 @@ async function runCode(sbx, argv, out) {
|
|
|
2084
2139
|
const execution = await sbx.interpreter.run(code, {
|
|
2085
2140
|
language,
|
|
2086
2141
|
...(flag(args, "context") ? { context: flag(args, "context") } : {}),
|
|
2087
|
-
...(timeout ? { timeoutMs: timeout * 1000 } : {}),
|
|
2142
|
+
...(timeout !== undefined ? { timeoutMs: timeout * 1000 } : {}),
|
|
2088
2143
|
...(out.json
|
|
2089
2144
|
? {}
|
|
2090
2145
|
: {
|
|
@@ -2133,8 +2188,8 @@ async function desktop(sbx, argv, out) {
|
|
|
2133
2188
|
case "start": {
|
|
2134
2189
|
const args = parse(rest, [], ["width", "height"]);
|
|
2135
2190
|
const started = await d.start({
|
|
2136
|
-
...(integer(args, "width") ? { width: integer(args, "width") } : {}),
|
|
2137
|
-
...(integer(args, "height") ? { height: integer(args, "height") } : {}),
|
|
2191
|
+
...(integer(args, "width") !== undefined ? { width: integer(args, "width") } : {}),
|
|
2192
|
+
...(integer(args, "height") !== undefined ? { height: integer(args, "height") } : {}),
|
|
2138
2193
|
});
|
|
2139
2194
|
print(`Desktop ${started.size} on ${started.display}. Watch it: ${started.streamUrl}`, started);
|
|
2140
2195
|
return 0;
|
|
@@ -2473,9 +2528,13 @@ read = readSecret) {
|
|
|
2473
2528
|
if (sources.filter((s) => s !== undefined).length !== 1)
|
|
2474
2529
|
throw usage("Give exactly one of a folder with a Dockerfile (or -f <Dockerfile>), --from <image>, or --pip/--apt/--npm.");
|
|
2475
2530
|
const build = {
|
|
2476
|
-
...(integer(args, "max-image-mib")
|
|
2477
|
-
|
|
2478
|
-
|
|
2531
|
+
...(integer(args, "max-image-mib") !== undefined
|
|
2532
|
+
? { maxImageMiB: integer(args, "max-image-mib") }
|
|
2533
|
+
: {}),
|
|
2534
|
+
...(integer(args, "disk-mib") !== undefined ? { diskMiB: integer(args, "disk-mib") } : {}),
|
|
2535
|
+
...(integer(args, "timeout") !== undefined
|
|
2536
|
+
? { timeoutSeconds: integer(args, "timeout") }
|
|
2537
|
+
: {}),
|
|
2479
2538
|
};
|
|
2480
2539
|
// -t app, -t app:v2 -t app:latest: one name, its tags.
|
|
2481
2540
|
let name = flag(args, "name");
|
|
@@ -2492,9 +2551,11 @@ read = readSecret) {
|
|
|
2492
2551
|
}
|
|
2493
2552
|
const start = {
|
|
2494
2553
|
...(flag(args, "start") ? { command: flag(args, "start") } : {}),
|
|
2495
|
-
...(integer(args, "ready-port")
|
|
2554
|
+
...(integer(args, "ready-port") !== undefined
|
|
2555
|
+
? { readyPort: integer(args, "ready-port") }
|
|
2556
|
+
: {}),
|
|
2496
2557
|
...(flag(args, "ready-command") ? { readyCommand: flag(args, "ready-command") } : {}),
|
|
2497
|
-
...(integer(args, "ready-timeout")
|
|
2558
|
+
...(integer(args, "ready-timeout") !== undefined
|
|
2498
2559
|
? { readyTimeoutSeconds: integer(args, "ready-timeout") }
|
|
2499
2560
|
: {}),
|
|
2500
2561
|
};
|
|
@@ -2758,23 +2819,29 @@ async function jobCommand(argv, env, out) {
|
|
|
2758
2819
|
? { cron, ...(flag(args, "timezone") ? { timezone: flag(args, "timezone") } : {}) }
|
|
2759
2820
|
: { at: atTime(at) },
|
|
2760
2821
|
command: cwd ? { argv, cwd } : argv,
|
|
2761
|
-
...(integer(args, "timeout")
|
|
2822
|
+
...(integer(args, "timeout") !== undefined
|
|
2823
|
+
? { timeoutSeconds: integer(args, "timeout") }
|
|
2824
|
+
: {}),
|
|
2762
2825
|
compute: {
|
|
2763
|
-
...(integer(args, "vcpu") ? { vcpu: integer(args, "vcpu") } : {}),
|
|
2764
|
-
...(integer(args, "memory") ? { memoryMiB: integer(args, "memory") } : {}),
|
|
2765
|
-
...(integer(args, "disk") ? { diskMiB: integer(args, "disk") } : {}),
|
|
2826
|
+
...(integer(args, "vcpu") !== undefined ? { vcpu: integer(args, "vcpu") } : {}),
|
|
2827
|
+
...(integer(args, "memory") !== undefined ? { memoryMiB: integer(args, "memory") } : {}),
|
|
2828
|
+
...(integer(args, "disk") !== undefined ? { diskMiB: integer(args, "disk") } : {}),
|
|
2766
2829
|
...(flag(args, "cpu")
|
|
2767
2830
|
? { cpuMode: oneOf(args, "cpu", ["shared", "reserved"]) }
|
|
2768
2831
|
: {}),
|
|
2769
|
-
...(integer(args, "cpu-floor")
|
|
2832
|
+
...(integer(args, "cpu-floor") !== undefined
|
|
2833
|
+
? { cpuFloorMillis: integer(args, "cpu-floor") }
|
|
2834
|
+
: {}),
|
|
2770
2835
|
...(flag(args, "region") ? { region: flag(args, "region") } : {}),
|
|
2771
|
-
...(usdMicros(args, "max-cost")
|
|
2836
|
+
...(usdMicros(args, "max-cost") !== undefined
|
|
2837
|
+
? { maxCostMicros: usdMicros(args, "max-cost") }
|
|
2838
|
+
: {}),
|
|
2772
2839
|
},
|
|
2773
2840
|
...(attempts !== undefined
|
|
2774
2841
|
? { retry: { maxAttempts: attempts, backoffSeconds: backoff ?? 30 } }
|
|
2775
2842
|
: {}),
|
|
2776
2843
|
...(secrets ? { secrets } : {}),
|
|
2777
|
-
...(usdMicros(args, "max-total-cost")
|
|
2844
|
+
...(usdMicros(args, "max-total-cost") !== undefined
|
|
2778
2845
|
? { maxTotalCostMicros: usdMicros(args, "max-total-cost") }
|
|
2779
2846
|
: {}),
|
|
2780
2847
|
});
|
|
@@ -2903,9 +2970,13 @@ async function jobCommand(argv, env, out) {
|
|
|
2903
2970
|
}
|
|
2904
2971
|
const VOLUME_HELP = `runtime volume <command>
|
|
2905
2972
|
|
|
2906
|
-
create --size-mib <N> [--name n]
|
|
2973
|
+
create --size-mib <N> [--name n] [--shared] A persistent disk; prints its id
|
|
2907
2974
|
ls Your volumes
|
|
2908
2975
|
get <id> One volume, its backups and where it is attached
|
|
2976
|
+
resize <id> --size-mib <N> Grow a detached volume on its current host
|
|
2977
|
+
attach <id> <sandboxId> --path /data Mount a shared disk in a running sandbox
|
|
2978
|
+
detach <id> <attachmentId> Flush and unmount its shared attachment
|
|
2979
|
+
attachment <id> <attachmentId> Read the attachment's state and error
|
|
2909
2980
|
rm <id> Delete it and everything on it (its backups stay)
|
|
2910
2981
|
backup <id> [--retention-days N] [--no-wait]
|
|
2911
2982
|
Back it up now, off its host; prints the backup id
|
|
@@ -2916,6 +2987,7 @@ const VOLUME_HELP = `runtime volume <command>
|
|
|
2916
2987
|
Daily backups on or off, and how long each is kept
|
|
2917
2988
|
Attach one: runtime sandbox create --volume <id>:/data
|
|
2918
2989
|
Volumes are backed up off their host daily unless you turn it off.
|
|
2990
|
+
Shared commands work where shared disks are enabled.
|
|
2919
2991
|
`;
|
|
2920
2992
|
async function volume(argv, env, out) {
|
|
2921
2993
|
const [verb, ...rest] = argv;
|
|
@@ -2927,10 +2999,11 @@ async function volume(argv, env, out) {
|
|
|
2927
2999
|
const api = (await client(env)).volumes;
|
|
2928
3000
|
switch (verb) {
|
|
2929
3001
|
case "create": {
|
|
2930
|
-
const args = parse(rest, [], ["size-mib", "name"]);
|
|
3002
|
+
const args = parse(rest, ["shared"], ["size-mib", "name"]);
|
|
2931
3003
|
const created = await api.create({
|
|
2932
3004
|
sizeMiB: integer(args, "size-mib") ?? Number(need(undefined, "--size-mib <N>")),
|
|
2933
3005
|
...(flag(args, "name") ? { name: flag(args, "name") } : {}),
|
|
3006
|
+
...(has(args, "shared") ? { shared: true } : {}),
|
|
2934
3007
|
});
|
|
2935
3008
|
print(created.id, created);
|
|
2936
3009
|
return 0;
|
|
@@ -2958,6 +3031,40 @@ async function volume(argv, env, out) {
|
|
|
2958
3031
|
print(describeRecord(found, table), found);
|
|
2959
3032
|
return 0;
|
|
2960
3033
|
}
|
|
3034
|
+
case "resize": {
|
|
3035
|
+
const args = parse(rest, [], ["size-mib"]);
|
|
3036
|
+
const id = need(args.positional[0], "a volume id");
|
|
3037
|
+
if (args.positional.length !== 1 || args.rest)
|
|
3038
|
+
throw usage(`Use ${me} volume resize <id> --size-mib <N>.`);
|
|
3039
|
+
const sizeMiB = integer(args, "size-mib") ?? Number(need(undefined, "--size-mib <N>"));
|
|
3040
|
+
const resized = await api.resize(id, { sizeMiB });
|
|
3041
|
+
print(describeRecord(resized, table), resized);
|
|
3042
|
+
return resized.resize?.state === "failed" ? 1 : 0;
|
|
3043
|
+
}
|
|
3044
|
+
case "attach": {
|
|
3045
|
+
const args = parse(rest, [], ["path"]);
|
|
3046
|
+
const id = need(args.positional[0], "a volume id");
|
|
3047
|
+
const sandboxId = need(args.positional[1], "a sandbox id");
|
|
3048
|
+
const path = need(flag(args, "path"), "--path /data");
|
|
3049
|
+
if (args.positional.length !== 2 || args.rest)
|
|
3050
|
+
throw usage(`Use ${me} volume attach <id> <sandboxId> --path /data.`);
|
|
3051
|
+
const attachment = await api.attach(id, { sandboxId, path });
|
|
3052
|
+
print(describeRecord(attachment, table), attachment);
|
|
3053
|
+
return attachment.error ? 1 : 0;
|
|
3054
|
+
}
|
|
3055
|
+
case "detach":
|
|
3056
|
+
case "attachment": {
|
|
3057
|
+
const args = parse(rest, [], []);
|
|
3058
|
+
const id = need(args.positional[0], "a volume id");
|
|
3059
|
+
const attachmentId = need(args.positional[1], "an attachment id");
|
|
3060
|
+
if (args.positional.length !== 2 || args.rest)
|
|
3061
|
+
throw usage(`Use ${me} volume ${verb} <id> <attachmentId>.`);
|
|
3062
|
+
const attachment = verb === "detach"
|
|
3063
|
+
? await api.detach(id, attachmentId)
|
|
3064
|
+
: await api.getAttachment(id, attachmentId);
|
|
3065
|
+
print(describeRecord(attachment, table), attachment);
|
|
3066
|
+
return verb === "detach" && attachment.error ? 1 : 0;
|
|
3067
|
+
}
|
|
2961
3068
|
case "rm":
|
|
2962
3069
|
case "delete": {
|
|
2963
3070
|
const found = await api.delete(need(rest[0], "a volume id"));
|
|
@@ -3029,6 +3136,10 @@ async function volume(argv, env, out) {
|
|
|
3029
3136
|
"create",
|
|
3030
3137
|
"ls",
|
|
3031
3138
|
"get",
|
|
3139
|
+
"resize",
|
|
3140
|
+
"attach",
|
|
3141
|
+
"detach",
|
|
3142
|
+
"attachment",
|
|
3032
3143
|
"rm",
|
|
3033
3144
|
"backup",
|
|
3034
3145
|
"backups",
|
|
@@ -3089,7 +3200,13 @@ function usdMicros(args, name) {
|
|
|
3089
3200
|
return undefined;
|
|
3090
3201
|
if (!/^\d+(\.\d{1,6})?$/.test(value))
|
|
3091
3202
|
throw usage(`--${name} takes dollars, such as 25 or 2.50.`);
|
|
3092
|
-
|
|
3203
|
+
// ARCHITECTURE.md section 10: integer microdollars throughout, with no
|
|
3204
|
+
// floating-point money path or rounding drift.
|
|
3205
|
+
const [whole, fraction = ""] = value.split(".");
|
|
3206
|
+
const micros = BigInt(whole) * 1000000n + BigInt(fraction.padEnd(6, "0"));
|
|
3207
|
+
if (micros > BigInt(Number.MAX_SAFE_INTEGER))
|
|
3208
|
+
throw usage(`--${name} is too large to represent exactly in microdollars.`);
|
|
3209
|
+
return Number(micros);
|
|
3093
3210
|
}
|
|
3094
3211
|
/** A create body from the command line's flags. */
|
|
3095
3212
|
function createInput(args) {
|
|
@@ -3109,17 +3226,23 @@ function createInput(args) {
|
|
|
3109
3226
|
...(flag(args, "name") ? { name: flag(args, "name") } : {}),
|
|
3110
3227
|
...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
|
|
3111
3228
|
...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
|
|
3112
|
-
...(integer(args, "vcpu") ? { vcpu: integer(args, "vcpu") } : {}),
|
|
3113
|
-
...(integer(args, "memory") ? { memoryMiB: integer(args, "memory") } : {}),
|
|
3114
|
-
...(integer(args, "disk") ? { diskMiB: integer(args, "disk") } : {}),
|
|
3229
|
+
...(integer(args, "vcpu") !== undefined ? { vcpu: integer(args, "vcpu") } : {}),
|
|
3230
|
+
...(integer(args, "memory") !== undefined ? { memoryMiB: integer(args, "memory") } : {}),
|
|
3231
|
+
...(integer(args, "disk") !== undefined ? { diskMiB: integer(args, "disk") } : {}),
|
|
3115
3232
|
...(flag(args, "cpu") ? { cpu: oneOf(args, "cpu", ["shared", "reserved"]) } : {}),
|
|
3116
|
-
...(integer(args, "cpu-floor")
|
|
3233
|
+
...(integer(args, "cpu-floor") !== undefined
|
|
3234
|
+
? { cpuFloorMillis: integer(args, "cpu-floor") }
|
|
3235
|
+
: {}),
|
|
3117
3236
|
...(flag(args, "region") ? { region: flag(args, "region") } : {}),
|
|
3118
|
-
...(usdMicros(args, "max-cost")
|
|
3119
|
-
|
|
3237
|
+
...(usdMicros(args, "max-cost") !== undefined
|
|
3238
|
+
? { maxCostMicros: usdMicros(args, "max-cost") }
|
|
3239
|
+
: {}),
|
|
3240
|
+
...(usdMicros(args, "max-total-cost") !== undefined
|
|
3120
3241
|
? { maxTotalCostMicros: usdMicros(args, "max-total-cost") }
|
|
3121
3242
|
: {}),
|
|
3122
|
-
...(integer(args, "timeout")
|
|
3243
|
+
...(integer(args, "timeout") !== undefined
|
|
3244
|
+
? { timeoutSeconds: integer(args, "timeout") }
|
|
3245
|
+
: {}),
|
|
3123
3246
|
...(flag(args, "on-timeout")
|
|
3124
3247
|
? { onLeaseEnd: oneOf(args, "on-timeout", ["pause", "stop"]) }
|
|
3125
3248
|
: {}),
|
|
@@ -3149,7 +3272,7 @@ ownInterrupt = true) {
|
|
|
3149
3272
|
...(stdin ? { stdin } : {}),
|
|
3150
3273
|
...(flag(args, "cwd") ? { cwd: flag(args, "cwd") } : {}),
|
|
3151
3274
|
...(args.flags.has("env") ? { env: pairs(args, "env") } : {}),
|
|
3152
|
-
...(timeout ? { timeoutMs: timeout * 1000 } : {}),
|
|
3275
|
+
...(timeout !== undefined ? { timeoutMs: timeout * 1000 } : {}),
|
|
3153
3276
|
};
|
|
3154
3277
|
// Ctrl-C stops the command in the sandbox too, and says so; without this
|
|
3155
3278
|
// the CLI exited and the command ran on unseen.
|
|
@@ -3222,6 +3345,10 @@ function defaultOut(argv) {
|
|
|
3222
3345
|
return {
|
|
3223
3346
|
json,
|
|
3224
3347
|
write: (text) => process.stdout.write(text.endsWith("\n") ? text : `${text}\n`),
|
|
3348
|
+
drain: async () => {
|
|
3349
|
+
if (process.stdout.writableNeedDrain)
|
|
3350
|
+
await once(process.stdout, "drain");
|
|
3351
|
+
},
|
|
3225
3352
|
error: (text) => process.stderr.write(`${text}\n`),
|
|
3226
3353
|
};
|
|
3227
3354
|
}
|
|
@@ -3279,6 +3406,10 @@ function spark(values) {
|
|
|
3279
3406
|
}
|
|
3280
3407
|
const mib = (bytes) => `${Math.round(bytes / 1_048_576).toLocaleString("en-US")} MiB`;
|
|
3281
3408
|
function metricsText(m) {
|
|
3409
|
+
const waiting = (value) => value == null ? "not measured" : `${value}%`;
|
|
3410
|
+
const waitingHistory = (field) => m.points.some((point) => point[field] != null)
|
|
3411
|
+
? spark(m.points.map((point) => point[field] ?? null))
|
|
3412
|
+
: "not measured";
|
|
3282
3413
|
const cpu = m.points.map((p) => p.cpuPercent);
|
|
3283
3414
|
const memory = m.points.map((p) => p.memoryBytes);
|
|
3284
3415
|
const peak = Math.max(0, ...m.points.map((p) => p.cpuPeakPercent ?? p.cpuPercent ?? 0));
|
|
@@ -3302,7 +3433,11 @@ function metricsText(m) {
|
|
|
3302
3433
|
"memory now",
|
|
3303
3434
|
still ?? (latest ? `${mib(latest.memoryBytes)} of ${mib(m.memoryLimitBytes)}` : "-"),
|
|
3304
3435
|
],
|
|
3436
|
+
["cpu wait now", still ?? waiting(latest?.cpuWaitPercent)],
|
|
3437
|
+
["memory stalls now", still ?? waiting(latest?.memoryStallPercent)],
|
|
3305
3438
|
[`cpu, ${m.range}`, m.points.length ? `${spark(cpu)} peak ${peak}%` : "no readings"],
|
|
3439
|
+
[`cpu wait, ${m.range}`, waitingHistory("cpuWaitPercent")],
|
|
3440
|
+
[`memory stalls, ${m.range}`, waitingHistory("memoryStallPercent")],
|
|
3306
3441
|
[
|
|
3307
3442
|
`memory, ${m.range}`,
|
|
3308
3443
|
m.points.length
|
|
@@ -3397,7 +3532,7 @@ async function webhooksCommand(argv, env, out) {
|
|
|
3397
3532
|
}
|
|
3398
3533
|
case "deliveries": {
|
|
3399
3534
|
const page = await hooks.deliveries(id(), {
|
|
3400
|
-
...(integer(args, "limit") ? { limit: integer(args, "limit") } : {}),
|
|
3535
|
+
...(integer(args, "limit") !== undefined ? { limit: integer(args, "limit") } : {}),
|
|
3401
3536
|
});
|
|
3402
3537
|
print(page.data.length
|
|
3403
3538
|
? table(page.data.map((d) => [
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RuntimeError } from "./errors.js";
|
|
2
2
|
import type { Page } from "./page.js";
|
|
3
3
|
import { type ClientExtensions } from "./products/index.js";
|
|
4
|
+
import { usageExport as makeUsageExport } from "./products/observability.js";
|
|
4
5
|
import { Sandbox } from "./sandbox.js";
|
|
5
6
|
import { Snapshots } from "./snapshots.js";
|
|
6
7
|
import { Transport, type RequestOptions } from "./transport.js";
|
|
@@ -11,7 +12,7 @@ export type RuntimeOptions = {
|
|
|
11
12
|
/** Default: RUNTIME_API_URL, then https://api.withruntime.com. */
|
|
12
13
|
baseUrl?: string;
|
|
13
14
|
fetch?: typeof fetch;
|
|
14
|
-
/** Per call, retries included. Default 5 minutes. */
|
|
15
|
+
/** Per call, queue and retries included. Default 5 minutes; 0 disables it. */
|
|
15
16
|
timeoutMs?: number;
|
|
16
17
|
/** Retries of transport failures, 429, 502, 503 and 504. Default 4. */
|
|
17
18
|
maxRetries?: number;
|
|
@@ -30,11 +31,16 @@ export interface Runtime extends ClientExtensions {
|
|
|
30
31
|
}
|
|
31
32
|
export declare class Runtime {
|
|
32
33
|
readonly transport: Transport;
|
|
34
|
+
/** The sandbox product; `sandboxes` remains the same client for compatibility. */
|
|
35
|
+
readonly sandbox: Sandboxes;
|
|
33
36
|
readonly sandboxes: Sandboxes;
|
|
37
|
+
readonly snapshot: Snapshots;
|
|
34
38
|
readonly snapshots: Snapshots;
|
|
35
39
|
readonly feedback: FeedbackApi;
|
|
36
40
|
readonly support: SupportApi;
|
|
37
41
|
readonly account: AccountApi;
|
|
42
|
+
/** Date-range settlements, paged with exact charges and the server's CSV. */
|
|
43
|
+
readonly usageExport: ReturnType<typeof makeUsageExport>;
|
|
38
44
|
constructor(options?: RuntimeOptions);
|
|
39
45
|
/** Who this key is: organization, agent and credential. */
|
|
40
46
|
me(options?: RequestOptions): Promise<{
|
|
@@ -50,6 +56,25 @@ export declare class Runtime {
|
|
|
50
56
|
/** Balance, holds, trial time and per-resource charges, in integer microdollars. */
|
|
51
57
|
/** Credit, holds, trial time and per-resource charges. */
|
|
52
58
|
usage(options?: RequestOptions): Promise<Usage>;
|
|
59
|
+
/** This account's authenticated API answers, in hourly windows. Counts are
|
|
60
|
+
* exact decimal strings, and an empty window has a null error percentage.
|
|
61
|
+
* A key needs usage permission. */
|
|
62
|
+
usageRequests(range?: "24h" | "7d" | "30d" | "90d", options?: RequestOptions): Promise<{
|
|
63
|
+
range: "24h" | "7d" | "30d" | "90d";
|
|
64
|
+
since: string;
|
|
65
|
+
until: string;
|
|
66
|
+
calls: string;
|
|
67
|
+
clientErrors: string;
|
|
68
|
+
serverErrors: string;
|
|
69
|
+
errorPercent: number | null;
|
|
70
|
+
operations: Array<{
|
|
71
|
+
operation: string;
|
|
72
|
+
calls: string;
|
|
73
|
+
clientErrors: string;
|
|
74
|
+
serverErrors: string;
|
|
75
|
+
errorPercent: number | null;
|
|
76
|
+
}>;
|
|
77
|
+
}>;
|
|
53
78
|
}
|
|
54
79
|
/** How `sandboxes.create` waits when every trial slot, the account's quota or
|
|
55
80
|
* the region is full. */
|
|
@@ -83,7 +108,8 @@ export declare class Sandboxes {
|
|
|
83
108
|
/** Deletes a sandbox for good, by id, without reading it first: see
|
|
84
109
|
* `sandbox.delete()`. Deleting it again answers the same. */
|
|
85
110
|
delete(id: string, options?: RequestOptions): Promise<DeletedSandbox>;
|
|
86
|
-
/** Live sandboxes, oldest first. Await for a page, or `for await` over all.
|
|
111
|
+
/** Live sandboxes, oldest first. Await for a page, or `for await` over all.
|
|
112
|
+
* Request options apply to the initial request and every subsequent page. */
|
|
87
113
|
list(filter?: {
|
|
88
114
|
state?: SandboxInfo["state"][];
|
|
89
115
|
includeStopped?: boolean;
|
|
@@ -91,7 +117,7 @@ export declare class Sandboxes {
|
|
|
91
117
|
labels?: Record<string, string>;
|
|
92
118
|
name?: string;
|
|
93
119
|
limit?: number;
|
|
94
|
-
}): Promise<Page<Sandbox>>;
|
|
120
|
+
}, options?: RequestOptions): Promise<Page<Sandbox>>;
|
|
95
121
|
}
|
|
96
122
|
export declare class FeedbackApi {
|
|
97
123
|
private readonly t;
|