vibeview 0.53.0 → 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/dist/{chunk-O6MXCYWR.js → chunk-77OGQKZQ.js} +10 -1
- package/dist/{chunk-YCFQKMG7.js → chunk-R4GTQPZH.js} +28 -4
- package/dist/cli.js +231 -41
- package/dist/{server-JLT3PQTH.js → server-BGOXC6GL.js} +3 -3
- package/dist/{upload-app-KQCXAZST.js → upload-app-CYGKCGJE.js} +1 -1
- package/package.json +1 -1
- package/skills/vibeview-agent/SKILL.md +1 -0
|
@@ -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).
|
|
@@ -676,8 +676,26 @@ var ApiClient = class {
|
|
|
676
676
|
async deletePlayKey(id) {
|
|
677
677
|
await this.requestRaw("DELETE", `/signing-credentials/play-keys/${id}`);
|
|
678
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
|
+
}
|
|
679
696
|
/** POST /api/v1/submissions — submit a succeeded production build's
|
|
680
|
-
* 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").
|
|
681
699
|
* `platform` is always sent: the backend derives the real platform from
|
|
682
700
|
* the build row and 400s on a mismatch, so a pasted wrong --build id
|
|
683
701
|
* fails loudly instead of silently submitting to the other store family.
|
|
@@ -694,16 +712,22 @@ var ApiClient = class {
|
|
|
694
712
|
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {},
|
|
695
713
|
...body.releaseStatus !== void 0 ? { release_status: body.releaseStatus } : {},
|
|
696
714
|
...body.rollout !== void 0 ? { rollout: body.rollout } : {},
|
|
697
|
-
...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 } : {}
|
|
698
720
|
});
|
|
699
721
|
}
|
|
700
722
|
/** GET /api/v1/submissions/preflight — is the store ready to receive
|
|
701
|
-
* this app? Answers by identifier (bundle id / package name
|
|
702
|
-
*
|
|
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
|
|
703
726
|
* answer carried in `status`/`message`, never a thrown error. */
|
|
704
727
|
async checkStorePreflight(q) {
|
|
705
728
|
const params = new URLSearchParams({ platform: q.platform, identifier: q.identifier });
|
|
706
729
|
if (q.ascKey !== void 0) params.set("asc_key", q.ascKey);
|
|
730
|
+
if (q.store !== void 0) params.set("store", q.store);
|
|
707
731
|
return this.requestRaw("GET", `/submissions/preflight?${params.toString()}`);
|
|
708
732
|
}
|
|
709
733
|
/** GET /api/v1/submissions?build=<id> — that build's submissions, newest
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
runAgentVerb,
|
|
37
37
|
runStepsNotChecked,
|
|
38
38
|
stepsNotCheckedLabel
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-77OGQKZQ.js";
|
|
40
40
|
import {
|
|
41
41
|
ApiClient,
|
|
42
42
|
ApiStatusError,
|
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
runCommandParseExitCode,
|
|
60
60
|
saveConfig,
|
|
61
61
|
uploadApp
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-R4GTQPZH.js";
|
|
63
63
|
|
|
64
64
|
// src/cli.ts
|
|
65
65
|
import { createRequire } from "module";
|
|
@@ -2377,6 +2377,14 @@ var SUBMIT_PLATFORMS = ["ios", "tvos", "android", "androidtv"];
|
|
|
2377
2377
|
function isSubmitPlatform(p) {
|
|
2378
2378
|
return SUBMIT_PLATFORMS.includes(p);
|
|
2379
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.';
|
|
2380
2388
|
var PLAY_TRACKS = ["internal", "alpha", "beta", "production"];
|
|
2381
2389
|
var RELEASE_STATUSES = ["completed", "draft", "inProgress"];
|
|
2382
2390
|
function submitConfigFor(cwd, platform) {
|
|
@@ -2432,9 +2440,25 @@ async function submitCommand(opts, deps) {
|
|
|
2432
2440
|
}
|
|
2433
2441
|
const platform = opts.platform;
|
|
2434
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;
|
|
2435
2459
|
if (opts.check && (opts.build || opts.latest)) {
|
|
2436
2460
|
throw new UsageError(
|
|
2437
|
-
"--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)")
|
|
2438
2462
|
);
|
|
2439
2463
|
}
|
|
2440
2464
|
if (!opts.check && opts.bundleId !== void 0) {
|
|
@@ -2450,38 +2474,63 @@ async function submitCommand(opts, deps) {
|
|
|
2450
2474
|
"pass --build <id> (see `vibeview builds list`) or --latest (the newest succeeded production build for this platform)"
|
|
2451
2475
|
);
|
|
2452
2476
|
}
|
|
2453
|
-
if (!
|
|
2454
|
-
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}`);
|
|
2455
2479
|
}
|
|
2456
|
-
if (!
|
|
2457
|
-
throw new UsageError(`--package only applies to android/androidtv (Play) \u2014 not ${
|
|
2480
|
+
if (!play && opts.package !== void 0) {
|
|
2481
|
+
throw new UsageError(`--package only applies to android/androidtv (Play) \u2014 not ${target}`);
|
|
2458
2482
|
}
|
|
2459
|
-
if (
|
|
2460
|
-
throw new UsageError(`--asc-key only applies to ios/tvos (TestFlight) \u2014 not ${
|
|
2483
|
+
if (!apple && opts.ascKey !== void 0) {
|
|
2484
|
+
throw new UsageError(`--asc-key only applies to ios/tvos (TestFlight) \u2014 not ${target}`);
|
|
2461
2485
|
}
|
|
2462
|
-
if (
|
|
2486
|
+
if (!apple && opts.bundleId !== void 0) {
|
|
2463
2487
|
throw new UsageError(
|
|
2464
|
-
`--bundle-id only applies to ios/tvos \u2014 for ${platform} pass --package <name>`
|
|
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>`
|
|
2465
2489
|
);
|
|
2466
2490
|
}
|
|
2467
|
-
if (!
|
|
2491
|
+
if (!play && opts.releaseStatus !== void 0) {
|
|
2468
2492
|
throw new UsageError(
|
|
2469
|
-
`--release-status only applies to android/androidtv (Play) \u2014 not ${
|
|
2493
|
+
`--release-status only applies to android/androidtv (Play) \u2014 not ${target}`
|
|
2470
2494
|
);
|
|
2471
2495
|
}
|
|
2472
|
-
if (!
|
|
2473
|
-
throw new UsageError(`--rollout only applies to android/androidtv (Play) \u2014 not ${
|
|
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
|
+
}
|
|
2474
2511
|
}
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
|
|
2512
|
+
const amazonAppId = opts.amazonAppId?.trim() || void 0;
|
|
2513
|
+
if (amazonAppId !== void 0 && !AMAZON_APP_ID_RE.test(amazonAppId)) {
|
|
2514
|
+
throw new UsageError(
|
|
2515
|
+
`--amazon-app-id must be an Amazon App ID (amzn1.devportal.mobileapp. followed by 32 hex characters) \u2014 got "${amazonAppId}"`
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
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
|
+
);
|
|
2523
|
+
}
|
|
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);
|
|
2478
2527
|
const statusUnchanged = opts.releaseStatus === void 0 || opts.releaseStatus === cfgDefaults.releaseStatus;
|
|
2479
|
-
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(
|
|
2480
2529
|
cfgDefaults.rollout,
|
|
2481
2530
|
`vibeview.json platforms.${platform}.submit.rollout`
|
|
2482
2531
|
) : void 0;
|
|
2483
|
-
const changesNotSentForReview =
|
|
2484
|
-
const ascKey2 = opts.ascKey ?? (
|
|
2532
|
+
const changesNotSentForReview = play ? cfgDefaults.changesNotSentForReview : void 0;
|
|
2533
|
+
const ascKey2 = opts.ascKey ?? (apple ? cfgDefaults.ascKey : void 0);
|
|
2485
2534
|
if (track !== void 0 && !PLAY_TRACKS.includes(track)) {
|
|
2486
2535
|
const source = opts.track !== void 0 ? "--track" : `vibeview.json platforms.${platform}.submit.track`;
|
|
2487
2536
|
throw new UsageError(`${source} must be one of ${PLAY_TRACKS.join(", ")} \u2014 got "${track}"`);
|
|
@@ -2506,11 +2555,11 @@ async function submitCommand(opts, deps) {
|
|
|
2506
2555
|
const client = new ApiClient(baseUrl, token);
|
|
2507
2556
|
const d = deps ?? defaultDeps2(client);
|
|
2508
2557
|
if (opts.check) {
|
|
2509
|
-
await checkStore(platform, androidFamily, ascKey2, opts, d);
|
|
2558
|
+
await checkStore(platform, { androidFamily, amazon, amazonAppId }, ascKey2, opts, d);
|
|
2510
2559
|
return;
|
|
2511
2560
|
}
|
|
2512
2561
|
try {
|
|
2513
|
-
const buildId = opts.build ?? await resolveLatestBuild(platform, d);
|
|
2562
|
+
const buildId = opts.build ?? await resolveLatestBuild(platform, amazon, d);
|
|
2514
2563
|
const sub = await d.createSubmission({
|
|
2515
2564
|
build: buildId,
|
|
2516
2565
|
platform,
|
|
@@ -2519,7 +2568,11 @@ async function submitCommand(opts, deps) {
|
|
|
2519
2568
|
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
|
|
2520
2569
|
...releaseStatus !== void 0 ? { releaseStatus } : {},
|
|
2521
2570
|
...rollout !== void 0 ? { rollout } : {},
|
|
2522
|
-
...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 } : {}
|
|
2523
2576
|
});
|
|
2524
2577
|
emit(opts.json, {
|
|
2525
2578
|
event: "submission_created",
|
|
@@ -2547,13 +2600,14 @@ async function submitCommand(opts, deps) {
|
|
|
2547
2600
|
process.exitCode = errorExitCode(err);
|
|
2548
2601
|
}
|
|
2549
2602
|
}
|
|
2550
|
-
async function checkStore(platform,
|
|
2551
|
-
const
|
|
2552
|
-
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)`;
|
|
2553
2607
|
let identifier = flagged;
|
|
2554
2608
|
if (!identifier) {
|
|
2555
2609
|
try {
|
|
2556
|
-
identifier = await linkedIdentifier(platform, opts, deps);
|
|
2610
|
+
identifier = await linkedIdentifier(platform, amazon, opts, deps);
|
|
2557
2611
|
} catch (err) {
|
|
2558
2612
|
printError2(opts, err);
|
|
2559
2613
|
process.exitCode = errorExitCode(err);
|
|
@@ -2565,7 +2619,8 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2565
2619
|
const result = await deps.checkStorePreflight({
|
|
2566
2620
|
platform,
|
|
2567
2621
|
identifier,
|
|
2568
|
-
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {}
|
|
2622
|
+
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
|
|
2623
|
+
...amazon ? { store: "amazon" } : {}
|
|
2569
2624
|
});
|
|
2570
2625
|
emit(opts.json, { event: "preflight", ...result });
|
|
2571
2626
|
if (!opts.json) {
|
|
@@ -2574,7 +2629,17 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2574
2629
|
if (result.ready) {
|
|
2575
2630
|
console.error(
|
|
2576
2631
|
chalk17.dim(
|
|
2577
|
-
|
|
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`
|
|
2578
2643
|
)
|
|
2579
2644
|
);
|
|
2580
2645
|
}
|
|
@@ -2585,7 +2650,7 @@ async function checkStore(platform, androidFamily, ascKey2, opts, deps) {
|
|
|
2585
2650
|
process.exitCode = errorExitCode(err);
|
|
2586
2651
|
}
|
|
2587
2652
|
}
|
|
2588
|
-
async function linkedIdentifier(platform, opts, deps) {
|
|
2653
|
+
async function linkedIdentifier(platform, amazon, opts, deps) {
|
|
2589
2654
|
if (!deps.getApp) return void 0;
|
|
2590
2655
|
let appId;
|
|
2591
2656
|
try {
|
|
@@ -2600,18 +2665,19 @@ async function linkedIdentifier(platform, opts, deps) {
|
|
|
2600
2665
|
} catch (err) {
|
|
2601
2666
|
if (err instanceof NotFoundError) {
|
|
2602
2667
|
throw new Error(
|
|
2603
|
-
`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>")
|
|
2604
2669
|
);
|
|
2605
2670
|
}
|
|
2606
2671
|
throw err;
|
|
2607
2672
|
}
|
|
2608
|
-
|
|
2673
|
+
const identifier = amazon ? app.amazon_app_id ?? void 0 : app.package_name;
|
|
2674
|
+
if (!identifier) return void 0;
|
|
2609
2675
|
if (!opts.json) {
|
|
2610
|
-
console.error(chalk17.dim(`Checking ${
|
|
2676
|
+
console.error(chalk17.dim(`Checking ${identifier} (${app.name}, linked in vibeview.json)`));
|
|
2611
2677
|
}
|
|
2612
|
-
return
|
|
2678
|
+
return identifier;
|
|
2613
2679
|
}
|
|
2614
|
-
async function resolveLatestBuild(platform, deps) {
|
|
2680
|
+
async function resolveLatestBuild(platform, apkOnly, deps) {
|
|
2615
2681
|
const { builds: builds2 } = await deps.listCloudBuilds({
|
|
2616
2682
|
platform,
|
|
2617
2683
|
status: "succeeded",
|
|
@@ -2619,8 +2685,13 @@ async function resolveLatestBuild(platform, deps) {
|
|
|
2619
2685
|
limit: 200
|
|
2620
2686
|
});
|
|
2621
2687
|
const match = builds2.find(
|
|
2622
|
-
(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"))
|
|
2623
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
|
+
}
|
|
2624
2695
|
if (!match) {
|
|
2625
2696
|
throw new Error(
|
|
2626
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\`).`
|
|
@@ -3203,7 +3274,24 @@ function defaultDeps6(client) {
|
|
|
3203
3274
|
}
|
|
3204
3275
|
await client.deletePlayKey(match.id);
|
|
3205
3276
|
},
|
|
3206
|
-
|
|
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 }),
|
|
3207
3295
|
isTTY: Boolean(process.stdin.isTTY),
|
|
3208
3296
|
readFile: (path) => readFileAsync2(path),
|
|
3209
3297
|
fileExists: (path) => access(path).then(
|
|
@@ -3636,6 +3724,88 @@ async function playKeyRm(name, opts, deps) {
|
|
|
3636
3724
|
process.exitCode = errorExitCode(err);
|
|
3637
3725
|
}
|
|
3638
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
|
+
}
|
|
3639
3809
|
async function credentialsList(opts, deps) {
|
|
3640
3810
|
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
3641
3811
|
try {
|
|
@@ -4433,7 +4603,7 @@ program.command("build").description("Run your configured build command and uplo
|
|
|
4433
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"
|
|
4434
4604
|
).option("--json", "Print the result as one JSON line (NDJSON events with --cloud)", false).action((opts) => buildCommand(opts));
|
|
4435
4605
|
program.command("submit").description(
|
|
4436
|
-
"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"
|
|
4437
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(
|
|
4438
4608
|
"--latest",
|
|
4439
4609
|
"Submit the newest succeeded production build for --platform (pass exactly one of --build/--latest)",
|
|
@@ -4453,9 +4623,21 @@ program.command("submit").description(
|
|
|
4453
4623
|
).option(
|
|
4454
4624
|
"--rollout <fraction>",
|
|
4455
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"
|
|
4456
4638
|
).option(
|
|
4457
4639
|
"--check",
|
|
4458
|
-
"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",
|
|
4459
4641
|
false
|
|
4460
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));
|
|
4461
4643
|
var builds = program.command("builds").description("Manage cloud builds (see `vibeview build --cloud`)");
|
|
@@ -4547,6 +4729,14 @@ playKey.command("add").description(
|
|
|
4547
4729
|
).option("--name <name>", "Key name (default: server-chosen)").option("--json", "Print the raw response as one JSON line", false).action((opts) => playKeyAdd(opts));
|
|
4548
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));
|
|
4549
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));
|
|
4550
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));
|
|
4551
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));
|
|
4552
4742
|
var webhooks = program.command("webhooks").description("Manage build-completion webhook endpoints");
|
|
@@ -4575,7 +4765,7 @@ program.command("dev-reload").description(
|
|
|
4575
4765
|
program.command("mcp").description(
|
|
4576
4766
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop/dev_reload as tools"
|
|
4577
4767
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
4578
|
-
const { runMcpServer } = await import("./server-
|
|
4768
|
+
const { runMcpServer } = await import("./server-BGOXC6GL.js");
|
|
4579
4769
|
await runMcpServer({ ...opts, version: pkg.version });
|
|
4580
4770
|
});
|
|
4581
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. |
|