vibeview 0.52.2 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{chunk-S4VPSFPP.js → chunk-77OGQKZQ.js} +11 -1
- package/dist/{chunk-LRPZFDKL.js → chunk-R4GTQPZH.js} +40 -10
- package/dist/cli.js +302 -57
- package/dist/{server-5VWZMZRE.js → server-BGOXC6GL.js} +3 -3
- package/dist/{upload-app-OV3QKL7O.js → upload-app-CYGKCGJE.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -91,6 +91,7 @@ Build-time secrets work the same way:
|
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
vibeview env set NPM_TOKEN "..." # secret by default, injected into builds
|
|
94
|
+
vibeview env set API_URL "https://staging.example.com" --plain --profile preview # only preview builds
|
|
94
95
|
vibeview webhooks add --url https://ci.example.com/hooks/vibeview
|
|
95
96
|
```
|
|
96
97
|
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
resolveArtifact,
|
|
14
14
|
resolveAuth,
|
|
15
15
|
uploadAppBuild
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-R4GTQPZH.js";
|
|
17
17
|
|
|
18
18
|
// src/lib/formatters/not-checked.ts
|
|
19
19
|
function notCheckedSteps(steps) {
|
|
@@ -5566,6 +5566,15 @@ var AGENT_COMMANDS = [
|
|
|
5566
5566
|
}
|
|
5567
5567
|
]
|
|
5568
5568
|
},
|
|
5569
|
+
{
|
|
5570
|
+
// shake: {} — the same control as the dashboard's Shake button. One shake
|
|
5571
|
+
// per call. Phones, tablets and foldables; refused on TV devices.
|
|
5572
|
+
verb: "shake",
|
|
5573
|
+
tool: "shake",
|
|
5574
|
+
summary: "Shake the device once (phones, tablets and foldables)",
|
|
5575
|
+
mutating: true,
|
|
5576
|
+
params: []
|
|
5577
|
+
},
|
|
5569
5578
|
{
|
|
5570
5579
|
// set_location: { latitude: -90..90, longitude: -180..180 } — both
|
|
5571
5580
|
// required (agent_control.py validate_tool_args refuses anything else).
|
|
@@ -6074,6 +6083,7 @@ export {
|
|
|
6074
6083
|
stepsNotCheckedLabel,
|
|
6075
6084
|
notCheckedSuffix,
|
|
6076
6085
|
outcomeOf,
|
|
6086
|
+
PROFILE_NAME_RE,
|
|
6077
6087
|
developmentProfile,
|
|
6078
6088
|
DEV_PLATFORMS,
|
|
6079
6089
|
PLATFORM_NEEDS_METRO,
|
|
@@ -560,7 +560,8 @@ var ApiClient = class {
|
|
|
560
560
|
return this.requestRaw("GET", `/build-env/${qs}`);
|
|
561
561
|
}
|
|
562
562
|
/** POST /api/v1/build-env/ (JSON body) for a plain/sensitive/secret value.
|
|
563
|
-
* Requires ADMIN role. Upserts by (org, app, name)
|
|
563
|
+
* Requires ADMIN role. Upserts by (org, app, profile, name); `profile`
|
|
564
|
+
* omitted = every build profile. */
|
|
564
565
|
async setBuildEnvVar(body) {
|
|
565
566
|
return this.requestRaw("POST", "/build-env/", body);
|
|
566
567
|
}
|
|
@@ -573,6 +574,7 @@ var ApiClient = class {
|
|
|
573
574
|
formData.append("name", body.name);
|
|
574
575
|
formData.append("visibility", body.visibility);
|
|
575
576
|
if (body.app) formData.append("app", body.app);
|
|
577
|
+
if (body.profile) formData.append("profile", body.profile);
|
|
576
578
|
const blob = new Blob([new Uint8Array(body.content)]);
|
|
577
579
|
formData.append("file", blob, body.filename);
|
|
578
580
|
return this.requestFormData("POST", "/build-env/", formData);
|
|
@@ -585,11 +587,15 @@ var ApiClient = class {
|
|
|
585
587
|
async deleteBuildEnvVar(id) {
|
|
586
588
|
await this.requestRaw("DELETE", `/build-env/${id}`);
|
|
587
589
|
}
|
|
588
|
-
/** GET /api/v1/build-env/pull[?app=]. Requires DEVELOPER role.
|
|
589
|
-
* endpoint that returns values — already excludes secret/file
|
|
590
|
-
* server-side.
|
|
591
|
-
|
|
592
|
-
|
|
590
|
+
/** GET /api/v1/build-env/pull[?app=][&profile=]. Requires DEVELOPER role.
|
|
591
|
+
* The only endpoint that returns values — already excludes secret/file
|
|
592
|
+
* variables server-side. With `profile`, the values a build of that
|
|
593
|
+
* profile gets (precedence resolved server-side). */
|
|
594
|
+
async pullBuildEnvVars(app, profile) {
|
|
595
|
+
const params = new URLSearchParams();
|
|
596
|
+
if (app) params.set("app", app);
|
|
597
|
+
if (profile) params.set("profile", profile);
|
|
598
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
593
599
|
return this.requestRaw("GET", `/build-env/pull${qs}`);
|
|
594
600
|
}
|
|
595
601
|
/** GET /api/v1/signing-credentials/[?app=]. Requires VIEWER role. Never
|
|
@@ -670,8 +676,26 @@ var ApiClient = class {
|
|
|
670
676
|
async deletePlayKey(id) {
|
|
671
677
|
await this.requestRaw("DELETE", `/signing-credentials/play-keys/${id}`);
|
|
672
678
|
}
|
|
679
|
+
/** GET /api/v1/signing-credentials/amazon-keys. VIEWER role. */
|
|
680
|
+
async listAmazonKeys() {
|
|
681
|
+
return this.requestRaw("GET", "/signing-credentials/amazon-keys");
|
|
682
|
+
}
|
|
683
|
+
/** POST /api/v1/signing-credentials/amazon-keys (JSON). ADMIN role. The
|
|
684
|
+
* backend mints an Amazon token first, so bad credentials fail here. */
|
|
685
|
+
async addAmazonKey(body) {
|
|
686
|
+
return this.requestRaw("POST", "/signing-credentials/amazon-keys", {
|
|
687
|
+
...body.name !== void 0 ? { name: body.name } : {},
|
|
688
|
+
client_id: body.clientId,
|
|
689
|
+
client_secret: body.clientSecret
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
/** DELETE /api/v1/signing-credentials/amazon-keys/{id}. ADMIN role. */
|
|
693
|
+
async deleteAmazonKey(id) {
|
|
694
|
+
await this.requestRaw("DELETE", `/signing-credentials/amazon-keys/${id}`);
|
|
695
|
+
}
|
|
673
696
|
/** POST /api/v1/submissions — submit a succeeded production build's
|
|
674
|
-
* artifact to TestFlight (ios/tvos)
|
|
697
|
+
* artifact to TestFlight (ios/tvos), a Play track (android/androidtv), or
|
|
698
|
+
* Amazon Appstore review (android/androidtv with store "amazon").
|
|
675
699
|
* `platform` is always sent: the backend derives the real platform from
|
|
676
700
|
* the build row and 400s on a mismatch, so a pasted wrong --build id
|
|
677
701
|
* fails loudly instead of silently submitting to the other store family.
|
|
@@ -688,16 +712,22 @@ var ApiClient = class {
|
|
|
688
712
|
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {},
|
|
689
713
|
...body.releaseStatus !== void 0 ? { release_status: body.releaseStatus } : {},
|
|
690
714
|
...body.rollout !== void 0 ? { rollout: body.rollout } : {},
|
|
691
|
-
...body.changesNotSentForReview !== void 0 ? { changes_not_sent_for_review: body.changesNotSentForReview } : {}
|
|
715
|
+
...body.changesNotSentForReview !== void 0 ? { changes_not_sent_for_review: body.changesNotSentForReview } : {},
|
|
716
|
+
...body.store !== void 0 ? { store: body.store } : {},
|
|
717
|
+
...body.amazonAppId !== void 0 ? { amazon_app_id: body.amazonAppId } : {},
|
|
718
|
+
...body.releaseNotes !== void 0 ? { release_notes: body.releaseNotes } : {},
|
|
719
|
+
...body.discardOpenEdit !== void 0 ? { discard_open_edit: body.discardOpenEdit } : {}
|
|
692
720
|
});
|
|
693
721
|
}
|
|
694
722
|
/** GET /api/v1/submissions/preflight — is the store ready to receive
|
|
695
|
-
* this app? Answers by identifier (bundle id / package name
|
|
696
|
-
*
|
|
723
|
+
* this app? Answers by identifier (bundle id / package name, or an
|
|
724
|
+
* Amazon App ID with store "amazon"), so it can run before any build
|
|
725
|
+
* exists. Always resolves — a "not ready" is an
|
|
697
726
|
* answer carried in `status`/`message`, never a thrown error. */
|
|
698
727
|
async checkStorePreflight(q) {
|
|
699
728
|
const params = new URLSearchParams({ platform: q.platform, identifier: q.identifier });
|
|
700
729
|
if (q.ascKey !== void 0) params.set("asc_key", q.ascKey);
|
|
730
|
+
if (q.store !== void 0) params.set("store", q.store);
|
|
701
731
|
return this.requestRaw("GET", `/submissions/preflight?${params.toString()}`);
|
|
702
732
|
}
|
|
703
733
|
/** GET /api/v1/submissions?build=<id> — that build's submissions, newest
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BUILD_TERMINAL_STATUSES,
|
|
5
5
|
FEEDBACK_KINDS,
|
|
6
6
|
FEEDBACK_RULES,
|
|
7
|
+
PROFILE_NAME_RE,
|
|
7
8
|
buildCommand,
|
|
8
9
|
clearDevState,
|
|
9
10
|
createRokuPush,
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
runAgentVerb,
|
|
36
37
|
runStepsNotChecked,
|
|
37
38
|
stepsNotCheckedLabel
|
|
38
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-77OGQKZQ.js";
|
|
39
40
|
import {
|
|
40
41
|
ApiClient,
|
|
41
42
|
ApiStatusError,
|
|
@@ -58,7 +59,7 @@ import {
|
|
|
58
59
|
runCommandParseExitCode,
|
|
59
60
|
saveConfig,
|
|
60
61
|
uploadApp
|
|
61
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-R4GTQPZH.js";
|
|
62
63
|
|
|
63
64
|
// src/cli.ts
|
|
64
65
|
import { createRequire } from "module";
|
|
@@ -2376,6 +2377,14 @@ var SUBMIT_PLATFORMS = ["ios", "tvos", "android", "androidtv"];
|
|
|
2376
2377
|
function isSubmitPlatform(p) {
|
|
2377
2378
|
return SUBMIT_PLATFORMS.includes(p);
|
|
2378
2379
|
}
|
|
2380
|
+
var SUBMIT_STORES = {
|
|
2381
|
+
ios: ["app_store"],
|
|
2382
|
+
tvos: ["app_store"],
|
|
2383
|
+
android: ["play", "amazon"],
|
|
2384
|
+
androidtv: ["play", "amazon"]
|
|
2385
|
+
};
|
|
2386
|
+
var AMAZON_APP_ID_RE = /^amzn1\.devportal\.mobileapp\.[0-9a-f]{32}$/;
|
|
2387
|
+
var AMAZON_APK_PROFILE_HINT = 'Amazon needs a build profile with "distribution": "production" whose command builds a signed .apk (e.g. ./gradlew assembleRelease) \u2014 see https://vibeview.io/docs/store-submission#build-an-apk.';
|
|
2379
2388
|
var PLAY_TRACKS = ["internal", "alpha", "beta", "production"];
|
|
2380
2389
|
var RELEASE_STATUSES = ["completed", "draft", "inProgress"];
|
|
2381
2390
|
function submitConfigFor(cwd, platform) {
|
|
@@ -2431,9 +2440,25 @@ async function submitCommand(opts, deps) {
|
|
|
2431
2440
|
}
|
|
2432
2441
|
const platform = opts.platform;
|
|
2433
2442
|
const androidFamily = platform === "android" || platform === "androidtv";
|
|
2443
|
+
if (opts.store !== void 0) {
|
|
2444
|
+
if (!androidFamily) {
|
|
2445
|
+
throw new UsageError(
|
|
2446
|
+
`--store only applies to android/androidtv (Google Play or Amazon Appstore) \u2014 ${platform} builds go to TestFlight`
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
if (!SUBMIT_STORES[platform].includes(opts.store)) {
|
|
2450
|
+
throw new UsageError(
|
|
2451
|
+
`--store must be one of ${SUBMIT_STORES[platform].join(", ")} \u2014 got "${opts.store}"`
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
const amazon = opts.store === "amazon";
|
|
2456
|
+
const play = androidFamily && !amazon;
|
|
2457
|
+
const apple = !androidFamily;
|
|
2458
|
+
const target = amazon ? "--store amazon" : platform;
|
|
2434
2459
|
if (opts.check && (opts.build || opts.latest)) {
|
|
2435
2460
|
throw new UsageError(
|
|
2436
|
-
"--check looks the app up by identifier, not by build \u2014 drop --build/--latest (pass --bundle-id for ios/tvos or --package for android/androidtv)"
|
|
2461
|
+
"--check looks the app up by identifier, not by build \u2014 drop --build/--latest " + (amazon ? "(pass --amazon-app-id)" : "(pass --bundle-id for ios/tvos or --package for android/androidtv)")
|
|
2437
2462
|
);
|
|
2438
2463
|
}
|
|
2439
2464
|
if (!opts.check && opts.bundleId !== void 0) {
|
|
@@ -2449,38 +2474,63 @@ async function submitCommand(opts, deps) {
|
|
|
2449
2474
|
"pass --build <id> (see `vibeview builds list`) or --latest (the newest succeeded production build for this platform)"
|
|
2450
2475
|
);
|
|
2451
2476
|
}
|
|
2452
|
-
if (!
|
|
2453
|
-
throw new UsageError(`--track only applies to android/androidtv (Play) \u2014 not ${
|
|
2477
|
+
if (!play && opts.track !== void 0) {
|
|
2478
|
+
throw new UsageError(`--track only applies to android/androidtv (Play) \u2014 not ${target}`);
|
|
2479
|
+
}
|
|
2480
|
+
if (!play && opts.package !== void 0) {
|
|
2481
|
+
throw new UsageError(`--package only applies to android/androidtv (Play) \u2014 not ${target}`);
|
|
2454
2482
|
}
|
|
2455
|
-
if (!
|
|
2456
|
-
throw new UsageError(`--
|
|
2483
|
+
if (!apple && opts.ascKey !== void 0) {
|
|
2484
|
+
throw new UsageError(`--asc-key only applies to ios/tvos (TestFlight) \u2014 not ${target}`);
|
|
2457
2485
|
}
|
|
2458
|
-
if (
|
|
2459
|
-
throw new UsageError(
|
|
2486
|
+
if (!apple && opts.bundleId !== void 0) {
|
|
2487
|
+
throw new UsageError(
|
|
2488
|
+
amazon ? "--bundle-id only applies to ios/tvos \u2014 for --store amazon pass --amazon-app-id <id>" : `--bundle-id only applies to ios/tvos \u2014 for ${platform} pass --package <name>`
|
|
2489
|
+
);
|
|
2460
2490
|
}
|
|
2461
|
-
if (
|
|
2491
|
+
if (!play && opts.releaseStatus !== void 0) {
|
|
2462
2492
|
throw new UsageError(
|
|
2463
|
-
`--
|
|
2493
|
+
`--release-status only applies to android/androidtv (Play) \u2014 not ${target}`
|
|
2464
2494
|
);
|
|
2465
2495
|
}
|
|
2466
|
-
if (!
|
|
2496
|
+
if (!play && opts.rollout !== void 0) {
|
|
2497
|
+
throw new UsageError(`--rollout only applies to android/androidtv (Play) \u2014 not ${target}`);
|
|
2498
|
+
}
|
|
2499
|
+
if (!amazon) {
|
|
2500
|
+
for (const [flag, value] of [
|
|
2501
|
+
["--amazon-app-id", opts.amazonAppId],
|
|
2502
|
+
["--release-notes", opts.releaseNotes],
|
|
2503
|
+
["--discard-open-edit", opts.discardOpenEdit || void 0]
|
|
2504
|
+
]) {
|
|
2505
|
+
if (value !== void 0) {
|
|
2506
|
+
throw new UsageError(
|
|
2507
|
+
`${flag} only applies with --store amazon (Amazon Appstore, android/androidtv)`
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
const amazonAppId = opts.amazonAppId?.trim() || void 0;
|
|
2513
|
+
if (amazonAppId !== void 0 && !AMAZON_APP_ID_RE.test(amazonAppId)) {
|
|
2467
2514
|
throw new UsageError(
|
|
2468
|
-
`--
|
|
2515
|
+
`--amazon-app-id must be an Amazon App ID (amzn1.devportal.mobileapp. followed by 32 hex characters) \u2014 got "${amazonAppId}"`
|
|
2469
2516
|
);
|
|
2470
2517
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2518
|
+
const releaseNotes = opts.releaseNotes?.trim() || void 0;
|
|
2519
|
+
if (amazon && !opts.check && releaseNotes === void 0) {
|
|
2520
|
+
throw new UsageError(
|
|
2521
|
+
'--release-notes "<text>" is required with --store amazon \u2014 they are written to every language of your Amazon listing'
|
|
2522
|
+
);
|
|
2473
2523
|
}
|
|
2474
|
-
const cfgDefaults = submitConfigFor(process.cwd(), platform);
|
|
2475
|
-
const track = opts.track ?? (
|
|
2476
|
-
const releaseStatus = opts.releaseStatus ?? (
|
|
2524
|
+
const cfgDefaults = amazon ? {} : submitConfigFor(process.cwd(), platform);
|
|
2525
|
+
const track = opts.track ?? (play ? cfgDefaults.track : void 0);
|
|
2526
|
+
const releaseStatus = opts.releaseStatus ?? (play ? cfgDefaults.releaseStatus : void 0);
|
|
2477
2527
|
const statusUnchanged = opts.releaseStatus === void 0 || opts.releaseStatus === cfgDefaults.releaseStatus;
|
|
2478
|
-
const rollout = opts.rollout !== void 0 ? validateRolloutFraction(Number(opts.rollout), "--rollout", opts.rollout) :
|
|
2528
|
+
const rollout = opts.rollout !== void 0 ? validateRolloutFraction(Number(opts.rollout), "--rollout", opts.rollout) : play && statusUnchanged && cfgDefaults.rollout !== void 0 ? validateRolloutFraction(
|
|
2479
2529
|
cfgDefaults.rollout,
|
|
2480
2530
|
`vibeview.json platforms.${platform}.submit.rollout`
|
|
2481
2531
|
) : void 0;
|
|
2482
|
-
const changesNotSentForReview =
|
|
2483
|
-
const ascKey2 = opts.ascKey ?? (
|
|
2532
|
+
const changesNotSentForReview = play ? cfgDefaults.changesNotSentForReview : void 0;
|
|
2533
|
+
const ascKey2 = opts.ascKey ?? (apple ? cfgDefaults.ascKey : void 0);
|
|
2484
2534
|
if (track !== void 0 && !PLAY_TRACKS.includes(track)) {
|
|
2485
2535
|
const source = opts.track !== void 0 ? "--track" : `vibeview.json platforms.${platform}.submit.track`;
|
|
2486
2536
|
throw new UsageError(`${source} must be one of ${PLAY_TRACKS.join(", ")} \u2014 got "${track}"`);
|
|
@@ -2505,11 +2555,11 @@ async function submitCommand(opts, deps) {
|
|
|
2505
2555
|
const client = new ApiClient(baseUrl, token);
|
|
2506
2556
|
const d = deps ?? defaultDeps2(client);
|
|
2507
2557
|
if (opts.check) {
|
|
2508
|
-
await checkStore(platform, androidFamily, ascKey2, opts, d);
|
|
2558
|
+
await checkStore(platform, { androidFamily, amazon, amazonAppId }, ascKey2, opts, d);
|
|
2509
2559
|
return;
|
|
2510
2560
|
}
|
|
2511
2561
|
try {
|
|
2512
|
-
const buildId = opts.build ?? await resolveLatestBuild(platform, d);
|
|
2562
|
+
const buildId = opts.build ?? await resolveLatestBuild(platform, amazon, d);
|
|
2513
2563
|
const sub = await d.createSubmission({
|
|
2514
2564
|
build: buildId,
|
|
2515
2565
|
platform,
|
|
@@ -2518,7 +2568,11 @@ async function submitCommand(opts, deps) {
|
|
|
2518
2568
|
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
|
|
2519
2569
|
...releaseStatus !== void 0 ? { releaseStatus } : {},
|
|
2520
2570
|
...rollout !== void 0 ? { rollout } : {},
|
|
2521
|
-
...changesNotSentForReview !== void 0 ? { changesNotSentForReview } : {}
|
|
2571
|
+
...changesNotSentForReview !== void 0 ? { changesNotSentForReview } : {},
|
|
2572
|
+
...amazon ? { store: "amazon" } : {},
|
|
2573
|
+
...amazonAppId !== void 0 ? { amazonAppId } : {},
|
|
2574
|
+
...releaseNotes !== void 0 ? { releaseNotes } : {},
|
|
2575
|
+
...amazon && opts.discardOpenEdit ? { discardOpenEdit: true } : {}
|
|
2522
2576
|
});
|
|
2523
2577
|
emit(opts.json, {
|
|
2524
2578
|
event: "submission_created",
|
|
@@ -2546,13 +2600,14 @@ async function submitCommand(opts, deps) {
|
|
|
2546
2600
|
process.exitCode = errorExitCode(err);
|
|
2547
2601
|
}
|
|
2548
2602
|
}
|
|
2549
|
-
async function checkStore(platform,
|
|
2550
|
-
const
|
|
2551
|
-
const
|
|
2603
|
+
async function checkStore(platform, family, ascKey2, opts, deps) {
|
|
2604
|
+
const { androidFamily, amazon } = family;
|
|
2605
|
+
const flagged = amazon ? family.amazonAppId : androidFamily ? opts.package : opts.bundleId;
|
|
2606
|
+
const usage = amazon ? "--check needs the Amazon App ID to look up: --amazon-app-id <id> (in the Amazon Developer Console open the app, then Upload Your App File \u2192 Additional information \u2192 App ID)" : 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)`;
|
|
2552
2607
|
let identifier = flagged;
|
|
2553
2608
|
if (!identifier) {
|
|
2554
2609
|
try {
|
|
2555
|
-
identifier = await linkedIdentifier(platform, opts, deps);
|
|
2610
|
+
identifier = await linkedIdentifier(platform, amazon, opts, deps);
|
|
2556
2611
|
} catch (err) {
|
|
2557
2612
|
printError2(opts, err);
|
|
2558
2613
|
process.exitCode = errorExitCode(err);
|
|
@@ -2564,7 +2619,8 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2564
2619
|
const result = await deps.checkStorePreflight({
|
|
2565
2620
|
platform,
|
|
2566
2621
|
identifier,
|
|
2567
|
-
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {}
|
|
2622
|
+
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
|
|
2623
|
+
...amazon ? { store: "amazon" } : {}
|
|
2568
2624
|
});
|
|
2569
2625
|
emit(opts.json, { event: "preflight", ...result });
|
|
2570
2626
|
if (!opts.json) {
|
|
@@ -2573,7 +2629,17 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2573
2629
|
if (result.ready) {
|
|
2574
2630
|
console.error(
|
|
2575
2631
|
chalk17.dim(
|
|
2576
|
-
|
|
2632
|
+
amazon ? (
|
|
2633
|
+
// Amazon takes an .apk — under the documented setup that is its
|
|
2634
|
+
// own profile, not --production (an .aab). The profile must
|
|
2635
|
+
// say "distribution": "production" (a prompt-created profile
|
|
2636
|
+
// has none, so it builds for the simulator and --latest never
|
|
2637
|
+
// finds it).
|
|
2638
|
+
// The suggested command never carries --discard-open-edit:
|
|
2639
|
+
// deleting the user's edit is their call, on its own line.
|
|
2640
|
+
` next: vibeview build --cloud --platform ${platform} --profile <your .apk profile, e.g. amazon>, then vibeview submit --platform ${platform} --store amazon --latest --release-notes "<what's new in this version>"` + (flagged ? ` --amazon-app-id ${flagged}` : "") + `
|
|
2641
|
+
${AMAZON_APK_PROFILE_HINT}` + (result.open_edit ? "\n An edit is open in the Amazon Developer Console \u2014 finish or delete it there, or add --discard-open-edit to delete it (and the changes in it)." : "")
|
|
2642
|
+
) : ` next: vibeview build --cloud --production --platform ${platform}, then vibeview submit --platform ${platform} --latest`
|
|
2577
2643
|
)
|
|
2578
2644
|
);
|
|
2579
2645
|
}
|
|
@@ -2584,7 +2650,7 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2584
2650
|
process.exitCode = errorExitCode(err);
|
|
2585
2651
|
}
|
|
2586
2652
|
}
|
|
2587
|
-
async function linkedIdentifier(platform, opts, deps) {
|
|
2653
|
+
async function linkedIdentifier(platform, amazon, opts, deps) {
|
|
2588
2654
|
if (!deps.getApp) return void 0;
|
|
2589
2655
|
let appId;
|
|
2590
2656
|
try {
|
|
@@ -2599,18 +2665,19 @@ async function linkedIdentifier(platform, opts, deps) {
|
|
|
2599
2665
|
} catch (err) {
|
|
2600
2666
|
if (err instanceof NotFoundError) {
|
|
2601
2667
|
throw new Error(
|
|
2602
|
-
`the app linked in vibeview.json for ${platform} (${appId}) no longer exists \u2014 ` + (platform === "android" || platform === "androidtv" ? "pass --package <name>" : "pass --bundle-id <id>")
|
|
2668
|
+
`the app linked in vibeview.json for ${platform} (${appId}) no longer exists \u2014 ` + (amazon ? "pass --amazon-app-id <id>" : platform === "android" || platform === "androidtv" ? "pass --package <name>" : "pass --bundle-id <id>")
|
|
2603
2669
|
);
|
|
2604
2670
|
}
|
|
2605
2671
|
throw err;
|
|
2606
2672
|
}
|
|
2607
|
-
|
|
2673
|
+
const identifier = amazon ? app.amazon_app_id ?? void 0 : app.package_name;
|
|
2674
|
+
if (!identifier) return void 0;
|
|
2608
2675
|
if (!opts.json) {
|
|
2609
|
-
console.error(chalk17.dim(`Checking ${
|
|
2676
|
+
console.error(chalk17.dim(`Checking ${identifier} (${app.name}, linked in vibeview.json)`));
|
|
2610
2677
|
}
|
|
2611
|
-
return
|
|
2678
|
+
return identifier;
|
|
2612
2679
|
}
|
|
2613
|
-
async function resolveLatestBuild(platform, deps) {
|
|
2680
|
+
async function resolveLatestBuild(platform, apkOnly, deps) {
|
|
2614
2681
|
const { builds: builds2 } = await deps.listCloudBuilds({
|
|
2615
2682
|
platform,
|
|
2616
2683
|
status: "succeeded",
|
|
@@ -2618,8 +2685,13 @@ async function resolveLatestBuild(platform, deps) {
|
|
|
2618
2685
|
limit: 200
|
|
2619
2686
|
});
|
|
2620
2687
|
const match = builds2.find(
|
|
2621
|
-
(b) => b.platform === platform && b.distribution === "production" && b.status === "succeeded" && b.has_artifact
|
|
2688
|
+
(b) => b.platform === platform && b.distribution === "production" && b.status === "succeeded" && b.has_artifact && (!apkOnly || (b.artifact_filename ?? "").toLowerCase().endsWith(".apk"))
|
|
2622
2689
|
);
|
|
2690
|
+
if (!match && apkOnly) {
|
|
2691
|
+
throw new Error(
|
|
2692
|
+
`no submittable ${platform} .apk build found \u2014 Amazon's API accepts only .apk files, so --latest needs a succeeded production .apk build whose artifact is still stored. ${AMAZON_APK_PROFILE_HINT} Or pass --build <id> (see \`vibeview builds list\`).`
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2623
2695
|
if (!match) {
|
|
2624
2696
|
throw new Error(
|
|
2625
2697
|
`no submittable ${platform} build found \u2014 --latest needs a succeeded production build whose artifact is still stored. Run \`vibeview build --cloud --production --platform ${platform}\` first, or pass --build <id> (see \`vibeview builds list\`).`
|
|
@@ -2882,6 +2954,17 @@ function isGitIgnored(path, cwd = process.cwd()) {
|
|
|
2882
2954
|
}
|
|
2883
2955
|
|
|
2884
2956
|
// src/commands/env.ts
|
|
2957
|
+
function checkProfileOption(profile) {
|
|
2958
|
+
if (profile === void 0) return;
|
|
2959
|
+
if (!PROFILE_NAME_RE.test(profile)) {
|
|
2960
|
+
throw new Error(
|
|
2961
|
+
`--profile must be a build profile name (lowercase letters, digits and dashes, starting with a letter), got "${profile}"`
|
|
2962
|
+
);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
function profileLabel(profile) {
|
|
2966
|
+
return profile ? `profile "${profile}"` : "every profile";
|
|
2967
|
+
}
|
|
2885
2968
|
function clientFor(opts) {
|
|
2886
2969
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2887
2970
|
return new ApiClient(baseUrl, token);
|
|
@@ -2900,10 +2983,20 @@ function defaultDeps5(client) {
|
|
|
2900
2983
|
listVars: (app) => client.listBuildEnvVars(app),
|
|
2901
2984
|
setVar: (body) => client.setBuildEnvVar(body),
|
|
2902
2985
|
setFileVar: (body) => client.setBuildEnvFileVar(body),
|
|
2903
|
-
rmVar: async ({ name, app }) => {
|
|
2986
|
+
rmVar: async ({ name, app, profile }) => {
|
|
2904
2987
|
const { variables } = await client.listBuildEnvVars(app);
|
|
2905
2988
|
const wantApp = app ?? null;
|
|
2906
|
-
const
|
|
2989
|
+
const wantProfile = profile ?? null;
|
|
2990
|
+
const inScope = variables.filter(
|
|
2991
|
+
(v) => v.name === name && (v.app_public_id ?? null) === wantApp
|
|
2992
|
+
);
|
|
2993
|
+
const match = inScope.find((v) => (v.profile ?? null) === wantProfile);
|
|
2994
|
+
if (!match && inScope.length > 0) {
|
|
2995
|
+
const others = inScope.map((v) => v.profile ? `--profile ${v.profile}` : "no --profile (every profile)").join(", ");
|
|
2996
|
+
throw new Error(
|
|
2997
|
+
`No variable named "${name}" for ${profileLabel(wantProfile)}${app ? ` in app "${app}"` : ""}. It exists for: ${others}. Re-run with the matching --profile to remove one.`
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
2907
3000
|
if (!match) {
|
|
2908
3001
|
const otherScope = variables.find((v) => v.name === name);
|
|
2909
3002
|
if (otherScope) {
|
|
@@ -2915,7 +3008,7 @@ function defaultDeps5(client) {
|
|
|
2915
3008
|
}
|
|
2916
3009
|
await client.deleteBuildEnvVar(match.id);
|
|
2917
3010
|
},
|
|
2918
|
-
pullVars: (app) => client.pullBuildEnvVars(app),
|
|
3011
|
+
pullVars: (app, profile) => client.pullBuildEnvVars(app, profile),
|
|
2919
3012
|
// `hidden` is always true in practice (env set only prompts for secret
|
|
2920
3013
|
// values), but the parameter exists so a future caller/test can't
|
|
2921
3014
|
// accidentally get echoed input by omitting it.
|
|
@@ -2928,9 +3021,11 @@ function defaultDeps5(client) {
|
|
|
2928
3021
|
};
|
|
2929
3022
|
}
|
|
2930
3023
|
async function envList(opts, deps) {
|
|
3024
|
+
checkProfileOption(opts.profile);
|
|
2931
3025
|
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
2932
3026
|
try {
|
|
2933
|
-
const
|
|
3027
|
+
const listed = (await d.listVars(opts.app)).variables;
|
|
3028
|
+
const variables = opts.profile ? listed.filter((v) => (v.profile ?? null) === null || v.profile === opts.profile) : listed;
|
|
2934
3029
|
if (opts.json) {
|
|
2935
3030
|
console.log(JSON.stringify(variables, null, 2));
|
|
2936
3031
|
} else {
|
|
@@ -2946,11 +3041,12 @@ function formatEnvVarsHuman(variables) {
|
|
|
2946
3041
|
if (variables.length === 0) {
|
|
2947
3042
|
return "No build variables found.\n";
|
|
2948
3043
|
}
|
|
2949
|
-
const HEADERS3 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
|
|
3044
|
+
const HEADERS3 = ["NAME", "VISIBILITY", "SCOPE", "PROFILE", "HINT"];
|
|
2950
3045
|
const rows = variables.map((v) => [
|
|
2951
3046
|
v.name,
|
|
2952
3047
|
v.is_file ? `${v.visibility} (file)` : v.visibility,
|
|
2953
3048
|
v.app_public_id ?? "org-wide",
|
|
3049
|
+
v.profile ?? "all",
|
|
2954
3050
|
v.value_hint ?? ""
|
|
2955
3051
|
]);
|
|
2956
3052
|
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
@@ -2961,6 +3057,7 @@ function formatEnvVarsHuman(variables) {
|
|
|
2961
3057
|
return lines.join("\n") + "\n";
|
|
2962
3058
|
}
|
|
2963
3059
|
async function envSet(name, value, opts, deps) {
|
|
3060
|
+
checkProfileOption(opts.profile);
|
|
2964
3061
|
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
2965
3062
|
let resolvedValue = value;
|
|
2966
3063
|
if (resolvedValue === void 0 && !opts.file) {
|
|
@@ -2975,7 +3072,14 @@ async function envSet(name, value, opts, deps) {
|
|
|
2975
3072
|
if (opts.file) {
|
|
2976
3073
|
const content = await d.readFile(opts.file);
|
|
2977
3074
|
const filename = basename2(opts.file);
|
|
2978
|
-
const result2 = await d.setFileVar({
|
|
3075
|
+
const result2 = await d.setFileVar({
|
|
3076
|
+
name,
|
|
3077
|
+
content,
|
|
3078
|
+
filename,
|
|
3079
|
+
visibility,
|
|
3080
|
+
app: opts.app,
|
|
3081
|
+
profile: opts.profile
|
|
3082
|
+
});
|
|
2979
3083
|
printSetResult(opts, result2);
|
|
2980
3084
|
process.exitCode = 0;
|
|
2981
3085
|
return;
|
|
@@ -2983,7 +3087,13 @@ async function envSet(name, value, opts, deps) {
|
|
|
2983
3087
|
if (resolvedValue === void 0) {
|
|
2984
3088
|
resolvedValue = await d.prompt({ message: `Value for ${name}`, hidden: true });
|
|
2985
3089
|
}
|
|
2986
|
-
const result = await d.setVar({
|
|
3090
|
+
const result = await d.setVar({
|
|
3091
|
+
name,
|
|
3092
|
+
value: resolvedValue,
|
|
3093
|
+
visibility,
|
|
3094
|
+
app: opts.app,
|
|
3095
|
+
profile: opts.profile
|
|
3096
|
+
});
|
|
2987
3097
|
printSetResult(opts, result);
|
|
2988
3098
|
process.exitCode = 0;
|
|
2989
3099
|
} catch (err) {
|
|
@@ -3006,17 +3116,20 @@ function printSetResult(opts, result) {
|
|
|
3006
3116
|
if (opts.json) {
|
|
3007
3117
|
console.log(JSON.stringify(result));
|
|
3008
3118
|
} else {
|
|
3009
|
-
|
|
3119
|
+
const forProfile = opts.profile ? `, profile ${opts.profile}` : "";
|
|
3120
|
+
console.log(chalk20.green(`\u2713 set ${result.name} (${result.visibility}${forProfile})`));
|
|
3010
3121
|
}
|
|
3011
3122
|
}
|
|
3012
3123
|
async function envRm(name, opts, deps) {
|
|
3124
|
+
checkProfileOption(opts.profile);
|
|
3013
3125
|
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
3014
3126
|
try {
|
|
3015
|
-
await d.rmVar({ name, app: opts.app });
|
|
3127
|
+
await d.rmVar({ name, app: opts.app, profile: opts.profile });
|
|
3016
3128
|
if (opts.json) {
|
|
3017
|
-
console.log(JSON.stringify({ event: "deleted", name }));
|
|
3129
|
+
console.log(JSON.stringify({ event: "deleted", name, profile: opts.profile ?? null }));
|
|
3018
3130
|
} else {
|
|
3019
|
-
|
|
3131
|
+
const forProfile = opts.profile ? ` (profile ${opts.profile})` : "";
|
|
3132
|
+
console.log(chalk20.green(`\u2713 removed ${name}${forProfile}`));
|
|
3020
3133
|
}
|
|
3021
3134
|
process.exitCode = 0;
|
|
3022
3135
|
} catch (err) {
|
|
@@ -3025,10 +3138,11 @@ async function envRm(name, opts, deps) {
|
|
|
3025
3138
|
}
|
|
3026
3139
|
}
|
|
3027
3140
|
async function envPull(opts, deps) {
|
|
3141
|
+
checkProfileOption(opts.profile);
|
|
3028
3142
|
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
3029
3143
|
const outPath = opts.out ?? ".env";
|
|
3030
3144
|
try {
|
|
3031
|
-
const { variables } = await d.pullVars(opts.app);
|
|
3145
|
+
const { variables } = await d.pullVars(opts.app, opts.profile);
|
|
3032
3146
|
const ignored = await d.isIgnored(outPath);
|
|
3033
3147
|
if (!ignored) {
|
|
3034
3148
|
d.warn(
|
|
@@ -3160,7 +3274,24 @@ function defaultDeps6(client) {
|
|
|
3160
3274
|
}
|
|
3161
3275
|
await client.deletePlayKey(match.id);
|
|
3162
3276
|
},
|
|
3163
|
-
|
|
3277
|
+
addAmazonKey: (body) => client.addAmazonKey(body),
|
|
3278
|
+
listAmazonKeys: () => client.listAmazonKeys(),
|
|
3279
|
+
deleteAmazonKey: async (name) => {
|
|
3280
|
+
const { keys } = await client.listAmazonKeys();
|
|
3281
|
+
const match = keys.find((k) => k.name === name);
|
|
3282
|
+
if (!match) {
|
|
3283
|
+
throw new Error(`No Amazon Appstore key named "${name}" found.`);
|
|
3284
|
+
}
|
|
3285
|
+
await client.deleteAmazonKey(match.id);
|
|
3286
|
+
},
|
|
3287
|
+
readStdin: async () => {
|
|
3288
|
+
const chunks = [];
|
|
3289
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
3290
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3291
|
+
},
|
|
3292
|
+
// Prompts render on stderr: inquirer defaults to stdout, which would put
|
|
3293
|
+
// "? Client secret" and its echo ahead of a --json line.
|
|
3294
|
+
prompt: ({ message, hidden }) => hidden ? password2({ message }, { output: process.stderr }) : input({ message }, { output: process.stderr }),
|
|
3164
3295
|
isTTY: Boolean(process.stdin.isTTY),
|
|
3165
3296
|
readFile: (path) => readFileAsync2(path),
|
|
3166
3297
|
fileExists: (path) => access(path).then(
|
|
@@ -3593,6 +3724,88 @@ async function playKeyRm(name, opts, deps) {
|
|
|
3593
3724
|
process.exitCode = errorExitCode(err);
|
|
3594
3725
|
}
|
|
3595
3726
|
}
|
|
3727
|
+
async function amazonKeyAdd(opts, deps) {
|
|
3728
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
3729
|
+
if (!opts.clientId?.trim()) {
|
|
3730
|
+
throw new UsageError(
|
|
3731
|
+
"--client-id is required (the security profile client ID from the Amazon Developer Console \u2192 API Access)"
|
|
3732
|
+
);
|
|
3733
|
+
}
|
|
3734
|
+
const raw = d.isTTY ? await d.prompt({ message: "Client secret", hidden: true }) : await d.readStdin();
|
|
3735
|
+
const clientSecret = raw.trim();
|
|
3736
|
+
if (!clientSecret) {
|
|
3737
|
+
throw new UsageError(
|
|
3738
|
+
"a client secret is required: run interactively to be prompted, or pipe it on stdin (secrets are never accepted as flags)"
|
|
3739
|
+
);
|
|
3740
|
+
}
|
|
3741
|
+
try {
|
|
3742
|
+
const result = await d.addAmazonKey({
|
|
3743
|
+
...opts.name !== void 0 ? { name: opts.name } : {},
|
|
3744
|
+
clientId: opts.clientId.trim(),
|
|
3745
|
+
clientSecret
|
|
3746
|
+
});
|
|
3747
|
+
if (opts.json) {
|
|
3748
|
+
console.log(JSON.stringify(result));
|
|
3749
|
+
} else {
|
|
3750
|
+
console.log(chalk21.green(`\u2713 added Amazon Appstore key ${result.name}`));
|
|
3751
|
+
console.log("");
|
|
3752
|
+
console.log(` Client ID: ${chalk21.bold(result.client_id)}`);
|
|
3753
|
+
console.log("");
|
|
3754
|
+
console.log(
|
|
3755
|
+
chalk21.yellow(
|
|
3756
|
+
" The client secret is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
3757
|
+
)
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3760
|
+
process.exitCode = 0;
|
|
3761
|
+
} catch (err) {
|
|
3762
|
+
printError5(opts, err);
|
|
3763
|
+
process.exitCode = errorExitCode(err);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
async function amazonKeyList(opts, deps) {
|
|
3767
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
3768
|
+
try {
|
|
3769
|
+
const { keys } = await d.listAmazonKeys();
|
|
3770
|
+
if (opts.json) {
|
|
3771
|
+
console.log(JSON.stringify(keys, null, 2));
|
|
3772
|
+
} else {
|
|
3773
|
+
process.stdout.write(formatAmazonKeysHuman(keys));
|
|
3774
|
+
}
|
|
3775
|
+
process.exitCode = 0;
|
|
3776
|
+
} catch (err) {
|
|
3777
|
+
printError5(opts, err);
|
|
3778
|
+
process.exitCode = errorExitCode(err);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
function formatAmazonKeysHuman(keys) {
|
|
3782
|
+
if (keys.length === 0) {
|
|
3783
|
+
return "No Amazon Appstore keys found.\n";
|
|
3784
|
+
}
|
|
3785
|
+
const HEADERS3 = ["NAME", "CLIENT ID", "CREATED"];
|
|
3786
|
+
const rows = keys.map((k) => [k.name, k.client_id, formatUtcTimestamp(k.created_at)]);
|
|
3787
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
3788
|
+
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
3789
|
+
const join5 = (cells) => cells.join(" ").trimEnd();
|
|
3790
|
+
const lines = [chalk21.bold(join5(padCells(HEADERS3)))];
|
|
3791
|
+
for (const row of rows) lines.push(join5(padCells(row)));
|
|
3792
|
+
return lines.join("\n") + "\n";
|
|
3793
|
+
}
|
|
3794
|
+
async function amazonKeyRm(name, opts, deps) {
|
|
3795
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
3796
|
+
try {
|
|
3797
|
+
await d.deleteAmazonKey(name);
|
|
3798
|
+
if (opts.json) {
|
|
3799
|
+
console.log(JSON.stringify({ event: "deleted", name }));
|
|
3800
|
+
} else {
|
|
3801
|
+
console.log(chalk21.green(`\u2713 removed Amazon Appstore key ${name}`));
|
|
3802
|
+
}
|
|
3803
|
+
process.exitCode = 0;
|
|
3804
|
+
} catch (err) {
|
|
3805
|
+
printError5(opts, err);
|
|
3806
|
+
process.exitCode = errorExitCode(err);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3596
3809
|
async function credentialsList(opts, deps) {
|
|
3597
3810
|
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
3598
3811
|
try {
|
|
@@ -4390,7 +4603,7 @@ program.command("build").description("Run your configured build command and uplo
|
|
|
4390
4603
|
"--cloud only: the Node.js major to build with (e.g. 24), overriding vibeview.json build.node and the project .nvmrc for this run"
|
|
4391
4604
|
).option("--json", "Print the result as one JSON line (NDJSON events with --cloud)", false).action((opts) => buildCommand(opts));
|
|
4392
4605
|
program.command("submit").description(
|
|
4393
|
-
"Submit a succeeded production cloud build's artifact to TestFlight (ios/tvos)
|
|
4606
|
+
"Submit a succeeded production cloud build's artifact to TestFlight (ios/tvos), a Google Play track (android/androidtv), or Amazon Appstore review (android/androidtv with --store amazon). TestFlight and Google Play: binary upload only \u2014 store listing and review stay in the store consoles. --store amazon also writes the release notes and sends the version for Amazon review"
|
|
4394
4607
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--platform <platform>", "ios, tvos, android, or androidtv").option("--build <id>", "Cloud build id to submit (see `vibeview builds list`)").option(
|
|
4395
4608
|
"--latest",
|
|
4396
4609
|
"Submit the newest succeeded production build for --platform (pass exactly one of --build/--latest)",
|
|
@@ -4410,9 +4623,21 @@ program.command("submit").description(
|
|
|
4410
4623
|
).option(
|
|
4411
4624
|
"--rollout <fraction>",
|
|
4412
4625
|
"android/androidtv only: staged-rollout fraction, 0 < f < 1 (requires --release-status inProgress)"
|
|
4626
|
+
).option(
|
|
4627
|
+
"--store <store>",
|
|
4628
|
+
"android/androidtv only: play (Google Play, default) or amazon (Amazon Appstore \u2014 .apk builds only; reaches Fire TV and Fire tablets)"
|
|
4629
|
+
).option(
|
|
4630
|
+
"--amazon-app-id <id>",
|
|
4631
|
+
"--store amazon only: the Amazon App ID (Developer Console \u2192 your app \u2192 Upload Your App File \u2192 Additional information); saved on the app, so later submits can omit it"
|
|
4632
|
+
).option(
|
|
4633
|
+
"--release-notes <text>",
|
|
4634
|
+
"--store amazon only, required: release notes written to every language of the Amazon listing"
|
|
4635
|
+
).option(
|
|
4636
|
+
"--discard-open-edit",
|
|
4637
|
+
"--store amazon only: discard an edit already open in the Amazon Developer Console"
|
|
4413
4638
|
).option(
|
|
4414
4639
|
"--check",
|
|
4415
|
-
"Only check store readiness: does the store know this app with your stored key? Takes --platform plus --bundle-id (ios/tvos)
|
|
4640
|
+
"Only check store readiness: does the store know this app with your stored key? Takes --platform plus --bundle-id (ios/tvos), --package (android/androidtv) or --amazon-app-id (with --store amazon), defaulting to the app linked in vibeview.json; no build needed. Exit 0 when ready",
|
|
4416
4641
|
false
|
|
4417
4642
|
).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));
|
|
4418
4643
|
var builds = program.command("builds").description("Manage cloud builds (see `vibeview build --cloud`)");
|
|
@@ -4432,17 +4657,29 @@ versions.command("set <number>").description(
|
|
|
4432
4657
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "App public id (see `vibeview list-apps`)").option("--platform <platform>", "ios, android, tvos, or androidtv").option("--json", "Print the updated version object as one JSON line", false).action((number, opts) => versionsSet(number, opts));
|
|
4433
4658
|
program.command("whoami").description("Show which account and role this CLI is authenticated as").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the identity as one JSON line", false).action((opts) => whoAmI(opts));
|
|
4434
4659
|
var env = program.command("env").description("Manage cloud build environment variables and secrets");
|
|
4435
|
-
env.command("list").description("List build variables and secrets (never prints a value)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option(
|
|
4660
|
+
env.command("list").description("List build variables and secrets (never prints a value)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option(
|
|
4661
|
+
"--profile <name>",
|
|
4662
|
+
"Only variables a build of this vibeview.json build profile can use (its own + every-profile ones)"
|
|
4663
|
+
).option("--json", "Print the raw variable list as a JSON array", false).action((opts) => envList(opts));
|
|
4436
4664
|
env.command("set <name> [value]").description(
|
|
4437
4665
|
"Set a build variable or secret. Defaults to --secret (never retrievable via the API) unless --plain or --sensitive is passed explicitly."
|
|
4438
|
-
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option(
|
|
4666
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option(
|
|
4667
|
+
"--profile <name>",
|
|
4668
|
+
"Limit to one vibeview.json build profile (default: every profile); its value beats the every-profile one"
|
|
4669
|
+
).option("--secret", "Store as secret (default): never returned by the API, including `env pull`").option(
|
|
4439
4670
|
"--sensitive",
|
|
4440
4671
|
"Store as sensitive: value_hint only in `env list`, full value via `env pull`"
|
|
4441
4672
|
).option("--plain", "Store as plain: value returned by `env pull`, unmasked").option("--file <path>", "Upload a file as the value (e.g. a keystore) instead of a text value").option("--json", "Print the resulting variable as one JSON line", false).action((name, value, opts) => envSet(name, value, opts));
|
|
4442
|
-
env.command("rm <name>").description("Remove a build variable or secret").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option(
|
|
4673
|
+
env.command("rm <name>").description("Remove a build variable or secret").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option(
|
|
4674
|
+
"--profile <name>",
|
|
4675
|
+
"Remove the one limited to this build profile (default: the every-profile one)"
|
|
4676
|
+
).option("--json", "Print the result as one JSON line", false).action((name, opts) => envRm(name, opts));
|
|
4443
4677
|
env.command("pull").description(
|
|
4444
4678
|
"Write plain/sensitive variable values to a .env file (secrets and files are never included)"
|
|
4445
|
-
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option(
|
|
4679
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide variables only)").option(
|
|
4680
|
+
"--profile <name>",
|
|
4681
|
+
"Write the values a build of this vibeview.json build profile gets (default: every-profile values only)"
|
|
4682
|
+
).option("--out <path>", "Output file path (default: .env)").option("--json", "Print the result as one JSON line", false).action((opts) => envPull(opts));
|
|
4446
4683
|
var credentials = program.command("credentials").description("Manage app-signing credentials for production cloud builds");
|
|
4447
4684
|
credentials.command("add").description(
|
|
4448
4685
|
"Add a signing credential. Prompts for passwords with hidden input \u2014 they are never accepted as flags (a flag lands in shell history and in `ps`)."
|
|
@@ -4492,6 +4729,14 @@ playKey.command("add").description(
|
|
|
4492
4729
|
).option("--name <name>", "Key name (default: server-chosen)").option("--json", "Print the raw response as one JSON line", false).action((opts) => playKeyAdd(opts));
|
|
4493
4730
|
playKey.command("list").description("List stored Google Play keys (metadata only \u2014 never the JSON key)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the raw key list as a JSON array", false).action((opts) => playKeyList(opts));
|
|
4494
4731
|
playKey.command("rm <name>").description("Remove a stored Google Play key").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((name, opts) => playKeyRm(name, opts));
|
|
4732
|
+
var amazonKey = credentials.command("amazon-key").description(
|
|
4733
|
+
"Manage Amazon Appstore keys (a security profile attached to the App Submission API) \u2014 they power `vibeview submit --store amazon`"
|
|
4734
|
+
);
|
|
4735
|
+
amazonKey.command("add").description(
|
|
4736
|
+
"Store an Amazon security profile client ID and secret. The secret is prompted for (or read from stdin when piped), stored encrypted and never shown again; adding the same name again rotates the key."
|
|
4737
|
+
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--client-id <id>", "The security profile client ID").option("--name <name>", "Key name (default: amazon)").option("--json", "Print the raw response as one JSON line", false).action((opts) => amazonKeyAdd(opts));
|
|
4738
|
+
amazonKey.command("list").description("List stored Amazon Appstore keys (metadata only \u2014 never the secret)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the raw key list as a JSON array", false).action((opts) => amazonKeyList(opts));
|
|
4739
|
+
amazonKey.command("rm <name>").description("Remove a stored Amazon Appstore key").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((name, opts) => amazonKeyRm(name, opts));
|
|
4495
4740
|
credentials.command("list").description("List signing credentials (never prints key material)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide credentials only)").option("--json", "Print the raw credential list as a JSON array", false).action((opts) => credentialsList(opts));
|
|
4496
4741
|
credentials.command("rm <name>").description("Remove a signing credential (replace by rm then add)").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "Scope to a single app (default: org-wide)").option("--json", "Print the result as one JSON line", false).action((name, opts) => credentialsRm(name, opts));
|
|
4497
4742
|
var webhooks = program.command("webhooks").description("Manage build-completion webhook endpoints");
|
|
@@ -4520,7 +4765,7 @@ program.command("dev-reload").description(
|
|
|
4520
4765
|
program.command("mcp").description(
|
|
4521
4766
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
4522
4767
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
4523
|
-
const { runMcpServer } = await import("./server-
|
|
4768
|
+
const { runMcpServer } = await import("./server-BGOXC6GL.js");
|
|
4524
4769
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
4525
4770
|
});
|
|
4526
4771
|
program.command("agent-setup").description(
|
|
@@ -35,12 +35,12 @@ import {
|
|
|
35
35
|
renderEnvelope,
|
|
36
36
|
resolveMetro,
|
|
37
37
|
startDevSession
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-77OGQKZQ.js";
|
|
39
39
|
import {
|
|
40
40
|
ApiClient,
|
|
41
41
|
NotFoundError,
|
|
42
42
|
resolveAuth
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-R4GTQPZH.js";
|
|
44
44
|
|
|
45
45
|
// src/mcp/server.ts
|
|
46
46
|
import { writeFileSync } from "fs";
|
|
@@ -999,7 +999,7 @@ async function handleUploadApp(args, deps) {
|
|
|
999
999
|
}
|
|
1000
1000
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
1001
1001
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
1002
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
1002
|
+
const { uploadAppBuild } = await import("./upload-app-CYGKCGJE.js");
|
|
1003
1003
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
1004
1004
|
return result2;
|
|
1005
1005
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.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",
|
|
@@ -311,6 +311,7 @@ human-readable text.
|
|
|
311
311
|
| `relaunch-app` | — | Close the app and start it again (cold start, data kept) — for "does it persist after a restart" checks. The app log keeps streaming. Not on embed sessions. |
|
|
312
312
|
| `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. |
|
|
313
313
|
| `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. |
|
|
314
|
+
| `shake` | — | Shake the device once, like the dashboard's Shake button, to open an app's shake-triggered menus (a bug-report screen, the React Native developer menu in a debug build). Phones, tablets and foldables; not on TV or embed sessions. |
|
|
314
315
|
| `set-language` | `<tag\|default>` | Restart the app in another language (`de`, `pt-BR`, …) or the device's (`default`). Only the app changes. iPhone, iPad, Apple TV, Android 13+; not Roku or embed sessions. |
|
|
315
316
|
| `set-status-bar` | `<clean\|default>` | Show the clean screenshot status bar (9:41, full battery and signal) or the device's own. iOS phones and tablets only. |
|
|
316
317
|
| `set-location` | `--lat <n> --lon <n>` | Set the device's simulated GPS position (decimal degrees; west and south are negative) for location-based flows. Phones, tablets and foldables; not on TV. |
|