vibeview 0.19.0 → 0.20.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 +24 -0
- package/dist/{chunk-T6FS4ELD.js → chunk-GUCKFB4K.js} +17 -2
- package/dist/{chunk-FG2J2BGS.js → chunk-MCCXPIGE.js} +20 -1
- package/dist/cli.js +326 -147
- package/dist/{server-GMC6F545.js → server-NR3ZPQF5.js} +3 -3
- package/dist/{upload-app-4PE5BEP7.js → upload-app-HUMMA6UY.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,12 +74,33 @@ vibeview webhooks add --url https://ci.example.com/hooks/vibeview
|
|
|
74
74
|
Webhooks POST a signed payload to your endpoint whenever a build finishes.
|
|
75
75
|
Manage running builds with `vibeview builds list | logs | download | cancel`.
|
|
76
76
|
|
|
77
|
+
Set `"autoIncrement": true` in a platform's `build` block and every
|
|
78
|
+
production build gets a server-assigned build number, injected into the
|
|
79
|
+
build as `VIBEVIEW_BUILD_NUMBER` (inspect or seed the counters with
|
|
80
|
+
`vibeview versions list/set`).
|
|
81
|
+
|
|
77
82
|
Expo managed projects work out of the box — the default build command runs
|
|
78
83
|
`npx expo prebuild` in the cloud, so no `ios/` or `android/` directory is
|
|
79
84
|
needed.
|
|
80
85
|
|
|
86
|
+
When a production build succeeds, ship it straight to the stores — no
|
|
87
|
+
Xcode, no Play Console upload page:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
vibeview submit --platform tvos --latest # → TestFlight
|
|
91
|
+
vibeview submit --platform android --track internal # → a Play track
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
TestFlight uploads use your stored App Store Connect key; Play uploads need
|
|
95
|
+
a Google Play service account key stored once with
|
|
96
|
+
`vibeview credentials play-key add --file service-account.json`. Choose a
|
|
97
|
+
Play track, upload as a draft, or start a staged rollout — and store your
|
|
98
|
+
defaults in `vibeview.json` so `vibeview submit` alone does the right
|
|
99
|
+
thing.
|
|
100
|
+
|
|
81
101
|
[Cloud Builds docs →](https://vibeview.io/docs/cloud-builds) ·
|
|
82
102
|
[App Signing →](https://vibeview.io/docs/app-signing) ·
|
|
103
|
+
[Store Submission →](https://vibeview.io/docs/store-submission) ·
|
|
83
104
|
[Build Webhooks →](https://vibeview.io/docs/build-webhooks)
|
|
84
105
|
|
|
85
106
|
## Let your AI agent drive the device
|
|
@@ -140,8 +161,11 @@ Output formats: `human`, `json`, and `junit` for any CI system. The exit code re
|
|
|
140
161
|
| `vibeview dev` | Live development on a cloud device |
|
|
141
162
|
| `vibeview build` | Build your app — locally, or on the build fleet with `--cloud` (add `--production` for a signed release) |
|
|
142
163
|
| `vibeview builds list/logs/download/cancel/delete-artifact` | Manage cloud builds and their artifacts |
|
|
164
|
+
| `vibeview versions list/set` | Server-side build-number counters for `autoIncrement` production builds |
|
|
143
165
|
| `vibeview credentials add/generate/list/rm/download` | App-signing credentials for production builds (`generate` covers iOS, tvOS, and Android) |
|
|
144
166
|
| `vibeview credentials asc-key add/list/rm` | Connect an Apple team via App Store Connect API key |
|
|
167
|
+
| `vibeview credentials play-key add/list/rm` | Store a Google Play service-account key for Play submissions |
|
|
168
|
+
| `vibeview submit` | Upload a production build to TestFlight or a Google Play track |
|
|
145
169
|
| `vibeview env list/set/rm/pull` | Build variables and secrets injected into cloud builds |
|
|
146
170
|
| `vibeview webhooks add/list/rm/test` | Notify your systems when builds finish (signed payloads) |
|
|
147
171
|
| `vibeview dev-status` / `dev-stop` | Check on, or stop, a backgrounded dev session |
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ApiClient,
|
|
4
|
+
UsageError,
|
|
4
5
|
createSpinner,
|
|
5
6
|
loadConfig,
|
|
6
7
|
resolveAuth,
|
|
7
8
|
uploadAppBuild
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MCCXPIGE.js";
|
|
9
10
|
|
|
10
11
|
// src/lib/dev-state.ts
|
|
11
12
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -1188,6 +1189,11 @@ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
|
1188
1189
|
for (const p of platforms) {
|
|
1189
1190
|
try {
|
|
1190
1191
|
validateBuildEnv(cfg?.platforms?.[p]?.build?.env, p);
|
|
1192
|
+
if (cfg?.platforms?.[p]?.build?.autoIncrement === true && opts.production && createFromArtifact.has(p)) {
|
|
1193
|
+
throw new UsageError(
|
|
1194
|
+
`platforms.${p}.build.autoIncrement needs the build linked to an app \u2014 set appId in vibeview.json (a brand-new app's first build can't auto-increment)`
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1191
1197
|
} catch (err) {
|
|
1192
1198
|
fail(opts, err instanceof Error ? err.message : String(err));
|
|
1193
1199
|
}
|
|
@@ -1232,6 +1238,14 @@ ${offenders}`);
|
|
|
1232
1238
|
const production = productionBuilds.get(p);
|
|
1233
1239
|
const command = production ? production.command : build.command;
|
|
1234
1240
|
const artifactPath = production ? production.artifact : build.artifact;
|
|
1241
|
+
const autoIncrement = build.autoIncrement === true;
|
|
1242
|
+
if (autoIncrement && !production && !opts.json) {
|
|
1243
|
+
console.error(
|
|
1244
|
+
chalk2.yellow(
|
|
1245
|
+
`note: platforms.${p}.build.autoIncrement only applies to production builds (--production) \u2014 ignoring it for this simulator build`
|
|
1246
|
+
)
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1235
1249
|
return {
|
|
1236
1250
|
platform: p,
|
|
1237
1251
|
// OMITTED, never invented, for a platform whose app the artifact will
|
|
@@ -1243,7 +1257,8 @@ ${offenders}`);
|
|
|
1243
1257
|
command,
|
|
1244
1258
|
artifact_path: artifactPath,
|
|
1245
1259
|
distribution: production ? "production" : "simulator",
|
|
1246
|
-
...build.env ? { env: build.env } : {}
|
|
1260
|
+
...build.env ? { env: build.env } : {},
|
|
1261
|
+
...autoIncrement && production ? { auto_increment: true } : {}
|
|
1247
1262
|
};
|
|
1248
1263
|
});
|
|
1249
1264
|
const uploadSpinner = !opts.json ? createSpinner("uploading source...") : null;
|
|
@@ -484,7 +484,10 @@ var ApiClient = class {
|
|
|
484
484
|
platform: body.platform,
|
|
485
485
|
...body.track !== void 0 ? { track: body.track } : {},
|
|
486
486
|
...body.package !== void 0 ? { package: body.package } : {},
|
|
487
|
-
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {}
|
|
487
|
+
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {},
|
|
488
|
+
...body.releaseStatus !== void 0 ? { release_status: body.releaseStatus } : {},
|
|
489
|
+
...body.rollout !== void 0 ? { rollout: body.rollout } : {},
|
|
490
|
+
...body.changesNotSentForReview !== void 0 ? { changes_not_sent_for_review: body.changesNotSentForReview } : {}
|
|
488
491
|
});
|
|
489
492
|
}
|
|
490
493
|
/** GET /api/v1/submissions?build=<id> — that build's submissions, newest
|
|
@@ -503,6 +506,22 @@ var ApiClient = class {
|
|
|
503
506
|
const qs = offset !== void 0 ? `?offset=${offset}` : "";
|
|
504
507
|
return this.requestRaw("GET", `/submissions/${id}/log${qs}`);
|
|
505
508
|
}
|
|
509
|
+
/** GET /api/v1/apps/{appPublicId}/versions — the app's server-side
|
|
510
|
+
* build-number counters, one per platform that has ever allocated (or
|
|
511
|
+
* been `versions set`) a number. */
|
|
512
|
+
async listAppVersions(appId) {
|
|
513
|
+
return this.requestRaw("GET", `/apps/${encodeURIComponent(appId)}/versions`);
|
|
514
|
+
}
|
|
515
|
+
/** PUT /api/v1/apps/{appPublicId}/versions/{platform} {build_number} —
|
|
516
|
+
* seed or overwrite one counter (org-scoped, DEVELOPER role, audited
|
|
517
|
+
* server-side). Returns the updated counter. */
|
|
518
|
+
async setAppVersion(appId, platform, buildNumber) {
|
|
519
|
+
return this.requestRaw(
|
|
520
|
+
"PUT",
|
|
521
|
+
`/apps/${encodeURIComponent(appId)}/versions/${encodeURIComponent(platform)}`,
|
|
522
|
+
{ build_number: buildNumber }
|
|
523
|
+
);
|
|
524
|
+
}
|
|
506
525
|
/** GET /api/v1/signing-credentials/{id}/material. Requires ADMIN role;
|
|
507
526
|
* audited server-side. 404s (NotFoundError) for non-generated rows —
|
|
508
527
|
* uploaded material is never readable back. */
|
package/dist/cli.js
CHANGED
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
fetchFinalSummary,
|
|
9
9
|
formatDuration,
|
|
10
10
|
killOwnedMetro,
|
|
11
|
+
loadDevConfig,
|
|
11
12
|
newTransitionState,
|
|
12
13
|
printOutcomeLine,
|
|
13
14
|
printTransition,
|
|
14
15
|
readDevState,
|
|
15
16
|
readMetroState,
|
|
16
17
|
runAgentVerb
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-GUCKFB4K.js";
|
|
18
19
|
import {
|
|
19
20
|
ApiClient,
|
|
20
21
|
EXIT_INFRA_ERROR,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
resolveAuth,
|
|
28
29
|
saveConfig,
|
|
29
30
|
uploadApp
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-MCCXPIGE.js";
|
|
31
32
|
|
|
32
33
|
// src/cli.ts
|
|
33
34
|
import { createRequire } from "module";
|
|
@@ -1395,6 +1396,47 @@ function isSubmitPlatform(p) {
|
|
|
1395
1396
|
return SUBMIT_PLATFORMS.includes(p);
|
|
1396
1397
|
}
|
|
1397
1398
|
var PLAY_TRACKS = ["internal", "alpha", "beta", "production"];
|
|
1399
|
+
var RELEASE_STATUSES = ["completed", "draft", "inProgress"];
|
|
1400
|
+
function submitConfigFor(cwd, platform) {
|
|
1401
|
+
let cfg;
|
|
1402
|
+
try {
|
|
1403
|
+
cfg = loadDevConfig(cwd);
|
|
1404
|
+
} catch (err) {
|
|
1405
|
+
console.error(
|
|
1406
|
+
chalk12.yellow(
|
|
1407
|
+
`warning: vibeview.json could not be read \u2014 ignoring submit defaults (${err instanceof Error ? err.message : String(err)})`
|
|
1408
|
+
)
|
|
1409
|
+
);
|
|
1410
|
+
return {};
|
|
1411
|
+
}
|
|
1412
|
+
const raw = cfg?.platforms?.[platform]?.submit;
|
|
1413
|
+
if (raw === void 0) return {};
|
|
1414
|
+
const path = `vibeview.json: platforms.${platform}.submit`;
|
|
1415
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1416
|
+
throw new UsageError(`${path} must be an object`);
|
|
1417
|
+
}
|
|
1418
|
+
const s = raw;
|
|
1419
|
+
for (const key of ["track", "releaseStatus", "ascKey"]) {
|
|
1420
|
+
if (s[key] !== void 0 && (typeof s[key] !== "string" || !s[key])) {
|
|
1421
|
+
throw new UsageError(`${path}.${key} must be a non-empty string`);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
if (s.rollout !== void 0 && typeof s.rollout !== "number") {
|
|
1425
|
+
throw new UsageError(`${path}.rollout must be a number strictly between 0 and 1`);
|
|
1426
|
+
}
|
|
1427
|
+
if (s.changesNotSentForReview !== void 0 && typeof s.changesNotSentForReview !== "boolean") {
|
|
1428
|
+
throw new UsageError(`${path}.changesNotSentForReview must be a boolean`);
|
|
1429
|
+
}
|
|
1430
|
+
return s;
|
|
1431
|
+
}
|
|
1432
|
+
function validateRolloutFraction(f, source, raw = f) {
|
|
1433
|
+
if (!Number.isFinite(f) || !(f > 0 && f < 1)) {
|
|
1434
|
+
throw new UsageError(
|
|
1435
|
+
`${source} must be a fraction strictly between 0 and 1 (e.g. 0.25) \u2014 got "${String(raw)}"`
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
return f;
|
|
1439
|
+
}
|
|
1398
1440
|
var SUBMISSION_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed"]);
|
|
1399
1441
|
var MAX_CONSECUTIVE_POLL_FAILURES2 = 5;
|
|
1400
1442
|
function emit(json, evt) {
|
|
@@ -1425,8 +1467,43 @@ async function submitCommand(opts, deps) {
|
|
|
1425
1467
|
if (androidFamily && opts.ascKey !== void 0) {
|
|
1426
1468
|
throw new UsageError(`--asc-key only applies to ios/tvos (TestFlight) \u2014 not ${platform}`);
|
|
1427
1469
|
}
|
|
1428
|
-
if (opts.
|
|
1429
|
-
throw new UsageError(
|
|
1470
|
+
if (!androidFamily && opts.releaseStatus !== void 0) {
|
|
1471
|
+
throw new UsageError(
|
|
1472
|
+
`--release-status only applies to android/androidtv (Play) \u2014 not ${platform}`
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
if (!androidFamily && opts.rollout !== void 0) {
|
|
1476
|
+
throw new UsageError(`--rollout only applies to android/androidtv (Play) \u2014 not ${platform}`);
|
|
1477
|
+
}
|
|
1478
|
+
const cfgDefaults = submitConfigFor(process.cwd(), platform);
|
|
1479
|
+
const track = opts.track ?? (androidFamily ? cfgDefaults.track : void 0);
|
|
1480
|
+
const releaseStatus = opts.releaseStatus ?? (androidFamily ? cfgDefaults.releaseStatus : void 0);
|
|
1481
|
+
const statusUnchanged = opts.releaseStatus === void 0 || opts.releaseStatus === cfgDefaults.releaseStatus;
|
|
1482
|
+
const rollout = opts.rollout !== void 0 ? validateRolloutFraction(Number(opts.rollout), "--rollout", opts.rollout) : androidFamily && statusUnchanged && cfgDefaults.rollout !== void 0 ? validateRolloutFraction(
|
|
1483
|
+
cfgDefaults.rollout,
|
|
1484
|
+
`vibeview.json platforms.${platform}.submit.rollout`
|
|
1485
|
+
) : void 0;
|
|
1486
|
+
const changesNotSentForReview = androidFamily ? cfgDefaults.changesNotSentForReview : void 0;
|
|
1487
|
+
const ascKey2 = opts.ascKey ?? (!androidFamily ? cfgDefaults.ascKey : void 0);
|
|
1488
|
+
if (track !== void 0 && !PLAY_TRACKS.includes(track)) {
|
|
1489
|
+
const source = opts.track !== void 0 ? "--track" : `vibeview.json platforms.${platform}.submit.track`;
|
|
1490
|
+
throw new UsageError(`${source} must be one of ${PLAY_TRACKS.join(", ")} \u2014 got "${track}"`);
|
|
1491
|
+
}
|
|
1492
|
+
if (releaseStatus !== void 0 && !RELEASE_STATUSES.includes(releaseStatus)) {
|
|
1493
|
+
const source = opts.releaseStatus !== void 0 ? "--release-status" : `vibeview.json platforms.${platform}.submit.releaseStatus`;
|
|
1494
|
+
throw new UsageError(
|
|
1495
|
+
`${source} must be one of ${RELEASE_STATUSES.join(", ")} \u2014 got "${releaseStatus}"`
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
if (rollout !== void 0 && releaseStatus !== "inProgress") {
|
|
1499
|
+
throw new UsageError(
|
|
1500
|
+
`a rollout fraction requires release status "inProgress" (a staged rollout) \u2014 got "${releaseStatus ?? "completed"}"`
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
if (releaseStatus === "inProgress" && rollout === void 0) {
|
|
1504
|
+
throw new UsageError(
|
|
1505
|
+
`release status "inProgress" requires a rollout fraction \u2014 pass --rollout (e.g. --rollout 0.1)`
|
|
1506
|
+
);
|
|
1430
1507
|
}
|
|
1431
1508
|
const { token, baseUrl } = resolveAuth(opts);
|
|
1432
1509
|
const client = new ApiClient(baseUrl, token);
|
|
@@ -1436,9 +1513,12 @@ async function submitCommand(opts, deps) {
|
|
|
1436
1513
|
const sub = await d.createSubmission({
|
|
1437
1514
|
build: buildId,
|
|
1438
1515
|
platform,
|
|
1439
|
-
...
|
|
1516
|
+
...track !== void 0 ? { track } : {},
|
|
1440
1517
|
...opts.package !== void 0 ? { package: opts.package } : {},
|
|
1441
|
-
...
|
|
1518
|
+
...ascKey2 !== void 0 ? { ascKey: ascKey2 } : {},
|
|
1519
|
+
...releaseStatus !== void 0 ? { releaseStatus } : {},
|
|
1520
|
+
...rollout !== void 0 ? { rollout } : {},
|
|
1521
|
+
...changesNotSentForReview !== void 0 ? { changesNotSentForReview } : {}
|
|
1442
1522
|
});
|
|
1443
1523
|
emit(opts.json, {
|
|
1444
1524
|
event: "submission_created",
|
|
@@ -1455,6 +1535,11 @@ async function submitCommand(opts, deps) {
|
|
|
1455
1535
|
console.error(` ${chalk12.cyan(sub.id)} ${chalk12.dim(`[${sub.platform}]`)}`);
|
|
1456
1536
|
}
|
|
1457
1537
|
const final = await followSubmission(sub, opts, d);
|
|
1538
|
+
if (!opts.json && final.status === "succeeded" && (final.release_status ?? releaseStatus) === "draft") {
|
|
1539
|
+
console.error(
|
|
1540
|
+
chalk12.dim("release status: draft \u2014 activate it from the Play Console when ready")
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1458
1543
|
process.exitCode = final.status === "succeeded" ? 0 : 1;
|
|
1459
1544
|
} catch (err) {
|
|
1460
1545
|
printError2(opts, err);
|
|
@@ -1562,9 +1647,90 @@ function printError2(opts, err) {
|
|
|
1562
1647
|
console.error(chalk12.red(`error: ${message}`));
|
|
1563
1648
|
}
|
|
1564
1649
|
|
|
1565
|
-
// src/commands/
|
|
1650
|
+
// src/commands/versions.ts
|
|
1566
1651
|
import chalk13 from "chalk";
|
|
1567
|
-
function defaultDeps3(
|
|
1652
|
+
function defaultDeps3(client) {
|
|
1653
|
+
return {
|
|
1654
|
+
listAppVersions: client.listAppVersions.bind(client),
|
|
1655
|
+
setAppVersion: client.setAppVersion.bind(client)
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
var VERSION_PLATFORMS = ["ios", "android", "tvos", "androidtv"];
|
|
1659
|
+
async function versionsList(opts, deps) {
|
|
1660
|
+
if (!opts.app) {
|
|
1661
|
+
throw new UsageError("--app <id> is required (see `vibeview list-apps` for ids)");
|
|
1662
|
+
}
|
|
1663
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
1664
|
+
const client = new ApiClient(baseUrl, token);
|
|
1665
|
+
const d = deps ?? defaultDeps3(client);
|
|
1666
|
+
try {
|
|
1667
|
+
const response = await d.listAppVersions(opts.app);
|
|
1668
|
+
if (opts.json) {
|
|
1669
|
+
console.log(JSON.stringify(response, null, 2));
|
|
1670
|
+
} else {
|
|
1671
|
+
process.stdout.write(formatVersionsHuman(response.versions));
|
|
1672
|
+
}
|
|
1673
|
+
process.exitCode = 0;
|
|
1674
|
+
} catch (err) {
|
|
1675
|
+
printError3(opts, err);
|
|
1676
|
+
process.exitCode = 1;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
async function versionsSet(buildNumber, opts, deps) {
|
|
1680
|
+
if (!opts.app) {
|
|
1681
|
+
throw new UsageError("--app <id> is required (see `vibeview list-apps` for ids)");
|
|
1682
|
+
}
|
|
1683
|
+
if (!opts.platform || !VERSION_PLATFORMS.includes(opts.platform)) {
|
|
1684
|
+
throw new UsageError(
|
|
1685
|
+
`--platform is required and must be one of ${VERSION_PLATFORMS.join(", ")}`
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
const parsed = /^\d+$/.test(buildNumber) ? Number(buildNumber) : NaN;
|
|
1689
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
1690
|
+
throw new UsageError(`build number must be a non-negative integer \u2014 got "${buildNumber}"`);
|
|
1691
|
+
}
|
|
1692
|
+
const { token, baseUrl } = resolveAuth(opts);
|
|
1693
|
+
const client = new ApiClient(baseUrl, token);
|
|
1694
|
+
const d = deps ?? defaultDeps3(client);
|
|
1695
|
+
try {
|
|
1696
|
+
const version = await d.setAppVersion(opts.app, opts.platform, parsed);
|
|
1697
|
+
if (opts.json) {
|
|
1698
|
+
console.log(JSON.stringify(version));
|
|
1699
|
+
} else {
|
|
1700
|
+
console.log(
|
|
1701
|
+
chalk13.green(`\u2713 ${version.platform} build number set to ${version.build_number}`) + chalk13.dim(
|
|
1702
|
+
" \u2014 the next autoIncrement production build gets " + (version.build_number + 1)
|
|
1703
|
+
)
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
process.exitCode = 0;
|
|
1707
|
+
} catch (err) {
|
|
1708
|
+
printError3(opts, err);
|
|
1709
|
+
process.exitCode = 1;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
var HEADERS2 = ["PLATFORM", "BUILD NUMBER", "UPDATED"];
|
|
1713
|
+
function formatVersionsHuman(versions2) {
|
|
1714
|
+
if (versions2.length === 0) {
|
|
1715
|
+
return "No version counters yet \u2014 they appear after the first autoIncrement production build, or seed one with `vibeview versions set`.\n";
|
|
1716
|
+
}
|
|
1717
|
+
const rows = versions2.map((v) => [v.platform, String(v.build_number), v.updated_at ?? ""]);
|
|
1718
|
+
const widths = HEADERS2.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1719
|
+
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1720
|
+
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
1721
|
+
const lines = [chalk13.bold(join3(padCells(HEADERS2)))];
|
|
1722
|
+
for (const row of rows) lines.push(join3(padCells(row)));
|
|
1723
|
+
return lines.join("\n") + "\n";
|
|
1724
|
+
}
|
|
1725
|
+
function printError3(opts, err) {
|
|
1726
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1727
|
+
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1728
|
+
console.error(chalk13.red(`error: ${message}`));
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// src/commands/whoami.ts
|
|
1732
|
+
import chalk14 from "chalk";
|
|
1733
|
+
function defaultDeps4(opts) {
|
|
1568
1734
|
const { token, baseUrl } = resolveAuth(opts);
|
|
1569
1735
|
const client = new ApiClient(baseUrl, token);
|
|
1570
1736
|
return { fetchMe: () => client.whoAmI() };
|
|
@@ -1576,7 +1742,7 @@ function effectiveRole(role, tokenRole) {
|
|
|
1576
1742
|
return (RANK[tokenRole] ?? 3) < (RANK[role] ?? 3) ? tokenRole : role;
|
|
1577
1743
|
}
|
|
1578
1744
|
async function whoAmI(opts, deps) {
|
|
1579
|
-
const d = deps ??
|
|
1745
|
+
const d = deps ?? defaultDeps4(opts);
|
|
1580
1746
|
try {
|
|
1581
1747
|
const me = await d.fetchMe();
|
|
1582
1748
|
const effective = effectiveRole(me.role, me.token_role);
|
|
@@ -1585,27 +1751,27 @@ async function whoAmI(opts, deps) {
|
|
|
1585
1751
|
process.exitCode = 0;
|
|
1586
1752
|
return;
|
|
1587
1753
|
}
|
|
1588
|
-
console.log(`${
|
|
1754
|
+
console.log(`${chalk14.bold("Logged in as")} ${me.email}`);
|
|
1589
1755
|
if (me.memberships.length === 1) {
|
|
1590
|
-
console.log(`${
|
|
1756
|
+
console.log(`${chalk14.bold("Organization")} ${me.memberships[0].organization_name}`);
|
|
1591
1757
|
} else if (me.memberships.length > 1) {
|
|
1592
|
-
console.log(
|
|
1758
|
+
console.log(chalk14.bold("Organizations"));
|
|
1593
1759
|
for (const m of me.memberships) {
|
|
1594
1760
|
console.log(` ${m.organization_name} (${m.role})`);
|
|
1595
1761
|
}
|
|
1596
1762
|
}
|
|
1597
|
-
console.log(`${
|
|
1598
|
-
console.log(`${
|
|
1599
|
-
console.log(`${
|
|
1763
|
+
console.log(`${chalk14.bold("Member role")} ${me.role ?? "\u2014"}`);
|
|
1764
|
+
console.log(`${chalk14.bold("Token role")} ${me.token_role ?? "\u2014 (session auth)"}`);
|
|
1765
|
+
console.log(`${chalk14.bold("Effective role")} ${effective ?? "\u2014"}`);
|
|
1600
1766
|
if (me.is_superadmin) {
|
|
1601
|
-
console.log(
|
|
1767
|
+
console.log(chalk14.dim("Superadmin (platform-wide; does not raise the org role above)"));
|
|
1602
1768
|
}
|
|
1603
1769
|
if (me.role === "admin" && me.token_role && me.token_role !== "admin") {
|
|
1604
1770
|
console.log(
|
|
1605
|
-
|
|
1771
|
+
chalk14.yellow(
|
|
1606
1772
|
`
|
|
1607
|
-
Your account is admin, but this CLI token is ${me.token_role}. The effective role is the lower of the two, so admin-only commands (such as ${
|
|
1608
|
-
Create an admin API token in the dashboard and use ${
|
|
1773
|
+
Your account is admin, but this CLI token is ${me.token_role}. The effective role is the lower of the two, so admin-only commands (such as ${chalk14.bold("vibeview env set")}) will fail with a permissions error.
|
|
1774
|
+
Create an admin API token in the dashboard and use ${chalk14.bold("--token")} or ${chalk14.bold("VIBEVIEW_API_TOKEN")}.`
|
|
1609
1775
|
)
|
|
1610
1776
|
);
|
|
1611
1777
|
}
|
|
@@ -1615,7 +1781,7 @@ Create an admin API token in the dashboard and use ${chalk13.bold("--token")} or
|
|
|
1615
1781
|
if (opts.json) {
|
|
1616
1782
|
console.log(JSON.stringify({ event: "error", message }));
|
|
1617
1783
|
}
|
|
1618
|
-
console.error(
|
|
1784
|
+
console.error(chalk14.red(`error: ${message}`));
|
|
1619
1785
|
process.exitCode = 1;
|
|
1620
1786
|
}
|
|
1621
1787
|
}
|
|
@@ -1623,7 +1789,7 @@ Create an admin API token in the dashboard and use ${chalk13.bold("--token")} or
|
|
|
1623
1789
|
// src/commands/env.ts
|
|
1624
1790
|
import { chmod, open as open2, readFile as readFileAsync } from "fs/promises";
|
|
1625
1791
|
import { basename } from "path";
|
|
1626
|
-
import
|
|
1792
|
+
import chalk15 from "chalk";
|
|
1627
1793
|
import { password } from "@inquirer/prompts";
|
|
1628
1794
|
|
|
1629
1795
|
// src/lib/gitignore.ts
|
|
@@ -1650,7 +1816,7 @@ async function writeSecureFile(path, content) {
|
|
|
1650
1816
|
}
|
|
1651
1817
|
await chmod(path, 384);
|
|
1652
1818
|
}
|
|
1653
|
-
function
|
|
1819
|
+
function defaultDeps5(client) {
|
|
1654
1820
|
return {
|
|
1655
1821
|
listVars: (app) => client.listBuildEnvVars(app),
|
|
1656
1822
|
setVar: (body) => client.setBuildEnvVar(body),
|
|
@@ -1679,11 +1845,11 @@ function defaultDeps4(client) {
|
|
|
1679
1845
|
isIgnored: (path) => isGitIgnored(path),
|
|
1680
1846
|
writeFile: writeSecureFile,
|
|
1681
1847
|
readFile: (path) => readFileAsync(path),
|
|
1682
|
-
warn: (message) => console.error(
|
|
1848
|
+
warn: (message) => console.error(chalk15.yellow(`warning: ${message}`))
|
|
1683
1849
|
};
|
|
1684
1850
|
}
|
|
1685
1851
|
async function envList(opts, deps) {
|
|
1686
|
-
const d = deps ??
|
|
1852
|
+
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
1687
1853
|
try {
|
|
1688
1854
|
const { variables } = await d.listVars(opts.app);
|
|
1689
1855
|
if (opts.json) {
|
|
@@ -1693,7 +1859,7 @@ async function envList(opts, deps) {
|
|
|
1693
1859
|
}
|
|
1694
1860
|
process.exitCode = 0;
|
|
1695
1861
|
} catch (err) {
|
|
1696
|
-
|
|
1862
|
+
printError4(opts, err);
|
|
1697
1863
|
process.exitCode = 1;
|
|
1698
1864
|
}
|
|
1699
1865
|
}
|
|
@@ -1701,22 +1867,22 @@ function formatEnvVarsHuman(variables) {
|
|
|
1701
1867
|
if (variables.length === 0) {
|
|
1702
1868
|
return "No build variables found.\n";
|
|
1703
1869
|
}
|
|
1704
|
-
const
|
|
1870
|
+
const HEADERS4 = ["NAME", "VISIBILITY", "SCOPE", "HINT"];
|
|
1705
1871
|
const rows = variables.map((v) => [
|
|
1706
1872
|
v.name,
|
|
1707
1873
|
v.is_file ? `${v.visibility} (file)` : v.visibility,
|
|
1708
1874
|
v.app_public_id ?? "org-wide",
|
|
1709
1875
|
v.value_hint ?? ""
|
|
1710
1876
|
]);
|
|
1711
|
-
const widths =
|
|
1877
|
+
const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1712
1878
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
1713
1879
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
1714
|
-
const lines = [
|
|
1880
|
+
const lines = [chalk15.bold(join3(padCells(HEADERS4)))];
|
|
1715
1881
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
1716
1882
|
return lines.join("\n") + "\n";
|
|
1717
1883
|
}
|
|
1718
1884
|
async function envSet(name, value, opts, deps) {
|
|
1719
|
-
const d = deps ??
|
|
1885
|
+
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
1720
1886
|
let resolvedValue = value;
|
|
1721
1887
|
if (resolvedValue === void 0 && !opts.file) {
|
|
1722
1888
|
if (opts.json || !d.isTTY) {
|
|
@@ -1742,7 +1908,7 @@ async function envSet(name, value, opts, deps) {
|
|
|
1742
1908
|
printSetResult(opts, result);
|
|
1743
1909
|
process.exitCode = 0;
|
|
1744
1910
|
} catch (err) {
|
|
1745
|
-
|
|
1911
|
+
printError4(opts, err);
|
|
1746
1912
|
process.exitCode = 1;
|
|
1747
1913
|
}
|
|
1748
1914
|
}
|
|
@@ -1761,26 +1927,26 @@ function printSetResult(opts, result) {
|
|
|
1761
1927
|
if (opts.json) {
|
|
1762
1928
|
console.log(JSON.stringify(result));
|
|
1763
1929
|
} else {
|
|
1764
|
-
console.log(
|
|
1930
|
+
console.log(chalk15.green(`\u2713 set ${result.name} (${result.visibility})`));
|
|
1765
1931
|
}
|
|
1766
1932
|
}
|
|
1767
1933
|
async function envRm(name, opts, deps) {
|
|
1768
|
-
const d = deps ??
|
|
1934
|
+
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
1769
1935
|
try {
|
|
1770
1936
|
await d.rmVar({ name, app: opts.app });
|
|
1771
1937
|
if (opts.json) {
|
|
1772
1938
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
1773
1939
|
} else {
|
|
1774
|
-
console.log(
|
|
1940
|
+
console.log(chalk15.green(`\u2713 removed ${name}`));
|
|
1775
1941
|
}
|
|
1776
1942
|
process.exitCode = 0;
|
|
1777
1943
|
} catch (err) {
|
|
1778
|
-
|
|
1944
|
+
printError4(opts, err);
|
|
1779
1945
|
process.exitCode = 1;
|
|
1780
1946
|
}
|
|
1781
1947
|
}
|
|
1782
1948
|
async function envPull(opts, deps) {
|
|
1783
|
-
const d = deps ??
|
|
1949
|
+
const d = deps ?? defaultDeps5(clientFor(opts));
|
|
1784
1950
|
const outPath = opts.out ?? ".env";
|
|
1785
1951
|
try {
|
|
1786
1952
|
const { variables } = await d.pullVars(opts.app);
|
|
@@ -1803,11 +1969,11 @@ async function envPull(opts, deps) {
|
|
|
1803
1969
|
if (opts.json) {
|
|
1804
1970
|
console.log(JSON.stringify({ event: "pulled", path: outPath, count: variables.length }));
|
|
1805
1971
|
} else {
|
|
1806
|
-
console.log(
|
|
1972
|
+
console.log(chalk15.green(`\u2713 wrote ${variables.length} variable(s) to ${outPath}`));
|
|
1807
1973
|
}
|
|
1808
1974
|
process.exitCode = 0;
|
|
1809
1975
|
} catch (err) {
|
|
1810
|
-
|
|
1976
|
+
printError4(opts, err);
|
|
1811
1977
|
process.exitCode = 1;
|
|
1812
1978
|
}
|
|
1813
1979
|
}
|
|
@@ -1839,10 +2005,10 @@ function escapeEnvValue(value) {
|
|
|
1839
2005
|
}
|
|
1840
2006
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1841
2007
|
}
|
|
1842
|
-
function
|
|
2008
|
+
function printError4(opts, err) {
|
|
1843
2009
|
const message = err instanceof Error ? err.message : String(err);
|
|
1844
2010
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
1845
|
-
console.error(
|
|
2011
|
+
console.error(chalk15.red(`error: ${message}`));
|
|
1846
2012
|
}
|
|
1847
2013
|
|
|
1848
2014
|
// src/commands/credentials.ts
|
|
@@ -1854,13 +2020,13 @@ import {
|
|
|
1854
2020
|
} from "fs/promises";
|
|
1855
2021
|
import { mkdirSync } from "fs";
|
|
1856
2022
|
import { basename as basename2, extname, join } from "path";
|
|
1857
|
-
import
|
|
2023
|
+
import chalk16 from "chalk";
|
|
1858
2024
|
import { input, password as password2 } from "@inquirer/prompts";
|
|
1859
2025
|
function clientFor2(opts) {
|
|
1860
2026
|
const { token, baseUrl } = resolveAuth(opts);
|
|
1861
2027
|
return new ApiClient(baseUrl, token);
|
|
1862
2028
|
}
|
|
1863
|
-
function
|
|
2029
|
+
function defaultDeps6(client) {
|
|
1864
2030
|
return {
|
|
1865
2031
|
addCredential: (body) => client.addSigningCredential({
|
|
1866
2032
|
platform: body.platform,
|
|
@@ -1927,7 +2093,7 @@ function defaultDeps5(client) {
|
|
|
1927
2093
|
mkdir: (dir) => mkdirSync(dir, { recursive: true }),
|
|
1928
2094
|
writeFile: (path, data, mode) => writeFileAsync(path, data, mode !== void 0 ? { mode } : void 0),
|
|
1929
2095
|
chmod: (path, mode) => chmodAsync(path, mode),
|
|
1930
|
-
warn: (message) => console.error(
|
|
2096
|
+
warn: (message) => console.error(chalk16.yellow(`warning: ${message}`))
|
|
1931
2097
|
};
|
|
1932
2098
|
}
|
|
1933
2099
|
var ANDROID_KIND = "android_keystore";
|
|
@@ -1937,7 +2103,7 @@ function isCredentialPlatform(p) {
|
|
|
1937
2103
|
return CREDENTIAL_PLATFORMS.includes(p);
|
|
1938
2104
|
}
|
|
1939
2105
|
async function credentialsAdd(opts, deps) {
|
|
1940
|
-
const d = deps ??
|
|
2106
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
1941
2107
|
if (!opts.platform || !isCredentialPlatform(opts.platform)) {
|
|
1942
2108
|
throw new UsageError('--platform is required and must be "ios", "android", or "tvos"');
|
|
1943
2109
|
}
|
|
@@ -1996,16 +2162,16 @@ async function credentialsAdd(opts, deps) {
|
|
|
1996
2162
|
if (opts.json) {
|
|
1997
2163
|
console.log(JSON.stringify(result));
|
|
1998
2164
|
} else {
|
|
1999
|
-
console.log(
|
|
2165
|
+
console.log(chalk16.green(`\u2713 added ${result.name} (${result.platform})`));
|
|
2000
2166
|
}
|
|
2001
2167
|
process.exitCode = 0;
|
|
2002
2168
|
} catch (err) {
|
|
2003
|
-
|
|
2169
|
+
printError5(opts, err);
|
|
2004
2170
|
process.exitCode = 1;
|
|
2005
2171
|
}
|
|
2006
2172
|
}
|
|
2007
2173
|
async function credentialsGenerate(opts, deps) {
|
|
2008
|
-
const d = deps ??
|
|
2174
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2009
2175
|
if (!opts.platform) {
|
|
2010
2176
|
throw new UsageError('--platform is required and must be "android", "ios", or "tvos"');
|
|
2011
2177
|
}
|
|
@@ -2054,27 +2220,27 @@ async function credentialsGenerate(opts, deps) {
|
|
|
2054
2220
|
}
|
|
2055
2221
|
if (opts.json) {
|
|
2056
2222
|
} else {
|
|
2057
|
-
console.log(
|
|
2223
|
+
console.log(chalk16.green(`\u2713 generated ${result.name} (android)`));
|
|
2058
2224
|
console.log("");
|
|
2059
|
-
console.log(` Keystore saved to: ${
|
|
2060
|
-
console.log(` Key alias: ${
|
|
2061
|
-
console.log(` Store password: ${
|
|
2062
|
-
console.log(` Key password: ${
|
|
2225
|
+
console.log(` Keystore saved to: ${chalk16.bold(savedTo)}`);
|
|
2226
|
+
console.log(` Key alias: ${chalk16.bold(result.key_alias)}`);
|
|
2227
|
+
console.log(` Store password: ${chalk16.bold(result.store_password)}`);
|
|
2228
|
+
console.log(` Key password: ${chalk16.bold(result.key_password)}`);
|
|
2063
2229
|
console.log("");
|
|
2064
2230
|
console.log(
|
|
2065
|
-
|
|
2231
|
+
chalk16.yellow(
|
|
2066
2232
|
" These are shown ONCE \u2014 VibeView cannot recover the keystore file or its passwords. Back them up somewhere safe."
|
|
2067
2233
|
)
|
|
2068
2234
|
);
|
|
2069
2235
|
console.log(
|
|
2070
|
-
|
|
2236
|
+
chalk16.yellow(
|
|
2071
2237
|
" The credential is already uploaded and active for android and androidtv production builds."
|
|
2072
2238
|
)
|
|
2073
2239
|
);
|
|
2074
2240
|
}
|
|
2075
2241
|
process.exitCode = 0;
|
|
2076
2242
|
} catch (err) {
|
|
2077
|
-
|
|
2243
|
+
printError5(opts, err);
|
|
2078
2244
|
process.exitCode = 1;
|
|
2079
2245
|
}
|
|
2080
2246
|
}
|
|
@@ -2093,29 +2259,29 @@ async function generateAppleCredential(platform, opts, d) {
|
|
|
2093
2259
|
if (opts.json) {
|
|
2094
2260
|
console.log(JSON.stringify(result));
|
|
2095
2261
|
} else {
|
|
2096
|
-
console.log(
|
|
2262
|
+
console.log(chalk16.green(`\u2713 generated ${result.name} (${result.platform})`));
|
|
2097
2263
|
console.log("");
|
|
2098
|
-
console.log(` Scope: ${
|
|
2264
|
+
console.log(` Scope: ${chalk16.bold(result.app_public_id ?? "org-wide")}`);
|
|
2099
2265
|
const expires = formatExpires(result);
|
|
2100
2266
|
if (expires) {
|
|
2101
|
-
console.log(` Expires: ${
|
|
2267
|
+
console.log(` Expires: ${chalk16.bold(expires)}`);
|
|
2102
2268
|
}
|
|
2103
2269
|
console.log("");
|
|
2104
2270
|
console.log(
|
|
2105
2271
|
` The certificate and provisioning profile are stored with VibeView and already active for ${result.platform} production builds.`
|
|
2106
2272
|
);
|
|
2107
2273
|
console.log(
|
|
2108
|
-
` Run ${
|
|
2274
|
+
` Run ${chalk16.bold(`vibeview credentials download ${result.name}`)} if you need the files locally.`
|
|
2109
2275
|
);
|
|
2110
2276
|
}
|
|
2111
2277
|
process.exitCode = 0;
|
|
2112
2278
|
} catch (err) {
|
|
2113
|
-
|
|
2279
|
+
printError5(opts, err);
|
|
2114
2280
|
process.exitCode = 1;
|
|
2115
2281
|
}
|
|
2116
2282
|
}
|
|
2117
2283
|
async function credentialsDownload(name, opts, deps) {
|
|
2118
|
-
const d = deps ??
|
|
2284
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2119
2285
|
const outDir = opts.out ?? process.cwd();
|
|
2120
2286
|
try {
|
|
2121
2287
|
const { credentials: credentials2 } = await d.listCredentials(opts.app);
|
|
@@ -2171,26 +2337,26 @@ async function credentialsDownload(name, opts, deps) {
|
|
|
2171
2337
|
await d.chmod(path, 384);
|
|
2172
2338
|
}
|
|
2173
2339
|
if (!opts.json) {
|
|
2174
|
-
console.log(
|
|
2340
|
+
console.log(chalk16.green(`\u2713 downloaded ${material.name} (${material.platform})`));
|
|
2175
2341
|
console.log("");
|
|
2176
|
-
console.log(` Saved to: ${
|
|
2342
|
+
console.log(` Saved to: ${chalk16.bold(savedTo)}`);
|
|
2177
2343
|
if (profileSavedTo !== null) {
|
|
2178
|
-
console.log(` Profile: ${
|
|
2344
|
+
console.log(` Profile: ${chalk16.bold(profileSavedTo)}`);
|
|
2179
2345
|
}
|
|
2180
2346
|
for (const [key, value] of Object.entries(material.passwords)) {
|
|
2181
|
-
console.log(` ${key}: ${
|
|
2347
|
+
console.log(` ${key}: ${chalk16.bold(value)}`);
|
|
2182
2348
|
}
|
|
2183
2349
|
console.log("");
|
|
2184
|
-
console.log(
|
|
2350
|
+
console.log(chalk16.yellow(" This download is recorded in your organization's audit log."));
|
|
2185
2351
|
}
|
|
2186
2352
|
process.exitCode = 0;
|
|
2187
2353
|
} catch (err) {
|
|
2188
|
-
|
|
2354
|
+
printError5(opts, err);
|
|
2189
2355
|
process.exitCode = 1;
|
|
2190
2356
|
}
|
|
2191
2357
|
}
|
|
2192
2358
|
async function ascKeyAdd(opts, deps) {
|
|
2193
|
-
const d = deps ??
|
|
2359
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2194
2360
|
if (!opts.keyId) {
|
|
2195
2361
|
throw new UsageError("--key-id is required (the 10-character App Store Connect key id)");
|
|
2196
2362
|
}
|
|
@@ -2212,25 +2378,25 @@ async function ascKeyAdd(opts, deps) {
|
|
|
2212
2378
|
if (opts.json) {
|
|
2213
2379
|
console.log(JSON.stringify(result));
|
|
2214
2380
|
} else {
|
|
2215
|
-
console.log(
|
|
2381
|
+
console.log(chalk16.green(`\u2713 added App Store Connect key ${result.name}`));
|
|
2216
2382
|
console.log("");
|
|
2217
|
-
console.log(` Key ID: ${
|
|
2218
|
-
console.log(` Issuer ID: ${
|
|
2383
|
+
console.log(` Key ID: ${chalk16.bold(result.key_id)}`);
|
|
2384
|
+
console.log(` Issuer ID: ${chalk16.bold(result.issuer_id)}`);
|
|
2219
2385
|
console.log("");
|
|
2220
2386
|
console.log(
|
|
2221
|
-
|
|
2387
|
+
chalk16.yellow(
|
|
2222
2388
|
" The .p8 is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
2223
2389
|
)
|
|
2224
2390
|
);
|
|
2225
2391
|
}
|
|
2226
2392
|
process.exitCode = 0;
|
|
2227
2393
|
} catch (err) {
|
|
2228
|
-
|
|
2394
|
+
printError5(opts, err);
|
|
2229
2395
|
process.exitCode = 1;
|
|
2230
2396
|
}
|
|
2231
2397
|
}
|
|
2232
2398
|
async function ascKeyList(opts, deps) {
|
|
2233
|
-
const d = deps ??
|
|
2399
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2234
2400
|
try {
|
|
2235
2401
|
const { keys } = await d.listAscKeys();
|
|
2236
2402
|
if (opts.json) {
|
|
@@ -2240,7 +2406,7 @@ async function ascKeyList(opts, deps) {
|
|
|
2240
2406
|
}
|
|
2241
2407
|
process.exitCode = 0;
|
|
2242
2408
|
} catch (err) {
|
|
2243
|
-
|
|
2409
|
+
printError5(opts, err);
|
|
2244
2410
|
process.exitCode = 1;
|
|
2245
2411
|
}
|
|
2246
2412
|
}
|
|
@@ -2248,32 +2414,32 @@ function formatAscKeysHuman(keys) {
|
|
|
2248
2414
|
if (keys.length === 0) {
|
|
2249
2415
|
return "No App Store Connect keys found.\n";
|
|
2250
2416
|
}
|
|
2251
|
-
const
|
|
2417
|
+
const HEADERS4 = ["NAME", "KEY ID", "ISSUER ID", "CREATED"];
|
|
2252
2418
|
const rows = keys.map((k) => [k.name, k.key_id, k.issuer_id, k.created_at ?? ""]);
|
|
2253
|
-
const widths =
|
|
2419
|
+
const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2254
2420
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2255
2421
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2256
|
-
const lines = [
|
|
2422
|
+
const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
|
|
2257
2423
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2258
2424
|
return lines.join("\n") + "\n";
|
|
2259
2425
|
}
|
|
2260
2426
|
async function ascKeyRm(name, opts, deps) {
|
|
2261
|
-
const d = deps ??
|
|
2427
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2262
2428
|
try {
|
|
2263
2429
|
await d.deleteAscKey(name);
|
|
2264
2430
|
if (opts.json) {
|
|
2265
2431
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2266
2432
|
} else {
|
|
2267
|
-
console.log(
|
|
2433
|
+
console.log(chalk16.green(`\u2713 removed App Store Connect key ${name}`));
|
|
2268
2434
|
}
|
|
2269
2435
|
process.exitCode = 0;
|
|
2270
2436
|
} catch (err) {
|
|
2271
|
-
|
|
2437
|
+
printError5(opts, err);
|
|
2272
2438
|
process.exitCode = 1;
|
|
2273
2439
|
}
|
|
2274
2440
|
}
|
|
2275
2441
|
async function playKeyAdd(opts, deps) {
|
|
2276
|
-
const d = deps ??
|
|
2442
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2277
2443
|
if (!opts.file) {
|
|
2278
2444
|
throw new UsageError(
|
|
2279
2445
|
"--file is required (the service-account JSON from Google Play Console \u2192 API access)"
|
|
@@ -2289,24 +2455,24 @@ async function playKeyAdd(opts, deps) {
|
|
|
2289
2455
|
if (opts.json) {
|
|
2290
2456
|
console.log(JSON.stringify(result));
|
|
2291
2457
|
} else {
|
|
2292
|
-
console.log(
|
|
2458
|
+
console.log(chalk16.green(`\u2713 added Google Play key ${result.name}`));
|
|
2293
2459
|
console.log("");
|
|
2294
|
-
console.log(` Service account: ${
|
|
2460
|
+
console.log(` Service account: ${chalk16.bold(result.client_email)}`);
|
|
2295
2461
|
console.log("");
|
|
2296
2462
|
console.log(
|
|
2297
|
-
|
|
2463
|
+
chalk16.yellow(
|
|
2298
2464
|
" The JSON key is stored encrypted and will never be shown again \u2014 keep your own copy somewhere safe."
|
|
2299
2465
|
)
|
|
2300
2466
|
);
|
|
2301
2467
|
}
|
|
2302
2468
|
process.exitCode = 0;
|
|
2303
2469
|
} catch (err) {
|
|
2304
|
-
|
|
2470
|
+
printError5(opts, err);
|
|
2305
2471
|
process.exitCode = 1;
|
|
2306
2472
|
}
|
|
2307
2473
|
}
|
|
2308
2474
|
async function playKeyList(opts, deps) {
|
|
2309
|
-
const d = deps ??
|
|
2475
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2310
2476
|
try {
|
|
2311
2477
|
const { keys } = await d.listPlayKeys();
|
|
2312
2478
|
if (opts.json) {
|
|
@@ -2316,7 +2482,7 @@ async function playKeyList(opts, deps) {
|
|
|
2316
2482
|
}
|
|
2317
2483
|
process.exitCode = 0;
|
|
2318
2484
|
} catch (err) {
|
|
2319
|
-
|
|
2485
|
+
printError5(opts, err);
|
|
2320
2486
|
process.exitCode = 1;
|
|
2321
2487
|
}
|
|
2322
2488
|
}
|
|
@@ -2324,32 +2490,32 @@ function formatPlayKeysHuman(keys) {
|
|
|
2324
2490
|
if (keys.length === 0) {
|
|
2325
2491
|
return "No Google Play keys found.\n";
|
|
2326
2492
|
}
|
|
2327
|
-
const
|
|
2493
|
+
const HEADERS4 = ["NAME", "CLIENT EMAIL", "CREATED"];
|
|
2328
2494
|
const rows = keys.map((k) => [k.name, k.client_email, k.created_at ?? ""]);
|
|
2329
|
-
const widths =
|
|
2495
|
+
const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2330
2496
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2331
2497
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2332
|
-
const lines = [
|
|
2498
|
+
const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
|
|
2333
2499
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2334
2500
|
return lines.join("\n") + "\n";
|
|
2335
2501
|
}
|
|
2336
2502
|
async function playKeyRm(name, opts, deps) {
|
|
2337
|
-
const d = deps ??
|
|
2503
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2338
2504
|
try {
|
|
2339
2505
|
await d.deletePlayKey(name);
|
|
2340
2506
|
if (opts.json) {
|
|
2341
2507
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2342
2508
|
} else {
|
|
2343
|
-
console.log(
|
|
2509
|
+
console.log(chalk16.green(`\u2713 removed Google Play key ${name}`));
|
|
2344
2510
|
}
|
|
2345
2511
|
process.exitCode = 0;
|
|
2346
2512
|
} catch (err) {
|
|
2347
|
-
|
|
2513
|
+
printError5(opts, err);
|
|
2348
2514
|
process.exitCode = 1;
|
|
2349
2515
|
}
|
|
2350
2516
|
}
|
|
2351
2517
|
async function credentialsList(opts, deps) {
|
|
2352
|
-
const d = deps ??
|
|
2518
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2353
2519
|
try {
|
|
2354
2520
|
const { credentials: credentials2 } = await d.listCredentials(opts.app);
|
|
2355
2521
|
for (const c of credentials2) {
|
|
@@ -2365,7 +2531,7 @@ async function credentialsList(opts, deps) {
|
|
|
2365
2531
|
}
|
|
2366
2532
|
process.exitCode = 0;
|
|
2367
2533
|
} catch (err) {
|
|
2368
|
-
|
|
2534
|
+
printError5(opts, err);
|
|
2369
2535
|
process.exitCode = 1;
|
|
2370
2536
|
}
|
|
2371
2537
|
}
|
|
@@ -2398,7 +2564,7 @@ function formatCredentialsHuman(credentials2) {
|
|
|
2398
2564
|
if (credentials2.length === 0) {
|
|
2399
2565
|
return "No signing credentials found.\n";
|
|
2400
2566
|
}
|
|
2401
|
-
const
|
|
2567
|
+
const HEADERS4 = ["NAME", "PLATFORM", "SCOPE", "IDENTITY", "EXPIRES"];
|
|
2402
2568
|
const rows = credentials2.map((c) => [
|
|
2403
2569
|
c.name,
|
|
2404
2570
|
c.platform,
|
|
@@ -2406,25 +2572,25 @@ function formatCredentialsHuman(credentials2) {
|
|
|
2406
2572
|
formatIdentity(c),
|
|
2407
2573
|
formatExpires(c)
|
|
2408
2574
|
]);
|
|
2409
|
-
const widths =
|
|
2575
|
+
const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2410
2576
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2411
2577
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2412
|
-
const lines = [
|
|
2578
|
+
const lines = [chalk16.bold(join3(padCells(HEADERS4)))];
|
|
2413
2579
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2414
2580
|
return lines.join("\n") + "\n";
|
|
2415
2581
|
}
|
|
2416
2582
|
async function credentialsRm(name, opts, deps) {
|
|
2417
|
-
const d = deps ??
|
|
2583
|
+
const d = deps ?? defaultDeps6(clientFor2(opts));
|
|
2418
2584
|
try {
|
|
2419
2585
|
await d.deleteCredential({ name, app: opts.app });
|
|
2420
2586
|
if (opts.json) {
|
|
2421
2587
|
console.log(JSON.stringify({ event: "deleted", name }));
|
|
2422
2588
|
} else {
|
|
2423
|
-
console.log(
|
|
2589
|
+
console.log(chalk16.green(`\u2713 removed ${name}`));
|
|
2424
2590
|
}
|
|
2425
2591
|
process.exitCode = 0;
|
|
2426
2592
|
} catch (err) {
|
|
2427
|
-
|
|
2593
|
+
printError5(opts, err);
|
|
2428
2594
|
process.exitCode = 1;
|
|
2429
2595
|
}
|
|
2430
2596
|
}
|
|
@@ -2432,16 +2598,16 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
2432
2598
|
const message = err instanceof Error ? err.message : String(err);
|
|
2433
2599
|
if (opts.json) {
|
|
2434
2600
|
console.log(JSON.stringify({ event: "error", message }));
|
|
2435
|
-
console.error(
|
|
2601
|
+
console.error(chalk16.red(`error: ${message}`));
|
|
2436
2602
|
return;
|
|
2437
2603
|
}
|
|
2438
|
-
console.error(
|
|
2604
|
+
console.error(chalk16.red(`error: ${message}`));
|
|
2439
2605
|
console.error(
|
|
2440
|
-
|
|
2606
|
+
chalk16.red(
|
|
2441
2607
|
`The keystore could NOT be saved locally, but the credential "${result.name}" WAS created server-side and its key material can never be re-fetched.`
|
|
2442
2608
|
)
|
|
2443
2609
|
);
|
|
2444
|
-
console.error(
|
|
2610
|
+
console.error(chalk16.red("Recover it NOW from the values below \u2014 they will not be shown again:"));
|
|
2445
2611
|
console.error("");
|
|
2446
2612
|
console.error(` Key alias: ${result.key_alias}`);
|
|
2447
2613
|
console.error(` Store password: ${result.store_password}`);
|
|
@@ -2452,19 +2618,19 @@ function printKeystoreWriteFailure(opts, err, result) {
|
|
|
2452
2618
|
` Restore the file with: echo '${result.keystore_base64}' | base64 -d > ${result.name}.keystore`
|
|
2453
2619
|
);
|
|
2454
2620
|
}
|
|
2455
|
-
function
|
|
2621
|
+
function printError5(opts, err) {
|
|
2456
2622
|
const message = err instanceof Error ? err.message : String(err);
|
|
2457
2623
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2458
|
-
console.error(
|
|
2624
|
+
console.error(chalk16.red(`error: ${message}`));
|
|
2459
2625
|
}
|
|
2460
2626
|
|
|
2461
2627
|
// src/commands/webhooks.ts
|
|
2462
|
-
import
|
|
2628
|
+
import chalk17 from "chalk";
|
|
2463
2629
|
function clientFor3(opts) {
|
|
2464
2630
|
const { token, baseUrl } = resolveAuth(opts);
|
|
2465
2631
|
return new ApiClient(baseUrl, token);
|
|
2466
2632
|
}
|
|
2467
|
-
function
|
|
2633
|
+
function defaultDeps7(client) {
|
|
2468
2634
|
return {
|
|
2469
2635
|
addWebhook: (body) => client.addWebhook(body),
|
|
2470
2636
|
listWebhooks: () => client.listWebhooks(),
|
|
@@ -2481,7 +2647,7 @@ async function webhooksAdd(opts, deps) {
|
|
|
2481
2647
|
if (!opts.url.startsWith("https://")) {
|
|
2482
2648
|
throw new UsageError(`--url must start with https:// (got "${opts.url}")`);
|
|
2483
2649
|
}
|
|
2484
|
-
const d = deps ??
|
|
2650
|
+
const d = deps ?? defaultDeps7(clientFor3(opts));
|
|
2485
2651
|
try {
|
|
2486
2652
|
const result = await d.addWebhook({
|
|
2487
2653
|
url: opts.url,
|
|
@@ -2490,19 +2656,19 @@ async function webhooksAdd(opts, deps) {
|
|
|
2490
2656
|
if (opts.json) {
|
|
2491
2657
|
console.log(JSON.stringify(result));
|
|
2492
2658
|
} else {
|
|
2493
|
-
console.log(
|
|
2659
|
+
console.log(chalk17.green(`\u2713 added ${result.id} ${result.url}`));
|
|
2494
2660
|
console.log("");
|
|
2495
|
-
console.log(` Signing secret: ${
|
|
2661
|
+
console.log(` Signing secret: ${chalk17.bold(result.secret)}`);
|
|
2496
2662
|
if (opts.secret !== void 0) {
|
|
2497
|
-
console.log(
|
|
2663
|
+
console.log(chalk17.yellow(" (This is the secret you provided.)"));
|
|
2498
2664
|
}
|
|
2499
2665
|
console.log(
|
|
2500
|
-
|
|
2666
|
+
chalk17.yellow(" This secret is shown ONCE and cannot be retrieved again \u2014 store it now.")
|
|
2501
2667
|
);
|
|
2502
2668
|
}
|
|
2503
2669
|
process.exitCode = 0;
|
|
2504
2670
|
} catch (err) {
|
|
2505
|
-
|
|
2671
|
+
printError6(opts, err);
|
|
2506
2672
|
process.exitCode = 1;
|
|
2507
2673
|
}
|
|
2508
2674
|
}
|
|
@@ -2517,7 +2683,7 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
2517
2683
|
if (webhooks2.length === 0) {
|
|
2518
2684
|
return "No webhooks found.\n";
|
|
2519
2685
|
}
|
|
2520
|
-
const
|
|
2686
|
+
const HEADERS4 = ["ID", "URL", "ACTIVE", "LAST DELIVERY", "CREATED"];
|
|
2521
2687
|
const rows = webhooks2.map((w) => [
|
|
2522
2688
|
w.id,
|
|
2523
2689
|
w.url,
|
|
@@ -2525,15 +2691,15 @@ function formatWebhooksHuman(webhooks2) {
|
|
|
2525
2691
|
formatLastDelivery(w.last_delivery),
|
|
2526
2692
|
w.created_at
|
|
2527
2693
|
]);
|
|
2528
|
-
const widths =
|
|
2694
|
+
const widths = HEADERS4.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2529
2695
|
const padCells = (cells) => cells.map((c, i) => c.padEnd(widths[i]));
|
|
2530
2696
|
const join3 = (cells) => cells.join(" ").trimEnd();
|
|
2531
|
-
const lines = [
|
|
2697
|
+
const lines = [chalk17.bold(join3(padCells(HEADERS4)))];
|
|
2532
2698
|
for (const row of rows) lines.push(join3(padCells(row)));
|
|
2533
2699
|
return lines.join("\n") + "\n";
|
|
2534
2700
|
}
|
|
2535
2701
|
async function webhooksList(opts, deps) {
|
|
2536
|
-
const d = deps ??
|
|
2702
|
+
const d = deps ?? defaultDeps7(clientFor3(opts));
|
|
2537
2703
|
try {
|
|
2538
2704
|
const { webhooks: webhooks2 } = await d.listWebhooks();
|
|
2539
2705
|
if (opts.json) {
|
|
@@ -2543,22 +2709,22 @@ async function webhooksList(opts, deps) {
|
|
|
2543
2709
|
}
|
|
2544
2710
|
process.exitCode = 0;
|
|
2545
2711
|
} catch (err) {
|
|
2546
|
-
|
|
2712
|
+
printError6(opts, err);
|
|
2547
2713
|
process.exitCode = 1;
|
|
2548
2714
|
}
|
|
2549
2715
|
}
|
|
2550
2716
|
async function webhooksRm(id, opts, deps) {
|
|
2551
|
-
const d = deps ??
|
|
2717
|
+
const d = deps ?? defaultDeps7(clientFor3(opts));
|
|
2552
2718
|
try {
|
|
2553
2719
|
await d.deleteWebhook(id);
|
|
2554
2720
|
if (opts.json) {
|
|
2555
2721
|
console.log(JSON.stringify({ event: "deleted", id }));
|
|
2556
2722
|
} else {
|
|
2557
|
-
console.log(
|
|
2723
|
+
console.log(chalk17.green(`\u2713 removed ${id}`));
|
|
2558
2724
|
}
|
|
2559
2725
|
process.exitCode = 0;
|
|
2560
2726
|
} catch (err) {
|
|
2561
|
-
|
|
2727
|
+
printError6(opts, err);
|
|
2562
2728
|
process.exitCode = 1;
|
|
2563
2729
|
}
|
|
2564
2730
|
}
|
|
@@ -2575,7 +2741,7 @@ function parseUtcTimestamp(iso) {
|
|
|
2575
2741
|
var TEST_POLL_INTERVAL_MS = 3e3;
|
|
2576
2742
|
var TEST_POLL_BUDGET_MS = 6e4;
|
|
2577
2743
|
async function webhooksTest(id, opts, deps) {
|
|
2578
|
-
const d = deps ??
|
|
2744
|
+
const d = deps ?? defaultDeps7(clientFor3(opts));
|
|
2579
2745
|
try {
|
|
2580
2746
|
const startedAt = d.now();
|
|
2581
2747
|
await d.testWebhook(id);
|
|
@@ -2587,13 +2753,13 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2587
2753
|
if (opts.json) console.log(JSON.stringify(ld));
|
|
2588
2754
|
if (ld.status === "succeeded") {
|
|
2589
2755
|
if (!opts.json) {
|
|
2590
|
-
console.log(
|
|
2756
|
+
console.log(chalk17.green(`\u2713 Delivered (HTTP ${ld.response_code})`));
|
|
2591
2757
|
}
|
|
2592
2758
|
process.exitCode = 0;
|
|
2593
2759
|
} else {
|
|
2594
2760
|
if (!opts.json) {
|
|
2595
2761
|
const code = ld.response_code != null ? `HTTP ${ld.response_code}` : "no response";
|
|
2596
|
-
console.error(
|
|
2762
|
+
console.error(chalk17.red(`\u2717 Failed (${code}) at ${ld.at}`));
|
|
2597
2763
|
}
|
|
2598
2764
|
process.exitCode = 1;
|
|
2599
2765
|
}
|
|
@@ -2602,25 +2768,25 @@ async function webhooksTest(id, opts, deps) {
|
|
|
2602
2768
|
}
|
|
2603
2769
|
if (opts.json) console.log(JSON.stringify({ status: "pending" }));
|
|
2604
2770
|
console.error(
|
|
2605
|
-
|
|
2771
|
+
chalk17.yellow(
|
|
2606
2772
|
`Delivery still pending after ${TEST_POLL_BUDGET_MS / 1e3}s \u2014 check \`vibeview webhooks list\` later for the result.`
|
|
2607
2773
|
)
|
|
2608
2774
|
);
|
|
2609
2775
|
process.exitCode = 1;
|
|
2610
2776
|
} catch (err) {
|
|
2611
|
-
|
|
2777
|
+
printError6(opts, err);
|
|
2612
2778
|
process.exitCode = 1;
|
|
2613
2779
|
}
|
|
2614
2780
|
}
|
|
2615
|
-
function
|
|
2781
|
+
function printError6(opts, err) {
|
|
2616
2782
|
const message = err instanceof Error ? err.message : String(err);
|
|
2617
2783
|
if (opts.json) console.log(JSON.stringify({ event: "error", message }));
|
|
2618
|
-
console.error(
|
|
2784
|
+
console.error(chalk17.red(`error: ${message}`));
|
|
2619
2785
|
}
|
|
2620
2786
|
|
|
2621
2787
|
// src/lib/formatters/devices.ts
|
|
2622
|
-
import
|
|
2623
|
-
var
|
|
2788
|
+
import chalk18 from "chalk";
|
|
2789
|
+
var HEADERS3 = ["DEVICE_ID", "NAME", "TYPE", "CATEGORY", "OS", "STATUS"];
|
|
2624
2790
|
function formatDevicesHuman(devices) {
|
|
2625
2791
|
if (devices.length === 0) {
|
|
2626
2792
|
return "No devices registered.\n";
|
|
@@ -2633,11 +2799,11 @@ function formatDevicesHuman(devices) {
|
|
|
2633
2799
|
d.os_version,
|
|
2634
2800
|
d.status
|
|
2635
2801
|
]);
|
|
2636
|
-
const widths =
|
|
2802
|
+
const widths = HEADERS3.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
2637
2803
|
const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
2638
|
-
const lines = [
|
|
2804
|
+
const lines = [chalk18.bold(fmt(HEADERS3))];
|
|
2639
2805
|
for (const row of rows.sort(rowSort)) {
|
|
2640
|
-
const statusColored = row[5] === "available" ?
|
|
2806
|
+
const statusColored = row[5] === "available" ? chalk18.green(row[5]) : row[5] === "busy" ? chalk18.yellow(row[5]) : chalk18.dim(row[5]);
|
|
2641
2807
|
lines.push(fmt([row[0], row[1], row[2], row[3], row[4], statusColored]));
|
|
2642
2808
|
}
|
|
2643
2809
|
return lines.join("\n") + "\n";
|
|
@@ -2727,7 +2893,7 @@ import { dirname, join as join2 } from "path";
|
|
|
2727
2893
|
import { fileURLToPath } from "url";
|
|
2728
2894
|
import { spawnSync } from "child_process";
|
|
2729
2895
|
import { confirm } from "@inquirer/prompts";
|
|
2730
|
-
import
|
|
2896
|
+
import chalk19 from "chalk";
|
|
2731
2897
|
function resolveShippedSkillPath(fromDir) {
|
|
2732
2898
|
for (const rel of ["..", join2("..", "..")]) {
|
|
2733
2899
|
const candidate = join2(fromDir, rel, "skills", "vibeview-agent", "SKILL.md");
|
|
@@ -2755,23 +2921,23 @@ function registerMcp() {
|
|
|
2755
2921
|
encoding: "utf8"
|
|
2756
2922
|
});
|
|
2757
2923
|
if (res.status === 0) {
|
|
2758
|
-
console.log(
|
|
2924
|
+
console.log(chalk19.green("\u2713 MCP server registered with Claude Code (claude mcp add vibeview)"));
|
|
2759
2925
|
return;
|
|
2760
2926
|
}
|
|
2761
2927
|
const out = `${res.stderr ?? ""}${res.stdout ?? ""}`;
|
|
2762
2928
|
if (out.includes("already exists")) {
|
|
2763
|
-
console.log(
|
|
2929
|
+
console.log(chalk19.dim("MCP server already registered with Claude Code \u2014 nothing to do"));
|
|
2764
2930
|
return;
|
|
2765
2931
|
}
|
|
2766
|
-
console.error(
|
|
2767
|
-
console.error(
|
|
2932
|
+
console.error(chalk19.yellow(`\u26A0 could not register the MCP server: ${out.trim() || res.error}`));
|
|
2933
|
+
console.error(chalk19.yellow(" register it manually: claude mcp add vibeview -- vibeview mcp"));
|
|
2768
2934
|
}
|
|
2769
2935
|
async function agentSetup(opts) {
|
|
2770
2936
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
2771
2937
|
const shipped = resolveShippedSkillPath(moduleDir);
|
|
2772
2938
|
if (!shipped) {
|
|
2773
2939
|
console.error(
|
|
2774
|
-
|
|
2940
|
+
chalk19.red(
|
|
2775
2941
|
"error: could not find the packaged skill (skills/vibeview-agent/SKILL.md) \u2014 is this a complete install of the vibeview package?"
|
|
2776
2942
|
)
|
|
2777
2943
|
);
|
|
@@ -2780,9 +2946,9 @@ async function agentSetup(opts) {
|
|
|
2780
2946
|
}
|
|
2781
2947
|
const targetDir = skillInstallDir(opts.project ?? false, process.cwd(), homedir());
|
|
2782
2948
|
const result = installSkill(shipped, targetDir);
|
|
2783
|
-
console.log(
|
|
2949
|
+
console.log(chalk19.green(`\u2713 vibeview-agent skill ${result}: ${join2(targetDir, "SKILL.md")}`));
|
|
2784
2950
|
console.log(
|
|
2785
|
-
|
|
2951
|
+
chalk19.dim(
|
|
2786
2952
|
opts.project ? " Claude Code discovers it automatically for anyone working in this project." : " Claude Code discovers it automatically in every project on this machine."
|
|
2787
2953
|
)
|
|
2788
2954
|
);
|
|
@@ -2790,7 +2956,7 @@ async function agentSetup(opts) {
|
|
|
2790
2956
|
if (!claudeCliAvailable()) {
|
|
2791
2957
|
if (opts.mcp === true) {
|
|
2792
2958
|
console.error(
|
|
2793
|
-
|
|
2959
|
+
chalk19.yellow(
|
|
2794
2960
|
"\u26A0 claude CLI not found \u2014 install Claude Code, then run: claude mcp add vibeview -- vibeview mcp"
|
|
2795
2961
|
)
|
|
2796
2962
|
);
|
|
@@ -2804,7 +2970,7 @@ async function agentSetup(opts) {
|
|
|
2804
2970
|
}
|
|
2805
2971
|
console.log(
|
|
2806
2972
|
`
|
|
2807
|
-
Suggested line for your project's ${
|
|
2973
|
+
Suggested line for your project's ${chalk19.bold("CLAUDE.md")} (or ${chalk19.bold(
|
|
2808
2974
|
"AGENTS.md"
|
|
2809
2975
|
)} for other agents):
|
|
2810
2976
|
`
|
|
@@ -2812,7 +2978,7 @@ Suggested line for your project's ${chalk18.bold("CLAUDE.md")} (or ${chalk18.bol
|
|
|
2812
2978
|
console.log(` ${CLAUDE_MD_SUGGESTION}
|
|
2813
2979
|
`);
|
|
2814
2980
|
console.log(
|
|
2815
|
-
|
|
2981
|
+
chalk19.dim(
|
|
2816
2982
|
"Other agents (Cursor, Copilot, Codex, ...): point them at the skill file above, or\nadd the MCP server per client \u2014 see https://vibeview.io/docs/agent-control"
|
|
2817
2983
|
)
|
|
2818
2984
|
);
|
|
@@ -2991,6 +3157,12 @@ program.command("submit").description(
|
|
|
2991
3157
|
).option(
|
|
2992
3158
|
"--asc-key <name>",
|
|
2993
3159
|
"ios/tvos only: which stored App Store Connect key to use (default when exactly one \u2014 see `vibeview credentials asc-key add`)"
|
|
3160
|
+
).option(
|
|
3161
|
+
"--release-status <status>",
|
|
3162
|
+
"android/androidtv only: Play release status \u2014 completed (default), draft, or inProgress"
|
|
3163
|
+
).option(
|
|
3164
|
+
"--rollout <fraction>",
|
|
3165
|
+
"android/androidtv only: staged-rollout fraction, 0 < f < 1 (requires --release-status inProgress)"
|
|
2994
3166
|
).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));
|
|
2995
3167
|
var builds = program.command("builds").description("Manage cloud builds (see `vibeview build --cloud`)");
|
|
2996
3168
|
builds.command("list").description("List cloud builds in your organization").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the raw build summaries as a JSON array", false).option("--limit <n>", "Max builds to return (default 50, max 200)").action((opts) => buildsList(opts));
|
|
@@ -3000,6 +3172,13 @@ builds.command("cancel <id>").description("Cancel a queued or running cloud buil
|
|
|
3000
3172
|
builds.command("delete-artifact <id>").description(
|
|
3001
3173
|
"Delete a production build's stored artifact file (irreversible; the build record, logs, and cost stay intact \u2014 only the downloadable file is removed)"
|
|
3002
3174
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--json", "Print the result as one JSON line", false).action((id, opts) => buildsDeleteArtifact(id, opts));
|
|
3175
|
+
var versions = program.command("versions").description(
|
|
3176
|
+
"Manage an app's server-side build-number counters (used by vibeview.json `build.autoIncrement` production builds)"
|
|
3177
|
+
);
|
|
3178
|
+
versions.command("list").description("List an app's build-number counters, one per platform").option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").option("--app <id>", "App public id (see `vibeview list-apps`)").option("--json", "Print the raw response as JSON", false).action((opts) => versionsList(opts));
|
|
3179
|
+
versions.command("set <number>").description(
|
|
3180
|
+
"Seed or overwrite one platform's counter \u2014 the next autoIncrement production build gets <number> + 1 (use it to start above a build number already in the store)"
|
|
3181
|
+
).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));
|
|
3003
3182
|
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));
|
|
3004
3183
|
var env = program.command("env").description("Manage cloud build environment variables and secrets");
|
|
3005
3184
|
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("--json", "Print the raw variable list as a JSON array", false).action((opts) => envList(opts));
|
|
@@ -3076,7 +3255,7 @@ program.command("dev-stop").description("Stop a `vibeview dev --detach` session
|
|
|
3076
3255
|
program.command("mcp").description(
|
|
3077
3256
|
"Run an MCP stdio server exposing the agent verbs (tap, swipe, type, ...) plus dev_start/dev_stop as tools"
|
|
3078
3257
|
).option("--token <token>", "API token (or set VIBEVIEW_API_TOKEN)").action(async (opts) => {
|
|
3079
|
-
const { runMcpServer } = await import("./server-
|
|
3258
|
+
const { runMcpServer } = await import("./server-NR3ZPQF5.js");
|
|
3080
3259
|
await runMcpServer(opts);
|
|
3081
3260
|
});
|
|
3082
3261
|
program.command("agent-setup").description(
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
readDevState,
|
|
12
12
|
renderEnvelope,
|
|
13
13
|
startDevSession
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GUCKFB4K.js";
|
|
15
15
|
import {
|
|
16
16
|
ApiClient,
|
|
17
17
|
resolveAuth
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MCCXPIGE.js";
|
|
19
19
|
|
|
20
20
|
// src/mcp/server.ts
|
|
21
21
|
import { writeFileSync } from "fs";
|
|
@@ -239,7 +239,7 @@ async function handleUploadApp(args, deps) {
|
|
|
239
239
|
}
|
|
240
240
|
const name = typeof args.name === "string" && args.name ? args.name : void 0;
|
|
241
241
|
const upload = deps.uploadApp ?? (async (f, n) => {
|
|
242
|
-
const { uploadAppBuild } = await import("./upload-app-
|
|
242
|
+
const { uploadAppBuild } = await import("./upload-app-HUMMA6UY.js");
|
|
243
243
|
const { result: result2 } = await uploadAppBuild(f, { ...deps.auth, name: n });
|
|
244
244
|
return result2;
|
|
245
245
|
});
|