vibeview 0.20.4 → 0.22.0

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
@@ -4,6 +4,7 @@ import {
4
4
  BUILD_TERMINAL_STATUSES,
5
5
  buildCommand,
6
6
  clearDevState,
7
+ createRokuPush,
7
8
  dev,
8
9
  fetchFinalSummary,
9
10
  formatDuration,
@@ -15,7 +16,7 @@ import {
15
16
  readDevState,
16
17
  readMetroState,
17
18
  runAgentVerb
18
- } from "./chunk-BBGXBKJQ.js";
19
+ } from "./chunk-SHS5PCSS.js";
19
20
  import {
20
21
  ApiClient,
21
22
  EXIT_INFRA_ERROR,
@@ -28,7 +29,7 @@ import {
28
29
  resolveAuth,
29
30
  saveConfig,
30
31
  uploadApp
31
- } from "./chunk-KON2FOEC.js";
32
+ } from "./chunk-UGCGJ6IN.js";
32
33
 
33
34
  // src/cli.ts
34
35
  import { createRequire } from "module";
@@ -1143,23 +1144,102 @@ async function devStop(opts) {
1143
1144
  process.exit(0);
1144
1145
  }
1145
1146
 
1147
+ // src/commands/dev-reload.ts
1148
+ import chalk10 from "chalk";
1149
+ function fail(opts, code, message, exit) {
1150
+ if (opts.json) {
1151
+ console.log(JSON.stringify({ reloaded: false, code, message }));
1152
+ } else {
1153
+ console.error(chalk10.red(`error: ${message}`));
1154
+ }
1155
+ process.exit(exit);
1156
+ }
1157
+ async function devReload(opts) {
1158
+ const cwd = process.cwd();
1159
+ const state = readDevState(cwd);
1160
+ if (!state) {
1161
+ fail(
1162
+ opts,
1163
+ "no_dev_session",
1164
+ "No dev session running (or it was not started with --detach).",
1165
+ 1
1166
+ );
1167
+ }
1168
+ if (state.platform !== "roku") {
1169
+ fail(
1170
+ opts,
1171
+ "not_roku",
1172
+ `dev-reload is for Roku channels; a ${state.platform} session hot-reloads through Metro on its own.`,
1173
+ 2
1174
+ );
1175
+ }
1176
+ let build;
1177
+ try {
1178
+ build = loadDevConfig(cwd)?.platforms?.roku?.build;
1179
+ } catch (err) {
1180
+ fail(
1181
+ opts,
1182
+ "config_required",
1183
+ `vibeview.json could not be read: ${err instanceof Error ? err.message : String(err)}`,
1184
+ 1
1185
+ );
1186
+ }
1187
+ if (!build) {
1188
+ fail(
1189
+ opts,
1190
+ "config_required",
1191
+ "no build config for roku \u2014 run `vibeview build --platform roku` once to set it up",
1192
+ 1
1193
+ );
1194
+ }
1195
+ const { token, baseUrl } = resolveAuth(opts);
1196
+ const api = new ApiClient(baseUrl, token);
1197
+ const push = createRokuPush({
1198
+ cwd,
1199
+ sessionId: state.session_id,
1200
+ build,
1201
+ token,
1202
+ baseUrl,
1203
+ api,
1204
+ // Build output and the "reloaded in" line are human output: stderr, so
1205
+ // stdout stays reserved for the --json result.
1206
+ log: (line) => console.error(chalk10.dim(line))
1207
+ });
1208
+ const started = Date.now();
1209
+ try {
1210
+ await push();
1211
+ } catch (err) {
1212
+ fail(opts, "reload_failed", err instanceof Error ? err.message : String(err), 1);
1213
+ }
1214
+ if (opts.json) {
1215
+ console.log(
1216
+ JSON.stringify({
1217
+ reloaded: true,
1218
+ session_id: state.session_id,
1219
+ seconds: (Date.now() - started) / 1e3
1220
+ })
1221
+ );
1222
+ }
1223
+ process.exit(0);
1224
+ }
1225
+
1146
1226
  // src/commands/builds.ts
1147
- import chalk11 from "chalk";
1227
+ import chalk12 from "chalk";
1148
1228
 
1149
1229
  // src/lib/formatters/builds.ts
1150
- import chalk10 from "chalk";
1230
+ import chalk11 from "chalk";
1151
1231
  var HEADERS = ["ID", "PLATFORM", "STATUS", "CREATED", "COST", "APP"];
1152
1232
  function formatCostCell(costMicro) {
1153
1233
  return costMicro > 0 ? `$${(costMicro / 1e6).toFixed(2)}` : "";
1154
1234
  }
1155
1235
  var STATUS_COLOR = {
1156
- succeeded: chalk10.green,
1157
- failed: chalk10.red,
1158
- timed_out: chalk10.red,
1159
- infra_failed: chalk10.red,
1160
- canceled: chalk10.dim,
1161
- running: chalk10.yellow,
1162
- queued: chalk10.dim
1236
+ succeeded: chalk11.green,
1237
+ failed: chalk11.red,
1238
+ timed_out: chalk11.red,
1239
+ infra_failed: chalk11.red,
1240
+ canceled: chalk11.dim,
1241
+ running: chalk11.yellow,
1242
+ queued: chalk11.dim
1163
1243
  };
1164
1244
  function formatBuildsHuman(builds2) {
1165
1245
  if (builds2.length === 0) {
@@ -1176,7 +1256,7 @@ function formatBuildsHuman(builds2) {
1176
1256
  const widths = HEADERS.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1177
1257
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
1178
1258
  const join3 = (cells) => cells.join(" ").trimEnd();
1179
- const lines = [chalk10.bold(join3(padCells(HEADERS)))];
1259
+ const lines = [chalk11.bold(join3(padCells(HEADERS)))];
1180
1260
  for (const row of rows) {
1181
1261
  const padded = padCells(row);
1182
1262
  const colorFn = STATUS_COLOR[row[2]] ?? ((s) => s);
@@ -1235,7 +1315,7 @@ async function buildsLogs(id, opts, deps) {
1235
1315
  console.log(JSON.stringify(logs));
1236
1316
  } else {
1237
1317
  if (logs.content) process.stdout.write(logs.content);
1238
- console.error(chalk11.dim(`status: ${logs.status}`));
1318
+ console.error(chalk12.dim(`status: ${logs.status}`));
1239
1319
  }
1240
1320
  process.exitCode = 0;
1241
1321
  return;
@@ -1332,7 +1412,7 @@ async function buildsDownload(id, opts, deps) {
1332
1412
  if (opts.json) {
1333
1413
  console.log(JSON.stringify(result));
1334
1414
  } else {
1335
- console.log(chalk11.green(`\u2713 saved ${result.path}`));
1415
+ console.log(chalk12.green(`\u2713 saved ${result.path}`));
1336
1416
  }
1337
1417
  process.exitCode = 0;
1338
1418
  } catch (err) {
@@ -1349,7 +1429,7 @@ async function buildsCancel(id, opts, deps) {
1349
1429
  if (opts.json) {
1350
1430
  console.log(JSON.stringify(build));
1351
1431
  } else {
1352
- console.log(`${chalk11.cyan(build.id)} ${build.status}`);
1432
+ console.log(`${chalk12.cyan(build.id)} ${build.status}`);
1353
1433
  }
1354
1434
  process.exitCode = 0;
1355
1435
  } catch (err) {
@@ -1366,7 +1446,7 @@ async function buildsDeleteArtifact(id, opts, deps) {
1366
1446
  if (opts.json) {
1367
1447
  console.log(JSON.stringify(result));
1368
1448
  } else {
1369
- console.log(chalk11.green(`\u2713 deleted artifact for ${id}`));
1449
+ console.log(chalk12.green(`\u2713 deleted artifact for ${id}`));
1370
1450
  }
1371
1451
  process.exitCode = 0;
1372
1452
  } catch (err) {
@@ -1377,11 +1457,11 @@ async function buildsDeleteArtifact(id, opts, deps) {
1377
1457
  function printError(opts, err) {
1378
1458
  const message = err instanceof Error ? err.message : String(err);
1379
1459
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
1380
- console.error(chalk11.red(`error: ${message}`));
1460
+ console.error(chalk12.red(`error: ${message}`));
1381
1461
  }
1382
1462
 
1383
1463
  // src/commands/submit.ts
1384
- import chalk12 from "chalk";
1464
+ import chalk13 from "chalk";
1385
1465
  function defaultDeps2(client) {
1386
1466
  return {
1387
1467
  createSubmission: client.createSubmission.bind(client),
@@ -1404,7 +1484,7 @@ function submitConfigFor(cwd, platform) {
1404
1484
  cfg = loadDevConfig(cwd);
1405
1485
  } catch (err) {
1406
1486
  console.error(
1407
- chalk12.yellow(
1487
+ chalk13.yellow(
1408
1488
  `warning: vibeview.json could not be read \u2014 ignoring submit defaults (${err instanceof Error ? err.message : String(err)})`
1409
1489
  )
1410
1490
  );
@@ -1552,12 +1632,12 @@ async function submitCommand(opts, deps) {
1552
1632
  return;
1553
1633
  }
1554
1634
  if (!opts.json) {
1555
- console.error(` ${chalk12.cyan(sub.id)} ${chalk12.dim(`[${sub.platform}]`)}`);
1635
+ console.error(` ${chalk13.cyan(sub.id)} ${chalk13.dim(`[${sub.platform}]`)}`);
1556
1636
  }
1557
1637
  const final = await followSubmission(sub, opts, d);
1558
1638
  if (!opts.json && final.status === "succeeded" && (final.release_status ?? releaseStatus) === "draft") {
1559
1639
  console.error(
1560
- chalk12.dim("release status: draft \u2014 activate it from the Play Console when ready")
1640
+ chalk13.dim("release status: draft \u2014 activate it from the Play Console when ready")
1561
1641
  );
1562
1642
  }
1563
1643
  process.exitCode = final.status === "succeeded" ? 0 : 1;
@@ -1581,11 +1661,11 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
1581
1661
  });
1582
1662
  emit(opts.json, { event: "preflight", ...result });
1583
1663
  if (!opts.json) {
1584
- const mark = result.ready ? chalk12.green("\u2713") : chalk12.red("\u2717");
1664
+ const mark = result.ready ? chalk13.green("\u2713") : chalk13.red("\u2717");
1585
1665
  console.error(`${mark} ${result.message}`);
1586
1666
  if (result.ready) {
1587
1667
  console.error(
1588
- chalk12.dim(
1668
+ chalk13.dim(
1589
1669
  ` next: vibeview build --cloud --production --platform ${platform}, then vibeview submit --platform ${platform} --latest`
1590
1670
  )
1591
1671
  );
@@ -1634,14 +1714,14 @@ async function followSubmission(created, opts, deps) {
1634
1714
  if (opts.json) {
1635
1715
  emit(opts.json, { event: "status", id: created.id, status: "queued" });
1636
1716
  } else {
1637
- console.error(chalk12.dim("\u23F3 queued \u2014 waiting for a submission slot"));
1717
+ console.error(chalk13.dim("\u23F3 queued \u2014 waiting for a submission slot"));
1638
1718
  }
1639
1719
  } else if (log.status === "running" && !printedRunning) {
1640
1720
  printedRunning = true;
1641
1721
  if (opts.json) {
1642
1722
  emit(opts.json, { event: "status", id: created.id, status: "running" });
1643
1723
  } else {
1644
- console.error(`${chalk12.cyan("\u25B6")} submitting`);
1724
+ console.error(`${chalk13.cyan("\u25B6")} submitting`);
1645
1725
  }
1646
1726
  }
1647
1727
  if (log.content) {
@@ -1683,7 +1763,7 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
1683
1763
  });
1684
1764
  if (!opts.json) {
1685
1765
  const ok = final.status === "succeeded";
1686
- const mark = ok ? chalk12.green("\u2713") : chalk12.red("\u2717");
1766
+ const mark = ok ? chalk13.green("\u2713") : chalk13.red("\u2717");
1687
1767
  const duration = formatDuration(final.started_at, final.finished_at);
1688
1768
  let line = `${mark} ${final.status}`;
1689
1769
  if (duration) line += ` (${duration})`;
@@ -1695,11 +1775,11 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
1695
1775
  function printError2(opts, err) {
1696
1776
  const message = err instanceof Error ? err.message : String(err);
1697
1777
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
1698
- console.error(chalk12.red(`error: ${message}`));
1778
+ console.error(chalk13.red(`error: ${message}`));
1699
1779
  }
1700
1780
 
1701
1781
  // src/commands/versions.ts
1702
- import chalk13 from "chalk";
1782
+ import chalk14 from "chalk";
1703
1783
  function defaultDeps3(client) {
1704
1784
  return {
1705
1785
  listAppVersions: client.listAppVersions.bind(client),
@@ -1749,7 +1829,7 @@ async function versionsSet(buildNumber, opts, deps) {
1749
1829
  console.log(JSON.stringify(version));
1750
1830
  } else {
1751
1831
  console.log(
1752
- chalk13.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk13.dim(
1832
+ chalk14.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk14.dim(
1753
1833
  " \u2014 the next autoIncrement production build gets " + (version.build_number + 1)
1754
1834
  )
1755
1835
  );
@@ -1769,18 +1849,18 @@ function formatVersionsHuman(versions2) {
1769
1849
  const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1770
1850
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
1771
1851
  const join3 = (cells) => cells.join(" ").trimEnd();
1772
- const lines = [chalk13.bold(join3(padCells(HEADERS2)))];
1852
+ const lines = [chalk14.bold(join3(padCells(HEADERS2)))];
1773
1853
  for (const row of rows) lines.push(join3(padCells(row)));
1774
1854
  return lines.join("\n") + "\n";
1775
1855
  }
1776
1856
  function printError3(opts, err) {
1777
1857
  const message = err instanceof Error ? err.message : String(err);
1778
1858
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
1779
- console.error(chalk13.red(`error: ${message}`));
1859
+ console.error(chalk14.red(`error: ${message}`));
1780
1860
  }
1781
1861
 
1782
1862
  // src/commands/whoami.ts
1783
- import chalk14 from "chalk";
1863
+ import chalk15 from "chalk";
1784
1864
  function defaultDeps4(opts) {
1785
1865
  const { token, baseUrl } = resolveAuth(opts);
1786
1866
  const client = new ApiClient(baseUrl, token);
@@ -1802,27 +1882,27 @@ async function whoAmI(opts, deps) {
1802
1882
  process.exitCode = 0;
1803
1883
  return;
1804
1884
  }
1805
- console.log(`${chalk14.bold("Logged in as")} ${me.email}`);
1885
+ console.log(`${chalk15.bold("Logged in as")} ${me.email}`);
1806
1886
  if (me.memberships.length === 1) {
1807
- console.log(`${chalk14.bold("Organization")} ${me.memberships[0].organization_name}`);
1887
+ console.log(`${chalk15.bold("Organization")} ${me.memberships[0].organization_name}`);
1808
1888
  } else if (me.memberships.length > 1) {
1809
- console.log(chalk14.bold("Organizations"));
1889
+ console.log(chalk15.bold("Organizations"));
1810
1890
  for (const m of me.memberships) {
1811
1891
  console.log(` ${m.organization_name} (${m.role})`);
1812
1892
  }
1813
1893
  }
1814
- console.log(`${chalk14.bold("Member role")} ${me.role ?? "\u2014"}`);
1815
- console.log(`${chalk14.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
1816
- console.log(`${chalk14.bold("Effective role")} ${effective ?? "\u2014"}`);
1894
+ console.log(`${chalk15.bold("Member role")} ${me.role ?? "\u2014"}`);
1895
+ console.log(`${chalk15.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
1896
+ console.log(`${chalk15.bold("Effective role")} ${effective ?? "\u2014"}`);
1817
1897
  if (me.is_superadmin) {
1818
- console.log(chalk14.dim("Superadmin (platform-wide; does not raise the org role above)"));
1898
+ console.log(chalk15.dim("Superadmin (platform-wide; does not raise the org role above)"));
1819
1899
  }
1820
1900
  if (me.role === "admin" && me.token_role && me.token_role !== "admin") {
1821
1901
  console.log(
1822
- chalk14.yellow(
1902
+ chalk15.yellow(
1823
1903
  `
1824
- 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.
1825
- Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or ${chalk14.bold("VIBEVIEW_API_TOKEN")}.`
1904
+ 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 ${chalk15.bold("vibeview env set")}) will fail with a permissions error.
1905
+ Create an admin API token in the dashboard and use ${chalk15.bold("--token")} or ${chalk15.bold("VIBEVIEW_API_TOKEN")}.`
1826
1906
  )
1827
1907
  );
1828
1908
  }
@@ -1832,7 +1912,7 @@ Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or
1832
1912
  if (opts.json) {
1833
1913
  console.log(JSON.stringify({ event: "error", message }));
1834
1914
  }
1835
- console.error(chalk14.red(`error: ${message}`));
1915
+ console.error(chalk15.red(`error: ${message}`));
1836
1916
  process.exitCode = 1;
1837
1917
  }
1838
1918
  }
@@ -1840,7 +1920,7 @@ Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or
1840
1920
  // src/commands/env.ts
1841
1921
  import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
1842
1922
  import { basename } from "path";
1843
- import chalk15 from "chalk";
1923
+ import chalk16 from "chalk";
1844
1924
  import { password } from "@inquirer/prompts";
1845
1925
 
1846
1926
  // src/lib/gitignore.ts
@@ -1896,7 +1976,7 @@ function defaultDeps5(client) {
1896
1976
  isIgnored: (path) => isGitIgnored(path),
1897
1977
  writeFile: writeSecureFile,
1898
1978
  readFile: (path) => readFileAsync(path),
1899
- warn: (message) => console.error(chalk15.yellow(`warning: ${message}`))
1979
+ warn: (message) => console.error(chalk16.yellow(`warning: ${message}`))
1900
1980
  };
1901
1981
  }
1902
1982
  async function envList(opts, deps) {
@@ -1928,7 +2008,7 @@ function formatEnvVarsHuman(variables) {
1928
2008
  const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
1929
2009
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
1930
2010
  const join3 = (cells) => cells.join(" ").trimEnd();
1931
- const lines = [chalk15.bold(join3(padCells(HEADERS4)))];
2011
+ const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
1932
2012
  for (const row of rows) lines.push(join3(padCells(row)));
1933
2013
  return lines.join("\n") + "\n";
1934
2014
  }
@@ -1978,7 +2058,7 @@ function printSetResult(opts, result) {
1978
2058
  if (opts.json) {
1979
2059
  console.log(JSON.stringify(result));
1980
2060
  } else {
1981
- console.log(chalk15.green(`\u2713 set ${result.name} (${result.visibility})`));
2061
+ console.log(chalk16.green(`\u2713 set ${result.name} (${result.visibility})`));
1982
2062
  }
1983
2063
  }
1984
2064
  async function envRm(name, opts, deps) {
@@ -1988,7 +2068,7 @@ async function envRm(name, opts, deps) {
1988
2068
  if (opts.json) {
1989
2069
  console.log(JSON.stringify({ event: "deleted", name }));
1990
2070
  } else {
1991
- console.log(chalk15.green(`\u2713 removed ${name}`));
2071
+ console.log(chalk16.green(`\u2713 removed ${name}`));
1992
2072
  }
1993
2073
  process.exitCode = 0;
1994
2074
  } catch (err) {
@@ -2020,7 +2100,7 @@ async function envPull(opts, deps) {
2020
2100
  if (opts.json) {
2021
2101
  console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
2022
2102
  } else {
2023
- console.log(chalk15.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
2103
+ console.log(chalk16.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
2024
2104
  }
2025
2105
  process.exitCode = 0;
2026
2106
  } catch (err) {
@@ -2059,7 +2139,7 @@ function escapeEnvValue(value) {
2059
2139
  function printError4(opts, err) {
2060
2140
  const message = err instanceof Error ? err.message : String(err);
2061
2141
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
2062
- console.error(chalk15.red(`error: ${message}`));
2142
+ console.error(chalk16.red(`error: ${message}`));
2063
2143
  }
2064
2144
 
2065
2145
  // src/commands/credentials.ts
@@ -2071,7 +2151,7 @@ import {
2071
2151
  } from "fs/promises";
2072
2152
  import { mkdirSync } from "fs";
2073
2153
  import { basename as basename2, extname, join } from "path";
2074
- import chalk16 from "chalk";
2154
+ import chalk17 from "chalk";
2075
2155
  import { input, password as password2 } from "@inquirer/prompts";
2076
2156
  function clientFor2(opts) {
2077
2157
  const { token, baseUrl } = resolveAuth(opts);
@@ -2144,7 +2224,7 @@ function defaultDeps6(client) {
2144
2224
  mkdir: (dir) => mkdirSync(dir, { recursive: true }),
2145
2225
  writeFile: (path, data, mode) => writeFileAsync(path, data, mode !== void 0 ? { mode } : void 0),
2146
2226
  chmod: (path, mode) => chmodAsync(path, mode),
2147
- warn: (message) => console.error(chalk16.yellow(`warning: ${message}`))
2227
+ warn: (message) => console.error(chalk17.yellow(`warning: ${message}`))
2148
2228
  };
2149
2229
  }
2150
2230
  var ANDROID_KIND = "android_keystore";
@@ -2213,7 +2293,7 @@ async function credentialsAdd(opts, deps) {
2213
2293
  if (opts.json) {
2214
2294
  console.log(JSON.stringify(result));
2215
2295
  } else {
2216
- console.log(chalk16.green(`\u2713 added ${result.name} (${result.platform})`));
2296
+ console.log(chalk17.green(`\u2713 added ${result.name} (${result.platform})`));
2217
2297
  }
2218
2298
  process.exitCode = 0;
2219
2299
  } catch (err) {
@@ -2271,20 +2351,20 @@ async function credentialsGenerate(opts, deps) {
2271
2351
  }
2272
2352
  if (opts.json) {
2273
2353
  } else {
2274
- console.log(chalk16.green(`\u2713 generated ${result.name} (android)`));
2354
+ console.log(chalk17.green(`\u2713 generated ${result.name} (android)`));
2275
2355
  console.log("");
2276
- console.log(` Keystore saved to: ${chalk16.bold(savedTo)}`);
2277
- console.log(` Key alias: ${chalk16.bold(result.key_alias)}`);
2278
- console.log(` Store password: ${chalk16.bold(result.store_password)}`);
2279
- console.log(` Key password: ${chalk16.bold(result.key_password)}`);
2356
+ console.log(` Keystore saved to: ${chalk17.bold(savedTo)}`);
2357
+ console.log(` Key alias: ${chalk17.bold(result.key_alias)}`);
2358
+ console.log(` Store password: ${chalk17.bold(result.store_password)}`);
2359
+ console.log(` Key password: ${chalk17.bold(result.key_password)}`);
2280
2360
  console.log("");
2281
2361
  console.log(
2282
- chalk16.yellow(
2362
+ chalk17.yellow(
2283
2363
  " These are shown ONCE \u2014 VibeView cannot recover the keystore file or its passwords. Back them up somewhere safe."
2284
2364
  )
2285
2365
  );
2286
2366
  console.log(
2287
- chalk16.yellow(
2367
+ chalk17.yellow(
2288
2368
  " The credential is already uploaded and active for android and androidtv production builds."
2289
2369
  )
2290
2370
  );
@@ -2310,19 +2390,19 @@ async function generateAppleCredential(platform, opts, d) {
2310
2390
  if (opts.json) {
2311
2391
  console.log(JSON.stringify(result));
2312
2392
  } else {
2313
- console.log(chalk16.green(`\u2713 generated ${result.name} (${result.platform})`));
2393
+ console.log(chalk17.green(`\u2713 generated ${result.name} (${result.platform})`));
2314
2394
  console.log("");
2315
- console.log(` Scope: ${chalk16.bold(result.app_public_id ?? "org-wide")}`);
2395
+ console.log(` Scope: ${chalk17.bold(result.app_public_id ?? "org-wide")}`);
2316
2396
  const expires = formatExpires(result);
2317
2397
  if (expires) {
2318
- console.log(` Expires: ${chalk16.bold(expires)}`);
2398
+ console.log(` Expires: ${chalk17.bold(expires)}`);
2319
2399
  }
2320
2400
  console.log("");
2321
2401
  console.log(
2322
2402
  ` The certificate and provisioning profile are stored with VibeView and already active for ${result.platform} production builds.`
2323
2403
  );
2324
2404
  console.log(
2325
- ` Run ${chalk16.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
2405
+ ` Run ${chalk17.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
2326
2406
  );
2327
2407
  }
2328
2408
  process.exitCode = 0;
@@ -2388,17 +2468,17 @@ async function credentialsDownload(name, opts, deps) {
2388
2468
  await d.chmod(path, 384);
2389
2469
  }
2390
2470
  if (!opts.json) {
2391
- console.log(chalk16.green(`\u2713 downloaded ${material.name} (${material.platform})`));
2471
+ console.log(chalk17.green(`\u2713 downloaded ${material.name} (${material.platform})`));
2392
2472
  console.log("");
2393
- console.log(` Saved to: ${chalk16.bold(savedTo)}`);
2473
+ console.log(` Saved to: ${chalk17.bold(savedTo)}`);
2394
2474
  if (profileSavedTo !== null) {
2395
- console.log(` Profile: ${chalk16.bold(profileSavedTo)}`);
2475
+ console.log(` Profile: ${chalk17.bold(profileSavedTo)}`);
2396
2476
  }
2397
2477
  for (const [key, value] of Object.entries(material.passwords)) {
2398
- console.log(` ${key}: ${chalk16.bold(value)}`);
2478
+ console.log(` ${key}: ${chalk17.bold(value)}`);
2399
2479
  }
2400
2480
  console.log("");
2401
- console.log(chalk16.yellow(" This download is recorded in your organization's audit log."));
2481
+ console.log(chalk17.yellow(" This download is recorded in your organization's audit log."));
2402
2482
  }
2403
2483
  process.exitCode = 0;
2404
2484
  } catch (err) {
@@ -2429,13 +2509,13 @@ async function ascKeyAdd(opts, deps) {
2429
2509
  if (opts.json) {
2430
2510
  console.log(JSON.stringify(result));
2431
2511
  } else {
2432
- console.log(chalk16.green(`\u2713 added App Store Connect key ${result.name}`));
2512
+ console.log(chalk17.green(`\u2713 added App Store Connect key ${result.name}`));
2433
2513
  console.log("");
2434
- console.log(` Key ID: ${chalk16.bold(result.key_id)}`);
2435
- console.log(` Issuer ID: ${chalk16.bold(result.issuer_id)}`);
2514
+ console.log(` Key ID: ${chalk17.bold(result.key_id)}`);
2515
+ console.log(` Issuer ID: ${chalk17.bold(result.issuer_id)}`);
2436
2516
  console.log("");
2437
2517
  console.log(
2438
- chalk16.yellow(
2518
+ chalk17.yellow(
2439
2519
  " The .p8 is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
2440
2520
  )
2441
2521
  );
@@ -2470,7 +2550,7 @@ function formatAscKeysHuman(keys) {
2470
2550
  const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2471
2551
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2472
2552
  const join3 = (cells) => cells.join(" ").trimEnd();
2473
- const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2553
+ const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
2474
2554
  for (const row of rows) lines.push(join3(padCells(row)));
2475
2555
  return lines.join("\n") + "\n";
2476
2556
  }
@@ -2481,7 +2561,7 @@ async function ascKeyRm(name, opts, deps) {
2481
2561
  if (opts.json) {
2482
2562
  console.log(JSON.stringify({ event: "deleted", name }));
2483
2563
  } else {
2484
- console.log(chalk16.green(`\u2713 removed App Store Connect key ${name}`));
2564
+ console.log(chalk17.green(`\u2713 removed App Store Connect key ${name}`));
2485
2565
  }
2486
2566
  process.exitCode = 0;
2487
2567
  } catch (err) {
@@ -2506,12 +2586,12 @@ async function playKeyAdd(opts, deps) {
2506
2586
  if (opts.json) {
2507
2587
  console.log(JSON.stringify(result));
2508
2588
  } else {
2509
- console.log(chalk16.green(`\u2713 added Google Play key ${result.name}`));
2589
+ console.log(chalk17.green(`\u2713 added Google Play key ${result.name}`));
2510
2590
  console.log("");
2511
- console.log(` Service account: ${chalk16.bold(result.client_email)}`);
2591
+ console.log(` Service account: ${chalk17.bold(result.client_email)}`);
2512
2592
  console.log("");
2513
2593
  console.log(
2514
- chalk16.yellow(
2594
+ chalk17.yellow(
2515
2595
  " The JSON key is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
2516
2596
  )
2517
2597
  );
@@ -2546,7 +2626,7 @@ function formatPlayKeysHuman(keys) {
2546
2626
  const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2547
2627
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2548
2628
  const join3 = (cells) => cells.join(" ").trimEnd();
2549
- const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2629
+ const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
2550
2630
  for (const row of rows) lines.push(join3(padCells(row)));
2551
2631
  return lines.join("\n") + "\n";
2552
2632
  }
@@ -2557,7 +2637,7 @@ async function playKeyRm(name, opts, deps) {
2557
2637
  if (opts.json) {
2558
2638
  console.log(JSON.stringify({ event: "deleted", name }));
2559
2639
  } else {
2560
- console.log(chalk16.green(`\u2713 removed Google Play key ${name}`));
2640
+ console.log(chalk17.green(`\u2713 removed Google Play key ${name}`));
2561
2641
  }
2562
2642
  process.exitCode = 0;
2563
2643
  } catch (err) {
@@ -2626,7 +2706,7 @@ function formatCredentialsHuman(credentials2) {
2626
2706
  const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2627
2707
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2628
2708
  const join3 = (cells) => cells.join(" ").trimEnd();
2629
- const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
2709
+ const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
2630
2710
  for (const row of rows) lines.push(join3(padCells(row)));
2631
2711
  return lines.join("\n") + "\n";
2632
2712
  }
@@ -2637,7 +2717,7 @@ async function credentialsRm(name, opts, deps) {
2637
2717
  if (opts.json) {
2638
2718
  console.log(JSON.stringify({ event: "deleted", name }));
2639
2719
  } else {
2640
- console.log(chalk16.green(`\u2713 removed ${name}`));
2720
+ console.log(chalk17.green(`\u2713 removed ${name}`));
2641
2721
  }
2642
2722
  process.exitCode = 0;
2643
2723
  } catch (err) {
@@ -2649,16 +2729,16 @@ function printKeystoreWriteFailure(opts, err, result) {
2649
2729
  const message = err instanceof Error ? err.message : String(err);
2650
2730
  if (opts.json) {
2651
2731
  console.log(JSON.stringify({ event: "error", message }));
2652
- console.error(chalk16.red(`error: ${message}`));
2732
+ console.error(chalk17.red(`error: ${message}`));
2653
2733
  return;
2654
2734
  }
2655
- console.error(chalk16.red(`error: ${message}`));
2735
+ console.error(chalk17.red(`error: ${message}`));
2656
2736
  console.error(
2657
- chalk16.red(
2737
+ chalk17.red(
2658
2738
  `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.`
2659
2739
  )
2660
2740
  );
2661
- console.error(chalk16.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
2741
+ console.error(chalk17.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
2662
2742
  console.error("");
2663
2743
  console.error(` Key alias: ${result.key_alias}`);
2664
2744
  console.error(` Store password: ${result.store_password}`);
@@ -2672,11 +2752,11 @@ function printKeystoreWriteFailure(opts, err, result) {
2672
2752
  function printError5(opts, err) {
2673
2753
  const message = err instanceof Error ? err.message : String(err);
2674
2754
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
2675
- console.error(chalk16.red(`error: ${message}`));
2755
+ console.error(chalk17.red(`error: ${message}`));
2676
2756
  }
2677
2757
 
2678
2758
  // src/commands/webhooks.ts
2679
- import chalk17 from "chalk";
2759
+ import chalk18 from "chalk";
2680
2760
  function clientFor3(opts) {
2681
2761
  const { token, baseUrl } = resolveAuth(opts);
2682
2762
  return new ApiClient(baseUrl, token);
@@ -2707,14 +2787,14 @@ async function webhooksAdd(opts, deps) {
2707
2787
  if (opts.json) {
2708
2788
  console.log(JSON.stringify(result));
2709
2789
  } else {
2710
- console.log(chalk17.green(`\u2713 added ${result.id} ${result.url}`));
2790
+ console.log(chalk18.green(`\u2713 added ${result.id} ${result.url}`));
2711
2791
  console.log("");
2712
- console.log(` Signing secret: ${chalk17.bold(result.secret)}`);
2792
+ console.log(` Signing secret: ${chalk18.bold(result.secret)}`);
2713
2793
  if (opts.secret !== void 0) {
2714
- console.log(chalk17.yellow(" (This is the secret you provided.)"));
2794
+ console.log(chalk18.yellow(" (This is the secret you provided.)"));
2715
2795
  }
2716
2796
  console.log(
2717
- chalk17.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
2797
+ chalk18.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
2718
2798
  );
2719
2799
  }
2720
2800
  process.exitCode = 0;
@@ -2745,7 +2825,7 @@ function formatWebhooksHuman(webhooks2) {
2745
2825
  const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2746
2826
  const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
2747
2827
  const join3 = (cells) => cells.join(" ").trimEnd();
2748
- const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
2828
+ const lines = [chalk18.bold(join3(padCells(HEADERS4)))];
2749
2829
  for (const row of rows) lines.push(join3(padCells(row)));
2750
2830
  return lines.join("\n") + "\n";
2751
2831
  }
@@ -2771,7 +2851,7 @@ async function webhooksRm(id, opts, deps) {
2771
2851
  if (opts.json) {
2772
2852
  console.log(JSON.stringify({ event: "deleted", id }));
2773
2853
  } else {
2774
- console.log(chalk17.green(`\u2713 removed ${id}`));
2854
+ console.log(chalk18.green(`\u2713 removed ${id}`));
2775
2855
  }
2776
2856
  process.exitCode = 0;
2777
2857
  } catch (err) {
@@ -2804,13 +2884,13 @@ async function webhooksTest(id, opts, deps) {
2804
2884
  if (opts.json) console.log(JSON.stringify(ld));
2805
2885
  if (ld.status === "succeeded") {
2806
2886
  if (!opts.json) {
2807
- console.log(chalk17.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
2887
+ console.log(chalk18.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
2808
2888
  }
2809
2889
  process.exitCode = 0;
2810
2890
  } else {
2811
2891
  if (!opts.json) {
2812
2892
  const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
2813
- console.error(chalk17.red(`\u2717 Failed (${code}) at ${ld.at}`));
2893
+ console.error(chalk18.red(`\u2717 Failed (${code}) at ${ld.at}`));
2814
2894
  }
2815
2895
  process.exitCode = 1;
2816
2896
  }
@@ -2819,7 +2899,7 @@ async function webhooksTest(id, opts, deps) {
2819
2899
  }
2820
2900
  if (opts.json) console.log(JSON.stringify({ status: "pending" }));
2821
2901
  console.error(
2822
- chalk17.yellow(
2902
+ chalk18.yellow(
2823
2903
  `Delivery still pending after ${TEST_POLL_BUDGET_MS / 1e3}s \u2014 check \`vibeview webhooks list\` later for the result.`
2824
2904
  )
2825
2905
  );
@@ -2832,11 +2912,11 @@ async function webhooksTest(id, opts, deps) {
2832
2912
  function printError6(opts, err) {
2833
2913
  const message = err instanceof Error ? err.message : String(err);
2834
2914
  if (opts.json) console.log(JSON.stringify({ event: "error", message }));
2835
- console.error(chalk17.red(`error: ${message}`));
2915
+ console.error(chalk18.red(`error: ${message}`));
2836
2916
  }
2837
2917
 
2838
2918
  // src/lib/formatters/devices.ts
2839
- import chalk18 from "chalk";
2919
+ import chalk19 from "chalk";
2840
2920
  var HEADERS3 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
2841
2921
  function formatDevicesHuman(devices) {
2842
2922
  if (devices.length === 0) {
@@ -2852,9 +2932,9 @@ function formatDevicesHuman(devices) {
2852
2932
  ]);
2853
2933
  const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
2854
2934
  const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
2855
- const lines = [chalk18.bold(fmt(HEADERS3))];
2935
+ const lines = [chalk19.bold(fmt(HEADERS3))];
2856
2936
  for (const row of rows.sort(rowSort)) {
2857
- const statusColored = row[5] === "available" ? chalk18.green(row[5]) : row[5] === "busy" ? chalk18.yellow(row[5]) : chalk18.dim(row[5]);
2937
+ const statusColored = row[5] === "available" ? chalk19.green(row[5]) : row[5] === "busy" ? chalk19.yellow(row[5]) : chalk19.dim(row[5]);
2858
2938
  lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
2859
2939
  }
2860
2940
  return lines.join("\n") + "\n";
@@ -2944,7 +3024,7 @@ import { dirname, join as join2 } from "path";
2944
3024
  import { fileURLToPath } from "url";
2945
3025
  import { spawnSync } from "child_process";
2946
3026
  import { confirm } from "@inquirer/prompts";
2947
- import chalk19 from "chalk";
3027
+ import chalk20 from "chalk";
2948
3028
  function resolveShippedSkillPath(fromDir) {
2949
3029
  for (const rel of ["..", join2("..", "..")]) {
2950
3030
  const candidate = join2(fromDir, rel, "skills", "vibeview-agent", "SKILL.md");
@@ -2972,23 +3052,23 @@ function registerMcp() {
2972
3052
  encoding: "utf8"
2973
3053
  });
2974
3054
  if (res.status === 0) {
2975
- console.log(chalk19.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
3055
+ console.log(chalk20.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
2976
3056
  return;
2977
3057
  }
2978
3058
  const out = `${res.stderr ?? ""}${res.stdout ?? ""}`;
2979
3059
  if (out.includes("already exists")) {
2980
- console.log(chalk19.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
3060
+ console.log(chalk20.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
2981
3061
  return;
2982
3062
  }
2983
- console.error(chalk19.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
2984
- console.error(chalk19.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
3063
+ console.error(chalk20.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
3064
+ console.error(chalk20.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
2985
3065
  }
2986
3066
  async function agentSetup(opts) {
2987
3067
  const moduleDir = dirname(fileURLToPath(import.meta.url));
2988
3068
  const shipped = resolveShippedSkillPath(moduleDir);
2989
3069
  if (!shipped) {
2990
3070
  console.error(
2991
- chalk19.red(
3071
+ chalk20.red(
2992
3072
  "error: could not find the packaged skill (skills/vibeview-agent/SKILL.md) \u2014 is this a complete install of the vibeview package?"
2993
3073
  )
2994
3074
  );
@@ -2997,9 +3077,9 @@ async function agentSetup(opts) {
2997
3077
  }
2998
3078
  const targetDir = skillInstallDir(opts.project ?? false, process.cwd(), homedir());
2999
3079
  const result = installSkill(shipped, targetDir);
3000
- console.log(chalk19.green(`\u2713 vibeview-agent skill ${result}: ${join2(targetDir, "SKILL.md")}`));
3080
+ console.log(chalk20.green(`\u2713 vibeview-agent skill ${result}: ${join2(targetDir, "SKILL.md")}`));
3001
3081
  console.log(
3002
- chalk19.dim(
3082
+ chalk20.dim(
3003
3083
  opts.project ? " Claude Code discovers it automatically for anyone working in this project." : " Claude Code discovers it automatically in every project on this machine."
3004
3084
  )
3005
3085
  );
@@ -3007,7 +3087,7 @@ async function agentSetup(opts) {
3007
3087
  if (!claudeCliAvailable()) {
3008
3088
  if (opts.mcp === true) {
3009
3089
  console.error(
3010
- chalk19.yellow(
3090
+ chalk20.yellow(
3011
3091
  "\u26A0 claude CLI not found \u2014 install Claude Code, then run: claude mcp add vibeview -- vibeview mcp"
3012
3092
  )
3013
3093
  );
@@ -3021,7 +3101,7 @@ async function agentSetup(opts) {
3021
3101
  }
3022
3102
  console.log(
3023
3103
  `
3024
- Suggested line for your project's ${chalk19.bold("CLAUDE.md")} (or ${chalk19.bold(
3104
+ Suggested line for your project's ${chalk20.bold("CLAUDE.md")} (or ${chalk20.bold(
3025
3105
  "AGENTS.md"
3026
3106
  )} for other agents):
3027
3107
  `
@@ -3029,7 +3109,7 @@ Suggested line for your project's ${chalk19.bold("CLAUDE.md")} (or ${chalk19.bol
3029
3109
  console.log(` ${CLAUDE_MD_SUGGESTION}
3030
3110
  `);
3031
3111
  console.log(
3032
- chalk19.dim(
3112
+ chalk20.dim(
3033
3113
  "Other agents (Cursor, Copilot, Codex, ...): install the same skill from GitHub with\n npx skills add vibeview/skills\nor point them at the skill file above, or add the MCP server per client \u2014\nsee https://vibeview.io/docs/agent-control"
3034
3114
  )
3035
3115
  );
@@ -3076,7 +3156,7 @@ function resolveMetadata(opts) {
3076
3156
  }
3077
3157
  var program = new Command();
3078
3158
  program.name("vibeview").description("VibeView CLI -- run mobile tests from CI/CD pipelines").version(pkg.version);
3079
- program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds", "300").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3159
+ program.command("run-suite <suite-id>").description("Run all test cases in a suite").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-case breakdown", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3080
3160
  "--device-category <category>",
3081
3161
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3082
3162
  parseDeviceCategory
@@ -3104,7 +3184,7 @@ program.command("run-suites").description("Run all suites matching a tag filter"
3104
3184
  ).option("--match <mode>", "any (default) or all", "any").option(
3105
3185
  "--platform <platform>",
3106
3186
  "Filter matched suites to one platform: ios, android, tvos, androidtv (default: no filter)"
3107
- ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "300").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3187
+ ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show per-suite progress", false).option("--timeout <seconds>", "Per-suite timeout, capped at 2 hours total", "1800").option("--commit-sha <sha>", "Git commit SHA propagated to every SuiteRun").option("--model <model>", "AI model").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3108
3188
  "--device-category <category>",
3109
3189
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3110
3190
  parseDeviceCategory
@@ -3124,7 +3204,7 @@ program.command("run-suites").description("Run all suites matching a tag filter"
3124
3204
  build: opts.build ? parseInt(opts.build, 10) : void 0
3125
3205
  });
3126
3206
  });
3127
- program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds", "300").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3207
+ program.command("run-test <test-id>").description("Run a single test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json, junit", "human").option("--verbose", "Show step details", false).option("--timeout <seconds>", "Max wait time in seconds (queue wait plus execution)", "1800").option("--commit-sha <sha>", "Git commit SHA for GitHub status checks").option("--model <model>", "AI model to use").option("--provider <provider>", "AI provider").option("--mode <mode>", "Execution mode: hybrid, ai, replay").option(
3128
3208
  "--device-category <category>",
3129
3209
  "Device category: phone, tablet, tv (default: phone when --device is not set)",
3130
3210
  parseDeviceCategory
@@ -3151,8 +3231,10 @@ program.command("list-suites").description("List available test suites").option(
3151
3231
  []
3152
3232
  ).option("--match <mode>", "When multiple --tag flags: any (default) or all", "any").action((opts) => listSuites(opts));
3153
3233
  program.command("list-apps").description("List apps in your organization").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").action((opts) => listApps(opts));
3154
- program.command("list-devices").description("List devices registered with the platform (filterable by type and category)").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--type <type>", "Filter by device type: ios, android").option("--category <category>", "Filter by category: phone, tablet, tv").option("--status <status>", "Filter by status: available, busy, offline").action((opts) => listDevicesCommand(opts));
3155
- program.command("upload-app <file>").description("Upload an app build (zipped simulator .app for iOS, .apk for Android)").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--name <name>", "App name (auto-detected if not provided)").action((file, opts) => uploadApp(file, opts));
3234
+ program.command("list-devices").description("List devices registered with the platform (filterable by type and category)").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--type <type>", "Filter by device type: ios, android, roku").option("--category <category>", "Filter by category: phone, tablet, tv").option("--status <status>", "Filter by status: available, busy, offline").action((opts) => listDevicesCommand(opts));
3235
+ program.command("upload-app <file>").description(
3236
+ "Upload an app build (zipped simulator .app for iOS/tvOS, .apk for Android/Android TV, or a Roku channel .zip)"
3237
+ ).option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--name <name>", "App name (auto-detected if not provided)").action((file, opts) => uploadApp(file, opts));
3156
3238
  program.command("create-test [name]").description("Create a standalone AI test case").option("--token <token>", "API token").option("--output <format>", "Output format: human, json", "human").option("--app <app>", "App ID (public_id) or package name (requires --platform)").option("--platform <platform>", "Platform (ios, android) when using package name for --app").option("--steps <json>", "Test steps as inline JSON array").option("--steps-file <path>", "Path to JSON file containing test steps").option("--context <text>", "Additional context for AI test execution").option("--test-name <name>", "Test case name (alternative to positional arg)").action((name, opts) => createTest({ ...opts, testName: name || opts.testName || "CLI Test" }));
3157
3239
  program.command("login").description("Log in to VibeView (interactive)").option("--base-url <url>", "VibeView server URL (defaults to https://vibeview.io)").action((opts) => login(opts));
3158
3240
  program.command("logout").description("Revoke the saved login token and remove it from ~/.vibeview/config.json").action(() => logout());
@@ -3167,7 +3249,7 @@ program.command("dev").description(
3167
3249
  parseDeviceCategory
3168
3250
  ).option("--no-open", "Do not open the browser automatically").option("--debug", "Log raw tunnel lifecycle events (close codes, reconnects) for diagnostics").option("--json", "Emit line-delimited JSON events on stdout instead of human output", false).option(
3169
3251
  "--detach",
3170
- "Run the dev session in the background; prints session info and exits (see `dev-status`, `dev-stop`)",
3252
+ "Run the dev session in the background; prints session info and exits (see `dev-status`, `dev-stop`, and `dev-reload` for Roku)",
3171
3253
  false
3172
3254
  ).option(
3173
3255
  "--build",
@@ -3307,10 +3389,13 @@ webhooks.command("rm <id>").description("Remove a webhook endpoint (rotate the s
3307
3389
  webhooks.command("test <id>").description("Send a ping delivery and wait up to 60s for the result").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the final delivery result as one JSON line", false).action((id, opts) => webhooksTest(id, opts));
3308
3390
  program.command("dev-status").description("Show the status of a `vibeview dev --detach` session in this project").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devStatus(opts));
3309
3391
  program.command("dev-stop").description("Stop a `vibeview dev --detach` session in this project").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devStop(opts));
3392
+ program.command("dev-reload").description(
3393
+ "Roku only: re-package, upload and reload the channel into this project's `vibeview dev --detach` session (the `r` key of the foreground loop)"
3394
+ ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devReload(opts));
3310
3395
  program.command("mcp").description(
3311
- "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
3396
+ "Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
3312
3397
  ).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
3313
- const { runMcpServer } = await import("./server-WOIB6CDQ.js");
3398
+ const { runMcpServer } = await import("./server-6IJ44HAX.js");
3314
3399
  await runMcpServer(opts);
3315
3400
  });
3316
3401
  program.command("agent-setup").description(