@capgo/cli 7.98.1 → 7.99.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": "7.98.1",
4
+ "version": "7.99.0",
5
5
  "description": "A CLI to upload to capgo servers",
6
6
  "author": "Martin martin@capgo.app",
7
7
  "license": "Apache 2.0",
@@ -0,0 +1,128 @@
1
+ export declare const ANDROIDPUBLISHER_API = "androidpublisher.googleapis.com";
2
+ export declare const DEFAULT_SERVICE_ACCOUNT_ID = "capgo-native-build";
3
+ export declare const DEFAULT_SERVICE_ACCOUNT_DISPLAY_NAME = "Capgo Native Build";
4
+ export declare const DEFAULT_SERVICE_ACCOUNT_DESCRIPTION = "Allows Capgo to build and submit the app to the Google Play Store";
5
+ export interface GcpProject {
6
+ projectId: string;
7
+ projectNumber: string;
8
+ name: string;
9
+ lifecycleState: 'ACTIVE' | 'DELETE_REQUESTED' | 'DELETE_IN_PROGRESS' | string;
10
+ }
11
+ export interface GcpServiceAccount {
12
+ name: string;
13
+ email: string;
14
+ projectId: string;
15
+ uniqueId: string;
16
+ displayName?: string;
17
+ }
18
+ export interface GcpServiceAccountKey {
19
+ /** Full resource name — e.g. `projects/{p}/serviceAccounts/{sa}/keys/{keyId}`. */
20
+ name: string;
21
+ /** Base64-encoded JSON key file — decode with `Buffer.from(..., 'base64')`. */
22
+ privateKeyDataBase64: string;
23
+ }
24
+ interface GcpOperation {
25
+ name: string;
26
+ done?: boolean;
27
+ error?: {
28
+ code: number;
29
+ message: string;
30
+ details?: unknown;
31
+ };
32
+ response?: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * List GCP projects the user has access to. Only ACTIVE projects are returned
36
+ * (pending-deletion projects are filtered out).
37
+ */
38
+ export declare function listProjects(accessToken: string): Promise<GcpProject[]>;
39
+ /**
40
+ * Create a GCP project and wait for the operation to finish.
41
+ *
42
+ * Google enforces:
43
+ * - projectId: 6–30 chars, lowercase letters / digits / hyphens, start with
44
+ * a letter, globally unique across all GCP
45
+ * - name: ≤30 chars
46
+ */
47
+ export declare function createProject(accessToken: string, projectId: string, displayName: string, options?: {
48
+ timeoutMs?: number;
49
+ }): Promise<GcpProject>;
50
+ /**
51
+ * Enable an API on a project (idempotent — no-op if already enabled).
52
+ */
53
+ export declare function enableService(accessToken: string, projectId: string, serviceName: string, options?: {
54
+ timeoutMs?: number;
55
+ }): Promise<void>;
56
+ /** List all service accounts in a project. */
57
+ export declare function listServiceAccounts(accessToken: string, projectId: string): Promise<GcpServiceAccount[]>;
58
+ /** Create a service account. accountId must match `[a-z]([-a-z0-9]*[a-z0-9])` and be 6–30 chars. */
59
+ export declare function createServiceAccount(args: {
60
+ accessToken: string;
61
+ projectId: string;
62
+ accountId: string;
63
+ displayName?: string;
64
+ description?: string;
65
+ }): Promise<GcpServiceAccount>;
66
+ /**
67
+ * Find an existing service account by email, or create it.
68
+ * Idempotent convenience used during onboarding so re-runs don't error out on
69
+ * "already exists".
70
+ */
71
+ export declare function ensureServiceAccount(args: {
72
+ accessToken: string;
73
+ projectId: string;
74
+ accountId: string;
75
+ displayName?: string;
76
+ description?: string;
77
+ }): Promise<{
78
+ account: GcpServiceAccount;
79
+ created: boolean;
80
+ }>;
81
+ /**
82
+ * Create a new JSON key for a service account. The response contains the only
83
+ * copy of the private key material — store it immediately. Google cannot
84
+ * retrieve the key later.
85
+ */
86
+ export declare function createServiceAccountKey(args: {
87
+ accessToken: string;
88
+ projectId: string;
89
+ serviceAccountEmail: string;
90
+ }): Promise<GcpServiceAccountKey>;
91
+ interface PollOperationOptions {
92
+ endpoint: string;
93
+ timeoutMs: number;
94
+ }
95
+ /**
96
+ * Poll a Google long-running Operation until `done: true` or the timeout
97
+ * elapses. Different Google APIs host `operations.get` at different base URLs;
98
+ * callers pass the endpoint used for the originating call.
99
+ */
100
+ export declare function pollOperation(accessToken: string, operationName: string, options: PollOperationOptions): Promise<GcpOperation>;
101
+ /**
102
+ * Normalize a user-supplied or generated string into a valid GCP project
103
+ * `displayName`. Google's rules (Cloud Resource Manager v1):
104
+ *
105
+ * - 4–30 characters
106
+ * - allowed chars: letters, digits, space, hyphen (`-`), apostrophe (`'`),
107
+ * exclamation (`!`), period (`.`)
108
+ * - must start and end with a letter or digit
109
+ *
110
+ * We strip any disallowed character (including em-dashes — which break the
111
+ * CLI's placeholder string literals if not handled here), collapse runs of
112
+ * whitespace, and trim the ends. Falls back to `"Capgo Build"` when the
113
+ * sanitized result would be shorter than 4 chars.
114
+ */
115
+ export declare function sanitizeGcpProjectDisplayName(input: string): string;
116
+ /**
117
+ * Generate a candidate GCP project ID for Capgo onboarding.
118
+ *
119
+ * Rules:
120
+ * - 6–30 chars
121
+ * - lowercase letters, digits, hyphens
122
+ * - must start with a letter, must not end with a hyphen
123
+ * - globally unique (caller should retry on 409 with a fresh random suffix)
124
+ *
125
+ * We keep the slug short and append a random tail so collisions are rare.
126
+ */
127
+ export declare function generateProjectId(appId: string): string;
128
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Extract every `applicationId` string literal in a Gradle file.
3
+ *
4
+ * Handles:
5
+ * - Groovy: applicationId "com.example.app"
6
+ * - Kotlin: applicationId = "com.example.app"
7
+ * - Single quotes, extra whitespace, indented defaultConfig / flavor blocks
8
+ *
9
+ * Ignores:
10
+ * - `applicationIdSuffix` (not a real package — it's a suffix fragment)
11
+ */
12
+ export declare function extractApplicationIds(gradleContent: string): string[];
13
+ /**
14
+ * Look at the usual Gradle locations under `{androidDir}/app/` and return every
15
+ * distinct `applicationId` found. Empty list if nothing is configured locally
16
+ * (e.g. the user hasn't run `npx cap add android` yet or we're pointed at the
17
+ * wrong directory).
18
+ */
19
+ export declare function findAndroidApplicationIds(androidDir: string, workingDir?: string): Promise<string[]>;
@@ -0,0 +1,77 @@
1
+ import { Buffer } from 'node:buffer';
2
+ export interface KeystoreDname {
3
+ commonName: string;
4
+ organizationName?: string;
5
+ countryCode?: string;
6
+ }
7
+ export interface KeystoreOptions {
8
+ alias: string;
9
+ storePassword: string;
10
+ keyPassword: string;
11
+ dname: KeystoreDname;
12
+ /** Default: 27 years (~10000 days, Android Play standard) */
13
+ validityYears?: number;
14
+ /** Default: 2048-bit RSA */
15
+ keySize?: number;
16
+ }
17
+ export interface KeystoreResult {
18
+ p12Base64: string;
19
+ p12Bytes: Buffer;
20
+ alias: string;
21
+ notAfter: Date;
22
+ }
23
+ /**
24
+ * Generate a URL-safe random password suitable for Android keystore use.
25
+ * 24 bytes → 32-char base64url string. Collision-resistant, never written in logs.
26
+ */
27
+ export declare function generateRandomPassword(): string;
28
+ /**
29
+ * Generate a PKCS#12 (.p12) keystore with a self-signed certificate.
30
+ *
31
+ * Key decisions:
32
+ * - 3DES encryption for Gradle/keytool compatibility (same as iOS csr.ts).
33
+ * - 27-year validity — Google Play requires keys to outlive all future app updates.
34
+ * - 2048-bit RSA — standard for Android app signing.
35
+ * - Subject/issuer identical (self-signed).
36
+ *
37
+ * Throws if alias or passwords are empty.
38
+ */
39
+ export declare function generateKeystore(options: KeystoreOptions): KeystoreResult;
40
+ export type ProbeKeyPasswordResult = {
41
+ ok: true;
42
+ } | {
43
+ ok: false;
44
+ reason: 'wrong-password' | 'unsupported-format' | 'parse-error' | 'no-private-key';
45
+ message: string;
46
+ };
47
+ /**
48
+ * Check whether the given password can both unlock a PKCS#12 keystore AND
49
+ * decrypt the private key inside it.
50
+ *
51
+ * Useful for the "skip the key-password prompt if it's the same as the store
52
+ * password" UX path: in practice most PKCS#12 keystores use a single password
53
+ * for both the integrity MAC and the encrypted private-key bag. If this
54
+ * returns `ok: true`, the CLI can use the store password as the key password
55
+ * without asking the user.
56
+ *
57
+ * Returns `unsupported-format` for JKS (node-forge can't parse it) — caller
58
+ * should fall back to prompting.
59
+ */
60
+ export declare function tryUnlockPrivateKey(bytes: Uint8Array, password: string): ProbeKeyPasswordResult;
61
+ export type ListAliasesResult = {
62
+ ok: true;
63
+ aliases: string[];
64
+ } | {
65
+ ok: false;
66
+ reason: 'wrong-password' | 'unsupported-format' | 'parse-error';
67
+ message: string;
68
+ };
69
+ /**
70
+ * Extract key aliases (PKCS#12 `friendlyName` attributes) from a keystore file.
71
+ *
72
+ * Works for PKCS#12 (.p12, .pfx) keystores. JKS (Java KeyStore — common for
73
+ * .jks / .keystore files created by `keytool`) is NOT PKCS#12 and cannot be
74
+ * parsed by node-forge; callers should treat `unsupported-format` as "ask the
75
+ * user for the alias manually".
76
+ */
77
+ export declare function listKeystoreAliases(bytes: Uint8Array, password: string): ListAliasesResult;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * YouTube tutorial explaining how to find a Google Play Console Developer
3
+ * account ID — shown as an option on the "paste your developer ID" step.
4
+ */
5
+ export declare const PLAY_DEV_ID_TUTORIAL_URL = "https://www.youtube.com/watch?v=Y1_Ngj8hHLU";
6
+ export interface CapgoOAuthClientConfig {
7
+ clientId: string;
8
+ clientSecret: string;
9
+ /**
10
+ * Scopes the backend tells the CLI to request. Always at least
11
+ * `https://www.googleapis.com/auth/androidpublisher`.
12
+ */
13
+ scopes: string[];
14
+ }
15
+ /**
16
+ * Fetch Capgo's Google OAuth client config from the backend.
17
+ *
18
+ * Returns the config when the backend has both `GOOGLE_OAUTH_CLIENT_ID` and
19
+ * `GOOGLE_OAUTH_CLIENT_SECRET` set (the `enabled: true` branch). Returns null
20
+ * if Google OAuth is not configured server-side — callers should treat that
21
+ * as "Android OAuth onboarding is not available, ask the user to use the
22
+ * manual flow from the docs".
23
+ */
24
+ export declare function fetchCapgoOAuthConfig(): Promise<CapgoOAuthClientConfig | null>;
@@ -0,0 +1,134 @@
1
+ export declare const GOOGLE_OAUTH_SCOPES_ANDROIDPUBLISHER: readonly ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/androidpublisher"];
2
+ export interface GoogleOAuthConfig {
3
+ clientId: string;
4
+ /**
5
+ * Desktop clients receive a "secret" from the Console that isn't truly
6
+ * confidential; pass it when available — Google accepts the token exchange
7
+ * with or without it as long as PKCE is used.
8
+ */
9
+ clientSecret?: string;
10
+ scopes: readonly string[];
11
+ /** Extra params to include on the auth URL (e.g. `login_hint`, `prompt`). */
12
+ extraAuthParams?: Record<string, string>;
13
+ }
14
+ export interface GoogleOAuthTokens {
15
+ accessToken: string;
16
+ refreshToken?: string;
17
+ /**
18
+ * Unix epoch in milliseconds — the wall-clock time the access token stops
19
+ * being accepted. Callers should refresh before this.
20
+ */
21
+ expiresAt: number;
22
+ idToken?: string;
23
+ scope: string;
24
+ tokenType: string;
25
+ }
26
+ export interface GoogleUserInfo {
27
+ sub: string;
28
+ email: string;
29
+ emailVerified: boolean;
30
+ name?: string;
31
+ picture?: string;
32
+ }
33
+ export interface RunOAuthFlowOptions {
34
+ /**
35
+ * Called once with the authorization URL right before we open it. Useful
36
+ * for logging the URL in case `open()` fails.
37
+ */
38
+ onAuthUrl?: (url: string) => void;
39
+ /** Called with user-visible status updates while we wait for the redirect. */
40
+ onStatus?: (message: string) => void;
41
+ /** Overall deadline for the whole flow. Defaults to 5 minutes. */
42
+ timeoutMs?: number;
43
+ /** Abort the flow early — useful for React cleanup. */
44
+ signal?: AbortSignal;
45
+ }
46
+ export interface PkcePair {
47
+ verifier: string;
48
+ challenge: string;
49
+ method: 'S256';
50
+ }
51
+ /**
52
+ * Generate a PKCE verifier (43–128 chars of unreserved URL chars) and its
53
+ * SHA-256 challenge. The verifier must be held until the token exchange.
54
+ */
55
+ export declare function generatePkcePair(): PkcePair;
56
+ export declare function generateState(): string;
57
+ export declare function buildAuthUrl(args: {
58
+ clientId: string;
59
+ redirectUri: string;
60
+ scopes: readonly string[];
61
+ state: string;
62
+ codeChallenge: string;
63
+ extra?: Record<string, string>;
64
+ }): string;
65
+ interface RawTokenResponse {
66
+ access_token: string;
67
+ expires_in: number;
68
+ refresh_token?: string;
69
+ scope: string;
70
+ token_type: string;
71
+ id_token?: string;
72
+ }
73
+ export declare function parseTokenResponse(raw: RawTokenResponse, now?: number): GoogleOAuthTokens;
74
+ /** Exchange an authorization code + PKCE verifier for tokens. */
75
+ export declare function exchangeAuthCode(args: {
76
+ config: GoogleOAuthConfig;
77
+ code: string;
78
+ codeVerifier: string;
79
+ redirectUri: string;
80
+ }): Promise<GoogleOAuthTokens>;
81
+ /**
82
+ * Use a stored refresh token to mint a new access token. Refresh tokens may be
83
+ * revoked by the user at any time; callers should surface a clean re-auth
84
+ * prompt if this throws.
85
+ */
86
+ export declare function refreshAccessToken(config: GoogleOAuthConfig, refreshToken: string): Promise<GoogleOAuthTokens>;
87
+ /** Fetch the signed-in user's email and subject (stable Google ID). */
88
+ export declare function fetchUserInfo(accessToken: string): Promise<GoogleUserInfo>;
89
+ /**
90
+ * Revoke a Google OAuth token. Accepts either an access or refresh token —
91
+ * revoking a refresh token also invalidates any access tokens minted from it.
92
+ */
93
+ export declare function revokeToken(token: string): Promise<void>;
94
+ /**
95
+ * Error thrown by runOAuthFlow when the user approves the consent screen but
96
+ * deselects one or more requested scopes. The CLI catches this specifically
97
+ * to route the user back to a "please grant all permissions" re-sign-in step
98
+ * instead of failing several phases later with confusing API errors.
99
+ */
100
+ export declare class MissingScopesError extends Error {
101
+ readonly missing: readonly string[];
102
+ readonly granted: string;
103
+ constructor(missing: readonly string[], granted: string);
104
+ }
105
+ /**
106
+ * Compare a space-separated `scope` string from a token response against the
107
+ * scopes the CLI requested. Returns the subset of requested scopes that the
108
+ * user did not grant.
109
+ *
110
+ * Google's tokeninfo response uses a space-separated, unordered list — the
111
+ * order in `requestedScopes` is not preserved. Empty strings are filtered out.
112
+ */
113
+ export declare function findMissingScopes(grantedScope: string, requestedScopes: readonly string[]): string[];
114
+ export interface LoopbackCallbackResult {
115
+ /** Authorization code Google returned in the query string. */
116
+ code: string;
117
+ /**
118
+ * Finishes the browser response with the given HTML. Call this AFTER doing
119
+ * the token exchange and scope validation so the user sees a result that
120
+ * reflects the post-exchange state (e.g. "missing permissions") rather than
121
+ * a generic "you can close this tab" page that's stale by the time it
122
+ * matters. Idempotent — second call is a no-op.
123
+ */
124
+ finishResponse: (html: string, statusCode?: number) => void;
125
+ }
126
+ /**
127
+ * Run the full browser-based OAuth flow and return tokens.
128
+ *
129
+ * Side effects:
130
+ * - Opens a browser window at Google's consent screen.
131
+ * - Starts (and later stops) a loopback HTTP server on 127.0.0.1.
132
+ */
133
+ export declare function runOAuthFlow(config: GoogleOAuthConfig, options?: RunOAuthFlowOptions): Promise<GoogleOAuthTokens>;
134
+ export {};
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The Play Developer API v3 has no public endpoint to enumerate the developer
3
+ * accounts a user can access. The caller must supply a developer account ID —
4
+ * the 16–20-digit number visible in the Play Console URL
5
+ * (`play.google.com/console/u/0/developers/{developerId}/...`).
6
+ */
7
+ export declare const PLAY_DEVELOPERS_URL = "https://play.google.com/console/u/0/developers/";
8
+ /** 10–25 digit numeric Play Console developer ID. */
9
+ export declare function isLikelyDeveloperId(value: string): boolean;
10
+ /**
11
+ * Normalize whatever the user pasted into a numeric developer ID.
12
+ *
13
+ * Accepts:
14
+ * - Raw numeric ID: `1234567890123456789`
15
+ * - Full Play Console URL: `https://play.google.com/console/u/0/developers/1234567890123456789/api-access`
16
+ * - URL without account prefix: `https://play.google.com/console/developers/1234567890123456789`
17
+ * - URLs wrapped in quotes or with surrounding whitespace
18
+ *
19
+ * Returns the extracted ID or null if nothing usable was found.
20
+ */
21
+ export declare function extractDeveloperId(input: string): string | null;
22
+ export interface PlayInvitedUser {
23
+ name: string;
24
+ email: string;
25
+ accessState?: string;
26
+ developerAccountPermissions?: string[];
27
+ }
28
+ /**
29
+ * Permissions granted to the Capgo service account.
30
+ *
31
+ * Play Developer API v3 splits permissions into two enums:
32
+ * - `DeveloperLevelPermission` — account-wide, all values end in `_GLOBAL`
33
+ * - `AppLevelPermission` — per-package, granted via `User.grants[]`
34
+ *
35
+ * References (authoritative — fetched 2026-04):
36
+ * - https://developers.google.com/android-publisher/api-ref/rest/v3/users
37
+ * - https://developers.google.com/android-publisher/api-ref/rest/v3/grants
38
+ *
39
+ * We grant the minimum needed for fastlane's `supply` to upload an AAB and
40
+ * roll out a release on the app the user is onboarding.
41
+ */
42
+ /**
43
+ * Developer-account-level permission. `CAN_MANAGE_DRAFT_APPS_GLOBAL` lets the
44
+ * SA see draft apps on this developer account — kept minimal so we don't ask
45
+ * for financial data or order management access.
46
+ */
47
+ export declare const CAPGO_SA_DEVELOPER_PERMISSIONS: readonly ["CAN_MANAGE_DRAFT_APPS_GLOBAL"];
48
+ /**
49
+ * App-level permissions granted via `User.grants[].appLevelPermissions[]`.
50
+ *
51
+ * - `CAN_ACCESS_APP` — baseline read access to the app
52
+ * - `CAN_MANAGE_DRAFT_APPS` — edit the app's draft state
53
+ * - `CAN_MANAGE_TRACK_APKS` — upload APKs/AABs to testing tracks
54
+ * (internal / alpha / beta)
55
+ * - `CAN_MANAGE_PUBLIC_APKS` — upload APKs/AABs to the production track
56
+ * and create/roll out production releases
57
+ */
58
+ export declare const CAPGO_SA_APP_PERMISSIONS: readonly ["CAN_ACCESS_APP", "CAN_MANAGE_DRAFT_APPS", "CAN_MANAGE_TRACK_APKS", "CAN_MANAGE_PUBLIC_APKS"];
59
+ /**
60
+ * Invite a service account into a Play Console developer account.
61
+ *
62
+ * The signed-in OAuth user MUST be an Admin on the developer account — Play
63
+ * returns 403 otherwise.
64
+ *
65
+ * Body shape follows the `User` resource:
66
+ * {
67
+ * email: "...",
68
+ * developerAccountPermissions: [ <DeveloperLevelPermission> ],
69
+ * grants: [
70
+ * { packageName: "com.example.app", permissions: [ <AppLevelPermission> ] }
71
+ * ]
72
+ * }
73
+ *
74
+ * `developerAccountPermissions` is optional but we always send at least one
75
+ * value so the SA shows up in the Play Console Users & permissions list.
76
+ */
77
+ export declare function inviteServiceAccount(args: {
78
+ accessToken: string;
79
+ developerId: string;
80
+ serviceAccountEmail: string;
81
+ /** DeveloperLevelPermission values — see CAPGO_SA_DEVELOPER_PERMISSIONS. */
82
+ developerAccountPermissions?: readonly string[];
83
+ /**
84
+ * Per-package grants. Each grant pins AppLevelPermission values to a
85
+ * specific `packageName`. The Capacitor app ID is usually the only entry.
86
+ */
87
+ grants?: ReadonlyArray<{
88
+ packageName: string;
89
+ permissions: readonly string[];
90
+ }>;
91
+ }): Promise<PlayInvitedUser>;
@@ -0,0 +1,21 @@
1
+ import type { AndroidOnboardingProgress, AndroidOnboardingStep } from './types.js';
2
+ export declare function loadAndroidProgress(appId: string, baseDir?: string): Promise<AndroidOnboardingProgress | null>;
3
+ export declare function saveAndroidProgress(appId: string, progress: AndroidOnboardingProgress, baseDir?: string): Promise<void>;
4
+ export declare function deleteAndroidProgress(appId: string, baseDir?: string): Promise<void>;
5
+ /**
6
+ * Determine the first incomplete step for the Android flow.
7
+ *
8
+ * Each phase is validated by checking both:
9
+ * 1. The `completedSteps.<phase>` marker (atomic write to a single field)
10
+ * 2. The ephemeral fields the marker depends on (separate top-level writes
11
+ * that can race against each other and against the marker)
12
+ *
13
+ * If a marker is present but the ephemeral data is missing, the phase is
14
+ * treated as incomplete — the user is routed back to the input step that
15
+ * collects the missing field, never further forward.
16
+ *
17
+ * This is the contract that makes the state machine self-healing: any
18
+ * inconsistent state on disk lands the user on a working input step instead
19
+ * of crashing several phases later.
20
+ */
21
+ export declare function getAndroidResumeStep(progress: AndroidOnboardingProgress | null): AndroidOnboardingStep;
@@ -0,0 +1,66 @@
1
+ export type AndroidOnboardingStep = 'welcome' | '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' | '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' | 'ask-build' | 'requesting-build' | 'build-complete' | 'error';
2
+ export type KeystoreMethod = 'existing' | 'generate';
3
+ export interface KeystoreReady {
4
+ keystorePath: string;
5
+ alias: string;
6
+ isGenerated: boolean;
7
+ }
8
+ export interface GoogleSignInComplete {
9
+ email: string;
10
+ googleSubject: string;
11
+ scope: string;
12
+ }
13
+ export interface PlayDeveloperAccountChoice {
14
+ developerId: string;
15
+ displayName?: string;
16
+ }
17
+ export interface GcpProjectChoice {
18
+ projectId: string;
19
+ projectNumber?: string;
20
+ displayName: string;
21
+ /** Whether this onboarding run created the project (vs. reusing an existing one). */
22
+ createdByOnboarding: boolean;
23
+ }
24
+ export interface ServiceAccountProvisioned {
25
+ email: string;
26
+ projectId: string;
27
+ uniqueId?: string;
28
+ }
29
+ export interface PlayInviteProvisioned {
30
+ developerId: string;
31
+ serviceAccountEmail: string;
32
+ }
33
+ export interface AndroidPackageChoice {
34
+ /** The Android applicationId that Play Console uses for this app. */
35
+ packageName: string;
36
+ /** How we picked it — useful for telemetry / resume clarity. */
37
+ source: 'gradle' | 'capacitor-config' | 'user-input';
38
+ }
39
+ export interface AndroidOnboardingProgress {
40
+ platform: 'android';
41
+ appId: string;
42
+ startedAt: string;
43
+ keystoreMethod?: KeystoreMethod;
44
+ keystoreExistingPath?: string;
45
+ keystoreAlias?: string;
46
+ keystoreStorePassword?: string;
47
+ keystoreKeyPassword?: string;
48
+ keystoreCommonName?: string;
49
+ pendingNewProjectId?: string;
50
+ pendingNewProjectDisplayName?: string;
51
+ completedSteps: {
52
+ keystoreReady?: KeystoreReady;
53
+ googleSignInComplete?: GoogleSignInComplete;
54
+ playAccountChosen?: PlayDeveloperAccountChoice;
55
+ gcpProjectChosen?: GcpProjectChoice;
56
+ androidPackageChosen?: AndroidPackageChoice;
57
+ serviceAccountProvisioned?: ServiceAccountProvisioned;
58
+ playInviteProvisioned?: PlayInviteProvisioned;
59
+ };
60
+ _oauthRefreshToken?: string;
61
+ _keystoreBase64?: string;
62
+ /** Base64 of the downloaded SA JSON key — saved as PLAY_CONFIG_JSON at end. */
63
+ _serviceAccountKeyBase64?: string;
64
+ }
65
+ export declare const ANDROID_STEP_PROGRESS: Record<AndroidOnboardingStep, number>;
66
+ export declare function getAndroidPhaseLabel(step: AndroidOnboardingStep): string;
@@ -0,0 +1,11 @@
1
+ import type { FC } from 'react';
2
+ import type { AndroidOnboardingProgress } from '../types.js';
3
+ interface AppProps {
4
+ appId: string;
5
+ initialProgress: AndroidOnboardingProgress | null;
6
+ androidDir: string;
7
+ /** Optional Capgo API key passed via -a/--apikey flag; takes precedence over saved key. */
8
+ apikey?: string;
9
+ }
10
+ declare const AndroidOnboardingApp: FC<AppProps>;
11
+ export default AndroidOnboardingApp;
@@ -1,4 +1,5 @@
1
1
  export interface OnboardingBuilderOptions {
2
2
  apikey?: string;
3
+ platform?: string;
3
4
  }
4
5
  export declare function onboardingBuilderCommand(options?: OnboardingBuilderOptions): Promise<void>;
@@ -9,3 +9,8 @@ export declare function canUseFilePicker(): boolean;
9
9
  */
10
10
  export declare function openFilePicker(): Promise<string | null>;
11
11
  export declare function openPackageJsonPicker(): Promise<string | null>;
12
+ /**
13
+ * Open the macOS native file picker filtered to Android keystore files.
14
+ * Accepts .jks, .keystore, and .p12 extensions.
15
+ */
16
+ export declare function openKeystorePicker(): Promise<string | null>;
@@ -20,6 +20,7 @@ export declare const ErrorLine: FC<{
20
20
  export declare const FilteredTextInput: FC<{
21
21
  placeholder?: string;
22
22
  filter?: string;
23
+ mask?: boolean;
23
24
  onSubmit: (value: string) => void;
24
25
  }>;
25
26
  export declare const Header: FC;