vibeview 0.46.0 → 0.48.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-GDTLLJFL.js → chunk-3LXR2YCS.js} +5 -0
- package/dist/{chunk-NHM2FHA2.js → chunk-OMIZWILH.js} +58 -3
- package/dist/cli.js +182 -135
- package/dist/{server-W66J6PY5.js → server-3JZ2MN5N.js} +61 -6
- package/dist/{upload-app-ZEQMGZIT.js → upload-app-TDR6PIWD.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +14 -0
|
@@ -331,6 +331,11 @@ var ApiClient = class {
|
|
|
331
331
|
async listApps() {
|
|
332
332
|
return this.requestRaw("GET", "/apps/");
|
|
333
333
|
}
|
|
334
|
+
/** POST /api/v1/agent-reports — a bug, friction or idea about VibeView
|
|
335
|
+
* itself. The server attaches the session's context. */
|
|
336
|
+
async sendFeedback(body) {
|
|
337
|
+
return this.requestRaw("POST", "/agent-reports", body);
|
|
338
|
+
}
|
|
334
339
|
/** GET /api/v1/apps/{appPublicId} — the app with its uploaded builds,
|
|
335
340
|
* newest first. */
|
|
336
341
|
async getApp(appId) {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveArtifact,
|
|
14
14
|
resolveAuth,
|
|
15
15
|
uploadAppBuild
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-3LXR2YCS.js";
|
|
17
17
|
|
|
18
18
|
// src/lib/formatters/not-checked.ts
|
|
19
19
|
function notCheckedSteps(steps) {
|
|
@@ -190,6 +190,24 @@ function validateBuildEnv(env, platformKey) {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
+
var XCODE_PLATFORMS = ["ios", "tvos"];
|
|
194
|
+
var XCODE_VERSION_RE = /^\d+\.\d+$/;
|
|
195
|
+
function validateBuildXcode(xcode, platformKey) {
|
|
196
|
+
if (xcode === void 0) return;
|
|
197
|
+
if (!XCODE_PLATFORMS.includes(platformKey)) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`vibeview.json: platforms.${platformKey}.build.xcode only applies to ios and tvos builds \u2014 remove it from the ${platformKey} block`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (typeof xcode !== "string" || !XCODE_VERSION_RE.test(xcode)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`vibeview.json: platforms.${platformKey}.build.xcode must be a version string like "27.1"`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function platformUsesXcode(platformKey) {
|
|
209
|
+
return XCODE_PLATFORMS.includes(platformKey);
|
|
210
|
+
}
|
|
193
211
|
function saveDevConfig(cwd, cfg) {
|
|
194
212
|
const target = join(cwd, FILE);
|
|
195
213
|
const tmp = join(cwd, `.${FILE}.${process.pid}.${Date.now()}.tmp`);
|
|
@@ -617,6 +635,19 @@ function clearDevStateIfOwned(cwd, pid) {
|
|
|
617
635
|
if (found && found.state.pid === pid) clearDevState(cwd);
|
|
618
636
|
}
|
|
619
637
|
|
|
638
|
+
// src/lib/feedback.ts
|
|
639
|
+
var FEEDBACK_KINDS = ["bug", "friction", "idea"];
|
|
640
|
+
function isFeedbackKind(value) {
|
|
641
|
+
return typeof value === "string" && FEEDBACK_KINDS.includes(value);
|
|
642
|
+
}
|
|
643
|
+
var FEEDBACK_RULES = "Only for problems with VibeView itself (the CLI, MCP tools, devices, sessions, runs, dashboard, docs) \u2014 never for bugs in the user's own app. Show the user what you will send and get their OK first. Never include passwords, tokens, API keys, test data values or personal data. One report per problem; do not resend it. kind: bug (something broke), friction (it worked but was confusing, slow or awkward), idea (a missing capability). user_quote: the user's own words, verbatim \u2014 never a paraphrase.";
|
|
644
|
+
function feedbackSentLine(result) {
|
|
645
|
+
if (result.duplicate) {
|
|
646
|
+
return `This was already sent (report ${result.id}); no need to send it again.`;
|
|
647
|
+
}
|
|
648
|
+
return `Sent (report ${result.id}). The VibeView team reads every report; you'll get a notification if we reply.`;
|
|
649
|
+
}
|
|
650
|
+
|
|
620
651
|
// src/lib/dev-use.ts
|
|
621
652
|
import {
|
|
622
653
|
existsSync as existsSync2,
|
|
@@ -2129,6 +2160,13 @@ async function buildCommand(opts, cloudDeps) {
|
|
|
2129
2160
|
"usage"
|
|
2130
2161
|
);
|
|
2131
2162
|
}
|
|
2163
|
+
if (opts.xcode !== void 0) {
|
|
2164
|
+
fail(
|
|
2165
|
+
opts,
|
|
2166
|
+
"--xcode requires --cloud: a local build uses the Xcode selected on this machine",
|
|
2167
|
+
"usage"
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2132
2170
|
const platform = await resolveBarePlatform(cwd, cfgRef.current, opts);
|
|
2133
2171
|
const build = await resolveBuildConfig(cwd, cfgRef, platform, opts);
|
|
2134
2172
|
const result = await runBuildForPlatform(
|
|
@@ -2395,7 +2433,17 @@ function formatCost(costMicro) {
|
|
|
2395
2433
|
return `$${(costMicro / 1e6).toFixed(2)}`;
|
|
2396
2434
|
}
|
|
2397
2435
|
async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
2436
|
+
if (opts.xcode !== void 0 && !XCODE_VERSION_RE.test(opts.xcode)) {
|
|
2437
|
+
fail(opts, `--xcode must be a version like 27.1, got "${opts.xcode}"`, "usage");
|
|
2438
|
+
}
|
|
2398
2439
|
const platforms = await resolveCloudPlatforms(cwd, cfgRef, opts);
|
|
2440
|
+
if (opts.xcode !== void 0 && !platforms.some(platformUsesXcode)) {
|
|
2441
|
+
fail(
|
|
2442
|
+
opts,
|
|
2443
|
+
`--xcode only applies to ios and tvos builds \u2014 this run builds ${platforms.join(", ")}`,
|
|
2444
|
+
"usage"
|
|
2445
|
+
);
|
|
2446
|
+
}
|
|
2399
2447
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2400
2448
|
const client = new ApiClient(baseUrl, token);
|
|
2401
2449
|
const d = deps ?? defaultCloudBuildDeps(client);
|
|
@@ -2410,6 +2458,7 @@ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
|
2410
2458
|
for (const p of platforms) {
|
|
2411
2459
|
try {
|
|
2412
2460
|
validateBuildEnv(cfg?.platforms?.[p]?.build?.env, p);
|
|
2461
|
+
validateBuildXcode(cfg?.platforms?.[p]?.build?.xcode, p);
|
|
2413
2462
|
if (cfg?.platforms?.[p]?.build?.autoIncrement === true && opts.production && createFromArtifact.has(p)) {
|
|
2414
2463
|
throw new UsageError(
|
|
2415
2464
|
`platforms.${p}.build.autoIncrement needs the build linked to an app \u2014 set appId in vibeview.json (a brand-new app's first build can't auto-increment)`
|
|
@@ -2486,6 +2535,7 @@ ${offenders}`);
|
|
|
2486
2535
|
)
|
|
2487
2536
|
);
|
|
2488
2537
|
}
|
|
2538
|
+
const xcode = platformUsesXcode(p) ? opts.xcode ?? build.xcode : void 0;
|
|
2489
2539
|
return {
|
|
2490
2540
|
platform: p,
|
|
2491
2541
|
// OMITTED, never invented, for a platform whose app the artifact will
|
|
@@ -2498,7 +2548,8 @@ ${offenders}`);
|
|
|
2498
2548
|
artifact_path: artifactPath,
|
|
2499
2549
|
distribution: production ? "production" : "simulator",
|
|
2500
2550
|
...build.env ? { env: build.env } : {},
|
|
2501
|
-
...autoIncrement && production ? { auto_increment: true } : {}
|
|
2551
|
+
...autoIncrement && production ? { auto_increment: true } : {},
|
|
2552
|
+
...xcode ? { xcode } : {}
|
|
2502
2553
|
};
|
|
2503
2554
|
});
|
|
2504
2555
|
const uploadSpinner = !opts.json ? createSpinner("uploading source...") : null;
|
|
@@ -4933,7 +4984,7 @@ var AGENT_COMMANDS = [
|
|
|
4933
4984
|
cli: "positional",
|
|
4934
4985
|
type: "string",
|
|
4935
4986
|
required: true,
|
|
4936
|
-
description: "'get' returns the alert message and button labels; 'accept' taps a button (first by default); 'dismiss' taps a button (
|
|
4987
|
+
description: "'get' returns the alert message and button labels; 'accept' taps a button (first by default); 'dismiss' taps a button (by default a Cancel, Don't Allow or Not Now style button, else the last)."
|
|
4937
4988
|
},
|
|
4938
4989
|
{
|
|
4939
4990
|
name: "button",
|
|
@@ -5603,6 +5654,10 @@ export {
|
|
|
5603
5654
|
clearDevState,
|
|
5604
5655
|
readMetroState,
|
|
5605
5656
|
killOwnedMetro,
|
|
5657
|
+
FEEDBACK_KINDS,
|
|
5658
|
+
isFeedbackKind,
|
|
5659
|
+
FEEDBACK_RULES,
|
|
5660
|
+
feedbackSentLine,
|
|
5606
5661
|
recordDevUse,
|
|
5607
5662
|
checkMetro,
|
|
5608
5663
|
loadLocalDevConfig,
|
package/dist/cli.js
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AGENT_COMMANDS,
|
|
4
4
|
BUILD_TERMINAL_STATUSES,
|
|
5
|
+
FEEDBACK_KINDS,
|
|
6
|
+
FEEDBACK_RULES,
|
|
5
7
|
buildCommand,
|
|
6
8
|
clearDevState,
|
|
7
9
|
createRokuPush,
|
|
8
10
|
dev,
|
|
11
|
+
feedbackSentLine,
|
|
9
12
|
fetchFinalSummary,
|
|
10
13
|
filterDeviceModels,
|
|
11
14
|
formatDeviceModelsHuman,
|
|
12
15
|
formatDevicesHuman,
|
|
13
16
|
formatDuration,
|
|
17
|
+
isFeedbackKind,
|
|
14
18
|
killOwnedMetro,
|
|
15
19
|
listBuilds,
|
|
16
20
|
loadDevConfig,
|
|
@@ -30,7 +34,7 @@ import {
|
|
|
30
34
|
runAgentVerb,
|
|
31
35
|
runStepsNotChecked,
|
|
32
36
|
stepsNotCheckedLabel
|
|
33
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-OMIZWILH.js";
|
|
34
38
|
import {
|
|
35
39
|
ApiClient,
|
|
36
40
|
ApiStatusError,
|
|
@@ -53,7 +57,7 @@ import {
|
|
|
53
57
|
runCommandParseExitCode,
|
|
54
58
|
saveConfig,
|
|
55
59
|
uploadApp
|
|
56
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-3LXR2YCS.js";
|
|
57
61
|
|
|
58
62
|
// src/cli.ts
|
|
59
63
|
import { createRequire } from "module";
|
|
@@ -1502,8 +1506,40 @@ async function stopCommand(sessionId, opts, deps) {
|
|
|
1502
1506
|
process.exitCode = 0;
|
|
1503
1507
|
}
|
|
1504
1508
|
|
|
1505
|
-
// src/commands/
|
|
1509
|
+
// src/commands/feedback.ts
|
|
1506
1510
|
import chalk10 from "chalk";
|
|
1511
|
+
async function feedbackCommand(kind, summary, opts, version, deps) {
|
|
1512
|
+
if (!isFeedbackKind(kind)) {
|
|
1513
|
+
throw new UsageError(`kind must be one of ${FEEDBACK_KINDS.join(", ")}`);
|
|
1514
|
+
}
|
|
1515
|
+
const line = summary?.trim();
|
|
1516
|
+
if (!line) throw new UsageError('a summary is required: vibeview feedback <kind> "<summary>"');
|
|
1517
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
1518
|
+
const d = deps ?? {
|
|
1519
|
+
sendFeedback: (b) => new ApiClient(baseUrl, token).sendFeedback(b)
|
|
1520
|
+
};
|
|
1521
|
+
const body = {
|
|
1522
|
+
kind,
|
|
1523
|
+
summary: line,
|
|
1524
|
+
client: "cli",
|
|
1525
|
+
client_version: version
|
|
1526
|
+
};
|
|
1527
|
+
if (opts.details?.trim()) body.details = opts.details;
|
|
1528
|
+
if (opts.quote?.trim()) body.user_quote = opts.quote;
|
|
1529
|
+
const sessionId = opts.session?.trim() || readDevState(process.cwd())?.session_id;
|
|
1530
|
+
if (sessionId) body.session_id = sessionId;
|
|
1531
|
+
try {
|
|
1532
|
+
const result = await d.sendFeedback(body);
|
|
1533
|
+
console.log(opts.json ? JSON.stringify(result) : feedbackSentLine(result));
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1536
|
+
console.error(chalk10.red(`error: ${message}`));
|
|
1537
|
+
process.exitCode = errorExitCode(err);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// src/commands/create-test.ts
|
|
1542
|
+
import chalk11 from "chalk";
|
|
1507
1543
|
import { existsSync, readFileSync } from "fs";
|
|
1508
1544
|
async function readStdin() {
|
|
1509
1545
|
return new Promise((resolve2, reject) => {
|
|
@@ -1618,23 +1654,23 @@ async function createTest(options) {
|
|
|
1618
1654
|
);
|
|
1619
1655
|
} else {
|
|
1620
1656
|
console.log(`
|
|
1621
|
-
${
|
|
1657
|
+
${chalk11.cyan(result.public_id)} ${result.name}`);
|
|
1622
1658
|
if (result.app_name) {
|
|
1623
|
-
console.log(` ${
|
|
1659
|
+
console.log(` ${chalk11.dim(`App: ${result.app_name}`)}`);
|
|
1624
1660
|
}
|
|
1625
1661
|
if (result.platform) {
|
|
1626
|
-
console.log(` ${
|
|
1662
|
+
console.log(` ${chalk11.dim(`Platform: ${result.platform}`)}`);
|
|
1627
1663
|
}
|
|
1628
1664
|
if (result.groups?.length) {
|
|
1629
|
-
console.log(` ${
|
|
1665
|
+
console.log(` ${chalk11.dim(`Groups: ${result.groups.map((g) => g.name).join(", ")}`)}`);
|
|
1630
1666
|
}
|
|
1631
|
-
console.log(` ${
|
|
1632
|
-
console.log(
|
|
1667
|
+
console.log(` ${chalk11.dim(`Steps: ${steps.length}`)}`);
|
|
1668
|
+
console.log(chalk11.dim(` ${testCaseUrl(baseUrl, result.public_id)}
|
|
1633
1669
|
`));
|
|
1634
1670
|
}
|
|
1635
1671
|
} catch (err) {
|
|
1636
1672
|
if (options.output === "json") printJsonError(err);
|
|
1637
|
-
console.error(
|
|
1673
|
+
console.error(chalk11.red(err instanceof Error ? err.message : "Failed to create test"));
|
|
1638
1674
|
process.exit(
|
|
1639
1675
|
err instanceof ApiStatusError && err.status === 409 ? EXIT_TEST_FAIL : EXIT_INFRA_ERROR
|
|
1640
1676
|
);
|
|
@@ -1642,7 +1678,7 @@ async function createTest(options) {
|
|
|
1642
1678
|
}
|
|
1643
1679
|
|
|
1644
1680
|
// src/commands/import-maestro.ts
|
|
1645
|
-
import
|
|
1681
|
+
import chalk12 from "chalk";
|
|
1646
1682
|
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
1647
1683
|
import { basename, extname, join, relative, resolve, sep } from "path";
|
|
1648
1684
|
import { zipSync } from "fflate";
|
|
@@ -1716,8 +1752,8 @@ var START_STATE_LABEL = {
|
|
|
1716
1752
|
};
|
|
1717
1753
|
function flagLine(f) {
|
|
1718
1754
|
const where = [f.file, f.where, f.command].filter(Boolean).join(" \xB7 ");
|
|
1719
|
-
const label = f.severity === "rewrite" ?
|
|
1720
|
-
return ` ${label} ${
|
|
1755
|
+
const label = f.severity === "rewrite" ? chalk12.red(FLAG_LABEL.rewrite) : FLAG_LABEL[f.severity];
|
|
1756
|
+
return ` ${label} ${chalk12.dim(where)} ${f.message}`;
|
|
1721
1757
|
}
|
|
1722
1758
|
function formatImport(result, baseUrl) {
|
|
1723
1759
|
const lines = [];
|
|
@@ -1734,16 +1770,16 @@ function formatImport(result, baseUrl) {
|
|
|
1734
1770
|
const meta = [`${t.steps.length} ${t.steps.length === 1 ? "step" : "steps"}`];
|
|
1735
1771
|
if (t.setup_keys.length) meta.push(`before this test: ${t.setup_keys.join(", ")}`);
|
|
1736
1772
|
if (t.start_state) meta.push(`start: ${START_STATE_LABEL[t.start_state] ?? t.start_state}`);
|
|
1737
|
-
const head = t.importable ? t.name :
|
|
1738
|
-
lines.push("", ` ${head} ${
|
|
1739
|
-
if (t.created_id) lines.push(` ${
|
|
1773
|
+
const head = t.importable ? t.name : chalk12.red(`${t.name} (not imported)`);
|
|
1774
|
+
lines.push("", ` ${head} ${chalk12.dim(`${t.key} \xB7 ${meta.join(" \xB7 ")}`)}`);
|
|
1775
|
+
if (t.created_id) lines.push(` ${chalk12.dim(testCaseUrl(baseUrl, t.created_id))}`);
|
|
1740
1776
|
for (const f of t.flags) lines.push(flagLine(f));
|
|
1741
1777
|
}
|
|
1742
1778
|
if (result.test_data.length) {
|
|
1743
1779
|
lines.push("", "These tests use test data:");
|
|
1744
1780
|
for (const d of result.test_data) {
|
|
1745
1781
|
const note = d.status === "needs_value" || d.status === "skipped" || d.suggested_value === null ? "add the value under App notes & data" : d.status === "kept" ? "already set, kept" : d.status === "created" ? "added from the flow" : d.secret ? "stored as a secret" : `flow value: ${d.suggested_value}`;
|
|
1746
|
-
lines.push(` ${d.key} ${
|
|
1782
|
+
lines.push(` ${d.key} ${chalk12.dim(note)}`);
|
|
1747
1783
|
}
|
|
1748
1784
|
}
|
|
1749
1785
|
return lines;
|
|
@@ -1782,7 +1818,7 @@ async function importMaestro(path, options) {
|
|
|
1782
1818
|
for (const line of formatImport(result, baseUrl)) console.log(line);
|
|
1783
1819
|
} catch (err) {
|
|
1784
1820
|
if (options.output === "json") printJsonError(err);
|
|
1785
|
-
console.error(
|
|
1821
|
+
console.error(chalk12.red(err instanceof Error ? err.message : "Import failed"));
|
|
1786
1822
|
const rejected = err instanceof UsageError || err instanceof NotFoundError || err instanceof ApiStatusError && err.status >= 400 && err.status < 500 ? EXIT_TEST_FAIL : EXIT_INFRA_ERROR;
|
|
1787
1823
|
process.exit(errorExitCode(err, rejected));
|
|
1788
1824
|
}
|
|
@@ -1865,7 +1901,7 @@ async function logout() {
|
|
|
1865
1901
|
}
|
|
1866
1902
|
|
|
1867
1903
|
// src/commands/dev-status.ts
|
|
1868
|
-
import
|
|
1904
|
+
import chalk13 from "chalk";
|
|
1869
1905
|
function buildLine(info) {
|
|
1870
1906
|
if (!info || typeof info.app_build_id !== "number") return null;
|
|
1871
1907
|
const version = info.app_version ? ` version ${info.app_version}${info.app_build_number ? ` (${info.app_build_number})` : ""}` : "";
|
|
@@ -1926,7 +1962,7 @@ async function devStatus(opts) {
|
|
|
1926
1962
|
console.error(` url: ${state.page_url}`);
|
|
1927
1963
|
console.error(` started: ${state.started_at}`);
|
|
1928
1964
|
}
|
|
1929
|
-
if (metro) console.error(
|
|
1965
|
+
if (metro) console.error(chalk13.dim(`Metro: pid ${metro.pid} on :${metro.port} (CLI-owned)`));
|
|
1930
1966
|
process.exit(0);
|
|
1931
1967
|
}
|
|
1932
1968
|
var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "queued", "starting", "active"]);
|
|
@@ -1951,7 +1987,7 @@ async function reportOrphan(api, state, opts) {
|
|
|
1951
1987
|
);
|
|
1952
1988
|
} else if (live) {
|
|
1953
1989
|
console.error(
|
|
1954
|
-
|
|
1990
|
+
chalk13.yellow(
|
|
1955
1991
|
`\u26A0 The dev process (pid ${state.pid}) has exited, but session ${state.session_id} is still ${status}.`
|
|
1956
1992
|
)
|
|
1957
1993
|
);
|
|
@@ -1999,12 +2035,12 @@ async function devStop(opts) {
|
|
|
1999
2035
|
}
|
|
2000
2036
|
|
|
2001
2037
|
// src/commands/dev-reload.ts
|
|
2002
|
-
import
|
|
2038
|
+
import chalk14 from "chalk";
|
|
2003
2039
|
function fail(opts, code, message, exit) {
|
|
2004
2040
|
if (opts.json) {
|
|
2005
2041
|
console.log(JSON.stringify({ reloaded: false, code, message }));
|
|
2006
2042
|
} else {
|
|
2007
|
-
console.error(
|
|
2043
|
+
console.error(chalk14.red(`error: ${message}`));
|
|
2008
2044
|
}
|
|
2009
2045
|
process.exit(exit);
|
|
2010
2046
|
}
|
|
@@ -2057,7 +2093,7 @@ async function devReload(opts) {
|
|
|
2057
2093
|
api,
|
|
2058
2094
|
// Build output and the "reloaded in" line are human output: stderr, so
|
|
2059
2095
|
// stdout stays reserved for the --json result.
|
|
2060
|
-
log: (line) => console.error(
|
|
2096
|
+
log: (line) => console.error(chalk14.dim(line))
|
|
2061
2097
|
});
|
|
2062
2098
|
const started = Date.now();
|
|
2063
2099
|
try {
|
|
@@ -2079,10 +2115,10 @@ async function devReload(opts) {
|
|
|
2079
2115
|
}
|
|
2080
2116
|
|
|
2081
2117
|
// src/commands/builds.ts
|
|
2082
|
-
import
|
|
2118
|
+
import chalk16 from "chalk";
|
|
2083
2119
|
|
|
2084
2120
|
// src/lib/formatters/builds.ts
|
|
2085
|
-
import
|
|
2121
|
+
import chalk15 from "chalk";
|
|
2086
2122
|
var HEADERS = ["ID", "PLATFORM", "STATUS", "CREATED", "COST", "APP"];
|
|
2087
2123
|
var UNBILLED_STATUSES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
2088
2124
|
function formatCostCell(costMicro, status) {
|
|
@@ -2090,13 +2126,13 @@ function formatCostCell(costMicro, status) {
|
|
|
2090
2126
|
return `$${(Math.max(costMicro, 0) / 1e6).toFixed(2)}`;
|
|
2091
2127
|
}
|
|
2092
2128
|
var STATUS_COLOR = {
|
|
2093
|
-
succeeded:
|
|
2094
|
-
failed:
|
|
2095
|
-
timed_out:
|
|
2096
|
-
infra_failed:
|
|
2097
|
-
canceled:
|
|
2098
|
-
running:
|
|
2099
|
-
queued:
|
|
2129
|
+
succeeded: chalk15.green,
|
|
2130
|
+
failed: chalk15.red,
|
|
2131
|
+
timed_out: chalk15.red,
|
|
2132
|
+
infra_failed: chalk15.red,
|
|
2133
|
+
canceled: chalk15.dim,
|
|
2134
|
+
running: chalk15.yellow,
|
|
2135
|
+
queued: chalk15.dim
|
|
2100
2136
|
};
|
|
2101
2137
|
function formatBuildsHuman(builds2) {
|
|
2102
2138
|
if (builds2.length === 0) {
|
|
@@ -2113,7 +2149,7 @@ function formatBuildsHuman(builds2) {
|
|
|
2113
2149
|
const widths = HEADERS.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2114
2150
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2115
2151
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
2116
|
-
const lines = [
|
|
2152
|
+
const lines = [chalk15.bold(join5(padCells(HEADERS)))];
|
|
2117
2153
|
for (const row of rows) {
|
|
2118
2154
|
const padded = padCells(row);
|
|
2119
2155
|
const colorFn = STATUS_COLOR[row[2]] ?? ((s) => s);
|
|
@@ -2172,7 +2208,7 @@ async function buildsLogs(id, opts, deps) {
|
|
|
2172
2208
|
console.log(JSON.stringify(logs));
|
|
2173
2209
|
} else {
|
|
2174
2210
|
if (logs.content) process.stdout.write(logs.content);
|
|
2175
|
-
console.error(
|
|
2211
|
+
console.error(chalk16.dim(`status: ${logs.status}`));
|
|
2176
2212
|
}
|
|
2177
2213
|
process.exitCode = 0;
|
|
2178
2214
|
return;
|
|
@@ -2269,7 +2305,7 @@ async function buildsDownload(id, opts, deps) {
|
|
|
2269
2305
|
if (opts.json) {
|
|
2270
2306
|
console.log(JSON.stringify(result));
|
|
2271
2307
|
} else {
|
|
2272
|
-
console.log(
|
|
2308
|
+
console.log(chalk16.green(`\u2713 saved ${result.path}`));
|
|
2273
2309
|
}
|
|
2274
2310
|
process.exitCode = 0;
|
|
2275
2311
|
} catch (err) {
|
|
@@ -2286,7 +2322,7 @@ async function buildsCancel(id, opts, deps) {
|
|
|
2286
2322
|
if (opts.json) {
|
|
2287
2323
|
console.log(JSON.stringify(build));
|
|
2288
2324
|
} else {
|
|
2289
|
-
console.log(`${
|
|
2325
|
+
console.log(`${chalk16.cyan(build.id)} ${build.status}`);
|
|
2290
2326
|
}
|
|
2291
2327
|
process.exitCode = 0;
|
|
2292
2328
|
} catch (err) {
|
|
@@ -2303,7 +2339,7 @@ async function buildsDeleteArtifact(id, opts, deps) {
|
|
|
2303
2339
|
if (opts.json) {
|
|
2304
2340
|
console.log(JSON.stringify(result));
|
|
2305
2341
|
} else {
|
|
2306
|
-
console.log(
|
|
2342
|
+
console.log(chalk16.green(`\u2713 deleted artifact for ${id}`));
|
|
2307
2343
|
}
|
|
2308
2344
|
process.exitCode = 0;
|
|
2309
2345
|
} catch (err) {
|
|
@@ -2314,11 +2350,11 @@ async function buildsDeleteArtifact(id, opts, deps) {
|
|
|
2314
2350
|
function printError(opts, err) {
|
|
2315
2351
|
const message = err instanceof Error ? err.message : String(err);
|
|
2316
2352
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2317
|
-
console.error(
|
|
2353
|
+
console.error(chalk16.red(`error: ${message}`));
|
|
2318
2354
|
}
|
|
2319
2355
|
|
|
2320
2356
|
// src/commands/submit.ts
|
|
2321
|
-
import
|
|
2357
|
+
import chalk17 from "chalk";
|
|
2322
2358
|
function defaultDeps2(client) {
|
|
2323
2359
|
return {
|
|
2324
2360
|
createSubmission: client.createSubmission.bind(client),
|
|
@@ -2342,7 +2378,7 @@ function submitConfigFor(cwd, platform) {
|
|
|
2342
2378
|
cfg = loadDevConfig(cwd);
|
|
2343
2379
|
} catch (err) {
|
|
2344
2380
|
console.error(
|
|
2345
|
-
|
|
2381
|
+
chalk17.yellow(
|
|
2346
2382
|
`warning: vibeview.json could not be read \u2014 ignoring submit defaults (${err instanceof Error ? err.message : String(err)})`
|
|
2347
2383
|
)
|
|
2348
2384
|
);
|
|
@@ -2490,12 +2526,12 @@ async function submitCommand(opts, deps) {
|
|
|
2490
2526
|
return;
|
|
2491
2527
|
}
|
|
2492
2528
|
if (!opts.json) {
|
|
2493
|
-
console.error(` ${
|
|
2529
|
+
console.error(` ${chalk17.cyan(sub.id)} ${chalk17.dim(`[${sub.platform}]`)}`);
|
|
2494
2530
|
}
|
|
2495
2531
|
const final = await followSubmission(sub, opts, d);
|
|
2496
2532
|
if (!opts.json && final.status === "succeeded" && (final.release_status ?? releaseStatus) === "draft") {
|
|
2497
2533
|
console.error(
|
|
2498
|
-
|
|
2534
|
+
chalk17.dim("release status: draft \u2014 activate it from the Play Console when ready")
|
|
2499
2535
|
);
|
|
2500
2536
|
}
|
|
2501
2537
|
process.exitCode = final.status === "succeeded" ? 0 : 1;
|
|
@@ -2526,11 +2562,11 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2526
2562
|
});
|
|
2527
2563
|
emit(opts.json, { event: "preflight", ...result });
|
|
2528
2564
|
if (!opts.json) {
|
|
2529
|
-
const mark = result.ready ?
|
|
2565
|
+
const mark = result.ready ? chalk17.green("\u2713") : chalk17.red("\u2717");
|
|
2530
2566
|
console.error(`${mark} ${result.message}`);
|
|
2531
2567
|
if (result.ready) {
|
|
2532
2568
|
console.error(
|
|
2533
|
-
|
|
2569
|
+
chalk17.dim(
|
|
2534
2570
|
` next: vibeview build --cloud --production --platform ${platform}, then vibeview submit --platform ${platform} --latest`
|
|
2535
2571
|
)
|
|
2536
2572
|
);
|
|
@@ -2564,7 +2600,7 @@ async function linkedIdentifier(platform, opts, deps) {
|
|
|
2564
2600
|
}
|
|
2565
2601
|
if (!app.package_name) return void 0;
|
|
2566
2602
|
if (!opts.json) {
|
|
2567
|
-
console.error(
|
|
2603
|
+
console.error(chalk17.dim(`Checking ${app.package_name} (${app.name}, linked in vibeview.json)`));
|
|
2568
2604
|
}
|
|
2569
2605
|
return app.package_name;
|
|
2570
2606
|
}
|
|
@@ -2605,14 +2641,14 @@ async function followSubmission(created, opts, deps) {
|
|
|
2605
2641
|
if (opts.json) {
|
|
2606
2642
|
emit(opts.json, { event: "status", id: created.id, status: "queued" });
|
|
2607
2643
|
} else {
|
|
2608
|
-
console.error(
|
|
2644
|
+
console.error(chalk17.dim("\u23F3 queued \u2014 waiting for a submission slot"));
|
|
2609
2645
|
}
|
|
2610
2646
|
} else if (log.status === "running" && !printedRunning) {
|
|
2611
2647
|
printedRunning = true;
|
|
2612
2648
|
if (opts.json) {
|
|
2613
2649
|
emit(opts.json, { event: "status", id: created.id, status: "running" });
|
|
2614
2650
|
} else {
|
|
2615
|
-
console.error(`${
|
|
2651
|
+
console.error(`${chalk17.cyan("\u25B6")} submitting`);
|
|
2616
2652
|
}
|
|
2617
2653
|
}
|
|
2618
2654
|
if (log.content) {
|
|
@@ -2654,7 +2690,7 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
|
|
|
2654
2690
|
});
|
|
2655
2691
|
if (!opts.json) {
|
|
2656
2692
|
const ok = final.status === "succeeded";
|
|
2657
|
-
const mark = ok ?
|
|
2693
|
+
const mark = ok ? chalk17.green("\u2713") : chalk17.red("\u2717");
|
|
2658
2694
|
const duration = formatDuration(final.started_at, final.finished_at);
|
|
2659
2695
|
let line = `${mark} ${final.status}`;
|
|
2660
2696
|
if (duration) line += ` (${duration})`;
|
|
@@ -2666,11 +2702,11 @@ The submission continues server-side \u2014 see its outcome on the Builds page i
|
|
|
2666
2702
|
function printError2(opts, err) {
|
|
2667
2703
|
const message = err instanceof Error ? err.message : String(err);
|
|
2668
2704
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2669
|
-
console.error(
|
|
2705
|
+
console.error(chalk17.red(`error: ${message}`));
|
|
2670
2706
|
}
|
|
2671
2707
|
|
|
2672
2708
|
// src/commands/versions.ts
|
|
2673
|
-
import
|
|
2709
|
+
import chalk18 from "chalk";
|
|
2674
2710
|
function defaultDeps3(client) {
|
|
2675
2711
|
return {
|
|
2676
2712
|
listAppVersions: client.listAppVersions.bind(client),
|
|
@@ -2720,7 +2756,7 @@ async function versionsSet(buildNumber, opts, deps) {
|
|
|
2720
2756
|
console.log(JSON.stringify(version));
|
|
2721
2757
|
} else {
|
|
2722
2758
|
console.log(
|
|
2723
|
-
|
|
2759
|
+
chalk18.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk18.dim(
|
|
2724
2760
|
" \u2014 the next autoIncrement production build gets " + (version.build_number + 1)
|
|
2725
2761
|
)
|
|
2726
2762
|
);
|
|
@@ -2740,18 +2776,18 @@ function formatVersionsHuman(versions2) {
|
|
|
2740
2776
|
const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2741
2777
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2742
2778
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
2743
|
-
const lines = [
|
|
2779
|
+
const lines = [chalk18.bold(join5(padCells(HEADERS2)))];
|
|
2744
2780
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
2745
2781
|
return lines.join("\n") + "\n";
|
|
2746
2782
|
}
|
|
2747
2783
|
function printError3(opts, err) {
|
|
2748
2784
|
const message = err instanceof Error ? err.message : String(err);
|
|
2749
2785
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2750
|
-
console.error(
|
|
2786
|
+
console.error(chalk18.red(`error: ${message}`));
|
|
2751
2787
|
}
|
|
2752
2788
|
|
|
2753
2789
|
// src/commands/whoami.ts
|
|
2754
|
-
import
|
|
2790
|
+
import chalk19 from "chalk";
|
|
2755
2791
|
function defaultDeps4(opts) {
|
|
2756
2792
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2757
2793
|
const client = new ApiClient(baseUrl, token);
|
|
@@ -2782,33 +2818,33 @@ async function whoAmI(opts, deps) {
|
|
|
2782
2818
|
process.exitCode = 0;
|
|
2783
2819
|
return;
|
|
2784
2820
|
}
|
|
2785
|
-
console.log(`${
|
|
2821
|
+
console.log(`${chalk19.bold("Logged in as")} ${me.email}`);
|
|
2786
2822
|
if (me.memberships.length === 1) {
|
|
2787
|
-
console.log(`${
|
|
2823
|
+
console.log(`${chalk19.bold("Organization")} ${me.memberships[0].organization_name}`);
|
|
2788
2824
|
} else if (me.memberships.length > 1) {
|
|
2789
|
-
console.log(
|
|
2825
|
+
console.log(chalk19.bold("Organizations"));
|
|
2790
2826
|
for (const m of me.memberships) {
|
|
2791
2827
|
console.log(` ${m.organization_name} (${m.role})`);
|
|
2792
2828
|
}
|
|
2793
2829
|
}
|
|
2794
|
-
console.log(`${
|
|
2795
|
-
console.log(`${
|
|
2796
|
-
console.log(`${
|
|
2830
|
+
console.log(`${chalk19.bold("Member role")} ${me.role ?? "\u2014"}`);
|
|
2831
|
+
console.log(`${chalk19.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
|
|
2832
|
+
console.log(`${chalk19.bold("Effective role")} ${effective ?? "\u2014"}`);
|
|
2797
2833
|
if (me.token_access) {
|
|
2798
2834
|
console.log(
|
|
2799
|
-
`${
|
|
2835
|
+
`${chalk19.bold("Access")} ${me.token_access === "ci" ? "CI only (upload builds, run tests, read results)" : "Full access"}`
|
|
2800
2836
|
);
|
|
2801
|
-
console.log(`${
|
|
2837
|
+
console.log(`${chalk19.bold("Expires")} ${describeExpiry(me.token_expires_at)}`);
|
|
2802
2838
|
}
|
|
2803
2839
|
if (me.is_superadmin) {
|
|
2804
|
-
console.log(
|
|
2840
|
+
console.log(chalk19.dim("Superadmin (platform-wide; does not raise the org role above)"));
|
|
2805
2841
|
}
|
|
2806
2842
|
if (me.role === "admin" && me.token_role && me.token_role !== "admin" && me.token_access !== "ci") {
|
|
2807
2843
|
console.log(
|
|
2808
|
-
|
|
2844
|
+
chalk19.yellow(
|
|
2809
2845
|
`
|
|
2810
|
-
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 ${
|
|
2811
|
-
Create an admin API token in the dashboard and use ${
|
|
2846
|
+
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 ${chalk19.bold("vibeview env set")}) will fail with a permissions error.
|
|
2847
|
+
Create an admin API token in the dashboard and use ${chalk19.bold("--token")} or ${chalk19.bold("VIBEVIEW_API_TOKEN")}.`
|
|
2812
2848
|
)
|
|
2813
2849
|
);
|
|
2814
2850
|
}
|
|
@@ -2818,7 +2854,7 @@ Create an admin API token in the dashboard and use ${chalk18.bold("--token")} or
|
|
|
2818
2854
|
if (opts.json) {
|
|
2819
2855
|
console.log(JSON.stringify({ event: "error", message }));
|
|
2820
2856
|
}
|
|
2821
|
-
console.error(
|
|
2857
|
+
console.error(chalk19.red(`error: ${message}`));
|
|
2822
2858
|
process.exitCode = errorExitCode(err);
|
|
2823
2859
|
}
|
|
2824
2860
|
}
|
|
@@ -2826,7 +2862,7 @@ Create an admin API token in the dashboard and use ${chalk18.bold("--token")} or
|
|
|
2826
2862
|
// src/commands/env.ts
|
|
2827
2863
|
import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
|
|
2828
2864
|
import { basename as basename2 } from "path";
|
|
2829
|
-
import
|
|
2865
|
+
import chalk20 from "chalk";
|
|
2830
2866
|
import { password } from "@inquirer/prompts";
|
|
2831
2867
|
|
|
2832
2868
|
// src/lib/gitignore.ts
|
|
@@ -2882,7 +2918,7 @@ function defaultDeps5(client) {
|
|
|
2882
2918
|
isIgnored: (path) => isGitIgnored(path),
|
|
2883
2919
|
writeFile: writeSecureFile,
|
|
2884
2920
|
readFile: (path) => readFileAsync(path),
|
|
2885
|
-
warn: (message) => console.error(
|
|
2921
|
+
warn: (message) => console.error(chalk20.yellow(`warning: ${message}`))
|
|
2886
2922
|
};
|
|
2887
2923
|
}
|
|
2888
2924
|
async function envList(opts, deps) {
|
|
@@ -2914,7 +2950,7 @@ function formatEnvVarsHuman(variables) {
|
|
|
2914
2950
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2915
2951
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2916
2952
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
2917
|
-
const lines = [
|
|
2953
|
+
const lines = [chalk20.bold(join5(padCells(HEADERS3)))];
|
|
2918
2954
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
2919
2955
|
return lines.join("\n") + "\n";
|
|
2920
2956
|
}
|
|
@@ -2964,7 +3000,7 @@ function printSetResult(opts, result) {
|
|
|
2964
3000
|
if (opts.json) {
|
|
2965
3001
|
console.log(JSON.stringify(result));
|
|
2966
3002
|
} else {
|
|
2967
|
-
console.log(
|
|
3003
|
+
console.log(chalk20.green(`\u2713 set ${result.name} (${result.visibility})`));
|
|
2968
3004
|
}
|
|
2969
3005
|
}
|
|
2970
3006
|
async function envRm(name, opts, deps) {
|
|
@@ -2974,7 +3010,7 @@ async function envRm(name, opts, deps) {
|
|
|
2974
3010
|
if (opts.json) {
|
|
2975
3011
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2976
3012
|
} else {
|
|
2977
|
-
console.log(
|
|
3013
|
+
console.log(chalk20.green(`\u2713 removed ${name}`));
|
|
2978
3014
|
}
|
|
2979
3015
|
process.exitCode = 0;
|
|
2980
3016
|
} catch (err) {
|
|
@@ -3006,7 +3042,7 @@ async function envPull(opts, deps) {
|
|
|
3006
3042
|
if (opts.json) {
|
|
3007
3043
|
console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
|
|
3008
3044
|
} else {
|
|
3009
|
-
console.log(
|
|
3045
|
+
console.log(chalk20.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
|
|
3010
3046
|
}
|
|
3011
3047
|
process.exitCode = 0;
|
|
3012
3048
|
} catch (err) {
|
|
@@ -3045,7 +3081,7 @@ function escapeEnvValue(value) {
|
|
|
3045
3081
|
function printError4(opts, err) {
|
|
3046
3082
|
const message = err instanceof Error ? err.message : String(err);
|
|
3047
3083
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
3048
|
-
console.error(
|
|
3084
|
+
console.error(chalk20.red(`error: ${message}`));
|
|
3049
3085
|
}
|
|
3050
3086
|
|
|
3051
3087
|
// src/commands/credentials.ts
|
|
@@ -3057,7 +3093,7 @@ import {
|
|
|
3057
3093
|
} from "fs/promises";
|
|
3058
3094
|
import { mkdirSync } from "fs";
|
|
3059
3095
|
import { basename as basename3, extname as extname2, join as join2 } from "path";
|
|
3060
|
-
import
|
|
3096
|
+
import chalk21 from "chalk";
|
|
3061
3097
|
import { input, password as password2 } from "@inquirer/prompts";
|
|
3062
3098
|
function clientFor2(opts) {
|
|
3063
3099
|
const { token, baseUrl } = resolveAuth(opts);
|
|
@@ -3130,7 +3166,7 @@ function defaultDeps6(client) {
|
|
|
3130
3166
|
mkdir: (dir) => mkdirSync(dir, { recursive: true }),
|
|
3131
3167
|
writeFile: (path, data, mode) => writeFileAsync(path, data, mode !== void 0 ? { mode } : void 0),
|
|
3132
3168
|
chmod: (path, mode) => chmodAsync(path, mode),
|
|
3133
|
-
warn: (message) => console.error(
|
|
3169
|
+
warn: (message) => console.error(chalk21.yellow(`warning: ${message}`))
|
|
3134
3170
|
};
|
|
3135
3171
|
}
|
|
3136
3172
|
var ANDROID_KIND = "android_keystore";
|
|
@@ -3199,7 +3235,7 @@ async function credentialsAdd(opts, deps) {
|
|
|
3199
3235
|
if (opts.json) {
|
|
3200
3236
|
console.log(JSON.stringify(result));
|
|
3201
3237
|
} else {
|
|
3202
|
-
console.log(
|
|
3238
|
+
console.log(chalk21.green(`\u2713 added ${result.name} (${result.platform})`));
|
|
3203
3239
|
}
|
|
3204
3240
|
process.exitCode = 0;
|
|
3205
3241
|
} catch (err) {
|
|
@@ -3257,20 +3293,20 @@ async function credentialsGenerate(opts, deps) {
|
|
|
3257
3293
|
}
|
|
3258
3294
|
if (opts.json) {
|
|
3259
3295
|
} else {
|
|
3260
|
-
console.log(
|
|
3296
|
+
console.log(chalk21.green(`\u2713 generated ${result.name} (android)`));
|
|
3261
3297
|
console.log("");
|
|
3262
|
-
console.log(` Keystore saved to: ${
|
|
3263
|
-
console.log(` Key alias: ${
|
|
3264
|
-
console.log(` Store password: ${
|
|
3265
|
-
console.log(` Key password: ${
|
|
3298
|
+
console.log(` Keystore saved to: ${chalk21.bold(savedTo)}`);
|
|
3299
|
+
console.log(` Key alias: ${chalk21.bold(result.key_alias)}`);
|
|
3300
|
+
console.log(` Store password: ${chalk21.bold(result.store_password)}`);
|
|
3301
|
+
console.log(` Key password: ${chalk21.bold(result.key_password)}`);
|
|
3266
3302
|
console.log("");
|
|
3267
3303
|
console.log(
|
|
3268
|
-
|
|
3304
|
+
chalk21.yellow(
|
|
3269
3305
|
" These are shown ONCE \u2014 VibeView cannot recover the keystore file or its passwords. Back them up somewhere safe."
|
|
3270
3306
|
)
|
|
3271
3307
|
);
|
|
3272
3308
|
console.log(
|
|
3273
|
-
|
|
3309
|
+
chalk21.yellow(
|
|
3274
3310
|
" The credential is already uploaded and active for android and androidtv production builds."
|
|
3275
3311
|
)
|
|
3276
3312
|
);
|
|
@@ -3296,19 +3332,19 @@ async function generateAppleCredential(platform, opts, d) {
|
|
|
3296
3332
|
if (opts.json) {
|
|
3297
3333
|
console.log(JSON.stringify(result));
|
|
3298
3334
|
} else {
|
|
3299
|
-
console.log(
|
|
3335
|
+
console.log(chalk21.green(`\u2713 generated ${result.name} (${result.platform})`));
|
|
3300
3336
|
console.log("");
|
|
3301
|
-
console.log(` Scope: ${
|
|
3337
|
+
console.log(` Scope: ${chalk21.bold(result.app_public_id ?? "org-wide")}`);
|
|
3302
3338
|
const expires = formatExpires(result);
|
|
3303
3339
|
if (expires) {
|
|
3304
|
-
console.log(` Expires: ${
|
|
3340
|
+
console.log(` Expires: ${chalk21.bold(expires)}`);
|
|
3305
3341
|
}
|
|
3306
3342
|
console.log("");
|
|
3307
3343
|
console.log(
|
|
3308
3344
|
` The certificate and provisioning profile are stored with VibeView and already active for ${result.platform} production builds.`
|
|
3309
3345
|
);
|
|
3310
3346
|
console.log(
|
|
3311
|
-
` Run ${
|
|
3347
|
+
` Run ${chalk21.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
|
|
3312
3348
|
);
|
|
3313
3349
|
}
|
|
3314
3350
|
process.exitCode = 0;
|
|
@@ -3374,17 +3410,17 @@ async function credentialsDownload(name, opts, deps) {
|
|
|
3374
3410
|
await d.chmod(path, 384);
|
|
3375
3411
|
}
|
|
3376
3412
|
if (!opts.json) {
|
|
3377
|
-
console.log(
|
|
3413
|
+
console.log(chalk21.green(`\u2713 downloaded ${material.name} (${material.platform})`));
|
|
3378
3414
|
console.log("");
|
|
3379
|
-
console.log(` Saved to: ${
|
|
3415
|
+
console.log(` Saved to: ${chalk21.bold(savedTo)}`);
|
|
3380
3416
|
if (profileSavedTo !== null) {
|
|
3381
|
-
console.log(` Profile: ${
|
|
3417
|
+
console.log(` Profile: ${chalk21.bold(profileSavedTo)}`);
|
|
3382
3418
|
}
|
|
3383
3419
|
for (const [key, value] of Object.entries(material.passwords)) {
|
|
3384
|
-
console.log(` ${key}: ${
|
|
3420
|
+
console.log(` ${key}: ${chalk21.bold(value)}`);
|
|
3385
3421
|
}
|
|
3386
3422
|
console.log("");
|
|
3387
|
-
console.log(
|
|
3423
|
+
console.log(chalk21.yellow(" This download is recorded in your organization's audit log."));
|
|
3388
3424
|
}
|
|
3389
3425
|
process.exitCode = 0;
|
|
3390
3426
|
} catch (err) {
|
|
@@ -3415,13 +3451,13 @@ async function ascKeyAdd(opts, deps) {
|
|
|
3415
3451
|
if (opts.json) {
|
|
3416
3452
|
console.log(JSON.stringify(result));
|
|
3417
3453
|
} else {
|
|
3418
|
-
console.log(
|
|
3454
|
+
console.log(chalk21.green(`\u2713 added App Store Connect key ${result.name}`));
|
|
3419
3455
|
console.log("");
|
|
3420
|
-
console.log(` Key ID: ${
|
|
3421
|
-
console.log(` Issuer ID: ${
|
|
3456
|
+
console.log(` Key ID: ${chalk21.bold(result.key_id)}`);
|
|
3457
|
+
console.log(` Issuer ID: ${chalk21.bold(result.issuer_id)}`);
|
|
3422
3458
|
console.log("");
|
|
3423
3459
|
console.log(
|
|
3424
|
-
|
|
3460
|
+
chalk21.yellow(
|
|
3425
3461
|
" The .p8 is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
3426
3462
|
)
|
|
3427
3463
|
);
|
|
@@ -3456,7 +3492,7 @@ function formatAscKeysHuman(keys) {
|
|
|
3456
3492
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
3457
3493
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
3458
3494
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
3459
|
-
const lines = [
|
|
3495
|
+
const lines = [chalk21.bold(join5(padCells(HEADERS3)))];
|
|
3460
3496
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
3461
3497
|
return lines.join("\n") + "\n";
|
|
3462
3498
|
}
|
|
@@ -3467,7 +3503,7 @@ async function ascKeyRm(name, opts, deps) {
|
|
|
3467
3503
|
if (opts.json) {
|
|
3468
3504
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
3469
3505
|
} else {
|
|
3470
|
-
console.log(
|
|
3506
|
+
console.log(chalk21.green(`\u2713 removed App Store Connect key ${name}`));
|
|
3471
3507
|
}
|
|
3472
3508
|
process.exitCode = 0;
|
|
3473
3509
|
} catch (err) {
|
|
@@ -3492,12 +3528,12 @@ async function playKeyAdd(opts, deps) {
|
|
|
3492
3528
|
if (opts.json) {
|
|
3493
3529
|
console.log(JSON.stringify(result));
|
|
3494
3530
|
} else {
|
|
3495
|
-
console.log(
|
|
3531
|
+
console.log(chalk21.green(`\u2713 added Google Play key ${result.name}`));
|
|
3496
3532
|
console.log("");
|
|
3497
|
-
console.log(` Service account: ${
|
|
3533
|
+
console.log(` Service account: ${chalk21.bold(result.client_email)}`);
|
|
3498
3534
|
console.log("");
|
|
3499
3535
|
console.log(
|
|
3500
|
-
|
|
3536
|
+
chalk21.yellow(
|
|
3501
3537
|
" The JSON key is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
3502
3538
|
)
|
|
3503
3539
|
);
|
|
@@ -3532,7 +3568,7 @@ function formatPlayKeysHuman(keys) {
|
|
|
3532
3568
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
3533
3569
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
3534
3570
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
3535
|
-
const lines = [
|
|
3571
|
+
const lines = [chalk21.bold(join5(padCells(HEADERS3)))];
|
|
3536
3572
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
3537
3573
|
return lines.join("\n") + "\n";
|
|
3538
3574
|
}
|
|
@@ -3543,7 +3579,7 @@ async function playKeyRm(name, opts, deps) {
|
|
|
3543
3579
|
if (opts.json) {
|
|
3544
3580
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
3545
3581
|
} else {
|
|
3546
|
-
console.log(
|
|
3582
|
+
console.log(chalk21.green(`\u2713 removed Google Play key ${name}`));
|
|
3547
3583
|
}
|
|
3548
3584
|
process.exitCode = 0;
|
|
3549
3585
|
} catch (err) {
|
|
@@ -3612,7 +3648,7 @@ function formatCredentialsHuman(credentials2) {
|
|
|
3612
3648
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
3613
3649
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
3614
3650
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
3615
|
-
const lines = [
|
|
3651
|
+
const lines = [chalk21.bold(join5(padCells(HEADERS3)))];
|
|
3616
3652
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
3617
3653
|
return lines.join("\n") + "\n";
|
|
3618
3654
|
}
|
|
@@ -3623,7 +3659,7 @@ async function credentialsRm(name, opts, deps) {
|
|
|
3623
3659
|
if (opts.json) {
|
|
3624
3660
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
3625
3661
|
} else {
|
|
3626
|
-
console.log(
|
|
3662
|
+
console.log(chalk21.green(`\u2713 removed ${name}`));
|
|
3627
3663
|
}
|
|
3628
3664
|
process.exitCode = 0;
|
|
3629
3665
|
} catch (err) {
|
|
@@ -3635,16 +3671,16 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
3635
3671
|
const message = err instanceof Error ? err.message : String(err);
|
|
3636
3672
|
if (opts.json) {
|
|
3637
3673
|
console.log(JSON.stringify({ event: "error", message }));
|
|
3638
|
-
console.error(
|
|
3674
|
+
console.error(chalk21.red(`error: ${message}`));
|
|
3639
3675
|
return;
|
|
3640
3676
|
}
|
|
3641
|
-
console.error(
|
|
3677
|
+
console.error(chalk21.red(`error: ${message}`));
|
|
3642
3678
|
console.error(
|
|
3643
|
-
|
|
3679
|
+
chalk21.red(
|
|
3644
3680
|
`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.`
|
|
3645
3681
|
)
|
|
3646
3682
|
);
|
|
3647
|
-
console.error(
|
|
3683
|
+
console.error(chalk21.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
|
|
3648
3684
|
console.error("");
|
|
3649
3685
|
console.error(` Key alias: ${result.key_alias}`);
|
|
3650
3686
|
console.error(` Store password: ${result.store_password}`);
|
|
@@ -3658,11 +3694,11 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
3658
3694
|
function printError5(opts, err) {
|
|
3659
3695
|
const message = err instanceof Error ? err.message : String(err);
|
|
3660
3696
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
3661
|
-
console.error(
|
|
3697
|
+
console.error(chalk21.red(`error: ${message}`));
|
|
3662
3698
|
}
|
|
3663
3699
|
|
|
3664
3700
|
// src/commands/webhooks.ts
|
|
3665
|
-
import
|
|
3701
|
+
import chalk22 from "chalk";
|
|
3666
3702
|
function clientFor3(opts) {
|
|
3667
3703
|
const { token, baseUrl } = resolveAuth(opts);
|
|
3668
3704
|
return new ApiClient(baseUrl, token);
|
|
@@ -3693,14 +3729,14 @@ async function webhooksAdd(opts, deps) {
|
|
|
3693
3729
|
if (opts.json) {
|
|
3694
3730
|
console.log(JSON.stringify(result));
|
|
3695
3731
|
} else {
|
|
3696
|
-
console.log(
|
|
3732
|
+
console.log(chalk22.green(`\u2713 added ${result.id} ${result.url}`));
|
|
3697
3733
|
console.log("");
|
|
3698
|
-
console.log(` Signing secret: ${
|
|
3734
|
+
console.log(` Signing secret: ${chalk22.bold(result.secret)}`);
|
|
3699
3735
|
if (opts.secret !== void 0) {
|
|
3700
|
-
console.log(
|
|
3736
|
+
console.log(chalk22.yellow(" (This is the secret you provided.)"));
|
|
3701
3737
|
}
|
|
3702
3738
|
console.log(
|
|
3703
|
-
|
|
3739
|
+
chalk22.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
|
|
3704
3740
|
);
|
|
3705
3741
|
}
|
|
3706
3742
|
process.exitCode = 0;
|
|
@@ -3731,7 +3767,7 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
3731
3767
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
3732
3768
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
3733
3769
|
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
3734
|
-
const lines = [
|
|
3770
|
+
const lines = [chalk22.bold(join5(padCells(HEADERS3)))];
|
|
3735
3771
|
for (const row of rows) lines.push(join5(padCells(row)));
|
|
3736
3772
|
return lines.join("\n") + "\n";
|
|
3737
3773
|
}
|
|
@@ -3757,7 +3793,7 @@ async function webhooksRm(id, opts, deps) {
|
|
|
3757
3793
|
if (opts.json) {
|
|
3758
3794
|
console.log(JSON.stringify({ event: "deleted", id }));
|
|
3759
3795
|
} else {
|
|
3760
|
-
console.log(
|
|
3796
|
+
console.log(chalk22.green(`\u2713 removed ${id}`));
|
|
3761
3797
|
}
|
|
3762
3798
|
process.exitCode = 0;
|
|
3763
3799
|
} catch (err) {
|
|
@@ -3790,13 +3826,13 @@ async function webhooksTest(id, opts, deps) {
|
|
|
3790
3826
|
if (opts.json) console.log(JSON.stringify(ld));
|
|
3791
3827
|
if (ld.status === "succeeded") {
|
|
3792
3828
|
if (!opts.json) {
|
|
3793
|
-
console.log(
|
|
3829
|
+
console.log(chalk22.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
|
|
3794
3830
|
}
|
|
3795
3831
|
process.exitCode = 0;
|
|
3796
3832
|
} else {
|
|
3797
3833
|
if (!opts.json) {
|
|
3798
3834
|
const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
|
|
3799
|
-
console.error(
|
|
3835
|
+
console.error(chalk22.red(`\u2717 Failed (${code}) at ${formatUtcTimestamp(ld.at)}`));
|
|
3800
3836
|
}
|
|
3801
3837
|
process.exitCode = 1;
|
|
3802
3838
|
}
|
|
@@ -3805,7 +3841,7 @@ async function webhooksTest(id, opts, deps) {
|
|
|
3805
3841
|
}
|
|
3806
3842
|
if (opts.json) console.log(JSON.stringify({ status: "pending" }));
|
|
3807
3843
|
console.error(
|
|
3808
|
-
|
|
3844
|
+
chalk22.yellow(
|
|
3809
3845
|
`Delivery still pending after ${TEST_POLL_BUDGET_MS / 1e3}s \u2014 check \`vibeview webhooks list\` later for the result.`
|
|
3810
3846
|
)
|
|
3811
3847
|
);
|
|
@@ -3818,7 +3854,7 @@ async function webhooksTest(id, opts, deps) {
|
|
|
3818
3854
|
function printError6(opts, err) {
|
|
3819
3855
|
const message = err instanceof Error ? err.message : String(err);
|
|
3820
3856
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
3821
|
-
console.error(
|
|
3857
|
+
console.error(chalk22.red(`error: ${message}`));
|
|
3822
3858
|
}
|
|
3823
3859
|
|
|
3824
3860
|
// src/commands/list-devices.ts
|
|
@@ -3916,7 +3952,7 @@ import { dirname, join as join3 } from "path";
|
|
|
3916
3952
|
import { fileURLToPath } from "url";
|
|
3917
3953
|
import { spawnSync } from "child_process";
|
|
3918
3954
|
import { confirm } from "@inquirer/prompts";
|
|
3919
|
-
import
|
|
3955
|
+
import chalk23 from "chalk";
|
|
3920
3956
|
function resolveShippedSkillPath(fromDir) {
|
|
3921
3957
|
for (const rel of ["..", join3("..", "..")]) {
|
|
3922
3958
|
const candidate = join3(fromDir, rel, "skills", "vibeview-agent", "SKILL.md");
|
|
@@ -3946,23 +3982,23 @@ function registerMcp() {
|
|
|
3946
3982
|
encoding: "utf8"
|
|
3947
3983
|
});
|
|
3948
3984
|
if (res.status === 0) {
|
|
3949
|
-
console.log(
|
|
3985
|
+
console.log(chalk23.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
|
|
3950
3986
|
return;
|
|
3951
3987
|
}
|
|
3952
3988
|
const out = `${res.stderr ?? ""}${res.stdout ?? ""}`;
|
|
3953
3989
|
if (out.includes("already exists")) {
|
|
3954
|
-
console.log(
|
|
3990
|
+
console.log(chalk23.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
|
|
3955
3991
|
return;
|
|
3956
3992
|
}
|
|
3957
|
-
console.error(
|
|
3958
|
-
console.error(
|
|
3993
|
+
console.error(chalk23.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
|
|
3994
|
+
console.error(chalk23.yellow(` register it manually: ${MCP_ADD_HINT}`));
|
|
3959
3995
|
}
|
|
3960
3996
|
async function agentSetup(opts) {
|
|
3961
3997
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
3962
3998
|
const shipped = resolveShippedSkillPath(moduleDir);
|
|
3963
3999
|
if (!shipped) {
|
|
3964
4000
|
console.error(
|
|
3965
|
-
|
|
4001
|
+
chalk23.red(
|
|
3966
4002
|
"error: could not find the packaged skill (skills/vibeview-agent/SKILL.md) \u2014 is this a complete install of the vibeview package?"
|
|
3967
4003
|
)
|
|
3968
4004
|
);
|
|
@@ -3971,9 +4007,9 @@ async function agentSetup(opts) {
|
|
|
3971
4007
|
}
|
|
3972
4008
|
const targetDir = skillInstallDir(opts.project ?? false, process.cwd(), homedir());
|
|
3973
4009
|
const result = installSkill(shipped, targetDir);
|
|
3974
|
-
console.log(
|
|
4010
|
+
console.log(chalk23.green(`\u2713 vibeview-agent skill ${result}: ${join3(targetDir, "SKILL.md")}`));
|
|
3975
4011
|
console.log(
|
|
3976
|
-
|
|
4012
|
+
chalk23.dim(
|
|
3977
4013
|
opts.project ? " Claude Code discovers it automatically for anyone working in this project." : " Claude Code discovers it automatically in every project on this machine."
|
|
3978
4014
|
)
|
|
3979
4015
|
);
|
|
@@ -3981,7 +4017,7 @@ async function agentSetup(opts) {
|
|
|
3981
4017
|
if (!claudeCliAvailable()) {
|
|
3982
4018
|
if (opts.mcp === true) {
|
|
3983
4019
|
console.error(
|
|
3984
|
-
|
|
4020
|
+
chalk23.yellow(`\u26A0 claude CLI not found \u2014 install Claude Code, then run: ${MCP_ADD_HINT}`)
|
|
3985
4021
|
);
|
|
3986
4022
|
}
|
|
3987
4023
|
} else if (opts.mcp === true || process.stdin.isTTY && await confirm({
|
|
@@ -3993,7 +4029,7 @@ async function agentSetup(opts) {
|
|
|
3993
4029
|
}
|
|
3994
4030
|
console.log(
|
|
3995
4031
|
`
|
|
3996
|
-
Suggested line for your project's ${
|
|
4032
|
+
Suggested line for your project's ${chalk23.bold("CLAUDE.md")} (or ${chalk23.bold(
|
|
3997
4033
|
"AGENTS.md"
|
|
3998
4034
|
)} for other agents):
|
|
3999
4035
|
`
|
|
@@ -4001,7 +4037,7 @@ Suggested line for your project's ${chalk22.bold("CLAUDE.md")} (or ${chalk22.bol
|
|
|
4001
4037
|
console.log(` ${CLAUDE_MD_SUGGESTION}
|
|
4002
4038
|
`);
|
|
4003
4039
|
console.log(
|
|
4004
|
-
|
|
4040
|
+
chalk23.dim(
|
|
4005
4041
|
"Other agents (Cursor, Copilot, Codex, ...): install the same skill from GitHub with\n npx skills add vibeview/skills\nor point them at the skill file above, or add the MCP server per client \u2014\nsee https://vibeview.io/docs/agent-control"
|
|
4006
4042
|
)
|
|
4007
4043
|
);
|
|
@@ -4337,6 +4373,9 @@ program.command("build").description("Run your configured build command and uplo
|
|
|
4337
4373
|
"--production",
|
|
4338
4374
|
"--cloud only: build a signed, installable production artifact (.aab/.ipa) using uploaded signing credentials (see `vibeview credentials add`), instead of the default unsigned simulator build",
|
|
4339
4375
|
false
|
|
4376
|
+
).option(
|
|
4377
|
+
"--xcode <version>",
|
|
4378
|
+
"--cloud only, ios/tvos: the Xcode version to build with (e.g. 27.1), overriding vibeview.json build.xcode for this run"
|
|
4340
4379
|
).option("--json", "Print the result as one JSON line (NDJSON events with --cloud)", false).action((opts) => buildCommand(opts));
|
|
4341
4380
|
program.command("submit").description(
|
|
4342
4381
|
"Submit a succeeded production cloud build's artifact to TestFlight (ios/tvos) or a Google Play track (android/androidtv) \u2014 binary upload only; store listing and review stay in the store consoles"
|
|
@@ -4455,13 +4494,21 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
|
|
|
4455
4494
|
program.command("stop <session-id>").description(
|
|
4456
4495
|
"Stop any session by id \u2014 e.g. one started from the API, the sandbox, or a collaboration link (`dev-stop` stops this project's detached dev session without an id)"
|
|
4457
4496
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((sessionId, opts) => stopCommand(sessionId, opts));
|
|
4497
|
+
program.command("feedback <kind> <summary>").description(
|
|
4498
|
+
"Send the VibeView team a bug report, friction note or feature idea about VibeView itself (kind: bug, friction or idea)"
|
|
4499
|
+
).addHelpText("after", `
|
|
4500
|
+
${FEEDBACK_RULES}
|
|
4501
|
+
`).option("--details <text>", "What you tried, what happened, what you expected").option(
|
|
4502
|
+
"--session <id>",
|
|
4503
|
+
"Session the report is about (default: this project's dev --detach session, if any)"
|
|
4504
|
+
).option("--quote <text>", "The user's own words, verbatim").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((kind, summary, opts) => feedbackCommand(kind, summary, opts, pkg.version));
|
|
4458
4505
|
program.command("dev-reload").description(
|
|
4459
4506
|
"Roku only: re-package, upload and reload the channel into this project's `vibeview dev --detach` session (the `r` key of the foreground loop)"
|
|
4460
4507
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print as one JSON line", false).action((opts) => devReload(opts));
|
|
4461
4508
|
program.command("mcp").description(
|
|
4462
4509
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
4463
4510
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
4464
|
-
const { runMcpServer } = await import("./server-
|
|
4511
|
+
const { runMcpServer } = await import("./server-3JZ2MN5N.js");
|
|
4465
4512
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
4466
4513
|
});
|
|
4467
4514
|
program.command("agent-setup").description(
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AGENT_COMMANDS,
|
|
4
4
|
DEV_PLATFORMS,
|
|
5
|
+
FEEDBACK_KINDS,
|
|
6
|
+
FEEDBACK_RULES,
|
|
5
7
|
PLATFORM_DEVICE,
|
|
6
8
|
PLATFORM_NEEDS_METRO,
|
|
7
9
|
buildLines,
|
|
@@ -14,10 +16,12 @@ import {
|
|
|
14
16
|
decodeScreenshotResult,
|
|
15
17
|
describeDevBuild,
|
|
16
18
|
effectiveDevConfig,
|
|
19
|
+
feedbackSentLine,
|
|
17
20
|
filterDeviceModels,
|
|
18
21
|
formatBuildRowsHuman,
|
|
19
22
|
formatDeviceModelsHuman,
|
|
20
23
|
isDevPlatform,
|
|
24
|
+
isFeedbackKind,
|
|
21
25
|
loadDevConfig,
|
|
22
26
|
loadLocalDevConfig,
|
|
23
27
|
outcomeOf,
|
|
@@ -30,12 +34,12 @@ import {
|
|
|
30
34
|
renderEnvelope,
|
|
31
35
|
resolveMetro,
|
|
32
36
|
startDevSession
|
|
33
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-OMIZWILH.js";
|
|
34
38
|
import {
|
|
35
39
|
ApiClient,
|
|
36
40
|
NotFoundError,
|
|
37
41
|
resolveAuth
|
|
38
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-3LXR2YCS.js";
|
|
39
43
|
|
|
40
44
|
// src/mcp/server.ts
|
|
41
45
|
import { writeFileSync } from "fs";
|
|
@@ -243,6 +247,29 @@ var LIST_APPS_DEF = {
|
|
|
243
247
|
},
|
|
244
248
|
required: []
|
|
245
249
|
};
|
|
250
|
+
var SEND_FEEDBACK_DEF = {
|
|
251
|
+
name: "send_feedback",
|
|
252
|
+
description: `Send the VibeView team a bug report, a friction note or a feature idea about VibeView itself. ${FEEDBACK_RULES} The report includes the session's device and status when it names one.`,
|
|
253
|
+
mutating: true,
|
|
254
|
+
properties: {
|
|
255
|
+
kind: {
|
|
256
|
+
type: "string",
|
|
257
|
+
description: "bug, friction or idea.",
|
|
258
|
+
enum: [...FEEDBACK_KINDS]
|
|
259
|
+
},
|
|
260
|
+
summary: { type: "string", description: "One line; kept to 200 characters." },
|
|
261
|
+
details: {
|
|
262
|
+
type: "string",
|
|
263
|
+
description: "What you tried, what happened, what you expected; kept to 4000 characters."
|
|
264
|
+
},
|
|
265
|
+
session_id: SESSION_ID_PROPERTY,
|
|
266
|
+
user_quote: {
|
|
267
|
+
type: "string",
|
|
268
|
+
description: "The user's own words about the problem, verbatim (optional)."
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
required: ["kind", "summary"]
|
|
272
|
+
};
|
|
246
273
|
var DEV_STOP_DEF = {
|
|
247
274
|
name: "dev_stop",
|
|
248
275
|
description: "Stop the dev-loop session started by dev_start.",
|
|
@@ -287,7 +314,8 @@ function buildToolDefinitions() {
|
|
|
287
314
|
CREATE_TEST_DEF,
|
|
288
315
|
RUN_TEST_DEF,
|
|
289
316
|
RUN_GROUP_DEF,
|
|
290
|
-
GET_RUN_DEF
|
|
317
|
+
GET_RUN_DEF,
|
|
318
|
+
SEND_FEEDBACK_DEF
|
|
291
319
|
];
|
|
292
320
|
}
|
|
293
321
|
var runClientOf = (deps) => deps.runClient ?? new ApiClient(deps.auth.baseUrl, deps.auth.token, { client: "mcp" });
|
|
@@ -551,6 +579,9 @@ async function dispatchToolCall(toolName, args, deps, extra = {}) {
|
|
|
551
579
|
if (toolName === "list_apps") {
|
|
552
580
|
return handleListApps(args, deps);
|
|
553
581
|
}
|
|
582
|
+
if (toolName === "send_feedback") {
|
|
583
|
+
return handleSendFeedback(args, deps);
|
|
584
|
+
}
|
|
554
585
|
if (toolName === "stop_session") {
|
|
555
586
|
return handleStopSession(args, deps);
|
|
556
587
|
}
|
|
@@ -839,6 +870,28 @@ async function handleListDeviceModels(args, deps) {
|
|
|
839
870
|
}))
|
|
840
871
|
});
|
|
841
872
|
}
|
|
873
|
+
async function handleSendFeedback(args, deps) {
|
|
874
|
+
if (!isFeedbackKind(args.kind)) {
|
|
875
|
+
return errorResult(`send_feedback: \`kind\` must be one of ${FEEDBACK_KINDS.join(", ")}`);
|
|
876
|
+
}
|
|
877
|
+
const summary = typeof args.summary === "string" ? args.summary.trim() : "";
|
|
878
|
+
if (!summary) return errorResult("send_feedback: `summary` is required");
|
|
879
|
+
const sessionId = typeof args.session_id === "string" && args.session_id.trim() ? args.session_id.trim() : deps.resolveSessionId();
|
|
880
|
+
const body = { kind: args.kind, summary, client: "mcp" };
|
|
881
|
+
if (typeof args.details === "string" && args.details.trim()) body.details = args.details;
|
|
882
|
+
if (typeof args.user_quote === "string" && args.user_quote.trim()) {
|
|
883
|
+
body.user_quote = args.user_quote;
|
|
884
|
+
}
|
|
885
|
+
if (sessionId) body.session_id = sessionId;
|
|
886
|
+
if (deps.clientVersion) body.client_version = deps.clientVersion;
|
|
887
|
+
const send = deps.sendFeedback ?? ((b) => new ApiClient(deps.auth.baseUrl, deps.auth.token, { client: "mcp" }).sendFeedback(b));
|
|
888
|
+
try {
|
|
889
|
+
const result = await send(body);
|
|
890
|
+
return textResult(feedbackSentLine(result), result);
|
|
891
|
+
} catch (err) {
|
|
892
|
+
return errorResult(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
842
895
|
async function handleListApps(args, deps) {
|
|
843
896
|
const list = deps.listApps ?? (() => new ApiClient(deps.auth.baseUrl, deps.auth.token).listApps());
|
|
844
897
|
const platform = typeof args.platform === "string" && args.platform ? args.platform : void 0;
|
|
@@ -945,7 +998,7 @@ async function handleUploadApp(args, deps) {
|
|
|
945
998
|
}
|
|
946
999
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
947
1000
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
948
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
1001
|
+
const { uploadAppBuild } = await import("./upload-app-TDR6PIWD.js");
|
|
949
1002
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
950
1003
|
return result2;
|
|
951
1004
|
});
|
|
@@ -1339,7 +1392,8 @@ var MCP_INSTRUCTIONS = [
|
|
|
1339
1392
|
"screenshot writes a PNG and returns its path; pass inline: true to see the image. logs reads the app output after a crash or blank screen.",
|
|
1340
1393
|
"Tests: create_test, then run_test (or run_group) and get_run. With session_id a run uses the session you hold; without it, a run starts its own new device (never your dev_start session).",
|
|
1341
1394
|
"A session stopped elsewhere is reported as ended; call dev_start again for a new one.",
|
|
1342
|
-
'A session nobody uses ends after the idle timeout. A result that ends with "Session \u2026 ends in N s for inactivity" means act (tap, type, swipe, press) to keep it, or dev_stop if you are done; ui_tree, screenshot, logs, find and wait do not count as use.'
|
|
1395
|
+
'A session nobody uses ends after the idle timeout. A result that ends with "Session \u2026 ends in N s for inactivity" means act (tap, type, swipe, press) to keep it, or dev_stop if you are done; ui_tree, screenshot, logs, find and wait do not count as use.',
|
|
1396
|
+
"If VibeView itself gets in your way (an error you cannot explain, a missing capability, confusing output), offer the user to send it with send_feedback."
|
|
1343
1397
|
].join("\n");
|
|
1344
1398
|
function toolInputSchema(def, z) {
|
|
1345
1399
|
const shape = {};
|
|
@@ -1370,7 +1424,8 @@ async function createMcpServer(opts) {
|
|
|
1370
1424
|
auth,
|
|
1371
1425
|
resolveSessionId: makeSessionResolver(devSession),
|
|
1372
1426
|
agentTool: client.agentTool.bind(client),
|
|
1373
|
-
devSession
|
|
1427
|
+
devSession,
|
|
1428
|
+
clientVersion: opts.version
|
|
1374
1429
|
};
|
|
1375
1430
|
const server = new McpServer(
|
|
1376
1431
|
{ name: "vibeview", version: opts.version ?? "0.0.0" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku devices: a React Native dev loop, device control for you or your coding agent (CLI or MCP), and tests from CI",
|
|
5
5
|
"mcpName": "io.github.vibeview/vibeview",
|
|
6
6
|
"type": "module",
|
|
@@ -252,6 +252,19 @@ hit one:
|
|
|
252
252
|
the confirmation alone. If it's still there, say what you still see and
|
|
253
253
|
ask again.
|
|
254
254
|
|
|
255
|
+
### When VibeView itself gets in your way
|
|
256
|
+
|
|
257
|
+
If VibeView — not the user's app — gets in your way (an error you can't
|
|
258
|
+
explain, a missing capability, confusing output), offer to tell the VibeView
|
|
259
|
+
team. Show the user what you will send and send it only with their OK:
|
|
260
|
+
|
|
261
|
+
vibeview feedback bug "dev_start says build not found for a build I just uploaded" \
|
|
262
|
+
--details "upload-app returned build 123; dev --build 123 failed" --session <id>
|
|
263
|
+
|
|
264
|
+
`kind` is `bug`, `friction` (worked, but confusing, slow or awkward) or `idea`.
|
|
265
|
+
Never include passwords, tokens, test data values or personal data. Over MCP
|
|
266
|
+
the same tool is `send_feedback`.
|
|
267
|
+
|
|
255
268
|
## 5. Cleanup
|
|
256
269
|
|
|
257
270
|
**Always** stop the session when done verifying — a running session bills
|
|
@@ -318,6 +331,7 @@ Dev-loop lifecycle commands (not registry verbs, but needed for every run):
|
|
|
318
331
|
| `dev-stop` | End the current project's detached dev session. |
|
|
319
332
|
| `vibeview stop <session-id>` | End any session by id — one created through the API, the sandbox, or a collaboration link. |
|
|
320
333
|
| `dev-reload` | Roku only: re-package, upload and restart the channel in the detached session (the `r` key of the foreground loop). |
|
|
334
|
+
| `vibeview feedback <bug\|friction\|idea> "<summary>"` | Send the VibeView team a report about VibeView itself (not your app), only after the user OKs it: `--details`, `--session` (defaults to the detached session, if any), `--quote` (the user's own words). Works with no session. |
|
|
321
335
|
|
|
322
336
|
## 7. If you're calling VibeView over MCP
|
|
323
337
|
|