vibeview 0.26.0 → 0.27.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-JK3T3AMW.js → chunk-46X7UN6Y.js} +76 -22
- package/dist/{chunk-RFFCCPGZ.js → chunk-U7FCGMWG.js} +24 -4
- package/dist/cli.js +80 -58
- package/dist/{server-KYDTXPRZ.js → server-XJREB7KU.js} +4 -4
- package/dist/{upload-app-E32EZXYQ.js → upload-app-QUUXKNR2.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +7 -4
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ApiClient,
|
|
4
|
+
AuthError,
|
|
5
|
+
NotFoundError,
|
|
4
6
|
UsageError,
|
|
5
7
|
createSpinner,
|
|
8
|
+
errorExitCode,
|
|
6
9
|
loadConfig,
|
|
7
10
|
resolveAuth,
|
|
8
11
|
uploadAppBuild
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-U7FCGMWG.js";
|
|
10
13
|
|
|
11
14
|
// src/lib/dev-config.ts
|
|
12
15
|
import { readFileSync, writeFileSync } from "fs";
|
|
@@ -246,6 +249,17 @@ function checkDevBuild(app, buildId, platform) {
|
|
|
246
249
|
|
|
247
250
|
// src/commands/list-builds.ts
|
|
248
251
|
import chalk from "chalk";
|
|
252
|
+
|
|
253
|
+
// src/lib/formatters/time.ts
|
|
254
|
+
function formatUtcTimestamp(iso) {
|
|
255
|
+
if (!iso) return "";
|
|
256
|
+
const hasZone = /(?:Z|[+-]\d{2}:?\d{2})$/i.test(iso);
|
|
257
|
+
const d = new Date(hasZone ? iso : `${iso}Z`);
|
|
258
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
259
|
+
return `${d.toISOString().slice(0, 19).replace("T", " ")} UTC`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/commands/list-builds.ts
|
|
249
263
|
function buildRows(app) {
|
|
250
264
|
return app.builds.map((b) => ({
|
|
251
265
|
id: b.id,
|
|
@@ -269,7 +283,7 @@ function formatBuildRowsHuman(app, rows) {
|
|
|
269
283
|
r.version ? `${r.version}${r.build_number ? ` (${r.build_number})` : ""}` : r.build_number ?? "",
|
|
270
284
|
r.platform,
|
|
271
285
|
r.kind,
|
|
272
|
-
r.uploaded_at
|
|
286
|
+
formatUtcTimestamp(r.uploaded_at),
|
|
273
287
|
(r.note ?? "").split("\n")[0]
|
|
274
288
|
]);
|
|
275
289
|
const widths = HEADERS.map((h, i) => Math.max(h.length, ...cells.map((c) => c[i].length)));
|
|
@@ -293,7 +307,7 @@ async function listBuilds(appId, opts, deps) {
|
|
|
293
307
|
const message = err instanceof Error ? err.message : String(err);
|
|
294
308
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
295
309
|
console.error(chalk.red(`error: ${message}`));
|
|
296
|
-
process.exitCode =
|
|
310
|
+
process.exitCode = errorExitCode(err);
|
|
297
311
|
}
|
|
298
312
|
}
|
|
299
313
|
|
|
@@ -1073,7 +1087,7 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
1073
1087
|
if (!(err instanceof BuildFailError)) {
|
|
1074
1088
|
printFailure(opts, err instanceof Error ? err.message : String(err));
|
|
1075
1089
|
}
|
|
1076
|
-
process.exitCode =
|
|
1090
|
+
process.exitCode = errorExitCode(err);
|
|
1077
1091
|
}
|
|
1078
1092
|
}
|
|
1079
1093
|
function printFailure(opts, message, code) {
|
|
@@ -2410,6 +2424,10 @@ function buildAppStrategy(apps, buildFlag, json, isTTY) {
|
|
|
2410
2424
|
if (json || !isTTY) return "pick";
|
|
2411
2425
|
return "ask";
|
|
2412
2426
|
}
|
|
2427
|
+
function noBuildConfigMessage(platform, interactive) {
|
|
2428
|
+
const addKey = `add platforms.${platform}.build {command, artifact} to vibeview.json`;
|
|
2429
|
+
return interactive ? `no build config for ${platform}: ${addKey}, or run \`vibeview build --platform ${platform}\` once to set it up` : `no build config for ${platform}: ${addKey}`;
|
|
2430
|
+
}
|
|
2413
2431
|
function requireNonInteractive(json, message) {
|
|
2414
2432
|
if (!json) return;
|
|
2415
2433
|
emit(json, { event: "error", code: "config_required", message });
|
|
@@ -2574,15 +2592,17 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2574
2592
|
firstRunMetro,
|
|
2575
2593
|
firstRunBuild
|
|
2576
2594
|
});
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2595
|
+
const commitConfig = () => {
|
|
2596
|
+
saveDevConfig(process.cwd(), cfg);
|
|
2597
|
+
if (isFirstRun) {
|
|
2598
|
+
console.error(chalk4.dim(`Created vibeview.json (platform: ${platform}, app: ${appId}).`));
|
|
2599
|
+
const hint = fillLaterHint(
|
|
2600
|
+
{ build: !firstRunBuild, metro: PLATFORM_NEEDS_METRO[platform] && !firstRunMetro },
|
|
2601
|
+
platform
|
|
2602
|
+
);
|
|
2603
|
+
if (hint) console.error(chalk4.dim(hint));
|
|
2604
|
+
}
|
|
2605
|
+
};
|
|
2586
2606
|
const finalMetro = isFirstRun ? resolveMetro(cfg, platform, opts.metroPort ? parseInt(opts.metroPort, 10) : void 0) : metroResolved;
|
|
2587
2607
|
return {
|
|
2588
2608
|
platform,
|
|
@@ -2592,7 +2612,9 @@ Run \`vibeview dev\` without --app to pick from the list.`
|
|
|
2592
2612
|
metroPort: finalMetro.port,
|
|
2593
2613
|
metroCommand: finalMetro.command,
|
|
2594
2614
|
platformHasMetroOverride: finalMetro.platformHasOverride,
|
|
2595
|
-
builtThisRun
|
|
2615
|
+
builtThisRun,
|
|
2616
|
+
savedConfig: cfg,
|
|
2617
|
+
commitConfig
|
|
2596
2618
|
};
|
|
2597
2619
|
}
|
|
2598
2620
|
async function runDevInProcess(opts) {
|
|
@@ -2641,11 +2663,20 @@ async function runDevInProcess(opts) {
|
|
|
2641
2663
|
process.exit(1);
|
|
2642
2664
|
}
|
|
2643
2665
|
}
|
|
2666
|
+
const interactive = !opts.json && Boolean(process.stdin.isTTY);
|
|
2667
|
+
const plannedBuild = cfg.savedConfig.platforms?.[cfg.platform]?.build;
|
|
2668
|
+
if (!plannedBuild && (opts.build === true && !cfg.builtThisRun || !PLATFORM_NEEDS_METRO[cfg.platform])) {
|
|
2669
|
+
const message = noBuildConfigMessage(cfg.platform, interactive);
|
|
2670
|
+
console.error(chalk4.red(`error: ${message}`));
|
|
2671
|
+
emit(opts.json, { event: "error", code: "config_required", message });
|
|
2672
|
+
process.exit(1);
|
|
2673
|
+
}
|
|
2674
|
+
cfg.commitConfig();
|
|
2644
2675
|
if (opts.build === true && !cfg.builtThisRun) {
|
|
2645
2676
|
const reloaded = loadDevConfig(process.cwd());
|
|
2646
2677
|
const build = reloaded?.platforms?.[cfg.platform]?.build;
|
|
2647
2678
|
if (!build) {
|
|
2648
|
-
const message =
|
|
2679
|
+
const message = noBuildConfigMessage(cfg.platform, interactive);
|
|
2649
2680
|
console.error(chalk4.red(`error: ${message}`));
|
|
2650
2681
|
emit(opts.json, { event: "error", code: "config_required", message });
|
|
2651
2682
|
process.exit(1);
|
|
@@ -2718,7 +2749,7 @@ async function runDevInProcess(opts) {
|
|
|
2718
2749
|
rokuBuild = rokuEntry?.build;
|
|
2719
2750
|
watchIgnore = rokuEntry?.watchIgnore ?? [];
|
|
2720
2751
|
if (!rokuBuild) {
|
|
2721
|
-
const message =
|
|
2752
|
+
const message = noBuildConfigMessage(cfg.platform, interactive);
|
|
2722
2753
|
console.error(chalk4.red(`error: ${message}`));
|
|
2723
2754
|
emit(opts.json, { event: "error", code: "config_required", message });
|
|
2724
2755
|
process.exit(1);
|
|
@@ -3056,6 +3087,26 @@ function formatDeviceModelsHuman(models) {
|
|
|
3056
3087
|
}
|
|
3057
3088
|
|
|
3058
3089
|
// src/agent/registry.ts
|
|
3090
|
+
var PRESS_BUTTONS_BY_PLATFORM = [
|
|
3091
|
+
["iPhone / iPad", ["home", "back", "lock", "siri"]],
|
|
3092
|
+
["Android phone / tablet", ["home", "back", "lock"]],
|
|
3093
|
+
[
|
|
3094
|
+
"Apple TV / Android TV",
|
|
3095
|
+
["dpad_up", "dpad_down", "dpad_left", "dpad_right", "dpad_center", "back", "home"]
|
|
3096
|
+
],
|
|
3097
|
+
[
|
|
3098
|
+
"Roku (beta)",
|
|
3099
|
+
["up", "down", "left", "right", "select", "back", "(dpad_* also work; home is not forwarded)"]
|
|
3100
|
+
]
|
|
3101
|
+
];
|
|
3102
|
+
function pressButtonsHelp() {
|
|
3103
|
+
return [
|
|
3104
|
+
"Valid buttons:",
|
|
3105
|
+
...PRESS_BUTTONS_BY_PLATFORM.map(
|
|
3106
|
+
([platform, buttons]) => ` ${platform}: ${buttons.join(", ")}`
|
|
3107
|
+
)
|
|
3108
|
+
].join("\n");
|
|
3109
|
+
}
|
|
3059
3110
|
var AGENT_COMMANDS = [
|
|
3060
3111
|
{
|
|
3061
3112
|
verb: "ui-tree",
|
|
@@ -3089,7 +3140,7 @@ var AGENT_COMMANDS = [
|
|
|
3089
3140
|
{
|
|
3090
3141
|
verb: "screenshot",
|
|
3091
3142
|
tool: "screenshot",
|
|
3092
|
-
summary: "Capture a screenshot of the current screen. Use it to check how something LOOKS \u2014 the UI tree carries no colour, contrast, or stacking information, so a screen can pass every check the tree can express and still be visibly broken. Saved to disk; over MCP, pass inline: true to receive the image itself.",
|
|
3143
|
+
summary: "Capture a screenshot of the current screen. Use it to check how something LOOKS \u2014 the UI tree carries no colour, contrast, or stacking information, so a screen can pass every check the tree can express and still be visibly broken. Saved to disk; over MCP, pass inline: true to receive the image itself. On a phone or tablet its pixels are the coordinates `tap`/`drag` take, so a point read off it can be tapped as is.",
|
|
3093
3144
|
mutating: false,
|
|
3094
3145
|
params: [
|
|
3095
3146
|
{
|
|
@@ -3324,7 +3375,7 @@ var AGENT_COMMANDS = [
|
|
|
3324
3375
|
cli: "positional",
|
|
3325
3376
|
type: "string",
|
|
3326
3377
|
required: true,
|
|
3327
|
-
description: "Text to type into the focused field."
|
|
3378
|
+
description: "Text to type into the focused field. On a phone or tablet a line break presses Enter, which submits the field (bash: $'query\\n'); `press enter` does the same."
|
|
3328
3379
|
}
|
|
3329
3380
|
]
|
|
3330
3381
|
},
|
|
@@ -3350,7 +3401,7 @@ var AGENT_COMMANDS = [
|
|
|
3350
3401
|
cli: "positional",
|
|
3351
3402
|
type: "string",
|
|
3352
3403
|
required: true,
|
|
3353
|
-
description: "Button to press: home, back, lock, siri, dpad_up, dpad_down, dpad_left, dpad_right,
|
|
3404
|
+
description: "Button to press. iPhone/iPad: home, back, lock, siri. Android: home, back, lock. Apple TV / Android TV: dpad_up, dpad_down, dpad_left, dpad_right, dpad_center, back, home. Roku: up, down, left, right, select, back (dpad_* also work; home is not forwarded)."
|
|
3354
3405
|
}
|
|
3355
3406
|
]
|
|
3356
3407
|
},
|
|
@@ -3358,7 +3409,7 @@ var AGENT_COMMANDS = [
|
|
|
3358
3409
|
// tools.py open_url: { url: string }.
|
|
3359
3410
|
verb: "open-url",
|
|
3360
3411
|
tool: "open_url",
|
|
3361
|
-
summary: "Open a deep link or URL in the app under test.",
|
|
3412
|
+
summary: "Open a deep link or URL in the app under test. The link goes to that app only; one it has no screen for fails rather than opening a browser.",
|
|
3362
3413
|
mutating: true,
|
|
3363
3414
|
params: [
|
|
3364
3415
|
{
|
|
@@ -3787,13 +3838,13 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
3787
3838
|
} catch (err) {
|
|
3788
3839
|
const message = err instanceof Error ? err.message : String(err);
|
|
3789
3840
|
if (opts.json) {
|
|
3790
|
-
const code =
|
|
3841
|
+
const code = err instanceof AuthError ? "auth_failed" : err instanceof NotFoundError ? "not_found" : void 0;
|
|
3791
3842
|
console.log(JSON.stringify({ event: "error", code, message }));
|
|
3792
3843
|
} else {
|
|
3793
3844
|
process.stderr.write(`error: ${message}
|
|
3794
3845
|
`);
|
|
3795
3846
|
}
|
|
3796
|
-
process.exit(
|
|
3847
|
+
process.exit(errorExitCode(err));
|
|
3797
3848
|
}
|
|
3798
3849
|
if (cmd.verb === "screenshot" && envelope.ok && envelope.result) {
|
|
3799
3850
|
const outPath = opts.out || defaultScreenshotPath();
|
|
@@ -3819,6 +3870,7 @@ async function runAgentVerb(cmd, positionals, opts) {
|
|
|
3819
3870
|
console.log(JSON.stringify(envelope));
|
|
3820
3871
|
} else {
|
|
3821
3872
|
console.log(renderEnvelope(envelope));
|
|
3873
|
+
if (cmd.verb === "press" && !envelope.ok) process.stderr.write(pressButtonsHelp() + "\n");
|
|
3822
3874
|
}
|
|
3823
3875
|
process.exit(agentExitCode(envelope));
|
|
3824
3876
|
}
|
|
@@ -3831,6 +3883,7 @@ export {
|
|
|
3831
3883
|
loadDevConfig,
|
|
3832
3884
|
parseBuildId,
|
|
3833
3885
|
checkDevBuild,
|
|
3886
|
+
formatUtcTimestamp,
|
|
3834
3887
|
buildRows,
|
|
3835
3888
|
formatBuildRowsHuman,
|
|
3836
3889
|
listBuilds,
|
|
@@ -3854,6 +3907,7 @@ export {
|
|
|
3854
3907
|
formatDevicesHuman,
|
|
3855
3908
|
filterDeviceModels,
|
|
3856
3909
|
formatDeviceModelsHuman,
|
|
3910
|
+
pressButtonsHelp,
|
|
3857
3911
|
AGENT_COMMANDS,
|
|
3858
3912
|
renderEnvelope,
|
|
3859
3913
|
buildToolCall,
|
|
@@ -42,6 +42,21 @@ import { createWriteStream, mkdirSync as mkdirSync2, renameSync, rmSync } from "
|
|
|
42
42
|
import { basename, join as join2, resolve, sep } from "path";
|
|
43
43
|
import { pipeline } from "stream/promises";
|
|
44
44
|
import { Readable } from "stream";
|
|
45
|
+
var AuthError = class extends Error {
|
|
46
|
+
constructor(message = "Authentication failed. Check your API token.") {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "AuthError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function errorExitCode(err, otherwise = 1) {
|
|
52
|
+
return err instanceof AuthError ? 2 : otherwise;
|
|
53
|
+
}
|
|
54
|
+
function reportCommandError(json, err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
if (json) console.log(JSON.stringify({ event: "error", message }));
|
|
57
|
+
process.stderr.write(`error: ${message}
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
45
60
|
var NotFoundError = class extends Error {
|
|
46
61
|
constructor(message) {
|
|
47
62
|
super(message);
|
|
@@ -52,6 +67,8 @@ function extractDetail(text) {
|
|
|
52
67
|
try {
|
|
53
68
|
const parsed = JSON.parse(text);
|
|
54
69
|
if (parsed && typeof parsed.detail === "string") return parsed.detail;
|
|
70
|
+
if (parsed && parsed.detail && typeof parsed.detail.message === "string")
|
|
71
|
+
return parsed.detail.message;
|
|
55
72
|
} catch {
|
|
56
73
|
}
|
|
57
74
|
return void 0;
|
|
@@ -96,7 +113,7 @@ var ApiClient = class {
|
|
|
96
113
|
});
|
|
97
114
|
if (!resp.ok) {
|
|
98
115
|
const text = await resp.text().catch(() => "");
|
|
99
|
-
if (resp.status === 401) throw new
|
|
116
|
+
if (resp.status === 401) throw new AuthError();
|
|
100
117
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
101
118
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
102
119
|
}
|
|
@@ -168,7 +185,7 @@ var ApiClient = class {
|
|
|
168
185
|
});
|
|
169
186
|
if (!resp.ok) {
|
|
170
187
|
const text = await resp.text().catch(() => "");
|
|
171
|
-
if (resp.status === 401) throw new
|
|
188
|
+
if (resp.status === 401) throw new AuthError();
|
|
172
189
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
173
190
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
174
191
|
}
|
|
@@ -188,7 +205,7 @@ var ApiClient = class {
|
|
|
188
205
|
});
|
|
189
206
|
if (!resp.ok) {
|
|
190
207
|
const text = await resp.text().catch(() => "");
|
|
191
|
-
if (resp.status === 401) throw new
|
|
208
|
+
if (resp.status === 401) throw new AuthError();
|
|
192
209
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
193
210
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
194
211
|
}
|
|
@@ -347,7 +364,7 @@ var ApiClient = class {
|
|
|
347
364
|
const resp = await this.fetchOrExplain(url, { method: "GET", headers });
|
|
348
365
|
if (!resp.ok) {
|
|
349
366
|
const text = await resp.text().catch(() => "");
|
|
350
|
-
if (resp.status === 401) throw new
|
|
367
|
+
if (resp.status === 401) throw new AuthError();
|
|
351
368
|
if (resp.status === 404)
|
|
352
369
|
throw new NotFoundError(`Not found: ${extractDetail(text) ?? `/builds/${id}/artifact`}`);
|
|
353
370
|
throw new Error(extractDetail(text) ?? `API error ${resp.status}: ${text}`);
|
|
@@ -729,6 +746,9 @@ export {
|
|
|
729
746
|
loadConfig,
|
|
730
747
|
saveConfig,
|
|
731
748
|
resolveAuth,
|
|
749
|
+
AuthError,
|
|
750
|
+
errorExitCode,
|
|
751
|
+
reportCommandError,
|
|
732
752
|
NotFoundError,
|
|
733
753
|
ApiClient,
|
|
734
754
|
createSpinner,
|
package/dist/cli.js
CHANGED
|
@@ -11,17 +11,19 @@ import {
|
|
|
11
11
|
formatDeviceModelsHuman,
|
|
12
12
|
formatDevicesHuman,
|
|
13
13
|
formatDuration,
|
|
14
|
+
formatUtcTimestamp,
|
|
14
15
|
killOwnedMetro,
|
|
15
16
|
listBuilds,
|
|
16
17
|
loadDevConfig,
|
|
17
18
|
newTransitionState,
|
|
18
19
|
platformWithArticle,
|
|
20
|
+
pressButtonsHelp,
|
|
19
21
|
printOutcomeLine,
|
|
20
22
|
printTransition,
|
|
21
23
|
readDevState,
|
|
22
24
|
readMetroState,
|
|
23
25
|
runAgentVerb
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-46X7UN6Y.js";
|
|
25
27
|
import {
|
|
26
28
|
ApiClient,
|
|
27
29
|
EXIT_INFRA_ERROR,
|
|
@@ -30,15 +32,17 @@ import {
|
|
|
30
32
|
NotFoundError,
|
|
31
33
|
UsageError,
|
|
32
34
|
createSpinner,
|
|
35
|
+
errorExitCode,
|
|
33
36
|
loadConfig,
|
|
37
|
+
reportCommandError,
|
|
34
38
|
resolveAuth,
|
|
35
39
|
saveConfig,
|
|
36
40
|
uploadApp
|
|
37
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-U7FCGMWG.js";
|
|
38
42
|
|
|
39
43
|
// src/cli.ts
|
|
40
44
|
import { createRequire } from "module";
|
|
41
|
-
import { Command } from "commander";
|
|
45
|
+
import { Command, Option } from "commander";
|
|
42
46
|
|
|
43
47
|
// src/commands/run-suite.ts
|
|
44
48
|
import chalk3 from "chalk";
|
|
@@ -834,10 +838,16 @@ import chalk6 from "chalk";
|
|
|
834
838
|
async function listSuites(options) {
|
|
835
839
|
const { token, baseUrl } = resolveAuth(options);
|
|
836
840
|
const client = new ApiClient(baseUrl, token);
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
+
let suites;
|
|
842
|
+
try {
|
|
843
|
+
suites = await client.listSuites({
|
|
844
|
+
tags: options.tag && options.tag.length > 0 ? options.tag : void 0,
|
|
845
|
+
match: options.match
|
|
846
|
+
});
|
|
847
|
+
} catch (err) {
|
|
848
|
+
reportCommandError(options.output === "json", err);
|
|
849
|
+
process.exit(EXIT_INFRA_ERROR);
|
|
850
|
+
}
|
|
841
851
|
if (options.output === "json") {
|
|
842
852
|
console.log(JSON.stringify(suites, null, 2));
|
|
843
853
|
return;
|
|
@@ -862,7 +872,13 @@ import chalk7 from "chalk";
|
|
|
862
872
|
async function listApps(options) {
|
|
863
873
|
const { token, baseUrl } = resolveAuth(options);
|
|
864
874
|
const client = new ApiClient(baseUrl, token);
|
|
865
|
-
|
|
875
|
+
let apps;
|
|
876
|
+
try {
|
|
877
|
+
apps = await client.listApps();
|
|
878
|
+
} catch (err) {
|
|
879
|
+
reportCommandError(options.output === "json", err);
|
|
880
|
+
process.exit(EXIT_INFRA_ERROR);
|
|
881
|
+
}
|
|
866
882
|
if (options.output === "json") {
|
|
867
883
|
console.log(JSON.stringify(apps, null, 2));
|
|
868
884
|
return;
|
|
@@ -896,7 +912,7 @@ async function stopCommand(sessionId, opts, deps) {
|
|
|
896
912
|
const message = err instanceof Error ? err.message : String(err);
|
|
897
913
|
if (opts.json) console.log(JSON.stringify({ stopped: false, session_id: id, message }));
|
|
898
914
|
console.error(chalk8.red(`error: ${message}`));
|
|
899
|
-
process.exitCode =
|
|
915
|
+
process.exitCode = errorExitCode(err);
|
|
900
916
|
return;
|
|
901
917
|
}
|
|
902
918
|
const state = readDevState(process.cwd());
|
|
@@ -1278,8 +1294,10 @@ import chalk13 from "chalk";
|
|
|
1278
1294
|
// src/lib/formatters/builds.ts
|
|
1279
1295
|
import chalk12 from "chalk";
|
|
1280
1296
|
var HEADERS = ["ID", "PLATFORM", "STATUS", "CREATED", "COST", "APP"];
|
|
1281
|
-
|
|
1282
|
-
|
|
1297
|
+
var UNBILLED_STATUSES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
1298
|
+
function formatCostCell(costMicro, status) {
|
|
1299
|
+
if (costMicro <= 0 && UNBILLED_STATUSES.has(status)) return "-";
|
|
1300
|
+
return `$${(Math.max(costMicro, 0) / 1e6).toFixed(2)}`;
|
|
1283
1301
|
}
|
|
1284
1302
|
var STATUS_COLOR = {
|
|
1285
1303
|
succeeded: chalk12.green,
|
|
@@ -1298,8 +1316,8 @@ function formatBuildsHuman(builds2) {
|
|
|
1298
1316
|
b.id,
|
|
1299
1317
|
b.platform,
|
|
1300
1318
|
b.status,
|
|
1301
|
-
b.created_at
|
|
1302
|
-
formatCostCell(b.cost_micro),
|
|
1319
|
+
formatUtcTimestamp(b.created_at),
|
|
1320
|
+
formatCostCell(b.cost_micro, b.status),
|
|
1303
1321
|
b.app_public_id ?? ""
|
|
1304
1322
|
]);
|
|
1305
1323
|
const widths = HEADERS.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
@@ -1350,7 +1368,7 @@ async function buildsList(opts, deps) {
|
|
|
1350
1368
|
process.exitCode = 0;
|
|
1351
1369
|
} catch (err) {
|
|
1352
1370
|
printError(opts, err);
|
|
1353
|
-
process.exitCode =
|
|
1371
|
+
process.exitCode = errorExitCode(err);
|
|
1354
1372
|
}
|
|
1355
1373
|
}
|
|
1356
1374
|
async function buildsLogs(id, opts, deps) {
|
|
@@ -1373,7 +1391,7 @@ async function buildsLogs(id, opts, deps) {
|
|
|
1373
1391
|
process.exitCode = final.status === "succeeded" ? 0 : 1;
|
|
1374
1392
|
} catch (err) {
|
|
1375
1393
|
printError(opts, err);
|
|
1376
|
-
process.exitCode =
|
|
1394
|
+
process.exitCode = errorExitCode(err);
|
|
1377
1395
|
}
|
|
1378
1396
|
}
|
|
1379
1397
|
var MAX_CONSECUTIVE_POLL_FAILURES = 5;
|
|
@@ -1466,7 +1484,7 @@ async function buildsDownload(id, opts, deps) {
|
|
|
1466
1484
|
process.exitCode = 0;
|
|
1467
1485
|
} catch (err) {
|
|
1468
1486
|
printError(opts, err);
|
|
1469
|
-
process.exitCode =
|
|
1487
|
+
process.exitCode = errorExitCode(err);
|
|
1470
1488
|
}
|
|
1471
1489
|
}
|
|
1472
1490
|
async function buildsCancel(id, opts, deps) {
|
|
@@ -1483,7 +1501,7 @@ async function buildsCancel(id, opts, deps) {
|
|
|
1483
1501
|
process.exitCode = 0;
|
|
1484
1502
|
} catch (err) {
|
|
1485
1503
|
printError(opts, err);
|
|
1486
|
-
process.exitCode =
|
|
1504
|
+
process.exitCode = errorExitCode(err);
|
|
1487
1505
|
}
|
|
1488
1506
|
}
|
|
1489
1507
|
async function buildsDeleteArtifact(id, opts, deps) {
|
|
@@ -1500,7 +1518,7 @@ async function buildsDeleteArtifact(id, opts, deps) {
|
|
|
1500
1518
|
process.exitCode = 0;
|
|
1501
1519
|
} catch (err) {
|
|
1502
1520
|
printError(opts, err);
|
|
1503
|
-
process.exitCode =
|
|
1521
|
+
process.exitCode = errorExitCode(err);
|
|
1504
1522
|
}
|
|
1505
1523
|
}
|
|
1506
1524
|
function printError(opts, err) {
|
|
@@ -1692,7 +1710,7 @@ async function submitCommand(opts, deps) {
|
|
|
1692
1710
|
process.exitCode = final.status === "succeeded" ? 0 : 1;
|
|
1693
1711
|
} catch (err) {
|
|
1694
1712
|
printError2(opts, err);
|
|
1695
|
-
process.exitCode =
|
|
1713
|
+
process.exitCode = errorExitCode(err);
|
|
1696
1714
|
}
|
|
1697
1715
|
}
|
|
1698
1716
|
async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
@@ -1723,7 +1741,7 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
1723
1741
|
process.exitCode = result.ready ? 0 : 1;
|
|
1724
1742
|
} catch (err) {
|
|
1725
1743
|
printError2(opts, err);
|
|
1726
|
-
process.exitCode =
|
|
1744
|
+
process.exitCode = errorExitCode(err);
|
|
1727
1745
|
}
|
|
1728
1746
|
}
|
|
1729
1747
|
async function resolveLatestBuild(platform, deps) {
|
|
@@ -1853,7 +1871,7 @@ async function versionsList(opts, deps) {
|
|
|
1853
1871
|
process.exitCode = 0;
|
|
1854
1872
|
} catch (err) {
|
|
1855
1873
|
printError3(opts, err);
|
|
1856
|
-
process.exitCode =
|
|
1874
|
+
process.exitCode = errorExitCode(err);
|
|
1857
1875
|
}
|
|
1858
1876
|
}
|
|
1859
1877
|
async function versionsSet(buildNumber, opts, deps) {
|
|
@@ -1886,7 +1904,7 @@ async function versionsSet(buildNumber, opts, deps) {
|
|
|
1886
1904
|
process.exitCode = 0;
|
|
1887
1905
|
} catch (err) {
|
|
1888
1906
|
printError3(opts, err);
|
|
1889
|
-
process.exitCode =
|
|
1907
|
+
process.exitCode = errorExitCode(err);
|
|
1890
1908
|
}
|
|
1891
1909
|
}
|
|
1892
1910
|
var HEADERS2 = ["PLATFORM", "BUILD NUMBER", "UPDATED"];
|
|
@@ -1962,7 +1980,7 @@ Create an admin API token in the dashboard and use ${chalk16.bold("--token")} or
|
|
|
1962
1980
|
console.log(JSON.stringify({ event: "error", message }));
|
|
1963
1981
|
}
|
|
1964
1982
|
console.error(chalk16.red(`error: ${message}`));
|
|
1965
|
-
process.exitCode =
|
|
1983
|
+
process.exitCode = errorExitCode(err);
|
|
1966
1984
|
}
|
|
1967
1985
|
}
|
|
1968
1986
|
|
|
@@ -2040,7 +2058,7 @@ async function envList(opts, deps) {
|
|
|
2040
2058
|
process.exitCode = 0;
|
|
2041
2059
|
} catch (err) {
|
|
2042
2060
|
printError4(opts, err);
|
|
2043
|
-
process.exitCode =
|
|
2061
|
+
process.exitCode = errorExitCode(err);
|
|
2044
2062
|
}
|
|
2045
2063
|
}
|
|
2046
2064
|
function formatEnvVarsHuman(variables) {
|
|
@@ -2089,7 +2107,7 @@ async function envSet(name, value, opts, deps) {
|
|
|
2089
2107
|
process.exitCode = 0;
|
|
2090
2108
|
} catch (err) {
|
|
2091
2109
|
printError4(opts, err);
|
|
2092
|
-
process.exitCode =
|
|
2110
|
+
process.exitCode = errorExitCode(err);
|
|
2093
2111
|
}
|
|
2094
2112
|
}
|
|
2095
2113
|
function resolveVisibility(opts) {
|
|
@@ -2122,7 +2140,7 @@ async function envRm(name, opts, deps) {
|
|
|
2122
2140
|
process.exitCode = 0;
|
|
2123
2141
|
} catch (err) {
|
|
2124
2142
|
printError4(opts, err);
|
|
2125
|
-
process.exitCode =
|
|
2143
|
+
process.exitCode = errorExitCode(err);
|
|
2126
2144
|
}
|
|
2127
2145
|
}
|
|
2128
2146
|
async function envPull(opts, deps) {
|
|
@@ -2154,7 +2172,7 @@ async function envPull(opts, deps) {
|
|
|
2154
2172
|
process.exitCode = 0;
|
|
2155
2173
|
} catch (err) {
|
|
2156
2174
|
printError4(opts, err);
|
|
2157
|
-
process.exitCode =
|
|
2175
|
+
process.exitCode = errorExitCode(err);
|
|
2158
2176
|
}
|
|
2159
2177
|
}
|
|
2160
2178
|
function isLossyEnvValue(value) {
|
|
@@ -2347,7 +2365,7 @@ async function credentialsAdd(opts, deps) {
|
|
|
2347
2365
|
process.exitCode = 0;
|
|
2348
2366
|
} catch (err) {
|
|
2349
2367
|
printError5(opts, err);
|
|
2350
|
-
process.exitCode =
|
|
2368
|
+
process.exitCode = errorExitCode(err);
|
|
2351
2369
|
}
|
|
2352
2370
|
}
|
|
2353
2371
|
async function credentialsGenerate(opts, deps) {
|
|
@@ -2421,7 +2439,7 @@ async function credentialsGenerate(opts, deps) {
|
|
|
2421
2439
|
process.exitCode = 0;
|
|
2422
2440
|
} catch (err) {
|
|
2423
2441
|
printError5(opts, err);
|
|
2424
|
-
process.exitCode =
|
|
2442
|
+
process.exitCode = errorExitCode(err);
|
|
2425
2443
|
}
|
|
2426
2444
|
}
|
|
2427
2445
|
async function generateAppleCredential(platform, opts, d) {
|
|
@@ -2457,7 +2475,7 @@ async function generateAppleCredential(platform, opts, d) {
|
|
|
2457
2475
|
process.exitCode = 0;
|
|
2458
2476
|
} catch (err) {
|
|
2459
2477
|
printError5(opts, err);
|
|
2460
|
-
process.exitCode =
|
|
2478
|
+
process.exitCode = errorExitCode(err);
|
|
2461
2479
|
}
|
|
2462
2480
|
}
|
|
2463
2481
|
async function credentialsDownload(name, opts, deps) {
|
|
@@ -2532,7 +2550,7 @@ async function credentialsDownload(name, opts, deps) {
|
|
|
2532
2550
|
process.exitCode = 0;
|
|
2533
2551
|
} catch (err) {
|
|
2534
2552
|
printError5(opts, err);
|
|
2535
|
-
process.exitCode =
|
|
2553
|
+
process.exitCode = errorExitCode(err);
|
|
2536
2554
|
}
|
|
2537
2555
|
}
|
|
2538
2556
|
async function ascKeyAdd(opts, deps) {
|
|
@@ -2572,7 +2590,7 @@ async function ascKeyAdd(opts, deps) {
|
|
|
2572
2590
|
process.exitCode = 0;
|
|
2573
2591
|
} catch (err) {
|
|
2574
2592
|
printError5(opts, err);
|
|
2575
|
-
process.exitCode =
|
|
2593
|
+
process.exitCode = errorExitCode(err);
|
|
2576
2594
|
}
|
|
2577
2595
|
}
|
|
2578
2596
|
async function ascKeyList(opts, deps) {
|
|
@@ -2587,7 +2605,7 @@ async function ascKeyList(opts, deps) {
|
|
|
2587
2605
|
process.exitCode = 0;
|
|
2588
2606
|
} catch (err) {
|
|
2589
2607
|
printError5(opts, err);
|
|
2590
|
-
process.exitCode =
|
|
2608
|
+
process.exitCode = errorExitCode(err);
|
|
2591
2609
|
}
|
|
2592
2610
|
}
|
|
2593
2611
|
function formatAscKeysHuman(keys) {
|
|
@@ -2595,7 +2613,7 @@ function formatAscKeysHuman(keys) {
|
|
|
2595
2613
|
return "No App Store Connect keys found.\n";
|
|
2596
2614
|
}
|
|
2597
2615
|
const HEADERS3 = ["NAME", "KEY ID", "ISSUER ID", "CREATED"];
|
|
2598
|
-
const rows = keys.map((k) => [k.name, k.key_id, k.issuer_id, k.created_at
|
|
2616
|
+
const rows = keys.map((k) => [k.name, k.key_id, k.issuer_id, formatUtcTimestamp(k.created_at)]);
|
|
2599
2617
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2600
2618
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2601
2619
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
@@ -2615,7 +2633,7 @@ async function ascKeyRm(name, opts, deps) {
|
|
|
2615
2633
|
process.exitCode = 0;
|
|
2616
2634
|
} catch (err) {
|
|
2617
2635
|
printError5(opts, err);
|
|
2618
|
-
process.exitCode =
|
|
2636
|
+
process.exitCode = errorExitCode(err);
|
|
2619
2637
|
}
|
|
2620
2638
|
}
|
|
2621
2639
|
async function playKeyAdd(opts, deps) {
|
|
@@ -2648,7 +2666,7 @@ async function playKeyAdd(opts, deps) {
|
|
|
2648
2666
|
process.exitCode = 0;
|
|
2649
2667
|
} catch (err) {
|
|
2650
2668
|
printError5(opts, err);
|
|
2651
|
-
process.exitCode =
|
|
2669
|
+
process.exitCode = errorExitCode(err);
|
|
2652
2670
|
}
|
|
2653
2671
|
}
|
|
2654
2672
|
async function playKeyList(opts, deps) {
|
|
@@ -2663,7 +2681,7 @@ async function playKeyList(opts, deps) {
|
|
|
2663
2681
|
process.exitCode = 0;
|
|
2664
2682
|
} catch (err) {
|
|
2665
2683
|
printError5(opts, err);
|
|
2666
|
-
process.exitCode =
|
|
2684
|
+
process.exitCode = errorExitCode(err);
|
|
2667
2685
|
}
|
|
2668
2686
|
}
|
|
2669
2687
|
function formatPlayKeysHuman(keys) {
|
|
@@ -2671,7 +2689,7 @@ function formatPlayKeysHuman(keys) {
|
|
|
2671
2689
|
return "No Google Play keys found.\n";
|
|
2672
2690
|
}
|
|
2673
2691
|
const HEADERS3 = ["NAME", "CLIENT EMAIL", "CREATED"];
|
|
2674
|
-
const rows = keys.map((k) => [k.name, k.client_email, k.created_at
|
|
2692
|
+
const rows = keys.map((k) => [k.name, k.client_email, formatUtcTimestamp(k.created_at)]);
|
|
2675
2693
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2676
2694
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2677
2695
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
@@ -2691,7 +2709,7 @@ async function playKeyRm(name, opts, deps) {
|
|
|
2691
2709
|
process.exitCode = 0;
|
|
2692
2710
|
} catch (err) {
|
|
2693
2711
|
printError5(opts, err);
|
|
2694
|
-
process.exitCode =
|
|
2712
|
+
process.exitCode = errorExitCode(err);
|
|
2695
2713
|
}
|
|
2696
2714
|
}
|
|
2697
2715
|
async function credentialsList(opts, deps) {
|
|
@@ -2712,7 +2730,7 @@ async function credentialsList(opts, deps) {
|
|
|
2712
2730
|
process.exitCode = 0;
|
|
2713
2731
|
} catch (err) {
|
|
2714
2732
|
printError5(opts, err);
|
|
2715
|
-
process.exitCode =
|
|
2733
|
+
process.exitCode = errorExitCode(err);
|
|
2716
2734
|
}
|
|
2717
2735
|
}
|
|
2718
2736
|
function formatIdentity(c) {
|
|
@@ -2731,10 +2749,10 @@ function earliestExpiry(c) {
|
|
|
2731
2749
|
function formatExpires(c) {
|
|
2732
2750
|
const parts = [];
|
|
2733
2751
|
if (c.profile_expires_at) {
|
|
2734
|
-
if (c.expires_at) parts.push(`cert ${c.expires_at}`);
|
|
2735
|
-
parts.push(`profile ${c.profile_expires_at}`);
|
|
2752
|
+
if (c.expires_at) parts.push(`cert ${formatUtcTimestamp(c.expires_at)}`);
|
|
2753
|
+
parts.push(`profile ${formatUtcTimestamp(c.profile_expires_at)}`);
|
|
2736
2754
|
} else if (c.expires_at) {
|
|
2737
|
-
parts.push(c.expires_at);
|
|
2755
|
+
parts.push(formatUtcTimestamp(c.expires_at));
|
|
2738
2756
|
}
|
|
2739
2757
|
if (parts.length === 0) return "";
|
|
2740
2758
|
const cell = parts.join(" \xB7 ");
|
|
@@ -2771,7 +2789,7 @@ async function credentialsRm(name, opts, deps) {
|
|
|
2771
2789
|
process.exitCode = 0;
|
|
2772
2790
|
} catch (err) {
|
|
2773
2791
|
printError5(opts, err);
|
|
2774
|
-
process.exitCode =
|
|
2792
|
+
process.exitCode = errorExitCode(err);
|
|
2775
2793
|
}
|
|
2776
2794
|
}
|
|
2777
2795
|
function printKeystoreWriteFailure(opts, err, result) {
|
|
@@ -2849,14 +2867,14 @@ async function webhooksAdd(opts, deps) {
|
|
|
2849
2867
|
process.exitCode = 0;
|
|
2850
2868
|
} catch (err) {
|
|
2851
2869
|
printError6(opts, err);
|
|
2852
|
-
process.exitCode =
|
|
2870
|
+
process.exitCode = errorExitCode(err);
|
|
2853
2871
|
}
|
|
2854
2872
|
}
|
|
2855
2873
|
function formatLastDelivery(ld) {
|
|
2856
2874
|
if (!ld) return "\u2014";
|
|
2857
2875
|
if (ld.status === "pending") return "pending";
|
|
2858
2876
|
const code = ld.response_code != null ? ` (${ld.response_code})` : "";
|
|
2859
|
-
const at = ld.at != null ? ` ${ld.at}` : "";
|
|
2877
|
+
const at = ld.at != null ? ` ${formatUtcTimestamp(ld.at)}` : "";
|
|
2860
2878
|
return `${ld.status}${code}${at}`;
|
|
2861
2879
|
}
|
|
2862
2880
|
function formatWebhooksHuman(webhooks2) {
|
|
@@ -2869,7 +2887,7 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
2869
2887
|
w.url,
|
|
2870
2888
|
w.active ? "yes" : "no",
|
|
2871
2889
|
formatLastDelivery(w.last_delivery),
|
|
2872
|
-
w.created_at
|
|
2890
|
+
formatUtcTimestamp(w.created_at)
|
|
2873
2891
|
]);
|
|
2874
2892
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2875
2893
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
@@ -2890,7 +2908,7 @@ async function webhooksList(opts, deps) {
|
|
|
2890
2908
|
process.exitCode = 0;
|
|
2891
2909
|
} catch (err) {
|
|
2892
2910
|
printError6(opts, err);
|
|
2893
|
-
process.exitCode =
|
|
2911
|
+
process.exitCode = errorExitCode(err);
|
|
2894
2912
|
}
|
|
2895
2913
|
}
|
|
2896
2914
|
async function webhooksRm(id, opts, deps) {
|
|
@@ -2905,7 +2923,7 @@ async function webhooksRm(id, opts, deps) {
|
|
|
2905
2923
|
process.exitCode = 0;
|
|
2906
2924
|
} catch (err) {
|
|
2907
2925
|
printError6(opts, err);
|
|
2908
|
-
process.exitCode =
|
|
2926
|
+
process.exitCode = errorExitCode(err);
|
|
2909
2927
|
}
|
|
2910
2928
|
}
|
|
2911
2929
|
function parseUtcTimestamp(iso) {
|
|
@@ -2939,7 +2957,7 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2939
2957
|
} else {
|
|
2940
2958
|
if (!opts.json) {
|
|
2941
2959
|
const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
|
|
2942
|
-
console.error(chalk19.red(`\u2717 Failed (${code}) at ${ld.at}`));
|
|
2960
|
+
console.error(chalk19.red(`\u2717 Failed (${code}) at ${formatUtcTimestamp(ld.at)}`));
|
|
2943
2961
|
}
|
|
2944
2962
|
process.exitCode = 1;
|
|
2945
2963
|
}
|
|
@@ -2955,7 +2973,7 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2955
2973
|
process.exitCode = 1;
|
|
2956
2974
|
} catch (err) {
|
|
2957
2975
|
printError6(opts, err);
|
|
2958
|
-
process.exitCode =
|
|
2976
|
+
process.exitCode = errorExitCode(err);
|
|
2959
2977
|
}
|
|
2960
2978
|
}
|
|
2961
2979
|
function printError6(opts, err) {
|
|
@@ -2978,8 +2996,7 @@ async function listDevicesCommand(options) {
|
|
|
2978
2996
|
}
|
|
2979
2997
|
process.exit(EXIT_PASS);
|
|
2980
2998
|
} catch (err) {
|
|
2981
|
-
|
|
2982
|
-
`);
|
|
2999
|
+
reportCommandError(options.output === "json", err);
|
|
2983
3000
|
process.exit(EXIT_INFRA_ERROR);
|
|
2984
3001
|
}
|
|
2985
3002
|
return;
|
|
@@ -2997,8 +3014,7 @@ async function listDevicesCommand(options) {
|
|
|
2997
3014
|
}
|
|
2998
3015
|
process.exit(EXIT_PASS);
|
|
2999
3016
|
} catch (err) {
|
|
3000
|
-
|
|
3001
|
-
`);
|
|
3017
|
+
reportCommandError(options.output === "json", err);
|
|
3002
3018
|
process.exit(EXIT_INFRA_ERROR);
|
|
3003
3019
|
}
|
|
3004
3020
|
}
|
|
@@ -3271,7 +3287,11 @@ program.command("list-apps").description("List apps in your organization").optio
|
|
|
3271
3287
|
program.command("list-builds <app>").description(
|
|
3272
3288
|
"List an app's uploaded builds, newest first: id, version, platform, debug/release, upload time and note (the ids `dev --build <id>` and `run-test --build` take)"
|
|
3273
3289
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the builds as a JSON array", false).action((app, opts) => listBuilds(app, opts));
|
|
3274
|
-
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").
|
|
3290
|
+
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").addOption(
|
|
3291
|
+
new Option("--type <type>", "Filter by device type").choices(["ios", "android", "roku"])
|
|
3292
|
+
).addOption(
|
|
3293
|
+
new Option("--category <category>", "Filter by category").choices(["phone", "tablet", "tv"])
|
|
3294
|
+
).addOption(new Option("--status <status>", "Filter by status").choices(["available", "busy"])).option(
|
|
3275
3295
|
"--models",
|
|
3276
3296
|
"List the device models you can start instead (the names `dev --model` takes)",
|
|
3277
3297
|
false
|
|
@@ -3445,8 +3465,8 @@ program.command("dev-reload").description(
|
|
|
3445
3465
|
program.command("mcp").description(
|
|
3446
3466
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
3447
3467
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
3448
|
-
const { runMcpServer } = await import("./server-
|
|
3449
|
-
await runMcpServer(opts);
|
|
3468
|
+
const { runMcpServer } = await import("./server-XJREB7KU.js");
|
|
3469
|
+
await runMcpServer({ ...opts, version: pkg.version });
|
|
3450
3470
|
});
|
|
3451
3471
|
program.command("agent-setup").description(
|
|
3452
3472
|
"Make VibeView discoverable to your coding agent: install the vibeview-agent skill (and optionally register the MCP server with Claude Code)"
|
|
@@ -3456,6 +3476,8 @@ for (const cmd of AGENT_COMMANDS) {
|
|
|
3456
3476
|
const flagParams = cmd.params.filter((p) => p.cli !== "positional");
|
|
3457
3477
|
const argSignature = positionalParams.map((p) => p.required ? `<${p.name}>` : `[${p.name}]`).join(" ");
|
|
3458
3478
|
let command = program.command(argSignature ? `${cmd.verb} ${argSignature}` : cmd.verb).description(cmd.summary);
|
|
3479
|
+
if (cmd.verb === "press") command.addHelpText("after", `
|
|
3480
|
+
${pressButtonsHelp()}`);
|
|
3459
3481
|
for (const param of flagParams) {
|
|
3460
3482
|
const flagArg = param.type === "number" ? `${param.cli} <n>` : `${param.cli} <value>`;
|
|
3461
3483
|
command = command.option(flagArg, param.description);
|
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
readDevState,
|
|
22
22
|
renderEnvelope,
|
|
23
23
|
startDevSession
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-46X7UN6Y.js";
|
|
25
25
|
import {
|
|
26
26
|
ApiClient,
|
|
27
27
|
resolveAuth
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-U7FCGMWG.js";
|
|
29
29
|
|
|
30
30
|
// src/mcp/server.ts
|
|
31
31
|
import { writeFileSync } from "fs";
|
|
@@ -377,7 +377,7 @@ async function handleUploadApp(args, deps) {
|
|
|
377
377
|
}
|
|
378
378
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
379
379
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
380
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
380
|
+
const { uploadAppBuild } = await import("./upload-app-QUUXKNR2.js");
|
|
381
381
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
382
382
|
return result2;
|
|
383
383
|
});
|
|
@@ -578,7 +578,7 @@ async function createMcpServer(opts) {
|
|
|
578
578
|
agentTool: client.agentTool.bind(client),
|
|
579
579
|
devSession
|
|
580
580
|
};
|
|
581
|
-
const server = new McpServer({ name: "vibeview", version: "
|
|
581
|
+
const server = new McpServer({ name: "vibeview", version: opts.version ?? "0.0.0" });
|
|
582
582
|
function zodShape(def) {
|
|
583
583
|
const shape = {};
|
|
584
584
|
for (const [key, prop] of Object.entries(def.properties)) {
|
package/package.json
CHANGED
|
@@ -134,6 +134,9 @@ it rather than letting it appear unexplained in their `git status`.
|
|
|
134
134
|
```bash
|
|
135
135
|
vibeview scroll-to "Delete account" --session <id>
|
|
136
136
|
```
|
|
137
|
+
A `scroll-to` that never finds its text, or a `type` whose field does not
|
|
138
|
+
end up holding the text, prints `error` and exits 1 — check the reason
|
|
139
|
+
line and the screen before retrying.
|
|
137
140
|
- **Precise two-point gestures** (sliders, drag-and-drop reorder, map
|
|
138
141
|
panning) need `drag` — `swipe`/`scroll` are direction-only and can't
|
|
139
142
|
express them. Each end is independently a ref or `x,y`:
|
|
@@ -247,18 +250,18 @@ human-readable text.
|
|
|
247
250
|
|------|------|---------|
|
|
248
251
|
| `ui-tree` | — | Fetch the ref-annotated UI tree for the current screen. |
|
|
249
252
|
| `logs` | `[--tail <n>] [--since <cursor>]` | Read recent app logs from the device (JS console, native errors, crash messages). The response's `cursor` feeds the next call's `--since`. |
|
|
250
|
-
| `screenshot` | `[--out <path>]` | Capture a screenshot of the current screen and save it to disk. |
|
|
251
|
-
| `tap` | `<target>` | Tap an element by ref (e.g. `@e5`), or
|
|
253
|
+
| `screenshot` | `[--out <path>]` | Capture a screenshot of the current screen and save it to disk. On a phone or tablet its pixels are tap coordinates: a point read off it can be tapped as is — never rescale. |
|
|
254
|
+
| `tap` | `<target>` | Tap an element by ref (e.g. `@e5`), or coordinates (e.g. `100,200`) in the space of the tree's rects and the screenshot's pixels. |
|
|
252
255
|
| `long-press` | `<ref>` `[--ms <n>]` | Long-press (touch and hold) an element by its ref. |
|
|
253
256
|
| `swipe` | `<direction>` `[--distance <short\|medium\|long>]` | Perform a scrollbar-semantic swipe gesture in one of four directions. |
|
|
254
257
|
| `scroll` | `<direction>` | Scroll the current view up, down, left, or right using a gesture preset. |
|
|
255
258
|
| `scroll-to` | `<text>` `[--direction <up\|down\|left\|right>] [--max-scrolls <n>] [--element-type <type>]` | Scroll until an element matching the text is visible. Stops at end of list. |
|
|
256
259
|
| `drag` | `<from>` `<to>` `[--velocity <n>] [--press-ms <n>] [--hold-ms <n>]` | Drag between two points, each an `@ref` or `x,y`. The only precise two-point gesture. |
|
|
257
260
|
| `alert` | `<get\|accept\|dismiss>` `[--button <label>]` | Inspect or respond to a system alert on iOS or Apple TV. |
|
|
258
|
-
| `type` | `<text>` | Type text into the currently focused input field. |
|
|
261
|
+
| `type` | `<text>` | Type text into the currently focused input field. On a phone or tablet a line break presses Enter and submits the field (`$'query\n'`), like `press enter`. |
|
|
259
262
|
| `clear-text` | — | Clear the text in the currently focused input field. |
|
|
260
263
|
| `press` | `<button>` | Press a device button or perform a system gesture (home, back, d-pad, etc). |
|
|
261
|
-
| `open-url` | `<url>` | Open a deep link or URL in the app under test. |
|
|
264
|
+
| `open-url` | `<url>` | Open a deep link or URL in the app under test. The link goes to that app only; a link it has no screen for fails (no browser). |
|
|
262
265
|
| `set-posture` | `<closed\|partial\|open>` or `--angle <0-180>` | Fold or unfold a foldable device (form factor `foldable`: the iPhone Duo, Android foldables) to a preset or an exact hinge angle — exactly one. Reports posture, angle and lit screen. Errors on a device without a hinge. |
|
|
263
266
|
| `rotate` | `[--degrees <90\|180\|270>]` | Rotate the device and report the new orientation. Phones/tablets and Android foldables toggle portrait/landscape (90 only); the iPhone Duo turns a quarter clockwise by default. Not on TV. |
|
|
264
267
|
| `wait` | `[--ms <n>]` | Wait for a specified number of seconds before continuing (default: 2s). |
|