vibeview 0.18.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 +64 -28
- package/dist/{chunk-MGP3UR7P.js → chunk-GUCKFB4K.js} +56 -7
- package/dist/{chunk-BJEDH672.js → chunk-MCCXPIGE.js} +80 -3
- package/dist/cli.js +607 -131
- package/dist/{server-TJQKNXDR.js → server-NR3ZPQF5.js} +3 -3
- package/dist/{upload-app-Y7MXT2PI.js → upload-app-HUMMA6UY.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,15 +49,22 @@ cloud device. Add `--production` for a **signed release build** on iOS,
|
|
|
49
49
|
tvOS, Android, or Android TV, using signing credentials you store once:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
# Apple: your
|
|
52
|
+
# Apple: connect your team once with an App Store Connect API key,
|
|
53
|
+
# then VibeView creates the certificate + provisioning profile for you
|
|
54
|
+
vibeview credentials asc-key add --key-id <K> --issuer-id <I> --file AuthKey_<K>.p8
|
|
55
|
+
vibeview credentials generate --platform ios --name release --app <app-id>
|
|
56
|
+
|
|
57
|
+
# ...or upload an exported certificate + profile yourself
|
|
53
58
|
vibeview credentials add --platform ios --file cert.p12 --profile app.mobileprovision
|
|
54
59
|
|
|
55
60
|
# Android: upload a keystore — or have VibeView generate one (no Java needed)
|
|
56
61
|
vibeview credentials generate --platform android --name release
|
|
57
62
|
```
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
Uploaded credentials are stored encrypted and never readable back;
|
|
65
|
+
credentials VibeView generated can be re-downloaded by org admins with
|
|
66
|
+
`vibeview credentials download <name>` (every download is audited).
|
|
67
|
+
Build-time secrets work the same way:
|
|
61
68
|
|
|
62
69
|
```bash
|
|
63
70
|
vibeview env set NPM_TOKEN "..." # secret by default, injected into builds
|
|
@@ -67,8 +74,33 @@ vibeview webhooks add --url https://ci.example.com/hooks/vibeview
|
|
|
67
74
|
Webhooks POST a signed payload to your endpoint whenever a build finishes.
|
|
68
75
|
Manage running builds with `vibeview builds list | logs | download | cancel`.
|
|
69
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
|
+
|
|
82
|
+
Expo managed projects work out of the box — the default build command runs
|
|
83
|
+
`npx expo prebuild` in the cloud, so no `ios/` or `android/` directory is
|
|
84
|
+
needed.
|
|
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
|
+
|
|
70
101
|
[Cloud Builds docs →](https://vibeview.io/docs/cloud-builds) ·
|
|
71
102
|
[App Signing →](https://vibeview.io/docs/app-signing) ·
|
|
103
|
+
[Store Submission →](https://vibeview.io/docs/store-submission) ·
|
|
72
104
|
[Build Webhooks →](https://vibeview.io/docs/build-webhooks)
|
|
73
105
|
|
|
74
106
|
## Let your AI agent drive the device
|
|
@@ -123,31 +155,35 @@ Output formats: `human`, `json`, and `junit` for any CI system. The exit code re
|
|
|
123
155
|
|
|
124
156
|
## All commands
|
|
125
157
|
|
|
126
|
-
| Command
|
|
127
|
-
|
|
|
128
|
-
| `vibeview login` / `logout` / `whoami`
|
|
129
|
-
| `vibeview dev`
|
|
130
|
-
| `vibeview build`
|
|
131
|
-
| `vibeview builds list/logs/download/cancel/delete-artifact` | Manage cloud builds and their artifacts
|
|
132
|
-
| `vibeview
|
|
133
|
-
| `vibeview
|
|
134
|
-
| `vibeview
|
|
135
|
-
| `vibeview
|
|
136
|
-
| `vibeview
|
|
137
|
-
| `vibeview
|
|
138
|
-
| `vibeview
|
|
139
|
-
| `vibeview
|
|
140
|
-
| `vibeview
|
|
141
|
-
| `vibeview
|
|
142
|
-
| `vibeview
|
|
143
|
-
| `vibeview
|
|
144
|
-
| `vibeview
|
|
145
|
-
| `vibeview
|
|
146
|
-
| `vibeview
|
|
147
|
-
| `vibeview
|
|
148
|
-
| `vibeview
|
|
149
|
-
| `vibeview
|
|
150
|
-
| `vibeview
|
|
158
|
+
| Command | Purpose |
|
|
159
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
160
|
+
| `vibeview login` / `logout` / `whoami` | Authenticate via browser, sign out, show who you are |
|
|
161
|
+
| `vibeview dev` | Live development on a cloud device |
|
|
162
|
+
| `vibeview build` | Build your app — locally, or on the build fleet with `--cloud` (add `--production` for a signed release) |
|
|
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 |
|
|
165
|
+
| `vibeview credentials add/generate/list/rm/download` | App-signing credentials for production builds (`generate` covers iOS, tvOS, and Android) |
|
|
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 |
|
|
169
|
+
| `vibeview env list/set/rm/pull` | Build variables and secrets injected into cloud builds |
|
|
170
|
+
| `vibeview webhooks add/list/rm/test` | Notify your systems when builds finish (signed payloads) |
|
|
171
|
+
| `vibeview dev-status` / `dev-stop` | Check on, or stop, a backgrounded dev session |
|
|
172
|
+
| `vibeview agent-setup` | Make VibeView discoverable to your coding agent (skill + MCP) |
|
|
173
|
+
| `vibeview mcp` | Run the MCP server so an agent can drive the device |
|
|
174
|
+
| `vibeview ui-tree` | The current screen's elements, each with a ref to act on |
|
|
175
|
+
| `vibeview logs` | Recent app logs from the device (console output, errors, crashes) |
|
|
176
|
+
| `vibeview screenshot` | Capture the screen to a PNG |
|
|
177
|
+
| `vibeview tap` / `type` / `scroll` / `press` | Drive the device |
|
|
178
|
+
| `vibeview scroll-to` / `drag` / `alert` / `find` | Search, precise gestures, iOS system alerts |
|
|
179
|
+
| `vibeview upload-app <file>` | Upload a build (zipped simulator `.app`, or `.apk`) |
|
|
180
|
+
| `vibeview list-apps` | List your apps |
|
|
181
|
+
| `vibeview list-devices` | List available device types |
|
|
182
|
+
| `vibeview list-suites` | List test suites |
|
|
183
|
+
| `vibeview create-test [name]` | Create a test case |
|
|
184
|
+
| `vibeview run-test <test-id>` | Run a single test |
|
|
185
|
+
| `vibeview run-suite <suite-id>` | Run a test suite |
|
|
186
|
+
| `vibeview run-suites` | Run multiple suites |
|
|
151
187
|
|
|
152
188
|
Run any command with `--help` for its options.
|
|
153
189
|
|
|
@@ -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";
|
|
@@ -358,6 +359,9 @@ function detectFlavor(cwd) {
|
|
|
358
359
|
if (isFile(join4(cwd, "manifest"))) return "roku";
|
|
359
360
|
return "unknown";
|
|
360
361
|
}
|
|
362
|
+
function isManagedExpo(cwd) {
|
|
363
|
+
return detectFlavor(cwd) === "expo" && !existsSync3(join4(cwd, "android", "gradlew")) && iosWorkspaceName(cwd) === null;
|
|
364
|
+
}
|
|
361
365
|
function iosWorkspaceName(cwd) {
|
|
362
366
|
try {
|
|
363
367
|
const found = readdirSync2(join4(cwd, "ios")).find((f) => f.endsWith(".xcworkspace"));
|
|
@@ -420,21 +424,51 @@ function defaultsFor(cwd, platform) {
|
|
|
420
424
|
var ANDROID_PRODUCTION_ARTIFACT = "android/app/build/outputs/bundle/release/*.aab";
|
|
421
425
|
var ANDROID_PRODUCTION_COMMAND = "yarn install --immutable && cd android && ./gradlew bundleRelease";
|
|
422
426
|
var IOS_PRODUCTION_ARTIFACT = "ios/build/*.ipa";
|
|
427
|
+
var MANAGED_EXPO_ANDROID_PRODUCTION_COMMAND = "yarn install --immutable && npx expo prebuild --platform android --no-install && cd android && ./gradlew bundleRelease";
|
|
428
|
+
function expoSanitizedName(name) {
|
|
429
|
+
return name.replace(/[\W_]+/g, "").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
430
|
+
}
|
|
431
|
+
function managedExpoIosName(cwd) {
|
|
432
|
+
if (existsSync3(join4(cwd, "app.config.js")) || existsSync3(join4(cwd, "app.config.ts"))) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
const raw = readJson(cwd, "app.config.json") ?? readJson(cwd, "app.json");
|
|
436
|
+
if (!raw) return null;
|
|
437
|
+
const config = "expo" in raw ? raw.expo : raw;
|
|
438
|
+
if (typeof config !== "object" || config === null) return null;
|
|
439
|
+
const name = config.name;
|
|
440
|
+
if (typeof name !== "string") return null;
|
|
441
|
+
return expoSanitizedName(name) || null;
|
|
442
|
+
}
|
|
443
|
+
function iosArchiveExportCommand(ws) {
|
|
444
|
+
const archivePath = `ios/build/${ws}.xcarchive`;
|
|
445
|
+
const archiveCmd = `xcodebuild archive -workspace ios/${ws}.xcworkspace -scheme ${ws} -configuration Release -archivePath ${archivePath} CODE_SIGN_STYLE=Manual \${VIBEVIEW_DEVELOPMENT_TEAM:+"DEVELOPMENT_TEAM=$VIBEVIEW_DEVELOPMENT_TEAM"} \${VIBEVIEW_PROVISIONING_PROFILE:+"PROVISIONING_PROFILE_SPECIFIER=$VIBEVIEW_PROVISIONING_PROFILE"} \${VIBEVIEW_CODE_SIGN_IDENTITY:+"CODE_SIGN_IDENTITY=$VIBEVIEW_CODE_SIGN_IDENTITY"}`;
|
|
446
|
+
const exportCmd = `xcodebuild -exportArchive -archivePath ${archivePath} -exportPath ios/build -exportOptionsPlist "$VIBEVIEW_EXPORT_OPTIONS_PLIST"`;
|
|
447
|
+
return `${archiveCmd} && ${exportCmd}`;
|
|
448
|
+
}
|
|
423
449
|
function productionDefaultsFor(cwd, platform) {
|
|
424
450
|
if (platform === "android" || platform === "androidtv") {
|
|
425
|
-
return {
|
|
451
|
+
return {
|
|
452
|
+
buildCommand: isManagedExpo(cwd) ? MANAGED_EXPO_ANDROID_PRODUCTION_COMMAND : ANDROID_PRODUCTION_COMMAND,
|
|
453
|
+
artifact: ANDROID_PRODUCTION_ARTIFACT
|
|
454
|
+
};
|
|
426
455
|
}
|
|
427
456
|
if (platform === "tvos") {
|
|
428
457
|
return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
429
458
|
}
|
|
430
459
|
if (platform === "ios") {
|
|
460
|
+
if (isManagedExpo(cwd)) {
|
|
461
|
+
const name = managedExpoIosName(cwd);
|
|
462
|
+
if (!name) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
463
|
+
return {
|
|
464
|
+
buildCommand: "yarn install --immutable && npx expo prebuild --platform ios && " + iosArchiveExportCommand(name),
|
|
465
|
+
artifact: IOS_PRODUCTION_ARTIFACT
|
|
466
|
+
};
|
|
467
|
+
}
|
|
431
468
|
const ws = iosWorkspaceName(cwd);
|
|
432
469
|
if (!ws) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
433
|
-
const archivePath = `ios/build/${ws}.xcarchive`;
|
|
434
|
-
const archiveCmd = `xcodebuild archive -workspace ios/${ws}.xcworkspace -scheme ${ws} -configuration Release -archivePath ${archivePath} CODE_SIGN_STYLE=Manual \${VIBEVIEW_DEVELOPMENT_TEAM:+"DEVELOPMENT_TEAM=$VIBEVIEW_DEVELOPMENT_TEAM"} \${VIBEVIEW_PROVISIONING_PROFILE:+"PROVISIONING_PROFILE_SPECIFIER=$VIBEVIEW_PROVISIONING_PROFILE"} \${VIBEVIEW_CODE_SIGN_IDENTITY:+"CODE_SIGN_IDENTITY=$VIBEVIEW_CODE_SIGN_IDENTITY"}`;
|
|
435
|
-
const exportCmd = `xcodebuild -exportArchive -archivePath ${archivePath} -exportPath ios/build -exportOptionsPlist "$VIBEVIEW_EXPORT_OPTIONS_PLIST"`;
|
|
436
470
|
return {
|
|
437
|
-
buildCommand: `yarn install --immutable && ${
|
|
471
|
+
buildCommand: `yarn install --immutable && ${iosArchiveExportCommand(ws)}`,
|
|
438
472
|
artifact: IOS_PRODUCTION_ARTIFACT
|
|
439
473
|
};
|
|
440
474
|
}
|
|
@@ -1155,6 +1189,11 @@ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
|
1155
1189
|
for (const p of platforms) {
|
|
1156
1190
|
try {
|
|
1157
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
|
+
}
|
|
1158
1197
|
} catch (err) {
|
|
1159
1198
|
fail(opts, err instanceof Error ? err.message : String(err));
|
|
1160
1199
|
}
|
|
@@ -1199,6 +1238,14 @@ ${offenders}`);
|
|
|
1199
1238
|
const production = productionBuilds.get(p);
|
|
1200
1239
|
const command = production ? production.command : build.command;
|
|
1201
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
|
+
}
|
|
1202
1249
|
return {
|
|
1203
1250
|
platform: p,
|
|
1204
1251
|
// OMITTED, never invented, for a platform whose app the artifact will
|
|
@@ -1210,7 +1257,8 @@ ${offenders}`);
|
|
|
1210
1257
|
command,
|
|
1211
1258
|
artifact_path: artifactPath,
|
|
1212
1259
|
distribution: production ? "production" : "simulator",
|
|
1213
|
-
...build.env ? { env: build.env } : {}
|
|
1260
|
+
...build.env ? { env: build.env } : {},
|
|
1261
|
+
...autoIncrement && production ? { auto_increment: true } : {}
|
|
1214
1262
|
};
|
|
1215
1263
|
});
|
|
1216
1264
|
const uploadSpinner = !opts.json ? createSpinner("uploading source...") : null;
|
|
@@ -3371,6 +3419,7 @@ export {
|
|
|
3371
3419
|
readMetroState,
|
|
3372
3420
|
killOwnedMetro,
|
|
3373
3421
|
buildCommand,
|
|
3422
|
+
formatDuration,
|
|
3374
3423
|
BUILD_TERMINAL_STATUSES,
|
|
3375
3424
|
fetchFinalSummary,
|
|
3376
3425
|
newTransitionState,
|
|
@@ -287,10 +287,15 @@ var ApiClient = class {
|
|
|
287
287
|
const qs = offset !== void 0 ? `?offset=${offset}` : "";
|
|
288
288
|
return this.requestRaw("GET", `/builds/${id}/logs${qs}`);
|
|
289
289
|
}
|
|
290
|
-
|
|
290
|
+
/** GET /api/v1/builds/ — the filter values are forwarded verbatim; the
|
|
291
|
+
* backend accepts comma-separated lists for platform/status. */
|
|
292
|
+
async listCloudBuilds(opts) {
|
|
291
293
|
const params = new URLSearchParams();
|
|
292
|
-
if (
|
|
293
|
-
if (
|
|
294
|
+
if (opts?.platform !== void 0) params.set("platform", opts.platform);
|
|
295
|
+
if (opts?.status !== void 0) params.set("status", opts.status);
|
|
296
|
+
if (opts?.distribution !== void 0) params.set("distribution", opts.distribution);
|
|
297
|
+
if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
|
|
298
|
+
if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
|
|
294
299
|
const qs = params.toString();
|
|
295
300
|
return this.requestRaw("GET", `/builds/${qs ? `?${qs}` : ""}`);
|
|
296
301
|
}
|
|
@@ -445,6 +450,78 @@ var ApiClient = class {
|
|
|
445
450
|
async deleteAscKey(id) {
|
|
446
451
|
await this.requestRaw("DELETE", `/signing-credentials/asc-keys/${id}`);
|
|
447
452
|
}
|
|
453
|
+
/** GET /api/v1/signing-credentials/play-keys. Requires VIEWER role.
|
|
454
|
+
* Metadata only — the service-account JSON is never returned. */
|
|
455
|
+
async listPlayKeys() {
|
|
456
|
+
return this.requestRaw("GET", "/signing-credentials/play-keys");
|
|
457
|
+
}
|
|
458
|
+
/** POST /api/v1/signing-credentials/play-keys (multipart). Requires ADMIN
|
|
459
|
+
* role. Same-name upload is a rotation upsert server-side — mirrors
|
|
460
|
+
* addAscKey exactly (there are no extra id fields: the client_email is
|
|
461
|
+
* read out of the JSON server-side). */
|
|
462
|
+
async addPlayKey(body) {
|
|
463
|
+
const formData = new FormData();
|
|
464
|
+
if (body.name !== void 0) formData.append("name", body.name);
|
|
465
|
+
formData.append("file", new Blob([new Uint8Array(body.file)]), body.filename);
|
|
466
|
+
return this.requestFormData("POST", "/signing-credentials/play-keys", formData);
|
|
467
|
+
}
|
|
468
|
+
/** DELETE /api/v1/signing-credentials/play-keys/{id}. Requires ADMIN role. */
|
|
469
|
+
async deletePlayKey(id) {
|
|
470
|
+
await this.requestRaw("DELETE", `/signing-credentials/play-keys/${id}`);
|
|
471
|
+
}
|
|
472
|
+
/** POST /api/v1/submissions — submit a succeeded production build's
|
|
473
|
+
* artifact to TestFlight (ios/tvos) or a Play track (android/androidtv).
|
|
474
|
+
* `platform` is always sent: the backend derives the real platform from
|
|
475
|
+
* the build row and 400s on a mismatch, so a pasted wrong --build id
|
|
476
|
+
* fails loudly instead of silently submitting to the other store family.
|
|
477
|
+
* As with addSigningCredential, the CLI's camelCase (`ascKey`) is
|
|
478
|
+
* converted to the route's snake_case at this boundary and nowhere else.
|
|
479
|
+
* 4xx details (including the 409 "already active" refusal and the 400
|
|
480
|
+
* platform-mismatch) surface verbatim via extractDetail. */
|
|
481
|
+
async createSubmission(body) {
|
|
482
|
+
return this.requestRaw("POST", "/submissions", {
|
|
483
|
+
build: body.build,
|
|
484
|
+
platform: body.platform,
|
|
485
|
+
...body.track !== void 0 ? { track: body.track } : {},
|
|
486
|
+
...body.package !== void 0 ? { package: body.package } : {},
|
|
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 } : {}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/** GET /api/v1/submissions?build=<id> — that build's submissions, newest
|
|
494
|
+
* first. */
|
|
495
|
+
async listSubmissions(build) {
|
|
496
|
+
return this.requestRaw("GET", `/submissions?build=${encodeURIComponent(build)}`);
|
|
497
|
+
}
|
|
498
|
+
/** GET /api/v1/submissions/{id}. */
|
|
499
|
+
async getSubmission(id) {
|
|
500
|
+
return this.requestRaw("GET", `/submissions/${id}`);
|
|
501
|
+
}
|
|
502
|
+
/** GET /api/v1/submissions/{id}/log[?offset=N] — same {content, offset,
|
|
503
|
+
* status} shape as getCloudBuildLogs, so the follow loops share one
|
|
504
|
+
* response type. */
|
|
505
|
+
async getSubmissionLog(id, offset) {
|
|
506
|
+
const qs = offset !== void 0 ? `?offset=${offset}` : "";
|
|
507
|
+
return this.requestRaw("GET", `/submissions/${id}/log${qs}`);
|
|
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
|
+
}
|
|
448
525
|
/** GET /api/v1/signing-credentials/{id}/material. Requires ADMIN role;
|
|
449
526
|
* audited server-side. 404s (NotFoundError) for non-generated rows —
|
|
450
527
|
* uploaded material is never readable back. */
|