vibeview 0.19.0 → 0.20.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/cli.js CHANGED
@@ -8,13 +8,14 @@ import {
8
8
  fetchFinalSummary,
9
9
  formatDuration,
10
10
  killOwnedMetro,
11
+ loadDevConfig,
11
12
  newTransitionState,
12
13
  printOutcomeLine,
13
14
  printTransition,
14
15
  readDevState,
15
16
  readMetroState,
16
17
  runAgentVerb
17
- } from "./chunk-T6FS4ELD.js";
18
+ } from "./chunk-QZNZPKHV.js";
18
19
  import {
19
20
  ApiClient,
20
21
  EXIT_INFRA_ERROR,
@@ -27,7 +28,7 @@ import {
27
28
  resolveAuth,
28
29
  saveConfig,
29
30
  uploadApp
30
- } from "./chunk-FG2J2BGS.js";
31
+ } from "./chunk-MCCXPIGE.js";
31
32
 
32
33
  // src/cli.ts
33
34
  import { createRequire } from "module";
@@ -1395,6 +1396,47 @@ function isSubmitPlatform(p) {
1395
1396
  return SUBMIT_PLATFORMS.includes(p);
1396
1397
  }
1397
1398
  var PLAY_TRACKS = ["internal", "alpha", "beta", "production"];
1399
+ var RELEASE_STATUSES = ["completed", "draft", "inProgress"];
1400
+ function submitConfigFor(cwd, platform) {
1401
+ let cfg;
1402
+ try {
1403
+ cfg = loadDevConfig(cwd);
1404
+ } catch (err) {
1405
+ console.error(
1406
+ chalk12.yellow(
1407
+ `warning: vibeview.json could not be read \u2014 ignoring submit defaults (${err instanceof Error ? err.message : String(err)})`
1408
+ )
1409
+ );
1410
+ return {};
1411
+ }
1412
+ const raw = cfg?.platforms?.[platform]?.submit;
1413
+ if (raw === void 0) return {};
1414
+ const path = `vibeview.json: platforms.${platform}.submit`;
1415
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
1416
+ throw new UsageError(`${path} must be an object`);
1417
+ }
1418
+ const s = raw;
1419
+ for (const key of ["track", "releaseStatus", "ascKey"]) {
1420
+ if (s[key] !== void 0 && (typeof s[key] !== "string" || !s[key])) {
1421
+ throw new UsageError(`${path}.${key} must be a non-empty string`);
1422
+ }
1423
+ }
1424
+ if (s.rollout !== void 0 && typeof s.rollout !== "number") {
1425
+ throw new UsageError(`${path}.rollout must be a number strictly between 0 and 1`);
1426
+ }
1427
+ if (s.changesNotSentForReview !== void 0 && typeof s.changesNotSentForReview !== "boolean") {
1428
+ throw new UsageError(`${path}.changesNotSentForReview must be a boolean`);
1429
+ }
1430
+ return s;
1431
+ }
1432
+ function validateRolloutFraction(f, source, raw = f) {
1433
+ if (!Number.isFinite(f) || !(f > 0 && f < 1)) {
1434
+ throw new UsageError(
1435
+ `${source} must be a fraction strictly between 0 and 1 (e.g. 0.25) \u2014 got "${String(raw)}"`
1436
+ );
1437
+ }
1438
+ return f;
1439
+ }
1398
1440
  var SUBMISSION_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed"]);
1399
1441
  var MAX_CONSECUTIVE_POLL_FAILURES2 = 5;
1400
1442
  function emit(json, evt) {
@@ -1425,8 +1467,43 @@ async function submitCommand(opts, deps) {
1425
1467
  if (androidFamily && opts.ascKey !== void 0) {
1426
1468
  throw new UsageError(`--asc-key only applies to ios/tvos (TestFlight) \u2014 not ${platform}`);
1427
1469
  }
1428
- if (opts.track !== void 0 && !PLAY_TRACKS.includes(opts.track)) {
1429
- throw new UsageError(`--track must be one of ${PLAY_TRACKS.join(", ")} \u2014 got "${opts.track}"`);
1470
+ if (!androidFamily && opts.releaseStatus !== void 0) {
1471
+ throw new UsageError(
1472
+ `--release-status only applies to android/androidtv (Play) \u2014 not ${platform}`
1473
+ );
1474
+ }
1475
+ if (!androidFamily && opts.rollout !== void 0) {
1476
+ throw new UsageError(`--rollout only applies to android/androidtv (Play) \u2014 not ${platform}`);
1477
+ }
1478
+ const cfgDefaults = submitConfigFor(process.cwd(), platform);
1479
+ const track = opts.track ?? (androidFamily ? cfgDefaults.track : void 0);
1480
+ const releaseStatus = opts.releaseStatus ?? (androidFamily ? cfgDefaults.releaseStatus : void 0);
1481
+ const statusUnchanged = opts.releaseStatus === void 0 || opts.releaseStatus === cfgDefaults.releaseStatus;
1482
+ const rollout = opts.rollout !== void 0 ? validateRolloutFraction(Number(opts.rollout), "--rollout", opts.rollout) : androidFamily && statusUnchanged && cfgDefaults.rollout !== void 0 ? validateRolloutFraction(
1483
+ cfgDefaults.rollout,
1484
+ `vibeview.json platforms.${platform}.submit.rollout`
1485
+ ) : void 0;
1486
+ const changesNotSentForReview = androidFamily ? cfgDefaults.changesNotSentForReview : void 0;
1487
+ const ascKey2 = opts.ascKey ?? (!androidFamily ? cfgDefaults.ascKey : void 0);
1488
+ if (track !== void 0 && !PLAY_TRACKS.includes(track)) {
1489
+ const source = opts.track !== void 0 ? "--track" : `vibeview.json platforms.${platform}.submit.track`;
1490
+ throw new UsageError(`${source} must be one of ${PLAY_TRACKS.join(", ")} \u2014 got "${track}"`);
1491
+ }
1492
+ if (releaseStatus !== void 0 && !RELEASE_STATUSES.includes(releaseStatus)) {
1493
+ const source = opts.releaseStatus !== void 0 ? "--release-status" : `vibeview.json platforms.${platform}.submit.releaseStatus`;
1494
+ throw new UsageError(
1495
+ `${source} must be one of ${RELEASE_STATUSES.join(", ")} \u2014 got "${releaseStatus}"`
1496
+ );
1497
+ }
1498
+ if (rollout !== void 0 && releaseStatus !== "inProgress") {
1499
+ throw new UsageError(
1500
+ `a rollout fraction requires release status "inProgress" (a staged rollout) \u2014 got "${releaseStatus ?? "completed"}"`
1501
+ );
1502
+ }
1503
+ if (releaseStatus === "inProgress" && rollout === void 0) {
1504
+ throw new UsageError(
1505
+ `release status "inProgress" requires a rollout fraction \u2014 pass --rollout (e.g. --rollout 0.1)`
1506
+ );
1430
1507
  }
1431
1508
  const { token, baseUrl } = resolveAuth(opts);
1432
1509
  const client = new ApiClient(baseUrl, token);
@@ -1436,9 +1513,12 @@ async function submitCommand(opts, deps) {
1436
1513
  const sub = await d.createSubmission({
1437
1514
  build: buildId,
1438
1515
  platform,
1439
- ...opts.track !== void 0 ? { track: opts.track } : {},
1516
+ ...track !== void 0 ? { track } : {},
1440
1517
  ...opts.package !== void 0 ? { package: opts.package } : {},
1441
- ...opts.ascKey !== void 0 ? { asc_key: opts.ascKey } : {}
1518
+ ...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
1519
+ ...releaseStatus !== void 0 ? { releaseStatus } : {},
1520
+ ...rollout !== void 0 ? { rollout } : {},
1521
+ ...changesNotSentForReview !== void 0 ? { changesNotSentForReview } : {}
1442
1522
  });
1443
1523
  emit(opts.json, {
1444
1524
  event: "submission_created",
@@ -1455,6 +1535,11 @@ async function submitCommand(opts, deps) {
1455
1535
  console.error(` ${chalk12.cyan(sub.id)} ${chalk12.dim(`[${sub.platform}]`)}`);
1456
1536
  }
1457
1537
  const final = await followSubmission(sub, opts, d);
1538
+ if (!opts.json && final.status === "succeeded" && (final.release_status ?? releaseStatus) === "draft") {
1539
+ console.error(
1540
+ chalk12.dim("release status: draft \u2014 activate it from the Play Console when ready")
1541
+ );
1542
+ }
1458
1543
  process.exitCode = final.status === "succeeded" ? 0 : 1;
1459
1544
  } catch (err) {
1460
1545
  printError2(opts, err);
@@ -1562,9 +1647,90 @@ function printError2(opts, err) {
1562
1647
  console.error(chalk12.red(`error: ${message}`));
1563
1648
  }
1564
1649
 
1565
- // src/commands/whoami.ts
1650
+ // src/commands/versions.ts
1566
1651
  import chalk13 from "chalk";
1567
- function defaultDeps3(opts) {
1652
+ function defaultDeps3(client) {
1653
+ return {
1654
+ listAppVersions: client.listAppVersions.bind(client),
1655
+ setAppVersion: client.setAppVersion.bind(client)
1656
+ };
1657
+ }
1658
+ var VERSION_PLATFORMS = ["ios", "android", "tvos", "androidtv"];
1659
+ async function versionsList(opts, deps) {
1660
+ if (!opts.app) {
1661
+ throw new UsageError("--app <id> is required (see `vibeview list-apps` for ids)");
1662
+ }
1663
+ const { token, baseUrl } = resolveAuth(opts);
1664
+ const client = new ApiClient(baseUrl, token);
1665
+ const d = deps ?? defaultDeps3(client);
1666
+ try {
1667
+ const response = await d.listAppVersions(opts.app);
1668
+ if (opts.json) {
1669
+ console.log(JSON.stringify(response, null, 2));
1670
+ } else {
1671
+ process.stdout.write(formatVersionsHuman(response.versions));
1672
+ }
1673
+ process.exitCode = 0;
1674
+ } catch (err) {
1675
+ printError3(opts, err);
1676
+ process.exitCode = 1;
1677
+ }
1678
+ }
1679
+ async function versionsSet(buildNumber, opts, deps) {
1680
+ if (!opts.app) {
1681
+ throw new UsageError("--app <id> is required (see `vibeview list-apps` for ids)");
1682
+ }
1683
+ if (!opts.platform || !VERSION_PLATFORMS.includes(opts.platform)) {
1684
+ throw new UsageError(
1685
+ `--platform is required and must be one of ${VERSION_PLATFORMS.join(", ")}`
1686
+ );
1687
+ }
1688
+ const parsed = /^\d+$/.test(buildNumber) ? Number(buildNumber) : NaN;
1689
+ if (!Number.isSafeInteger(parsed) || parsed < 0) {
1690
+ throw new UsageError(`build number must be a non-negative integer \u2014 got "${buildNumber}"`);
1691
+ }
1692
+ const { token, baseUrl } = resolveAuth(opts);
1693
+ const client = new ApiClient(baseUrl, token);
1694
+ const d = deps ?? defaultDeps3(client);
1695
+ try {
1696
+ const version = await d.setAppVersion(opts.app, opts.platform, parsed);
1697
+ if (opts.json) {
1698
+ console.log(JSON.stringify(version));
1699
+ } else {
1700
+ console.log(
1701
+ chalk13.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk13.dim(
1702
+ " \u2014 the next autoIncrement production build gets " + (version.build_number + 1)
1703
+ )
1704
+ );
1705
+ }
1706
+ process.exitCode = 0;
1707
+ } catch (err) {
1708
+ printError3(opts, err);
1709
+ process.exitCode = 1;
1710
+ }
1711
+ }
1712
+ var HEADERS2 = ["PLATFORM", "BUILD NUMBER", "UPDATED"];
1713
+ function formatVersionsHuman(versions2) {
1714
+ if (versions2.length === 0) {
1715
+ return "No version counters yet \u2014 they appear after the first autoIncrement production build, or seed one with `vibeview versions set`.\n";
1716
+ }
1717
+ const rows = versions2.map((v) => [v.platform, String(v.build_number), v.updated_at ?? ""]);
1718
+ const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1719
+ const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
1720
+ const join3 = (cells) => cells.join(" ").trimEnd();
1721
+ const lines = [chalk13.bold(join3(padCells(HEADERS2)))];
1722
+ for (const row of rows) lines.push(join3(padCells(row)));
1723
+ return lines.join("\n") + "\n";
1724
+ }
1725
+ function printError3(opts, err) {
1726
+ const message = err instanceof Error ? err.message : String(err);
1727
+ if (opts.json) console.log(JSON.stringify({ event: "error", message }));
1728
+ console.error(chalk13.red(`error: ${message}`));
1729
+ }
1730
+
1731
+ // src/commands/whoami.ts
1732
+ import chalk14 from "chalk";
1733
+ function defaultDeps4(opts) {
1568
1734
  const { token, baseUrl } = resolveAuth(opts);
1569
1735
  const client = new ApiClient(baseUrl, token);
1570
1736
  return { fetchMe: () => client.whoAmI() };
@@ -1576,7 +1742,7 @@ function effectiveRole(role, tokenRole) {
1576
1742
  return (RANK[tokenRole] ?? 3) < (RANK[role] ?? 3) ? tokenRole : role;
1577
1743
  }
1578
1744
  async function whoAmI(opts, deps) {
1579
- const d = deps ?? defaultDeps3(opts);
1745
+ const d = deps ?? defaultDeps4(opts);
1580
1746
  try {
1581
1747
  const me = await d.fetchMe();
1582
1748
  const effective = effectiveRole(me.role, me.token_role);
@@ -1585,27 +1751,27 @@ async function whoAmI(opts, deps) {
1585
1751
  process.exitCode = 0;
1586
1752
  return;
1587
1753
  }
1588
- console.log(`${chalk13.bold("Logged in as")} ${me.email}`);
1754
+ console.log(`${chalk14.bold("Logged in as")} ${me.email}`);
1589
1755
  if (me.memberships.length === 1) {
1590
- console.log(`${chalk13.bold("Organization")} ${me.memberships[0].organization_name}`);
1756
+ console.log(`${chalk14.bold("Organization")} ${me.memberships[0].organization_name}`);
1591
1757
  } else if (me.memberships.length > 1) {
1592
- console.log(chalk13.bold("Organizations"));
1758
+ console.log(chalk14.bold("Organizations"));
1593
1759
  for (const m of me.memberships) {
1594
1760
  console.log(` ${m.organization_name} (${m.role})`);
1595
1761
  }
1596
1762
  }
1597
- console.log(`${chalk13.bold("Member role")} ${me.role ?? "\u2014"}`);
1598
- console.log(`${chalk13.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
1599
- console.log(`${chalk13.bold("Effective role")} ${effective ?? "\u2014"}`);
1763
+ console.log(`${chalk14.bold("Member role")} ${me.role ?? "\u2014"}`);
1764
+ console.log(`${chalk14.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
1765
+ console.log(`${chalk14.bold("Effective role")} ${effective ?? "\u2014"}`);
1600
1766
  if (me.is_superadmin) {
1601
- console.log(chalk13.dim("Superadmin (platform-wide; does not raise the org role above)"));
1767
+ console.log(chalk14.dim("Superadmin (platform-wide; does not raise the org role above)"));
1602
1768
  }
1603
1769
  if (me.role === "admin" && me.token_role && me.token_role !== "admin") {
1604
1770
  console.log(
1605
- chalk13.yellow(
1771
+ chalk14.yellow(
1606
1772
  `
1607
- Your account is admin, but this CLI token is ${me.token_role}. The effective role is the lower of the two, so admin-only commands (such as ${chalk13.bold("vibeview env set")}) will fail with a permissions error.
1608
- Create an admin API token in the dashboard and use ${chalk13.bold("--token")} or ${chalk13.bold("VIBEVIEW_API_TOKEN")}.`
1773
+ Your account is admin, but this CLI token is ${me.token_role}. The effective role is the lower of the two, so admin-only commands (such as ${chalk14.bold("vibeview env set")}) will fail with a permissions error.
1774
+ Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or ${chalk14.bold("VIBEVIEW_API_TOKEN")}.`
1609
1775
  )
1610
1776
  );
1611
1777
  }
@@ -1615,7 +1781,7 @@ Create an admin API token in the dashboard and use ${chalk13.bold("--token")} or
1615
1781
  if (opts.json) {
1616
1782
  console.log(JSON.stringify({ event: "error", message }));
1617
1783
  }
1618
- console.error(chalk13.red(`error: ${message}`));
1784
+ console.error(chalk14.red(`error: ${message}`));
1619
1785
  process.exitCode = 1;
1620
1786
  }
1621
1787
  }
@@ -1623,7 +1789,7 @@ Create an admin API token in the dashboard and use ${chalk13.bold("--token")} or
1623
1789
  // src/commands/env.ts
1624
1790
  import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
1625
1791
  import { basename } from "path";
1626
- import chalk14 from "chalk";
1792
+ import chalk15 from "chalk";
1627
1793
  import { password } from "@inquirer/prompts";
1628
1794
 
1629
1795
  // src/lib/gitignore.ts
@@ -1650,7 +1816,7 @@ async function writeSecureFile(path, content) {
1650
1816
  }
1651
1817
  await chmod(path, 384);
1652
1818
  }
1653
- function defaultDeps4(client) {
1819
+ function defaultDeps5(client) {
1654
1820
  return {
1655
1821
  listVars: (app) => client.listBuildEnvVars(app),
1656
1822
  setVar: (body) => client.setBuildEnvVar(body),
@@ -1679,11 +1845,11 @@ function defaultDeps4(client) {
1679
1845
  isIgnored: (path) => isGitIgnored(path),
1680
1846
  writeFile: writeSecureFile,
1681
1847
  readFile: (path) => readFileAsync(path),
1682
- warn: (message) => console.error(chalk14.yellow(`warning: ${message}`))
1848
+ warn: (message) => console.error(chalk15.yellow(`warning: ${message}`))
1683
1849
  };
1684
1850
  }
1685
1851
  async function envList(opts, deps) {
1686
- const d = deps ?? defaultDeps4(clientFor(opts));
1852
+ const d = deps ?? defaultDeps5(clientFor(opts));
1687
1853
  try {
1688
1854
  const { variables } = await d.listVars(opts.app);
1689
1855
  if (opts.json) {
@@ -1693,7 +1859,7 @@ async function envList(opts, deps) {
1693
1859
  }
1694
1860
  process.exitCode = 0;
1695
1861
  } catch (err) {
1696
- printError3(opts, err);
1862
+ printError4(opts, err);
1697
1863
  process.exitCode = 1;
1698
1864
  }
1699
1865
  }
@@ -1701,22 +1867,22 @@ function formatEnvVarsHuman(variables) {
1701
1867
  if (variables.length === 0) {
1702
1868
  return "No build variables found.\n";
1703
1869
  }
1704
- const HEADERS3 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
1870
+ const HEADERS4 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
1705
1871
  const rows = variables.map((v) => [
1706
1872
  v.name,
1707
1873
  v.is_file ? `${v.visibility} (file)` : v.visibility,
1708
1874
  v.app_public_id ?? "org-wide",
1709
1875
  v.value_hint ?? ""
1710
1876
  ]);
1711
- const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1877
+ const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1712
1878
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
1713
1879
  const join3 = (cells) => cells.join(" ").trimEnd();
1714
- const lines = [chalk14.bold(join3(padCells(HEADERS3)))];
1880
+ const lines = [chalk15.bold(join3(padCells(HEADERS4)))];
1715
1881
  for (const row of rows) lines.push(join3(padCells(row)));
1716
1882
  return lines.join("\n") + "\n";
1717
1883
  }
1718
1884
  async function envSet(name, value, opts, deps) {
1719
- const d = deps ?? defaultDeps4(clientFor(opts));
1885
+ const d = deps ?? defaultDeps5(clientFor(opts));
1720
1886
  let resolvedValue = value;
1721
1887
  if (resolvedValue === void 0 && !opts.file) {
1722
1888
  if (opts.json || !d.isTTY) {
@@ -1742,7 +1908,7 @@ async function envSet(name, value, opts, deps) {
1742
1908
  printSetResult(opts, result);
1743
1909
  process.exitCode = 0;
1744
1910
  } catch (err) {
1745
- printError3(opts, err);
1911
+ printError4(opts, err);
1746
1912
  process.exitCode = 1;
1747
1913
  }
1748
1914
  }
@@ -1761,26 +1927,26 @@ function printSetResult(opts, result) {
1761
1927
  if (opts.json) {
1762
1928
  console.log(JSON.stringify(result));
1763
1929
  } else {
1764
- console.log(chalk14.green(`\u2713 set ${result.name} (${result.visibility})`));
1930
+ console.log(chalk15.green(`\u2713 set ${result.name} (${result.visibility})`));
1765
1931
  }
1766
1932
  }
1767
1933
  async function envRm(name, opts, deps) {
1768
- const d = deps ?? defaultDeps4(clientFor(opts));
1934
+ const d = deps ?? defaultDeps5(clientFor(opts));
1769
1935
  try {
1770
1936
  await d.rmVar({ name, app: opts.app });
1771
1937
  if (opts.json) {
1772
1938
  console.log(JSON.stringify({ event: "deleted", name }));
1773
1939
  } else {
1774
- console.log(chalk14.green(`\u2713 removed ${name}`));
1940
+ console.log(chalk15.green(`\u2713 removed ${name}`));
1775
1941
  }
1776
1942
  process.exitCode = 0;
1777
1943
  } catch (err) {
1778
- printError3(opts, err);
1944
+ printError4(opts, err);
1779
1945
  process.exitCode = 1;
1780
1946
  }
1781
1947
  }
1782
1948
  async function envPull(opts, deps) {
1783
- const d = deps ?? defaultDeps4(clientFor(opts));
1949
+ const d = deps ?? defaultDeps5(clientFor(opts));
1784
1950
  const outPath = opts.out ?? ".env";
1785
1951
  try {
1786
1952
  const { variables } = await d.pullVars(opts.app);
@@ -1803,11 +1969,11 @@ async function envPull(opts, deps) {
1803
1969
  if (opts.json) {
1804
1970
  console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
1805
1971
  } else {
1806
- console.log(chalk14.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
1972
+ console.log(chalk15.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
1807
1973
  }
1808
1974
  process.exitCode = 0;
1809
1975
  } catch (err) {
1810
- printError3(opts, err);
1976
+ printError4(opts, err);
1811
1977
  process.exitCode = 1;
1812
1978
  }
1813
1979
  }
@@ -1839,10 +2005,10 @@ function escapeEnvValue(value) {
1839
2005
  }
1840
2006
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
1841
2007
  }
1842
- function printError3(opts, err) {
2008
+ function printError4(opts, err) {
1843
2009
  const message = err instanceof Error ? err.message : String(err);
1844
2010
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
1845
- console.error(chalk14.red(`error: ${message}`));
2011
+ console.error(chalk15.red(`error: ${message}`));
1846
2012
  }
1847
2013
 
1848
2014
  // src/commands/credentials.ts
@@ -1854,13 +2020,13 @@ import {
1854
2020
  } from "fs/promises";
1855
2021
  import { mkdirSync } from "fs";
1856
2022
  import { basename as basename2, extname, join } from "path";
1857
- import chalk15 from "chalk";
2023
+ import chalk16 from "chalk";
1858
2024
  import { input, password as password2 } from "@inquirer/prompts";
1859
2025
  function clientFor2(opts) {
1860
2026
  const { token, baseUrl } = resolveAuth(opts);
1861
2027
  return new ApiClient(baseUrl, token);
1862
2028
  }
1863
- function defaultDeps5(client) {
2029
+ function defaultDeps6(client) {
1864
2030
  return {
1865
2031
  addCredential: (body) => client.addSigningCredential({
1866
2032
  platform: body.platform,
@@ -1927,7 +2093,7 @@ function defaultDeps5(client) {
1927
2093
  mkdir: (dir) => mkdirSync(dir, { recursive: true }),
1928
2094
  writeFile: (path, data, mode) => writeFileAsync(path, data, mode !== void 0 ? { mode } : void 0),
1929
2095
  chmod: (path, mode) => chmodAsync(path, mode),
1930
- warn: (message) => console.error(chalk15.yellow(`warning: ${message}`))
2096
+ warn: (message) => console.error(chalk16.yellow(`warning: ${message}`))
1931
2097
  };
1932
2098
  }
1933
2099
  var ANDROID_KIND = "android_keystore";
@@ -1937,7 +2103,7 @@ function isCredentialPlatform(p) {
1937
2103
  return CREDENTIAL_PLATFORMS.includes(p);
1938
2104
  }
1939
2105
  async function credentialsAdd(opts, deps) {
1940
- const d = deps ?? defaultDeps5(clientFor2(opts));
2106
+ const d = deps ?? defaultDeps6(clientFor2(opts));
1941
2107
  if (!opts.platform || !isCredentialPlatform(opts.platform)) {
1942
2108
  throw new UsageError('--platform is required and must be "ios", "android", or "tvos"');
1943
2109
  }
@@ -1996,16 +2162,16 @@ async function credentialsAdd(opts, deps) {
1996
2162
  if (opts.json) {
1997
2163
  console.log(JSON.stringify(result));
1998
2164
  } else {
1999
- console.log(chalk15.green(`\u2713 added ${result.name} (${result.platform})`));
2165
+ console.log(chalk16.green(`\u2713 added ${result.name} (${result.platform})`));
2000
2166
  }
2001
2167
  process.exitCode = 0;
2002
2168
  } catch (err) {
2003
- printError4(opts, err);
2169
+ printError5(opts, err);
2004
2170
  process.exitCode = 1;
2005
2171
  }
2006
2172
  }
2007
2173
  async function credentialsGenerate(opts, deps) {
2008
- const d = deps ?? defaultDeps5(clientFor2(opts));
2174
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2009
2175
  if (!opts.platform) {
2010
2176
  throw new UsageError('--platform is required and must be "android", "ios", or "tvos"');
2011
2177
  }
@@ -2054,27 +2220,27 @@ async function credentialsGenerate(opts, deps) {
2054
2220
  }
2055
2221
  if (opts.json) {
2056
2222
  } else {
2057
- console.log(chalk15.green(`\u2713 generated ${result.name} (android)`));
2223
+ console.log(chalk16.green(`\u2713 generated ${result.name} (android)`));
2058
2224
  console.log("");
2059
- console.log(` Keystore saved to: ${chalk15.bold(savedTo)}`);
2060
- console.log(` Key alias: ${chalk15.bold(result.key_alias)}`);
2061
- console.log(` Store password: ${chalk15.bold(result.store_password)}`);
2062
- console.log(` Key password: ${chalk15.bold(result.key_password)}`);
2225
+ console.log(` Keystore saved to: ${chalk16.bold(savedTo)}`);
2226
+ console.log(` Key alias: ${chalk16.bold(result.key_alias)}`);
2227
+ console.log(` Store password: ${chalk16.bold(result.store_password)}`);
2228
+ console.log(` Key password: ${chalk16.bold(result.key_password)}`);
2063
2229
  console.log("");
2064
2230
  console.log(
2065
- chalk15.yellow(
2231
+ chalk16.yellow(
2066
2232
  " These are shown ONCE \u2014 VibeView cannot recover the keystore file or its passwords. Back them up somewhere safe."
2067
2233
  )
2068
2234
  );
2069
2235
  console.log(
2070
- chalk15.yellow(
2236
+ chalk16.yellow(
2071
2237
  " The credential is already uploaded and active for android and androidtv production builds."
2072
2238
  )
2073
2239
  );
2074
2240
  }
2075
2241
  process.exitCode = 0;
2076
2242
  } catch (err) {
2077
- printError4(opts, err);
2243
+ printError5(opts, err);
2078
2244
  process.exitCode = 1;
2079
2245
  }
2080
2246
  }
@@ -2093,29 +2259,29 @@ async function generateAppleCredential(platform, opts, d) {
2093
2259
  if (opts.json) {
2094
2260
  console.log(JSON.stringify(result));
2095
2261
  } else {
2096
- console.log(chalk15.green(`\u2713 generated ${result.name} (${result.platform})`));
2262
+ console.log(chalk16.green(`\u2713 generated ${result.name} (${result.platform})`));
2097
2263
  console.log("");
2098
- console.log(` Scope: ${chalk15.bold(result.app_public_id ?? "org-wide")}`);
2264
+ console.log(` Scope: ${chalk16.bold(result.app_public_id ?? "org-wide")}`);
2099
2265
  const expires = formatExpires(result);
2100
2266
  if (expires) {
2101
- console.log(` Expires: ${chalk15.bold(expires)}`);
2267
+ console.log(` Expires: ${chalk16.bold(expires)}`);
2102
2268
  }
2103
2269
  console.log("");
2104
2270
  console.log(
2105
2271
  ` The certificate and provisioning profile are stored with VibeView and already active for ${result.platform} production builds.`
2106
2272
  );
2107
2273
  console.log(
2108
- ` Run ${chalk15.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
2274
+ ` Run ${chalk16.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
2109
2275
  );
2110
2276
  }
2111
2277
  process.exitCode = 0;
2112
2278
  } catch (err) {
2113
- printError4(opts, err);
2279
+ printError5(opts, err);
2114
2280
  process.exitCode = 1;
2115
2281
  }
2116
2282
  }
2117
2283
  async function credentialsDownload(name, opts, deps) {
2118
- const d = deps ?? defaultDeps5(clientFor2(opts));
2284
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2119
2285
  const outDir = opts.out ?? process.cwd();
2120
2286
  try {
2121
2287
  const { credentials: credentials2 } = await d.listCredentials(opts.app);
@@ -2171,26 +2337,26 @@ async function credentialsDownload(name, opts, deps) {
2171
2337
  await d.chmod(path, 384);
2172
2338
  }
2173
2339
  if (!opts.json) {
2174
- console.log(chalk15.green(`\u2713 downloaded ${material.name} (${material.platform})`));
2340
+ console.log(chalk16.green(`\u2713 downloaded ${material.name} (${material.platform})`));
2175
2341
  console.log("");
2176
- console.log(` Saved to: ${chalk15.bold(savedTo)}`);
2342
+ console.log(` Saved to: ${chalk16.bold(savedTo)}`);
2177
2343
  if (profileSavedTo !== null) {
2178
- console.log(` Profile: ${chalk15.bold(profileSavedTo)}`);
2344
+ console.log(` Profile: ${chalk16.bold(profileSavedTo)}`);
2179
2345
  }
2180
2346
  for (const [key, value] of Object.entries(material.passwords)) {
2181
- console.log(` ${key}: ${chalk15.bold(value)}`);
2347
+ console.log(` ${key}: ${chalk16.bold(value)}`);
2182
2348
  }
2183
2349
  console.log("");
2184
- console.log(chalk15.yellow(" This download is recorded in your organization's audit log."));
2350
+ console.log(chalk16.yellow(" This download is recorded in your organization's audit log."));
2185
2351
  }
2186
2352
  process.exitCode = 0;
2187
2353
  } catch (err) {
2188
- printError4(opts, err);
2354
+ printError5(opts, err);
2189
2355
  process.exitCode = 1;
2190
2356
  }
2191
2357
  }
2192
2358
  async function ascKeyAdd(opts, deps) {
2193
- const d = deps ?? defaultDeps5(clientFor2(opts));
2359
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2194
2360
  if (!opts.keyId) {
2195
2361
  throw new UsageError("--key-id is required (the 10-character App Store Connect key id)");
2196
2362
  }
@@ -2212,25 +2378,25 @@ async function ascKeyAdd(opts, deps) {
2212
2378
  if (opts.json) {
2213
2379
  console.log(JSON.stringify(result));
2214
2380
  } else {
2215
- console.log(chalk15.green(`\u2713 added App Store Connect key ${result.name}`));
2381
+ console.log(chalk16.green(`\u2713 added App Store Connect key ${result.name}`));
2216
2382
  console.log("");
2217
- console.log(` Key ID: ${chalk15.bold(result.key_id)}`);
2218
- console.log(` Issuer ID: ${chalk15.bold(result.issuer_id)}`);
2383
+ console.log(` Key ID: ${chalk16.bold(result.key_id)}`);
2384
+ console.log(` Issuer ID: ${chalk16.bold(result.issuer_id)}`);
2219
2385
  console.log("");
2220
2386
  console.log(
2221
- chalk15.yellow(
2387
+ chalk16.yellow(
2222
2388
  " The .p8 is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
2223
2389
  )
2224
2390
  );
2225
2391
  }
2226
2392
  process.exitCode = 0;
2227
2393
  } catch (err) {
2228
- printError4(opts, err);
2394
+ printError5(opts, err);
2229
2395
  process.exitCode = 1;
2230
2396
  }
2231
2397
  }
2232
2398
  async function ascKeyList(opts, deps) {
2233
- const d = deps ?? defaultDeps5(clientFor2(opts));
2399
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2234
2400
  try {
2235
2401
  const { keys } = await d.listAscKeys();
2236
2402
  if (opts.json) {
@@ -2240,7 +2406,7 @@ async function ascKeyList(opts, deps) {
2240
2406
  }
2241
2407
  process.exitCode = 0;
2242
2408
  } catch (err) {
2243
- printError4(opts, err);
2409
+ printError5(opts, err);
2244
2410
  process.exitCode = 1;
2245
2411
  }
2246
2412
  }
@@ -2248,32 +2414,32 @@ function formatAscKeysHuman(keys) {
2248
2414
  if (keys.length === 0) {
2249
2415
  return "No App Store Connect keys found.\n";
2250
2416
  }
2251
- const HEADERS3 = ["NAME", "KEY ID", "ISSUER ID", "CREATED"];
2417
+ const HEADERS4 = ["NAME", "KEY ID", "ISSUER ID", "CREATED"];
2252
2418
  const rows = keys.map((k) => [k.name, k.key_id, k.issuer_id, k.created_at ?? ""]);
2253
- const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2419
+ const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2254
2420
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2255
2421
  const join3 = (cells) => cells.join(" ").trimEnd();
2256
- const lines = [chalk15.bold(join3(padCells(HEADERS3)))];
2422
+ const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2257
2423
  for (const row of rows) lines.push(join3(padCells(row)));
2258
2424
  return lines.join("\n") + "\n";
2259
2425
  }
2260
2426
  async function ascKeyRm(name, opts, deps) {
2261
- const d = deps ?? defaultDeps5(clientFor2(opts));
2427
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2262
2428
  try {
2263
2429
  await d.deleteAscKey(name);
2264
2430
  if (opts.json) {
2265
2431
  console.log(JSON.stringify({ event: "deleted", name }));
2266
2432
  } else {
2267
- console.log(chalk15.green(`\u2713 removed App Store Connect key ${name}`));
2433
+ console.log(chalk16.green(`\u2713 removed App Store Connect key ${name}`));
2268
2434
  }
2269
2435
  process.exitCode = 0;
2270
2436
  } catch (err) {
2271
- printError4(opts, err);
2437
+ printError5(opts, err);
2272
2438
  process.exitCode = 1;
2273
2439
  }
2274
2440
  }
2275
2441
  async function playKeyAdd(opts, deps) {
2276
- const d = deps ?? defaultDeps5(clientFor2(opts));
2442
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2277
2443
  if (!opts.file) {
2278
2444
  throw new UsageError(
2279
2445
  "--file is required (the service-account JSON from Google Play Console \u2192 API access)"
@@ -2289,24 +2455,24 @@ async function playKeyAdd(opts, deps) {
2289
2455
  if (opts.json) {
2290
2456
  console.log(JSON.stringify(result));
2291
2457
  } else {
2292
- console.log(chalk15.green(`\u2713 added Google Play key ${result.name}`));
2458
+ console.log(chalk16.green(`\u2713 added Google Play key ${result.name}`));
2293
2459
  console.log("");
2294
- console.log(` Service account: ${chalk15.bold(result.client_email)}`);
2460
+ console.log(` Service account: ${chalk16.bold(result.client_email)}`);
2295
2461
  console.log("");
2296
2462
  console.log(
2297
- chalk15.yellow(
2463
+ chalk16.yellow(
2298
2464
  " The JSON key is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
2299
2465
  )
2300
2466
  );
2301
2467
  }
2302
2468
  process.exitCode = 0;
2303
2469
  } catch (err) {
2304
- printError4(opts, err);
2470
+ printError5(opts, err);
2305
2471
  process.exitCode = 1;
2306
2472
  }
2307
2473
  }
2308
2474
  async function playKeyList(opts, deps) {
2309
- const d = deps ?? defaultDeps5(clientFor2(opts));
2475
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2310
2476
  try {
2311
2477
  const { keys } = await d.listPlayKeys();
2312
2478
  if (opts.json) {
@@ -2316,7 +2482,7 @@ async function playKeyList(opts, deps) {
2316
2482
  }
2317
2483
  process.exitCode = 0;
2318
2484
  } catch (err) {
2319
- printError4(opts, err);
2485
+ printError5(opts, err);
2320
2486
  process.exitCode = 1;
2321
2487
  }
2322
2488
  }
@@ -2324,32 +2490,32 @@ function formatPlayKeysHuman(keys) {
2324
2490
  if (keys.length === 0) {
2325
2491
  return "No Google Play keys found.\n";
2326
2492
  }
2327
- const HEADERS3 = ["NAME", "CLIENT EMAIL", "CREATED"];
2493
+ const HEADERS4 = ["NAME", "CLIENT EMAIL", "CREATED"];
2328
2494
  const rows = keys.map((k) => [k.name, k.client_email, k.created_at ?? ""]);
2329
- const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2495
+ const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2330
2496
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2331
2497
  const join3 = (cells) => cells.join(" ").trimEnd();
2332
- const lines = [chalk15.bold(join3(padCells(HEADERS3)))];
2498
+ const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2333
2499
  for (const row of rows) lines.push(join3(padCells(row)));
2334
2500
  return lines.join("\n") + "\n";
2335
2501
  }
2336
2502
  async function playKeyRm(name, opts, deps) {
2337
- const d = deps ?? defaultDeps5(clientFor2(opts));
2503
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2338
2504
  try {
2339
2505
  await d.deletePlayKey(name);
2340
2506
  if (opts.json) {
2341
2507
  console.log(JSON.stringify({ event: "deleted", name }));
2342
2508
  } else {
2343
- console.log(chalk15.green(`\u2713 removed Google Play key ${name}`));
2509
+ console.log(chalk16.green(`\u2713 removed Google Play key ${name}`));
2344
2510
  }
2345
2511
  process.exitCode = 0;
2346
2512
  } catch (err) {
2347
- printError4(opts, err);
2513
+ printError5(opts, err);
2348
2514
  process.exitCode = 1;
2349
2515
  }
2350
2516
  }
2351
2517
  async function credentialsList(opts, deps) {
2352
- const d = deps ?? defaultDeps5(clientFor2(opts));
2518
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2353
2519
  try {
2354
2520
  const { credentials: credentials2 } = await d.listCredentials(opts.app);
2355
2521
  for (const c of credentials2) {
@@ -2365,7 +2531,7 @@ async function credentialsList(opts, deps) {
2365
2531
  }
2366
2532
  process.exitCode = 0;
2367
2533
  } catch (err) {
2368
- printError4(opts, err);
2534
+ printError5(opts, err);
2369
2535
  process.exitCode = 1;
2370
2536
  }
2371
2537
  }
@@ -2398,7 +2564,7 @@ function formatCredentialsHuman(credentials2) {
2398
2564
  if (credentials2.length === 0) {
2399
2565
  return "No signing credentials found.\n";
2400
2566
  }
2401
- const HEADERS3 = ["NAME", "PLATFORM", "SCOPE", "IDENTITY", "EXPIRES"];
2567
+ const HEADERS4 = ["NAME", "PLATFORM", "SCOPE", "IDENTITY", "EXPIRES"];
2402
2568
  const rows = credentials2.map((c) => [
2403
2569
  c.name,
2404
2570
  c.platform,
@@ -2406,25 +2572,25 @@ function formatCredentialsHuman(credentials2) {
2406
2572
  formatIdentity(c),
2407
2573
  formatExpires(c)
2408
2574
  ]);
2409
- const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2575
+ const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2410
2576
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2411
2577
  const join3 = (cells) => cells.join(" ").trimEnd();
2412
- const lines = [chalk15.bold(join3(padCells(HEADERS3)))];
2578
+ const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2413
2579
  for (const row of rows) lines.push(join3(padCells(row)));
2414
2580
  return lines.join("\n") + "\n";
2415
2581
  }
2416
2582
  async function credentialsRm(name, opts, deps) {
2417
- const d = deps ?? defaultDeps5(clientFor2(opts));
2583
+ const d = deps ?? defaultDeps6(clientFor2(opts));
2418
2584
  try {
2419
2585
  await d.deleteCredential({ name, app: opts.app });
2420
2586
  if (opts.json) {
2421
2587
  console.log(JSON.stringify({ event: "deleted", name }));
2422
2588
  } else {
2423
- console.log(chalk15.green(`\u2713 removed ${name}`));
2589
+ console.log(chalk16.green(`\u2713 removed ${name}`));
2424
2590
  }
2425
2591
  process.exitCode = 0;
2426
2592
  } catch (err) {
2427
- printError4(opts, err);
2593
+ printError5(opts, err);
2428
2594
  process.exitCode = 1;
2429
2595
  }
2430
2596
  }
@@ -2432,16 +2598,16 @@ function printKeystoreWriteFailure(opts, err, result) {
2432
2598
  const message = err instanceof Error ? err.message : String(err);
2433
2599
  if (opts.json) {
2434
2600
  console.log(JSON.stringify({ event: "error", message }));
2435
- console.error(chalk15.red(`error: ${message}`));
2601
+ console.error(chalk16.red(`error: ${message}`));
2436
2602
  return;
2437
2603
  }
2438
- console.error(chalk15.red(`error: ${message}`));
2604
+ console.error(chalk16.red(`error: ${message}`));
2439
2605
  console.error(
2440
- chalk15.red(
2606
+ chalk16.red(
2441
2607
  `The keystore could NOT be saved locally, but the credential "${result.name}" WAS created server-side and its key material can never be re-fetched.`
2442
2608
  )
2443
2609
  );
2444
- console.error(chalk15.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
2610
+ console.error(chalk16.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
2445
2611
  console.error("");
2446
2612
  console.error(` Key alias: ${result.key_alias}`);
2447
2613
  console.error(` Store password: ${result.store_password}`);
@@ -2452,19 +2618,19 @@ function printKeystoreWriteFailure(opts, err, result) {
2452
2618
  ` Restore the file with: echo '${result.keystore_base64}' | base64 -d > ${result.name}.keystore`
2453
2619
  );
2454
2620
  }
2455
- function printError4(opts, err) {
2621
+ function printError5(opts, err) {
2456
2622
  const message = err instanceof Error ? err.message : String(err);
2457
2623
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
2458
- console.error(chalk15.red(`error: ${message}`));
2624
+ console.error(chalk16.red(`error: ${message}`));
2459
2625
  }
2460
2626
 
2461
2627
  // src/commands/webhooks.ts
2462
- import chalk16 from "chalk";
2628
+ import chalk17 from "chalk";
2463
2629
  function clientFor3(opts) {
2464
2630
  const { token, baseUrl } = resolveAuth(opts);
2465
2631
  return new ApiClient(baseUrl, token);
2466
2632
  }
2467
- function defaultDeps6(client) {
2633
+ function defaultDeps7(client) {
2468
2634
  return {
2469
2635
  addWebhook: (body) => client.addWebhook(body),
2470
2636
  listWebhooks: () => client.listWebhooks(),
@@ -2481,7 +2647,7 @@ async function webhooksAdd(opts, deps) {
2481
2647
  if (!opts.url.startsWith("https://")) {
2482
2648
  throw new UsageError(`--url must start with https:// (got "${opts.url}")`);
2483
2649
  }
2484
- const d = deps ?? defaultDeps6(clientFor3(opts));
2650
+ const d = deps ?? defaultDeps7(clientFor3(opts));
2485
2651
  try {
2486
2652
  const result = await d.addWebhook({
2487
2653
  url: opts.url,
@@ -2490,19 +2656,19 @@ async function webhooksAdd(opts, deps) {
2490
2656
  if (opts.json) {
2491
2657
  console.log(JSON.stringify(result));
2492
2658
  } else {
2493
- console.log(chalk16.green(`\u2713 added ${result.id} ${result.url}`));
2659
+ console.log(chalk17.green(`\u2713 added ${result.id} ${result.url}`));
2494
2660
  console.log("");
2495
- console.log(` Signing secret: ${chalk16.bold(result.secret)}`);
2661
+ console.log(` Signing secret: ${chalk17.bold(result.secret)}`);
2496
2662
  if (opts.secret !== void 0) {
2497
- console.log(chalk16.yellow(" (This is the secret you provided.)"));
2663
+ console.log(chalk17.yellow(" (This is the secret you provided.)"));
2498
2664
  }
2499
2665
  console.log(
2500
- chalk16.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
2666
+ chalk17.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
2501
2667
  );
2502
2668
  }
2503
2669
  process.exitCode = 0;
2504
2670
  } catch (err) {
2505
- printError5(opts, err);
2671
+ printError6(opts, err);
2506
2672
  process.exitCode = 1;
2507
2673
  }
2508
2674
  }
@@ -2517,7 +2683,7 @@ function formatWebhooksHuman(webhooks2) {
2517
2683
  if (webhooks2.length === 0) {
2518
2684
  return "No webhooks found.\n";
2519
2685
  }
2520
- const HEADERS3 = ["ID", "URL", "ACTIVE", "LAST DELIVERY", "CREATED"];
2686
+ const HEADERS4 = ["ID", "URL", "ACTIVE", "LAST DELIVERY", "CREATED"];
2521
2687
  const rows = webhooks2.map((w) => [
2522
2688
  w.id,
2523
2689
  w.url,
@@ -2525,15 +2691,15 @@ function formatWebhooksHuman(webhooks2) {
2525
2691
  formatLastDelivery(w.last_delivery),
2526
2692
  w.created_at
2527
2693
  ]);
2528
- const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2694
+ const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2529
2695
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2530
2696
  const join3 = (cells) => cells.join(" ").trimEnd();
2531
- const lines = [chalk16.bold(join3(padCells(HEADERS3)))];
2697
+ const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
2532
2698
  for (const row of rows) lines.push(join3(padCells(row)));
2533
2699
  return lines.join("\n") + "\n";
2534
2700
  }
2535
2701
  async function webhooksList(opts, deps) {
2536
- const d = deps ?? defaultDeps6(clientFor3(opts));
2702
+ const d = deps ?? defaultDeps7(clientFor3(opts));
2537
2703
  try {
2538
2704
  const { webhooks: webhooks2 } = await d.listWebhooks();
2539
2705
  if (opts.json) {
@@ -2543,22 +2709,22 @@ async function webhooksList(opts, deps) {
2543
2709
  }
2544
2710
  process.exitCode = 0;
2545
2711
  } catch (err) {
2546
- printError5(opts, err);
2712
+ printError6(opts, err);
2547
2713
  process.exitCode = 1;
2548
2714
  }
2549
2715
  }
2550
2716
  async function webhooksRm(id, opts, deps) {
2551
- const d = deps ?? defaultDeps6(clientFor3(opts));
2717
+ const d = deps ?? defaultDeps7(clientFor3(opts));
2552
2718
  try {
2553
2719
  await d.deleteWebhook(id);
2554
2720
  if (opts.json) {
2555
2721
  console.log(JSON.stringify({ event: "deleted", id }));
2556
2722
  } else {
2557
- console.log(chalk16.green(`\u2713 removed ${id}`));
2723
+ console.log(chalk17.green(`\u2713 removed ${id}`));
2558
2724
  }
2559
2725
  process.exitCode = 0;
2560
2726
  } catch (err) {
2561
- printError5(opts, err);
2727
+ printError6(opts, err);
2562
2728
  process.exitCode = 1;
2563
2729
  }
2564
2730
  }
@@ -2575,7 +2741,7 @@ function parseUtcTimestamp(iso) {
2575
2741
  var TEST_POLL_INTERVAL_MS = 3e3;
2576
2742
  var TEST_POLL_BUDGET_MS = 6e4;
2577
2743
  async function webhooksTest(id, opts, deps) {
2578
- const d = deps ?? defaultDeps6(clientFor3(opts));
2744
+ const d = deps ?? defaultDeps7(clientFor3(opts));
2579
2745
  try {
2580
2746
  const startedAt = d.now();
2581
2747
  await d.testWebhook(id);
@@ -2587,13 +2753,13 @@ async function webhooksTest(id, opts, deps) {
2587
2753
  if (opts.json) console.log(JSON.stringify(ld));
2588
2754
  if (ld.status === "succeeded") {
2589
2755
  if (!opts.json) {
2590
- console.log(chalk16.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
2756
+ console.log(chalk17.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
2591
2757
  }
2592
2758
  process.exitCode = 0;
2593
2759
  } else {
2594
2760
  if (!opts.json) {
2595
2761
  const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
2596
- console.error(chalk16.red(`\u2717 Failed (${code}) at ${ld.at}`));
2762
+ console.error(chalk17.red(`\u2717 Failed (${code}) at ${ld.at}`));
2597
2763
  }
2598
2764
  process.exitCode = 1;
2599
2765
  }
@@ -2602,25 +2768,25 @@ async function webhooksTest(id, opts, deps) {
2602
2768
  }
2603
2769
  if (opts.json) console.log(JSON.stringify({ status: "pending" }));
2604
2770
  console.error(
2605
- chalk16.yellow(
2771
+ chalk17.yellow(
2606
2772
  `Delivery still pending after ${TEST_POLL_BUDGET_MS / 1e3}s \u2014 check \`vibeview webhooks list\` later for the result.`
2607
2773
  )
2608
2774
  );
2609
2775
  process.exitCode = 1;
2610
2776
  } catch (err) {
2611
- printError5(opts, err);
2777
+ printError6(opts, err);
2612
2778
  process.exitCode = 1;
2613
2779
  }
2614
2780
  }
2615
- function printError5(opts, err) {
2781
+ function printError6(opts, err) {
2616
2782
  const message = err instanceof Error ? err.message : String(err);
2617
2783
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
2618
- console.error(chalk16.red(`error: ${message}`));
2784
+ console.error(chalk17.red(`error: ${message}`));
2619
2785
  }
2620
2786
 
2621
2787
  // src/lib/formatters/devices.ts
2622
- import chalk17 from "chalk";
2623
- var HEADERS2 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
2788
+ import chalk18 from "chalk";
2789
+ var HEADERS3 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
2624
2790
  function formatDevicesHuman(devices) {
2625
2791
  if (devices.length === 0) {
2626
2792
  return "No devices registered.\n";
@@ -2633,11 +2799,11 @@ function formatDevicesHuman(devices) {
2633
2799
  d.os_version,
2634
2800
  d.status
2635
2801
  ]);
2636
- const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2802
+ const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2637
2803
  const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
2638
- const lines = [chalk17.bold(fmt(HEADERS2))];
2804
+ const lines = [chalk18.bold(fmt(HEADERS3))];
2639
2805
  for (const row of rows.sort(rowSort)) {
2640
- const statusColored = row[5] === "available" ? chalk17.green(row[5]) : row[5] === "busy" ? chalk17.yellow(row[5]) : chalk17.dim(row[5]);
2806
+ const statusColored = row[5] === "available" ? chalk18.green(row[5]) : row[5] === "busy" ? chalk18.yellow(row[5]) : chalk18.dim(row[5]);
2641
2807
  lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
2642
2808
  }
2643
2809
  return lines.join("\n") + "\n";
@@ -2727,7 +2893,7 @@ import { dirname, join as join2 } from "path";
2727
2893
  import { fileURLToPath } from "url";
2728
2894
  import { spawnSync } from "child_process";
2729
2895
  import { confirm } from "@inquirer/prompts";
2730
- import chalk18 from "chalk";
2896
+ import chalk19 from "chalk";
2731
2897
  function resolveShippedSkillPath(fromDir) {
2732
2898
  for (const rel of ["..", join2("..", "..")]) {
2733
2899
  const candidate = join2(fromDir, rel, "skills", "vibeview-agent", "SKILL.md");
@@ -2755,23 +2921,23 @@ function registerMcp() {
2755
2921
  encoding: "utf8"
2756
2922
  });
2757
2923
  if (res.status === 0) {
2758
- console.log(chalk18.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
2924
+ console.log(chalk19.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
2759
2925
  return;
2760
2926
  }
2761
2927
  const out = `${res.stderr ?? ""}${res.stdout ?? ""}`;
2762
2928
  if (out.includes("already exists")) {
2763
- console.log(chalk18.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
2929
+ console.log(chalk19.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
2764
2930
  return;
2765
2931
  }
2766
- console.error(chalk18.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
2767
- console.error(chalk18.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
2932
+ console.error(chalk19.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
2933
+ console.error(chalk19.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
2768
2934
  }
2769
2935
  async function agentSetup(opts) {
2770
2936
  const moduleDir = dirname(fileURLToPath(import.meta.url));
2771
2937
  const shipped = resolveShippedSkillPath(moduleDir);
2772
2938
  if (!shipped) {
2773
2939
  console.error(
2774
- chalk18.red(
2940
+ chalk19.red(
2775
2941
  "error: could not find the packaged skill (skills/vibeview-agent/SKILL.md) \u2014 is this a complete install of the vibeview package?"
2776
2942
  )
2777
2943
  );
@@ -2780,9 +2946,9 @@ async function agentSetup(opts) {
2780
2946
  }
2781
2947
  const targetDir = skillInstallDir(opts.project ?? false, process.cwd(), homedir());
2782
2948
  const result = installSkill(shipped, targetDir);
2783
- console.log(chalk18.green(`\u2713 vibeview-agent skill ${result}: ${join2(targetDir, "SKILL.md")}`));
2949
+ console.log(chalk19.green(`\u2713 vibeview-agent skill ${result}: ${join2(targetDir, "SKILL.md")}`));
2784
2950
  console.log(
2785
- chalk18.dim(
2951
+ chalk19.dim(
2786
2952
  opts.project ? " Claude Code discovers it automatically for anyone working in this project." : " Claude Code discovers it automatically in every project on this machine."
2787
2953
  )
2788
2954
  );
@@ -2790,7 +2956,7 @@ async function agentSetup(opts) {
2790
2956
  if (!claudeCliAvailable()) {
2791
2957
  if (opts.mcp === true) {
2792
2958
  console.error(
2793
- chalk18.yellow(
2959
+ chalk19.yellow(
2794
2960
  "\u26A0 claude CLI not found \u2014 install Claude Code, then run: claude mcp add vibeview -- vibeview mcp"
2795
2961
  )
2796
2962
  );
@@ -2804,7 +2970,7 @@ async function agentSetup(opts) {
2804
2970
  }
2805
2971
  console.log(
2806
2972
  `
2807
- Suggested line for your project's ${chalk18.bold("CLAUDE.md")} (or ${chalk18.bold(
2973
+ Suggested line for your project's ${chalk19.bold("CLAUDE.md")} (or ${chalk19.bold(
2808
2974
  "AGENTS.md"
2809
2975
  )} for other agents):
2810
2976
  `
@@ -2812,7 +2978,7 @@ Suggested line for your project's ${chalk18.bold("CLAUDE.md")} (or ${chalk18.bol
2812
2978
  console.log(` ${CLAUDE_MD_SUGGESTION}
2813
2979
  `);
2814
2980
  console.log(
2815
- chalk18.dim(
2981
+ chalk19.dim(
2816
2982
  "Other agents (Cursor, Copilot, Codex, ...): point them at the skill file above, or\nadd the MCP server per client \u2014 see https://vibeview.io/docs/agent-control"
2817
2983
  )
2818
2984
  );
@@ -2991,6 +3157,12 @@ program.command("submit").description(
2991
3157
  ).option(
2992
3158
  "--asc-key <name>",
2993
3159
  "ios/tvos only: which stored App Store Connect key to use (default when exactly one \u2014 see `vibeview credentials asc-key add`)"
3160
+ ).option(
3161
+ "--release-status <status>",
3162
+ "android/androidtv only: Play release status \u2014 completed (default), draft, or inProgress"
3163
+ ).option(
3164
+ "--rollout <fraction>",
3165
+ "android/androidtv only: staged-rollout fraction, 0 < f < 1 (requires --release-status inProgress)"
2994
3166
  ).option("--no-wait", "Print the submission id and exit without waiting for completion").option("--json", "Emit line-delimited JSON events on stdout instead of human output", false).action((opts) => submitCommand(opts));
2995
3167
  var builds = program.command("builds").description("Manage cloud builds (see `vibeview build --cloud`)");
2996
3168
  builds.command("list").description("List cloud builds in your organization").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the raw build summaries as a JSON array", false).option("--limit <n>", "Max builds to return (default 50, max 200)").action((opts) => buildsList(opts));
@@ -3000,6 +3172,13 @@ builds.command("cancel <id>").description("Cancel a queued or running cloud buil
3000
3172
  builds.command("delete-artifact <id>").description(
3001
3173
  "Delete a production build's stored artifact file (irreversible; the build record, logs, and cost stay intact \u2014 only the downloadable file is removed)"
3002
3174
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((id, opts) => buildsDeleteArtifact(id, opts));
3175
+ var versions = program.command("versions").description(
3176
+ "Manage an app's server-side build-number counters (used by vibeview.json `build.autoIncrement` production builds)"
3177
+ );
3178
+ versions.command("list").description("List an app's build-number counters, one per platform").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "App public id (see `vibeview list-apps`)").option("--json", "Print the raw response as JSON", false).action((opts) => versionsList(opts));
3179
+ versions.command("set <number>").description(
3180
+ "Seed or overwrite one platform's counter \u2014 the next autoIncrement production build gets <number> + 1 (use it to start above a build number already in the store)"
3181
+ ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "App public id (see `vibeview list-apps`)").option("--platform <platform>", "ios, android, tvos, or androidtv").option("--json", "Print the updated version object as one JSON line", false).action((number, opts) => versionsSet(number, opts));
3003
3182
  program.command("whoami").description("Show which account and role this CLI is authenticated as").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the identity as one JSON line", false).action((opts) => whoAmI(opts));
3004
3183
  var env = program.command("env").description("Manage cloud build environment variables and secrets");
3005
3184
  env.command("list").description("List build variables and secrets (never prints a value)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option("--json", "Print the raw variable list as a JSON array", false).action((opts) => envList(opts));
@@ -3076,7 +3255,7 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
3076
3255
  program.command("mcp").description(
3077
3256
  "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
3078
3257
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
3079
- const { runMcpServer } = await import("./server-GMC6F545.js");
3258
+ const { runMcpServer } = await import("./server-UN5JRYGY.js");
3080
3259
  await runMcpServer(opts);
3081
3260
  });
3082
3261
  program.command("agent-setup").description(