@kvbox/cli 0.5.13 → 0.5.14

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 Command45 } from "commander";
150
+ import { Command as Command48 } 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.13" : void 0);
165
+ var VERSION = versionOf(import.meta.url, true ? "0.5.14" : void 0);
166
166
 
167
167
  // src/commands/vm/index.ts
168
- import { Command as Command21 } from "commander";
168
+ import { Command as Command24 } from "commander";
169
169
 
170
170
  // src/commands/vm/connect.ts
171
171
  import { spawnSync as spawnSync2 } from "child_process";
@@ -396,6 +396,18 @@ var PortForwardSchema = z2.object({
396
396
  var PortsRequest = z2.object({
397
397
  ports: z2.array(z2.object({ guestPort: z2.number().int().min(1).max(65535), proto: PortProtoSchema.optional() })).min(1).max(MAX_BOX_PORTS)
398
398
  });
399
+ var BoxPortSchema = z2.number().int().min(1).max(65535);
400
+ var ClaimSubdomainRequest = z2.object({ name: z2.string(), port: BoxPortSchema });
401
+ var SubdomainPortRequest = z2.object({ port: BoxPortSchema });
402
+ var SubdomainRouteRequest = z2.object({ name: z2.string(), port: BoxPortSchema });
403
+ var SubdomainSchema = z2.object({
404
+ owner: z2.string(),
405
+ box: z2.string(),
406
+ subdomain: z2.string(),
407
+ port: BoxPortSchema,
408
+ url: z2.string(),
409
+ routes: z2.array(z2.object({ name: z2.string(), port: BoxPortSchema, url: z2.string() }))
410
+ });
399
411
  var MachineSchema = z2.object({
400
412
  /** Host-unique short id (the tap is `kv-<id>`). Missing only on a box made before ids that has not restarted since. */
401
413
  id: z2.string(),
@@ -946,6 +958,27 @@ var ServerClient = class {
946
958
  removePorts(name, req) {
947
959
  return this.call("DELETE", `/machines/${encodeURIComponent(name)}/ports`, MachineSchema, req);
948
960
  }
961
+ /** HTTPS names (#31), the control plane's only: the box's subdomain, claimed for its default port. */
962
+ claimSubdomain(name, req) {
963
+ return this.call("PUT", `/machines/${encodeURIComponent(name)}/subdomain`, SubdomainSchema, req);
964
+ }
965
+ setSubdomainPort(name, req) {
966
+ return this.call("POST", `/machines/${encodeURIComponent(name)}/subdomain/port`, SubdomainSchema, req);
967
+ }
968
+ releaseSubdomain(name) {
969
+ return this.call("DELETE", `/machines/${encodeURIComponent(name)}/subdomain`, SubdomainSchema);
970
+ }
971
+ /** `<route>.<sub>`: a named port under the box's subdomain. */
972
+ addSubdomainRoute(name, req) {
973
+ return this.call("POST", `/machines/${encodeURIComponent(name)}/subdomain/routes`, SubdomainSchema, req);
974
+ }
975
+ removeSubdomainRoute(name, route) {
976
+ return this.call("DELETE", `/machines/${encodeURIComponent(name)}/subdomain/routes/${encodeURIComponent(route)}`, SubdomainSchema);
977
+ }
978
+ /** Every readable box's subdomain. */
979
+ listSubdomains() {
980
+ return this.call("GET", "/subdomains", z3.array(SubdomainSchema));
981
+ }
949
982
  /** A running box's own load, memory and disk, asked of its guest (#67). */
950
983
  machineStats(name) {
951
984
  return this.call("GET", `/machines/${encodeURIComponent(name)}/stats`, MachineStatsSchema);
@@ -1421,12 +1454,74 @@ var pauseCommand = new Command6("pause").description("freeze a box in place (mem
1421
1454
  });
1422
1455
 
1423
1456
  // src/commands/vm/proxy/index.ts
1424
- import { Command as Command10 } from "commander";
1457
+ import { Command as Command13 } from "commander";
1425
1458
 
1426
1459
  // src/commands/vm/proxy/add.ts
1427
1460
  import { Command as Command7 } from "commander";
1428
1461
 
1462
+ // ../shared/src/names.ts
1463
+ import { createHmac } from "crypto";
1464
+ var LABEL_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
1465
+ var RESERVED_SUBDOMAINS = /* @__PURE__ */ new Set([
1466
+ "admin",
1467
+ "api",
1468
+ "app",
1469
+ "assets",
1470
+ "auth",
1471
+ "billing",
1472
+ "blog",
1473
+ "cdn",
1474
+ "dashboard",
1475
+ "dev",
1476
+ "docs",
1477
+ "edge",
1478
+ "help",
1479
+ "install",
1480
+ "kvbox",
1481
+ "login",
1482
+ "mail",
1483
+ "relay",
1484
+ "root",
1485
+ "smtp",
1486
+ "static",
1487
+ "status",
1488
+ "support",
1489
+ "www"
1490
+ ]);
1491
+ function subdomainProblem(text) {
1492
+ if (text.length < 3 || text.length > 63) return `"${text}" must be 3 to 63 characters`;
1493
+ if (!LABEL_RE.test(text)) return `"${text}" may hold only lowercase letters, digits and '-', not at either end`;
1494
+ if (RESERVED_SUBDOMAINS.has(text)) return `"${text}" is reserved; pick another name`;
1495
+ return null;
1496
+ }
1497
+ function routeNameProblem(text) {
1498
+ if (text.length < 1 || text.length > 63) return `"${text}" must be 1 to 63 characters`;
1499
+ if (!LABEL_RE.test(text)) return `"${text}" may hold only lowercase letters, digits and '-', not at either end`;
1500
+ return null;
1501
+ }
1502
+ function assertSubdomain(text) {
1503
+ const problem = subdomainProblem(text);
1504
+ if (problem) throw new KvboxError(`subdomain ${problem}`);
1505
+ return text;
1506
+ }
1507
+ function assertRouteName(text) {
1508
+ const problem = routeNameProblem(text);
1509
+ if (problem) throw new KvboxError(`name ${problem}`);
1510
+ return text;
1511
+ }
1512
+
1429
1513
  // src/commands/vm/proxy/shared.ts
1514
+ var boxOf = (sub, user) => user !== void 0 && sub.owner !== user ? `${sub.owner}/${sub.box}` : sub.box;
1515
+ function subdomainJson(sub, user) {
1516
+ return { box: boxOf(sub, user), subdomain: sub.subdomain, port: sub.port, url: sub.url, routes: sub.routes };
1517
+ }
1518
+ function httpsRowsOf(sub, user) {
1519
+ const box = boxOf(sub, user);
1520
+ return [{ box, guestPort: sub.port, proto: "https", url: sub.url }, ...sub.routes.map((r) => ({ box, guestPort: r.port, proto: "https", url: r.url, name: r.name }))];
1521
+ }
1522
+ function requireCloud(client) {
1523
+ if (client.target.kind !== "cloud") throw new KvboxError("HTTPS names need the kvbox cloud: kvbox login (this host's own socket has none)");
1524
+ }
1430
1525
  function rowsOf(box, user, only) {
1431
1526
  return (box.ports ?? []).filter(only ?? (() => true)).map((f) => ({ box: displayName(box, user), ...f }));
1432
1527
  }
@@ -1448,34 +1543,91 @@ async function findBox(client, text) {
1448
1543
  }
1449
1544
 
1450
1545
  // src/commands/vm/proxy/add.ts
1451
- var addCommand = new Command7("add").description("publish box ports on the internet; each gets a random public port").argument("<box>", "box name").argument("<port...>", "ports inside the box, e.g. 3000 8080").option("--udp", "UDP instead of TCP").option("--json", "print the new forwards as JSON").action(async (name, portTexts, opts, cmd) => {
1546
+ var addCommand = new Command7("add").description("publish box ports on the internet; each gets a random public port, or an HTTPS name with --name").argument("<box>", "box name").argument("<port...>", "ports inside the box, e.g. 3000 8080").option("--udp", "UDP instead of TCP").option("--name <name>", "serve the port at https://<name>.<subdomain>.kvbox.app (one port; needs kvbox vm proxy claim first)").option("--json", "print the new forwards as JSON").action(async (name, portTexts, opts, cmd) => {
1452
1547
  const guestPorts = parsePorts(portTexts);
1548
+ if (opts.name !== void 0) {
1549
+ assertRouteName(opts.name);
1550
+ if (guestPorts.length !== 1) throw new KvboxError("--name publishes one port: kvbox vm proxy add <box> <port> --name <name>");
1551
+ if (opts.udp) throw new KvboxError("--name serves HTTPS, which is TCP: drop --udp");
1552
+ }
1453
1553
  const proto = opts.udp ? "udp" : "tcp";
1454
1554
  const client = await clientFor(cmd.optsWithGlobals());
1455
1555
  try {
1456
1556
  const user = me(client);
1557
+ if (opts.name !== void 0) requireCloud(client);
1457
1558
  const found = await findBox(client, name);
1458
- const box = await spin(`publishing ${guestPorts.join(", ")} on ${name}\u2026`, client.addPorts(displayName(found, user), { ports: guestPorts.map((guestPort) => ({ guestPort, proto })) }));
1459
- const rows = rowsOf(box, user, (f) => f.proto === proto && guestPorts.includes(f.guestPort));
1559
+ const box = displayName(found, user);
1560
+ if (opts.name !== void 0) {
1561
+ const route = opts.name;
1562
+ const sub = await spin(`publishing ${guestPorts[0]} on ${name} as ${route}\u2026`, client.addSubdomainRoute(box, { name: route, port: guestPorts[0] }));
1563
+ const url = sub.routes.find((r) => r.name === route).url;
1564
+ if (opts.json) {
1565
+ json({ box, guestPort: guestPorts[0], proto: "https", url, name: route });
1566
+ return;
1567
+ }
1568
+ info(`${color.green("\u2713")} ${color.bold(url)} \u2192 box ${color.bold(box)} port ${guestPorts[0]}`);
1569
+ info(" the first name under a subdomain waits for its certificate, about a minute");
1570
+ if (found.status !== "running") info(` box ${box} is ${found.status}: the address answers once it runs`);
1571
+ return;
1572
+ }
1573
+ const updated = await spin(`publishing ${guestPorts.join(", ")} on ${name}\u2026`, client.addPorts(box, { ports: guestPorts.map((guestPort) => ({ guestPort, proto })) }));
1574
+ const rows = rowsOf(updated, user, (f) => f.proto === proto && guestPorts.includes(f.guestPort));
1460
1575
  if (opts.json) {
1461
1576
  json(rows);
1462
1577
  return;
1463
1578
  }
1464
1579
  for (const row of rows) info(`${color.green("\u2713")} box ${color.bold(row.box)} port ${row.guestPort} is reachable at ${reaches(row)} (${row.proto})`);
1465
- if (box.status !== "running") info(` box ${name} is ${box.status}: the ports open when it runs`);
1580
+ if (updated.status !== "running") info(` box ${name} is ${updated.status}: the ports open when it runs`);
1466
1581
  } finally {
1467
1582
  await client.close();
1468
1583
  }
1469
1584
  });
1470
1585
 
1471
- // src/commands/vm/proxy/ls.ts
1586
+ // src/commands/vm/proxy/claim.ts
1472
1587
  import { Command as Command8 } from "commander";
1473
- var lsCommand2 = new Command8("ls").description("list the box ports published on the internet").argument("[box]", "only this box").option("--json", "print as JSON").action(async (name, opts, cmd) => {
1588
+ var claimCommand = new Command8("claim").description("give a box its HTTPS address, https://<subdomain>.kvbox.app, serving one of its ports").argument("<box>", "box name").argument("<subdomain>", "3-63 lowercase letters, digits and '-'; must be free").requiredOption("--port <port>", "the box port the address serves").option("--json", "print the subdomain as JSON").action(async (name, subdomain, opts, cmd) => {
1589
+ assertSubdomain(subdomain);
1590
+ const [port] = parsePorts([opts.port]);
1591
+ const client = await clientFor(cmd.optsWithGlobals());
1592
+ try {
1593
+ requireCloud(client);
1594
+ const user = me(client);
1595
+ const found = await findBox(client, name);
1596
+ const box = displayName(found, user);
1597
+ const sub = await spin(`claiming ${subdomain}\u2026`, client.claimSubdomain(box, { name: subdomain, port }));
1598
+ if (opts.json) {
1599
+ json(subdomainJson(sub, me(client)));
1600
+ return;
1601
+ }
1602
+ info(`${color.green("\u2713")} ${color.bold(sub.url)} \u2192 box ${color.bold(box)} port ${sub.port}`);
1603
+ info(` more ports under it: kvbox vm proxy add ${box} <port> --name <name> \u2192 https://<name>.${sub.url.replace(/^https:\/\//, "")}`);
1604
+ if (found.status !== "running") info(` box ${box} is ${found.status}: the address answers once it runs`);
1605
+ } finally {
1606
+ await client.close();
1607
+ }
1608
+ });
1609
+
1610
+ // src/commands/vm/proxy/ls.ts
1611
+ import { Command as Command9 } from "commander";
1612
+ async function subdomainsOf(client) {
1613
+ try {
1614
+ return await client.listSubdomains();
1615
+ } catch (err) {
1616
+ warn(`HTTPS names not listed: ${err.message}`);
1617
+ return [];
1618
+ }
1619
+ }
1620
+ var lsCommand2 = new Command9("ls").description("list the box ports published on the internet").argument("[box]", "only this box").option("--json", "print as JSON").action(async (name, opts, cmd) => {
1474
1621
  const client = await clientFor(cmd.optsWithGlobals());
1475
1622
  try {
1476
1623
  const user = me(client);
1477
- const boxes = name === void 0 ? await client.listMachines() : [await findBox(client, name)];
1478
- const rows = boxes.flatMap((b) => rowsOf(b, user)).sort((a, b) => a.box.localeCompare(b.box) || a.guestPort - b.guestPort);
1624
+ const [boxes, subs] = await Promise.all([
1625
+ name === void 0 ? client.listMachines() : findBox(client, name).then((b) => [b]),
1626
+ client.target.kind === "cloud" ? subdomainsOf(client) : []
1627
+ ]);
1628
+ const shown = new Set(boxes.map((b) => displayName(b, user)));
1629
+ const https = subs.flatMap((s) => httpsRowsOf(s, user)).filter((r) => shown.has(r.box));
1630
+ const rows = [...boxes.flatMap((b) => rowsOf(b, user)), ...https].sort((a, b) => a.box.localeCompare(b.box) || a.guestPort - b.guestPort);
1479
1631
  if (opts.json) {
1480
1632
  json(rows);
1481
1633
  return;
@@ -1484,39 +1636,91 @@ var lsCommand2 = new Command8("ls").description("list the box ports published on
1484
1636
  info(name === void 0 ? "no published ports (kvbox vm proxy add <box> <port>)" : `box ${name} publishes no ports (kvbox vm proxy add ${name} <port>)`);
1485
1637
  return;
1486
1638
  }
1487
- table(rows.map((r) => ({ box: r.box, port: r.guestPort, reaches: reaches(r, true), proto: r.proto })));
1639
+ table(rows.map((r) => ({ box: r.box, port: r.guestPort, reaches: r.proto === "https" ? r.url : reaches(r, true), proto: r.proto })));
1640
+ } finally {
1641
+ await client.close();
1642
+ }
1643
+ });
1644
+
1645
+ // src/commands/vm/proxy/release.ts
1646
+ import { Command as Command10 } from "commander";
1647
+ var releaseCommand = new Command10("release").description("give up a box's HTTPS address and every name under it").argument("<box>", "box name").option("-y, --yes", "do not ask for confirmation").action(async (name, opts, cmd) => {
1648
+ const client = await clientFor(cmd.optsWithGlobals());
1649
+ try {
1650
+ requireCloud(client);
1651
+ const box = displayName(await findBox(client, name), me(client));
1652
+ if (!await confirm(`Give up box ${box}'s HTTPS address and every name under it? Anyone may claim it after.`, opts.yes ?? false)) {
1653
+ info("cancelled");
1654
+ process.exitCode = 1;
1655
+ return;
1656
+ }
1657
+ const sub = await spin(`releasing ${name}'s address\u2026`, client.releaseSubdomain(box));
1658
+ const names = sub.routes.length === 0 ? "" : ` and ${sub.routes.length} name${sub.routes.length > 1 ? "s" : ""} under it`;
1659
+ info(`${color.green("\u2713")} ${sub.url.replace(/^https:\/\//, "")}${names} no longer reach box ${color.bold(box)}`);
1488
1660
  } finally {
1489
1661
  await client.close();
1490
1662
  }
1491
1663
  });
1492
1664
 
1493
1665
  // src/commands/vm/proxy/rm.ts
1494
- import { Command as Command9 } from "commander";
1495
- var rmCommand = new Command9("rm").description("stop publishing box ports").argument("<box>", "box name").argument("<port...>", "ports inside the box, as given to add").option("--udp", "the UDP forwards of those ports").option("-y, --yes", "do not ask for confirmation").action(async (name, portTexts, opts, cmd) => {
1666
+ import { Command as Command11 } from "commander";
1667
+ var rmCommand = new Command11("rm").description("stop publishing box ports, or an HTTPS name with --name").argument("<box>", "box name").argument("[port...]", "ports inside the box, as given to add").option("--udp", "the UDP forwards of those ports").option("--name <name>", "the HTTPS name to free (kvbox vm proxy release frees the whole subdomain)").option("-y, --yes", "do not ask for confirmation").action(async (name, portTexts, opts, cmd) => {
1668
+ if (opts.name !== void 0) {
1669
+ assertRouteName(opts.name);
1670
+ if (portTexts.length > 0) throw new KvboxError("--name frees a name, not ports: kvbox vm proxy rm <box> --name <name>");
1671
+ } else if (portTexts.length === 0) {
1672
+ throw new KvboxError("which ports? kvbox vm proxy rm <box> <port...>, or --name <name> for an HTTPS name");
1673
+ }
1496
1674
  const guestPorts = parsePorts(portTexts);
1497
1675
  const proto = opts.udp ? "udp" : "tcp";
1498
- if (!await confirm(`Stop publishing port${guestPorts.length > 1 ? "s" : ""} ${guestPorts.join(", ")} of box ${name}?`, opts.yes ?? false)) {
1499
- info("cancelled");
1500
- process.exitCode = 1;
1501
- return;
1502
- }
1503
1676
  const client = await clientFor(cmd.optsWithGlobals());
1504
1677
  try {
1505
- const found = await findBox(client, name);
1506
- await spin(`closing ${guestPorts.join(", ")} on ${name}\u2026`, client.removePorts(displayName(found, me(client)), { ports: guestPorts.map((guestPort) => ({ guestPort, proto })) }));
1678
+ if (opts.name !== void 0) requireCloud(client);
1679
+ const box = displayName(await findBox(client, name), me(client));
1680
+ const question = opts.name !== void 0 ? `Stop serving ${opts.name} under box ${box}'s subdomain?` : `Stop publishing port${guestPorts.length > 1 ? "s" : ""} ${guestPorts.join(", ")} of box ${box}?`;
1681
+ if (!await confirm(question, opts.yes ?? false)) {
1682
+ info("cancelled");
1683
+ process.exitCode = 1;
1684
+ return;
1685
+ }
1686
+ if (opts.name !== void 0) {
1687
+ const sub = await spin(`freeing ${opts.name} on ${name}\u2026`, client.removeSubdomainRoute(box, opts.name));
1688
+ info(`${color.green("\u2713")} ${opts.name}.${sub.url.replace(/^https:\/\//, "")} no longer reaches box ${color.bold(box)}`);
1689
+ return;
1690
+ }
1691
+ await spin(`closing ${guestPorts.join(", ")} on ${name}\u2026`, client.removePorts(box, { ports: guestPorts.map((guestPort) => ({ guestPort, proto })) }));
1507
1692
  info(`${color.green("\u2713")} box ${color.bold(name)} no longer publishes ${guestPorts.join(", ")} (${proto})`);
1508
1693
  } finally {
1509
1694
  await client.close();
1510
1695
  }
1511
1696
  });
1512
1697
 
1698
+ // src/commands/vm/proxy/set-port.ts
1699
+ import { Command as Command12 } from "commander";
1700
+ var setPortCommand = new Command12("set-port").description("point a box's HTTPS address at another of its ports").argument("<box>", "box name").argument("<port>", "the box port https://<subdomain>.kvbox.app serves from now on").option("--json", "print the subdomain as JSON").action(async (name, portText, opts, cmd) => {
1701
+ const [port] = parsePorts([portText]);
1702
+ const client = await clientFor(cmd.optsWithGlobals());
1703
+ try {
1704
+ requireCloud(client);
1705
+ const box = displayName(await findBox(client, name), me(client));
1706
+ const sub = await spin(`pointing ${name} at port ${port}\u2026`, client.setSubdomainPort(box, { port }));
1707
+ if (opts.json) {
1708
+ json(subdomainJson(sub, me(client)));
1709
+ return;
1710
+ }
1711
+ info(`${color.green("\u2713")} ${color.bold(sub.url)} \u2192 box ${color.bold(box)} port ${sub.port}`);
1712
+ } finally {
1713
+ await client.close();
1714
+ }
1715
+ });
1716
+
1513
1717
  // src/commands/vm/proxy/index.ts
1514
- var proxyCommand = new Command10("proxy").description("publish box ports on the internet").addCommand(addCommand).addCommand(lsCommand2).addCommand(rmCommand);
1718
+ var proxyCommand = new Command13("proxy").description("publish box ports on the internet").addCommand(addCommand).addCommand(lsCommand2).addCommand(rmCommand).addCommand(claimCommand).addCommand(setPortCommand).addCommand(releaseCommand);
1515
1719
  for (const cmd of proxyCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1516
1720
 
1517
1721
  // src/commands/vm/rescale.ts
1518
- import { Command as Command11, Option as Option2 } from "commander";
1519
- var rescaleCommand = new Command11("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) => {
1722
+ import { Command as Command14, Option as Option2 } from "commander";
1723
+ var rescaleCommand = new Command14("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) => {
1520
1724
  const req = {
1521
1725
  ...opts.size !== void 0 ? { size: opts.size } : {},
1522
1726
  ...opts.vcpu !== void 0 ? { vcpu: parseCount(opts.vcpu) } : {},
@@ -1561,8 +1765,8 @@ function parseGib(flag, text) {
1561
1765
  }
1562
1766
 
1563
1767
  // src/commands/vm/resume.ts
1564
- import { Command as Command12 } from "commander";
1565
- var resumeCommand = new Command12("resume").description("continue a box that is in standby (kvbox pause)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1768
+ import { Command as Command15 } from "commander";
1769
+ var resumeCommand = new Command15("resume").description("continue a box that is in standby (kvbox pause)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1566
1770
  const client = await clientFor(cmd.optsWithGlobals());
1567
1771
  try {
1568
1772
  const box = await spin(`resuming ${name}\u2026`, client.resumeMachine(name));
@@ -1574,8 +1778,8 @@ var resumeCommand = new Command12("resume").description("continue a box that is
1574
1778
  });
1575
1779
 
1576
1780
  // src/commands/vm/rm.ts
1577
- import { Command as Command13 } from "commander";
1578
- var rmCommand2 = new Command13("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) => {
1781
+ import { Command as Command16 } from "commander";
1782
+ var rmCommand2 = new Command16("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) => {
1579
1783
  const client = await clientFor(cmd.optsWithGlobals());
1580
1784
  try {
1581
1785
  const box = pickRef(await boxesFor(client, name), name, me(client));
@@ -1594,8 +1798,8 @@ var rmCommand2 = new Command13("rm").description("destroy a box (its disk is del
1594
1798
  });
1595
1799
 
1596
1800
  // src/commands/vm/set.ts
1597
- import { Command as Command14 } from "commander";
1598
- var setCommand = new Command14("set").description("change a box's settings, e.g. --auto-hibernate 30m (off: never)").argument("<name>", "box name").option("--auto-hibernate <duration>", "hibernate the box after this long without network traffic (30m, 4h, 1d; off: never)").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1801
+ import { Command as Command17 } from "commander";
1802
+ var setCommand = new Command17("set").description("change a box's settings, e.g. --auto-hibernate 30m (off: never)").argument("<name>", "box name").option("--auto-hibernate <duration>", "hibernate the box after this long without network traffic (30m, 4h, 1d; off: never)").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1599
1803
  if (opts.autoHibernate === void 0) throw new KvboxError("nothing to change: give --auto-hibernate");
1600
1804
  const autoHibernateSec = parseIdleTimeout(opts.autoHibernate);
1601
1805
  const client = await clientFor(cmd.optsWithGlobals());
@@ -1614,8 +1818,8 @@ var setCommand = new Command14("set").description("change a box's settings, e.g.
1614
1818
  });
1615
1819
 
1616
1820
  // src/commands/vm/start.ts
1617
- import { Command as Command15 } from "commander";
1618
- var startCommand = new Command15("start").description("boot a stopped box again (same name, IP and disk)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1821
+ import { Command as Command18 } from "commander";
1822
+ var startCommand = new Command18("start").description("boot a stopped box again (same name, IP and disk)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1619
1823
  const client = await clientFor(cmd.optsWithGlobals());
1620
1824
  try {
1621
1825
  const box = await spin(`booting ${name}\u2026`, client.startMachine(name));
@@ -1627,8 +1831,8 @@ var startCommand = new Command15("start").description("boot a stopped box again
1627
1831
  });
1628
1832
 
1629
1833
  // src/commands/vm/stop.ts
1630
- import { Command as Command16 } from "commander";
1631
- var stopCommand = new Command16("stop").description("shut a box down (its disk is kept; start it again with kvbox start)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1834
+ import { Command as Command19 } from "commander";
1835
+ var stopCommand = new Command19("stop").description("shut a box down (its disk is kept; start it again with kvbox start)").argument("<name>", "box name").option("--json", "print the box as JSON").action(async (name, opts, cmd) => {
1632
1836
  const client = await clientFor(cmd.optsWithGlobals());
1633
1837
  try {
1634
1838
  const box = await spin(`shutting ${name} down\u2026`, client.stopMachine(name));
@@ -1640,8 +1844,8 @@ var stopCommand = new Command16("stop").description("shut a box down (its disk i
1640
1844
  });
1641
1845
 
1642
1846
  // src/commands/vm/top.ts
1643
- import { Command as Command17 } from "commander";
1644
- var topCommand = new Command17("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) => {
1847
+ import { Command as Command20 } from "commander";
1848
+ var topCommand = new Command20("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) => {
1645
1849
  const client = await clientFor(cmd.optsWithGlobals());
1646
1850
  try {
1647
1851
  const running = (await client.listMachines({ all: opts.all })).filter((b) => b.status === "running");
@@ -1701,8 +1905,8 @@ function topRows(entries, cloud) {
1701
1905
  }
1702
1906
 
1703
1907
  // src/commands/vm/update.ts
1704
- import { Command as Command18 } from "commander";
1705
- var updateCommand = new Command18("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) => {
1908
+ import { Command as Command21 } from "commander";
1909
+ var updateCommand = new Command21("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) => {
1706
1910
  const client = await clientFor(cmd.optsWithGlobals());
1707
1911
  try {
1708
1912
  const before = pickRef(await boxesFor(client, name), name, me(client));
@@ -1724,8 +1928,8 @@ var updateCommand = new Command18("update").description("move a box onto the cur
1724
1928
  });
1725
1929
 
1726
1930
  // src/commands/vm/usage.ts
1727
- import { Command as Command19 } from "commander";
1728
- var usageCommand = new Command19("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) => {
1931
+ import { Command as Command22 } from "commander";
1932
+ var usageCommand = new Command22("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) => {
1729
1933
  let since;
1730
1934
  if (opts.since !== void 0) {
1731
1935
  const t = Date.parse(opts.since);
@@ -1772,8 +1976,8 @@ function block(u) {
1772
1976
  }
1773
1977
 
1774
1978
  // src/commands/vm/wake.ts
1775
- import { Command as Command20 } from "commander";
1776
- var wakeCommand = new Command20("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) => {
1979
+ import { Command as Command23 } from "commander";
1980
+ var wakeCommand = new Command23("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) => {
1777
1981
  const client = await clientFor(cmd.optsWithGlobals());
1778
1982
  try {
1779
1983
  const box = await spin(`waking ${name} (loading its memory from disk)\u2026`, client.wakeMachine(name));
@@ -1785,15 +1989,15 @@ var wakeCommand = new Command20("wake").description("continue a hibernated box w
1785
1989
  });
1786
1990
 
1787
1991
  // src/commands/vm/index.ts
1788
- var vmCommand = new Command21("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(proxyCommand).addCommand(rmCommand2);
1992
+ var vmCommand = new Command24("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(proxyCommand).addCommand(rmCommand2);
1789
1993
  for (const cmd of vmCommand.commands) if (cmd !== proxyCommand) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1790
1994
  var vmAliases = [newCommand, lsCommand, connectCommand, execCommand, stopCommand, startCommand, pauseCommand, resumeCommand, hibernateCommand, wakeCommand, rmCommand2];
1791
1995
 
1792
1996
  // src/commands/server/index.ts
1793
- import { Command as Command29 } from "commander";
1997
+ import { Command as Command32 } from "commander";
1794
1998
 
1795
1999
  // src/commands/server/join.ts
1796
- import { Command as Command22 } from "commander";
2000
+ import { Command as Command25 } from "commander";
1797
2001
 
1798
2002
  // src/commands/server/daemon.ts
1799
2003
  import { spawn as spawn3 } from "child_process";
@@ -1847,7 +2051,7 @@ function runKvboxServer(args) {
1847
2051
  }
1848
2052
 
1849
2053
  // src/commands/server/join.ts
1850
- var joinCommand = new Command22("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) => {
2054
+ var joinCommand = new Command25("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) => {
1851
2055
  const args = ["join", url, "--token", opts.token];
1852
2056
  if (opts.advertise !== void 0) args.push("--advertise", opts.advertise);
1853
2057
  if (opts.name !== void 0) args.push("--name", opts.name);
@@ -1857,8 +2061,8 @@ var joinCommand = new Command22("join").description("put this machine behind a c
1857
2061
  });
1858
2062
 
1859
2063
  // src/commands/server/leave.ts
1860
- import { Command as Command23 } from "commander";
1861
- var leaveCommand = new Command23("leave").description("take this machine out from behind its control plane and restart its server").action(async () => {
2064
+ import { Command as Command26 } from "commander";
2065
+ var leaveCommand = new Command26("leave").description("take this machine out from behind its control plane and restart its server").action(async () => {
1862
2066
  const p = paths();
1863
2067
  for (const line of (await runKvboxServer(["leave"])).split("\n")) info(line);
1864
2068
  if (await runningPid(p) === null) return;
@@ -1867,14 +2071,14 @@ var leaveCommand = new Command23("leave").description("take this machine out fro
1867
2071
  });
1868
2072
 
1869
2073
  // src/commands/server/relay.ts
1870
- import { Command as Command24 } from "commander";
1871
- var relayCommand = new Command24("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) => {
2074
+ import { Command as Command27 } from "commander";
2075
+ var relayCommand = new Command27("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) => {
1872
2076
  for (const line of (await runKvboxServer(["relay", joinString])).split("\n")) info(line);
1873
2077
  });
1874
2078
 
1875
2079
  // src/commands/server/start.ts
1876
- import { Command as Command25 } from "commander";
1877
- var startCommand2 = new Command25("start").description("start the local kvbox-server in the background").action(async (_opts, cmd) => {
2080
+ import { Command as Command28 } from "commander";
2081
+ var startCommand2 = new Command28("start").description("start the local kvbox-server in the background").action(async (_opts, cmd) => {
1878
2082
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true }, { autoStart: false });
1879
2083
  try {
1880
2084
  const health = await client.health();
@@ -1890,8 +2094,8 @@ var startCommand2 = new Command25("start").description("start the local kvbox-se
1890
2094
  });
1891
2095
 
1892
2096
  // src/commands/server/status.ts
1893
- import { Command as Command26 } from "commander";
1894
- var statusCommand = new Command26("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) => {
2097
+ import { Command as Command29 } from "commander";
2098
+ var statusCommand = new Command29("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) => {
1895
2099
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
1896
2100
  try {
1897
2101
  const health = await client.health();
@@ -1923,8 +2127,8 @@ var statusCommand = new Command26("status").description("show whether this machi
1923
2127
  });
1924
2128
 
1925
2129
  // src/commands/server/stop.ts
1926
- import { Command as Command27 } from "commander";
1927
- var stopCommand2 = new Command27("stop").description("stop the local server (running boxes are not affected)").action(async () => {
2130
+ import { Command as Command30 } from "commander";
2131
+ var stopCommand2 = new Command30("stop").description("stop the local server (running boxes are not affected)").action(async () => {
1928
2132
  const pid = await runningPid(paths());
1929
2133
  if (pid === null) throw new KvboxError("kvbox-server is not running");
1930
2134
  await stopLocalServer(pid);
@@ -1932,8 +2136,8 @@ var stopCommand2 = new Command27("stop").description("stop the local server (run
1932
2136
  });
1933
2137
 
1934
2138
  // src/commands/server/trust-ca.ts
1935
- import { Command as Command28 } from "commander";
1936
- var trustCaCommand = new Command28("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) => {
2139
+ import { Command as Command31 } from "commander";
2140
+ var trustCaCommand = new Command31("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) => {
1937
2141
  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");
1938
2142
  if (publicKey !== void 0 && opts.fromCloud) throw new KvboxError("either a public key or --from-cloud, not both");
1939
2143
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
@@ -1971,7 +2175,7 @@ function describeReport(report) {
1971
2175
  }
1972
2176
 
1973
2177
  // src/commands/server/index.ts
1974
- var serverCommand = new Command29("server").description("run or manage the kvbox server").addCommand(startCommand2).addCommand(statusCommand).addCommand(stopCommand2).addCommand(joinCommand).addCommand(leaveCommand).addCommand(relayCommand).addCommand(trustCaCommand);
2178
+ var serverCommand = new Command32("server").description("run or manage the kvbox server").addCommand(startCommand2).addCommand(statusCommand).addCommand(stopCommand2).addCommand(joinCommand).addCommand(leaveCommand).addCommand(relayCommand).addCommand(trustCaCommand);
1975
2179
 
1976
2180
  // src/commands/login.ts
1977
2181
  import { spawnSync as spawnSync3 } from "child_process";
@@ -1979,8 +2183,8 @@ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from
1979
2183
  import { hostname as osHostname } from "os";
1980
2184
  import { createInterface } from "readline/promises";
1981
2185
  import { Writable } from "stream";
1982
- import { Command as Command30 } from "commander";
1983
- var loginCommand = new Command30("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) => {
2186
+ import { Command as Command33 } from "commander";
2187
+ var loginCommand = new Command33("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) => {
1984
2188
  const url = normalizeUrl(urlArg ?? DEFAULT_CLOUD_URL);
1985
2189
  const creds = await readCredentials(opts);
1986
2190
  const session = await runLogin(url, creds, paths());
@@ -2078,8 +2282,8 @@ async function firstLine(stream) {
2078
2282
  }
2079
2283
 
2080
2284
  // src/commands/logout.ts
2081
- import { Command as Command31 } from "commander";
2082
- var logoutCommand = new Command31("logout").description("log out of the control plane; commands target this machine's kvbox-server again").action(async () => {
2285
+ import { Command as Command34 } from "commander";
2286
+ var logoutCommand = new Command34("logout").description("log out of the control plane; commands target this machine's kvbox-server again").action(async () => {
2083
2287
  const p = paths();
2084
2288
  const session = await readSession(p).catch(() => null);
2085
2289
  switch (await runLogout(p)) {
@@ -2116,8 +2320,8 @@ async function runLogout(p = paths()) {
2116
2320
  }
2117
2321
 
2118
2322
  // src/commands/whoami.ts
2119
- import { Command as Command32 } from "commander";
2120
- var whoamiCommand = new Command32("whoami").description("show which account and control plane commands target (or local mode)").option("--json", "print as JSON").action(async (opts) => {
2323
+ import { Command as Command35 } from "commander";
2324
+ var whoamiCommand = new Command35("whoami").description("show which account and control plane commands target (or local mode)").option("--json", "print as JSON").action(async (opts) => {
2121
2325
  const session = await readSession();
2122
2326
  if (opts.json) {
2123
2327
  json(session ? { user: session.user, url: session.url, sshCertExpires: session.sshCertExpires ?? null } : null);
@@ -2133,11 +2337,11 @@ function describeSession(session) {
2133
2337
  }
2134
2338
 
2135
2339
  // src/commands/snapshots/index.ts
2136
- import { Command as Command36 } from "commander";
2340
+ import { Command as Command39 } from "commander";
2137
2341
 
2138
2342
  // src/commands/snapshots/ls.ts
2139
- import { Command as Command33 } from "commander";
2140
- var lsCommand3 = new Command33("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) => {
2343
+ import { Command as Command36 } from "commander";
2344
+ var lsCommand3 = new Command36("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) => {
2141
2345
  const client = await clientFor(cmd.optsWithGlobals());
2142
2346
  try {
2143
2347
  const cloud = client.target.kind === "cloud";
@@ -2168,8 +2372,8 @@ var lsCommand3 = new Command33("ls").description("list snapshots (yours and your
2168
2372
  });
2169
2373
 
2170
2374
  // src/commands/snapshots/new.ts
2171
- import { Command as Command34 } from "commander";
2172
- var newCommand2 = new Command34("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) => {
2375
+ import { Command as Command37 } from "commander";
2376
+ var newCommand2 = new Command37("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) => {
2173
2377
  const client = await clientFor(cmd.optsWithGlobals());
2174
2378
  try {
2175
2379
  const snap = await spin(`saving snapshot ${name} from box ${box} (memory + disk)\u2026`, client.createSnapshot({ box, name }));
@@ -2181,8 +2385,8 @@ var newCommand2 = new Command34("new").description("freeze a running box (memory
2181
2385
  });
2182
2386
 
2183
2387
  // src/commands/snapshots/rm.ts
2184
- import { Command as Command35 } from "commander";
2185
- var rmCommand3 = new Command35("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) => {
2388
+ import { Command as Command38 } from "commander";
2389
+ var rmCommand3 = new Command38("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) => {
2186
2390
  if (!await confirm(`Delete snapshot ${name}?`, opts.yes ?? false)) {
2187
2391
  info("cancelled");
2188
2392
  process.exitCode = 1;
@@ -2198,14 +2402,14 @@ var rmCommand3 = new Command35("rm").description("delete a snapshot (boxes made
2198
2402
  });
2199
2403
 
2200
2404
  // src/commands/snapshots/index.ts
2201
- var snapshotsCommand = new Command36("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand3).addCommand(rmCommand3);
2405
+ var snapshotsCommand = new Command39("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand3).addCommand(rmCommand3);
2202
2406
  for (const cmd of snapshotsCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
2203
2407
 
2204
2408
  // src/commands/org/index.ts
2205
- import { Command as Command42 } from "commander";
2409
+ import { Command as Command45 } from "commander";
2206
2410
 
2207
2411
  // src/commands/org/ls.ts
2208
- import { Command as Command37 } from "commander";
2412
+ import { Command as Command40 } from "commander";
2209
2413
 
2210
2414
  // src/commands/org/context.ts
2211
2415
  var NOT_LOGGED_IN = "orgs live on the control plane: kvbox login first";
@@ -2225,7 +2429,7 @@ function orgFor(opts, target) {
2225
2429
  }
2226
2430
 
2227
2431
  // src/commands/org/ls.ts
2228
- var lsCommand4 = new Command37("ls").description("list the orgs you belong to and your role in each").option("--json", "print as JSON").action(async (opts, cmd) => {
2432
+ var lsCommand4 = new Command40("ls").description("list the orgs you belong to and your role in each").option("--json", "print as JSON").action(async (opts, cmd) => {
2229
2433
  const client = await cloudClientFor(cmd.optsWithGlobals());
2230
2434
  try {
2231
2435
  const orgs = await client.listOrgs();
@@ -2247,8 +2451,8 @@ function orgRows(orgs) {
2247
2451
  }
2248
2452
 
2249
2453
  // src/commands/org/members.ts
2250
- import { Command as Command38, Option as Option3 } from "commander";
2251
- var lsCommand5 = new Command38("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) => {
2454
+ import { Command as Command41, Option as Option3 } from "commander";
2455
+ var lsCommand5 = new Command41("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) => {
2252
2456
  const client = await cloudClientFor(cmd.optsWithGlobals());
2253
2457
  try {
2254
2458
  const org = await client.getOrg(orgFor(opts, client.target));
@@ -2262,7 +2466,7 @@ var lsCommand5 = new Command38("ls").description("list an org's members and thei
2262
2466
  await client.close();
2263
2467
  }
2264
2468
  });
2265
- var addCommand2 = new Command38("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) => {
2469
+ var addCommand2 = new Command41("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) => {
2266
2470
  const client = await cloudClientFor(cmd.optsWithGlobals());
2267
2471
  try {
2268
2472
  const slug = orgFor(opts, client.target);
@@ -2272,7 +2476,7 @@ var addCommand2 = new Command38("add").description("add a user to an org (admins
2272
2476
  await client.close();
2273
2477
  }
2274
2478
  });
2275
- var rmCommand4 = new Command38("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) => {
2479
+ var rmCommand4 = new Command41("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) => {
2276
2480
  const client = await cloudClientFor(cmd.optsWithGlobals());
2277
2481
  try {
2278
2482
  const slug = orgFor(opts, client.target);
@@ -2288,11 +2492,11 @@ var rmCommand4 = new Command38("rm").description("remove a member (or leave: rem
2288
2492
  await client.close();
2289
2493
  }
2290
2494
  });
2291
- var membersCommand = new Command38("members").description("who is in an org and what they may do").addCommand(lsCommand5).addCommand(addCommand2).addCommand(rmCommand4);
2495
+ var membersCommand = new Command41("members").description("who is in an org and what they may do").addCommand(lsCommand5).addCommand(addCommand2).addCommand(rmCommand4);
2292
2496
 
2293
2497
  // src/commands/org/new.ts
2294
- import { Command as Command39 } from "commander";
2295
- var newCommand3 = new Command39("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) => {
2498
+ import { Command as Command42 } from "commander";
2499
+ var newCommand3 = new Command42("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) => {
2296
2500
  const client = await cloudClientFor(cmd.optsWithGlobals());
2297
2501
  try {
2298
2502
  const org = await client.createOrg({ slug, name: opts.name ?? slug });
@@ -2308,8 +2512,8 @@ var newCommand3 = new Command39("new").description("create an org; you become it
2308
2512
  });
2309
2513
 
2310
2514
  // src/commands/org/rm.ts
2311
- import { Command as Command40 } from "commander";
2312
- var rmCommand5 = new Command40("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) => {
2515
+ import { Command as Command43 } from "commander";
2516
+ var rmCommand5 = new Command43("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) => {
2313
2517
  if (!await confirm(`Delete org ${slug}? Its members lose access; its slug stays taken.`, opts.yes ?? false)) {
2314
2518
  info("cancelled");
2315
2519
  process.exitCode = 1;
@@ -2325,8 +2529,8 @@ var rmCommand5 = new Command40("rm").description("delete an org (owners only; re
2325
2529
  });
2326
2530
 
2327
2531
  // src/commands/org/role.ts
2328
- import { Argument, Command as Command41 } from "commander";
2329
- var roleCommand = new Command41("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) => {
2532
+ import { Argument, Command as Command44 } from "commander";
2533
+ var roleCommand = new Command44("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) => {
2330
2534
  const client = await cloudClientFor(cmd.optsWithGlobals());
2331
2535
  try {
2332
2536
  const slug = orgFor(opts, client.target);
@@ -2338,11 +2542,11 @@ var roleCommand = new Command41("role").description("change a member's role (own
2338
2542
  });
2339
2543
 
2340
2544
  // src/commands/org/index.ts
2341
- var orgCommand = new Command42("org").description("orgs: shared slugs that own boxes, with owners, admins and members").addCommand(lsCommand4).addCommand(newCommand3).addCommand(membersCommand).addCommand(roleCommand).addCommand(rmCommand5);
2545
+ var orgCommand = new Command45("org").description("orgs: shared slugs that own boxes, with owners, admins and members").addCommand(lsCommand4).addCommand(newCommand3).addCommand(membersCommand).addCommand(roleCommand).addCommand(rmCommand5);
2342
2546
 
2343
2547
  // src/commands/usage.ts
2344
- import { Command as Command43 } from "commander";
2345
- var usageCommand2 = new Command43("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) => {
2548
+ import { Command as Command46 } from "commander";
2549
+ var usageCommand2 = new Command46("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) => {
2346
2550
  for (const [name, value] of [
2347
2551
  ["since", opts.since],
2348
2552
  ["until", opts.until]
@@ -2415,8 +2619,8 @@ function usageRows(report, cloud) {
2415
2619
  }
2416
2620
 
2417
2621
  // src/commands/credit.ts
2418
- import { Command as Command44 } from "commander";
2419
- var creditCommand = new Command44("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) => {
2622
+ import { Command as Command47 } from "commander";
2623
+ var creditCommand = new Command47("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) => {
2420
2624
  const client = await clientFor(cmd.optsWithGlobals(), { autoStart: false });
2421
2625
  try {
2422
2626
  if (client.target.kind === "local") throw new KvboxError("credit lives on the control plane: kvbox login first");
@@ -2445,7 +2649,7 @@ function creditRow(b, me2) {
2445
2649
 
2446
2650
  // src/program.ts
2447
2651
  function buildProgram() {
2448
- const program = new Command45("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();
2652
+ const program = new Command48("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();
2449
2653
  program.addCommand(vmCommand);
2450
2654
  program.addCommand(snapshotsCommand);
2451
2655
  program.addCommand(serverCommand);