vibeview 0.21.0 → 0.23.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/{chunk-VVKF7GGL.js → chunk-E2LZSLRF.js} +245 -58
- package/dist/{chunk-KON2FOEC.js → chunk-PSFXVUHG.js} +11 -0
- package/dist/cli.js +224 -141
- package/dist/{server-WWNWYD4Q.js → server-XMSBLIWY.js} +165 -12
- package/dist/{upload-app-W6NOSJPA.js → upload-app-ZAKAOJKE.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +39 -3
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,12 @@ import {
|
|
|
4
4
|
BUILD_TERMINAL_STATUSES,
|
|
5
5
|
buildCommand,
|
|
6
6
|
clearDevState,
|
|
7
|
+
createRokuPush,
|
|
7
8
|
dev,
|
|
8
9
|
fetchFinalSummary,
|
|
10
|
+
filterDeviceModels,
|
|
11
|
+
formatDeviceModelsHuman,
|
|
12
|
+
formatDevicesHuman,
|
|
9
13
|
formatDuration,
|
|
10
14
|
killOwnedMetro,
|
|
11
15
|
loadDevConfig,
|
|
@@ -15,7 +19,7 @@ import {
|
|
|
15
19
|
readDevState,
|
|
16
20
|
readMetroState,
|
|
17
21
|
runAgentVerb
|
|
18
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-E2LZSLRF.js";
|
|
19
23
|
import {
|
|
20
24
|
ApiClient,
|
|
21
25
|
EXIT_INFRA_ERROR,
|
|
@@ -28,7 +32,7 @@ import {
|
|
|
28
32
|
resolveAuth,
|
|
29
33
|
saveConfig,
|
|
30
34
|
uploadApp
|
|
31
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-PSFXVUHG.js";
|
|
32
36
|
|
|
33
37
|
// src/cli.ts
|
|
34
38
|
import { createRequire } from "module";
|
|
@@ -1143,23 +1147,102 @@ async function devStop(opts) {
|
|
|
1143
1147
|
process.exit(0);
|
|
1144
1148
|
}
|
|
1145
1149
|
|
|
1150
|
+
// src/commands/dev-reload.ts
|
|
1151
|
+
import chalk10 from "chalk";
|
|
1152
|
+
function fail(opts, code, message, exit) {
|
|
1153
|
+
if (opts.json) {
|
|
1154
|
+
console.log(JSON.stringify({ reloaded: false, code, message }));
|
|
1155
|
+
} else {
|
|
1156
|
+
console.error(chalk10.red(`error: ${message}`));
|
|
1157
|
+
}
|
|
1158
|
+
process.exit(exit);
|
|
1159
|
+
}
|
|
1160
|
+
async function devReload(opts) {
|
|
1161
|
+
const cwd = process.cwd();
|
|
1162
|
+
const state = readDevState(cwd);
|
|
1163
|
+
if (!state) {
|
|
1164
|
+
fail(
|
|
1165
|
+
opts,
|
|
1166
|
+
"no_dev_session",
|
|
1167
|
+
"No dev session running (or it was not started with --detach).",
|
|
1168
|
+
1
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
if (state.platform !== "roku") {
|
|
1172
|
+
fail(
|
|
1173
|
+
opts,
|
|
1174
|
+
"not_roku",
|
|
1175
|
+
`dev-reload is for Roku channels; a ${state.platform} session hot-reloads through Metro on its own.`,
|
|
1176
|
+
2
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
let build;
|
|
1180
|
+
try {
|
|
1181
|
+
build = loadDevConfig(cwd)?.platforms?.roku?.build;
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
fail(
|
|
1184
|
+
opts,
|
|
1185
|
+
"config_required",
|
|
1186
|
+
`vibeview.json could not be read: ${err instanceof Error ? err.message : String(err)}`,
|
|
1187
|
+
1
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
if (!build) {
|
|
1191
|
+
fail(
|
|
1192
|
+
opts,
|
|
1193
|
+
"config_required",
|
|
1194
|
+
"no build config for roku \u2014 run `vibeview build --platform roku` once to set it up",
|
|
1195
|
+
1
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
1199
|
+
const api = new ApiClient(baseUrl, token);
|
|
1200
|
+
const push = createRokuPush({
|
|
1201
|
+
cwd,
|
|
1202
|
+
sessionId: state.session_id,
|
|
1203
|
+
build,
|
|
1204
|
+
token,
|
|
1205
|
+
baseUrl,
|
|
1206
|
+
api,
|
|
1207
|
+
// Build output and the "reloaded in" line are human output: stderr, so
|
|
1208
|
+
// stdout stays reserved for the --json result.
|
|
1209
|
+
log: (line) => console.error(chalk10.dim(line))
|
|
1210
|
+
});
|
|
1211
|
+
const started = Date.now();
|
|
1212
|
+
try {
|
|
1213
|
+
await push();
|
|
1214
|
+
} catch (err) {
|
|
1215
|
+
fail(opts, "reload_failed", err instanceof Error ? err.message : String(err), 1);
|
|
1216
|
+
}
|
|
1217
|
+
if (opts.json) {
|
|
1218
|
+
console.log(
|
|
1219
|
+
JSON.stringify({
|
|
1220
|
+
reloaded: true,
|
|
1221
|
+
session_id: state.session_id,
|
|
1222
|
+
seconds: (Date.now() - started) / 1e3
|
|
1223
|
+
})
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
process.exit(0);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1146
1229
|
// src/commands/builds.ts
|
|
1147
|
-
import
|
|
1230
|
+
import chalk12 from "chalk";
|
|
1148
1231
|
|
|
1149
1232
|
// src/lib/formatters/builds.ts
|
|
1150
|
-
import
|
|
1233
|
+
import chalk11 from "chalk";
|
|
1151
1234
|
var HEADERS = ["ID", "PLATFORM", "STATUS", "CREATED", "COST", "APP"];
|
|
1152
1235
|
function formatCostCell(costMicro) {
|
|
1153
1236
|
return costMicro > 0 ? `$${(costMicro / 1e6).toFixed(2)}` : "";
|
|
1154
1237
|
}
|
|
1155
1238
|
var STATUS_COLOR = {
|
|
1156
|
-
succeeded:
|
|
1157
|
-
failed:
|
|
1158
|
-
timed_out:
|
|
1159
|
-
infra_failed:
|
|
1160
|
-
canceled:
|
|
1161
|
-
running:
|
|
1162
|
-
queued:
|
|
1239
|
+
succeeded: chalk11.green,
|
|
1240
|
+
failed: chalk11.red,
|
|
1241
|
+
timed_out: chalk11.red,
|
|
1242
|
+
infra_failed: chalk11.red,
|
|
1243
|
+
canceled: chalk11.dim,
|
|
1244
|
+
running: chalk11.yellow,
|
|
1245
|
+
queued: chalk11.dim
|
|
1163
1246
|
};
|
|
1164
1247
|
function formatBuildsHuman(builds2) {
|
|
1165
1248
|
if (builds2.length === 0) {
|
|
@@ -1176,7 +1259,7 @@ function formatBuildsHuman(builds2) {
|
|
|
1176
1259
|
const widths = HEADERS.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1177
1260
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1178
1261
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
1179
|
-
const lines = [
|
|
1262
|
+
const lines = [chalk11.bold(join3(padCells(HEADERS)))];
|
|
1180
1263
|
for (const row of rows) {
|
|
1181
1264
|
const padded = padCells(row);
|
|
1182
1265
|
const colorFn = STATUS_COLOR[row[2]] ?? ((s) => s);
|
|
@@ -1235,7 +1318,7 @@ async function buildsLogs(id, opts, deps) {
|
|
|
1235
1318
|
console.log(JSON.stringify(logs));
|
|
1236
1319
|
} else {
|
|
1237
1320
|
if (logs.content) process.stdout.write(logs.content);
|
|
1238
|
-
console.error(
|
|
1321
|
+
console.error(chalk12.dim(`status: ${logs.status}`));
|
|
1239
1322
|
}
|
|
1240
1323
|
process.exitCode = 0;
|
|
1241
1324
|
return;
|
|
@@ -1332,7 +1415,7 @@ async function buildsDownload(id, opts, deps) {
|
|
|
1332
1415
|
if (opts.json) {
|
|
1333
1416
|
console.log(JSON.stringify(result));
|
|
1334
1417
|
} else {
|
|
1335
|
-
console.log(
|
|
1418
|
+
console.log(chalk12.green(`\u2713 saved ${result.path}`));
|
|
1336
1419
|
}
|
|
1337
1420
|
process.exitCode = 0;
|
|
1338
1421
|
} catch (err) {
|
|
@@ -1349,7 +1432,7 @@ async function buildsCancel(id, opts, deps) {
|
|
|
1349
1432
|
if (opts.json) {
|
|
1350
1433
|
console.log(JSON.stringify(build));
|
|
1351
1434
|
} else {
|
|
1352
|
-
console.log(`${
|
|
1435
|
+
console.log(`${chalk12.cyan(build.id)} ${build.status}`);
|
|
1353
1436
|
}
|
|
1354
1437
|
process.exitCode = 0;
|
|
1355
1438
|
} catch (err) {
|
|
@@ -1366,7 +1449,7 @@ async function buildsDeleteArtifact(id, opts, deps) {
|
|
|
1366
1449
|
if (opts.json) {
|
|
1367
1450
|
console.log(JSON.stringify(result));
|
|
1368
1451
|
} else {
|
|
1369
|
-
console.log(
|
|
1452
|
+
console.log(chalk12.green(`\u2713 deleted artifact for ${id}`));
|
|
1370
1453
|
}
|
|
1371
1454
|
process.exitCode = 0;
|
|
1372
1455
|
} catch (err) {
|
|
@@ -1377,11 +1460,11 @@ async function buildsDeleteArtifact(id, opts, deps) {
|
|
|
1377
1460
|
function printError(opts, err) {
|
|
1378
1461
|
const message = err instanceof Error ? err.message : String(err);
|
|
1379
1462
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1380
|
-
console.error(
|
|
1463
|
+
console.error(chalk12.red(`error: ${message}`));
|
|
1381
1464
|
}
|
|
1382
1465
|
|
|
1383
1466
|
// src/commands/submit.ts
|
|
1384
|
-
import
|
|
1467
|
+
import chalk13 from "chalk";
|
|
1385
1468
|
function defaultDeps2(client) {
|
|
1386
1469
|
return {
|
|
1387
1470
|
createSubmission: client.createSubmission.bind(client),
|
|
@@ -1404,7 +1487,7 @@ function submitConfigFor(cwd, platform) {
|
|
|
1404
1487
|
cfg = loadDevConfig(cwd);
|
|
1405
1488
|
} catch (err) {
|
|
1406
1489
|
console.error(
|
|
1407
|
-
|
|
1490
|
+
chalk13.yellow(
|
|
1408
1491
|
`warning: vibeview.json could not be read \u2014 ignoring submit defaults (${err instanceof Error ? err.message : String(err)})`
|
|
1409
1492
|
)
|
|
1410
1493
|
);
|
|
@@ -1552,12 +1635,12 @@ async function submitCommand(opts, deps) {
|
|
|
1552
1635
|
return;
|
|
1553
1636
|
}
|
|
1554
1637
|
if (!opts.json) {
|
|
1555
|
-
console.error(` ${
|
|
1638
|
+
console.error(` ${chalk13.cyan(sub.id)} ${chalk13.dim(`[${sub.platform}]`)}`);
|
|
1556
1639
|
}
|
|
1557
1640
|
const final = await followSubmission(sub, opts, d);
|
|
1558
1641
|
if (!opts.json && final.status === "succeeded" && (final.release_status ?? releaseStatus) === "draft") {
|
|
1559
1642
|
console.error(
|
|
1560
|
-
|
|
1643
|
+
chalk13.dim("release status: draft \u2014 activate it from the Play Console when ready")
|
|
1561
1644
|
);
|
|
1562
1645
|
}
|
|
1563
1646
|
process.exitCode = final.status === "succeeded" ? 0 : 1;
|
|
@@ -1581,11 +1664,11 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
1581
1664
|
});
|
|
1582
1665
|
emit(opts.json, { event: "preflight", ...result });
|
|
1583
1666
|
if (!opts.json) {
|
|
1584
|
-
const mark = result.ready ?
|
|
1667
|
+
const mark = result.ready ? chalk13.green("\u2713") : chalk13.red("\u2717");
|
|
1585
1668
|
console.error(`${mark} ${result.message}`);
|
|
1586
1669
|
if (result.ready) {
|
|
1587
1670
|
console.error(
|
|
1588
|
-
|
|
1671
|
+
chalk13.dim(
|
|
1589
1672
|
` next: vibeview build --cloud --production --platform ${platform}, then vibeview submit --platform ${platform} --latest`
|
|
1590
1673
|
)
|
|
1591
1674
|
);
|
|
@@ -1634,14 +1717,14 @@ async function followSubmission(created, opts, deps) {
|
|
|
1634
1717
|
if (opts.json) {
|
|
1635
1718
|
emit(opts.json, { event: "status", id: created.id, status: "queued" });
|
|
1636
1719
|
} else {
|
|
1637
|
-
console.error(
|
|
1720
|
+
console.error(chalk13.dim("\u23F3 queued \u2014 waiting for a submission slot"));
|
|
1638
1721
|
}
|
|
1639
1722
|
} else if (log.status === "running" && !printedRunning) {
|
|
1640
1723
|
printedRunning = true;
|
|
1641
1724
|
if (opts.json) {
|
|
1642
1725
|
emit(opts.json, { event: "status", id: created.id, status: "running" });
|
|
1643
1726
|
} else {
|
|
1644
|
-
console.error(`${
|
|
1727
|
+
console.error(`${chalk13.cyan("\u25B6")} submitting`);
|
|
1645
1728
|
}
|
|
1646
1729
|
}
|
|
1647
1730
|
if (log.content) {
|
|
@@ -1683,7 +1766,7 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
|
|
|
1683
1766
|
});
|
|
1684
1767
|
if (!opts.json) {
|
|
1685
1768
|
const ok = final.status === "succeeded";
|
|
1686
|
-
const mark = ok ?
|
|
1769
|
+
const mark = ok ? chalk13.green("\u2713") : chalk13.red("\u2717");
|
|
1687
1770
|
const duration = formatDuration(final.started_at, final.finished_at);
|
|
1688
1771
|
let line = `${mark} ${final.status}`;
|
|
1689
1772
|
if (duration) line += ` (${duration})`;
|
|
@@ -1695,11 +1778,11 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
|
|
|
1695
1778
|
function printError2(opts, err) {
|
|
1696
1779
|
const message = err instanceof Error ? err.message : String(err);
|
|
1697
1780
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1698
|
-
console.error(
|
|
1781
|
+
console.error(chalk13.red(`error: ${message}`));
|
|
1699
1782
|
}
|
|
1700
1783
|
|
|
1701
1784
|
// src/commands/versions.ts
|
|
1702
|
-
import
|
|
1785
|
+
import chalk14 from "chalk";
|
|
1703
1786
|
function defaultDeps3(client) {
|
|
1704
1787
|
return {
|
|
1705
1788
|
listAppVersions: client.listAppVersions.bind(client),
|
|
@@ -1749,7 +1832,7 @@ async function versionsSet(buildNumber, opts, deps) {
|
|
|
1749
1832
|
console.log(JSON.stringify(version));
|
|
1750
1833
|
} else {
|
|
1751
1834
|
console.log(
|
|
1752
|
-
|
|
1835
|
+
chalk14.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk14.dim(
|
|
1753
1836
|
" \u2014 the next autoIncrement production build gets " + (version.build_number + 1)
|
|
1754
1837
|
)
|
|
1755
1838
|
);
|
|
@@ -1769,18 +1852,18 @@ function formatVersionsHuman(versions2) {
|
|
|
1769
1852
|
const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1770
1853
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1771
1854
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
1772
|
-
const lines = [
|
|
1855
|
+
const lines = [chalk14.bold(join3(padCells(HEADERS2)))];
|
|
1773
1856
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
1774
1857
|
return lines.join("\n") + "\n";
|
|
1775
1858
|
}
|
|
1776
1859
|
function printError3(opts, err) {
|
|
1777
1860
|
const message = err instanceof Error ? err.message : String(err);
|
|
1778
1861
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1779
|
-
console.error(
|
|
1862
|
+
console.error(chalk14.red(`error: ${message}`));
|
|
1780
1863
|
}
|
|
1781
1864
|
|
|
1782
1865
|
// src/commands/whoami.ts
|
|
1783
|
-
import
|
|
1866
|
+
import chalk15 from "chalk";
|
|
1784
1867
|
function defaultDeps4(opts) {
|
|
1785
1868
|
const { token, baseUrl } = resolveAuth(opts);
|
|
1786
1869
|
const client = new ApiClient(baseUrl, token);
|
|
@@ -1802,27 +1885,27 @@ async function whoAmI(opts, deps) {
|
|
|
1802
1885
|
process.exitCode = 0;
|
|
1803
1886
|
return;
|
|
1804
1887
|
}
|
|
1805
|
-
console.log(`${
|
|
1888
|
+
console.log(`${chalk15.bold("Logged in as")} ${me.email}`);
|
|
1806
1889
|
if (me.memberships.length === 1) {
|
|
1807
|
-
console.log(`${
|
|
1890
|
+
console.log(`${chalk15.bold("Organization")} ${me.memberships[0].organization_name}`);
|
|
1808
1891
|
} else if (me.memberships.length > 1) {
|
|
1809
|
-
console.log(
|
|
1892
|
+
console.log(chalk15.bold("Organizations"));
|
|
1810
1893
|
for (const m of me.memberships) {
|
|
1811
1894
|
console.log(` ${m.organization_name} (${m.role})`);
|
|
1812
1895
|
}
|
|
1813
1896
|
}
|
|
1814
|
-
console.log(`${
|
|
1815
|
-
console.log(`${
|
|
1816
|
-
console.log(`${
|
|
1897
|
+
console.log(`${chalk15.bold("Member role")} ${me.role ?? "\u2014"}`);
|
|
1898
|
+
console.log(`${chalk15.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
|
|
1899
|
+
console.log(`${chalk15.bold("Effective role")} ${effective ?? "\u2014"}`);
|
|
1817
1900
|
if (me.is_superadmin) {
|
|
1818
|
-
console.log(
|
|
1901
|
+
console.log(chalk15.dim("Superadmin (platform-wide; does not raise the org role above)"));
|
|
1819
1902
|
}
|
|
1820
1903
|
if (me.role === "admin" && me.token_role && me.token_role !== "admin") {
|
|
1821
1904
|
console.log(
|
|
1822
|
-
|
|
1905
|
+
chalk15.yellow(
|
|
1823
1906
|
`
|
|
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 ${
|
|
1825
|
-
Create an admin API token in the dashboard and use ${
|
|
1907
|
+
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.
|
|
1908
|
+
Create an admin API token in the dashboard and use ${chalk15.bold("--token")} or ${chalk15.bold("VIBEVIEW_API_TOKEN")}.`
|
|
1826
1909
|
)
|
|
1827
1910
|
);
|
|
1828
1911
|
}
|
|
@@ -1832,7 +1915,7 @@ Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or
|
|
|
1832
1915
|
if (opts.json) {
|
|
1833
1916
|
console.log(JSON.stringify({ event: "error", message }));
|
|
1834
1917
|
}
|
|
1835
|
-
console.error(
|
|
1918
|
+
console.error(chalk15.red(`error: ${message}`));
|
|
1836
1919
|
process.exitCode = 1;
|
|
1837
1920
|
}
|
|
1838
1921
|
}
|
|
@@ -1840,7 +1923,7 @@ Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or
|
|
|
1840
1923
|
// src/commands/env.ts
|
|
1841
1924
|
import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
|
|
1842
1925
|
import { basename } from "path";
|
|
1843
|
-
import
|
|
1926
|
+
import chalk16 from "chalk";
|
|
1844
1927
|
import { password } from "@inquirer/prompts";
|
|
1845
1928
|
|
|
1846
1929
|
// src/lib/gitignore.ts
|
|
@@ -1896,7 +1979,7 @@ function defaultDeps5(client) {
|
|
|
1896
1979
|
isIgnored: (path) => isGitIgnored(path),
|
|
1897
1980
|
writeFile: writeSecureFile,
|
|
1898
1981
|
readFile: (path) => readFileAsync(path),
|
|
1899
|
-
warn: (message) => console.error(
|
|
1982
|
+
warn: (message) => console.error(chalk16.yellow(`warning: ${message}`))
|
|
1900
1983
|
};
|
|
1901
1984
|
}
|
|
1902
1985
|
async function envList(opts, deps) {
|
|
@@ -1918,17 +2001,17 @@ function formatEnvVarsHuman(variables) {
|
|
|
1918
2001
|
if (variables.length === 0) {
|
|
1919
2002
|
return "No build variables found.\n";
|
|
1920
2003
|
}
|
|
1921
|
-
const
|
|
2004
|
+
const HEADERS3 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
|
|
1922
2005
|
const rows = variables.map((v) => [
|
|
1923
2006
|
v.name,
|
|
1924
2007
|
v.is_file ? `${v.visibility} (file)` : v.visibility,
|
|
1925
2008
|
v.app_public_id ?? "org-wide",
|
|
1926
2009
|
v.value_hint ?? ""
|
|
1927
2010
|
]);
|
|
1928
|
-
const widths =
|
|
2011
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1929
2012
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1930
2013
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
1931
|
-
const lines = [
|
|
2014
|
+
const lines = [chalk16.bold(join3(padCells(HEADERS3)))];
|
|
1932
2015
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
1933
2016
|
return lines.join("\n") + "\n";
|
|
1934
2017
|
}
|
|
@@ -1978,7 +2061,7 @@ function printSetResult(opts, result) {
|
|
|
1978
2061
|
if (opts.json) {
|
|
1979
2062
|
console.log(JSON.stringify(result));
|
|
1980
2063
|
} else {
|
|
1981
|
-
console.log(
|
|
2064
|
+
console.log(chalk16.green(`\u2713 set ${result.name} (${result.visibility})`));
|
|
1982
2065
|
}
|
|
1983
2066
|
}
|
|
1984
2067
|
async function envRm(name, opts, deps) {
|
|
@@ -1988,7 +2071,7 @@ async function envRm(name, opts, deps) {
|
|
|
1988
2071
|
if (opts.json) {
|
|
1989
2072
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
1990
2073
|
} else {
|
|
1991
|
-
console.log(
|
|
2074
|
+
console.log(chalk16.green(`\u2713 removed ${name}`));
|
|
1992
2075
|
}
|
|
1993
2076
|
process.exitCode = 0;
|
|
1994
2077
|
} catch (err) {
|
|
@@ -2020,7 +2103,7 @@ async function envPull(opts, deps) {
|
|
|
2020
2103
|
if (opts.json) {
|
|
2021
2104
|
console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
|
|
2022
2105
|
} else {
|
|
2023
|
-
console.log(
|
|
2106
|
+
console.log(chalk16.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
|
|
2024
2107
|
}
|
|
2025
2108
|
process.exitCode = 0;
|
|
2026
2109
|
} catch (err) {
|
|
@@ -2059,7 +2142,7 @@ function escapeEnvValue(value) {
|
|
|
2059
2142
|
function printError4(opts, err) {
|
|
2060
2143
|
const message = err instanceof Error ? err.message : String(err);
|
|
2061
2144
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2062
|
-
console.error(
|
|
2145
|
+
console.error(chalk16.red(`error: ${message}`));
|
|
2063
2146
|
}
|
|
2064
2147
|
|
|
2065
2148
|
// src/commands/credentials.ts
|
|
@@ -2071,7 +2154,7 @@ import {
|
|
|
2071
2154
|
} from "fs/promises";
|
|
2072
2155
|
import { mkdirSync } from "fs";
|
|
2073
2156
|
import { basename as basename2, extname, join } from "path";
|
|
2074
|
-
import
|
|
2157
|
+
import chalk17 from "chalk";
|
|
2075
2158
|
import { input, password as password2 } from "@inquirer/prompts";
|
|
2076
2159
|
function clientFor2(opts) {
|
|
2077
2160
|
const { token, baseUrl } = resolveAuth(opts);
|
|
@@ -2144,7 +2227,7 @@ function defaultDeps6(client) {
|
|
|
2144
2227
|
mkdir: (dir) => mkdirSync(dir, { recursive: true }),
|
|
2145
2228
|
writeFile: (path, data, mode) => writeFileAsync(path, data, mode !== void 0 ? { mode } : void 0),
|
|
2146
2229
|
chmod: (path, mode) => chmodAsync(path, mode),
|
|
2147
|
-
warn: (message) => console.error(
|
|
2230
|
+
warn: (message) => console.error(chalk17.yellow(`warning: ${message}`))
|
|
2148
2231
|
};
|
|
2149
2232
|
}
|
|
2150
2233
|
var ANDROID_KIND = "android_keystore";
|
|
@@ -2213,7 +2296,7 @@ async function credentialsAdd(opts, deps) {
|
|
|
2213
2296
|
if (opts.json) {
|
|
2214
2297
|
console.log(JSON.stringify(result));
|
|
2215
2298
|
} else {
|
|
2216
|
-
console.log(
|
|
2299
|
+
console.log(chalk17.green(`\u2713 added ${result.name} (${result.platform})`));
|
|
2217
2300
|
}
|
|
2218
2301
|
process.exitCode = 0;
|
|
2219
2302
|
} catch (err) {
|
|
@@ -2271,20 +2354,20 @@ async function credentialsGenerate(opts, deps) {
|
|
|
2271
2354
|
}
|
|
2272
2355
|
if (opts.json) {
|
|
2273
2356
|
} else {
|
|
2274
|
-
console.log(
|
|
2357
|
+
console.log(chalk17.green(`\u2713 generated ${result.name} (android)`));
|
|
2275
2358
|
console.log("");
|
|
2276
|
-
console.log(` Keystore saved to: ${
|
|
2277
|
-
console.log(` Key alias: ${
|
|
2278
|
-
console.log(` Store password: ${
|
|
2279
|
-
console.log(` Key password: ${
|
|
2359
|
+
console.log(` Keystore saved to: ${chalk17.bold(savedTo)}`);
|
|
2360
|
+
console.log(` Key alias: ${chalk17.bold(result.key_alias)}`);
|
|
2361
|
+
console.log(` Store password: ${chalk17.bold(result.store_password)}`);
|
|
2362
|
+
console.log(` Key password: ${chalk17.bold(result.key_password)}`);
|
|
2280
2363
|
console.log("");
|
|
2281
2364
|
console.log(
|
|
2282
|
-
|
|
2365
|
+
chalk17.yellow(
|
|
2283
2366
|
" These are shown ONCE \u2014 VibeView cannot recover the keystore file or its passwords. Back them up somewhere safe."
|
|
2284
2367
|
)
|
|
2285
2368
|
);
|
|
2286
2369
|
console.log(
|
|
2287
|
-
|
|
2370
|
+
chalk17.yellow(
|
|
2288
2371
|
" The credential is already uploaded and active for android and androidtv production builds."
|
|
2289
2372
|
)
|
|
2290
2373
|
);
|
|
@@ -2310,19 +2393,19 @@ async function generateAppleCredential(platform, opts, d) {
|
|
|
2310
2393
|
if (opts.json) {
|
|
2311
2394
|
console.log(JSON.stringify(result));
|
|
2312
2395
|
} else {
|
|
2313
|
-
console.log(
|
|
2396
|
+
console.log(chalk17.green(`\u2713 generated ${result.name} (${result.platform})`));
|
|
2314
2397
|
console.log("");
|
|
2315
|
-
console.log(` Scope: ${
|
|
2398
|
+
console.log(` Scope: ${chalk17.bold(result.app_public_id ?? "org-wide")}`);
|
|
2316
2399
|
const expires = formatExpires(result);
|
|
2317
2400
|
if (expires) {
|
|
2318
|
-
console.log(` Expires: ${
|
|
2401
|
+
console.log(` Expires: ${chalk17.bold(expires)}`);
|
|
2319
2402
|
}
|
|
2320
2403
|
console.log("");
|
|
2321
2404
|
console.log(
|
|
2322
2405
|
` The certificate and provisioning profile are stored with VibeView and already active for ${result.platform} production builds.`
|
|
2323
2406
|
);
|
|
2324
2407
|
console.log(
|
|
2325
|
-
` Run ${
|
|
2408
|
+
` Run ${chalk17.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
|
|
2326
2409
|
);
|
|
2327
2410
|
}
|
|
2328
2411
|
process.exitCode = 0;
|
|
@@ -2388,17 +2471,17 @@ async function credentialsDownload(name, opts, deps) {
|
|
|
2388
2471
|
await d.chmod(path, 384);
|
|
2389
2472
|
}
|
|
2390
2473
|
if (!opts.json) {
|
|
2391
|
-
console.log(
|
|
2474
|
+
console.log(chalk17.green(`\u2713 downloaded ${material.name} (${material.platform})`));
|
|
2392
2475
|
console.log("");
|
|
2393
|
-
console.log(` Saved to: ${
|
|
2476
|
+
console.log(` Saved to: ${chalk17.bold(savedTo)}`);
|
|
2394
2477
|
if (profileSavedTo !== null) {
|
|
2395
|
-
console.log(` Profile: ${
|
|
2478
|
+
console.log(` Profile: ${chalk17.bold(profileSavedTo)}`);
|
|
2396
2479
|
}
|
|
2397
2480
|
for (const [key, value] of Object.entries(material.passwords)) {
|
|
2398
|
-
console.log(` ${key}: ${
|
|
2481
|
+
console.log(` ${key}: ${chalk17.bold(value)}`);
|
|
2399
2482
|
}
|
|
2400
2483
|
console.log("");
|
|
2401
|
-
console.log(
|
|
2484
|
+
console.log(chalk17.yellow(" This download is recorded in your organization's audit log."));
|
|
2402
2485
|
}
|
|
2403
2486
|
process.exitCode = 0;
|
|
2404
2487
|
} catch (err) {
|
|
@@ -2429,13 +2512,13 @@ async function ascKeyAdd(opts, deps) {
|
|
|
2429
2512
|
if (opts.json) {
|
|
2430
2513
|
console.log(JSON.stringify(result));
|
|
2431
2514
|
} else {
|
|
2432
|
-
console.log(
|
|
2515
|
+
console.log(chalk17.green(`\u2713 added App Store Connect key ${result.name}`));
|
|
2433
2516
|
console.log("");
|
|
2434
|
-
console.log(` Key ID: ${
|
|
2435
|
-
console.log(` Issuer ID: ${
|
|
2517
|
+
console.log(` Key ID: ${chalk17.bold(result.key_id)}`);
|
|
2518
|
+
console.log(` Issuer ID: ${chalk17.bold(result.issuer_id)}`);
|
|
2436
2519
|
console.log("");
|
|
2437
2520
|
console.log(
|
|
2438
|
-
|
|
2521
|
+
chalk17.yellow(
|
|
2439
2522
|
" The .p8 is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
2440
2523
|
)
|
|
2441
2524
|
);
|
|
@@ -2465,12 +2548,12 @@ function formatAscKeysHuman(keys) {
|
|
|
2465
2548
|
if (keys.length === 0) {
|
|
2466
2549
|
return "No App Store Connect keys found.\n";
|
|
2467
2550
|
}
|
|
2468
|
-
const
|
|
2551
|
+
const HEADERS3 = ["NAME", "KEY ID", "ISSUER ID", "CREATED"];
|
|
2469
2552
|
const rows = keys.map((k) => [k.name, k.key_id, k.issuer_id, k.created_at ?? ""]);
|
|
2470
|
-
const widths =
|
|
2553
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2471
2554
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2472
2555
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2473
|
-
const lines = [
|
|
2556
|
+
const lines = [chalk17.bold(join3(padCells(HEADERS3)))];
|
|
2474
2557
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2475
2558
|
return lines.join("\n") + "\n";
|
|
2476
2559
|
}
|
|
@@ -2481,7 +2564,7 @@ async function ascKeyRm(name, opts, deps) {
|
|
|
2481
2564
|
if (opts.json) {
|
|
2482
2565
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2483
2566
|
} else {
|
|
2484
|
-
console.log(
|
|
2567
|
+
console.log(chalk17.green(`\u2713 removed App Store Connect key ${name}`));
|
|
2485
2568
|
}
|
|
2486
2569
|
process.exitCode = 0;
|
|
2487
2570
|
} catch (err) {
|
|
@@ -2506,12 +2589,12 @@ async function playKeyAdd(opts, deps) {
|
|
|
2506
2589
|
if (opts.json) {
|
|
2507
2590
|
console.log(JSON.stringify(result));
|
|
2508
2591
|
} else {
|
|
2509
|
-
console.log(
|
|
2592
|
+
console.log(chalk17.green(`\u2713 added Google Play key ${result.name}`));
|
|
2510
2593
|
console.log("");
|
|
2511
|
-
console.log(` Service account: ${
|
|
2594
|
+
console.log(` Service account: ${chalk17.bold(result.client_email)}`);
|
|
2512
2595
|
console.log("");
|
|
2513
2596
|
console.log(
|
|
2514
|
-
|
|
2597
|
+
chalk17.yellow(
|
|
2515
2598
|
" The JSON key is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
2516
2599
|
)
|
|
2517
2600
|
);
|
|
@@ -2541,12 +2624,12 @@ function formatPlayKeysHuman(keys) {
|
|
|
2541
2624
|
if (keys.length === 0) {
|
|
2542
2625
|
return "No Google Play keys found.\n";
|
|
2543
2626
|
}
|
|
2544
|
-
const
|
|
2627
|
+
const HEADERS3 = ["NAME", "CLIENT EMAIL", "CREATED"];
|
|
2545
2628
|
const rows = keys.map((k) => [k.name, k.client_email, k.created_at ?? ""]);
|
|
2546
|
-
const widths =
|
|
2629
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2547
2630
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2548
2631
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2549
|
-
const lines = [
|
|
2632
|
+
const lines = [chalk17.bold(join3(padCells(HEADERS3)))];
|
|
2550
2633
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2551
2634
|
return lines.join("\n") + "\n";
|
|
2552
2635
|
}
|
|
@@ -2557,7 +2640,7 @@ async function playKeyRm(name, opts, deps) {
|
|
|
2557
2640
|
if (opts.json) {
|
|
2558
2641
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2559
2642
|
} else {
|
|
2560
|
-
console.log(
|
|
2643
|
+
console.log(chalk17.green(`\u2713 removed Google Play key ${name}`));
|
|
2561
2644
|
}
|
|
2562
2645
|
process.exitCode = 0;
|
|
2563
2646
|
} catch (err) {
|
|
@@ -2615,7 +2698,7 @@ function formatCredentialsHuman(credentials2) {
|
|
|
2615
2698
|
if (credentials2.length === 0) {
|
|
2616
2699
|
return "No signing credentials found.\n";
|
|
2617
2700
|
}
|
|
2618
|
-
const
|
|
2701
|
+
const HEADERS3 = ["NAME", "PLATFORM", "SCOPE", "IDENTITY", "EXPIRES"];
|
|
2619
2702
|
const rows = credentials2.map((c) => [
|
|
2620
2703
|
c.name,
|
|
2621
2704
|
c.platform,
|
|
@@ -2623,10 +2706,10 @@ function formatCredentialsHuman(credentials2) {
|
|
|
2623
2706
|
formatIdentity(c),
|
|
2624
2707
|
formatExpires(c)
|
|
2625
2708
|
]);
|
|
2626
|
-
const widths =
|
|
2709
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2627
2710
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2628
2711
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2629
|
-
const lines = [
|
|
2712
|
+
const lines = [chalk17.bold(join3(padCells(HEADERS3)))];
|
|
2630
2713
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2631
2714
|
return lines.join("\n") + "\n";
|
|
2632
2715
|
}
|
|
@@ -2637,7 +2720,7 @@ async function credentialsRm(name, opts, deps) {
|
|
|
2637
2720
|
if (opts.json) {
|
|
2638
2721
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2639
2722
|
} else {
|
|
2640
|
-
console.log(
|
|
2723
|
+
console.log(chalk17.green(`\u2713 removed ${name}`));
|
|
2641
2724
|
}
|
|
2642
2725
|
process.exitCode = 0;
|
|
2643
2726
|
} catch (err) {
|
|
@@ -2649,16 +2732,16 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
2649
2732
|
const message = err instanceof Error ? err.message : String(err);
|
|
2650
2733
|
if (opts.json) {
|
|
2651
2734
|
console.log(JSON.stringify({ event: "error", message }));
|
|
2652
|
-
console.error(
|
|
2735
|
+
console.error(chalk17.red(`error: ${message}`));
|
|
2653
2736
|
return;
|
|
2654
2737
|
}
|
|
2655
|
-
console.error(
|
|
2738
|
+
console.error(chalk17.red(`error: ${message}`));
|
|
2656
2739
|
console.error(
|
|
2657
|
-
|
|
2740
|
+
chalk17.red(
|
|
2658
2741
|
`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
2742
|
)
|
|
2660
2743
|
);
|
|
2661
|
-
console.error(
|
|
2744
|
+
console.error(chalk17.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
|
|
2662
2745
|
console.error("");
|
|
2663
2746
|
console.error(` Key alias: ${result.key_alias}`);
|
|
2664
2747
|
console.error(` Store password: ${result.store_password}`);
|
|
@@ -2672,11 +2755,11 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
2672
2755
|
function printError5(opts, err) {
|
|
2673
2756
|
const message = err instanceof Error ? err.message : String(err);
|
|
2674
2757
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2675
|
-
console.error(
|
|
2758
|
+
console.error(chalk17.red(`error: ${message}`));
|
|
2676
2759
|
}
|
|
2677
2760
|
|
|
2678
2761
|
// src/commands/webhooks.ts
|
|
2679
|
-
import
|
|
2762
|
+
import chalk18 from "chalk";
|
|
2680
2763
|
function clientFor3(opts) {
|
|
2681
2764
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2682
2765
|
return new ApiClient(baseUrl, token);
|
|
@@ -2707,14 +2790,14 @@ async function webhooksAdd(opts, deps) {
|
|
|
2707
2790
|
if (opts.json) {
|
|
2708
2791
|
console.log(JSON.stringify(result));
|
|
2709
2792
|
} else {
|
|
2710
|
-
console.log(
|
|
2793
|
+
console.log(chalk18.green(`\u2713 added ${result.id} ${result.url}`));
|
|
2711
2794
|
console.log("");
|
|
2712
|
-
console.log(` Signing secret: ${
|
|
2795
|
+
console.log(` Signing secret: ${chalk18.bold(result.secret)}`);
|
|
2713
2796
|
if (opts.secret !== void 0) {
|
|
2714
|
-
console.log(
|
|
2797
|
+
console.log(chalk18.yellow(" (This is the secret you provided.)"));
|
|
2715
2798
|
}
|
|
2716
2799
|
console.log(
|
|
2717
|
-
|
|
2800
|
+
chalk18.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
|
|
2718
2801
|
);
|
|
2719
2802
|
}
|
|
2720
2803
|
process.exitCode = 0;
|
|
@@ -2734,7 +2817,7 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
2734
2817
|
if (webhooks2.length === 0) {
|
|
2735
2818
|
return "No webhooks found.\n";
|
|
2736
2819
|
}
|
|
2737
|
-
const
|
|
2820
|
+
const HEADERS3 = ["ID", "URL", "ACTIVE", "LAST DELIVERY", "CREATED"];
|
|
2738
2821
|
const rows = webhooks2.map((w) => [
|
|
2739
2822
|
w.id,
|
|
2740
2823
|
w.url,
|
|
@@ -2742,10 +2825,10 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
2742
2825
|
formatLastDelivery(w.last_delivery),
|
|
2743
2826
|
w.created_at
|
|
2744
2827
|
]);
|
|
2745
|
-
const widths =
|
|
2828
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2746
2829
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2747
2830
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2748
|
-
const lines = [
|
|
2831
|
+
const lines = [chalk18.bold(join3(padCells(HEADERS3)))];
|
|
2749
2832
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2750
2833
|
return lines.join("\n") + "\n";
|
|
2751
2834
|
}
|
|
@@ -2771,7 +2854,7 @@ async function webhooksRm(id, opts, deps) {
|
|
|
2771
2854
|
if (opts.json) {
|
|
2772
2855
|
console.log(JSON.stringify({ event: "deleted", id }));
|
|
2773
2856
|
} else {
|
|
2774
|
-
console.log(
|
|
2857
|
+
console.log(chalk18.green(`\u2713 removed ${id}`));
|
|
2775
2858
|
}
|
|
2776
2859
|
process.exitCode = 0;
|
|
2777
2860
|
} catch (err) {
|
|
@@ -2804,13 +2887,13 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2804
2887
|
if (opts.json) console.log(JSON.stringify(ld));
|
|
2805
2888
|
if (ld.status === "succeeded") {
|
|
2806
2889
|
if (!opts.json) {
|
|
2807
|
-
console.log(
|
|
2890
|
+
console.log(chalk18.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
|
|
2808
2891
|
}
|
|
2809
2892
|
process.exitCode = 0;
|
|
2810
2893
|
} else {
|
|
2811
2894
|
if (!opts.json) {
|
|
2812
2895
|
const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
|
|
2813
|
-
console.error(
|
|
2896
|
+
console.error(chalk18.red(`\u2717 Failed (${code}) at ${ld.at}`));
|
|
2814
2897
|
}
|
|
2815
2898
|
process.exitCode = 1;
|
|
2816
2899
|
}
|
|
@@ -2819,7 +2902,7 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2819
2902
|
}
|
|
2820
2903
|
if (opts.json) console.log(JSON.stringify({ status: "pending" }));
|
|
2821
2904
|
console.error(
|
|
2822
|
-
|
|
2905
|
+
chalk18.yellow(
|
|
2823
2906
|
`Delivery still pending after ${TEST_POLL_BUDGET_MS / 1e3}s \u2014 check \`vibeview webhooks list\` later for the result.`
|
|
2824
2907
|
)
|
|
2825
2908
|
);
|
|
@@ -2832,41 +2915,29 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2832
2915
|
function printError6(opts, err) {
|
|
2833
2916
|
const message = err instanceof Error ? err.message : String(err);
|
|
2834
2917
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2835
|
-
console.error(
|
|
2836
|
-
}
|
|
2837
|
-
|
|
2838
|
-
// src/lib/formatters/devices.ts
|
|
2839
|
-
import chalk18 from "chalk";
|
|
2840
|
-
var HEADERS3 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
|
|
2841
|
-
function formatDevicesHuman(devices) {
|
|
2842
|
-
if (devices.length === 0) {
|
|
2843
|
-
return "No devices registered.\n";
|
|
2844
|
-
}
|
|
2845
|
-
const rows = devices.map((d) => [
|
|
2846
|
-
d.device_id,
|
|
2847
|
-
d.name,
|
|
2848
|
-
d.type,
|
|
2849
|
-
d.category,
|
|
2850
|
-
d.os_version,
|
|
2851
|
-
d.status
|
|
2852
|
-
]);
|
|
2853
|
-
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2854
|
-
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
2855
|
-
const lines = [chalk18.bold(fmt(HEADERS3))];
|
|
2856
|
-
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]);
|
|
2858
|
-
lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
|
|
2859
|
-
}
|
|
2860
|
-
return lines.join("\n") + "\n";
|
|
2861
|
-
}
|
|
2862
|
-
function rowSort(a, b) {
|
|
2863
|
-
return a[2].localeCompare(b[2]) || a[3].localeCompare(b[3]) || a[0].localeCompare(b[0]);
|
|
2918
|
+
console.error(chalk18.red(`error: ${message}`));
|
|
2864
2919
|
}
|
|
2865
2920
|
|
|
2866
2921
|
// src/commands/list-devices.ts
|
|
2867
2922
|
async function listDevicesCommand(options) {
|
|
2868
2923
|
const { token, baseUrl } = resolveAuth(options);
|
|
2869
2924
|
const client = new ApiClient(baseUrl, token);
|
|
2925
|
+
if (options.models) {
|
|
2926
|
+
try {
|
|
2927
|
+
const models = filterDeviceModels(await client.listDeviceModels(), options);
|
|
2928
|
+
if (options.output === "json") {
|
|
2929
|
+
process.stdout.write(JSON.stringify(models, null, 2) + "\n");
|
|
2930
|
+
} else {
|
|
2931
|
+
process.stdout.write(formatDeviceModelsHuman(models));
|
|
2932
|
+
}
|
|
2933
|
+
process.exit(EXIT_PASS);
|
|
2934
|
+
} catch (err) {
|
|
2935
|
+
process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}
|
|
2936
|
+
`);
|
|
2937
|
+
process.exit(EXIT_INFRA_ERROR);
|
|
2938
|
+
}
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2870
2941
|
try {
|
|
2871
2942
|
const devices = await client.listDevices({
|
|
2872
2943
|
type: options.type,
|
|
@@ -3151,8 +3222,14 @@ program.command("list-suites").description("List available test suites").option(
|
|
|
3151
3222
|
[]
|
|
3152
3223
|
).option("--match <mode>", "When multiple --tag flags: any (default) or all", "any").action((opts) => listSuites(opts));
|
|
3153
3224
|
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").
|
|
3155
|
-
|
|
3225
|
+
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").option(
|
|
3226
|
+
"--models",
|
|
3227
|
+
"List the device models you can start instead (the names `dev --model` takes)",
|
|
3228
|
+
false
|
|
3229
|
+
).action((opts) => listDevicesCommand(opts));
|
|
3230
|
+
program.command("upload-app <file>").description(
|
|
3231
|
+
"Upload an app build (zipped simulator .app for iOS/tvOS, .apk for Android/Android TV, or a Roku channel .zip)"
|
|
3232
|
+
).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
3233
|
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
3234
|
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
3235
|
program.command("logout").description("Revoke the saved login token and remove it from ~/.vibeview/config.json").action(() => logout());
|
|
@@ -3165,9 +3242,12 @@ program.command("dev").description(
|
|
|
3165
3242
|
"--device-category <category>",
|
|
3166
3243
|
"phone or tablet (default: phone; TV platforms always use tv)",
|
|
3167
3244
|
parseDeviceCategory
|
|
3245
|
+
).option(
|
|
3246
|
+
"--model <name>",
|
|
3247
|
+
'Run on this exact device model, e.g. "iPhone Duo" (see `vibeview list-devices --models`)'
|
|
3168
3248
|
).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
3249
|
"--detach",
|
|
3170
|
-
"Run the dev session in the background; prints session info and exits (see `dev-status`, `dev-stop`)",
|
|
3250
|
+
"Run the dev session in the background; prints session info and exits (see `dev-status`, `dev-stop`, and `dev-reload` for Roku)",
|
|
3171
3251
|
false
|
|
3172
3252
|
).option(
|
|
3173
3253
|
"--build",
|
|
@@ -3307,10 +3387,13 @@ webhooks.command("rm <id>").description("Remove a webhook endpoint (rotate the s
|
|
|
3307
3387
|
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
3388
|
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
3389
|
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));
|
|
3390
|
+
program.command("dev-reload").description(
|
|
3391
|
+
"Roku only: re-package, upload and reload the channel into this project's `vibeview dev --detach` session (the `r` key of the foreground loop)"
|
|
3392
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devReload(opts));
|
|
3310
3393
|
program.command("mcp").description(
|
|
3311
|
-
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
|
|
3394
|
+
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
3312
3395
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
3313
|
-
const { runMcpServer } = await import("./server-
|
|
3396
|
+
const { runMcpServer } = await import("./server-XMSBLIWY.js");
|
|
3314
3397
|
await runMcpServer(opts);
|
|
3315
3398
|
});
|
|
3316
3399
|
program.command("agent-setup").description(
|