@kvbox/cli 0.5.12 → 0.5.13

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 Command41 } from "commander";
150
+ import { Command as Command45 } 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.12" : void 0);
165
+ var VERSION = versionOf(import.meta.url, true ? "0.5.13" : void 0);
166
166
 
167
167
  // src/commands/vm/index.ts
168
- import { Command as Command17 } from "commander";
168
+ import { Command as Command21 } from "commander";
169
169
 
170
170
  // src/commands/vm/connect.ts
171
171
  import { spawnSync as spawnSync2 } from "child_process";
@@ -384,6 +384,18 @@ function parseMib(text) {
384
384
  var SizeSchema = z2.enum(SIZE_NAMES);
385
385
  var BOX_SSH_USER = "kvbox";
386
386
  var BoxStatusSchema = z2.enum(["running", "standby", "hibernated", "stopped", "unknown"]);
387
+ var PortProtoSchema = z2.enum(["tcp", "udp"]);
388
+ var MAX_BOX_PORTS = 16;
389
+ var PortForwardSchema = z2.object({
390
+ guestPort: z2.number().int().min(1).max(65535),
391
+ hostPort: z2.number().int(),
392
+ proto: PortProtoSchema,
393
+ /** The host's public address; only the control plane fills it in, as for `ssh`. */
394
+ address: z2.string().optional()
395
+ });
396
+ var PortsRequest = z2.object({
397
+ ports: z2.array(z2.object({ guestPort: z2.number().int().min(1).max(65535), proto: PortProtoSchema.optional() })).min(1).max(MAX_BOX_PORTS)
398
+ });
387
399
  var MachineSchema = z2.object({
388
400
  /** Host-unique short id (the tap is `kv-<id>`). Missing only on a box made before ids that has not restarted since. */
389
401
  id: z2.string(),
@@ -416,6 +428,8 @@ var MachineSchema = z2.object({
416
428
  * whose host reported no port or did not answer.
417
429
  */
418
430
  ssh: z2.object({ host: z2.string(), port: z2.number().int(), user: z2.string() }).optional(),
431
+ /** Box ports published on the host (#30). Absent: none. */
432
+ ports: z2.array(PortForwardSchema).optional(),
419
433
  /** Whether the owner slug is a user or an org. Only the control plane knows; it fills it in on `GET /machines`. */
420
434
  ownerKind: z2.enum(["user", "org"]).optional(),
421
435
  /** Seconds without network traffic before the host hibernates the box (#47). Absent: never. */
@@ -924,6 +938,14 @@ var ServerClient = class {
924
938
  setMachine(name, req) {
925
939
  return this.call("POST", `/machines/${encodeURIComponent(name)}/set`, MachineSchema, req);
926
940
  }
941
+ /** Publish box ports on the host (#30); the answer is the box with every port it publishes. */
942
+ addPorts(name, req) {
943
+ return this.call("POST", `/machines/${encodeURIComponent(name)}/ports`, MachineSchema, req);
944
+ }
945
+ /** Stop publishing them (#30). */
946
+ removePorts(name, req) {
947
+ return this.call("DELETE", `/machines/${encodeURIComponent(name)}/ports`, MachineSchema, req);
948
+ }
927
949
  /** A running box's own load, memory and disk, asked of its guest (#67). */
928
950
  machineStats(name) {
929
951
  return this.call("GET", `/machines/${encodeURIComponent(name)}/stats`, MachineStatsSchema);
@@ -1398,9 +1420,103 @@ var pauseCommand = new Command6("pause").description("freeze a box in place (mem
1398
1420
  }
1399
1421
  });
1400
1422
 
1423
+ // src/commands/vm/proxy/index.ts
1424
+ import { Command as Command10 } from "commander";
1425
+
1426
+ // src/commands/vm/proxy/add.ts
1427
+ import { Command as Command7 } from "commander";
1428
+
1429
+ // src/commands/vm/proxy/shared.ts
1430
+ function rowsOf(box, user, only) {
1431
+ return (box.ports ?? []).filter(only ?? (() => true)).map((f) => ({ box: displayName(box, user), ...f }));
1432
+ }
1433
+ function reaches(row, short = false) {
1434
+ if (row.address !== void 0) return `${row.address}:${row.hostPort}`;
1435
+ return short ? `:${row.hostPort}` : `port ${row.hostPort} of this host's public address`;
1436
+ }
1437
+ function parsePorts(texts) {
1438
+ return texts.map((t) => {
1439
+ const n = Number(t);
1440
+ if (!/^\d+$/.test(t) || n < 1 || n > 65535) throw new KvboxError(`"${t}" is not a port (1-65535)`);
1441
+ return n;
1442
+ });
1443
+ }
1444
+ async function findBox(client, text) {
1445
+ const found = pickRef(await boxesFor(client, text), text, me(client));
1446
+ if (!found) throw new KvboxError(`box ${text}: not found (kvbox ls)`);
1447
+ return found;
1448
+ }
1449
+
1450
+ // 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) => {
1452
+ const guestPorts = parsePorts(portTexts);
1453
+ const proto = opts.udp ? "udp" : "tcp";
1454
+ const client = await clientFor(cmd.optsWithGlobals());
1455
+ try {
1456
+ const user = me(client);
1457
+ 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));
1460
+ if (opts.json) {
1461
+ json(rows);
1462
+ return;
1463
+ }
1464
+ 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`);
1466
+ } finally {
1467
+ await client.close();
1468
+ }
1469
+ });
1470
+
1471
+ // src/commands/vm/proxy/ls.ts
1472
+ 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) => {
1474
+ const client = await clientFor(cmd.optsWithGlobals());
1475
+ try {
1476
+ 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);
1479
+ if (opts.json) {
1480
+ json(rows);
1481
+ return;
1482
+ }
1483
+ if (rows.length === 0) {
1484
+ 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
+ return;
1486
+ }
1487
+ table(rows.map((r) => ({ box: r.box, port: r.guestPort, reaches: reaches(r, true), proto: r.proto })));
1488
+ } finally {
1489
+ await client.close();
1490
+ }
1491
+ });
1492
+
1493
+ // 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) => {
1496
+ const guestPorts = parsePorts(portTexts);
1497
+ 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
+ const client = await clientFor(cmd.optsWithGlobals());
1504
+ 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 })) }));
1507
+ info(`${color.green("\u2713")} box ${color.bold(name)} no longer publishes ${guestPorts.join(", ")} (${proto})`);
1508
+ } finally {
1509
+ await client.close();
1510
+ }
1511
+ });
1512
+
1513
+ // 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);
1515
+ for (const cmd of proxyCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1516
+
1401
1517
  // src/commands/vm/rescale.ts
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) => {
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) => {
1404
1520
  const req = {
1405
1521
  ...opts.size !== void 0 ? { size: opts.size } : {},
1406
1522
  ...opts.vcpu !== void 0 ? { vcpu: parseCount(opts.vcpu) } : {},
@@ -1445,8 +1561,8 @@ function parseGib(flag, text) {
1445
1561
  }
1446
1562
 
1447
1563
  // src/commands/vm/resume.ts
1448
- import { Command as Command8 } from "commander";
1449
- var resumeCommand = new Command8("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) => {
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) => {
1450
1566
  const client = await clientFor(cmd.optsWithGlobals());
1451
1567
  try {
1452
1568
  const box = await spin(`resuming ${name}\u2026`, client.resumeMachine(name));
@@ -1458,8 +1574,8 @@ var resumeCommand = new Command8("resume").description("continue a box that is i
1458
1574
  });
1459
1575
 
1460
1576
  // src/commands/vm/rm.ts
1461
- import { Command as Command9 } from "commander";
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) => {
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) => {
1463
1579
  const client = await clientFor(cmd.optsWithGlobals());
1464
1580
  try {
1465
1581
  const box = pickRef(await boxesFor(client, name), name, me(client));
@@ -1478,8 +1594,8 @@ var rmCommand = new Command9("rm").description("destroy a box (its disk is delet
1478
1594
  });
1479
1595
 
1480
1596
  // src/commands/vm/set.ts
1481
- import { Command as Command10 } from "commander";
1482
- var setCommand = new Command10("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) => {
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) => {
1483
1599
  if (opts.autoHibernate === void 0) throw new KvboxError("nothing to change: give --auto-hibernate");
1484
1600
  const autoHibernateSec = parseIdleTimeout(opts.autoHibernate);
1485
1601
  const client = await clientFor(cmd.optsWithGlobals());
@@ -1498,8 +1614,8 @@ var setCommand = new Command10("set").description("change a box's settings, e.g.
1498
1614
  });
1499
1615
 
1500
1616
  // src/commands/vm/start.ts
1501
- import { Command as Command11 } from "commander";
1502
- var startCommand = new Command11("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) => {
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) => {
1503
1619
  const client = await clientFor(cmd.optsWithGlobals());
1504
1620
  try {
1505
1621
  const box = await spin(`booting ${name}\u2026`, client.startMachine(name));
@@ -1511,8 +1627,8 @@ var startCommand = new Command11("start").description("boot a stopped box again
1511
1627
  });
1512
1628
 
1513
1629
  // src/commands/vm/stop.ts
1514
- import { Command as Command12 } from "commander";
1515
- var stopCommand = new Command12("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) => {
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) => {
1516
1632
  const client = await clientFor(cmd.optsWithGlobals());
1517
1633
  try {
1518
1634
  const box = await spin(`shutting ${name} down\u2026`, client.stopMachine(name));
@@ -1524,8 +1640,8 @@ var stopCommand = new Command12("stop").description("shut a box down (its disk i
1524
1640
  });
1525
1641
 
1526
1642
  // src/commands/vm/top.ts
1527
- import { Command as Command13 } from "commander";
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) => {
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) => {
1529
1645
  const client = await clientFor(cmd.optsWithGlobals());
1530
1646
  try {
1531
1647
  const running = (await client.listMachines({ all: opts.all })).filter((b) => b.status === "running");
@@ -1585,8 +1701,8 @@ function topRows(entries, cloud) {
1585
1701
  }
1586
1702
 
1587
1703
  // 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) => {
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) => {
1590
1706
  const client = await clientFor(cmd.optsWithGlobals());
1591
1707
  try {
1592
1708
  const before = pickRef(await boxesFor(client, name), name, me(client));
@@ -1608,8 +1724,8 @@ var updateCommand = new Command14("update").description("move a box onto the cur
1608
1724
  });
1609
1725
 
1610
1726
  // src/commands/vm/usage.ts
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) => {
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) => {
1613
1729
  let since;
1614
1730
  if (opts.since !== void 0) {
1615
1731
  const t = Date.parse(opts.since);
@@ -1656,8 +1772,8 @@ function block(u) {
1656
1772
  }
1657
1773
 
1658
1774
  // src/commands/vm/wake.ts
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) => {
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) => {
1661
1777
  const client = await clientFor(cmd.optsWithGlobals());
1662
1778
  try {
1663
1779
  const box = await spin(`waking ${name} (loading its memory from disk)\u2026`, client.wakeMachine(name));
@@ -1669,15 +1785,15 @@ var wakeCommand = new Command16("wake").description("continue a hibernated box w
1669
1785
  });
1670
1786
 
1671
1787
  // src/commands/vm/index.ts
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);
1673
- for (const cmd of vmCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1674
- var vmAliases = [newCommand, lsCommand, connectCommand, execCommand, stopCommand, startCommand, pauseCommand, resumeCommand, hibernateCommand, wakeCommand, rmCommand];
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);
1789
+ for (const cmd of vmCommand.commands) if (cmd !== proxyCommand) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
1790
+ var vmAliases = [newCommand, lsCommand, connectCommand, execCommand, stopCommand, startCommand, pauseCommand, resumeCommand, hibernateCommand, wakeCommand, rmCommand2];
1675
1791
 
1676
1792
  // src/commands/server/index.ts
1677
- import { Command as Command25 } from "commander";
1793
+ import { Command as Command29 } from "commander";
1678
1794
 
1679
1795
  // src/commands/server/join.ts
1680
- import { Command as Command18 } from "commander";
1796
+ import { Command as Command22 } from "commander";
1681
1797
 
1682
1798
  // src/commands/server/daemon.ts
1683
1799
  import { spawn as spawn3 } from "child_process";
@@ -1731,7 +1847,7 @@ function runKvboxServer(args) {
1731
1847
  }
1732
1848
 
1733
1849
  // src/commands/server/join.ts
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) => {
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) => {
1735
1851
  const args = ["join", url, "--token", opts.token];
1736
1852
  if (opts.advertise !== void 0) args.push("--advertise", opts.advertise);
1737
1853
  if (opts.name !== void 0) args.push("--name", opts.name);
@@ -1741,8 +1857,8 @@ var joinCommand = new Command18("join").description("put this machine behind a c
1741
1857
  });
1742
1858
 
1743
1859
  // src/commands/server/leave.ts
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 () => {
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 () => {
1746
1862
  const p = paths();
1747
1863
  for (const line of (await runKvboxServer(["leave"])).split("\n")) info(line);
1748
1864
  if (await runningPid(p) === null) return;
@@ -1751,14 +1867,14 @@ var leaveCommand = new Command19("leave").description("take this machine out fro
1751
1867
  });
1752
1868
 
1753
1869
  // src/commands/server/relay.ts
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) => {
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) => {
1756
1872
  for (const line of (await runKvboxServer(["relay", joinString])).split("\n")) info(line);
1757
1873
  });
1758
1874
 
1759
1875
  // src/commands/server/start.ts
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) => {
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) => {
1762
1878
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true }, { autoStart: false });
1763
1879
  try {
1764
1880
  const health = await client.health();
@@ -1774,8 +1890,8 @@ var startCommand2 = new Command21("start").description("start the local kvbox-se
1774
1890
  });
1775
1891
 
1776
1892
  // src/commands/server/status.ts
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) => {
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) => {
1779
1895
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
1780
1896
  try {
1781
1897
  const health = await client.health();
@@ -1807,8 +1923,8 @@ var statusCommand = new Command22("status").description("show whether this machi
1807
1923
  });
1808
1924
 
1809
1925
  // src/commands/server/stop.ts
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 () => {
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 () => {
1812
1928
  const pid = await runningPid(paths());
1813
1929
  if (pid === null) throw new KvboxError("kvbox-server is not running");
1814
1930
  await stopLocalServer(pid);
@@ -1816,8 +1932,8 @@ var stopCommand2 = new Command23("stop").description("stop the local server (run
1816
1932
  });
1817
1933
 
1818
1934
  // src/commands/server/trust-ca.ts
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) => {
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) => {
1821
1937
  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");
1822
1938
  if (publicKey !== void 0 && opts.fromCloud) throw new KvboxError("either a public key or --from-cloud, not both");
1823
1939
  const client = await clientFor({ ...cmd.optsWithGlobals(), local: true });
@@ -1855,7 +1971,7 @@ function describeReport(report) {
1855
1971
  }
1856
1972
 
1857
1973
  // src/commands/server/index.ts
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);
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);
1859
1975
 
1860
1976
  // src/commands/login.ts
1861
1977
  import { spawnSync as spawnSync3 } from "child_process";
@@ -1863,8 +1979,8 @@ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from
1863
1979
  import { hostname as osHostname } from "os";
1864
1980
  import { createInterface } from "readline/promises";
1865
1981
  import { Writable } from "stream";
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) => {
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) => {
1868
1984
  const url = normalizeUrl(urlArg ?? DEFAULT_CLOUD_URL);
1869
1985
  const creds = await readCredentials(opts);
1870
1986
  const session = await runLogin(url, creds, paths());
@@ -1962,8 +2078,8 @@ async function firstLine(stream) {
1962
2078
  }
1963
2079
 
1964
2080
  // src/commands/logout.ts
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 () => {
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 () => {
1967
2083
  const p = paths();
1968
2084
  const session = await readSession(p).catch(() => null);
1969
2085
  switch (await runLogout(p)) {
@@ -2000,8 +2116,8 @@ async function runLogout(p = paths()) {
2000
2116
  }
2001
2117
 
2002
2118
  // src/commands/whoami.ts
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) => {
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) => {
2005
2121
  const session = await readSession();
2006
2122
  if (opts.json) {
2007
2123
  json(session ? { user: session.user, url: session.url, sshCertExpires: session.sshCertExpires ?? null } : null);
@@ -2017,11 +2133,11 @@ function describeSession(session) {
2017
2133
  }
2018
2134
 
2019
2135
  // src/commands/snapshots/index.ts
2020
- import { Command as Command32 } from "commander";
2136
+ import { Command as Command36 } from "commander";
2021
2137
 
2022
2138
  // src/commands/snapshots/ls.ts
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) => {
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) => {
2025
2141
  const client = await clientFor(cmd.optsWithGlobals());
2026
2142
  try {
2027
2143
  const cloud = client.target.kind === "cloud";
@@ -2052,8 +2168,8 @@ var lsCommand2 = new Command29("ls").description("list snapshots (yours and your
2052
2168
  });
2053
2169
 
2054
2170
  // src/commands/snapshots/new.ts
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) => {
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) => {
2057
2173
  const client = await clientFor(cmd.optsWithGlobals());
2058
2174
  try {
2059
2175
  const snap = await spin(`saving snapshot ${name} from box ${box} (memory + disk)\u2026`, client.createSnapshot({ box, name }));
@@ -2065,8 +2181,8 @@ var newCommand2 = new Command30("new").description("freeze a running box (memory
2065
2181
  });
2066
2182
 
2067
2183
  // src/commands/snapshots/rm.ts
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) => {
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) => {
2070
2186
  if (!await confirm(`Delete snapshot ${name}?`, opts.yes ?? false)) {
2071
2187
  info("cancelled");
2072
2188
  process.exitCode = 1;
@@ -2082,14 +2198,14 @@ var rmCommand2 = new Command31("rm").description("delete a snapshot (boxes made
2082
2198
  });
2083
2199
 
2084
2200
  // src/commands/snapshots/index.ts
2085
- var snapshotsCommand = new Command32("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand2).addCommand(rmCommand2);
2201
+ var snapshotsCommand = new Command36("snapshots").aliases(["snapshot", "snap"]).description("freeze running boxes into snapshots and manage them").addCommand(newCommand2).addCommand(lsCommand3).addCommand(rmCommand3);
2086
2202
  for (const cmd of snapshotsCommand.commands) cmd.option("--org <slug>", "act in this org (- for your personal boxes)");
2087
2203
 
2088
2204
  // src/commands/org/index.ts
2089
- import { Command as Command38 } from "commander";
2205
+ import { Command as Command42 } from "commander";
2090
2206
 
2091
2207
  // src/commands/org/ls.ts
2092
- import { Command as Command33 } from "commander";
2208
+ import { Command as Command37 } from "commander";
2093
2209
 
2094
2210
  // src/commands/org/context.ts
2095
2211
  var NOT_LOGGED_IN = "orgs live on the control plane: kvbox login first";
@@ -2109,7 +2225,7 @@ function orgFor(opts, target) {
2109
2225
  }
2110
2226
 
2111
2227
  // src/commands/org/ls.ts
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) => {
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) => {
2113
2229
  const client = await cloudClientFor(cmd.optsWithGlobals());
2114
2230
  try {
2115
2231
  const orgs = await client.listOrgs();
@@ -2131,8 +2247,8 @@ function orgRows(orgs) {
2131
2247
  }
2132
2248
 
2133
2249
  // src/commands/org/members.ts
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) => {
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) => {
2136
2252
  const client = await cloudClientFor(cmd.optsWithGlobals());
2137
2253
  try {
2138
2254
  const org = await client.getOrg(orgFor(opts, client.target));
@@ -2146,7 +2262,7 @@ var lsCommand4 = new Command34("ls").description("list an org's members and thei
2146
2262
  await client.close();
2147
2263
  }
2148
2264
  });
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) => {
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) => {
2150
2266
  const client = await cloudClientFor(cmd.optsWithGlobals());
2151
2267
  try {
2152
2268
  const slug = orgFor(opts, client.target);
@@ -2156,7 +2272,7 @@ var addCommand = new Command34("add").description("add a user to an org (admins
2156
2272
  await client.close();
2157
2273
  }
2158
2274
  });
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) => {
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) => {
2160
2276
  const client = await cloudClientFor(cmd.optsWithGlobals());
2161
2277
  try {
2162
2278
  const slug = orgFor(opts, client.target);
@@ -2172,11 +2288,11 @@ var rmCommand3 = new Command34("rm").description("remove a member (or leave: rem
2172
2288
  await client.close();
2173
2289
  }
2174
2290
  });
2175
- var membersCommand = new Command34("members").description("who is in an org and what they may do").addCommand(lsCommand4).addCommand(addCommand).addCommand(rmCommand3);
2291
+ var membersCommand = new Command38("members").description("who is in an org and what they may do").addCommand(lsCommand5).addCommand(addCommand2).addCommand(rmCommand4);
2176
2292
 
2177
2293
  // src/commands/org/new.ts
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) => {
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) => {
2180
2296
  const client = await cloudClientFor(cmd.optsWithGlobals());
2181
2297
  try {
2182
2298
  const org = await client.createOrg({ slug, name: opts.name ?? slug });
@@ -2192,8 +2308,8 @@ var newCommand3 = new Command35("new").description("create an org; you become it
2192
2308
  });
2193
2309
 
2194
2310
  // src/commands/org/rm.ts
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) => {
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) => {
2197
2313
  if (!await confirm(`Delete org ${slug}? Its members lose access; its slug stays taken.`, opts.yes ?? false)) {
2198
2314
  info("cancelled");
2199
2315
  process.exitCode = 1;
@@ -2209,8 +2325,8 @@ var rmCommand4 = new Command36("rm").description("delete an org (owners only; re
2209
2325
  });
2210
2326
 
2211
2327
  // src/commands/org/role.ts
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) => {
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) => {
2214
2330
  const client = await cloudClientFor(cmd.optsWithGlobals());
2215
2331
  try {
2216
2332
  const slug = orgFor(opts, client.target);
@@ -2222,11 +2338,11 @@ var roleCommand = new Command37("role").description("change a member's role (own
2222
2338
  });
2223
2339
 
2224
2340
  // src/commands/org/index.ts
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);
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);
2226
2342
 
2227
2343
  // src/commands/usage.ts
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) => {
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) => {
2230
2346
  for (const [name, value] of [
2231
2347
  ["since", opts.since],
2232
2348
  ["until", opts.until]
@@ -2299,8 +2415,8 @@ function usageRows(report, cloud) {
2299
2415
  }
2300
2416
 
2301
2417
  // src/commands/credit.ts
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) => {
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) => {
2304
2420
  const client = await clientFor(cmd.optsWithGlobals(), { autoStart: false });
2305
2421
  try {
2306
2422
  if (client.target.kind === "local") throw new KvboxError("credit lives on the control plane: kvbox login first");
@@ -2329,7 +2445,7 @@ function creditRow(b, me2) {
2329
2445
 
2330
2446
  // src/program.ts
2331
2447
  function buildProgram() {
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();
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();
2333
2449
  program.addCommand(vmCommand);
2334
2450
  program.addCommand(snapshotsCommand);
2335
2451
  program.addCommand(serverCommand);