@videan/cli 0.4.0 → 0.4.1

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/index.js CHANGED
@@ -733,7 +733,11 @@ var PLANS2 = {
733
733
  includedUsageCents: 300,
734
734
  maxApps: 3,
735
735
  maxInstancesPerApp: 1,
736
- // One app at this size, running all month, is inside the included usage.
736
+ // A *ceiling*, not a promise of free compute: a container task at even
737
+ // 256/512 running all month is ~$16 of usage against the $3 allowance.
738
+ // That is why apps created on this plan default to serverless mode, which
739
+ // meters nothing while idle -- the free tier is "free while it sleeps",
740
+ // and a container app on this plan is a deliberate, metered choice.
737
741
  maxCpu: 512,
738
742
  maxMemory: 1024,
739
743
  maxStorageMb: 1024,
@@ -1463,39 +1467,79 @@ var rollbackCommand = new Command8("rollback").description("Rollback to the prev
1463
1467
 
1464
1468
  // src/commands/secrets.ts
1465
1469
  import { Command as Command9 } from "commander";
1466
- import chalk10 from "chalk";
1470
+ import chalk11 from "chalk";
1471
+
1472
+ // src/prompt.ts
1467
1473
  import inquirer3 from "inquirer";
1474
+ import chalk10 from "chalk";
1475
+ async function prompt(questions, nonInteractiveHint) {
1476
+ if (!process.stdin.isTTY) {
1477
+ console.error(
1478
+ chalk10.red(`This command needs a terminal to prompt. ${nonInteractiveHint}`)
1479
+ );
1480
+ process.exit(1);
1481
+ }
1482
+ try {
1483
+ return await inquirer3.prompt(questions);
1484
+ } catch (err) {
1485
+ if (isStreamClosed(err)) {
1486
+ console.error(chalk10.red(`Input ended before the prompt was answered. ${nonInteractiveHint}`));
1487
+ process.exit(1);
1488
+ }
1489
+ throw err;
1490
+ }
1491
+ }
1492
+ function isStreamClosed(err) {
1493
+ const e = err;
1494
+ return e?.code === "ERR_USE_AFTER_CLOSE" || /closed/i.test(e?.message ?? "");
1495
+ }
1496
+ function installPromptCrashGuard() {
1497
+ process.on("uncaughtException", (err) => {
1498
+ if (isStreamClosed(err)) {
1499
+ console.error(
1500
+ chalk10.red(
1501
+ "Input stream closed mid-prompt. Re-run with the answers as flags \u2014 every prompt has one (see --help)."
1502
+ )
1503
+ );
1504
+ process.exit(1);
1505
+ }
1506
+ throw err;
1507
+ });
1508
+ }
1509
+
1510
+ // src/commands/secrets.ts
1468
1511
  var secretsCommand = new Command9("secrets").description("Manage app secrets");
1469
1512
  secretsCommand.command("set <key> [value]").description("Set a secret").option("-a, --app <id>", "App ID").action(async (key, value, opts) => {
1470
1513
  const apiKey = getApiKey();
1471
1514
  requireApiKey(apiKey);
1472
1515
  const appId = opts.app || getCurrentApp();
1473
1516
  if (!appId) {
1474
- console.error(chalk10.red("No app specified."));
1517
+ console.error(chalk11.red("No app specified."));
1475
1518
  process.exit(1);
1476
1519
  }
1477
1520
  if (!value) {
1478
- const answers = await inquirer3.prompt([
1479
- { type: "password", name: "value", message: `Value for ${key}:` }
1480
- ]);
1521
+ const answers = await prompt(
1522
+ [{ type: "password", name: "value", message: `Value for ${key}:` }],
1523
+ `Pass the value as an argument: videan secrets set ${key} <value>`
1524
+ );
1481
1525
  value = answers.value;
1482
1526
  }
1483
1527
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1484
1528
  await client4.secrets.set(appId, key, value);
1485
- console.log(chalk10.green(`Secret "${key}" set successfully.`));
1529
+ console.log(chalk11.green(`Secret "${key}" set successfully.`));
1486
1530
  });
1487
1531
  secretsCommand.command("list").description("List all secrets").option("-a, --app <id>", "App ID").action(async (opts) => {
1488
1532
  const apiKey = getApiKey();
1489
1533
  requireApiKey(apiKey);
1490
1534
  const appId = opts.app || getCurrentApp();
1491
1535
  if (!appId) {
1492
- console.error(chalk10.red("No app specified."));
1536
+ console.error(chalk11.red("No app specified."));
1493
1537
  process.exit(1);
1494
1538
  }
1495
1539
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1496
1540
  const secrets = await client4.secrets.list(appId);
1497
1541
  if (secrets.length === 0) {
1498
- console.log(chalk10.gray("No secrets configured."));
1542
+ console.log(chalk11.gray("No secrets configured."));
1499
1543
  return;
1500
1544
  }
1501
1545
  console.log(formatRow(["KEY", "UPDATED"], [30, 24]));
@@ -1508,24 +1552,23 @@ secretsCommand.command("remove <key>").description("Remove a secret").option("-a
1508
1552
  requireApiKey(apiKey);
1509
1553
  const appId = opts.app || getCurrentApp();
1510
1554
  if (!appId) {
1511
- console.error(chalk10.red("No app specified."));
1555
+ console.error(chalk11.red("No app specified."));
1512
1556
  process.exit(1);
1513
1557
  }
1514
1558
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1515
1559
  await client4.secrets.delete(appId, key);
1516
- console.log(chalk10.green(`Secret "${key}" removed.`));
1560
+ console.log(chalk11.green(`Secret "${key}" removed.`));
1517
1561
  });
1518
1562
 
1519
1563
  // src/commands/apps.ts
1520
1564
  import { Command as Command10 } from "commander";
1521
- import chalk11 from "chalk";
1522
- import inquirer4 from "inquirer";
1565
+ import chalk12 from "chalk";
1523
1566
  var appsCommand = new Command10("apps").description("Manage apps");
1524
1567
  function accessOption(value) {
1525
1568
  if (value === void 0) return void 0;
1526
1569
  const tier = value.trim().toLowerCase();
1527
1570
  if (!isVisibility(tier)) {
1528
- console.error(chalk11.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1571
+ console.error(chalk12.red(`Unknown --access value "${value}". Use one of: ${VISIBILITIES.join(", ")}`));
1529
1572
  process.exit(1);
1530
1573
  }
1531
1574
  return tier;
@@ -1534,7 +1577,7 @@ function modeOption(value) {
1534
1577
  if (value === void 0) return void 0;
1535
1578
  const mode = value.trim().toLowerCase();
1536
1579
  if (!isAppMode(mode)) {
1537
- console.error(chalk11.red(`Unknown --mode value "${value}". Use one of: ${APP_MODES.join(", ")}`));
1580
+ console.error(chalk12.red(`Unknown --mode value "${value}". Use one of: ${APP_MODES.join(", ")}`));
1538
1581
  process.exit(1);
1539
1582
  }
1540
1583
  return mode;
@@ -1550,7 +1593,7 @@ appsCommand.command("list").description("List all apps").option("-o, --org <id>"
1550
1593
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1551
1594
  const apps = await client4.apps.list(orgId);
1552
1595
  if (apps.length === 0) {
1553
- console.log(chalk11.gray("No apps yet. Create one with `videan apps create`."));
1596
+ console.log(chalk12.gray("No apps yet. Create one with `videan apps create`."));
1554
1597
  return;
1555
1598
  }
1556
1599
  const widths = [20, 24, 10, 44];
@@ -1572,13 +1615,14 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1572
1615
  const orgId = requireOrg(opts.org || getDefaultOrg());
1573
1616
  let name = opts.name;
1574
1617
  if (!name) {
1575
- const answers = await inquirer4.prompt([
1576
- { type: "input", name: "name", message: "App name:" }
1577
- ]);
1618
+ const answers = await prompt(
1619
+ [{ type: "input", name: "name", message: "App name:" }],
1620
+ "Pass the name: videan apps create --name <name>"
1621
+ );
1578
1622
  name = answers.name;
1579
1623
  }
1580
1624
  if (!name) {
1581
- console.error(chalk11.red("An app name is required."));
1625
+ console.error(chalk12.red("An app name is required."));
1582
1626
  process.exit(1);
1583
1627
  }
1584
1628
  const visibility = accessOption(opts.access) ?? (opts.public ? "public" : void 0);
@@ -1595,11 +1639,11 @@ appsCommand.command("create").description("Create a new app").option("-n, --name
1595
1639
  mode
1596
1640
  });
1597
1641
  setCurrentApp(app.id);
1598
- console.log(chalk11.green(`App "${app.name}" created (${app.id})`));
1599
- console.log(chalk11.gray(` Mode: ${app.mode ?? "container"}`));
1600
- console.log(chalk11.gray(` Access: ${describeAccess(app)}`));
1601
- console.log(chalk11.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1602
- console.log(chalk11.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1642
+ console.log(chalk12.green(`App "${app.name}" created (${app.id})`));
1643
+ console.log(chalk12.gray(` Mode: ${app.mode ?? "container"}`));
1644
+ console.log(chalk12.gray(` Access: ${describeAccess(app)}`));
1645
+ console.log(chalk12.gray(` URL after first deploy: ${appUrl(app.slug)}`));
1646
+ console.log(chalk12.gray(" Push code with `videan deploy` \u2014 the runtime is auto-detected."));
1603
1647
  }
1604
1648
  );
1605
1649
  appsCommand.command("set").description("Change an app's settings (scale, rate limit, gate, capabilities)").option("-a, --app <id>", "App ID").option("-n, --name <name>", "Rename the app (the URL keeps its original slug)").option("--cpu <units>", "CPU units per instance (256 = 0.25 vCPU)").option("--memory <mib>", "Memory per instance, in MiB").option("--instances <n>", "Number of instances to run").option("--rate-limit <rpm>", "Requests per minute per client").option("--no-rate-limit", "Remove the rate limit").option("--access <tier>", `Who may open the app: ${VISIBILITIES.join(" | ")}`).option("--public", "Serve without the platform login gate (same as --access public)").option("--private", "Require platform login (leaves the tier alone if it is already gated)").option("--postgres", "Provision a managed Postgres database").option("--storage", "Give the app an object-storage prefix").option("--email", "Let the app send mail through the platform").option("--stack <label>", "Group this app with its siblings under a shared label").option("--no-stack", "Remove the app from its stack").option("--mode <mode>", `How the app runs: ${APP_MODES.join(" | ")} (applies on the next deploy or restart)`).option("--json", "Print the raw response").action(
@@ -1608,16 +1652,16 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1608
1652
  requireApiKey(apiKey);
1609
1653
  const appId = opts.app || getCurrentApp();
1610
1654
  if (!appId) {
1611
- console.error(chalk11.red("No app specified. Use --app or link an app."));
1655
+ console.error(chalk12.red("No app specified. Use --app or link an app."));
1612
1656
  process.exit(1);
1613
1657
  }
1614
1658
  if (opts.public && opts.private) {
1615
- console.error(chalk11.red("--public and --private contradict each other."));
1659
+ console.error(chalk12.red("--public and --private contradict each other."));
1616
1660
  process.exit(1);
1617
1661
  }
1618
1662
  const access = accessOption(opts.access);
1619
1663
  if (access && (opts.public || opts.private)) {
1620
- console.error(chalk11.red("--access already says who may open the app; drop --public/--private."));
1664
+ console.error(chalk12.red("--access already says who may open the app; drop --public/--private."));
1621
1665
  process.exit(1);
1622
1666
  }
1623
1667
  const mode = modeOption(opts.mode);
@@ -1642,7 +1686,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1642
1686
  if (opts.stack === false) changes.stack = null;
1643
1687
  else if (typeof opts.stack === "string") changes.stack = opts.stack;
1644
1688
  if (Object.keys(changes).length === 0) {
1645
- console.error(chalk11.red("Nothing to change. Pass at least one option."));
1689
+ console.error(chalk12.red("Nothing to change. Pass at least one option."));
1646
1690
  process.exit(1);
1647
1691
  }
1648
1692
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
@@ -1651,7 +1695,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1651
1695
  console.log(JSON.stringify(app, null, 2));
1652
1696
  return;
1653
1697
  }
1654
- console.log(chalk11.green(`Updated ${app.name} (${app.id})`));
1698
+ console.log(chalk12.green(`Updated ${app.name} (${app.id})`));
1655
1699
  console.log(` Mode: ${app.mode ?? "container"}`);
1656
1700
  console.log(` Access: ${describeAccess(app)}`);
1657
1701
  console.log(` Scale: ${app.cpu ?? "default"} CPU / ${app.memory ?? "default"} MiB`);
@@ -1660,7 +1704,7 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1660
1704
  );
1661
1705
  if (app.restartRequired) {
1662
1706
  console.log(
1663
- chalk11.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1707
+ chalk12.yellow("\n These settings apply to the next deploy. Run `videan deploy` to pick them up.")
1664
1708
  );
1665
1709
  }
1666
1710
  }
@@ -1668,37 +1712,42 @@ appsCommand.command("set").description("Change an app's settings (scale, rate li
1668
1712
  function intOption(flag, raw) {
1669
1713
  const n = Number(raw);
1670
1714
  if (!Number.isInteger(n) || n <= 0) {
1671
- console.error(chalk11.red(`${flag} expects a positive whole number, got "${raw}".`));
1715
+ console.error(chalk12.red(`${flag} expects a positive whole number, got "${raw}".`));
1672
1716
  process.exit(1);
1673
1717
  }
1674
1718
  return n;
1675
1719
  }
1676
- appsCommand.command("delete <id>").description("Delete an app and all its resources").action(async (id) => {
1720
+ appsCommand.command("delete <id>").description("Delete an app and all its resources").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
1677
1721
  const apiKey = getApiKey();
1678
1722
  requireApiKey(apiKey);
1679
- const { confirm } = await inquirer4.prompt([
1680
- {
1681
- type: "confirm",
1682
- name: "confirm",
1683
- message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,
1684
- default: false
1685
- }
1686
- ]);
1687
- if (!confirm) return;
1723
+ if (!opts.yes) {
1724
+ const { confirm } = await prompt(
1725
+ [
1726
+ {
1727
+ type: "confirm",
1728
+ name: "confirm",
1729
+ message: `Delete app ${id}? This stops the app and destroys its database, secrets and snapshots.`,
1730
+ default: false
1731
+ }
1732
+ ],
1733
+ "Confirm with a flag: videan apps delete <id> --yes"
1734
+ );
1735
+ if (!confirm) return;
1736
+ }
1688
1737
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1689
1738
  const { warnings } = await client4.apps.delete(id);
1690
- console.log(chalk11.green("App deleted."));
1739
+ console.log(chalk12.green("App deleted."));
1691
1740
  if (warnings.length > 0) {
1692
- console.log(chalk11.yellow("\nSome resources could not be released:"));
1741
+ console.log(chalk12.yellow("\nSome resources could not be released:"));
1693
1742
  for (const w of warnings) {
1694
- console.log(chalk11.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1743
+ console.log(chalk12.yellow(` ${w.step}: ${w.error || "unknown error"}`));
1695
1744
  }
1696
1745
  }
1697
1746
  });
1698
1747
 
1699
1748
  // src/commands/status.ts
1700
1749
  import { Command as Command11 } from "commander";
1701
- import chalk12 from "chalk";
1750
+ import chalk13 from "chalk";
1702
1751
  function accessLine(app) {
1703
1752
  const tier = app.visibility ? visibilityOf(app.visibility) : app.authRequired ? "org" : "public";
1704
1753
  return `${tier} \u2014 ${VISIBILITY_LABELS[tier].toLowerCase()}`;
@@ -1708,42 +1757,41 @@ var statusCommand = new Command11("status").description("Show current app status
1708
1757
  requireApiKey(apiKey);
1709
1758
  const appId = opts.app || getCurrentApp();
1710
1759
  if (!appId) {
1711
- console.error(chalk12.red("No app specified. Use --app or link an app."));
1760
+ console.error(chalk13.red("No app specified. Use --app or link an app."));
1712
1761
  process.exit(1);
1713
1762
  }
1714
1763
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1715
1764
  const app = await client4.apps.get(appId);
1716
1765
  const health = await client4.apps.health(appId);
1717
- console.log(chalk12.bold(app.name));
1766
+ console.log(chalk13.bold(app.name));
1718
1767
  console.log(` ID: ${app.id}`);
1719
1768
  console.log(` Created: ${app.createdAt}`);
1720
1769
  console.log(` Postgres: ${app.postgresEnabled ? "enabled" : "disabled"}`);
1721
1770
  console.log(` Access: ${accessLine(app)}`);
1722
1771
  console.log(` State: ${formatStatus(health.state)}`);
1723
- console.log(` Healthy: ${health.healthy ? chalk12.green("yes") : chalk12.red("no")}`);
1724
- console.log(` URL: ${chalk12.cyan(health.url || appUrl(app.slug))}`);
1772
+ console.log(` Healthy: ${health.healthy ? chalk13.green("yes") : chalk13.red("no")}`);
1773
+ console.log(` URL: ${chalk13.cyan(health.url || appUrl(app.slug))}`);
1725
1774
  const task = health.task;
1726
1775
  if (task) {
1727
1776
  if (task.exit_code !== void 0 && task.exit_code !== null) {
1728
1777
  const code = String(task.exit_code);
1729
- console.log(` Exit code: ${task.exit_code === 0 ? code : chalk12.red(code)}`);
1778
+ console.log(` Exit code: ${task.exit_code === 0 ? code : chalk13.red(code)}`);
1730
1779
  }
1731
1780
  if (task.stopped_reason) {
1732
- console.log(` Stopped: ${chalk12.yellow(task.stopped_reason)}`);
1781
+ console.log(` Stopped: ${chalk13.yellow(task.stopped_reason)}`);
1733
1782
  }
1734
1783
  if (task.container_reason) {
1735
- console.log(` Detail: ${chalk12.yellow(task.container_reason)}`);
1784
+ console.log(` Detail: ${chalk13.yellow(task.container_reason)}`);
1736
1785
  }
1737
1786
  }
1738
1787
  if (!health.healthy) {
1739
- console.log(chalk12.gray("\nRun `videan logs` to see why."));
1788
+ console.log(chalk13.gray("\nRun `videan logs` to see why."));
1740
1789
  }
1741
1790
  });
1742
1791
 
1743
1792
  // src/commands/keys.ts
1744
1793
  import { Command as Command12 } from "commander";
1745
- import chalk13 from "chalk";
1746
- import inquirer5 from "inquirer";
1794
+ import chalk14 from "chalk";
1747
1795
  var keysCommand = new Command12("keys").description("Manage API keys for programmatic access");
1748
1796
  keysCommand.command("create <name>").description("Create an API key").option("-o, --org <id>", "Organization ID").option("-e, --expires <days>", "Expire the key after N days").option(
1749
1797
  "--app <id>",
@@ -1756,7 +1804,7 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1756
1804
  if (opts.expires !== void 0) {
1757
1805
  expiresInDays = Number(opts.expires);
1758
1806
  if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
1759
- console.error(chalk13.red("--expires must be a whole number of days, at least 1."));
1807
+ console.error(chalk14.red("--expires must be a whole number of days, at least 1."));
1760
1808
  process.exit(1);
1761
1809
  }
1762
1810
  }
@@ -1767,22 +1815,22 @@ keysCommand.command("create <name>").description("Create an API key").option("-o
1767
1815
  expiresInDays,
1768
1816
  appId: opts.app
1769
1817
  });
1770
- console.log(chalk13.green(`Created key "${key.name}" (${key.id})`));
1818
+ console.log(chalk14.green(`Created key "${key.name}" (${key.id})`));
1771
1819
  console.log(`
1772
- ${chalk13.bold(key.key)}
1820
+ ${chalk14.bold(key.key)}
1773
1821
  `);
1774
- console.log(chalk13.yellow("Copy it now. It cannot be shown again."));
1822
+ console.log(chalk14.yellow("Copy it now. It cannot be shown again."));
1775
1823
  if (key.appId) {
1776
1824
  console.log(
1777
- chalk13.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1825
+ chalk14.gray(`Service token, scoped to app ${key.appId}. It cannot reach anything else.`)
1778
1826
  );
1779
1827
  } else {
1780
1828
  console.log(
1781
- chalk13.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1829
+ chalk14.gray("Acts as you across every app in the org -- do not deploy it inside an app.")
1782
1830
  );
1783
1831
  }
1784
1832
  if (key.expiresAt) {
1785
- console.log(chalk13.gray(`Expires ${key.expiresAt}`));
1833
+ console.log(chalk14.gray(`Expires ${key.expiresAt}`));
1786
1834
  }
1787
1835
  });
1788
1836
  keysCommand.command("list").description("List API keys").option("-o, --org <id>", "Organization ID").action(async (opts) => {
@@ -1792,13 +1840,13 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1792
1840
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1793
1841
  const keys = await client4.apiKeys.list(orgId);
1794
1842
  if (keys.length === 0) {
1795
- console.log(chalk13.gray("No API keys. Create one with `videan keys create <name>`."));
1843
+ console.log(chalk14.gray("No API keys. Create one with `videan keys create <name>`."));
1796
1844
  return;
1797
1845
  }
1798
1846
  const widths = [24, 24, 16, 22, 12, 24];
1799
1847
  console.log(formatRow(["NAME", "ID", "PREFIX", "LAST USED", "STATE", "SCOPE"], widths));
1800
1848
  for (const key of keys) {
1801
- const state = key.revokedAt ? chalk13.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk13.yellow("expired") : chalk13.green("active");
1849
+ const state = key.revokedAt ? chalk14.red("revoked") : key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date() ? chalk14.yellow("expired") : chalk14.green("active");
1802
1850
  console.log(formatRow(
1803
1851
  [
1804
1852
  key.name,
@@ -1812,21 +1860,24 @@ keysCommand.command("list").description("List API keys").option("-o, --org <id>"
1812
1860
  ));
1813
1861
  }
1814
1862
  });
1815
- keysCommand.command("revoke <id>").description("Revoke an API key").action(async (id) => {
1863
+ keysCommand.command("revoke <id>").description("Revoke an API key").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
1816
1864
  const apiKey = getApiKey();
1817
1865
  requireApiKey(apiKey);
1818
- const { confirm } = await inquirer5.prompt([
1819
- { type: "confirm", name: "confirm", message: `Revoke key ${id}?`, default: false }
1820
- ]);
1821
- if (!confirm) return;
1866
+ if (!opts.yes) {
1867
+ const { confirm } = await prompt(
1868
+ [{ type: "confirm", name: "confirm", message: `Revoke key ${id}?`, default: false }],
1869
+ "Confirm with a flag: videan keys revoke <id> --yes"
1870
+ );
1871
+ if (!confirm) return;
1872
+ }
1822
1873
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1823
1874
  await client4.apiKeys.revoke(id);
1824
- console.log(chalk13.green("Key revoked."));
1875
+ console.log(chalk14.green("Key revoked."));
1825
1876
  });
1826
1877
 
1827
1878
  // src/commands/orgs.ts
1828
1879
  import { Command as Command13 } from "commander";
1829
- import chalk14 from "chalk";
1880
+ import chalk15 from "chalk";
1830
1881
  var orgsCommand = new Command13("orgs").description("Manage organizations");
1831
1882
  orgsCommand.command("list").description("List your organizations").action(async () => {
1832
1883
  const apiKey = getApiKey();
@@ -1835,14 +1886,14 @@ orgsCommand.command("list").description("List your organizations").action(async
1835
1886
  const orgs = await client4.orgs.list();
1836
1887
  const current = getDefaultOrg();
1837
1888
  if (orgs.length === 0) {
1838
- console.log(chalk14.gray("No organizations. Create one with `videan orgs create <name>`."));
1889
+ console.log(chalk15.gray("No organizations. Create one with `videan orgs create <name>`."));
1839
1890
  return;
1840
1891
  }
1841
1892
  const widths = [24, 24, 10, 8];
1842
1893
  console.log(formatRow(["NAME", "ID", "PLAN", ""], widths));
1843
1894
  for (const org of orgs) {
1844
1895
  console.log(formatRow(
1845
- [org.name, org.id, org.plan, org.id === current ? chalk14.green("default") : ""],
1896
+ [org.name, org.id, org.plan, org.id === current ? chalk15.green("default") : ""],
1846
1897
  widths
1847
1898
  ));
1848
1899
  }
@@ -1853,7 +1904,7 @@ orgsCommand.command("create <name>").description("Create an organization").actio
1853
1904
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1854
1905
  const org = await client4.orgs.create(name);
1855
1906
  if (!getDefaultOrg()) setDefaultOrg(org.id);
1856
- console.log(chalk14.green(`Organization "${org.name}" created (${org.id})`));
1907
+ console.log(chalk15.green(`Organization "${org.name}" created (${org.id})`));
1857
1908
  });
1858
1909
  orgsCommand.command("use <id>").description("Set the default organization for this machine").action(async (id) => {
1859
1910
  const apiKey = getApiKey();
@@ -1861,7 +1912,7 @@ orgsCommand.command("use <id>").description("Set the default organization for th
1861
1912
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1862
1913
  const org = await client4.orgs.get(id);
1863
1914
  setDefaultOrg(org.id);
1864
- console.log(chalk14.green(`Default organization set to "${org.name}".`));
1915
+ console.log(chalk15.green(`Default organization set to "${org.name}".`));
1865
1916
  });
1866
1917
  orgsCommand.command("members").description("List organization members").option("-o, --org <id>", "Organization ID").action(async (opts) => {
1867
1918
  const apiKey = getApiKey();
@@ -1884,12 +1935,12 @@ orgsCommand.command("invite <email>").description("Invite a member by email").op
1884
1935
  const orgId = requireOrg(opts.org || getDefaultOrg());
1885
1936
  const client4 = new CloudClient({ apiKey, baseUrl: getBaseUrl() });
1886
1937
  await client4.orgs.invite(orgId, email);
1887
- console.log(chalk14.green(`Invited ${email}.`));
1938
+ console.log(chalk15.green(`Invited ${email}.`));
1888
1939
  });
1889
1940
 
1890
1941
  // src/commands/storage.ts
1891
1942
  import { Command as Command14 } from "commander";
1892
- import chalk15 from "chalk";
1943
+ import chalk16 from "chalk";
1893
1944
  import fs5 from "fs-extra";
1894
1945
  import path5 from "node:path";
1895
1946
  var storageCommand = new Command14("storage").description("Store files for an app");
@@ -1901,7 +1952,7 @@ function client() {
1901
1952
  function requireApp(opt) {
1902
1953
  const appId = opt || getCurrentApp();
1903
1954
  if (!appId) {
1904
- console.error(chalk15.red("No app specified. Use --app or link an app."));
1955
+ console.error(chalk16.red("No app specified. Use --app or link an app."));
1905
1956
  process.exit(1);
1906
1957
  }
1907
1958
  return appId;
@@ -1924,7 +1975,7 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1924
1975
  return;
1925
1976
  }
1926
1977
  if (objects.length === 0) {
1927
- console.log(chalk15.gray("No objects stored. Upload one with `videan storage put <file>`."));
1978
+ console.log(chalk16.gray("No objects stored. Upload one with `videan storage put <file>`."));
1928
1979
  return;
1929
1980
  }
1930
1981
  const widths = [52, 12, 24];
@@ -1933,13 +1984,13 @@ storageCommand.command("ls [prefix]").description("List stored objects").option(
1933
1984
  console.log(formatRow([o.key, human(o.size), o.lastModified || "-"], widths));
1934
1985
  }
1935
1986
  const total = objects.reduce((sum, o) => sum + o.size, 0);
1936
- console.log(chalk15.dim(`
1987
+ console.log(chalk16.dim(`
1937
1988
  ${objects.length} object(s), ${human(total)}`));
1938
1989
  });
1939
1990
  storageCommand.command("put <file> [key]").description("Upload a file (key defaults to the file's name)").option("-a, --app <id>", "App ID").option("-t, --type <mime>", "Content type").action(async (file, key, opts) => {
1940
1991
  const appId = requireApp(opts.app);
1941
1992
  if (!await fs5.pathExists(file)) {
1942
- console.error(chalk15.red(`No such file: ${file}`));
1993
+ console.error(chalk16.red(`No such file: ${file}`));
1943
1994
  process.exit(1);
1944
1995
  }
1945
1996
  const body = await fs5.readFile(file);
@@ -1948,27 +1999,27 @@ storageCommand.command("put <file> [key]").description("Upload a file (key defau
1948
1999
  contentType: opts.type
1949
2000
  });
1950
2001
  console.log(
1951
- chalk15.green(`Uploaded ${objectKey}`) + chalk15.dim(` (${human(body.length)})`)
2002
+ chalk16.green(`Uploaded ${objectKey}`) + chalk16.dim(` (${human(body.length)})`)
1952
2003
  );
1953
- if (stored.etag) console.log(chalk15.dim(`etag ${stored.etag}`));
2004
+ if (stored.etag) console.log(chalk16.dim(`etag ${stored.etag}`));
1954
2005
  });
1955
2006
  storageCommand.command("get <key> [file]").description("Download an object (defaults to the key's basename)").option("-a, --app <id>", "App ID").action(async (key, file, opts) => {
1956
2007
  const appId = requireApp(opts.app);
1957
2008
  const bytes = await client().storage.download(appId, key);
1958
2009
  const dest = file || path5.basename(key);
1959
2010
  await fs5.writeFile(dest, bytes);
1960
- console.log(chalk15.green(`Wrote ${dest}`) + chalk15.dim(` (${human(bytes.length)})`));
2011
+ console.log(chalk16.green(`Wrote ${dest}`) + chalk16.dim(` (${human(bytes.length)})`));
1961
2012
  });
1962
2013
  storageCommand.command("rm <key>").description("Delete an object").option("-a, --app <id>", "App ID").action(async (key, opts) => {
1963
2014
  const appId = requireApp(opts.app);
1964
2015
  await client().storage.delete(appId, key);
1965
- console.log(chalk15.green(`Deleted ${key}`));
2016
+ console.log(chalk16.green(`Deleted ${key}`));
1966
2017
  });
1967
2018
  storageCommand.command("url <key>").description("Print a temporary link to an object").option("-a, --app <id>", "App ID").option("-e, --expires <seconds>", "Lifetime in seconds (30-3600)", "900").option("--upload", "Sign an upload (PUT) URL instead of a download").option("-d, --download <filename>", "Serve as a download with this filename").action(async (key, opts) => {
1968
2019
  const appId = requireApp(opts.app);
1969
2020
  const expiresIn = Number(opts.expires);
1970
2021
  if (!Number.isInteger(expiresIn) || expiresIn < 30 || expiresIn > 3600) {
1971
- console.error(chalk15.red("--expires must be a whole number of seconds between 30 and 3600."));
2022
+ console.error(chalk16.red("--expires must be a whole number of seconds between 30 and 3600."));
1972
2023
  process.exit(1);
1973
2024
  }
1974
2025
  const signed = await client().storage.presign(appId, key, {
@@ -1978,7 +2029,7 @@ storageCommand.command("url <key>").description("Print a temporary link to an ob
1978
2029
  });
1979
2030
  console.log(signed.url);
1980
2031
  console.error(
1981
- chalk15.yellow(
2032
+ chalk16.yellow(
1982
2033
  `This ${signed.method} link works for ${expiresIn}s for anyone who has it.`
1983
2034
  )
1984
2035
  );
@@ -1991,7 +2042,7 @@ storageCommand.command("usage").description("Total objects and bytes stored").op
1991
2042
 
1992
2043
  // src/commands/email.ts
1993
2044
  import { Command as Command15 } from "commander";
1994
- import chalk16 from "chalk";
2045
+ import chalk17 from "chalk";
1995
2046
  import fs6 from "fs-extra";
1996
2047
  var emailCommand = new Command15("email").description("Send email from an app");
1997
2048
  function client2() {
@@ -2002,7 +2053,7 @@ function client2() {
2002
2053
  function requireApp2(opt) {
2003
2054
  const appId = opt || getCurrentApp();
2004
2055
  if (!appId) {
2005
- console.error(chalk16.red("No app specified. Use --app or link an app."));
2056
+ console.error(chalk17.red("No app specified. Use --app or link an app."));
2006
2057
  process.exit(1);
2007
2058
  }
2008
2059
  return appId;
@@ -2018,7 +2069,7 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
2018
2069
  else text = contents;
2019
2070
  }
2020
2071
  if (!text && !html) {
2021
- console.error(chalk16.red("Nothing to send. Pass --text, --html or --body-file."));
2072
+ console.error(chalk17.red("Nothing to send. Pass --text, --html or --body-file."));
2022
2073
  process.exit(1);
2023
2074
  }
2024
2075
  const sent = await client2().email.send(appId, {
@@ -2032,12 +2083,12 @@ emailCommand.command("send").description("Send an email").requiredOption("-t, --
2032
2083
  console.log(JSON.stringify(sent, null, 2));
2033
2084
  return;
2034
2085
  }
2035
- console.log(chalk16.green("Sent"));
2086
+ console.log(chalk17.green("Sent"));
2036
2087
  console.log(` From: ${sent.from}`);
2037
2088
  console.log(` To: ${sent.to.join(", ")}`);
2038
2089
  console.log(` Message: ${sent.messageId ?? "(no id returned)"}`);
2039
2090
  console.log(
2040
- chalk16.dim(
2091
+ chalk17.dim(
2041
2092
  ` Quota: ${sent.quota.sent}/${sent.quota.quota} today, resets ${sent.quota.resetsAt}`
2042
2093
  )
2043
2094
  );
@@ -2052,12 +2103,12 @@ emailCommand.command("quota").description("Show today's sending allowance").opti
2052
2103
  console.log(`Sent today: ${quota.sent}`);
2053
2104
  console.log(`Daily quota: ${quota.quota}`);
2054
2105
  console.log(`Remaining: ${quota.remaining}`);
2055
- console.log(chalk16.dim(`Resets at: ${quota.resetsAt}`));
2106
+ console.log(chalk17.dim(`Resets at: ${quota.resetsAt}`));
2056
2107
  });
2057
2108
 
2058
2109
  // src/commands/jobs.ts
2059
2110
  import { Command as Command16 } from "commander";
2060
- import chalk17 from "chalk";
2111
+ import chalk18 from "chalk";
2061
2112
  import fs7 from "fs-extra";
2062
2113
  var jobsCommand = new Command16("jobs").description("Enqueue and inspect background jobs");
2063
2114
  function client3() {
@@ -2068,7 +2119,7 @@ function client3() {
2068
2119
  function requireApp3(opt) {
2069
2120
  const appId = opt || getCurrentApp();
2070
2121
  if (!appId) {
2071
- console.error(chalk17.red("No app specified. Use --app or run `videan init` first."));
2122
+ console.error(chalk18.red("No app specified. Use --app or run `videan init` first."));
2072
2123
  process.exit(1);
2073
2124
  }
2074
2125
  return appId;
@@ -2076,20 +2127,20 @@ function requireApp3(opt) {
2076
2127
  function intOption2(flag, raw) {
2077
2128
  const n = Number(raw);
2078
2129
  if (!Number.isInteger(n) || n < 0) {
2079
- console.error(chalk17.red(`${flag} expects a whole number, got "${raw}".`));
2130
+ console.error(chalk18.red(`${flag} expects a whole number, got "${raw}".`));
2080
2131
  process.exit(1);
2081
2132
  }
2082
2133
  return n;
2083
2134
  }
2084
2135
  var STATUS_COLOR = {
2085
- queued: chalk17.yellow,
2086
- running: chalk17.cyan,
2087
- succeeded: chalk17.green,
2088
- dead: chalk17.red,
2089
- cancelled: chalk17.gray
2136
+ queued: chalk18.yellow,
2137
+ running: chalk18.cyan,
2138
+ succeeded: chalk18.green,
2139
+ dead: chalk18.red,
2140
+ cancelled: chalk18.gray
2090
2141
  };
2091
2142
  function statusLabel(status) {
2092
- return (STATUS_COLOR[status] ?? chalk17.white)(status);
2143
+ return (STATUS_COLOR[status] ?? chalk18.white)(status);
2093
2144
  }
2094
2145
  jobsCommand.command("enqueue").description("Queue a job for the app to run").option("-a, --app <id>", "App ID").option("-q, --queue <name>", "Queue name (default: default)").option("-p, --path <path>", "Path on the app to POST to (default: /jobs)").option("-d, --data <json>", "JSON payload").option("-f, --file <path>", "Read the JSON payload from a file").option("--delay <seconds>", "Run no earlier than this many seconds from now").option("--attempts <n>", "Attempts before the job is given up on (default: 3)").option("--timeout <seconds>", "How long the handler gets per attempt (default: 60)").option("--key <key>", "Idempotency key -- a repeat returns the same job").option("--json", "Print the raw response").action(async (opts) => {
2095
2146
  const appId = requireApp3(opts.app);
@@ -2099,7 +2150,7 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
2099
2150
  try {
2100
2151
  payload = JSON.parse(raw);
2101
2152
  } catch (e) {
2102
- console.error(chalk17.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
2153
+ console.error(chalk18.red(`--${opts.file ? "file" : "data"} is not valid JSON: ${e.message}`));
2103
2154
  process.exit(1);
2104
2155
  }
2105
2156
  }
@@ -2117,13 +2168,13 @@ jobsCommand.command("enqueue").description("Queue a job for the app to run").opt
2117
2168
  return;
2118
2169
  }
2119
2170
  if (job.deduplicated) {
2120
- console.log(chalk17.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
2171
+ console.log(chalk18.yellow(`Already queued as ${job.id} (idempotency key "${opts.key}")`));
2121
2172
  } else {
2122
- console.log(chalk17.green(`Queued ${job.id}`));
2173
+ console.log(chalk18.green(`Queued ${job.id}`));
2123
2174
  }
2124
- console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2175
+ console.log(chalk18.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2125
2176
  if (job.runAt && Date.parse(job.runAt) > Date.now() + 1e3) {
2126
- console.log(chalk17.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2177
+ console.log(chalk18.gray(` Runs at ${new Date(job.runAt).toLocaleString()}`));
2127
2178
  }
2128
2179
  });
2129
2180
  jobsCommand.command("list").description("List jobs, newest first").option("-a, --app <id>", "App ID").option("-s, --status <status>", "queued, running, succeeded, dead or cancelled").option("-q, --queue <name>", "Only this queue").option("-n, --limit <n>", "How many to show (default: 20)").option("--json", "Print the raw response").action(async (opts) => {
@@ -2138,17 +2189,17 @@ jobsCommand.command("list").description("List jobs, newest first").option("-a, -
2138
2189
  return;
2139
2190
  }
2140
2191
  const depth = Object.entries(counts).map(([status, n]) => `${statusLabel(status)} ${n}`).join(" ");
2141
- console.log(depth || chalk17.gray("No jobs yet."));
2192
+ console.log(depth || chalk18.gray("No jobs yet."));
2142
2193
  if (jobs.length === 0) return;
2143
2194
  console.log();
2144
2195
  for (const job of jobs) {
2145
- const attempts = job.attempts > 0 ? chalk17.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2196
+ const attempts = job.attempts > 0 ? chalk18.gray(` ${job.attempts}/${job.maxAttempts}`) : "";
2146
2197
  console.log(
2147
- `${statusLabel(job.status).padEnd(20)} ${chalk17.white(job.id)} ${chalk17.gray(
2198
+ `${statusLabel(job.status).padEnd(20)} ${chalk18.white(job.id)} ${chalk18.gray(
2148
2199
  `${job.queue} ${job.path}`
2149
2200
  )}${attempts}`
2150
2201
  );
2151
- if (job.error) console.log(chalk17.red(` ${job.error}`));
2202
+ if (job.error) console.log(chalk18.red(` ${job.error}`));
2152
2203
  }
2153
2204
  });
2154
2205
  jobsCommand.command("get <job-id>").description("Show one job in full").option("-a, --app <id>", "App ID").option("--json", "Print the raw response").action(async (jobId, opts) => {
@@ -2157,17 +2208,17 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2157
2208
  console.log(JSON.stringify(job, null, 2));
2158
2209
  return;
2159
2210
  }
2160
- console.log(`${chalk17.white(job.id)} ${statusLabel(job.status)}`);
2161
- console.log(chalk17.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2162
- console.log(chalk17.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2163
- if (job.statusCode) console.log(chalk17.gray(` Last response: HTTP ${job.statusCode}`));
2164
- if (job.durationMs) console.log(chalk17.gray(` Took: ${job.durationMs}ms`));
2165
- if (job.error) console.log(chalk17.red(` Error: ${job.error}`));
2211
+ console.log(`${chalk18.white(job.id)} ${statusLabel(job.status)}`);
2212
+ console.log(chalk18.gray(` ${job.method} ${job.path} on queue "${job.queue}"`));
2213
+ console.log(chalk18.gray(` Attempts: ${job.attempts}/${job.maxAttempts}`));
2214
+ if (job.statusCode) console.log(chalk18.gray(` Last response: HTTP ${job.statusCode}`));
2215
+ if (job.durationMs) console.log(chalk18.gray(` Took: ${job.durationMs}ms`));
2216
+ if (job.error) console.log(chalk18.red(` Error: ${job.error}`));
2166
2217
  if (job.status === "queued" && job.attempts > 0) {
2167
- console.log(chalk17.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2218
+ console.log(chalk18.yellow(` Retrying at ${new Date(job.runAt).toLocaleString()}`));
2168
2219
  }
2169
2220
  if (job.payload !== null && job.payload !== void 0) {
2170
- console.log(chalk17.gray(" Payload:"));
2221
+ console.log(chalk18.gray(" Payload:"));
2171
2222
  console.log(
2172
2223
  JSON.stringify(job.payload, null, 2).split("\n").map((l) => ` ${l}`).join("\n")
2173
2224
  );
@@ -2175,16 +2226,16 @@ jobsCommand.command("get <job-id>").description("Show one job in full").option("
2175
2226
  });
2176
2227
  jobsCommand.command("retry <job-id>").description("Run a dead job again, from a clean attempt count").option("-a, --app <id>", "App ID").action(async (jobId, opts) => {
2177
2228
  const job = await client3().jobs.retry(requireApp3(opts.app), jobId);
2178
- console.log(chalk17.green(`Requeued ${job.id}`));
2229
+ console.log(chalk18.green(`Requeued ${job.id}`));
2179
2230
  });
2180
2231
  jobsCommand.command("cancel <job-id>").description("Cancel a job that has not started yet").option("-a, --app <id>", "App ID").action(async (jobId, opts) => {
2181
2232
  const job = await client3().jobs.cancel(requireApp3(opts.app), jobId);
2182
- console.log(chalk17.green(`Cancelled ${job.id}`));
2233
+ console.log(chalk18.green(`Cancelled ${job.id}`));
2183
2234
  });
2184
2235
 
2185
2236
  // src/commands/usage.ts
2186
2237
  import { Command as Command17 } from "commander";
2187
- import chalk18 from "chalk";
2238
+ import chalk19 from "chalk";
2188
2239
  var usageCommand = new Command17("usage").description("Metered usage and cost for the current month").option("-o, --org <id>", "Organization ID").option("-a, --app <id>", "Show one app's share instead of the whole org").option("-p, --period <YYYY-MM>", "Month to report on (default: the month in progress)").option("--json", "Print the raw response").action(async (opts) => {
2189
2240
  const apiKey = getApiKey();
2190
2241
  requireApiKey(apiKey);
@@ -2193,61 +2244,61 @@ var usageCommand = new Command17("usage").description("Metered usage and cost fo
2193
2244
  const appId = opts.app || getCurrentApp();
2194
2245
  const report2 = await client4.apps.usage(appId, opts.period);
2195
2246
  if (opts.json) return void console.log(JSON.stringify(report2, null, 2));
2196
- console.log(chalk18.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2247
+ console.log(chalk19.dim(`${period(report2.periodStart, report2.periodEnd)} app ${appId}`));
2197
2248
  console.log();
2198
2249
  printMetrics(report2.metrics);
2199
2250
  console.log();
2200
2251
  for (const line of report2.lines) {
2201
- console.log(` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2252
+ console.log(` ${chalk19.dim(line.label.padEnd(22))} ${money(line.cents)}`);
2202
2253
  }
2203
- console.log(` ${chalk18.bold("Share of the bill".padEnd(22))} ${chalk18.bold(report2.cost)}`);
2254
+ console.log(` ${chalk19.bold("Share of the bill".padEnd(22))} ${chalk19.bold(report2.cost)}`);
2204
2255
  console.log();
2205
2256
  console.log(
2206
- chalk18.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2257
+ chalk19.dim("At list price, with no plan allowance applied \u2014 see `videan usage` for the org total.")
2207
2258
  );
2208
2259
  return;
2209
2260
  }
2210
2261
  const orgId = opts.org || getDefaultOrg();
2211
2262
  if (!orgId) {
2212
- console.error(chalk18.red("No org specified. Use --org, or --app for a single app."));
2263
+ console.error(chalk19.red("No org specified. Use --org, or --app for a single app."));
2213
2264
  process.exit(1);
2214
2265
  }
2215
2266
  const report = await client4.orgs.usage(orgId, opts.period);
2216
2267
  if (opts.json) return void console.log(JSON.stringify(report, null, 2));
2217
2268
  const inv = report.invoice;
2218
2269
  console.log(
2219
- chalk18.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2270
+ chalk19.dim(`${period(report.periodStart, report.periodEnd)} org ${orgId} plan ${report.plan}`)
2220
2271
  );
2221
2272
  console.log();
2222
2273
  printMetrics(report.metrics);
2223
2274
  if (report.apps.length > 0) {
2224
2275
  console.log();
2225
- console.log(chalk18.bold("By app"));
2276
+ console.log(chalk19.bold("By app"));
2226
2277
  for (const app of [...report.apps].sort((a, b) => b.cents - a.cents)) {
2227
2278
  const name = app.slug || app.name || app.appId || "(deleted apps)";
2228
- console.log(` ${chalk18.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2279
+ console.log(` ${chalk19.dim(String(name).padEnd(24))} ${money(app.cents)}`);
2229
2280
  }
2230
2281
  }
2231
2282
  console.log();
2232
2283
  for (const line of inv.lines) {
2233
2284
  console.log(
2234
- ` ${chalk18.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk18.dim(
2285
+ ` ${chalk19.dim(line.label.padEnd(22))} ${money(line.cents)} ${chalk19.dim(
2235
2286
  `(${line.quantity} \xD7 ${line.unitPriceCents}\xA2)`
2236
2287
  )}`
2237
2288
  );
2238
2289
  }
2239
- console.log(` ${chalk18.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2290
+ console.log(` ${chalk19.dim("Usage".padEnd(22))} ${money(inv.usageCents)}`);
2240
2291
  if (inv.includedUsageCents > 0) {
2241
2292
  console.log(
2242
- ` ${chalk18.dim("Included".padEnd(22))} ${chalk18.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2293
+ ` ${chalk19.dim("Included".padEnd(22))} ${chalk19.green(`-${money(Math.min(inv.usageCents, inv.includedUsageCents))}`)}`
2243
2294
  );
2244
2295
  }
2245
2296
  if (inv.baseCents > 0) {
2246
- console.log(` ${chalk18.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2297
+ console.log(` ${chalk19.dim("Plan".padEnd(22))} ${money(inv.baseCents)}`);
2247
2298
  }
2248
- console.log(` ${chalk18.bold("Total".padEnd(22))} ${chalk18.bold(report.total)}`);
2299
+ console.log(` ${chalk19.bold("Total".padEnd(22))} ${chalk19.bold(report.total)}`);
2249
2300
  console.log();
2250
- console.log(chalk18.dim("Usage to date. The period is priced as it stands right now."));
2301
+ console.log(chalk19.dim("Usage to date. The period is priced as it stands right now."));
2251
2302
  });
2252
2303
  usageCommand.command("plans").description("The plan catalogue and what each one allows").option("--json", "Print the raw response").action(async (opts) => {
2253
2304
  const client4 = new CloudClient({ apiKey: getApiKey() || "", baseUrl: getBaseUrl() });
@@ -2255,11 +2306,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2255
2306
  if (opts.json) return void console.log(JSON.stringify(plans, null, 2));
2256
2307
  for (const plan of plans) {
2257
2308
  console.log(
2258
- `${chalk18.bold(plan.name)} ${chalk18.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk18.dim(`, ${plan.includedUsage} usage included`) : "")
2309
+ `${chalk19.bold(plan.name)} ${chalk19.dim(`(${plan.id})`)} ${plan.base}/mo` + (plan.includedUsageCents > 0 ? chalk19.dim(`, ${plan.includedUsage} usage included`) : "")
2259
2310
  );
2260
2311
  const l = plan.limits;
2261
2312
  console.log(
2262
- chalk18.dim(
2313
+ chalk19.dim(
2263
2314
  ` ${l.apps} apps \xB7 ${l.instancesPerApp} instances/app \xB7 up to ${l.maxCpu} CPU / ${l.maxMemory} MB \xB7 ${l.maxStorageMb} MB storage${l.postgres ? " \xB7 postgres" : ""}${l.customDomains ? " \xB7 custom domains" : ""}`
2264
2315
  )
2265
2316
  );
@@ -2268,11 +2319,11 @@ usageCommand.command("plans").description("The plan catalogue and what each one
2268
2319
  });
2269
2320
  function printMetrics(metrics) {
2270
2321
  if (metrics.length === 0) {
2271
- console.log(chalk18.dim(" Nothing metered in this period yet."));
2322
+ console.log(chalk19.dim(" Nothing metered in this period yet."));
2272
2323
  return;
2273
2324
  }
2274
2325
  for (const m of metrics) {
2275
- console.log(` ${chalk18.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2326
+ console.log(` ${chalk19.dim(m.metric.padEnd(22))} ${round(m.quantity)}`);
2276
2327
  }
2277
2328
  }
2278
2329
  function round(n) {
@@ -2287,8 +2338,9 @@ function period(start, end) {
2287
2338
  }
2288
2339
 
2289
2340
  // src/index.ts
2341
+ installPromptCrashGuard();
2290
2342
  var program = new Command18();
2291
- program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.4.0");
2343
+ program.name("videan").description("Videan CLI \u2014 deploy apps in seconds").version("0.4.1");
2292
2344
  program.addCommand(loginCommand);
2293
2345
  program.addCommand(logoutCommand);
2294
2346
  program.addCommand(initCommand);