withruntime 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BLAXEL.md +8 -9
- package/DAYTONA.md +8 -9
- package/E2B.md +63 -21
- package/README.md +6 -5
- package/README.src.md +6 -5
- package/VERCEL.md +3 -2
- package/compatibility-lock.json +69 -14
- package/dist/api-defaults.d.ts +1 -1
- package/dist/api-defaults.js +1 -1
- package/dist/blaxel/errors.js +4 -4
- package/dist/blaxel/lookup.d.ts +2 -1
- package/dist/blaxel/lookup.js +2 -1
- package/dist/blaxel/sandbox.d.ts +8 -7
- package/dist/blaxel/sandbox.js +16 -12
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +99 -27
- package/dist/client.d.ts +16 -60
- package/dist/client.js +14 -103
- package/dist/compat/files.js +5 -1
- package/dist/compat/lease.d.ts +1 -0
- package/dist/compat/lease.js +14 -8
- package/dist/credentials.d.ts +9 -0
- package/dist/credentials.js +26 -4
- package/dist/daytona/daytona.d.ts +1 -2
- package/dist/daytona/daytona.js +30 -14
- package/dist/daytona/sandbox.d.ts +9 -4
- package/dist/daytona/sandbox.js +24 -5
- package/dist/e2b/client.d.ts +15 -2
- package/dist/e2b/client.js +61 -7
- package/dist/e2b/commands.d.ts +3 -0
- package/dist/e2b/commands.js +16 -5
- package/dist/e2b/errors.d.ts +26 -2
- package/dist/e2b/errors.js +38 -5
- package/dist/e2b/filesystem.d.ts +1 -1
- package/dist/e2b/filesystem.js +82 -16
- package/dist/e2b/index.d.ts +2 -2
- package/dist/e2b/index.js +2 -2
- package/dist/e2b/sandbox.d.ts +25 -12
- package/dist/e2b/sandbox.js +196 -165
- package/dist/e2b/unsupported.d.ts +19 -0
- package/dist/e2b/unsupported.js +21 -0
- package/dist/e2b/users.d.ts +9 -0
- package/dist/e2b/users.js +22 -0
- package/dist/errors.d.ts +10 -4
- package/dist/errors.js +18 -10
- package/dist/identity.js +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/machine.d.ts +180 -0
- package/dist/machine.js +504 -0
- package/dist/modal/index.js +6 -2
- package/dist/network-cli.js +7 -7
- package/dist/openai-agents/index.d.ts +1 -1
- package/dist/openai-agents/index.js +1 -1
- package/dist/products/browser.d.ts +2 -1
- package/dist/products/browser.js +9 -19
- package/dist/products/desktop.d.ts +2 -1
- package/dist/products/desktop.js +5 -30
- package/dist/products/jobs.d.ts +6 -5
- package/dist/products/jobs.js +6 -3
- package/dist/products/limits.d.ts +4 -10
- package/dist/products/mcp.d.ts +5 -1
- package/dist/products/mcp.js +29 -8
- package/dist/products/network-products.d.ts +1 -1
- package/dist/products/network-products.js +1 -1
- package/dist/products/network.d.ts +1 -1
- package/dist/products/observability.d.ts +1 -1
- package/dist/products/private-network.d.ts +1 -1
- package/dist/products/secrets.d.ts +3 -3
- package/dist/products/switching.d.ts +2 -3
- package/dist/runloop/index.js +6 -2
- package/dist/sandbox.d.ts +7 -84
- package/dist/sandbox.js +58 -345
- package/dist/tar.d.ts +2 -0
- package/dist/tar.js +74 -16
- package/dist/transport.d.ts +36 -8
- package/dist/transport.js +174 -37
- package/dist/types.d.ts +102 -12
- package/dist/vercel/filesystem.js +3 -1
- package/dist/vercel/sandbox.d.ts +18 -12
- package/dist/vercel/sandbox.js +27 -15
- package/dist/wait.d.ts +12 -0
- package/dist/wait.js +47 -0
- package/package.json +1 -1
package/dist/blaxel/sandbox.js
CHANGED
|
@@ -71,10 +71,12 @@ export function durationMs(value) {
|
|
|
71
71
|
total += Number(count) * unit[letter];
|
|
72
72
|
return total;
|
|
73
73
|
}
|
|
74
|
-
/** The
|
|
75
|
-
* lifecycle: never deleted before Blaxel would delete it. A fixed
|
|
76
|
-
* within the hour is a
|
|
77
|
-
*
|
|
74
|
+
/** The time limit and the paused retention that honour Blaxel's TTL, expiry
|
|
75
|
+
* and lifecycle: never deleted before Blaxel would delete it. A fixed
|
|
76
|
+
* deadline within the hour is a time limit that ends the sandbox; past that
|
|
77
|
+
* there is none, so it runs while it works and pauses when idle (0300), and
|
|
78
|
+
* a paused sandbox is kept that many days, rounded up; none keeps it 365
|
|
79
|
+
* days. */
|
|
78
80
|
export function lifetimeOf(asked) {
|
|
79
81
|
const now = Date.now();
|
|
80
82
|
const fixed = [];
|
|
@@ -110,7 +112,7 @@ export function lifetimeOf(asked) {
|
|
|
110
112
|
: KEEP_DAYS;
|
|
111
113
|
return soonest <= LEASE_SECONDS * 1000
|
|
112
114
|
? { timeoutSeconds: Math.max(60, Math.ceil(soonest / 1000)), onLeaseEnd: "stop", days }
|
|
113
|
-
: {
|
|
115
|
+
: { onLeaseEnd: "pause", days };
|
|
114
116
|
}
|
|
115
117
|
/** Blaxel's network settings as Runtime's rules: allowed and forbidden
|
|
116
118
|
* domains, from the network itself or from its proxy (Blaxel's current
|
|
@@ -402,9 +404,10 @@ export class SandboxInstance {
|
|
|
402
404
|
}
|
|
403
405
|
// ---- the live sandbox ---------------------------------------------------
|
|
404
406
|
/** The sandbox, ready for a call. A paused one wakes by itself on the call
|
|
405
|
-
* (autoWake); one made without autoWake is woken here. A
|
|
406
|
-
*
|
|
407
|
-
*
|
|
407
|
+
* (autoWake); one made without autoWake is woken here. A time limit
|
|
408
|
+
* nearing its end (an hour, on a sandbox made before 0300) is renewed in
|
|
409
|
+
* the background, so a sandbox in use is not paused by it; one with no
|
|
410
|
+
* time limit renews itself. */
|
|
408
411
|
async #live() {
|
|
409
412
|
if (this.#deleted)
|
|
410
413
|
throw responseError(404, `Sandbox ${this.name} was deleted.`);
|
|
@@ -414,6 +417,8 @@ export class SandboxInstance {
|
|
|
414
417
|
await this.#wake();
|
|
415
418
|
else if (state === "running" &&
|
|
416
419
|
rt.info.onLeaseEnd === "pause" &&
|
|
420
|
+
// One with no time limit renews itself (0300).
|
|
421
|
+
rt.info.endsAt !== null &&
|
|
417
422
|
!this.#renewing &&
|
|
418
423
|
Date.parse(rt.info.expiresAt) - Date.now() < RENEW_BELOW_MS)
|
|
419
424
|
this.#renewing = rt
|
|
@@ -461,7 +466,7 @@ export class SandboxInstance {
|
|
|
461
466
|
labels: { ...labels, [IDLE_PAUSE]: labels[IDLE_PAUSE] ?? String(current ?? 0) },
|
|
462
467
|
}))
|
|
463
468
|
: undefined,
|
|
464
|
-
rt.info.onLeaseEnd === "pause" && need >= 1
|
|
469
|
+
rt.info.onLeaseEnd === "pause" && rt.info.endsAt !== null && need >= 1
|
|
465
470
|
? rt.extend(Math.ceil(need)).catch(() => undefined)
|
|
466
471
|
: undefined,
|
|
467
472
|
]);
|
|
@@ -518,8 +523,7 @@ export class SandboxInstance {
|
|
|
518
523
|
// ---- create, get, list ---------------------------------------------------
|
|
519
524
|
/** Creates a sandbox and answers once it runs. Blaxel's defaults: the base
|
|
520
525
|
* image (Runtime's stock image) and 4096 MB, with vCPUs at one per 2048 MB.
|
|
521
|
-
* Funding is left to Runtime: the
|
|
522
|
-
* time, then prepaid credit. A name another live sandbox holds is refused
|
|
526
|
+
* Funding is left to Runtime: the included usage first, then prepaid credit. A name another live sandbox holds is refused
|
|
523
527
|
* with a 409, as in Blaxel. */
|
|
524
528
|
static async create(sandbox, { createIfNotExist = false } = {}) {
|
|
525
529
|
const config = configOf(sandbox);
|
|
@@ -545,7 +549,7 @@ export class SandboxInstance {
|
|
|
545
549
|
vcpu: Math.min(MAX_VCPU, Math.max(1, Math.round(memory / MEMORY_PER_VCPU))),
|
|
546
550
|
memoryMiB: memory,
|
|
547
551
|
...STANDBY,
|
|
548
|
-
timeoutSeconds: lifetime.timeoutSeconds,
|
|
552
|
+
...(lifetime.timeoutSeconds === undefined ? {} : { timeoutSeconds: lifetime.timeoutSeconds }),
|
|
549
553
|
onLeaseEnd: lifetime.onLeaseEnd,
|
|
550
554
|
...(config.name ? { name: config.name } : {}),
|
|
551
555
|
labels,
|
package/dist/cli.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ declare const kit: {
|
|
|
30
30
|
};
|
|
31
31
|
export type CliKit = typeof kit;
|
|
32
32
|
export declare function run(argv: string[], env?: NodeJS.ProcessEnv, out?: Out): Promise<number>;
|
|
33
|
-
/** `runtime usage` for a person: the balance, the
|
|
33
|
+
/** `runtime usage` for a person: the balance, the included usage and what each kind
|
|
34
34
|
* of resource was charged, in dollars. --json keeps every figure, and each
|
|
35
35
|
* resource with its rates and CPU time. */
|
|
36
36
|
/** One row per resource, for a spreadsheet: when it ran, how big it was, how
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ Account
|
|
|
36
36
|
The account's audit log (owner or admin keys)
|
|
37
37
|
sso Single sign-on and SCIM directory sync (owner or
|
|
38
38
|
admin keys); an owner changes it on the website
|
|
39
|
-
usage [--csv] Balance,
|
|
39
|
+
usage [--csv] Balance, included usage and charges; --csv, each resource
|
|
40
40
|
usage export --since <UTC time> --until <UTC time>
|
|
41
41
|
Every settlement in the range as CSV; --json, each page
|
|
42
42
|
limits Whether this key is read-only, and its daily spending limit
|
|
@@ -140,10 +140,10 @@ const SANDBOX_HELP = `runtime sandbox <command>
|
|
|
140
140
|
resize <id> [--vcpu 2] [--memory 4096] --restart
|
|
141
141
|
Change its size by a restart: the whole disk stays,
|
|
142
142
|
programs stop (snapshot it first to keep memory)
|
|
143
|
-
extend <id> <seconds>
|
|
143
|
+
extend <id> <seconds> Move a time limit on (one with none needs nothing)
|
|
144
144
|
update <id> [--name n] [--label k=v]... [--env K=V]... [--unset-env K]... [--idle-pause <seconds>] [--auto-wake on|off]
|
|
145
145
|
[--persistent on|off] [--max-total-cost <usd>|none]
|
|
146
|
-
Change its settings; persistent keeps it running
|
|
146
|
+
Change its settings; persistent keeps it running until stopped
|
|
147
147
|
snapshot <id> [--name n] [--retention days] Keep its whole machine; prints the snapshot id
|
|
148
148
|
mount <id> <s3|r2|gcs>://<bucket>[/prefix] <path> [--secret NAME] [--region r]
|
|
149
149
|
[--endpoint https://...] [--account-id id] [--read-only]
|
|
@@ -517,8 +517,18 @@ function describe(info) {
|
|
|
517
517
|
],
|
|
518
518
|
["funding", info.funding],
|
|
519
519
|
["region", info.region],
|
|
520
|
-
|
|
521
|
-
|
|
520
|
+
// A sandbox with no time limit runs while it works (0300): endsAt is
|
|
521
|
+
// null. An older server sends no endsAt; its expiresAt was the end.
|
|
522
|
+
["time limit", info.timeoutSeconds ? `${info.timeoutSeconds} s` : "none"],
|
|
523
|
+
[
|
|
524
|
+
"ends",
|
|
525
|
+
info.endsAt === null
|
|
526
|
+
? info.state === "running"
|
|
527
|
+
? "when idle, stopped or out of credit"
|
|
528
|
+
: "-"
|
|
529
|
+
: (info.endsAt ?? info.expiresAt ?? "-"),
|
|
530
|
+
],
|
|
531
|
+
["at its end", info.onLeaseEnd ?? "-"],
|
|
522
532
|
// Why a sandbox is no longer running (troubleshooting.md, "A sandbox
|
|
523
533
|
// stopped on its own"); `get` left both out until 30 September 2026.
|
|
524
534
|
...(info.pausedAt ? [["paused at", info.pausedAt]] : []),
|
|
@@ -685,16 +695,18 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
685
695
|
const [who, spend] = await Promise.all([rt.me(), rt.usage().catch(() => undefined)]);
|
|
686
696
|
const trialLeft = spend?.trial ? spend.trial.availableMs / 3_600_000 : 0;
|
|
687
697
|
const credit = spend ? BigInt(spend.available) : 0n;
|
|
698
|
+
// A pilot's sandboxes run on the pilot, before anything else and free.
|
|
688
699
|
const funding = !spend
|
|
689
700
|
? undefined
|
|
690
701
|
: [
|
|
702
|
+
...(spend.pilot ? [pilotFunding(spend.pilot)] : []),
|
|
691
703
|
...(trialLeft > 0
|
|
692
704
|
? [
|
|
693
|
-
`
|
|
705
|
+
`included usage, ${trialLeft.toLocaleString("en-US", { maximumFractionDigits: 1 })} hours left without credit`,
|
|
694
706
|
]
|
|
695
707
|
: []),
|
|
696
708
|
...(credit > 0n ? [`${dollars(credit)} of credit`] : []),
|
|
697
|
-
].join("; ") || "no
|
|
709
|
+
].join("; ") || "no included usage or credit left: runtime billing";
|
|
698
710
|
print(table([
|
|
699
711
|
["organization", who.orgName ? `${who.orgName} (${who.orgId})` : who.orgId],
|
|
700
712
|
...(who.role ? [["role", who.role]] : []),
|
|
@@ -755,7 +767,10 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
755
767
|
return 0;
|
|
756
768
|
}
|
|
757
769
|
if (command === "limits") {
|
|
758
|
-
const
|
|
770
|
+
const rt = await client(env);
|
|
771
|
+
// The usage call only adds the pilot's line: a key that cannot read it
|
|
772
|
+
// still gets its limits.
|
|
773
|
+
const [l, spend] = await Promise.all([rt.limits.get(), rt.usage().catch(() => undefined)]);
|
|
759
774
|
// As `usage` shows money: $0.0014 of spend read $0.00 until 30 September 2026.
|
|
760
775
|
const usd = (micros) => dollars(micros);
|
|
761
776
|
const access = l.access === "read"
|
|
@@ -773,7 +788,13 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
773
788
|
],
|
|
774
789
|
["used, last 24 hours", usd(l.daily.usedMicros)],
|
|
775
790
|
...(l.daily.remainingMicros === null ? [] : [["left", usd(l.daily.remainingMicros)]]),
|
|
776
|
-
...(l.trial ? [["free
|
|
791
|
+
...(l.trial ? [["free time", trialLeft(l.trial)]] : []),
|
|
792
|
+
...(spend?.pilot
|
|
793
|
+
? [
|
|
794
|
+
["pilot", pilotFunding(spend.pilot)],
|
|
795
|
+
["pilot sandbox hours", pilotLeft(spend.pilot)],
|
|
796
|
+
]
|
|
797
|
+
: []),
|
|
777
798
|
]), l);
|
|
778
799
|
return 0;
|
|
779
800
|
}
|
|
@@ -1019,7 +1040,7 @@ export async function run(argv, env = process.env, out = defaultOut(argv)) {
|
|
|
1019
1040
|
}
|
|
1020
1041
|
throw unknown("command", command, COMMANDS);
|
|
1021
1042
|
}
|
|
1022
|
-
/** `runtime usage` for a person: the balance, the
|
|
1043
|
+
/** `runtime usage` for a person: the balance, the included usage and what each kind
|
|
1023
1044
|
* of resource was charged, in dollars. --json keeps every figure, and each
|
|
1024
1045
|
* resource with its rates and CPU time. */
|
|
1025
1046
|
/** One row per resource, for a spreadsheet: when it ran, how big it was, how
|
|
@@ -1071,10 +1092,45 @@ export function usageCsv(u) {
|
|
|
1071
1092
|
.join(","));
|
|
1072
1093
|
return [head.join(","), ...rows].join("\n");
|
|
1073
1094
|
}
|
|
1074
|
-
/**
|
|
1095
|
+
/** Milliseconds as hours, to a tenth: "8,731.2". */
|
|
1096
|
+
const hours = (ms) => (ms / 3_600_000).toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
1097
|
+
/** The machine time left without credit, as `runtime usage` and `runtime
|
|
1098
|
+
* limits` say it: this month's included usage, as hours at 2 vCPU and 4 GiB,
|
|
1099
|
+
* or for an account given free hours before 5 October 2026, those hours too. */
|
|
1075
1100
|
function trialLeft(trial) {
|
|
1076
|
-
const
|
|
1077
|
-
return
|
|
1101
|
+
const renews = trial.renewsAt ? `, renews ${trial.renewsAt.slice(0, 10)}` : "";
|
|
1102
|
+
return trial.totalMs > 0
|
|
1103
|
+
? `${hours(trial.availableMs)} hours left (${hours(trial.totalMs)} free hours given before 5 October 2026, and the included usage${renews})`
|
|
1104
|
+
: `${hours(trial.availableMs)} hours at 2 vCPU and 4 GiB left this month${renews}`;
|
|
1105
|
+
}
|
|
1106
|
+
/** This month's included usage, one row per product, as `runtime usage`
|
|
1107
|
+
* says it. */
|
|
1108
|
+
function allowanceRows(allowances) {
|
|
1109
|
+
const amount = (n) => n.toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
1110
|
+
return allowances.map((a) => [
|
|
1111
|
+
`included ${a.pool.replace(/-/g, " ")}`,
|
|
1112
|
+
`${amount(a.left)} of ${amount(a.quantity)} ${a.unit}s left`,
|
|
1113
|
+
]);
|
|
1114
|
+
}
|
|
1115
|
+
/** What a pilot gives, as `runtime whoami` and `runtime limits` say it: the
|
|
1116
|
+
* sandboxes it runs free and until when, past which credit does not run
|
|
1117
|
+
* them. */
|
|
1118
|
+
function pilotFunding(pilot) {
|
|
1119
|
+
const at = (iso) => `${iso.slice(0, 16).replace("T", " ")} UTC`;
|
|
1120
|
+
return `a pilot: up to ${pilot.sandboxes.toLocaleString("en-US")} sandboxes free, running until ${at(pilot.graceEndsAt)}; credit does not run it; to extend it, write to support`;
|
|
1121
|
+
}
|
|
1122
|
+
/** A pilot's sandbox hours left, as `runtime usage` says them. */
|
|
1123
|
+
function pilotLeft(pilot) {
|
|
1124
|
+
const left = `${hours(pilot.leftMs)} of ${pilot.hours.toLocaleString("en-US")} left`;
|
|
1125
|
+
// How long they last at the rate its sandboxes run now (an API before
|
|
1126
|
+
// 0414 does not say how many run).
|
|
1127
|
+
const running = pilot.running ?? 0;
|
|
1128
|
+
const pace = running > 0
|
|
1129
|
+
? `; at ${running.toLocaleString("en-US")} running, about ${hours(pilot.leftMs / running)} hours`
|
|
1130
|
+
: "";
|
|
1131
|
+
return pilot.leftMs > 0
|
|
1132
|
+
? `${left}${pace}; new pilot sandboxes until ${pilot.endsAt.slice(0, 16).replace("T", " ")} UTC`
|
|
1133
|
+
: "This pilot's sandbox hours are used.";
|
|
1078
1134
|
}
|
|
1079
1135
|
function usageSummary(u) {
|
|
1080
1136
|
const micros = (value) => typeof value === "string" || typeof value === "number" || typeof value === "bigint"
|
|
@@ -1088,15 +1144,19 @@ function usageSummary(u) {
|
|
|
1088
1144
|
// its own line, so "spent" reads as what was used.
|
|
1089
1145
|
["used", dollars(micros(u.spent) - takenBack)],
|
|
1090
1146
|
...(takenBack > 0n ? [["returned by refunds and disputes", dollars(takenBack)]] : []),
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
1093
|
-
//
|
|
1094
|
-
["
|
|
1147
|
+
// What running sandboxes and the hour ahead of stored snapshots, images
|
|
1148
|
+
// and volumes are about to use, so an account with nothing running can
|
|
1149
|
+
// still show some. Said without "held": no guide teaches holds.
|
|
1150
|
+
["set aside for running sandboxes and this hour's storage", dollars(micros(u.held))],
|
|
1095
1151
|
];
|
|
1096
1152
|
if (micros(u.expired) > 0n)
|
|
1097
1153
|
rows.push(["expired or taken back", dollars(micros(u.expired))]);
|
|
1098
1154
|
if (u.trial)
|
|
1099
|
-
rows.push(["free
|
|
1155
|
+
rows.push(["free time", trialLeft(u.trial)]);
|
|
1156
|
+
if (u.allowances)
|
|
1157
|
+
rows.push(...allowanceRows(u.allowances));
|
|
1158
|
+
if (u.pilot)
|
|
1159
|
+
rows.push(["pilot sandbox hours", pilotLeft(u.pilot)]);
|
|
1100
1160
|
if (u.outbound) {
|
|
1101
1161
|
const gib = (bytes) => `${(bytes / 1_073_741_824).toLocaleString("en-US", { maximumFractionDigits: 1 })} GiB`;
|
|
1102
1162
|
const left = Math.max(0, u.outbound.allowanceBytes - u.outbound.freeBytes);
|
|
@@ -1106,7 +1166,15 @@ function usageSummary(u) {
|
|
|
1106
1166
|
]);
|
|
1107
1167
|
}
|
|
1108
1168
|
const kinds = new Map();
|
|
1109
|
-
|
|
1169
|
+
// Every resource, by kind, from an API that totals them (6 October 2026);
|
|
1170
|
+
// an older one sends only its hundred newest, counted here.
|
|
1171
|
+
for (const k of u.kinds ?? [])
|
|
1172
|
+
kinds.set(k.kind, {
|
|
1173
|
+
count: k.resources,
|
|
1174
|
+
charged: micros(k.chargedMicros),
|
|
1175
|
+
held: micros(k.heldMicros),
|
|
1176
|
+
});
|
|
1177
|
+
for (const resource of u.kinds ? [] : (u.resources ?? [])) {
|
|
1110
1178
|
const kind = typeof resource.kind === "string" ? resource.kind : "other";
|
|
1111
1179
|
const entry = kinds.get(kind) ?? { count: 0, charged: 0n, held: 0n };
|
|
1112
1180
|
entry.count++;
|
|
@@ -1285,7 +1353,7 @@ function runningTime(seconds) {
|
|
|
1285
1353
|
});
|
|
1286
1354
|
return `${shown} ${unit}${shown === "1" ? "" : "s"}`;
|
|
1287
1355
|
}
|
|
1288
|
-
/** The sandboxes
|
|
1356
|
+
/** The sandboxes free usage paid for, said beside what was priced. */
|
|
1289
1357
|
function trialNote(trial, all, trialSeconds) {
|
|
1290
1358
|
if (!trial)
|
|
1291
1359
|
return "";
|
|
@@ -1294,7 +1362,7 @@ function trialNote(trial, all, trialSeconds) {
|
|
|
1294
1362
|
? "It"
|
|
1295
1363
|
: "All of them"
|
|
1296
1364
|
: `${trial} of them, ${runningTime(trialSeconds)} of that time,`;
|
|
1297
|
-
return ` ${which} ran on
|
|
1365
|
+
return ` ${which} ran on free usage, which charged nothing; Runtime's side prices ${trial === 1 ? "it" : "them"} at the standard rates, what the same work costs on paid credit.`;
|
|
1298
1366
|
}
|
|
1299
1367
|
async function switching(command, argv, env, out) {
|
|
1300
1368
|
const args = parse(argv, [], command === "compare" ? ["from", "days"] : ["from"]);
|
|
@@ -1474,11 +1542,13 @@ async function sandbox(argv, env, out) {
|
|
|
1474
1542
|
s.state,
|
|
1475
1543
|
`${s.info.vcpu}/${s.info.memoryMiB}`,
|
|
1476
1544
|
s.info.funding,
|
|
1477
|
-
s
|
|
1545
|
+
// When it stops or pauses by itself (0381): its time limit's end, or
|
|
1546
|
+
// "-" when it never will. An older server sends no endsAt.
|
|
1547
|
+
(s.info.endsAt === undefined ? s.info.expiresAt : s.info.endsAt) ?? "-",
|
|
1478
1548
|
]);
|
|
1479
1549
|
print([
|
|
1480
1550
|
rows.length
|
|
1481
|
-
? table([["ID", "NAME", "STATE", "CPU/MIB", "FUNDING", "
|
|
1551
|
+
? table([["ID", "NAME", "STATE", "CPU/MIB", "FUNDING", "ENDS"], ...rows])
|
|
1482
1552
|
: stopped
|
|
1483
1553
|
? "No live sandboxes."
|
|
1484
1554
|
: "No sandboxes. Create one: runtime sandbox create",
|
|
@@ -1704,7 +1774,9 @@ async function sandbox(argv, env, out) {
|
|
|
1704
1774
|
const sbx = await get(rest[0]);
|
|
1705
1775
|
const seconds = Number(need(rest[1], "seconds, e.g. runtime sandbox extend <id> 600"));
|
|
1706
1776
|
await sbx.extend(seconds);
|
|
1707
|
-
print(
|
|
1777
|
+
print(sbx.info.endsAt === null
|
|
1778
|
+
? `${sbx.id} has no time limit: it runs while it works, so nothing was extended`
|
|
1779
|
+
: `${sbx.id} now ends at ${sbx.info.endsAt ?? sbx.info.expiresAt}`, sbx.info);
|
|
1708
1780
|
return 0;
|
|
1709
1781
|
}
|
|
1710
1782
|
case "exec": {
|
|
@@ -2783,8 +2855,8 @@ read = readSecret) {
|
|
|
2783
2855
|
}
|
|
2784
2856
|
const JOB_HELP = `runtime job <command>
|
|
2785
2857
|
|
|
2786
|
-
A job runs a command in a fresh sandbox, once or on a schedule. Each run
|
|
2787
|
-
|
|
2858
|
+
A job runs a command in a fresh sandbox, once or on a schedule. Each run spends
|
|
2859
|
+
the included usage first, then credit at the sandbox rates.
|
|
2788
2860
|
|
|
2789
2861
|
create <name> (--cron "<min hour day month weekday>" [--timezone Europe/Berlin] | --at <time|now>)
|
|
2790
2862
|
[--vcpu 2] [--memory 4096] [--disk 4096] [--cpu shared|reserved] [--cpu-floor <thousandths>]
|
|
@@ -3230,7 +3302,7 @@ const CREATE_FLAGS = [
|
|
|
3230
3302
|
"get-or-create",
|
|
3231
3303
|
];
|
|
3232
3304
|
/** Says on standard error, once for each reason, why a create has not
|
|
3233
|
-
* answered yet: it is waiting for a
|
|
3305
|
+
* answered yet: it is waiting for a slot without credit or room, which can take up to
|
|
3234
3306
|
* two minutes, and silence reads as a hang. */
|
|
3235
3307
|
function waiting(out) {
|
|
3236
3308
|
const said = new Set();
|
package/dist/client.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { RuntimeError } from "./errors.js";
|
|
2
|
-
import type { Page } from "./page.js";
|
|
3
1
|
import { type ClientExtensions } from "./products/index.js";
|
|
4
2
|
import { usageExport as makeUsageExport } from "./products/observability.js";
|
|
3
|
+
import { Machines } from "./machine.js";
|
|
5
4
|
import { Sandbox } from "./sandbox.js";
|
|
6
5
|
import { Snapshots } from "./snapshots.js";
|
|
7
6
|
import { Transport, type RequestOptions } from "./transport.js";
|
|
8
7
|
import type { CreateSandbox, DeletedSandbox, FeedbackKind, SandboxInfo, Usage } from "./types.js";
|
|
8
|
+
export type { CreateOptions } from "./machine.js";
|
|
9
9
|
export type RuntimeOptions = {
|
|
10
10
|
/** Default: the RUNTIME_API_KEY environment variable. */
|
|
11
11
|
apiKey?: string;
|
|
@@ -16,11 +16,18 @@ export type RuntimeOptions = {
|
|
|
16
16
|
timeoutMs?: number;
|
|
17
17
|
/** Retries of transport failures, 429, 502, 503 and 504. Default 4. */
|
|
18
18
|
maxRetries?: number;
|
|
19
|
-
/**
|
|
19
|
+
/** Connections held at once, calls and streams (a command's output, a
|
|
20
|
+
* download) together; more wait their turn, first come first served, and
|
|
21
|
+
* reuse them. Default 48: one address may hold 64 connections to the API
|
|
22
|
+
* before it has used a valid key. 8 are kept for calls whatever the
|
|
23
|
+
* streams hold. */
|
|
20
24
|
maxConnections?: number;
|
|
25
|
+
/** Called each time a call has to wait for one of those connections: an
|
|
26
|
+
* adapter warns with it, once, that its caller is being held back. */
|
|
27
|
+
onQueued?: (maxConnections: number) => void;
|
|
21
28
|
/** How long `sandboxes.create` keeps retrying, with the same key and input,
|
|
22
|
-
* when the
|
|
23
|
-
* (
|
|
29
|
+
* when the slots without credit, the account's quota or the region is full
|
|
30
|
+
* (no_credit_running_limit, quota_exceeded, no_capacity and the like). Default 120 000
|
|
24
31
|
* (two minutes); 0 fails at once. The refusal is thrown as it came when the
|
|
25
32
|
* wait runs out. */
|
|
26
33
|
waitForCapacityMs?: number;
|
|
@@ -53,8 +60,8 @@ export declare class Runtime {
|
|
|
53
60
|
/** The role of the member who made this key: owner, admin, developer or billing. */
|
|
54
61
|
role?: string | null;
|
|
55
62
|
}>;
|
|
56
|
-
/** Balance, holds,
|
|
57
|
-
/** Credit, holds,
|
|
63
|
+
/** Balance, holds, free time and per-resource charges, in integer microdollars. */
|
|
64
|
+
/** Credit, holds, free time and per-resource charges. */
|
|
58
65
|
usage(options?: RequestOptions): Promise<Usage>;
|
|
59
66
|
/** This account's authenticated API answers, in hourly windows. Counts are
|
|
60
67
|
* exact decimal strings, and an empty window has a null error percentage.
|
|
@@ -76,60 +83,9 @@ export declare class Runtime {
|
|
|
76
83
|
}>;
|
|
77
84
|
}>;
|
|
78
85
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
export type CreateOptions = RequestOptions & {
|
|
82
|
-
/** How long to keep retrying a refusal that clears by itself. Default: the
|
|
83
|
-
* client's, two minutes; 0 fails at once. */
|
|
84
|
-
waitForCapacityMs?: number;
|
|
85
|
-
/** Called before each wait, with the refusal and the milliseconds until the
|
|
86
|
-
* next try: to tell a person why nothing has happened yet. */
|
|
87
|
-
onCapacityWait?: (refusal: RuntimeError, waitMs: number) => void;
|
|
88
|
-
};
|
|
89
|
-
export declare class Sandboxes {
|
|
90
|
-
private readonly t;
|
|
86
|
+
/** `runtime.sandboxes`: the sandbox product of the machine engine. */
|
|
87
|
+
export declare class Sandboxes extends Machines<Sandbox, SandboxInfo, CreateSandbox, DeletedSandbox> {
|
|
91
88
|
constructor(t: Transport);
|
|
92
|
-
/** Creates a sandbox and waits until it is running. Every field is optional:
|
|
93
|
-
* with none you get the free trial while it lasts, the default region and a
|
|
94
|
-
* 2 vCPU / 4 GiB machine from a warm template. `wait: false` returns at once.
|
|
95
|
-
* When every trial slot or the account's quota is taken, it waits for one to
|
|
96
|
-
* free, up to `waitForCapacityMs` (the client's, two minutes by default). */
|
|
97
|
-
create(input?: CreateSandbox & {
|
|
98
|
-
wait?: boolean;
|
|
99
|
-
}, options?: CreateOptions): Promise<Sandbox>;
|
|
100
|
-
/** The sandbox named `name` in this account, ready to use: running as it
|
|
101
|
-
* is, woken if it is paused, restarted if it is stopped and persistent, or
|
|
102
|
-
* created with `input` when no sandbox has the name. `sandbox.info.reused`
|
|
103
|
-
* says which. The other fields apply only when it is created. */
|
|
104
|
-
getOrCreate(name: string, input?: Omit<CreateSandbox, "name" | "getOrCreate"> & {
|
|
105
|
-
wait?: boolean;
|
|
106
|
-
}, options?: CreateOptions): Promise<Sandbox>;
|
|
107
|
-
get(id: string, options?: RequestOptions): Promise<Sandbox>;
|
|
108
|
-
/** Deletes a sandbox for good, by id, without reading it first: see
|
|
109
|
-
* `sandbox.delete()`. Deleting it again answers the same. */
|
|
110
|
-
delete(id: string, options?: RequestOptions): Promise<DeletedSandbox>;
|
|
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. */
|
|
113
|
-
list(filter?: {
|
|
114
|
-
state?: SandboxInfo["state"][];
|
|
115
|
-
includeStopped?: boolean;
|
|
116
|
-
/** Every label must match. */
|
|
117
|
-
labels?: Record<string, string>;
|
|
118
|
-
name?: string;
|
|
119
|
-
limit?: number;
|
|
120
|
-
}, options?: RequestOptions): Promise<Page<Sandbox>>;
|
|
121
|
-
/** Stops every live sandbox whose labels all match, eight at a time, and
|
|
122
|
-
* says which stopped and which failed; one failure does not stop the rest.
|
|
123
|
-
* Needs at least one label, so stopping everything is never one call. */
|
|
124
|
-
stopAll(filter: {
|
|
125
|
-
labels: Record<string, string>;
|
|
126
|
-
}, options?: RequestOptions): Promise<{
|
|
127
|
-
stopped: string[];
|
|
128
|
-
failed: {
|
|
129
|
-
id: string;
|
|
130
|
-
error: unknown;
|
|
131
|
-
}[];
|
|
132
|
-
}>;
|
|
133
89
|
}
|
|
134
90
|
export declare class FeedbackApi {
|
|
135
91
|
private readonly t;
|
package/dist/client.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { WAIT_FOR_TIMEOUT_SECONDS } from "./api-defaults.js";
|
|
2
|
-
import { RuntimeError } from "./errors.js";
|
|
3
1
|
import { clientFactories, clientProductAliases } from "./products/index.js";
|
|
4
2
|
import { usageExport as makeUsageExport } from "./products/observability.js";
|
|
5
|
-
import {
|
|
3
|
+
import { Machines } from "./machine.js";
|
|
4
|
+
import { SANDBOXES, Sandbox } from "./sandbox.js";
|
|
6
5
|
import { Snapshots } from "./snapshots.js";
|
|
7
|
-
import { Transport, missingKey } from "./transport.js";
|
|
6
|
+
import { DEFAULT_BASE_URL, Transport, missingKey } from "./transport.js";
|
|
8
7
|
/** The key `runtime login` saved for this machine and this API origin, found
|
|
9
8
|
* on first use. A constructor override must never receive another API's key. */
|
|
10
9
|
const savedKey = async (apiOrigin) => {
|
|
@@ -30,15 +29,20 @@ export class Runtime {
|
|
|
30
29
|
/** Date-range settlements, paged with exact charges and the server's CSV. */
|
|
31
30
|
usageExport;
|
|
32
31
|
constructor(options = {}) {
|
|
32
|
+
// The saved key is found by the origin as given, as `runtime login` saved
|
|
33
|
+
// it; the calls go where that origin is reachable from here, which in a
|
|
34
|
+
// sandbox is runtime.internal (transport.ts, `reachable`).
|
|
35
|
+
const origin = options.baseUrl || env("RUNTIME_API_URL") || DEFAULT_BASE_URL;
|
|
33
36
|
this.transport = new Transport({
|
|
34
37
|
// A key given, then RUNTIME_API_KEY, then (in Node and Bun) the key
|
|
35
38
|
// `runtime login` saved for this machine, read on the first call.
|
|
36
|
-
apiKey: options.apiKey ?? env("RUNTIME_API_KEY") ?? (() => savedKey(
|
|
39
|
+
apiKey: options.apiKey ?? env("RUNTIME_API_KEY") ?? (() => savedKey(origin)),
|
|
37
40
|
baseUrl: options.baseUrl ?? env("RUNTIME_API_URL"),
|
|
38
41
|
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
39
42
|
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
40
43
|
...(options.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),
|
|
41
44
|
...(options.maxConnections !== undefined ? { maxConnections: options.maxConnections } : {}),
|
|
45
|
+
...(options.onQueued ? { onQueued: options.onQueued } : {}),
|
|
42
46
|
...(options.waitForCapacityMs !== undefined
|
|
43
47
|
? { waitForCapacityMs: options.waitForCapacityMs }
|
|
44
48
|
: {}),
|
|
@@ -64,8 +68,8 @@ export class Runtime {
|
|
|
64
68
|
...options,
|
|
65
69
|
});
|
|
66
70
|
}
|
|
67
|
-
/** Balance, holds,
|
|
68
|
-
/** Credit, holds,
|
|
71
|
+
/** Balance, holds, free time and per-resource charges, in integer microdollars. */
|
|
72
|
+
/** Credit, holds, free time and per-resource charges. */
|
|
69
73
|
usage(options = {}) {
|
|
70
74
|
return this.transport.json({
|
|
71
75
|
method: "GET",
|
|
@@ -85,103 +89,10 @@ export class Runtime {
|
|
|
85
89
|
});
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
/** `runtime.sandboxes`: the sandbox product of the machine engine. */
|
|
93
|
+
export class Sandboxes extends Machines {
|
|
90
94
|
constructor(t) {
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
/** Creates a sandbox and waits until it is running. Every field is optional:
|
|
94
|
-
* with none you get the free trial while it lasts, the default region and a
|
|
95
|
-
* 2 vCPU / 4 GiB machine from a warm template. `wait: false` returns at once.
|
|
96
|
-
* When every trial slot or the account's quota is taken, it waits for one to
|
|
97
|
-
* free, up to `waitForCapacityMs` (the client's, two minutes by default). */
|
|
98
|
-
async create(input = {}, options = {}) {
|
|
99
|
-
const { wait, ...body } = input;
|
|
100
|
-
const info = await this.t.json({
|
|
101
|
-
method: "POST",
|
|
102
|
-
path: "/v1/sandboxes",
|
|
103
|
-
body,
|
|
104
|
-
wait: wait === false ? 0 : 60,
|
|
105
|
-
...options,
|
|
106
|
-
waitForCapacityMs: options.waitForCapacityMs ?? this.t.waitForCapacityMs,
|
|
107
|
-
});
|
|
108
|
-
const sandbox = new Sandbox(this.t, info);
|
|
109
|
-
if (wait !== false && info.state !== "running") {
|
|
110
|
-
await sandbox.waitFor("running", {
|
|
111
|
-
timeoutSeconds: WAIT_FOR_TIMEOUT_SECONDS,
|
|
112
|
-
signal: options.signal,
|
|
113
|
-
timeoutMs: options.timeoutMs,
|
|
114
|
-
idempotencyKey: options.idempotencyKey,
|
|
115
|
-
});
|
|
116
|
-
if (sandbox.state !== "running")
|
|
117
|
-
throw new RuntimeError({
|
|
118
|
-
message: `Sandbox ${info.id} is ${sandbox.state}, not running.`,
|
|
119
|
-
code: "start_failed",
|
|
120
|
-
status: 0,
|
|
121
|
-
hint: "Read it with runtime.sandboxes.get(id); stopReason says why.",
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
return sandbox;
|
|
125
|
-
}
|
|
126
|
-
/** The sandbox named `name` in this account, ready to use: running as it
|
|
127
|
-
* is, woken if it is paused, restarted if it is stopped and persistent, or
|
|
128
|
-
* created with `input` when no sandbox has the name. `sandbox.info.reused`
|
|
129
|
-
* says which. The other fields apply only when it is created. */
|
|
130
|
-
async getOrCreate(name, input = {}, options = {}) {
|
|
131
|
-
return this.create({ ...input, name, getOrCreate: true }, options);
|
|
132
|
-
}
|
|
133
|
-
async get(id, options = {}) {
|
|
134
|
-
return new Sandbox(this.t, await this.t.json({
|
|
135
|
-
method: "GET",
|
|
136
|
-
path: `/v1/sandboxes/${encodeURIComponent(id)}`,
|
|
137
|
-
...options,
|
|
138
|
-
}));
|
|
139
|
-
}
|
|
140
|
-
/** Deletes a sandbox for good, by id, without reading it first: see
|
|
141
|
-
* `sandbox.delete()`. Deleting it again answers the same. */
|
|
142
|
-
delete(id, options = {}) {
|
|
143
|
-
return deleteSandbox(this.t, id, options);
|
|
144
|
-
}
|
|
145
|
-
/** Live sandboxes, oldest first. Await for a page, or `for await` over all.
|
|
146
|
-
* Request options apply to the initial request and every subsequent page. */
|
|
147
|
-
async list(filter = {}, options = {}) {
|
|
148
|
-
const query = {
|
|
149
|
-
...(filter.state ? { state: filter.state } : {}),
|
|
150
|
-
...(filter.includeStopped ? { includeStopped: true } : {}),
|
|
151
|
-
...(filter.labels
|
|
152
|
-
? { label: Object.entries(filter.labels).map(([k, v]) => `${k}:${v}`) }
|
|
153
|
-
: {}),
|
|
154
|
-
...(filter.name ? { name: filter.name } : {}),
|
|
155
|
-
...(filter.limit ? { limit: filter.limit } : {}),
|
|
156
|
-
};
|
|
157
|
-
return sandboxPage(this.t, await this.t.json({ method: "GET", path: "/v1/sandboxes", query, ...options }), query, options);
|
|
158
|
-
}
|
|
159
|
-
/** Stops every live sandbox whose labels all match, eight at a time, and
|
|
160
|
-
* says which stopped and which failed; one failure does not stop the rest.
|
|
161
|
-
* Needs at least one label, so stopping everything is never one call. */
|
|
162
|
-
async stopAll(filter, options = {}) {
|
|
163
|
-
if (!filter?.labels || Object.keys(filter.labels).length === 0)
|
|
164
|
-
throw new RuntimeError({
|
|
165
|
-
message: "stopAll needs at least one label to match.",
|
|
166
|
-
code: "invalid_request",
|
|
167
|
-
status: 0,
|
|
168
|
-
hint: "Stop one sandbox with sandbox.stop(), or label the ones to stop together.",
|
|
169
|
-
});
|
|
170
|
-
const live = await (await this.list({ labels: filter.labels, limit: 100 }, options)).toArray(Infinity);
|
|
171
|
-
// One key cannot name several stops: each stop makes its own.
|
|
172
|
-
const { idempotencyKey: _one, ...each } = options;
|
|
173
|
-
// In list order, oldest first, whichever finishes first.
|
|
174
|
-
const errors = new Array(live.length);
|
|
175
|
-
let next = 0;
|
|
176
|
-
const worker = async () => {
|
|
177
|
-
for (let at = next++; at < live.length; at = next++)
|
|
178
|
-
await live[at].stop(each).catch((error) => (errors[at] = error ?? "failed"));
|
|
179
|
-
};
|
|
180
|
-
await Promise.all(Array.from({ length: Math.min(8, live.length) }, worker));
|
|
181
|
-
return {
|
|
182
|
-
stopped: live.filter((_, at) => errors[at] === undefined).map((sbx) => sbx.id),
|
|
183
|
-
failed: live.flatMap((sbx, at) => errors[at] === undefined ? [] : [{ id: sbx.id, error: errors[at] }]),
|
|
184
|
-
};
|
|
95
|
+
super(SANDBOXES, t, (transport, info) => new Sandbox(transport, info));
|
|
185
96
|
}
|
|
186
97
|
}
|
|
187
98
|
export class FeedbackApi {
|
package/dist/compat/files.js
CHANGED
|
@@ -63,10 +63,14 @@ export class GuestFiles {
|
|
|
63
63
|
if (!overwrite) {
|
|
64
64
|
// cp -n alone reports success when it skips a raced destination. Stage
|
|
65
65
|
// privately, then publish without replacement and verify it moved.
|
|
66
|
+
// mv -n that skips succeeds before coreutils 9.2 and fails from it on
|
|
67
|
+
// (the guest's 9.4 says "not replacing"), so a skip is told by the
|
|
68
|
+
// staged copy still being there beside a destination; with none there,
|
|
69
|
+
// mv's own error stands.
|
|
66
70
|
await checked(sandbox, [
|
|
67
71
|
"bash",
|
|
68
72
|
"-c",
|
|
69
|
-
'set -e; parent=$(dirname -- "$2"); stage="$parent/.runtime-copy-$4"; mkdir -m 700 -- "$stage"; trap \'rm -rf -- "$stage"\' EXIT; if [ "$3" = true ]; then cp -R
|
|
73
|
+
'set -e; parent=$(dirname -- "$2"); stage="$parent/.runtime-copy-$4"; mkdir -m 700 -- "$stage"; trap \'rm -rf -- "$stage"\' EXIT; if [ "$3" = true ]; then cp -R -- "$1" "$stage/item"; else cp -- "$1" "$stage/item"; fi; mv -n -T -- "$stage/item" "$2" 2>"$stage/mv.err" || :; if [ -e "$stage/item" ] || [ -L "$stage/item" ]; then if [ -e "$2" ] || [ -L "$2" ]; then printf "Destination exists: %s\\n" "$2" >&2; exit 73; fi; cat -- "$stage/mv.err" >&2; exit 1; fi',
|
|
70
74
|
"bash",
|
|
71
75
|
this.path(from),
|
|
72
76
|
this.path(to),
|