@kvbox/cli 0.5.10 → 0.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/kvbox.js CHANGED
@@ -147,7 +147,7 @@ async function spin(message, work, opts = {}) {
147
147
  }
148
148
 
149
149
  // src/program.ts
150
- import { Command as Command40 } from "commander";
150
+ import { Command as Command41 } from "commander";
151
151
 
152
152
  // ../shared/src/version.ts
153
153
  import { createRequire } from "module";
@@ -162,10 +162,10 @@ function versionOf(srcFileUrl, injected) {
162
162
  }
163
163
 
164
164
  // src/version.ts
165
- var VERSION = versionOf(import.meta.url, true ? "0.5.10" : void 0);
165
+ var VERSION = versionOf(import.meta.url, true ? "0.5.12" : void 0);
166
166
 
167
167
  // src/commands/vm/index.ts
168
- import { Command as Command16 } from "commander";
168
+ import { Command as Command17 } from "commander";
169
169
 
170
170
  // src/commands/vm/connect.ts
171
171
  import { spawnSync as spawnSync2 } from "child_process";
@@ -529,6 +529,23 @@ var MachineUsageSchema = z2.object({
529
529
  estimatedStops: z2.number().int(),
530
530
  historyFrom: z2.string().nullable()
531
531
  });
532
+ var GuestStatsSchema = z2.object({
533
+ /** Load average over 1, 5 and 15 minutes. */
534
+ load: z2.tuple([z2.number(), z2.number(), z2.number()]),
535
+ cpus: z2.number().int(),
536
+ memTotalMib: z2.number().int(),
537
+ /** Total minus available: page cache counts as free. */
538
+ memUsedMib: z2.number().int(),
539
+ /** The box's root, which is its overlay disk: the box's own writes, not the shared base. */
540
+ diskTotalBytes: z2.number().int(),
541
+ diskUsedBytes: z2.number().int(),
542
+ uptimeSec: z2.number().int()
543
+ });
544
+ var MachineStatsSchema = z2.object({
545
+ name: z2.string(),
546
+ owner: z2.string(),
547
+ stats: GuestStatsSchema.nullable()
548
+ });
532
549
  var UsageStatusSchema = z2.enum([...BoxStatusSchema.options, "removed"]);
533
550
  var BoxUsageSchema = MachineUsageSchema.omit({ window: true, status: true }).extend({
534
551
  /** The host's box id; a name removed and recreated appears twice, with two ids. */
@@ -803,6 +820,7 @@ async function resolveTarget(opts, p = paths()) {
803
820
  }
804
821
  return { kind: "local", socketPath: p.serverSock };
805
822
  }
823
+ var allQuery = (opts) => opts.all ? "?all=1" : "";
806
824
  var ServerClient = class {
807
825
  constructor(target, opts = {}) {
808
826
  this.target = target;
@@ -869,9 +887,12 @@ var ServerClient = class {
869
887
  capacity() {
870
888
  return this.call("GET", "/capacity", CapacitySchema);
871
889
  }
872
- /** Through a control plane: every box the user can see, or the `--org` org's. */
873
- listMachines() {
874
- return this.call("GET", "/machines", z3.array(MachineSchema));
890
+ /**
891
+ * Through a control plane: the user's boxes and their orgs', the `--org` org's, or `org`'s;
892
+ * `all`: every box on the platform (platform admins only, #78).
893
+ */
894
+ listMachines(opts = {}) {
895
+ return this.call("GET", `/machines${allQuery(opts)}`, z3.array(MachineSchema), void 0, opts.org);
875
896
  }
876
897
  createMachine(req) {
877
898
  return this.call("POST", "/machines", MachineSchema, req);
@@ -903,14 +924,19 @@ var ServerClient = class {
903
924
  setMachine(name, req) {
904
925
  return this.call("POST", `/machines/${encodeURIComponent(name)}/set`, MachineSchema, req);
905
926
  }
927
+ /** A running box's own load, memory and disk, asked of its guest (#67). */
928
+ machineStats(name) {
929
+ return this.call("GET", `/machines/${encodeURIComponent(name)}/stats`, MachineStatsSchema);
930
+ }
906
931
  /** `since`: an ISO date, already validated; the server counts from the box's creation without it. */
907
932
  machineUsage(name, since) {
908
933
  const query = since === void 0 ? "" : `?since=${encodeURIComponent(since)}`;
909
934
  return this.call("GET", `/machines/${encodeURIComponent(name)}/usage${query}`, MachineUsageSchema);
910
935
  }
911
936
  /** Every box of the caller over the window (#41 on a host, #42 through a control plane); dates as the user wrote them. */
912
- usageReport(since, until) {
937
+ usageReport(since, until, all = false) {
913
938
  const q = new URLSearchParams();
939
+ if (all) q.set("all", "1");
914
940
  if (since !== void 0) q.set("since", since);
915
941
  if (until !== void 0) q.set("until", until);
916
942
  const query = q.size === 0 ? "" : `?${q.toString()}`;
@@ -923,8 +949,9 @@ var ServerClient = class {
923
949
  deleteMachine(name) {
924
950
  return this.call("DELETE", `/machines/${encodeURIComponent(name)}`, z3.undefined());
925
951
  }
926
- listSnapshots() {
927
- return this.call("GET", "/snapshots", z3.array(SnapshotSchema));
952
+ /** Like `listMachines`, for snapshots. */
953
+ listSnapshots(opts = {}) {
954
+ return this.call("GET", `/snapshots${allQuery(opts)}`, z3.array(SnapshotSchema), void 0, opts.org);
928
955
  }
929
956
  createSnapshot(req) {
930
957
  return this.call("POST", "/snapshots", SnapshotSchema, req);
@@ -1010,6 +1037,11 @@ async function wakeUp(client, box, name) {
1010
1037
  return box;
1011
1038
  }
1012
1039
  }
1040
+ function boxesFor(client, text) {
1041
+ const slash = text.indexOf("/");
1042
+ if (slash === -1 || client.target.kind !== "cloud") return client.listMachines();
1043
+ return client.listMachines({ org: text.slice(0, slash).split("@")[0] });
1044
+ }
1013
1045
  var me = (client) => client.target.kind === "cloud" ? client.target.user : void 0;
1014
1046
  async function clientFor(opts, client = {}) {
1015
1047
  return new ServerClient(await resolveTarget(opts), client);
@@ -1179,7 +1211,7 @@ var connectCommand = new Command2("connect").alias("cn").description("open a she
1179
1211
  let cert;
1180
1212
  let owner;
1181
1213
  try {
1182
- let box = pickRef(await client.listMachines(), name, me(client));
1214
+ let box = pickRef(await boxesFor(client, name), name, me(client));
1183
1215
  if (!box) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1184
1216
  cert = await ensureCert(client, void 0, void 0, { owner: box.owner });
1185
1217
  owner = box.owner;
@@ -1257,7 +1289,7 @@ var execCommand = new Command3("exec").description("run a command in a box and e
1257
1289
  let cert;
1258
1290
  let owner;
1259
1291
  try {
1260
- let box = pickRef(await client.listMachines(), name, me(client));
1292
+ let box = pickRef(await boxesFor(client, name), name, me(client));
1261
1293
  if (!box) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1262
1294
  if (box.status === "stopped") throw new KvboxError(`box ${name} is stopped (kvbox start ${name})`);
1263
1295
  cert = await ensureCert(client, void 0, void 0, { owner: box.owner });
@@ -1313,7 +1345,7 @@ var hibernateCommand = new Command4("hibernate").description("save a box's memor
1313
1345
  });
1314
1346
 
1315
1347
  // src/commands/vm/ls.ts
1316
- import { Command as Command5, Option as Option2 } from "commander";
1348
+ import { Command as Command5 } from "commander";
1317
1349
  function ownerCell(b, me2) {
1318
1350
  const slug = slugOf(b.owner);
1319
1351
  if (slug === me2) return "private";
@@ -1323,10 +1355,10 @@ function addressOf(b) {
1323
1355
  if (b.ssh !== void 0) return `${b.ssh.host}:${b.ssh.port}`;
1324
1356
  return b.ip === "" ? "-" : b.ip;
1325
1357
  }
1326
- var lsCommand = new Command5("ls").description("list boxes with status and address (yours and your orgs'; --org <slug> for one org)").addOption(new Option2("-a, --all", "kept for older scripts: the listing covers everything you can see anyway").hideHelp()).option("--json", "print as JSON").action(async (opts, cmd) => {
1358
+ var lsCommand = new Command5("ls").description("list boxes with status and address (yours and your orgs'; --org <slug> for one org)").option("-a, --all", "every box on the platform (platform admins only)").option("--json", "print as JSON").action(async (opts, cmd) => {
1327
1359
  const client = await clientFor(cmd.optsWithGlobals());
1328
1360
  try {
1329
- const boxes = await client.listMachines();
1361
+ const boxes = await client.listMachines({ all: opts.all });
1330
1362
  if (opts.json) {
1331
1363
  json(boxes);
1332
1364
  return;
@@ -1367,8 +1399,8 @@ var pauseCommand = new Command6("pause").description("freeze a box in place (mem
1367
1399
  });
1368
1400
 
1369
1401
  // src/commands/vm/rescale.ts
1370
- import { Command as Command7, Option as Option3 } from "commander";
1371
- var rescaleCommand = new Command7("rescale").description("change a box's vCPUs, memory or disk (e.g. --vcpu 4 --mem 16GB); a running box restarts").argument("<name>", "box name").addOption(new Option3("-s, --size <size>", "a preset to start from").choices(SIZE_NAMES)).option("--vcpu <n>", "number of vCPUs").option("--mem <size>", "memory, e.g. 16GB or 4096MB").option("--disk <size>", "disk size, e.g. 32GB (grow only)").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1402
+ import { Command as Command7, Option as Option2 } from "commander";
1403
+ var rescaleCommand = new Command7("rescale").description("change a box's vCPUs, memory or disk (e.g. --vcpu 4 --mem 16GB); a running box restarts").argument("<name>", "box name").addOption(new Option2("-s, --size <size>", "a preset to start from").choices(SIZE_NAMES)).option("--vcpu <n>", "number of vCPUs").option("--mem <size>", "memory, e.g. 16GB or 4096MB").option("--disk <size>", "disk size, e.g. 32GB (grow only)").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1372
1404
  const req = {
1373
1405
  ...opts.size !== void 0 ? { size: opts.size } : {},
1374
1406
  ...opts.vcpu !== void 0 ? { vcpu: parseCount(opts.vcpu) } : {},
@@ -1378,7 +1410,7 @@ var rescaleCommand = new Command7("rescale").description("change a box's vCPUs,
1378
1410
  if (Object.keys(req).length === 0) throw new KvboxError("nothing to change: give --size, --vcpu, --mem or --disk");
1379
1411
  const client = await clientFor(cmd.optsWithGlobals());
1380
1412
  try {
1381
- const before = pickRef(await client.listMachines(), name, me(client));
1413
+ const before = pickRef(await boxesFor(client, name), name, me(client));
1382
1414
  if (!before) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1383
1415
  const box = await spin(`rescaling ${name}\u2026`, client.rescaleMachine(displayName(before, me(client)), req));
1384
1416
  if (opts.json) {
@@ -1430,7 +1462,7 @@ import { Command as Command9 } from "commander";
1430
1462
  var rmCommand = new Command9("rm").description("destroy a box (its disk is deleted; snapshots are kept)").argument("<name>", "box name").option("-y, --yes", "do not ask for confirmation").action(async (name, opts, cmd) => {
1431
1463
  const client = await clientFor(cmd.optsWithGlobals());
1432
1464
  try {
1433
- const box = pickRef(await client.listMachines(), name, me(client));
1465
+ const box = pickRef(await boxesFor(client, name), name, me(client));
1434
1466
  if (!box) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1435
1467
  const ref = displayName(box, me(client));
1436
1468
  if (!await confirm(`Destroy box ${ref} and its disk?`, opts.yes ?? false)) {
@@ -1452,7 +1484,7 @@ var setCommand = new Command10("set").description("change a box's settings, e.g.
1452
1484
  const autoHibernateSec = parseIdleTimeout(opts.autoHibernate);
1453
1485
  const client = await clientFor(cmd.optsWithGlobals());
1454
1486
  try {
1455
- const found = pickRef(await client.listMachines(), name, me(client));
1487
+ const found = pickRef(await boxesFor(client, name), name, me(client));
1456
1488
  if (!found) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1457
1489
  const box = await spin(`updating ${name}\u2026`, client.setMachine(displayName(found, me(client)), { autoHibernateSec }));
1458
1490
  if (opts.json) {
@@ -1491,12 +1523,73 @@ var stopCommand = new Command12("stop").description("shut a box down (its disk i
1491
1523
  }
1492
1524
  });
1493
1525
 
1494
- // src/commands/vm/update.ts
1526
+ // src/commands/vm/top.ts
1495
1527
  import { Command as Command13 } from "commander";
1496
- var updateCommand = new Command13("update").description("move a box onto the current base image (its files stay; a running box restarts)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1528
+ var topCommand = new Command13("top").description("load, memory and disk of each running box, as the box itself sees them").option("-a, --all", "every running box on the platform (platform admins only)").option("--json", "print as JSON").action(async (opts, cmd) => {
1529
+ const client = await clientFor(cmd.optsWithGlobals());
1530
+ try {
1531
+ const running = (await client.listMachines({ all: opts.all })).filter((b) => b.status === "running");
1532
+ const entries = await mapLimit(running, ASK_AT_ONCE, async (b) => {
1533
+ const who = { name: b.name, owner: b.owner, ...b.ownerKind === void 0 ? {} : { ownerKind: b.ownerKind } };
1534
+ try {
1535
+ return { ...who, stats: (await client.machineStats(`${slugOf(b.owner)}/${b.name}`)).stats };
1536
+ } catch (err) {
1537
+ return { ...who, stats: null, error: err.message };
1538
+ }
1539
+ });
1540
+ if (entries.length > 0 && entries.every((e) => e.error !== void 0)) process.exitCode = 1;
1541
+ if (opts.json) {
1542
+ json(entries);
1543
+ return;
1544
+ }
1545
+ if (entries.length === 0) {
1546
+ info("no running boxes");
1547
+ return;
1548
+ }
1549
+ table(topRows(entries, client.target.kind === "cloud" ? { me: client.target.user } : void 0));
1550
+ for (const e of entries) if (e.error !== void 0) info(color.dim(`${e.name}: ${e.error}`));
1551
+ if (entries.some((e) => e.stats === null && e.error === void 0)) {
1552
+ info(color.dim("-: the box's image is older than base v0.7.0; kvbox vm update <box> moves it onto the current one"));
1553
+ }
1554
+ } finally {
1555
+ await client.close();
1556
+ }
1557
+ });
1558
+ var ASK_AT_ONCE = 8;
1559
+ async function mapLimit(items, limit, fn) {
1560
+ const out = new Array(items.length);
1561
+ let next = 0;
1562
+ const worker = async () => {
1563
+ while (next < items.length) {
1564
+ const i = next++;
1565
+ out[i] = await fn(items[i]);
1566
+ }
1567
+ };
1568
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
1569
+ return out;
1570
+ }
1571
+ var pct = (used, total) => total > 0 ? color.dim(` ${Math.round(used / total * 100)}%`) : "";
1572
+ function topRows(entries, cloud) {
1573
+ return entries.map((e) => {
1574
+ const s = e.stats;
1575
+ return {
1576
+ ...cloud ? { owner: ownerCell(e, cloud.me) } : {},
1577
+ name: boxName(e.name),
1578
+ load: s ? `${s.load[0].toFixed(2)} ${s.load[1].toFixed(2)} ${color.dim(`/${s.cpus}`)}` : "-",
1579
+ mem: s ? `${gbShort(s.memUsedMib * 1024 ** 2)}/${gbShort(s.memTotalMib * 1024 ** 2)}${pct(s.memUsedMib, s.memTotalMib)}` : "-",
1580
+ disk: s ? `${gbShort(s.diskUsedBytes)}/${gbShort(s.diskTotalBytes)}${pct(s.diskUsedBytes, s.diskTotalBytes)}` : "-",
1581
+ // duration() writes "-" for 0, which here would read as "no numbers".
1582
+ uptime: s ? s.uptimeSec >= 1 ? duration(s.uptimeSec) : "0s" : "-"
1583
+ };
1584
+ });
1585
+ }
1586
+
1587
+ // src/commands/vm/update.ts
1588
+ import { Command as Command14 } from "commander";
1589
+ var updateCommand = new Command14("update").description("move a box onto the current base image (its files stay; a running box restarts)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1497
1590
  const client = await clientFor(cmd.optsWithGlobals());
1498
1591
  try {
1499
- const before = pickRef(await client.listMachines(), name, me(client));
1592
+ const before = pickRef(await boxesFor(client, name), name, me(client));
1500
1593
  if (!before) throw new KvboxError(`box ${name}: not found (kvbox ls)`);
1501
1594
  const box = await spin(`updating ${name}\u2026`, client.updateMachine(displayName(before, me(client))));
1502
1595
  if (opts.json) {
@@ -1515,8 +1608,8 @@ var updateCommand = new Command13("update").description("move a box onto the cur
1515
1608
  });
1516
1609
 
1517
1610
  // src/commands/vm/usage.ts
1518
- import { Command as Command14 } from "commander";
1519
- var usageCommand = new Command14("usage").description("how long a box ran, slept and sat stopped, as vCPU-hours, RAM and disk GiB-hours (and cost, when the host prices them)").argument("<name>", "box name").option("--since <date>", "count from this date instead of the box's creation, e.g. 2026-10-01").option("--json", "print as JSON").action(async (name, opts, cmd) => {
1611
+ import { Command as Command15 } from "commander";
1612
+ var usageCommand = new Command15("usage").description("how long a box ran, slept and sat stopped, as vCPU-hours, RAM and disk GiB-hours (and cost, when the host prices them)").argument("<name>", "box name").option("--since <date>", "count from this date instead of the box's creation, e.g. 2026-10-01").option("--json", "print as JSON").action(async (name, opts, cmd) => {
1520
1613
  let since;
1521
1614
  if (opts.since !== void 0) {
1522
1615
  const t = Date.parse(opts.since);
@@ -1563,8 +1656,8 @@ function block(u) {
1563
1656
  }
1564
1657
 
1565
1658
  // src/commands/vm/wake.ts
1566
- import { Command as Command15 } from "commander";
1567
- var wakeCommand = new Command15("wake").description("continue a hibernated box where it left off (same name, IP and disk)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1659
+ import { Command as Command16 } from "commander";
1660
+ var wakeCommand = new Command16("wake").description("continue a hibernated box where it left off (same name, IP and disk)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1568
1661
  const client = await clientFor(cmd.optsWithGlobals());
1569
1662
  try {
1570
1663
  const box = await spin(`waking ${name} (loading its memory from disk)\u2026`, client.wakeMachine(name));
@@ -1576,15 +1669,15 @@ var wakeCommand = new Command15("wake").description("continue a hibernated box w
1576
1669
  });
1577
1670
 
1578
1671
  // src/commands/vm/index.ts
1579
- var vmCommand = new Command16("vm").enablePositionalOptions().description("manage boxes (each one is a Firecracker microVM)").addCommand(newCommand).addCommand(lsCommand).addCommand(connectCommand).addCommand(execCommand).addCommand(stopCommand).addCommand(startCommand).addCommand(pauseCommand).addCommand(resumeCommand).addCommand(hibernateCommand).addCommand(wakeCommand).addCommand(updateCommand).addCommand(rescaleCommand).addCommand(setCommand).addCommand(usageCommand).addCommand(rmCommand);
1672
+ var vmCommand = new Command17("vm").enablePositionalOptions().description("manage boxes (each one is a Firecracker microVM)").addCommand(newCommand).addCommand(lsCommand).addCommand(connectCommand).addCommand(execCommand).addCommand(stopCommand).addCommand(startCommand).addCommand(pauseCommand).addCommand(resumeCommand).addCommand(hibernateCommand).addCommand(wakeCommand).addCommand(updateCommand).addCommand(rescaleCommand).addCommand(setCommand).addCommand(usageCommand).addCommand(topCommand).addCommand(rmCommand);
1580
1673
  for (const cmd of vmCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1581
1674
  var vmAliases = [newCommand, lsCommand, connectCommand, execCommand, stopCommand, startCommand, pauseCommand, resumeCommand, hibernateCommand, wakeCommand, rmCommand];
1582
1675
 
1583
1676
  // src/commands/server/index.ts
1584
- import { Command as Command24 } from "commander";
1677
+ import { Command as Command25 } from "commander";
1585
1678
 
1586
1679
  // src/commands/server/join.ts
1587
- import { Command as Command17 } from "commander";
1680
+ import { Command as Command18 } from "commander";
1588
1681
 
1589
1682
  // src/commands/server/daemon.ts
1590
1683
  import { spawn as spawn3 } from "child_process";
@@ -1638,7 +1731,7 @@ function runKvboxServer(args) {
1638
1731
  }
1639
1732
 
1640
1733
  // src/commands/server/join.ts
1641
- var joinCommand = new Command17("join").description("put this machine behind a control plane (needs a one-time token minted there) and restart its server").argument("<control-plane-url>", "the control plane, e.g. https://api.kvbox.sh").requiredOption("--token <token>", "one-time join token from the control plane").option("--advertise <host-or-ip>", "address the control plane reaches this machine at (default: its default-route address)").option("--name <name>", "how the control plane lists this machine (default: hostname)").action(async (url, opts) => {
1734
+ var joinCommand = new Command18("join").description("put this machine behind a control plane (needs a one-time token minted there) and restart its server").argument("<control-plane-url>", "the control plane, e.g. https://api.kvbox.sh").requiredOption("--token <token>", "one-time join token from the control plane").option("--advertise <host-or-ip>", "address the control plane reaches this machine at (default: its default-route address)").option("--name <name>", "how the control plane lists this machine (default: hostname)").action(async (url, opts) => {
1642
1735
  const args = ["join", url, "--token", opts.token];
1643
1736
  if (opts.advertise !== void 0) args.push("--advertise", opts.advertise);
1644
1737
  if (opts.name !== void 0) args.push("--name", opts.name);
@@ -1648,8 +1741,8 @@ var joinCommand = new Command17("join").description("put this machine behind a c
1648
1741
  });
1649
1742
 
1650
1743
  // src/commands/server/leave.ts
1651
- import { Command as Command18 } from "commander";
1652
- var leaveCommand = new Command18("leave").description("take this machine out from behind its control plane and restart its server").action(async () => {
1744
+ import { Command as Command19 } from "commander";
1745
+ var leaveCommand = new Command19("leave").description("take this machine out from behind its control plane and restart its server").action(async () => {
1653
1746
  const p = paths();
1654
1747
  for (const line of (await runKvboxServer(["leave"])).split("\n")) info(line);
1655
1748
  if (await runningPid(p) === null) return;
@@ -1658,14 +1751,14 @@ var leaveCommand = new Command18("leave").description("take this machine out fro
1658
1751
  });
1659
1752
 
1660
1753
  // src/commands/server/relay.ts
1661
- import { Command as Command19 } from "commander";
1662
- var relayCommand = new Command19("relay").description("reach the internet through a relay (paste what `kvbox-relay add` printed on the relay VM); `off` takes the tunnel down").argument("<join-string>", "the line `kvbox-relay add` printed, or `off`").action(async (joinString) => {
1754
+ import { Command as Command20 } from "commander";
1755
+ var relayCommand = new Command20("relay").description("reach the internet through a relay (paste what `kvbox-relay add` printed on the relay VM); `off` takes the tunnel down").argument("<join-string>", "the line `kvbox-relay add` printed, or `off`").action(async (joinString) => {
1663
1756
  for (const line of (await runKvboxServer(["relay", joinString])).split("\n")) info(line);
1664
1757
  });
1665
1758
 
1666
1759
  // src/commands/server/start.ts
1667
- import { Command as Command20 } from "commander";
1668
- var startCommand2 = new Command20("start").description("start the local kvbox-server in the background").action(async (_opts, cmd) => {
1760
+ import { Command as Command21 } from "commander";
1761
+ var startCommand2 = new Command21("start").description("start the local kvbox-server in the background").action(async (_opts, cmd) => {
1669
1762
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true }, { autoStart: false });
1670
1763
  try {
1671
1764
  const health = await client.health();
@@ -1681,8 +1774,8 @@ var startCommand2 = new Command20("start").description("start the local kvbox-se
1681
1774
  });
1682
1775
 
1683
1776
  // src/commands/server/status.ts
1684
- import { Command as Command21 } from "commander";
1685
- var statusCommand = new Command21("status").description("show whether this machine's server is running, its control plane and which base image new boxes get").option("--json", "print as JSON").action(async (opts, cmd) => {
1777
+ import { Command as Command22 } from "commander";
1778
+ var statusCommand = new Command22("status").description("show whether this machine's server is running, its control plane and which base image new boxes get").option("--json", "print as JSON").action(async (opts, cmd) => {
1686
1779
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
1687
1780
  try {
1688
1781
  const health = await client.health();
@@ -1714,8 +1807,8 @@ var statusCommand = new Command21("status").description("show whether this machi
1714
1807
  });
1715
1808
 
1716
1809
  // src/commands/server/stop.ts
1717
- import { Command as Command22 } from "commander";
1718
- var stopCommand2 = new Command22("stop").description("stop the local server (running boxes are not affected)").action(async () => {
1810
+ import { Command as Command23 } from "commander";
1811
+ var stopCommand2 = new Command23("stop").description("stop the local server (running boxes are not affected)").action(async () => {
1719
1812
  const pid = await runningPid(paths());
1720
1813
  if (pid === null) throw new KvboxError("kvbox-server is not running");
1721
1814
  await stopLocalServer(pid);
@@ -1723,8 +1816,8 @@ var stopCommand2 = new Command22("stop").description("stop the local server (run
1723
1816
  });
1724
1817
 
1725
1818
  // src/commands/server/trust-ca.ts
1726
- import { Command as Command23 } from "commander";
1727
- var trustCaCommand = new Command23("trust-ca").description("make every cloud box on this machine trust a new SSH CA (after `ca rotate` on the control plane)").argument("[public-key]", "the CA public key (or its cert-authority line); or --from-cloud").option("--from-cloud", "ask the control plane this host joined for its current CA, show the fingerprint, then ask").option("-y, --yes", "do not ask for confirmation").option("--json", "print the report as JSON").action(async (publicKey, opts, cmd) => {
1819
+ import { Command as Command24 } from "commander";
1820
+ var trustCaCommand = new Command24("trust-ca").description("make every cloud box on this machine trust a new SSH CA (after `ca rotate` on the control plane)").argument("[public-key]", "the CA public key (or its cert-authority line); or --from-cloud").option("--from-cloud", "ask the control plane this host joined for its current CA, show the fingerprint, then ask").option("-y, --yes", "do not ask for confirmation").option("--json", "print the report as JSON").action(async (publicKey, opts, cmd) => {
1728
1821
  if (publicKey === void 0 && !opts.fromCloud) throw new KvboxError("give the CA public key, or --from-cloud to fetch it from the control plane this host joined");
1729
1822
  if (publicKey !== void 0 && opts.fromCloud) throw new KvboxError("either a public key or --from-cloud, not both");
1730
1823
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
@@ -1762,7 +1855,7 @@ function describeReport(report) {
1762
1855
  }
1763
1856
 
1764
1857
  // src/commands/server/index.ts
1765
- var serverCommand = new Command24("server").description("run or manage the kvbox server").addCommand(startCommand2).addCommand(statusCommand).addCommand(stopCommand2).addCommand(joinCommand).addCommand(leaveCommand).addCommand(relayCommand).addCommand(trustCaCommand);
1858
+ var serverCommand = new Command25("server").description("run or manage the kvbox server").addCommand(startCommand2).addCommand(statusCommand).addCommand(stopCommand2).addCommand(joinCommand).addCommand(leaveCommand).addCommand(relayCommand).addCommand(trustCaCommand);
1766
1859
 
1767
1860
  // src/commands/login.ts
1768
1861
  import { spawnSync as spawnSync3 } from "child_process";
@@ -1770,8 +1863,8 @@ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from
1770
1863
  import { hostname as osHostname } from "os";
1771
1864
  import { createInterface } from "readline/promises";
1772
1865
  import { Writable } from "stream";
1773
- import { Command as Command25 } from "commander";
1774
- var loginCommand = new Command25("login").description("log in to the kvbox cloud (or your own control plane) and target it from now on").argument("[url]", `control plane URL (default: ${DEFAULT_CLOUD_URL})`).option("-e, --email <email>", "account email (prompted otherwise)").option("--password-stdin", "read the password from the first line of stdin (for scripts)").action(async (urlArg, opts) => {
1866
+ import { Command as Command26 } from "commander";
1867
+ var loginCommand = new Command26("login").description("log in to the kvbox cloud (or your own control plane) and target it from now on").argument("[url]", `control plane URL (default: ${DEFAULT_CLOUD_URL})`).option("-e, --email <email>", "account email (prompted otherwise)").option("--password-stdin", "read the password from the first line of stdin (for scripts)").action(async (urlArg, opts) => {
1775
1868
  const url = normalizeUrl(urlArg ?? DEFAULT_CLOUD_URL);
1776
1869
  const creds = await readCredentials(opts);
1777
1870
  const session = await runLogin(url, creds, paths());
@@ -1869,8 +1962,8 @@ async function firstLine(stream) {
1869
1962
  }
1870
1963
 
1871
1964
  // src/commands/logout.ts
1872
- import { Command as Command26 } from "commander";
1873
- var logoutCommand = new Command26("logout").description("log out of the control plane; commands target this machine's kvbox-server again").action(async () => {
1965
+ import { Command as Command27 } from "commander";
1966
+ var logoutCommand = new Command27("logout").description("log out of the control plane; commands target this machine's kvbox-server again").action(async () => {
1874
1967
  const p = paths();
1875
1968
  const session = await readSession(p).catch(() => null);
1876
1969
  switch (await runLogout(p)) {
@@ -1907,8 +2000,8 @@ async function runLogout(p = paths()) {
1907
2000
  }
1908
2001
 
1909
2002
  // src/commands/whoami.ts
1910
- import { Command as Command27 } from "commander";
1911
- var whoamiCommand = new Command27("whoami").description("show which account and control plane commands target (or local mode)").option("--json", "print as JSON").action(async (opts) => {
2003
+ import { Command as Command28 } from "commander";
2004
+ var whoamiCommand = new Command28("whoami").description("show which account and control plane commands target (or local mode)").option("--json", "print as JSON").action(async (opts) => {
1912
2005
  const session = await readSession();
1913
2006
  if (opts.json) {
1914
2007
  json(session ? { user: session.user, url: session.url, sshCertExpires: session.sshCertExpires ?? null } : null);
@@ -1924,15 +2017,15 @@ function describeSession(session) {
1924
2017
  }
1925
2018
 
1926
2019
  // src/commands/snapshots/index.ts
1927
- import { Command as Command31 } from "commander";
2020
+ import { Command as Command32 } from "commander";
1928
2021
 
1929
2022
  // src/commands/snapshots/ls.ts
1930
- import { Command as Command28, Option as Option4 } from "commander";
1931
- var lsCommand2 = new Command28("ls").description("list snapshots (yours and your orgs'; --org <slug> for one org)").addOption(new Option4("-a, --all", "kept for older scripts: the listing covers everything you can see anyway").hideHelp()).option("--json", "print as JSON").action(async (opts, cmd) => {
2023
+ import { Command as Command29 } from "commander";
2024
+ var lsCommand2 = new Command29("ls").description("list snapshots (yours and your orgs'; --org <slug> for one org)").option("-a, --all", "every snapshot on the platform (platform admins only)").option("--json", "print as JSON").action(async (opts, cmd) => {
1932
2025
  const client = await clientFor(cmd.optsWithGlobals());
1933
2026
  try {
1934
2027
  const cloud = client.target.kind === "cloud";
1935
- const snaps = await client.listSnapshots();
2028
+ const snaps = await client.listSnapshots({ all: opts.all });
1936
2029
  if (opts.json) {
1937
2030
  json(snaps);
1938
2031
  return;
@@ -1959,8 +2052,8 @@ var lsCommand2 = new Command28("ls").description("list snapshots (yours and your
1959
2052
  });
1960
2053
 
1961
2054
  // src/commands/snapshots/new.ts
1962
- import { Command as Command29 } from "commander";
1963
- var newCommand2 = new Command29("new").description("freeze a running box (memory + disk) into a snapshot; the box keeps running").argument("<box>", "box name").argument("<name>", "snapshot name").option("--json", "print the snapshot as JSON").action(async (box, name, opts, cmd) => {
2055
+ import { Command as Command30 } from "commander";
2056
+ var newCommand2 = new Command30("new").description("freeze a running box (memory + disk) into a snapshot; the box keeps running").argument("<box>", "box name").argument("<name>", "snapshot name").option("--json", "print the snapshot as JSON").action(async (box, name, opts, cmd) => {
1964
2057
  const client = await clientFor(cmd.optsWithGlobals());
1965
2058
  try {
1966
2059
  const snap = await spin(`saving snapshot ${name} from box ${box} (memory + disk)\u2026`, client.createSnapshot({ box, name }));
@@ -1972,8 +2065,8 @@ var newCommand2 = new Command29("new").description("freeze a running box (memory
1972
2065
  });
1973
2066
 
1974
2067
  // src/commands/snapshots/rm.ts
1975
- import { Command as Command30 } from "commander";
1976
- var rmCommand2 = new Command30("rm").description("delete a snapshot (boxes made from it are not affected)").argument("<name>", "snapshot name").option("-y, --yes", "do not ask for confirmation").action(async (name, opts, cmd) => {
2068
+ import { Command as Command31 } from "commander";
2069
+ var rmCommand2 = new Command31("rm").description("delete a snapshot (boxes made from it are not affected)").argument("<name>", "snapshot name").option("-y, --yes", "do not ask for confirmation").action(async (name, opts, cmd) => {
1977
2070
  if (!await confirm(`Delete snapshot ${name}?`, opts.yes ?? false)) {
1978
2071
  info("cancelled");
1979
2072
  process.exitCode = 1;
@@ -1989,14 +2082,14 @@ var rmCommand2 = new Command30("rm").description("delete a snapshot (boxes made
1989
2082
  });
1990
2083
 
1991
2084
  // src/commands/snapshots/index.ts
1992
- var snapshotsCommand = new Command31("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand2).addCommand(rmCommand2);
2085
+ var snapshotsCommand = new Command32("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand2).addCommand(rmCommand2);
1993
2086
  for (const cmd of snapshotsCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1994
2087
 
1995
2088
  // src/commands/org/index.ts
1996
- import { Command as Command37 } from "commander";
2089
+ import { Command as Command38 } from "commander";
1997
2090
 
1998
2091
  // src/commands/org/ls.ts
1999
- import { Command as Command32 } from "commander";
2092
+ import { Command as Command33 } from "commander";
2000
2093
 
2001
2094
  // src/commands/org/context.ts
2002
2095
  var NOT_LOGGED_IN = "orgs live on the control plane: kvbox login first";
@@ -2016,7 +2109,7 @@ function orgFor(opts, target) {
2016
2109
  }
2017
2110
 
2018
2111
  // src/commands/org/ls.ts
2019
- var lsCommand3 = new Command32("ls").description("list the orgs you belong to and your role in each").option("--json", "print as JSON").action(async (opts, cmd) => {
2112
+ var lsCommand3 = new Command33("ls").description("list the orgs you belong to and your role in each").option("--json", "print as JSON").action(async (opts, cmd) => {
2020
2113
  const client = await cloudClientFor(cmd.optsWithGlobals());
2021
2114
  try {
2022
2115
  const orgs = await client.listOrgs();
@@ -2038,8 +2131,8 @@ function orgRows(orgs) {
2038
2131
  }
2039
2132
 
2040
2133
  // src/commands/org/members.ts
2041
- import { Command as Command33, Option as Option5 } from "commander";
2042
- var lsCommand4 = new Command33("ls").description("list an org's members and their roles").option("-o, --org <slug>", "the org (or --org before the command)").option("--json", "print as JSON").action(async (opts, cmd) => {
2134
+ import { Command as Command34, Option as Option3 } from "commander";
2135
+ var lsCommand4 = new Command34("ls").description("list an org's members and their roles").option("-o, --org <slug>", "the org (or --org before the command)").option("--json", "print as JSON").action(async (opts, cmd) => {
2043
2136
  const client = await cloudClientFor(cmd.optsWithGlobals());
2044
2137
  try {
2045
2138
  const org = await client.getOrg(orgFor(opts, client.target));
@@ -2053,7 +2146,7 @@ var lsCommand4 = new Command33("ls").description("list an org's members and thei
2053
2146
  await client.close();
2054
2147
  }
2055
2148
  });
2056
- var addCommand = new Command33("add").description("add a user to an org (admins and owners; only an owner can add an owner)").argument("<user>", "their username").addOption(new Option5("-r, --role <role>", "their role").choices(ORG_ROLES).default("member")).option("-o, --org <slug>", "the org (or --org before the command)").action(async (user, opts, cmd) => {
2149
+ var addCommand = new Command34("add").description("add a user to an org (admins and owners; only an owner can add an owner)").argument("<user>", "their username").addOption(new Option3("-r, --role <role>", "their role").choices(ORG_ROLES).default("member")).option("-o, --org <slug>", "the org (or --org before the command)").action(async (user, opts, cmd) => {
2057
2150
  const client = await cloudClientFor(cmd.optsWithGlobals());
2058
2151
  try {
2059
2152
  const slug = orgFor(opts, client.target);
@@ -2063,7 +2156,7 @@ var addCommand = new Command33("add").description("add a user to an org (admins
2063
2156
  await client.close();
2064
2157
  }
2065
2158
  });
2066
- var rmCommand3 = new Command33("rm").description("remove a member (or leave: remove yourself)").argument("<user>", "their username").option("-o, --org <slug>", "the org (or --org before the command)").option("-y, --yes", "do not ask for confirmation").action(async (user, opts, cmd) => {
2159
+ var rmCommand3 = new Command34("rm").description("remove a member (or leave: remove yourself)").argument("<user>", "their username").option("-o, --org <slug>", "the org (or --org before the command)").option("-y, --yes", "do not ask for confirmation").action(async (user, opts, cmd) => {
2067
2160
  const client = await cloudClientFor(cmd.optsWithGlobals());
2068
2161
  try {
2069
2162
  const slug = orgFor(opts, client.target);
@@ -2079,11 +2172,11 @@ var rmCommand3 = new Command33("rm").description("remove a member (or leave: rem
2079
2172
  await client.close();
2080
2173
  }
2081
2174
  });
2082
- var membersCommand = new Command33("members").description("who is in an org and what they may do").addCommand(lsCommand4).addCommand(addCommand).addCommand(rmCommand3);
2175
+ var membersCommand = new Command34("members").description("who is in an org and what they may do").addCommand(lsCommand4).addCommand(addCommand).addCommand(rmCommand3);
2083
2176
 
2084
2177
  // src/commands/org/new.ts
2085
- import { Command as Command34 } from "commander";
2086
- var newCommand3 = new Command34("new").description("create an org; you become its owner (the slug is permanent, the name is not)").argument("<slug>", "the org's slug: lowercase, like a username (acme)").option("-n, --name <name>", "display name (default: the slug)").option("--json", "print the org as JSON").action(async (slug, opts, cmd) => {
2178
+ import { Command as Command35 } from "commander";
2179
+ var newCommand3 = new Command35("new").description("create an org; you become its owner (the slug is permanent, the name is not)").argument("<slug>", "the org's slug: lowercase, like a username (acme)").option("-n, --name <name>", "display name (default: the slug)").option("--json", "print the org as JSON").action(async (slug, opts, cmd) => {
2087
2180
  const client = await cloudClientFor(cmd.optsWithGlobals());
2088
2181
  try {
2089
2182
  const org = await client.createOrg({ slug, name: opts.name ?? slug });
@@ -2099,8 +2192,8 @@ var newCommand3 = new Command34("new").description("create an org; you become it
2099
2192
  });
2100
2193
 
2101
2194
  // src/commands/org/rm.ts
2102
- import { Command as Command35 } from "commander";
2103
- var rmCommand4 = new Command35("rm").description("delete an org (owners only; remove its boxes and snapshots first)").argument("<slug>", "the org").option("-y, --yes", "do not ask for confirmation").action(async (slug, opts, cmd) => {
2195
+ import { Command as Command36 } from "commander";
2196
+ var rmCommand4 = new Command36("rm").description("delete an org (owners only; remove its boxes and snapshots first)").argument("<slug>", "the org").option("-y, --yes", "do not ask for confirmation").action(async (slug, opts, cmd) => {
2104
2197
  if (!await confirm(`Delete org ${slug}? Its members lose access; its slug stays taken.`, opts.yes ?? false)) {
2105
2198
  info("cancelled");
2106
2199
  process.exitCode = 1;
@@ -2116,8 +2209,8 @@ var rmCommand4 = new Command35("rm").description("delete an org (owners only; re
2116
2209
  });
2117
2210
 
2118
2211
  // src/commands/org/role.ts
2119
- import { Argument, Command as Command36 } from "commander";
2120
- var roleCommand = new Command36("role").description("change a member's role (owners only)").argument("<user>", "their username").addArgument(new Argument("<role>", "owner, admin or member").choices(ORG_ROLES)).option("-o, --org <slug>", "the org (default: the current context)").action(async (user, role, opts, cmd) => {
2212
+ import { Argument, Command as Command37 } from "commander";
2213
+ var roleCommand = new Command37("role").description("change a member's role (owners only)").argument("<user>", "their username").addArgument(new Argument("<role>", "owner, admin or member").choices(ORG_ROLES)).option("-o, --org <slug>", "the org (default: the current context)").action(async (user, role, opts, cmd) => {
2121
2214
  const client = await cloudClientFor(cmd.optsWithGlobals());
2122
2215
  try {
2123
2216
  const slug = orgFor(opts, client.target);
@@ -2129,11 +2222,11 @@ var roleCommand = new Command36("role").description("change a member's role (own
2129
2222
  });
2130
2223
 
2131
2224
  // src/commands/org/index.ts
2132
- var orgCommand = new Command37("org").description("orgs: shared slugs that own boxes, with owners, admins and members").addCommand(lsCommand3).addCommand(newCommand3).addCommand(membersCommand).addCommand(roleCommand).addCommand(rmCommand4);
2225
+ var orgCommand = new Command38("org").description("orgs: shared slugs that own boxes, with owners, admins and members").addCommand(lsCommand3).addCommand(newCommand3).addCommand(membersCommand).addCommand(roleCommand).addCommand(rmCommand4);
2133
2226
 
2134
2227
  // src/commands/usage.ts
2135
- import { Command as Command38 } from "commander";
2136
- var usageCommand2 = new Command38("usage").description("what your boxes used this month (or --since/--until): running time, vCPU-h, RAM and disk GiB-h, cost").option("--since <date>", "count from this date, e.g. 2026-10-01 (default: the 1st of this month)").option("--until <date>", "count up to this date (default: now)").option("--org <slug>", "one org's boxes only").option("--json", "print as JSON").action(async (opts, cmd) => {
2228
+ import { Command as Command39 } from "commander";
2229
+ var usageCommand2 = new Command39("usage").description("what your boxes used this month (or --since/--until): running time, vCPU-h, RAM and disk GiB-h, cost").option("--since <date>", "count from this date, e.g. 2026-10-01 (default: the 1st of this month)").option("--until <date>", "count up to this date (default: now)").option("--org <slug>", "one org's boxes only").option("-a, --all", "every box on the platform (platform admins only)").option("--json", "print as JSON").action(async (opts, cmd) => {
2137
2230
  for (const [name, value] of [
2138
2231
  ["since", opts.since],
2139
2232
  ["until", opts.until]
@@ -2143,7 +2236,7 @@ var usageCommand2 = new Command38("usage").description("what your boxes used thi
2143
2236
  const client = await clientFor(cmd.optsWithGlobals());
2144
2237
  try {
2145
2238
  if (client.target.kind === "local" && cmd.optsWithGlobals().org) throw new KvboxError("--org needs a control plane: kvbox login, or drop --org");
2146
- const report = await client.usageReport(opts.since, opts.until);
2239
+ const report = await client.usageReport(opts.since, opts.until, opts.all);
2147
2240
  if (opts.json) {
2148
2241
  json(report);
2149
2242
  return;
@@ -2206,8 +2299,8 @@ function usageRows(report, cloud) {
2206
2299
  }
2207
2300
 
2208
2301
  // src/commands/credit.ts
2209
- import { Command as Command39 } from "commander";
2210
- var creditCommand = new Command39("credit").description("the prepaid credit left on your account and on each of your orgs").option("--org <slug>", "one org only").option("--json", "print as JSON").action(async (opts, cmd) => {
2302
+ import { Command as Command40 } from "commander";
2303
+ var creditCommand = new Command40("credit").description("the prepaid credit left on your account and on each of your orgs").option("--org <slug>", "one org only").option("--json", "print as JSON").action(async (opts, cmd) => {
2211
2304
  const client = await clientFor(cmd.optsWithGlobals(), { autoStart: false });
2212
2305
  try {
2213
2306
  if (client.target.kind === "local") throw new KvboxError("credit lives on the control plane: kvbox login first");
@@ -2236,7 +2329,7 @@ function creditRow(b, me2) {
2236
2329
 
2237
2330
  // src/program.ts
2238
2331
  function buildProgram() {
2239
- const program = new Command40("kvbox").description("self-hosted Firecracker microVMs with Docker-like ergonomics").version(VERSION, "-v, --version").option("--local", "use this machine's kvbox-server over its socket, ignoring kvbox login").option("--org <slug>", "the org: where kvbox new creates, and which box a name means when it is not one of yours; before the command here, or after it").showHelpAfterError().enablePositionalOptions();
2332
+ const program = new Command41("kvbox").description("self-hosted Firecracker microVMs with Docker-like ergonomics").version(VERSION, "-v, --version").option("--local", "use this machine's kvbox-server over its socket, ignoring kvbox login").option("--org <slug>", "the org: where kvbox new creates, and which box a name means when it is not one of yours; before the command here, or after it").showHelpAfterError().enablePositionalOptions();
2240
2333
  program.addCommand(vmCommand);
2241
2334
  program.addCommand(snapshotsCommand);
2242
2335
  program.addCommand(serverCommand);