vibeview 0.28.1 → 0.30.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-3DKY443B.js → chunk-NOZGZT3G.js} +355 -117
- package/dist/{chunk-PBMB6TG4.js → chunk-ZZK7YQSP.js} +33 -6
- package/dist/cli.js +123 -17
- package/dist/{server-BGQJOVSN.js → server-2JNYWF5L.js} +8 -4
- package/dist/{upload-app-FEZG23NU.js → upload-app-2LVG4LMB.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +9 -5
|
@@ -74,6 +74,25 @@ var AuthError = class extends Error {
|
|
|
74
74
|
this.name = "AuthError";
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
|
+
var TokenScopeError = class extends AuthError {
|
|
78
|
+
constructor(message) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = "TokenScopeError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var SCOPE_REFUSAL_PREFIX = "This API token has the 'ci' scope";
|
|
84
|
+
function credentialError(status, text) {
|
|
85
|
+
const detail = extractDetail(text);
|
|
86
|
+
if (status === 401) {
|
|
87
|
+
return detail && /expired/i.test(detail) ? new AuthError(detail) : new AuthError();
|
|
88
|
+
}
|
|
89
|
+
if (status === 403 && detail?.startsWith(SCOPE_REFUSAL_PREFIX)) {
|
|
90
|
+
return new TokenScopeError(
|
|
91
|
+
`${detail} Set VIBEVIEW_API_TOKEN (or --token) to a full-access token for this command.`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
77
96
|
function errorExitCode(err, otherwise = 1) {
|
|
78
97
|
return err instanceof AuthError ? 2 : otherwise;
|
|
79
98
|
}
|
|
@@ -168,7 +187,8 @@ var ApiClient = class {
|
|
|
168
187
|
});
|
|
169
188
|
if (!resp.ok) {
|
|
170
189
|
const text = await resp.text().catch(() => "");
|
|
171
|
-
|
|
190
|
+
const credErr = credentialError(resp.status, text);
|
|
191
|
+
if (credErr) throw credErr;
|
|
172
192
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
173
193
|
throw new ApiStatusError(
|
|
174
194
|
resp.status,
|
|
@@ -197,7 +217,9 @@ var ApiClient = class {
|
|
|
197
217
|
device_category: options?.device ? void 0 : options?.deviceCategory,
|
|
198
218
|
device_id: options?.device,
|
|
199
219
|
build_id: options?.build,
|
|
200
|
-
metadata: options?.metadata
|
|
220
|
+
metadata: options?.metadata,
|
|
221
|
+
platform: options?.platform,
|
|
222
|
+
app_id: options?.app
|
|
201
223
|
});
|
|
202
224
|
}
|
|
203
225
|
async runTest(testId, options) {
|
|
@@ -210,7 +232,9 @@ var ApiClient = class {
|
|
|
210
232
|
device_category: options?.device ? void 0 : options?.deviceCategory,
|
|
211
233
|
device_id: options?.device,
|
|
212
234
|
build_id: options?.build,
|
|
213
|
-
metadata: options?.metadata
|
|
235
|
+
metadata: options?.metadata,
|
|
236
|
+
platform: options?.platform,
|
|
237
|
+
app_id: options?.app
|
|
214
238
|
});
|
|
215
239
|
}
|
|
216
240
|
/** Cancel a still-queued CI run by its queue entry id. Idempotent best-effort. */
|
|
@@ -244,7 +268,8 @@ var ApiClient = class {
|
|
|
244
268
|
});
|
|
245
269
|
if (!resp.ok) {
|
|
246
270
|
const text = await resp.text().catch(() => "");
|
|
247
|
-
|
|
271
|
+
const credErr = credentialError(resp.status, text);
|
|
272
|
+
if (credErr) throw credErr;
|
|
248
273
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
249
274
|
throw new ApiStatusError(
|
|
250
275
|
resp.status,
|
|
@@ -267,7 +292,8 @@ var ApiClient = class {
|
|
|
267
292
|
});
|
|
268
293
|
if (!resp.ok) {
|
|
269
294
|
const text = await resp.text().catch(() => "");
|
|
270
|
-
|
|
295
|
+
const credErr = credentialError(resp.status, text);
|
|
296
|
+
if (credErr) throw credErr;
|
|
271
297
|
if (resp.status === 404) throw new NotFoundError(`Not found: ${extractDetail(text) ?? path}`);
|
|
272
298
|
throw new ApiStatusError(
|
|
273
299
|
resp.status,
|
|
@@ -429,7 +455,8 @@ var ApiClient = class {
|
|
|
429
455
|
const resp = await this.fetchOrExplain(url, { method: "GET", headers });
|
|
430
456
|
if (!resp.ok) {
|
|
431
457
|
const text = await resp.text().catch(() => "");
|
|
432
|
-
|
|
458
|
+
const credErr = credentialError(resp.status, text);
|
|
459
|
+
if (credErr) throw credErr;
|
|
433
460
|
if (resp.status === 404)
|
|
434
461
|
throw new NotFoundError(`Not found: ${extractDetail(text) ?? `/builds/${id}/artifact`}`);
|
|
435
462
|
throw new ApiStatusError(
|
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
listBuilds,
|
|
16
16
|
loadDevConfig,
|
|
17
17
|
newTransitionState,
|
|
18
|
+
platformLabel,
|
|
18
19
|
platformWithArticle,
|
|
19
20
|
pressButtonsHelp,
|
|
20
21
|
printOutcomeLine,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
readDevState,
|
|
23
24
|
readMetroState,
|
|
24
25
|
runAgentVerb
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-NOZGZT3G.js";
|
|
26
27
|
import {
|
|
27
28
|
ApiClient,
|
|
28
29
|
ApiStatusError,
|
|
@@ -43,7 +44,7 @@ import {
|
|
|
43
44
|
resolveAuth,
|
|
44
45
|
saveConfig,
|
|
45
46
|
uploadApp
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-ZZK7YQSP.js";
|
|
47
48
|
|
|
48
49
|
// src/cli.ts
|
|
49
50
|
import { createRequire } from "module";
|
|
@@ -570,6 +571,29 @@ function mapExitCode(status) {
|
|
|
570
571
|
return EXIT_INFRA_ERROR;
|
|
571
572
|
}
|
|
572
573
|
|
|
574
|
+
// src/lib/run-target.ts
|
|
575
|
+
var RUN_PLATFORMS = ["ios", "android", "tvos", "androidtv"];
|
|
576
|
+
function parseRunPlatform(value) {
|
|
577
|
+
if (value === void 0) return void 0;
|
|
578
|
+
if (!RUN_PLATFORMS.includes(value)) {
|
|
579
|
+
throw new Error(`--platform must be one of ${RUN_PLATFORMS.join(", ")}, got "${value}"`);
|
|
580
|
+
}
|
|
581
|
+
return value;
|
|
582
|
+
}
|
|
583
|
+
function describeRunTarget(response, options) {
|
|
584
|
+
const asked = Boolean(options.platform || options.app);
|
|
585
|
+
if (!response.app_id) {
|
|
586
|
+
if (asked) {
|
|
587
|
+
throw new Error(
|
|
588
|
+
"this VibeView server does not support --platform/--app on test runs yet; the run was stopped rather than run on the test\u2019s own platform"
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
return null;
|
|
592
|
+
}
|
|
593
|
+
if (!asked) return null;
|
|
594
|
+
return `${response.app_id} (${platformLabel(response.platform ?? "")})`;
|
|
595
|
+
}
|
|
596
|
+
|
|
573
597
|
// src/commands/run-suite.ts
|
|
574
598
|
async function runSuite(suiteId, options) {
|
|
575
599
|
const { token, baseUrl } = resolveAuth(options);
|
|
@@ -635,14 +659,18 @@ ${signal} received.
|
|
|
635
659
|
deviceCategory: options.deviceCategory,
|
|
636
660
|
device: options.device,
|
|
637
661
|
build: options.build,
|
|
638
|
-
metadata: options.metadata
|
|
662
|
+
metadata: options.metadata,
|
|
663
|
+
platform: options.platform,
|
|
664
|
+
app: options.app
|
|
639
665
|
});
|
|
640
666
|
sessionId = response.session_id;
|
|
641
667
|
runId = response.run_id;
|
|
668
|
+
const target = describeRunTarget(response, options);
|
|
642
669
|
spinner.stop();
|
|
643
670
|
process.stderr.write(
|
|
644
671
|
`${chalk3.dim("Suite:")} ${suiteId}
|
|
645
|
-
|
|
672
|
+
` + (target ? `${chalk3.dim("App:")} ${target}
|
|
673
|
+
` : "") + `${chalk3.dim("Run:")} ${response.run_id}
|
|
646
674
|
` + (sessionId ? `${chalk3.dim("Session:")} ${sessionId}
|
|
647
675
|
` : "")
|
|
648
676
|
);
|
|
@@ -1009,16 +1037,20 @@ ${signal} received.
|
|
|
1009
1037
|
deviceCategory: options.deviceCategory,
|
|
1010
1038
|
device: options.device,
|
|
1011
1039
|
build: options.build,
|
|
1012
|
-
metadata: options.metadata
|
|
1040
|
+
metadata: options.metadata,
|
|
1041
|
+
platform: options.platform,
|
|
1042
|
+
app: options.app
|
|
1013
1043
|
});
|
|
1014
1044
|
sessionId = response.session_id;
|
|
1015
1045
|
runId = response.run_id;
|
|
1046
|
+
const target = describeRunTarget(response, options);
|
|
1016
1047
|
spinner.stop();
|
|
1017
1048
|
process.stderr.write(
|
|
1018
1049
|
`${chalk5.dim("Session:")} ${sessionId || "unknown"}
|
|
1019
1050
|
${chalk5.dim("Device:")} ${response.device_name || "allocating..."}
|
|
1020
1051
|
${chalk5.dim("Test:")} ${testId}
|
|
1021
|
-
|
|
1052
|
+
` + (target ? `${chalk5.dim("App:")} ${target}
|
|
1053
|
+
` : "") + `${chalk5.dim("Run:")} ${response.run_id}
|
|
1022
1054
|
`
|
|
1023
1055
|
);
|
|
1024
1056
|
spinner.start("Waiting for device...");
|
|
@@ -1785,7 +1817,8 @@ function defaultDeps2(client) {
|
|
|
1785
1817
|
getSubmission: client.getSubmission.bind(client),
|
|
1786
1818
|
getSubmissionLog: client.getSubmissionLog.bind(client),
|
|
1787
1819
|
listCloudBuilds: client.listCloudBuilds.bind(client),
|
|
1788
|
-
checkStorePreflight: client.checkStorePreflight.bind(client)
|
|
1820
|
+
checkStorePreflight: client.checkStorePreflight.bind(client),
|
|
1821
|
+
getApp: client.getApp.bind(client)
|
|
1789
1822
|
};
|
|
1790
1823
|
}
|
|
1791
1824
|
var POLL_INTERVAL_MS2 = 2e3;
|
|
@@ -1910,7 +1943,7 @@ async function submitCommand(opts, deps) {
|
|
|
1910
1943
|
}
|
|
1911
1944
|
if (rollout !== void 0 && releaseStatus !== "inProgress") {
|
|
1912
1945
|
throw new UsageError(
|
|
1913
|
-
`a rollout fraction requires release status "inProgress" (a staged rollout) \u2014 got "${releaseStatus ?? "completed"}"`
|
|
1946
|
+
`a rollout fraction requires release status "inProgress" (a staged rollout) \u2014 got "${releaseStatus ?? (track === "production" ? "draft" : "completed")}"`
|
|
1914
1947
|
);
|
|
1915
1948
|
}
|
|
1916
1949
|
if (releaseStatus === "inProgress" && rollout === void 0) {
|
|
@@ -1964,12 +1997,19 @@ async function submitCommand(opts, deps) {
|
|
|
1964
1997
|
}
|
|
1965
1998
|
}
|
|
1966
1999
|
async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
1967
|
-
const
|
|
2000
|
+
const flagged = androidFamily ? opts.package : opts.bundleId;
|
|
2001
|
+
const usage = androidFamily ? `--check needs the package name to look up: --package <name> (e.g. --package io.example.app)` : `--check needs the bundle identifier to look up: --bundle-id <id> (e.g. --bundle-id io.example.app)`;
|
|
2002
|
+
let identifier = flagged;
|
|
1968
2003
|
if (!identifier) {
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
)
|
|
2004
|
+
try {
|
|
2005
|
+
identifier = await linkedIdentifier(platform, opts, deps);
|
|
2006
|
+
} catch (err) {
|
|
2007
|
+
printError2(opts, err);
|
|
2008
|
+
process.exitCode = errorExitCode(err);
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
1972
2011
|
}
|
|
2012
|
+
if (!identifier) throw new UsageError(usage);
|
|
1973
2013
|
try {
|
|
1974
2014
|
const result = await deps.checkStorePreflight({
|
|
1975
2015
|
platform,
|
|
@@ -1994,6 +2034,32 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
1994
2034
|
process.exitCode = errorExitCode(err);
|
|
1995
2035
|
}
|
|
1996
2036
|
}
|
|
2037
|
+
async function linkedIdentifier(platform, opts, deps) {
|
|
2038
|
+
if (!deps.getApp) return void 0;
|
|
2039
|
+
let appId;
|
|
2040
|
+
try {
|
|
2041
|
+
appId = loadDevConfig(process.cwd())?.platforms?.[platform]?.appId;
|
|
2042
|
+
} catch {
|
|
2043
|
+
return void 0;
|
|
2044
|
+
}
|
|
2045
|
+
if (!appId) return void 0;
|
|
2046
|
+
let app;
|
|
2047
|
+
try {
|
|
2048
|
+
app = await deps.getApp(appId);
|
|
2049
|
+
} catch (err) {
|
|
2050
|
+
if (err instanceof NotFoundError) {
|
|
2051
|
+
throw new Error(
|
|
2052
|
+
`the app linked in vibeview.json for ${platform} (${appId}) no longer exists \u2014 ` + (platform === "android" || platform === "androidtv" ? "pass --package <name>" : "pass --bundle-id <id>")
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
throw err;
|
|
2056
|
+
}
|
|
2057
|
+
if (!app.package_name) return void 0;
|
|
2058
|
+
if (!opts.json) {
|
|
2059
|
+
console.error(chalk14.dim(`Checking ${app.package_name} (${app.name}, linked in vibeview.json)`));
|
|
2060
|
+
}
|
|
2061
|
+
return app.package_name;
|
|
2062
|
+
}
|
|
1997
2063
|
async function resolveLatestBuild(platform, deps) {
|
|
1998
2064
|
const { builds: builds2 } = await deps.listCloudBuilds({
|
|
1999
2065
|
platform,
|
|
@@ -3534,11 +3600,24 @@ program.command("run-suite <suite-id>").description("Run all test cases in a sui
|
|
|
3534
3600
|
).option(
|
|
3535
3601
|
"--device <device_id>",
|
|
3536
3602
|
"Pin run to a specific device (use `vibeview list-devices` to find one). Conflicts with --device-category."
|
|
3603
|
+
).option(
|
|
3604
|
+
"--platform <platform>",
|
|
3605
|
+
"Run the suite on another platform (ios, android, tvos, androidtv): runs your app there with the same bundle id / package"
|
|
3606
|
+
).option(
|
|
3607
|
+
"--app <app-id>",
|
|
3608
|
+
"Run the suite against this app instead of its own (e.g. another platform build with a different bundle id)"
|
|
3537
3609
|
).option("--build <id>", "Specific AppBuild id to run against (defaults to latest)").option("--metadata <json>", "JSON object passed through to webhooks (e.g. PR context)").option("--metadata-file <path>", "Read JSON object from a file (use for nested JSON in CI)").action((suiteId, opts) => {
|
|
3538
3610
|
if (opts.device && opts.deviceCategory) {
|
|
3539
3611
|
process.stderr.write("error: --device cannot be combined with --device-category\n");
|
|
3540
3612
|
process.exit(2);
|
|
3541
3613
|
}
|
|
3614
|
+
try {
|
|
3615
|
+
parseRunPlatform(opts.platform);
|
|
3616
|
+
} catch (err) {
|
|
3617
|
+
process.stderr.write(`error: ${err.message}
|
|
3618
|
+
`);
|
|
3619
|
+
process.exit(2);
|
|
3620
|
+
}
|
|
3542
3621
|
const metadata = resolveMetadata(opts);
|
|
3543
3622
|
return runSuite(suiteId, {
|
|
3544
3623
|
...opts,
|
|
@@ -3567,6 +3646,13 @@ program.command("run-suites").description("Run all suites matching a tag filter"
|
|
|
3567
3646
|
process.stderr.write("error: --device cannot be combined with --device-category\n");
|
|
3568
3647
|
process.exit(2);
|
|
3569
3648
|
}
|
|
3649
|
+
try {
|
|
3650
|
+
parseRunPlatform(opts.platform);
|
|
3651
|
+
} catch (err) {
|
|
3652
|
+
process.stderr.write(`error: ${err.message}
|
|
3653
|
+
`);
|
|
3654
|
+
process.exit(2);
|
|
3655
|
+
}
|
|
3570
3656
|
const metadata = resolveMetadata(opts);
|
|
3571
3657
|
return runSuites({
|
|
3572
3658
|
...opts,
|
|
@@ -3582,11 +3668,24 @@ program.command("run-test <test-id>").description("Run a single test case").opti
|
|
|
3582
3668
|
).option(
|
|
3583
3669
|
"--device <device_id>",
|
|
3584
3670
|
"Pin run to a specific device (use `vibeview list-devices` to find one). Conflicts with --device-category."
|
|
3671
|
+
).option(
|
|
3672
|
+
"--platform <platform>",
|
|
3673
|
+
"Run the test on another platform (ios, android, tvos, androidtv): runs your app there with the same bundle id / package"
|
|
3674
|
+
).option(
|
|
3675
|
+
"--app <app-id>",
|
|
3676
|
+
"Run the test against this app instead of its own (e.g. another platform build with a different bundle id)"
|
|
3585
3677
|
).option("--build <id>", "Specific AppBuild id to run against (defaults to latest)").option("--metadata <json>", "JSON object passed through to webhooks (e.g. PR context)").option("--metadata-file <path>", "Read JSON object from a file (use for nested JSON in CI)").action((testId, opts) => {
|
|
3586
3678
|
if (opts.device && opts.deviceCategory) {
|
|
3587
3679
|
process.stderr.write("error: --device cannot be combined with --device-category\n");
|
|
3588
3680
|
process.exit(2);
|
|
3589
3681
|
}
|
|
3682
|
+
try {
|
|
3683
|
+
parseRunPlatform(opts.platform);
|
|
3684
|
+
} catch (err) {
|
|
3685
|
+
process.stderr.write(`error: ${err.message}
|
|
3686
|
+
`);
|
|
3687
|
+
process.exit(2);
|
|
3688
|
+
}
|
|
3590
3689
|
const metadata = resolveMetadata(opts);
|
|
3591
3690
|
return runTest(testId, {
|
|
3592
3691
|
...opts,
|
|
@@ -3635,7 +3734,10 @@ program.command("dev").description(
|
|
|
3635
3734
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--web-url <url>", "Dashboard URL if different from the API URL (local dev)").option(
|
|
3636
3735
|
"--platform <platform>",
|
|
3637
3736
|
"ios, android, tvos, androidtv, or roku (first run is interactive otherwise)"
|
|
3638
|
-
).option("--app <app_id>", "Uploaded debug build to run (see `vibeview list-apps`)").option(
|
|
3737
|
+
).option("--app <app_id>", "Uploaded debug build to run (see `vibeview list-apps`)").option(
|
|
3738
|
+
"--metro-port <port>",
|
|
3739
|
+
"Local Metro port (default: 8081, or vibeview.json); remembered for you in .vibeview/local.json"
|
|
3740
|
+
).option(
|
|
3639
3741
|
"--device-category <category>",
|
|
3640
3742
|
"phone or tablet (default: phone; TV platforms always use tv)",
|
|
3641
3743
|
parseDeviceCategory
|
|
@@ -3648,11 +3750,15 @@ program.command("dev").description(
|
|
|
3648
3750
|
false
|
|
3649
3751
|
).option(
|
|
3650
3752
|
"--build [id]",
|
|
3651
|
-
"Bare: build and upload first (uses platforms.<platform>.build from vibeview.json). With an id: run that uploaded debug build instead of the newest one (see `vibeview list-builds <app>`)",
|
|
3753
|
+
"Bare: build and upload first (uses platforms.<platform>.build from vibeview.json). With an id: run that uploaded debug build instead of the newest one (any build with --standalone) (see `vibeview list-builds <app>`)",
|
|
3652
3754
|
false
|
|
3653
3755
|
).option(
|
|
3654
3756
|
"--watch [seconds]",
|
|
3655
3757
|
"Roku only: auto-reload after file changes settle (default: 2 seconds of quiet)"
|
|
3758
|
+
).option(
|
|
3759
|
+
"--standalone",
|
|
3760
|
+
"Run the build as it is, with no Metro and no tunnel (a release, CI or cloud build); without --build <id>, the newest release build. Code changes do not hot-reload",
|
|
3761
|
+
false
|
|
3656
3762
|
).action((opts) => dev(opts));
|
|
3657
3763
|
program.command("build").description("Run your configured build command and upload the result (see vibeview.json)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option(
|
|
3658
3764
|
"--platform <platform>",
|
|
@@ -3687,13 +3793,13 @@ program.command("submit").description(
|
|
|
3687
3793
|
"ios/tvos only: which stored App Store Connect key to use (default when exactly one \u2014 see `vibeview credentials asc-key add`)"
|
|
3688
3794
|
).option(
|
|
3689
3795
|
"--release-status <status>",
|
|
3690
|
-
"android/androidtv only: Play release status \u2014 completed (default
|
|
3796
|
+
"android/androidtv only: Play release status \u2014 completed, draft, or inProgress (default: draft on the production track, completed on the others)"
|
|
3691
3797
|
).option(
|
|
3692
3798
|
"--rollout <fraction>",
|
|
3693
3799
|
"android/androidtv only: staged-rollout fraction, 0 < f < 1 (requires --release-status inProgress)"
|
|
3694
3800
|
).option(
|
|
3695
3801
|
"--check",
|
|
3696
|
-
"Only check store readiness: does the store know this app with your stored key? Takes --platform plus --bundle-id (ios/tvos) or --package (android/androidtv); no build needed. Exit 0 when ready",
|
|
3802
|
+
"Only check store readiness: does the store know this app with your stored key? Takes --platform plus --bundle-id (ios/tvos) or --package (android/androidtv), defaulting to the app linked in vibeview.json; no build needed. Exit 0 when ready",
|
|
3697
3803
|
false
|
|
3698
3804
|
).option("--bundle-id <id>", "ios/tvos, with --check: the bundle identifier to look up").option("--no-wait", "Print the submission id and exit without waiting for completion").option("--json", "Emit line-delimited JSON events on stdout instead of human output", false).action((opts) => submitCommand(opts));
|
|
3699
3805
|
var builds = program.command("builds").description("Manage cloud builds (see `vibeview build --cloud`)");
|
|
@@ -3793,7 +3899,7 @@ program.command("dev-reload").description(
|
|
|
3793
3899
|
program.command("mcp").description(
|
|
3794
3900
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
3795
3901
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
3796
|
-
const { runMcpServer } = await import("./server-
|
|
3902
|
+
const { runMcpServer } = await import("./server-2JNYWF5L.js");
|
|
3797
3903
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
3798
3904
|
});
|
|
3799
3905
|
program.command("agent-setup").description(
|
|
@@ -11,22 +11,25 @@ import {
|
|
|
11
11
|
checkMetro,
|
|
12
12
|
createRokuPush,
|
|
13
13
|
decodeScreenshotResult,
|
|
14
|
+
effectiveDevConfig,
|
|
14
15
|
filterDeviceModels,
|
|
15
16
|
formatBuildRowsHuman,
|
|
16
17
|
formatDeviceModelsHuman,
|
|
17
18
|
isDevPlatform,
|
|
18
19
|
loadDevConfig,
|
|
20
|
+
loadLocalDevConfig,
|
|
19
21
|
parseBuildId,
|
|
20
22
|
platformWithArticle,
|
|
21
23
|
readDevState,
|
|
22
24
|
renderEnvelope,
|
|
25
|
+
resolveMetro,
|
|
23
26
|
startDevSession
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-NOZGZT3G.js";
|
|
25
28
|
import {
|
|
26
29
|
ApiClient,
|
|
27
30
|
NotFoundError,
|
|
28
31
|
resolveAuth
|
|
29
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-ZZK7YQSP.js";
|
|
30
33
|
|
|
31
34
|
// src/mcp/server.ts
|
|
32
35
|
import { writeFileSync } from "fs";
|
|
@@ -539,7 +542,7 @@ async function handleUploadApp(args, deps) {
|
|
|
539
542
|
}
|
|
540
543
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
541
544
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
542
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
545
|
+
const { uploadAppBuild } = await import("./upload-app-2LVG4LMB.js");
|
|
543
546
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
544
547
|
return result2;
|
|
545
548
|
});
|
|
@@ -624,6 +627,7 @@ function createDevSessionController(auth, webUrl) {
|
|
|
624
627
|
} catch {
|
|
625
628
|
devConfig = null;
|
|
626
629
|
}
|
|
630
|
+
devConfig = effectiveDevConfig(devConfig, loadLocalDevConfig(process.cwd()));
|
|
627
631
|
const entry = devConfig?.platforms?.[cfg.platform];
|
|
628
632
|
const appId = cfg.app || entry?.appId;
|
|
629
633
|
if (!appId) {
|
|
@@ -649,7 +653,7 @@ function createDevSessionController(auth, webUrl) {
|
|
|
649
653
|
}
|
|
650
654
|
const standalone = cfg.standalone === true;
|
|
651
655
|
const usesMetro = PLATFORM_NEEDS_METRO[cfg.platform] && !standalone;
|
|
652
|
-
const metroPort = cfg.
|
|
656
|
+
const metroPort = resolveMetro(devConfig, cfg.platform, cfg.metroPort).port;
|
|
653
657
|
if (usesMetro) {
|
|
654
658
|
const metro = await checkMetro(metroPort);
|
|
655
659
|
if (metro === "down") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "VibeView CLI -- run your app on cloud iOS simulators, Android emulators, Apple TV, Android TV and Roku (beta) 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",
|
|
@@ -64,11 +64,15 @@ https://vibeview.io/docs/preparing-your-build.
|
|
|
64
64
|
the page is `https://vibeview.io/sandbox/<session_id>`.)
|
|
65
65
|
|
|
66
66
|
Running `dev` writes two things into the project root: a `.vibeview/`
|
|
67
|
-
directory (live session state
|
|
68
|
-
`
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
directory (live session state, plus `local.json` with this developer's own
|
|
68
|
+
flag overrides such as `--metro-port` — local machine state, never commit)
|
|
69
|
+
and a `vibeview.json` (the platform/app/build setup, which teammates DO
|
|
70
|
+
want). Flags never rewrite a value `vibeview.json` already has; they are
|
|
71
|
+
saved to `.vibeview/local.json` instead, and the CLI prints what it saved.
|
|
72
|
+
Make sure the project's `.gitignore` has `.vibeview/*` and
|
|
73
|
+
`!.vibeview/test-spec.json`, adding them if missing. Leave `vibeview.json`
|
|
74
|
+
tracked, but tell the developer you created it rather than letting it
|
|
75
|
+
appear unexplained in their `git status`.
|
|
72
76
|
|
|
73
77
|
## 3. Verifying
|
|
74
78
|
|