@capgo/cli 8.22.0 → 8.23.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
3
  "type": "module",
4
- "version": "8.22.0",
4
+ "version": "8.23.0",
5
5
  "description": "A CLI to upload to capgo servers",
6
6
  "author": "Martin martin@capgo.app",
7
7
  "license": "Apache 2.0",
@@ -156,7 +156,7 @@
156
156
  "test:ios-verify-app": "bun test/test-ios-verify-app.mjs",
157
157
  "test:platform-flow-contract": "bun test/test-platform-flow-contract.mjs",
158
158
  "test:tail-engine-shared": "bun test/test-tail-engine-shared.mjs",
159
- "test": "bun run build && bun run test:helper-dce && bun run test:version-detection:setup && bun run test:bundle && bun run test:functional && bun run test:semver && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:fail-on-incompatible && bun run test:credentials && bun run test:credentials-validation && bun run test:android-service-account-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:build-needed && bun run test:ci-prompts && bun run test:ci-secrets && bun run test:android-onboarding-progress && bun run test:onboarding-telemetry && bun run test:v2-event-migration && bun run test:analytics && bun run test:analytics-error-category && bun run test:analytics-org-resolver && bun run test:supabase-perf && bun run test:preview-qr && bun run test:app-set-options && bun run test:mcp-analytics && bun run test:mcp-instructions && bun run test:mcp-live-update-onboarding && bun run test:mcp-stdout-guard && bun run test:mcp-platform-select && bun run test:mcp-explain-scopes && bun run test:mcp-oauth-reopen && bun run test:mcp-broker-oauth && bun run test:mcp-broker-session && bun run test:mcp-credentials-manage && bun run test:mcp-resume-prompt && bun run test:mcp-build-job && bun run test:mcp-build-tools && bun run test:app-created-source && bun run test:doctor-analytics && bun run test:posthog-exception && bun run test:build-platform-selection && bun run test:onboarding-recovery && bun run test:onboarding-progress && bun run test:onboarding-run-targets && bun run test:run-device-command && bun run test:init-app-conflict && bun run test:init-guardrails && bun run test:init-replay && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:mcp-no-key-handshake && bun run test:auth-session && bun run test:version-detection && bun run test:platform-paths && bun run test:project-type-detection && bun run test:payload-split && bun run test:manifest-path-encoding && bun run test:macos-signing && bun run test:asc-key-protocol && bun run test:apple-api-import-helpers && bun run test:apple-api-verify-key && bun run test:bundle-id-detector && bun run test:apple-api-app-list && bun run test:app-verification && bun run test:pbxproj-parser && bun run test:ai-log-capture && bun run test:ai-analyze-flow && bun run test:cicd-failure-help && bun run test:ai-sse-parser && bun run test:ai-render-markdown && bun run test:ai-stream-markdown && bun run test:ai-onboarding-mode && bun run test:ai-fit && bun run test:platform-layout && bun run test:frame-fit && bun run test:onboarding-min-size && bun run test:min-size-gate && bun run test:shell-size-gate && bun run test:build-log-sanitize && bun run test:build-output-viewport && bun run test:diff-viewer-viewport && bun run test:build-complete-exit && bun run test:ai-analyze-stream && bun run test:support-mailto && bun run test:support-redact && bun run test:support-internal-log && bun run test:support-help-menu && bun run test:support-contact && bun run test:support-upload-prompt && bun run test:support-bundle-files && bun run test:self-update && bun run test:update-prompt && bun run test:apple-api-cert-create && bun run test:android-tail-engine && bun run test:android-tail-render && bun run test:android-tail-routing && bun run test:dev-gate-stripped && bun run test:frame-fit-ios-shared && bun run test:ios-confirm-app-id && bun run test:ios-create-new && bun run test:ios-e2e && bun run test:ios-flow-contract && bun run test:ios-import-discovery && bun run test:ios-import-export && bun run test:ios-import-pickers && bun run test:ios-import-recovery && bun run test:ios-recovery && bun run test:ios-resume && bun run test:ios-tail-handoff && bun run test:ios-tui-render && bun run test:p8-error && bun run test:ios-tui-routing && bun run test:ios-updater-sync-validation && bun run test:ios-verify-app && bun run test:platform-flow-contract && bun run test:tail-engine-shared && bun run test:prescan",
159
+ "test": "bun run build && bun run test:helper-dce && bun run test:version-detection:setup && bun run test:bundle && bun run test:functional && bun run test:semver && bun run test:version-edge-cases && bun run test:regex && bun run test:upload && bun run test:fail-on-incompatible && bun run test:credentials && bun run test:credentials-validation && bun run test:android-service-account-validation && bun run test:build-zip-filter && bun run test:checksum && bun run test:build-needed && bun run test:ci-prompts && bun run test:ci-secrets && bun run test:android-onboarding-progress && bun run test:onboarding-telemetry && bun run test:v2-event-migration && bun run test:analytics && bun run test:analytics-error-category && bun run test:analytics-org-resolver && bun run test:supabase-perf && bun run test:preview-qr && bun run test:app-set-options && bun run test:mcp-analytics && bun run test:mcp-instructions && bun run test:mcp-live-update-onboarding && bun run test:mcp-stdout-guard && bun run test:mcp-platform-select && bun run test:mcp-explain-scopes && bun run test:mcp-oauth-reopen && bun run test:mcp-broker-oauth && bun run test:mcp-broker-session && bun run test:mcp-credentials-manage && bun run test:mcp-resume-prompt && bun run test:mcp-build-job && bun run test:mcp-build-tools && bun run test:app-created-source && bun run test:doctor-analytics && bun run test:posthog-exception && bun run test:build-platform-selection && bun run test:onboarding-recovery && bun run test:onboarding-progress && bun run test:onboarding-run-targets && bun run test:run-device-command && bun run test:init-app-conflict && bun run test:init-guardrails && bun run test:init-replay && bun run test:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:mcp-no-key-handshake && bun run test:auth-session && bun run test:version-detection && bun run test:platform-paths && bun run test:project-type-detection && bun run test:payload-split && bun run test:manifest-path-encoding && bun run test:macos-signing && bun run test:asc-key-protocol && bun run test:apple-api-import-helpers && bun run test:apple-api-verify-key && bun run test:bundle-id-detector && bun run test:apple-api-app-list && bun run test:app-verification && bun run test:pbxproj-parser && bun run test:ai-log-capture && bun run test:ai-analyze-flow && bun run test:cicd-failure-help && bun run test:ai-sse-parser && bun run test:ai-render-markdown && bun run test:ai-stream-markdown && bun run test:ai-onboarding-mode && bun run test:ai-fit && bun run test:platform-layout && bun run test:frame-fit && bun run test:onboarding-min-size && bun run test:min-size-gate && bun run test:shell-size-gate && bun run test:build-log-sanitize && bun run test:build-output-viewport && bun run test:diff-viewer-viewport && bun run test:build-complete-exit && bun run test:ai-analyze-stream && bun run test:support-mailto && bun run test:support-redact && bun run test:support-internal-log && bun run test:support-help-menu && bun run test:support-contact && bun run test:support-upload-prompt && bun run test:support-bundle-files && bun run test:self-update && bun run test:update-prompt && bun run test:apple-api-cert-create && bun run test:android-tail-engine && bun run test:android-tail-render && bun run test:android-tail-routing && bun run test:dev-gate-stripped && bun run test:frame-fit-ios-shared && bun run test:ios-confirm-app-id && bun run test:ios-create-new && bun run test:ios-e2e && bun run test:ios-flow-contract && bun run test:ios-import-discovery && bun run test:ios-import-export && bun run test:ios-import-pickers && bun run test:ios-import-recovery && bun run test:ios-recovery && bun run test:ios-resume && bun run test:ios-tail-handoff && bun run test:ios-tui-render && bun run test:p8-error && bun run test:ios-tui-routing && bun run test:ios-updater-sync-validation && bun run test:ios-verify-app && bun run test:platform-flow-contract && bun run test:tail-engine-shared && bun run test:prescan && bun run test:android-reporting-api && bun run test:android-app-verification && bun run test:android-rename",
160
160
  "test:build-platform-selection": "bun test/test-build-platform-selection.mjs",
161
161
  "test:ai-log-capture": "bun test/test-ai-log-capture.mjs",
162
162
  "test:ai-analyze-flow": "bun test/test-ai-analyze-flow.mjs",
@@ -184,6 +184,9 @@
184
184
  "test:support-upload-prompt": "bun test/test-support-upload-prompt.mjs",
185
185
  "test:support-bundle-files": "bun test/test-support-bundle-files.mjs",
186
186
  "test:mcp-live-update-onboarding": "bun test/test-mcp-live-update-onboarding.mjs",
187
+ "test:android-reporting-api": "bun test/test-android-reporting-api.mjs",
188
+ "test:android-app-verification": "bun test/test-android-app-verification.mjs",
189
+ "test:android-rename": "bun test/test-android-rename.mjs",
187
190
  "test:app-set-options": "bun test/test-app-set-options.mjs"
188
191
  },
189
192
  "dependencies": {
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Pinned `@trapezedev/project` version installed on demand into the temp rename
3
+ * workspace. Trapeze is NOT bundled with the CLI (keeps it lean) — it's only
4
+ * installed when the user explicitly opts into the rename. Pinned so the rename
5
+ * behavior is reproducible.
6
+ */
7
+ export declare const TRAPEZE_PROJECT_VERSION = "7.1.4";
8
+ export interface RenameWorkspaceFiles {
9
+ /** Contents of the temp `package.json` (`type: module` + pinned Trapeze). */
10
+ packageJson: string;
11
+ /** Contents of the `rename.mjs` script run as `node rename.mjs <appId>`. */
12
+ renameMjs: string;
13
+ }
14
+ /**
15
+ * Build the two files written into the temp rename workspace.
16
+ *
17
+ * The `rename.mjs` script runs the proven 3-call Trapeze sequence — ALWAYS all
18
+ * three setters (`setPackageName` + `setApplicationId` + `setNamespace`).
19
+ * Skipping `namespace` is not an option: AGP 8 requires it, and a package move
20
+ * with a stale namespace breaks `R`/`BuildConfig` imports and fails the build.
21
+ * The `appId` is read from `process.argv[2]` so the caller passes the target
22
+ * package as a CLI argument rather than templating it into the script.
23
+ *
24
+ * `pkg` is accepted for symmetry / future validation but is intentionally NOT
25
+ * interpolated into the script — the appId always flows in via argv at runtime,
26
+ * which avoids any string-injection of an attacker-controlled package into the
27
+ * generated JS.
28
+ *
29
+ * `androidDir` is the resolved native directory (from capacitor.config; it may be
30
+ * a non-default path like `apps/mobile/platforms/android-native`). It is baked
31
+ * into the MobileProject config JSON-escaped so the rename targets the CONFIGURED
32
+ * native project, never a hardcoded `./android` (which could mutate a stale tree).
33
+ */
34
+ export declare function buildRenameWorkspaceFiles(pkg: string, androidDir: string): RenameWorkspaceFiles;
35
+ /** Whether Android Studio is holding the project's native files open. */
36
+ export type AndroidStudioState = 'running' | 'not-running' | 'unknown';
37
+ /**
38
+ * Pure predicate for the close-Android-Studio gate. Editing native files while
39
+ * Studio holds them open risks a half-written project / Studio clobbering the
40
+ * change.
41
+ *
42
+ * Only macOS can be determined here: `pgrep -f "Android Studio"` is the source
43
+ * of `pgrepOutput`, so a non-empty (trimmed) result means it's running and an
44
+ * empty one means it's closed. On any other platform we can't reliably probe,
45
+ * so the caller falls back to a one-time confirm → `unknown`.
46
+ */
47
+ export declare function isAndroidStudioRunning(platform: string, pgrepOutput: string): AndroidStudioState;
48
+ /**
49
+ * Verify the rename landed: re-read Gradle ids (via `findAndroidApplicationIds`,
50
+ * which returns `string[]`) and confirm the list now contains the target
51
+ * package. Returns `false` so the caller surfaces output + a manual fallback
52
+ * instead of claiming a false success.
53
+ */
54
+ export declare function verifyRenamed(gradleIds: string[], target: string): boolean;
@@ -0,0 +1,44 @@
1
+ /** Minimal shape of a Play Store app needed for reconciliation. */
2
+ export interface PlayAppLike {
3
+ packageName: string;
4
+ displayName: string;
5
+ }
6
+ /**
7
+ * Reconcile outcome for the Android package-select step.
8
+ *
9
+ * - `exact-match` — a Play app's `packageName` == a single clean Gradle id.
10
+ * Auto-confirm, no picker. Carries the matched package.
11
+ * - `wrong-build-id` — apps exist but the build id matches none → Path A picker.
12
+ * - `no-app` — no Play apps at all → Path B (create the app).
13
+ * - `multi-gradle` — several Gradle flavors and no clean single match → force
14
+ * the picker so the user disambiguates.
15
+ */
16
+ export type AndroidReconcileResult = {
17
+ kind: 'exact-match';
18
+ packageName: string;
19
+ } | {
20
+ kind: 'wrong-build-id';
21
+ } | {
22
+ kind: 'no-app';
23
+ } | {
24
+ kind: 'multi-gradle';
25
+ };
26
+ export interface ReconcileAndroidAppInput {
27
+ /** Every distinct `applicationId` found in the project's Gradle files (≥0). */
28
+ gradleIds: string[];
29
+ /** Apps that actually exist in the user's Play Console. */
30
+ apps: PlayAppLike[];
31
+ }
32
+ /**
33
+ * Pure reconciliation of the Android app-existence invariant.
34
+ *
35
+ * The common Capacitor case is a single Gradle id, which defers entirely to the
36
+ * shared iOS classifier (`packageName` ↦ `bundleId`). Android has no
37
+ * Developer-portal registration split, so both iOS `no-app-*` results collapse
38
+ * to a single `no-app` route.
39
+ *
40
+ * Multiple Gradle ids are handled here: exactly one matching a Play app is
41
+ * still a clean single match (`exact-match`); zero apps is `no-app`; anything
42
+ * else forces the picker (`multi-gradle`).
43
+ */
44
+ export declare function reconcileAndroidApp(input: ReconcileAndroidAppInput): AndroidReconcileResult;
@@ -46,6 +46,18 @@ export interface AndroidStepView {
46
46
  export interface AndroidStepCtx {
47
47
  appId: string;
48
48
  detectedPackageIds?: string[];
49
+ /**
50
+ * android-app-verify transient: the user's real Play Store apps (apps:search)
51
+ * + the reconcile outcome for the chosen package. Set in the effect's
52
+ * transient ONLY when the chosen package is not an exact match (and the
53
+ * fetch succeeded), so the view renders the create-app / re-check gate.
54
+ * Absent → the step is still auto (run the fetch) or already advanced.
55
+ */
56
+ playVerifyApps?: {
57
+ packageName: string;
58
+ displayName: string;
59
+ }[];
60
+ playVerifyOutcome?: 'no-app' | 'wrong-build-id' | 'multi-gradle';
49
61
  gcpProjects?: {
50
62
  projectId: string;
51
63
  name: string;
@@ -209,6 +221,9 @@ export type AndroidInput = {
209
221
  packageName: string;
210
222
  source: 'gradle' | 'capacitor-config' | 'user-input';
211
223
  serviceAccountMethod: 'generate' | 'existing';
224
+ } | {
225
+ step: 'android-app-verify';
226
+ verifyAction: 'open' | 'recheck' | 'cancel' | 'proceed';
212
227
  } | {
213
228
  step: 'ci-secrets-setup';
214
229
  value: 'retry' | 'skip';
@@ -320,6 +335,17 @@ export interface AndroidEffectDeps {
320
335
  * Mirrors gcp-api.ts: listProjects(accessToken).
321
336
  */
322
337
  listProjects: (accessToken: string) => Promise<GcpProject[]>;
338
+ /**
339
+ * List the user's Play Store apps via the Play Developer Reporting API
340
+ * (apps:search). OPTIONAL — present only when the optional
341
+ * playdeveloperreporting scope was granted; absent → android-app-verify
342
+ * degrades (advances without verifying, never blocks). Mirrors
343
+ * reporting-api.ts: listPlayApps(accessToken).
344
+ */
345
+ listPlayApps?: (accessToken: string) => Promise<{
346
+ packageName: string;
347
+ displayName: string;
348
+ }[]>;
323
349
  /**
324
350
  * Create a GCP project and wait for the operation to finish.
325
351
  * Mirrors gcp-api.ts: createProject(accessToken, projectId, displayName).
@@ -8,6 +8,13 @@ export interface GoogleOAuthConfig {
8
8
  */
9
9
  clientSecret?: string;
10
10
  scopes: readonly string[];
11
+ /**
12
+ * Subset of `scopes` whose absence must FAIL sign-in (throw
13
+ * MissingScopesError). Scopes in `scopes` but NOT here are OPTIONAL — the user
14
+ * may decline them on the consent screen and sign-in still succeeds; the
15
+ * caller is responsible for degrading gracefully. Defaults to all `scopes`.
16
+ */
17
+ requiredScopes?: readonly string[];
11
18
  /** Extra params to include on the auth URL (e.g. `login_hint`, `prompt`). */
12
19
  extraAuthParams?: Record<string, string>;
13
20
  }
@@ -111,6 +118,18 @@ export declare class MissingScopesError extends Error {
111
118
  * order in `requestedScopes` is not preserved. Empty strings are filtered out.
112
119
  */
113
120
  export declare function findMissingScopes(grantedScope: string, requestedScopes: readonly string[]): string[];
121
+ export interface MissingScopeSplit {
122
+ /** Required scopes the user didn't grant — sign-in must fail with a retry. */
123
+ missingRequired: string[];
124
+ /** Optional scopes the user declined — proceed, but tell them what they lose. */
125
+ skippedOptional: string[];
126
+ }
127
+ /**
128
+ * Split the requested-but-not-granted scopes into the required ones (block
129
+ * sign-in → MissingScopesError) and the optional ones (sign-in proceeds; the
130
+ * success page + caller warn about the degraded behavior).
131
+ */
132
+ export declare function splitMissingScopes(grantedScope: string, scopes: readonly string[], requiredScopes: readonly string[]): MissingScopeSplit;
114
133
  export interface LoopbackCallbackResult {
115
134
  /** Authorization code Google returned in the query string. */
116
135
  code: string;
@@ -1,4 +1,10 @@
1
1
  /** OAuth scopes for Capgo Android onboarding — androidpublisher (Play) plus
2
- * cloud-platform (create GCP projects / service accounts / keys). Shared by the
3
- * Ink wizard and the MCP bridge so the two drivers can never drift. */
4
- export declare const OAUTH_SCOPES_FOR_ONBOARDING: readonly ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/androidpublisher", "https://www.googleapis.com/auth/cloud-platform"];
2
+ * cloud-platform (create GCP projects / service accounts / keys), and the
3
+ * OPTIONAL playdeveloperreporting scope used by the app-existence verification
4
+ * step (apps:search). Shared by the Ink wizard and the MCP bridge so the two
5
+ * drivers can never drift. */
6
+ export declare const OAUTH_SCOPES_FOR_ONBOARDING: readonly ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/androidpublisher", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/playdeveloperreporting"];
7
+ /** The subset of {@link OAUTH_SCOPES_FOR_ONBOARDING} whose absence must FAIL
8
+ * sign-in. Excludes the optional playdeveloperreporting scope so a user who
9
+ * declines app-listing still completes onboarding (graceful degradation). */
10
+ export declare const OAUTH_REQUIRED_SCOPES: readonly ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/androidpublisher", "https://www.googleapis.com/auth/cloud-platform"];
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A Play Store app record as returned by `apps:search`. Used by the Android
3
+ * app-verification step to check whether an app exists whose `packageName`
4
+ * matches the project's Gradle `applicationId`.
5
+ */
6
+ export interface PlayApp {
7
+ packageName: string;
8
+ displayName: string;
9
+ }
10
+ /**
11
+ * Parse an `apps:search` response into {@link PlayApp} records. Tolerant of a
12
+ * missing `apps` array and of individual apps missing `displayName` — Google
13
+ * omits fields rather than nulling them, and a malformed page must never throw
14
+ * into the wizard (the whole feature degrades gracefully).
15
+ *
16
+ * Entries missing `packageName` are DROPPED: the package is the join key for
17
+ * reconciliation, and letting an empty one through could spuriously
18
+ * "exact-match" a project whose Gradle parse found no applicationId. The API
19
+ * documents `packageName` as always present, so this only guards malformed
20
+ * pages.
21
+ */
22
+ export declare function parseAppsSearchResponse(json: any): PlayApp[];
23
+ /**
24
+ * Carries the HTTP status alongside the message so callers can distinguish a
25
+ * 403 (scope not granted / Reporting API disabled → graceful degrade to the
26
+ * plain Gradle picker) from other failures.
27
+ */
28
+ export declare class ReportingApiHttpError extends Error {
29
+ readonly status: number;
30
+ constructor(status: number, message: string);
31
+ }
32
+ /** Injectable fetch — defaults to the global `fetch`, overridable in tests. */
33
+ export type FetchImpl = typeof fetch;
34
+ export interface ListPlayAppsOptions {
35
+ /**
36
+ * Override the global `fetch`. Tests inject a stub returning a Response-like
37
+ * object; production omits this and uses `globalThis.fetch`.
38
+ */
39
+ fetchImpl?: FetchImpl;
40
+ }
41
+ /**
42
+ * List every Play Store app accessible to the signed-in user, following
43
+ * pagination. Authenticates with the supplied user OAuth access token (must
44
+ * carry the `playdeveloperreporting` scope).
45
+ *
46
+ * `GET …/v1beta1/apps:search?pageSize=1000`, following `nextPageToken` up to
47
+ * {@link MAX_LIST_PAGES}. Throws {@link ReportingApiHttpError} on a non-OK
48
+ * response so callers can branch on `.status` (e.g. 403 → degrade).
49
+ */
50
+ export declare function listPlayApps(accessToken: string, opts?: ListPlayAppsOptions): Promise<PlayApp[]>;
@@ -1,5 +1,5 @@
1
1
  import type { TailProgress } from '../tail-types.js';
2
- export type AndroidOnboardingStep = 'welcome' | 'resume-prompt' | 'credentials-exist' | 'backing-up' | 'no-platform' | 'keystore-method-select' | 'keystore-explainer' | 'keystore-existing-path' | 'keystore-existing-picker' | 'keystore-existing-store-password' | 'keystore-existing-detecting-alias' | 'keystore-existing-alias-select' | 'keystore-existing-alias' | 'keystore-existing-key-password' | 'keystore-new-alias' | 'keystore-new-password-method' | 'keystore-new-store-password' | 'keystore-new-key-password' | 'keystore-new-cn' | 'keystore-generating' | 'service-account-method-select' | 'sa-json-existing-path' | 'sa-json-existing-picker' | 'sa-json-validating' | 'sa-json-validation-failed' | 'google-sign-in' | 'google-sign-in-running' | 'play-developer-id-input' | 'gcp-projects-loading' | 'gcp-projects-select' | 'gcp-project-create-name' | 'android-package-select' | 'gcp-setup-running' | 'saving-credentials' | 'detecting-ci-secrets' | 'ci-secrets-setup' | 'ci-secrets-target-select' | 'ask-ci-secrets' | 'checking-ci-secrets' | 'confirm-ci-secret-overwrite' | 'uploading-ci-secrets' | 'ci-secrets-failed' | 'ask-github-actions-setup' | 'confirm-secrets-push' | 'ask-export-env' | 'exporting-env' | 'confirm-env-export-overwrite' | 'overwrite-and-export-env' | 'pick-package-manager' | 'pick-build-script' | 'pick-build-script-custom' | 'preview-workflow-file' | 'view-workflow-diff' | 'writing-workflow-file' | 'ask-build' | 'requesting-build' | 'ai-analysis-prompt' | 'ai-analysis-running' | 'ai-analysis-result' | 'ai-analysis-result-scroll' | 'build-complete' | 'error' | 'support-confirm' | 'support-log-view' | 'support-uploading';
2
+ export type AndroidOnboardingStep = 'welcome' | 'resume-prompt' | 'credentials-exist' | 'backing-up' | 'no-platform' | 'keystore-method-select' | 'keystore-explainer' | 'keystore-existing-path' | 'keystore-existing-picker' | 'keystore-existing-store-password' | 'keystore-existing-detecting-alias' | 'keystore-existing-alias-select' | 'keystore-existing-alias' | 'keystore-existing-key-password' | 'keystore-new-alias' | 'keystore-new-password-method' | 'keystore-new-store-password' | 'keystore-new-key-password' | 'keystore-new-cn' | 'keystore-generating' | 'service-account-method-select' | 'sa-json-existing-path' | 'sa-json-existing-picker' | 'sa-json-validating' | 'sa-json-validation-failed' | 'google-sign-in' | 'google-sign-in-running' | 'play-developer-id-input' | 'gcp-projects-loading' | 'gcp-projects-select' | 'gcp-project-create-name' | 'android-package-select' | 'android-app-verify' | 'gcp-setup-running' | 'saving-credentials' | 'detecting-ci-secrets' | 'ci-secrets-setup' | 'ci-secrets-target-select' | 'ask-ci-secrets' | 'checking-ci-secrets' | 'confirm-ci-secret-overwrite' | 'uploading-ci-secrets' | 'ci-secrets-failed' | 'ask-github-actions-setup' | 'confirm-secrets-push' | 'ask-export-env' | 'exporting-env' | 'confirm-env-export-overwrite' | 'overwrite-and-export-env' | 'pick-package-manager' | 'pick-build-script' | 'pick-build-script-custom' | 'preview-workflow-file' | 'view-workflow-diff' | 'writing-workflow-file' | 'ask-build' | 'requesting-build' | 'ai-analysis-prompt' | 'ai-analysis-running' | 'ai-analysis-result' | 'ai-analysis-result-scroll' | 'build-complete' | 'error' | 'support-confirm' | 'support-log-view' | 'support-uploading';
3
3
  export type AndroidOnboardingErrorCategory = 'keystore_invalid' | 'google_oauth_failed' | 'play_account_id_invalid' | 'sa_json_shape_invalid' | 'sa_json_token_rejected' | 'sa_json_no_app_access' | 'sa_json_network_error' | 'unknown';
4
4
  export type KeystoreMethod = 'existing' | 'generate';
5
5
  export type ServiceAccountMethod = 'existing' | 'generate';
@@ -75,6 +75,14 @@ export interface AndroidOnboardingProgress extends TailProgress {
75
75
  playAccountChosen?: PlayDeveloperAccountChoice;
76
76
  gcpProjectChosen?: GcpProjectChoice;
77
77
  androidPackageChosen?: AndroidPackageChoice;
78
+ /** Set once the chosen package was verified to exist in the user's Play
79
+ * Console (apps:search exact-match), or the user explicitly proceeded past
80
+ * a degraded/un-verifiable check. Gates `gcp-setup-running` so the
81
+ * per-package SA invite never 400s on a non-existent package. */
82
+ playAppVerified?: {
83
+ packageName: string;
84
+ verified: boolean;
85
+ };
78
86
  serviceAccountProvisioned?: ServiceAccountProvisioned;
79
87
  playInviteProvisioned?: PlayInviteProvisioned;
80
88
  /** Set once `saving-credentials` wrote credentials.json. Gates tail entry. */
@@ -30,6 +30,7 @@ interface OnboardingInput {
30
30
  gcpProjectId?: string;
31
31
  gcpProjectName?: string;
32
32
  androidPackage?: string;
33
+ androidVerifyAction?: 'open' | 'recheck' | 'cancel' | 'proceed';
33
34
  saMethodChoice?: 'retry' | 'save-anyway' | 'oauth';
34
35
  /** Set true at google-sign-in to (re)open the browser for a fresh OAuth — recovery when the browser didn't open, was closed, or the sign-in stalled. */
35
36
  reopenSignIn?: boolean;
@@ -17,6 +17,12 @@ export declare const onboardingNextStepSchema: z.ZodObject<{
17
17
  gcpProjectId: z.ZodOptional<z.ZodString>;
18
18
  gcpProjectName: z.ZodOptional<z.ZodString>;
19
19
  androidPackage: z.ZodOptional<z.ZodString>;
20
+ androidVerifyAction: z.ZodOptional<z.ZodEnum<{
21
+ open: "open";
22
+ proceed: "proceed";
23
+ cancel: "cancel";
24
+ recheck: "recheck";
25
+ }>>;
20
26
  saMethodChoice: z.ZodOptional<z.ZodEnum<{
21
27
  retry: "retry";
22
28
  oauth: "oauth";