vibeview 0.23.0 → 0.25.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/README.md +2 -0
- package/dist/{chunk-E2LZSLRF.js → chunk-GEKYHEWU.js} +247 -103
- package/dist/{chunk-PSFXVUHG.js → chunk-RFFCCPGZ.js} +44 -6
- package/dist/cli.js +176 -125
- package/dist/{server-XMSBLIWY.js → server-6EUBLXKE.js} +94 -5
- package/dist/{upload-app-ZAKAOJKE.js → upload-app-E32EZXYQ.js} +3 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +11 -1
|
@@ -6,7 +6,99 @@ import {
|
|
|
6
6
|
loadConfig,
|
|
7
7
|
resolveAuth,
|
|
8
8
|
uploadAppBuild
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RFFCCPGZ.js";
|
|
10
|
+
|
|
11
|
+
// src/lib/dev-build.ts
|
|
12
|
+
function parseBuildId(raw) {
|
|
13
|
+
const text = typeof raw === "number" ? String(raw) : typeof raw === "string" ? raw.trim() : "";
|
|
14
|
+
if (!/^\d+$/.test(text) || Number(text) < 1) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`--build expects a build id (a positive number, see \`vibeview list-builds <app>\`), got "${String(raw)}"`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return Number(text);
|
|
20
|
+
}
|
|
21
|
+
function buildKind(isDebug) {
|
|
22
|
+
if (isDebug === true) return "debug";
|
|
23
|
+
if (isDebug === false) return "release";
|
|
24
|
+
return "unknown";
|
|
25
|
+
}
|
|
26
|
+
function checkDevBuild(app, buildId, platform) {
|
|
27
|
+
if (platform === "roku") {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"a build id cannot be pinned on roku \u2014 `vibeview dev` packages and sideloads the channel itself"
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (app.platform !== platform) {
|
|
33
|
+
throw new Error(`app ${app.public_id} is a ${app.platform} app, not ${platform}`);
|
|
34
|
+
}
|
|
35
|
+
const build = app.builds.find((b) => b.id === buildId);
|
|
36
|
+
if (!build) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`build ${buildId} is not a build of app ${app.public_id} \u2014 see \`vibeview list-builds ${app.public_id}\``
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (build.is_debug !== true) {
|
|
42
|
+
const why = build.is_debug === false ? "it runs standalone (a release build, or one with an embedded JS bundle)" : "it could not be identified as a Metro debug build";
|
|
43
|
+
throw new Error(
|
|
44
|
+
`build ${buildId} is not a debug build \u2014 ${why}. \`vibeview dev\` needs a ${platform} debug simulator/emulator build without an embedded JS bundle (see \`vibeview list-builds ${app.public_id}\`)`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return build;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/commands/list-builds.ts
|
|
51
|
+
import chalk from "chalk";
|
|
52
|
+
function buildRows(app) {
|
|
53
|
+
return app.builds.map((b) => ({
|
|
54
|
+
id: b.id,
|
|
55
|
+
version: b.version ?? null,
|
|
56
|
+
build_number: b.build_number ?? null,
|
|
57
|
+
platform: app.platform,
|
|
58
|
+
kind: buildKind(b.is_debug),
|
|
59
|
+
is_debug: b.is_debug ?? null,
|
|
60
|
+
uploaded_at: b.uploaded_at ?? null,
|
|
61
|
+
note: b.note ?? null
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
var HEADERS = ["ID", "VERSION", "PLATFORM", "KIND", "UPLOADED", "NOTE"];
|
|
65
|
+
function formatBuildRowsHuman(app, rows) {
|
|
66
|
+
if (rows.length === 0) {
|
|
67
|
+
return `No builds uploaded for ${app.name} (${app.public_id}) \u2014 upload one with \`vibeview upload-app\`.
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
const cells = rows.map((r) => [
|
|
71
|
+
String(r.id),
|
|
72
|
+
r.version ? `${r.version}${r.build_number ? ` (${r.build_number})` : ""}` : r.build_number ?? "",
|
|
73
|
+
r.platform,
|
|
74
|
+
r.kind,
|
|
75
|
+
r.uploaded_at ?? "",
|
|
76
|
+
(r.note ?? "").split("\n")[0]
|
|
77
|
+
]);
|
|
78
|
+
const widths = HEADERS.map((h, i) => Math.max(h.length, ...cells.map((c) => c[i].length)));
|
|
79
|
+
const pad = (row) => row.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
80
|
+
const lines = [chalk.bold(pad(HEADERS)), ...cells.map(pad)];
|
|
81
|
+
return lines.join("\n") + "\n";
|
|
82
|
+
}
|
|
83
|
+
async function listBuilds(appId, opts, deps) {
|
|
84
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
85
|
+
const d = deps ?? { getApp: (id) => new ApiClient(baseUrl, token).getApp(id) };
|
|
86
|
+
try {
|
|
87
|
+
const app = await d.getApp(appId);
|
|
88
|
+
const rows = buildRows(app);
|
|
89
|
+
if (opts.json) {
|
|
90
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
91
|
+
} else {
|
|
92
|
+
process.stdout.write(formatBuildRowsHuman(app, rows));
|
|
93
|
+
}
|
|
94
|
+
process.exitCode = 0;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
+
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
98
|
+
console.error(chalk.red(`error: ${message}`));
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
10
102
|
|
|
11
103
|
// src/lib/dev-state.ts
|
|
12
104
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -267,7 +359,7 @@ async function checkMetro(port, timeoutMs = 1500) {
|
|
|
267
359
|
import { spawn as spawn2 } from "child_process";
|
|
268
360
|
import { createInterface } from "readline";
|
|
269
361
|
import { select as select2 } from "@inquirer/prompts";
|
|
270
|
-
import
|
|
362
|
+
import chalk3 from "chalk";
|
|
271
363
|
|
|
272
364
|
// src/lib/artifact.ts
|
|
273
365
|
import { existsSync as existsSync2, mkdtempSync, readdirSync, rmSync as rmSync2, statSync } from "fs";
|
|
@@ -327,7 +419,7 @@ function prepareUploadArtifact(path) {
|
|
|
327
419
|
}
|
|
328
420
|
|
|
329
421
|
// src/lib/dev-setup.ts
|
|
330
|
-
import
|
|
422
|
+
import chalk2 from "chalk";
|
|
331
423
|
import { input, select } from "@inquirer/prompts";
|
|
332
424
|
|
|
333
425
|
// src/lib/project-detect.ts
|
|
@@ -556,7 +648,7 @@ async function promptBuildConfig(cwd, platform) {
|
|
|
556
648
|
return { command: defaults.buildCommand, artifact: defaults.artifact };
|
|
557
649
|
}
|
|
558
650
|
console.log(
|
|
559
|
-
|
|
651
|
+
chalk2.dim(
|
|
560
652
|
" Tip: include your install step \u2014 dependencies are not uploaded.\n e.g. `yarn install --immutable && yarn build:android`"
|
|
561
653
|
)
|
|
562
654
|
);
|
|
@@ -582,7 +674,7 @@ async function promptProductionBuildConfig(cwd, platform) {
|
|
|
582
674
|
};
|
|
583
675
|
}
|
|
584
676
|
console.log(
|
|
585
|
-
|
|
677
|
+
chalk2.dim(
|
|
586
678
|
" Tip: include your install step \u2014 dependencies are not uploaded.\n e.g. `yarn install --immutable && cd android && ./gradlew bundleRelease`"
|
|
587
679
|
)
|
|
588
680
|
);
|
|
@@ -831,7 +923,7 @@ function runShell(command, cwd, log) {
|
|
|
831
923
|
}
|
|
832
924
|
async function resolveBarePlatform(cwd, cfg, opts) {
|
|
833
925
|
if (opts.platform && !isDevPlatform(opts.platform)) {
|
|
834
|
-
console.error(
|
|
926
|
+
console.error(chalk3.red(`error: --platform must be one of ${DEV_PLATFORMS.join(", ")}`));
|
|
835
927
|
process.exit(2);
|
|
836
928
|
}
|
|
837
929
|
let platform = opts.platform || cfg?.defaultPlatform;
|
|
@@ -946,9 +1038,9 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
946
1038
|
platform,
|
|
947
1039
|
build,
|
|
948
1040
|
opts,
|
|
949
|
-
(l) => console.error(
|
|
1041
|
+
(l) => console.error(chalk3.dim(l))
|
|
950
1042
|
);
|
|
951
|
-
for (const w of result.warnings ?? []) console.error(
|
|
1043
|
+
for (const w of result.warnings ?? []) console.error(chalk3.yellow(`\u26A0 ${w}`));
|
|
952
1044
|
persistUploadedAppId(cwd, cfg, platform, build, result.public_id);
|
|
953
1045
|
if (opts.json) {
|
|
954
1046
|
console.log(
|
|
@@ -956,8 +1048,8 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
956
1048
|
);
|
|
957
1049
|
} else {
|
|
958
1050
|
console.error(
|
|
959
|
-
|
|
960
|
-
${
|
|
1051
|
+
chalk3.green(`\u2713 built and uploaded ${result.name}`) + `
|
|
1052
|
+
${chalk3.cyan(result.public_id)} ${chalk3.dim(`[${result.platform}]`)}`
|
|
961
1053
|
);
|
|
962
1054
|
}
|
|
963
1055
|
} catch (err) {
|
|
@@ -969,7 +1061,7 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
969
1061
|
}
|
|
970
1062
|
function printFailure(opts, message, code) {
|
|
971
1063
|
if (opts.json) console.log(JSON.stringify({ event: "error", code, message }));
|
|
972
|
-
console.error(
|
|
1064
|
+
console.error(chalk3.red(`error: ${message}`));
|
|
973
1065
|
}
|
|
974
1066
|
function fail(opts, message, code) {
|
|
975
1067
|
printFailure(opts, message, code);
|
|
@@ -992,7 +1084,7 @@ async function resolveCloudPlatforms(cwd, cfgRef, opts) {
|
|
|
992
1084
|
for (const p of unsupported) {
|
|
993
1085
|
const reason = productionUnsupportedReason(p);
|
|
994
1086
|
emitCloud(opts.json, { event: "skipped", platform: p, reason });
|
|
995
|
-
if (!opts.json) console.error(
|
|
1087
|
+
if (!opts.json) console.error(chalk3.yellow(`\u26A0 skipping ${p}: ${reason}`));
|
|
996
1088
|
}
|
|
997
1089
|
chosenPlatforms = chosenPlatforms.filter((p) => !unsupported.includes(p));
|
|
998
1090
|
}
|
|
@@ -1033,7 +1125,7 @@ async function resolveCloudPlatforms(cwd, cfgRef, opts) {
|
|
|
1033
1125
|
function validateExplicitPlatform(raw) {
|
|
1034
1126
|
if (!isDevPlatform(raw)) {
|
|
1035
1127
|
console.error(
|
|
1036
|
-
|
|
1128
|
+
chalk3.red(`error: --platform must be one of ${DEV_PLATFORMS.join(", ")}, or "all"`)
|
|
1037
1129
|
);
|
|
1038
1130
|
process.exit(2);
|
|
1039
1131
|
}
|
|
@@ -1093,7 +1185,7 @@ async function resolveCloudAppIds(cwd, cfgRef, platforms, opts, listApps) {
|
|
|
1093
1185
|
createFromArtifact.add(platform);
|
|
1094
1186
|
if (!opts.json) {
|
|
1095
1187
|
console.error(
|
|
1096
|
-
|
|
1188
|
+
chalk3.dim(
|
|
1097
1189
|
opts.production ? `no ${platform} apps yet \u2014 production builds aren't linked to an app; the signed artifact will be available from the build itself` : `no ${platform} apps yet \u2014 this build will create one from the artifact`
|
|
1098
1190
|
)
|
|
1099
1191
|
);
|
|
@@ -1270,7 +1362,7 @@ ${offenders}`);
|
|
|
1270
1362
|
const autoIncrement = build.autoIncrement === true;
|
|
1271
1363
|
if (autoIncrement && !production && !opts.json) {
|
|
1272
1364
|
console.error(
|
|
1273
|
-
|
|
1365
|
+
chalk3.yellow(
|
|
1274
1366
|
`note: platforms.${p}.build.autoIncrement only applies to production builds (--production) \u2014 ignoring it for this simulator build`
|
|
1275
1367
|
)
|
|
1276
1368
|
);
|
|
@@ -1307,7 +1399,7 @@ ${offenders}`);
|
|
|
1307
1399
|
}
|
|
1308
1400
|
if (!opts.json) {
|
|
1309
1401
|
for (const b of created) {
|
|
1310
|
-
console.error(` ${
|
|
1402
|
+
console.error(` ${chalk3.cyan(b.id)} ${chalk3.dim(`[${b.platform}]`)}`);
|
|
1311
1403
|
}
|
|
1312
1404
|
}
|
|
1313
1405
|
const shouldWait = opts.wait !== false || opts.download;
|
|
@@ -1332,7 +1424,7 @@ ${offenders}`);
|
|
|
1332
1424
|
path: result.path
|
|
1333
1425
|
});
|
|
1334
1426
|
if (!opts.json) {
|
|
1335
|
-
console.error(
|
|
1427
|
+
console.error(chalk3.green(`\u2713 saved ${result.filename}`));
|
|
1336
1428
|
}
|
|
1337
1429
|
} catch (err) {
|
|
1338
1430
|
downloadFailed = true;
|
|
@@ -1341,7 +1433,7 @@ ${offenders}`);
|
|
|
1341
1433
|
event: "error",
|
|
1342
1434
|
message: `error downloading ${b.id}: ${message}`
|
|
1343
1435
|
});
|
|
1344
|
-
if (!opts.json) console.error(
|
|
1436
|
+
if (!opts.json) console.error(chalk3.red(`error downloading ${b.id}: ${message}`));
|
|
1345
1437
|
}
|
|
1346
1438
|
}
|
|
1347
1439
|
}
|
|
@@ -1398,7 +1490,7 @@ async function waitForCloudBuilds(builds, opts, deps) {
|
|
|
1398
1490
|
const giveUpLocally = (id, message) => {
|
|
1399
1491
|
abandoned.add(id);
|
|
1400
1492
|
emitCloud(opts.json, { event: "error", message });
|
|
1401
|
-
if (!opts.json) console.error(
|
|
1493
|
+
if (!opts.json) console.error(chalk3.red(message));
|
|
1402
1494
|
};
|
|
1403
1495
|
const pollAndProcess = async (build) => {
|
|
1404
1496
|
const offset = offsets.get(build.id) ?? 0;
|
|
@@ -1559,21 +1651,21 @@ function printTransition(id, platform, status, state, json, printLine) {
|
|
|
1559
1651
|
if (json) {
|
|
1560
1652
|
emitCloud(json, { event: "status", id, status: "queued" });
|
|
1561
1653
|
} else {
|
|
1562
|
-
printLine(id, platform,
|
|
1654
|
+
printLine(id, platform, chalk3.dim("\u23F3 queued \u2014 waiting for a build slot"));
|
|
1563
1655
|
}
|
|
1564
1656
|
} else if (status === "running" && !state.printedRunning) {
|
|
1565
1657
|
state.printedRunning = true;
|
|
1566
1658
|
if (json) {
|
|
1567
1659
|
emitCloud(json, { event: "status", id, status: "running" });
|
|
1568
1660
|
} else {
|
|
1569
|
-
printLine(id, platform, `${
|
|
1661
|
+
printLine(id, platform, `${chalk3.cyan("\u25B6")} building`);
|
|
1570
1662
|
}
|
|
1571
1663
|
}
|
|
1572
1664
|
}
|
|
1573
1665
|
function printOutcomeLine(build, multi) {
|
|
1574
1666
|
const prefix = multi ? `[${build.platform}] ` : "";
|
|
1575
1667
|
const ok = build.status === "succeeded";
|
|
1576
|
-
const mark = ok ?
|
|
1668
|
+
const mark = ok ? chalk3.green("\u2713") : chalk3.red("\u2717");
|
|
1577
1669
|
const duration = formatDuration(build.started_at, build.finished_at);
|
|
1578
1670
|
let line = `${prefix}${mark} ${build.status}`;
|
|
1579
1671
|
if (duration) line += ` (${duration})`;
|
|
@@ -1620,7 +1712,7 @@ import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
|
1620
1712
|
import { dirname as dirname5 } from "path";
|
|
1621
1713
|
import { createInterface as createInterface3 } from "readline";
|
|
1622
1714
|
import { select as select3 } from "@inquirer/prompts";
|
|
1623
|
-
import
|
|
1715
|
+
import chalk4 from "chalk";
|
|
1624
1716
|
import openBrowser from "open";
|
|
1625
1717
|
|
|
1626
1718
|
// src/lib/tunnel-client.ts
|
|
@@ -1646,6 +1738,26 @@ function decodeFrame(data) {
|
|
|
1646
1738
|
return { type, streamId: data.readUInt32BE(1), payload: data.subarray(HEADER_LEN) };
|
|
1647
1739
|
}
|
|
1648
1740
|
|
|
1741
|
+
// src/lib/redact.ts
|
|
1742
|
+
var SECRET_PARAMS = /* @__PURE__ */ new Set([
|
|
1743
|
+
"token",
|
|
1744
|
+
"access_token",
|
|
1745
|
+
"refresh_token",
|
|
1746
|
+
"api_key",
|
|
1747
|
+
"apikey",
|
|
1748
|
+
"key",
|
|
1749
|
+
"secret",
|
|
1750
|
+
"signature",
|
|
1751
|
+
"sig",
|
|
1752
|
+
"jwt"
|
|
1753
|
+
]);
|
|
1754
|
+
function redactSecrets(text) {
|
|
1755
|
+
return text.replace(
|
|
1756
|
+
/\b([A-Za-z_]+)=([^&\s"'#]+)/g,
|
|
1757
|
+
(match, name) => SECRET_PARAMS.has(name.toLowerCase()) ? `${name}=[redacted]` : match
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1649
1761
|
// src/lib/tunnel-client.ts
|
|
1650
1762
|
var FATAL_CLOSE_CODES = /* @__PURE__ */ new Set([4008, 4009]);
|
|
1651
1763
|
var MAX_BACKOFF_MS = 8e3;
|
|
@@ -1665,7 +1777,7 @@ var TunnelClient = class {
|
|
|
1665
1777
|
url;
|
|
1666
1778
|
connCount = 0;
|
|
1667
1779
|
debug(msg) {
|
|
1668
|
-
this.opts.onDebug?.(msg);
|
|
1780
|
+
this.opts.onDebug?.(redactSecrets(msg));
|
|
1669
1781
|
}
|
|
1670
1782
|
start() {
|
|
1671
1783
|
this.connect();
|
|
@@ -2158,7 +2270,8 @@ function devSessionCreateBody(cfg) {
|
|
|
2158
2270
|
app_id: cfg.appId,
|
|
2159
2271
|
// Roku has no JS bundle to serve, so there is nothing to tunnel.
|
|
2160
2272
|
tunnel: PLATFORM_NEEDS_METRO[cfg.platform],
|
|
2161
|
-
...cfg.model ? { model: cfg.model } : {}
|
|
2273
|
+
...cfg.model ? { model: cfg.model } : {},
|
|
2274
|
+
...cfg.buildId ? { build_id: cfg.buildId } : {}
|
|
2162
2275
|
};
|
|
2163
2276
|
}
|
|
2164
2277
|
function resolveDeviceModel(pools, platform, model) {
|
|
@@ -2292,7 +2405,7 @@ function requireNonInteractive(json) {
|
|
|
2292
2405
|
async function ensureConfig(api, opts) {
|
|
2293
2406
|
const existing = loadDevConfig(process.cwd());
|
|
2294
2407
|
if (opts.platform && !isDevPlatform(opts.platform)) {
|
|
2295
|
-
console.error(
|
|
2408
|
+
console.error(chalk4.red(`error: --platform must be one of ${DEV_PLATFORMS.join(", ")}`));
|
|
2296
2409
|
process.exit(2);
|
|
2297
2410
|
}
|
|
2298
2411
|
if (!opts.platform && !existing?.defaultPlatform) {
|
|
@@ -2305,21 +2418,21 @@ async function ensureConfig(api, opts) {
|
|
|
2305
2418
|
const isTv = PLATFORM_DEVICE[platform].tv;
|
|
2306
2419
|
if (isTv && opts.deviceCategory) {
|
|
2307
2420
|
console.error(
|
|
2308
|
-
|
|
2421
|
+
chalk4.red(`error: --device-category does not apply to ${platform} (always a tv device)`)
|
|
2309
2422
|
);
|
|
2310
2423
|
process.exit(2);
|
|
2311
2424
|
}
|
|
2312
2425
|
if (!isTv && opts.deviceCategory === "tv") {
|
|
2313
|
-
console.error(
|
|
2426
|
+
console.error(chalk4.red(`error: use --platform tvos or --platform androidtv for TV devices`));
|
|
2314
2427
|
process.exit(2);
|
|
2315
2428
|
}
|
|
2316
2429
|
if (opts.model !== void 0 && opts.deviceCategory) {
|
|
2317
|
-
console.error(
|
|
2430
|
+
console.error(chalk4.red("error: pass --model or --device-category, not both"));
|
|
2318
2431
|
process.exit(2);
|
|
2319
2432
|
}
|
|
2320
2433
|
if (opts.model !== void 0 && !opts.model.trim()) {
|
|
2321
2434
|
console.error(
|
|
2322
|
-
|
|
2435
|
+
chalk4.red("error: --model needs a model name (see `vibeview list-devices --models`)")
|
|
2323
2436
|
);
|
|
2324
2437
|
process.exit(2);
|
|
2325
2438
|
}
|
|
@@ -2343,7 +2456,7 @@ async function ensureConfig(api, opts) {
|
|
|
2343
2456
|
allApps = await api.listApps();
|
|
2344
2457
|
} catch (err) {
|
|
2345
2458
|
const message = `could not fetch your app list \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
2346
|
-
console.error(
|
|
2459
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2347
2460
|
emit(opts.json, { event: "error", message });
|
|
2348
2461
|
process.exit(1);
|
|
2349
2462
|
}
|
|
@@ -2352,7 +2465,7 @@ async function ensureConfig(api, opts) {
|
|
|
2352
2465
|
if (choice.staleFlag) {
|
|
2353
2466
|
const message = `app "${choice.staleFlag}" not found \u2014 it may have been deleted.`;
|
|
2354
2467
|
console.error(
|
|
2355
|
-
|
|
2468
|
+
chalk4.red(`error: ${message}`) + `
|
|
2356
2469
|
Run \`vibeview dev\` without --app to pick from the list.`
|
|
2357
2470
|
);
|
|
2358
2471
|
emit(opts.json, { event: "error", message });
|
|
@@ -2362,12 +2475,12 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2362
2475
|
let builtThisRun = false;
|
|
2363
2476
|
if (choice.staleCached) {
|
|
2364
2477
|
console.error(
|
|
2365
|
-
|
|
2478
|
+
chalk4.yellow(`\u26A0 app "${choice.staleCached}" no longer exists \u2014 pick your debug build again`)
|
|
2366
2479
|
);
|
|
2367
2480
|
}
|
|
2368
2481
|
const buildConfig = firstRunBuild ?? entry?.build ?? null;
|
|
2369
|
-
if (!appId && buildConfig) {
|
|
2370
|
-
const strategy = buildAppStrategy(apps,
|
|
2482
|
+
if (!appId && buildConfig && typeof opts.build !== "string") {
|
|
2483
|
+
const strategy = buildAppStrategy(apps, opts.build === true, opts.json, process.stdin.isTTY);
|
|
2371
2484
|
let action = strategy === "ask" ? "pick" : strategy;
|
|
2372
2485
|
if (strategy === "ask") {
|
|
2373
2486
|
action = await select3({
|
|
@@ -2379,20 +2492,20 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2379
2492
|
});
|
|
2380
2493
|
}
|
|
2381
2494
|
if (action === "build") {
|
|
2382
|
-
console.error(
|
|
2495
|
+
console.error(chalk4.dim(`Building ${platform}...`));
|
|
2383
2496
|
try {
|
|
2384
2497
|
const result = await runBuildForPlatform(
|
|
2385
2498
|
process.cwd(),
|
|
2386
2499
|
platform,
|
|
2387
2500
|
buildConfig,
|
|
2388
2501
|
opts,
|
|
2389
|
-
(l) => console.error(
|
|
2502
|
+
(l) => console.error(chalk4.dim(l))
|
|
2390
2503
|
);
|
|
2391
2504
|
appId = result.public_id;
|
|
2392
2505
|
builtThisRun = true;
|
|
2393
2506
|
} catch (err) {
|
|
2394
2507
|
const message = err.message;
|
|
2395
|
-
console.error(
|
|
2508
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2396
2509
|
emit(opts.json, { event: "error", message });
|
|
2397
2510
|
process.exit(1);
|
|
2398
2511
|
}
|
|
@@ -2401,20 +2514,20 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2401
2514
|
if (!appId) {
|
|
2402
2515
|
if (apps.length === 0) {
|
|
2403
2516
|
const message = noAppsMessage(platform, entry?.build !== void 0);
|
|
2404
|
-
console.error(
|
|
2517
|
+
console.error(chalk4.red(message));
|
|
2405
2518
|
emit(opts.json, { event: "error", code: "no_apps", message });
|
|
2406
2519
|
process.exit(1);
|
|
2407
2520
|
}
|
|
2408
2521
|
if (choice.staleCached) {
|
|
2409
2522
|
const message = `app "${choice.staleCached}" for ${platform} no longer exists \u2014 pass --app <id> (see \`vibeview list-apps\`) or run interactively to pick one.`;
|
|
2410
|
-
console.error(
|
|
2523
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2411
2524
|
emit(opts.json, { event: "error", code: "app_deleted", message });
|
|
2412
2525
|
process.exit(1);
|
|
2413
2526
|
}
|
|
2414
2527
|
requireNonInteractive(opts.json);
|
|
2415
2528
|
if (!process.stdin.isTTY) {
|
|
2416
2529
|
console.error(
|
|
2417
|
-
|
|
2530
|
+
chalk4.red(
|
|
2418
2531
|
`error: no valid app configured for ${platform} \u2014 run interactively to pick one, or pass --app <id>`
|
|
2419
2532
|
)
|
|
2420
2533
|
);
|
|
@@ -2450,7 +2563,7 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2450
2563
|
{ build: !firstRunBuild, metro: PLATFORM_NEEDS_METRO[platform] && !firstRunMetro },
|
|
2451
2564
|
platform
|
|
2452
2565
|
);
|
|
2453
|
-
if (hint) console.error(
|
|
2566
|
+
if (hint) console.error(chalk4.dim(hint));
|
|
2454
2567
|
}
|
|
2455
2568
|
const finalMetro = isFirstRun ? resolveMetro(cfg, platform, opts.metroPort ? parseInt(opts.metroPort, 10) : void 0) : metroResolved;
|
|
2456
2569
|
return {
|
|
@@ -2470,10 +2583,21 @@ async function runDevInProcess(opts) {
|
|
|
2470
2583
|
watchSeconds = parseWatchSeconds(opts.watch);
|
|
2471
2584
|
} catch (err) {
|
|
2472
2585
|
const message = err.message;
|
|
2473
|
-
console.error(
|
|
2586
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2474
2587
|
emit(opts.json, { event: "error", code: "invalid_flag", message });
|
|
2475
2588
|
process.exit(2);
|
|
2476
2589
|
}
|
|
2590
|
+
let pinnedBuildId;
|
|
2591
|
+
if (typeof opts.build === "string") {
|
|
2592
|
+
try {
|
|
2593
|
+
pinnedBuildId = parseBuildId(opts.build);
|
|
2594
|
+
} catch (err) {
|
|
2595
|
+
const message = err.message;
|
|
2596
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2597
|
+
emit(opts.json, { event: "error", code: "invalid_flag", message });
|
|
2598
|
+
process.exit(2);
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2477
2601
|
if (opts.json) {
|
|
2478
2602
|
const hasToken = !!(opts.token || process.env.VIBEVIEW_API_TOKEN || loadConfig().token);
|
|
2479
2603
|
if (!hasToken) {
|
|
@@ -2486,26 +2610,36 @@ async function runDevInProcess(opts) {
|
|
|
2486
2610
|
}
|
|
2487
2611
|
}
|
|
2488
2612
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2489
|
-
console.error(
|
|
2613
|
+
console.error(chalk4.dim(`Using ${baseUrl}`));
|
|
2490
2614
|
const api = new ApiClient(baseUrl, token);
|
|
2491
2615
|
const cfg = await ensureConfig(api, opts);
|
|
2492
|
-
if (
|
|
2616
|
+
if (pinnedBuildId !== void 0) {
|
|
2617
|
+
try {
|
|
2618
|
+
checkDevBuild(await api.getApp(cfg.appId), pinnedBuildId, cfg.platform);
|
|
2619
|
+
} catch (err) {
|
|
2620
|
+
const message = err.message;
|
|
2621
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2622
|
+
emit(opts.json, { event: "error", code: "invalid_build", message });
|
|
2623
|
+
process.exit(1);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
if (opts.build === true && !cfg.builtThisRun) {
|
|
2493
2627
|
const reloaded = loadDevConfig(process.cwd());
|
|
2494
2628
|
const build = reloaded?.platforms?.[cfg.platform]?.build;
|
|
2495
2629
|
if (!build) {
|
|
2496
2630
|
const message = `no build config for ${cfg.platform} \u2014 run vibeview build once to set it up`;
|
|
2497
|
-
console.error(
|
|
2631
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2498
2632
|
emit(opts.json, { event: "error", code: "config_required", message });
|
|
2499
2633
|
process.exit(1);
|
|
2500
2634
|
}
|
|
2501
|
-
console.error(
|
|
2635
|
+
console.error(chalk4.dim(`Building ${cfg.platform}...`));
|
|
2502
2636
|
try {
|
|
2503
2637
|
const result = await runBuildForPlatform(
|
|
2504
2638
|
process.cwd(),
|
|
2505
2639
|
cfg.platform,
|
|
2506
2640
|
build,
|
|
2507
2641
|
{ token, baseUrl },
|
|
2508
|
-
(l) => console.error(
|
|
2642
|
+
(l) => console.error(chalk4.dim(l))
|
|
2509
2643
|
);
|
|
2510
2644
|
cfg.appId = result.public_id;
|
|
2511
2645
|
if (reloaded && reloaded.platforms[cfg.platform] && reloaded.platforms[cfg.platform].appId !== result.public_id) {
|
|
@@ -2514,7 +2648,7 @@ async function runDevInProcess(opts) {
|
|
|
2514
2648
|
}
|
|
2515
2649
|
} catch (err) {
|
|
2516
2650
|
const message = err.message;
|
|
2517
|
-
console.error(
|
|
2651
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2518
2652
|
emit(opts.json, { event: "error", message });
|
|
2519
2653
|
process.exit(1);
|
|
2520
2654
|
}
|
|
@@ -2536,7 +2670,7 @@ async function runDevInProcess(opts) {
|
|
|
2536
2670
|
metroLogStream.write(line.replace(/^\[metro\] /, "") + "\n");
|
|
2537
2671
|
} catch {
|
|
2538
2672
|
}
|
|
2539
|
-
if (shouldEchoMetroLine(line, opts.debug === true)) console.error(
|
|
2673
|
+
if (shouldEchoMetroLine(line, opts.debug === true)) console.error(chalk4.dim(line));
|
|
2540
2674
|
};
|
|
2541
2675
|
metroResult = await ensureMetro(
|
|
2542
2676
|
process.cwd(),
|
|
@@ -2549,15 +2683,15 @@ async function runDevInProcess(opts) {
|
|
|
2549
2683
|
);
|
|
2550
2684
|
} catch (err) {
|
|
2551
2685
|
const message = err.message;
|
|
2552
|
-
console.error(
|
|
2686
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2553
2687
|
emit(opts.json, { event: "error", code: "metro_down", message });
|
|
2554
2688
|
process.exit(1);
|
|
2555
2689
|
}
|
|
2556
|
-
for (const w of metroResult.warnings) console.error(
|
|
2690
|
+
for (const w of metroResult.warnings) console.error(chalk4.yellow(`\u26A0 ${w}`));
|
|
2557
2691
|
if (metroResult.status === "spawned" || metroResult.status === "respawned") {
|
|
2558
|
-
console.error(
|
|
2692
|
+
console.error(chalk4.green(`\u2713 started Metro (${cfg.metroCommand})`));
|
|
2559
2693
|
console.error(
|
|
2560
|
-
|
|
2694
|
+
chalk4.dim(` full Metro output: ${metroLogPath(process.cwd())} (--debug echoes it here)`)
|
|
2561
2695
|
);
|
|
2562
2696
|
}
|
|
2563
2697
|
metroOwnedThisRun = metroResult.status === "spawned" || metroResult.status === "respawned";
|
|
@@ -2567,19 +2701,23 @@ async function runDevInProcess(opts) {
|
|
|
2567
2701
|
watchIgnore = rokuEntry?.watchIgnore ?? [];
|
|
2568
2702
|
if (!rokuBuild) {
|
|
2569
2703
|
const message = `no build config for ${cfg.platform} \u2014 run \`vibeview build --platform ${cfg.platform}\` once to set it up`;
|
|
2570
|
-
console.error(
|
|
2704
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2571
2705
|
emit(opts.json, { event: "error", code: "config_required", message });
|
|
2572
2706
|
process.exit(1);
|
|
2573
2707
|
}
|
|
2574
2708
|
}
|
|
2575
|
-
console.error(
|
|
2709
|
+
console.error(
|
|
2710
|
+
chalk4.dim(
|
|
2711
|
+
`Starting a ${cfg.platform} dev session for ${cfg.appId}${pinnedBuildId !== void 0 ? ` (build ${pinnedBuildId})` : ""}...`
|
|
2712
|
+
)
|
|
2713
|
+
);
|
|
2576
2714
|
let stopping = false;
|
|
2577
2715
|
let handle = null;
|
|
2578
2716
|
let reloadLoop = null;
|
|
2579
2717
|
const stopSession = async (code) => {
|
|
2580
2718
|
if (stopping) return;
|
|
2581
2719
|
stopping = true;
|
|
2582
|
-
console.error(
|
|
2720
|
+
console.error(chalk4.dim("\nStopping session..."));
|
|
2583
2721
|
reloadLoop?.stop();
|
|
2584
2722
|
await handle?.stop();
|
|
2585
2723
|
if (metroOwnedThisRun) killOwnedMetro(process.cwd());
|
|
@@ -2602,6 +2740,7 @@ async function runDevInProcess(opts) {
|
|
|
2602
2740
|
appId: cfg.appId,
|
|
2603
2741
|
deviceCategory: cfg.deviceCategory,
|
|
2604
2742
|
model: cfg.model,
|
|
2743
|
+
buildId: pinnedBuildId,
|
|
2605
2744
|
metroPort: cfg.metroPort ?? 8081,
|
|
2606
2745
|
// startDevSession's own browser-open happens before we've written the
|
|
2607
2746
|
// state file / emitted session_ready — open it ourselves afterward
|
|
@@ -2613,26 +2752,26 @@ async function runDevInProcess(opts) {
|
|
|
2613
2752
|
if (evt.event === "warning") {
|
|
2614
2753
|
if (nextWarningIsMetroUnreachable) {
|
|
2615
2754
|
nextWarningIsMetroUnreachable = false;
|
|
2616
|
-
console.error(
|
|
2755
|
+
console.error(chalk4.red(`\u2717 ${evt.message}`));
|
|
2617
2756
|
} else {
|
|
2618
|
-
console.error(
|
|
2757
|
+
console.error(chalk4.yellow(`\u26A0 ${evt.message}`));
|
|
2619
2758
|
}
|
|
2620
2759
|
} else if (evt.event === "queued") {
|
|
2621
2760
|
const where = evt.position ? ` (position ${evt.position})` : "";
|
|
2622
2761
|
console.error(
|
|
2623
|
-
|
|
2762
|
+
chalk4.yellow(
|
|
2624
2763
|
`\u23F3 every device is busy \u2014 waiting in the queue${where}, Ctrl-C to give up`
|
|
2625
2764
|
)
|
|
2626
2765
|
);
|
|
2627
2766
|
} else if (evt.event === "tunnel_connected") {
|
|
2628
2767
|
everConnected = true;
|
|
2629
|
-
console.error(
|
|
2768
|
+
console.error(chalk4.green("\u2713 tunnel connected \u2014 edit away, Fast Refresh is live"));
|
|
2630
2769
|
} else if (evt.event === "installing") {
|
|
2631
2770
|
console.error(
|
|
2632
|
-
|
|
2771
|
+
chalk4.dim(" Putting the channel on the Roku \u2014 the link appears once it is up.")
|
|
2633
2772
|
);
|
|
2634
2773
|
} else if (evt.event === "session_ended" && !stopping && handle) {
|
|
2635
|
-
console.error(
|
|
2774
|
+
console.error(chalk4.red("tunnel closed by server \u2014 session ended"));
|
|
2636
2775
|
stopping = true;
|
|
2637
2776
|
endedEmitted = true;
|
|
2638
2777
|
emit(opts.json, evt);
|
|
@@ -2651,20 +2790,20 @@ async function runDevInProcess(opts) {
|
|
|
2651
2790
|
if (!waitingPrinted) {
|
|
2652
2791
|
waitingPrinted = true;
|
|
2653
2792
|
console.error(
|
|
2654
|
-
|
|
2793
|
+
chalk4.dim("\u2026 waiting for the device to come up (tunnel not ready yet)")
|
|
2655
2794
|
);
|
|
2656
2795
|
}
|
|
2657
2796
|
return;
|
|
2658
2797
|
}
|
|
2659
|
-
console.error(
|
|
2798
|
+
console.error(chalk4.yellow("\u2026 tunnel dropped, reconnecting"));
|
|
2660
2799
|
} else if (s === "metro-unreachable") {
|
|
2661
2800
|
nextWarningIsMetroUnreachable = true;
|
|
2662
2801
|
}
|
|
2663
2802
|
}
|
|
2664
2803
|
);
|
|
2665
|
-
const keysLine = rokuBuild ? `Press ${
|
|
2804
|
+
const keysLine = rokuBuild ? `Press ${chalk4.bold("r")} to reload the channel, ${chalk4.bold("q")} or Ctrl-C to end the session.` : "Ctrl-C to end the session.";
|
|
2666
2805
|
console.error(`
|
|
2667
|
-
Watch and interact: ${
|
|
2806
|
+
Watch and interact: ${chalk4.cyan(handle.pageUrl)}
|
|
2668
2807
|
${keysLine}
|
|
2669
2808
|
`);
|
|
2670
2809
|
writeDevState(process.cwd(), {
|
|
@@ -2696,13 +2835,13 @@ async function runDevInProcess(opts) {
|
|
|
2696
2835
|
// Build output and loop notices are human output, so they go to
|
|
2697
2836
|
// stderr like every other line this command prints — stdout stays
|
|
2698
2837
|
// reserved for the `--json` event stream.
|
|
2699
|
-
log: (line) => console.error(
|
|
2838
|
+
log: (line) => console.error(chalk4.dim(line))
|
|
2700
2839
|
}),
|
|
2701
|
-
log: (line) => console.error(
|
|
2840
|
+
log: (line) => console.error(chalk4.yellow(line)),
|
|
2702
2841
|
// A failed reload is an error, not a notice — same red as every other
|
|
2703
2842
|
// failure this command prints, so it doesn't read like the yellow
|
|
2704
2843
|
// "already reloading" line.
|
|
2705
|
-
logError: (line) => console.error(
|
|
2844
|
+
logError: (line) => console.error(chalk4.red(`\u2717 ${line}`)),
|
|
2706
2845
|
...watchSeconds !== void 0 ? {
|
|
2707
2846
|
watchSeconds,
|
|
2708
2847
|
watch: watchProjectFiles(process.cwd(), rokuBuild.artifact, watchIgnore)
|
|
@@ -2710,7 +2849,7 @@ async function runDevInProcess(opts) {
|
|
|
2710
2849
|
});
|
|
2711
2850
|
if (watchSeconds !== void 0) {
|
|
2712
2851
|
console.error(
|
|
2713
|
-
|
|
2852
|
+
chalk4.dim(` Watching for changes \u2014 reloading after ${watchSeconds}s of quiet.`)
|
|
2714
2853
|
);
|
|
2715
2854
|
}
|
|
2716
2855
|
if (process.stdin.isTTY) {
|
|
@@ -2745,7 +2884,7 @@ async function runDevInProcess(opts) {
|
|
|
2745
2884
|
}
|
|
2746
2885
|
const verdict = sessionLivenessVerdict(info);
|
|
2747
2886
|
if (verdict.end && !stopping) {
|
|
2748
|
-
console.error(
|
|
2887
|
+
console.error(chalk4.red(verdict.message));
|
|
2749
2888
|
emit(opts.json, { event: "session_ended", session_id: sessionId, code: 1 });
|
|
2750
2889
|
endedEmitted = true;
|
|
2751
2890
|
await stopSession(1);
|
|
@@ -2754,7 +2893,7 @@ async function runDevInProcess(opts) {
|
|
|
2754
2893
|
const warningVerdict = lateWarningVerdict(info, reportedWarnings);
|
|
2755
2894
|
for (const w of warningVerdict.toReport) {
|
|
2756
2895
|
reportedWarnings.add(w);
|
|
2757
|
-
console.error(
|
|
2896
|
+
console.error(chalk4.yellow(`\u26A0 ${w}`));
|
|
2758
2897
|
emit(opts.json, { event: "warning", message: w });
|
|
2759
2898
|
}
|
|
2760
2899
|
if (warningVerdict.blocking && !stopping) {
|
|
@@ -2772,7 +2911,7 @@ async function runDevInProcess(opts) {
|
|
|
2772
2911
|
}
|
|
2773
2912
|
} catch (err) {
|
|
2774
2913
|
const message = err.message;
|
|
2775
|
-
console.error(
|
|
2914
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2776
2915
|
emit(opts.json, { event: "error", message });
|
|
2777
2916
|
await stopSession(1);
|
|
2778
2917
|
}
|
|
@@ -2802,12 +2941,12 @@ async function dev(opts) {
|
|
|
2802
2941
|
if (opts.json) {
|
|
2803
2942
|
console.log(JSON.stringify(parsed));
|
|
2804
2943
|
} else {
|
|
2805
|
-
console.error(
|
|
2806
|
-
console.error(
|
|
2944
|
+
console.error(chalk4.dim(`Using ${resolveAuth(opts).baseUrl}`));
|
|
2945
|
+
console.error(chalk4.green(`\u2713 dev session started in the background (pid ${parsed.pid})`));
|
|
2807
2946
|
console.error(`
|
|
2808
|
-
Watch and interact: ${
|
|
2809
|
-
console.error(
|
|
2810
|
-
console.error(
|
|
2947
|
+
Watch and interact: ${chalk4.cyan(parsed.url)}`);
|
|
2948
|
+
console.error(chalk4.dim(` vibeview dev-status \u2014 check on it`));
|
|
2949
|
+
console.error(chalk4.dim(` vibeview dev-stop \u2014 end it
|
|
2811
2950
|
`));
|
|
2812
2951
|
}
|
|
2813
2952
|
child.unref();
|
|
@@ -2817,13 +2956,13 @@ async function dev(opts) {
|
|
|
2817
2956
|
settled = true;
|
|
2818
2957
|
rl.close();
|
|
2819
2958
|
emit(opts.json, parsed);
|
|
2820
|
-
if (!opts.json) console.error(
|
|
2959
|
+
if (!opts.json) console.error(chalk4.red(`error: ${parsed.message}`));
|
|
2821
2960
|
reject(new Error(parsed.message));
|
|
2822
2961
|
} else if (parsed.event === "warning") {
|
|
2823
2962
|
if (opts.json) {
|
|
2824
2963
|
console.log(JSON.stringify(parsed));
|
|
2825
2964
|
} else {
|
|
2826
|
-
console.error(
|
|
2965
|
+
console.error(chalk4.yellow(`\u26A0 ${parsed.message}`));
|
|
2827
2966
|
}
|
|
2828
2967
|
}
|
|
2829
2968
|
});
|
|
@@ -2832,14 +2971,14 @@ async function dev(opts) {
|
|
|
2832
2971
|
rl.close();
|
|
2833
2972
|
const message = `dev session exited before starting (code ${code ?? "unknown"})`;
|
|
2834
2973
|
emit(opts.json, { event: "error", message });
|
|
2835
|
-
if (!opts.json) console.error(
|
|
2974
|
+
if (!opts.json) console.error(chalk4.red(`error: ${message}`));
|
|
2836
2975
|
reject(new Error(message));
|
|
2837
2976
|
});
|
|
2838
2977
|
child.on("error", (err) => {
|
|
2839
2978
|
if (settled) return;
|
|
2840
2979
|
rl.close();
|
|
2841
2980
|
emit(opts.json, { event: "error", message: err.message });
|
|
2842
|
-
if (!opts.json) console.error(
|
|
2981
|
+
if (!opts.json) console.error(chalk4.red(`error: ${err.message}`));
|
|
2843
2982
|
reject(err);
|
|
2844
2983
|
});
|
|
2845
2984
|
}).catch(() => {
|
|
@@ -2850,8 +2989,8 @@ async function dev(opts) {
|
|
|
2850
2989
|
}
|
|
2851
2990
|
|
|
2852
2991
|
// src/lib/formatters/devices.ts
|
|
2853
|
-
import
|
|
2854
|
-
var
|
|
2992
|
+
import chalk5 from "chalk";
|
|
2993
|
+
var HEADERS2 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
|
|
2855
2994
|
function formatDevicesHuman(devices) {
|
|
2856
2995
|
if (devices.length === 0) {
|
|
2857
2996
|
return "No devices registered.\n";
|
|
@@ -2864,11 +3003,11 @@ function formatDevicesHuman(devices) {
|
|
|
2864
3003
|
d.os_version,
|
|
2865
3004
|
d.status
|
|
2866
3005
|
]);
|
|
2867
|
-
const widths =
|
|
3006
|
+
const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2868
3007
|
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
2869
|
-
const lines = [
|
|
3008
|
+
const lines = [chalk5.bold(fmt(HEADERS2))];
|
|
2870
3009
|
for (const row of rows.sort(rowSort)) {
|
|
2871
|
-
const statusColored = row[5] === "available" ?
|
|
3010
|
+
const statusColored = row[5] === "available" ? chalk5.green(row[5]) : row[5] === "busy" ? chalk5.yellow(row[5]) : chalk5.dim(row[5]);
|
|
2872
3011
|
lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
|
|
2873
3012
|
}
|
|
2874
3013
|
return lines.join("\n") + "\n";
|
|
@@ -2895,7 +3034,7 @@ function formatDeviceModelsHuman(models) {
|
|
|
2895
3034
|
);
|
|
2896
3035
|
const widths = MODEL_HEADERS.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2897
3036
|
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
2898
|
-
return [
|
|
3037
|
+
return [chalk5.bold(fmt(MODEL_HEADERS)), ...rows.map(fmt)].join("\n") + "\n";
|
|
2899
3038
|
}
|
|
2900
3039
|
|
|
2901
3040
|
// src/agent/registry.ts
|
|
@@ -3350,18 +3489,18 @@ var AGENT_COMMANDS = [
|
|
|
3350
3489
|
];
|
|
3351
3490
|
|
|
3352
3491
|
// src/agent/render.ts
|
|
3353
|
-
import
|
|
3492
|
+
import chalk6 from "chalk";
|
|
3354
3493
|
function renderEnvelope(env) {
|
|
3355
3494
|
const lines = [];
|
|
3356
3495
|
if (env.ok) {
|
|
3357
|
-
lines.push(
|
|
3496
|
+
lines.push(chalk6.green(`ok`) + chalk6.dim(` (${env.tool})`));
|
|
3358
3497
|
} else {
|
|
3359
|
-
lines.push(
|
|
3498
|
+
lines.push(chalk6.red(`error`) + chalk6.dim(` (${env.tool})`));
|
|
3360
3499
|
}
|
|
3361
3500
|
lines.push(...resultLines(env.result));
|
|
3362
3501
|
if (env.observation) {
|
|
3363
3502
|
if (env.observation.kind === "unavailable") {
|
|
3364
|
-
lines.push(
|
|
3503
|
+
lines.push(chalk6.yellow(`warning: ${env.observation.text}`));
|
|
3365
3504
|
} else {
|
|
3366
3505
|
lines.push(env.observation.text);
|
|
3367
3506
|
}
|
|
@@ -3374,17 +3513,17 @@ function resultLines(result) {
|
|
|
3374
3513
|
const lines = [];
|
|
3375
3514
|
if (Array.isArray(r.entries) && typeof r.cursor === "number") {
|
|
3376
3515
|
if (r.entries.length === 0) {
|
|
3377
|
-
lines.push(
|
|
3516
|
+
lines.push(chalk6.dim("(no log lines)"));
|
|
3378
3517
|
}
|
|
3379
3518
|
for (const e of r.entries) {
|
|
3380
3519
|
if (!e || typeof e !== "object") continue;
|
|
3381
3520
|
const entry = e;
|
|
3382
3521
|
const ts = typeof entry.timestamp === "number" ? new Date(entry.timestamp).toISOString().slice(11, 23) : "??:??:??.???";
|
|
3383
3522
|
const level = typeof entry.level === "string" ? entry.level : "info";
|
|
3384
|
-
const levelTag = level === "error" ?
|
|
3385
|
-
lines.push(`${
|
|
3523
|
+
const levelTag = level === "error" ? chalk6.red(level) : level === "warn" ? chalk6.yellow(level) : chalk6.dim(level);
|
|
3524
|
+
lines.push(`${chalk6.dim(ts)} ${levelTag} ${String(entry.message ?? "")}`);
|
|
3386
3525
|
}
|
|
3387
|
-
lines.push(
|
|
3526
|
+
lines.push(chalk6.dim(`cursor: ${r.cursor}`));
|
|
3388
3527
|
}
|
|
3389
3528
|
if (r.found === true && typeof r.ref === "string" && r.ref) {
|
|
3390
3529
|
let found = `found: @${r.ref}`;
|
|
@@ -3396,14 +3535,14 @@ function resultLines(result) {
|
|
|
3396
3535
|
const text = typeof a.text === "string" && a.text ? ` ${JSON.stringify(a.text)}` : "";
|
|
3397
3536
|
return `${ref}${text}`;
|
|
3398
3537
|
});
|
|
3399
|
-
if (alts.length > 0) lines.push(
|
|
3538
|
+
if (alts.length > 0) lines.push(chalk6.dim(`alternatives: ${alts.join(", ")}`));
|
|
3400
3539
|
}
|
|
3401
3540
|
}
|
|
3402
3541
|
if (r.action === "posture_set" || r.action === "rotated") {
|
|
3403
3542
|
const state = deviceStateLine(r);
|
|
3404
3543
|
if (state) lines.push(state);
|
|
3405
3544
|
}
|
|
3406
|
-
if (typeof r.note === "string" && r.note) lines.push(
|
|
3545
|
+
if (typeof r.note === "string" && r.note) lines.push(chalk6.dim(r.note));
|
|
3407
3546
|
if (typeof r.message === "string" && r.message) lines.push(r.message);
|
|
3408
3547
|
if (typeof r.warning === "string" && r.warning) lines.push(r.warning);
|
|
3409
3548
|
return lines;
|
|
@@ -3412,9 +3551,9 @@ function deviceStateLine(r) {
|
|
|
3412
3551
|
const parts = [];
|
|
3413
3552
|
if (r.action === "rotated") {
|
|
3414
3553
|
if (typeof r.device_orientation === "string") {
|
|
3415
|
-
let o = `
|
|
3554
|
+
let o = `device held ${r.device_orientation}`;
|
|
3416
3555
|
if (typeof r.screen_orientation === "string" && r.screen_orientation !== r.device_orientation) {
|
|
3417
|
-
o += ` (screen
|
|
3556
|
+
o += ` (picture on the lit screen: ${r.screen_orientation})`;
|
|
3418
3557
|
}
|
|
3419
3558
|
parts.push(o);
|
|
3420
3559
|
}
|
|
@@ -3667,15 +3806,20 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
3667
3806
|
}
|
|
3668
3807
|
|
|
3669
3808
|
export {
|
|
3809
|
+
parseBuildId,
|
|
3810
|
+
checkDevBuild,
|
|
3811
|
+
buildRows,
|
|
3812
|
+
formatBuildRowsHuman,
|
|
3813
|
+
listBuilds,
|
|
3670
3814
|
readDevState,
|
|
3671
3815
|
clearDevState,
|
|
3816
|
+
readMetroState,
|
|
3817
|
+
killOwnedMetro,
|
|
3672
3818
|
DEV_PLATFORMS,
|
|
3673
3819
|
PLATFORM_NEEDS_METRO,
|
|
3674
3820
|
isDevPlatform,
|
|
3675
3821
|
loadDevConfig,
|
|
3676
3822
|
checkMetro,
|
|
3677
|
-
readMetroState,
|
|
3678
|
-
killOwnedMetro,
|
|
3679
3823
|
buildCommand,
|
|
3680
3824
|
formatDuration,
|
|
3681
3825
|
BUILD_TERMINAL_STATUSES,
|