@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/README.md +2 -1
- package/dist/index.js +588 -510
- package/dist/package.json +1 -1
- package/dist/src/build/onboarding/android/gcp-api.d.ts +128 -0
- package/dist/src/build/onboarding/android/gradle-parser.d.ts +19 -0
- package/dist/src/build/onboarding/android/keystore.d.ts +77 -0
- package/dist/src/build/onboarding/android/oauth-config.d.ts +24 -0
- package/dist/src/build/onboarding/android/oauth-google.d.ts +134 -0
- package/dist/src/build/onboarding/android/play-api.d.ts +91 -0
- package/dist/src/build/onboarding/android/progress.d.ts +21 -0
- package/dist/src/build/onboarding/android/types.d.ts +66 -0
- package/dist/src/build/onboarding/android/ui/app.d.ts +11 -0
- package/dist/src/build/onboarding/command.d.ts +1 -0
- package/dist/src/build/onboarding/file-picker.d.ts +5 -0
- package/dist/src/build/onboarding/ui/components.d.ts +1 -0
- package/dist/src/sdk.js +202 -202
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -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;
|
|
@@ -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>;
|