@capgo/cli 8.3.0 → 8.4.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/index.js +608 -608
- package/dist/package.json +26 -2
- package/dist/src/build/mobileprovision-parser.d.ts +9 -0
- package/dist/src/build/onboarding/android/flow.d.ts +505 -0
- package/dist/src/build/onboarding/android/keystore.d.ts +18 -0
- package/dist/src/build/onboarding/android/oauth-google.d.ts +31 -0
- package/dist/src/build/onboarding/android/oauth-scopes.d.ts +4 -0
- package/dist/src/build/onboarding/android/types.d.ts +42 -1
- package/dist/src/build/onboarding/apple-api.d.ts +5 -1
- package/dist/src/build/onboarding/env-export.d.ts +12 -1
- package/dist/src/build/onboarding/flow/android-flow.d.ts +3 -0
- package/dist/src/build/onboarding/flow/contract.d.ts +24 -0
- package/dist/src/build/onboarding/flow/ios-flow.d.ts +3 -0
- package/dist/src/build/onboarding/ios/flow.d.ts +650 -0
- package/dist/src/build/onboarding/ios/progress.d.ts +2 -0
- package/dist/src/build/onboarding/mcp/app-id-validation.d.ts +8 -0
- package/dist/src/build/onboarding/mcp/contract.d.ts +51 -0
- package/dist/src/build/onboarding/mcp/engine.d.ts +131 -0
- package/dist/src/build/onboarding/mcp/explanations.d.ts +4 -0
- package/dist/src/build/onboarding/mcp/oauth-session.d.ts +29 -0
- package/dist/src/build/onboarding/mcp/onboarding-tools.d.ts +17 -0
- package/dist/src/build/onboarding/mcp/step-input.d.ts +45 -0
- package/dist/src/build/onboarding/mcp/terminal-launch.d.ts +22 -0
- package/dist/src/build/onboarding/tail/flow.d.ts +283 -0
- package/dist/src/build/onboarding/tail-types.d.ts +29 -0
- package/dist/src/build/onboarding/types.d.ts +82 -1
- package/dist/src/build/onboarding/ui/p8-error.d.ts +13 -0
- package/dist/src/build/output-record.d.ts +17 -0
- package/dist/src/schemas/onboarding.d.ts +41 -0
- package/dist/src/sdk.js +9 -9
- package/package.json +26 -2
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "8.
|
|
4
|
+
"version": "8.4.0",
|
|
5
5
|
"description": "A CLI to upload to capgo servers",
|
|
6
6
|
"author": "Martin martin@capgo.app",
|
|
7
7
|
"license": "Apache 2.0",
|
|
@@ -109,12 +109,36 @@
|
|
|
109
109
|
"test:apple-api-import-helpers": "bun test/test-apple-api-import-helpers.mjs",
|
|
110
110
|
"test:bundle-id-detector": "bun test/test-bundle-id-detector.mjs",
|
|
111
111
|
"test:apple-api-app-list": "bun test/test-apple-api-app-list.mjs",
|
|
112
|
+
"test:apple-api-cert-create": "bun test/test-apple-api-cert-create.mjs",
|
|
112
113
|
"test:app-verification": "bun test/test-app-verification.mjs",
|
|
113
114
|
"test:pbxproj-parser": "bun test/test-pbxproj-parser.mjs",
|
|
114
115
|
"test:manifest-path-encoding": "bun test/test-manifest-path-encoding.mjs",
|
|
115
116
|
"test:self-update": "bun test/test-self-update.mjs",
|
|
116
117
|
"test:update-prompt": "bun test/test-update-prompt.mjs",
|
|
117
|
-
"test
|
|
118
|
+
"test:android-tail-engine": "bun test/test-android-tail-engine.mjs",
|
|
119
|
+
"test:android-tail-render": "bun test/test-android-tail-render.mjs",
|
|
120
|
+
"test:android-tail-routing": "bun test/test-android-tail-routing.mjs",
|
|
121
|
+
"test:dev-gate-stripped": "bun test/test-dev-gate-stripped.mjs",
|
|
122
|
+
"test:frame-fit-ios-shared": "bun test/test-frame-fit-ios-shared.mjs",
|
|
123
|
+
"test:ios-confirm-app-id": "bun test/test-ios-confirm-app-id.mjs",
|
|
124
|
+
"test:ios-create-new": "bun test/test-ios-create-new.mjs",
|
|
125
|
+
"test:ios-e2e": "bun test/test-ios-e2e.mjs",
|
|
126
|
+
"test:ios-flow-contract": "bun test/test-ios-flow-contract.mjs",
|
|
127
|
+
"test:ios-import-discovery": "bun test/test-ios-import-discovery.mjs",
|
|
128
|
+
"test:ios-import-export": "bun test/test-ios-import-export.mjs",
|
|
129
|
+
"test:ios-import-pickers": "bun test/test-ios-import-pickers.mjs",
|
|
130
|
+
"test:ios-import-recovery": "bun test/test-ios-import-recovery.mjs",
|
|
131
|
+
"test:ios-recovery": "bun test/test-ios-recovery.mjs",
|
|
132
|
+
"test:ios-resume": "bun test/test-ios-resume.mjs",
|
|
133
|
+
"test:ios-tail-handoff": "bun test/test-ios-tail-handoff.mjs",
|
|
134
|
+
"test:ios-tui-render": "bun test/test-ios-tui-render.mjs",
|
|
135
|
+
"test:p8-error": "bun test/test-p8-error.mjs",
|
|
136
|
+
"test:ios-tui-routing": "bun test/test-ios-tui-routing.mjs",
|
|
137
|
+
"test:ios-updater-sync-validation": "bun test/test-ios-updater-sync-validation.mjs",
|
|
138
|
+
"test:ios-verify-app": "bun test/test-ios-verify-app.mjs",
|
|
139
|
+
"test:platform-flow-contract": "bun test/test-platform-flow-contract.mjs",
|
|
140
|
+
"test:tail-engine-shared": "bun test/test-tail-engine-shared.mjs",
|
|
141
|
+
"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:mcp-analytics && 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:prompt-preferences && bun run test:esm-sdk && bun run test:mcp && bun run test:version-detection && bun run test:platform-paths && bun run test:payload-split && bun run test:manifest-path-encoding && bun run test:macos-signing && bun run test:apple-api-import-helpers && 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: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-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",
|
|
118
142
|
"test:build-platform-selection": "bun test/test-build-platform-selection.mjs",
|
|
119
143
|
"test:ai-log-capture": "bun test/test-ai-log-capture.mjs",
|
|
120
144
|
"test:ai-analyze-flow": "bun test/test-ai-analyze-flow.mjs",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
1
2
|
export interface MobileprovisionInfo {
|
|
2
3
|
name: string;
|
|
3
4
|
uuid: string;
|
|
@@ -33,3 +34,11 @@ export declare function parseMobileprovisionFromBase64(base64Content: string): M
|
|
|
33
34
|
* - SHA1 of each embedded developer certificate (used for cert↔profile matching)
|
|
34
35
|
*/
|
|
35
36
|
export declare function parseMobileprovisionDetailed(filePath: string): MobileprovisionDetail;
|
|
37
|
+
/**
|
|
38
|
+
* Buffer-based variant of {@link parseMobileprovisionDetailed} — parses the raw
|
|
39
|
+
* .mobileprovision bytes directly instead of reading a path. Used by the iOS
|
|
40
|
+
* onboarding engine's import-provide-profile-path effect, which reads the file
|
|
41
|
+
* via its injected `readFile` dep (so the IO stays at the boundary) and parses
|
|
42
|
+
* the returned Buffer here. `source` is a label used only in error messages.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseMobileprovisionBufferDetailed(data: Buffer, source?: string): MobileprovisionDetail;
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import type { Buffer } from 'node:buffer';
|
|
2
|
+
import type { AndroidOnboardingProgress, AndroidOnboardingStep } from './types.js';
|
|
3
|
+
import type { KeystoreOptions, KeystoreResult, ListAliasesResult, ProbeKeyPasswordResult } from './keystore.js';
|
|
4
|
+
import type { ValidateOptions, ValidationResult } from './service-account-validation.js';
|
|
5
|
+
import type { GcpProject, GcpServiceAccount, GcpServiceAccountKey } from './gcp-api.js';
|
|
6
|
+
import type { GoogleOAuthTokens, GoogleUserInfo, PendingOAuthSession, RunOAuthFlowOptions } from './oauth-google.js';
|
|
7
|
+
import type { BuildCredentials } from '../../../schemas/build.js';
|
|
8
|
+
import type { BuildLogger, BuildRequestOptions, BuildRequestResult } from '../../request.js';
|
|
9
|
+
import type { AsyncCommandRunner, CiSecretDiscovery, CiSecretEntry, CiSecretSetupAdvice, CiSecretTarget, CommandRunner } from '../ci-secrets.js';
|
|
10
|
+
import type { EnvExportOpts, EnvExportResult } from '../env-export.js';
|
|
11
|
+
import type { BuildScriptChoice, GeneratedWorkflow, PackageManager, WorkflowGeneratorOpts } from '../workflow-generator.js';
|
|
12
|
+
import type { WorkflowWriteOptions, WorkflowWriteResult } from '../workflow-writer.js';
|
|
13
|
+
/**
|
|
14
|
+
* Pure helper: given persisted progress, validated OAuth tokens and the user's
|
|
15
|
+
* profile, return a NEW progress object (immutable spread) with:
|
|
16
|
+
* - `_oauthRefreshToken` set to `tokens.refreshToken`
|
|
17
|
+
* - `completedSteps.googleSignInComplete` set to `{ email, googleSubject, scope }`
|
|
18
|
+
*
|
|
19
|
+
* This is the single canonical place where Google sign-in state is written to
|
|
20
|
+
* progress — shared by the Ink core effect (`google-sign-in-running`) and the
|
|
21
|
+
* MCP bridge so both produce identical progress objects.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyGoogleSignIn(progress: AndroidOnboardingProgress, tokens: GoogleOAuthTokens, info: GoogleUserInfo): AndroidOnboardingProgress;
|
|
24
|
+
export type AndroidStepKind = 'auto' | 'input' | 'choice' | 'done' | 'error';
|
|
25
|
+
export interface AndroidStepOption {
|
|
26
|
+
value: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
note?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AndroidStepView {
|
|
31
|
+
step: AndroidOnboardingStep;
|
|
32
|
+
kind: AndroidStepKind;
|
|
33
|
+
title?: string;
|
|
34
|
+
prompt?: string;
|
|
35
|
+
collect?: string[];
|
|
36
|
+
options?: AndroidStepOption[];
|
|
37
|
+
message?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface AndroidStepCtx {
|
|
40
|
+
appId: string;
|
|
41
|
+
detectedPackageIds?: string[];
|
|
42
|
+
gcpProjects?: {
|
|
43
|
+
projectId: string;
|
|
44
|
+
name: string;
|
|
45
|
+
projectNumber?: string;
|
|
46
|
+
}[];
|
|
47
|
+
detectedAliases?: string[];
|
|
48
|
+
saValidation?: {
|
|
49
|
+
ok: false;
|
|
50
|
+
kind: string;
|
|
51
|
+
message: string;
|
|
52
|
+
} | {
|
|
53
|
+
ok: true;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Task 3 — keystore-existing-key-password prompt boundary.
|
|
57
|
+
* Set to true in AndroidEffectResult.transient when the auto-probe could not
|
|
58
|
+
* resolve the key password and the driver should show the manual input.
|
|
59
|
+
* After the user submits, applyAndroidInput records keystoreKeyPassword and
|
|
60
|
+
* re-running the effect finds it set and completes the phase.
|
|
61
|
+
*/
|
|
62
|
+
needsKeyPasswordPrompt?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Task 4 — fresh access token returned from google-sign-in-running so the
|
|
65
|
+
* driver can seed its token cache and avoid an immediate refresh on the next
|
|
66
|
+
* step. If the driver ignores it, deps.getAccessToken() will mint one —
|
|
67
|
+
* behaviorally identical.
|
|
68
|
+
*/
|
|
69
|
+
accessToken?: string;
|
|
70
|
+
/**
|
|
71
|
+
* keystore-existing-detecting-alias wrong-password signal.
|
|
72
|
+
* Set to true in AndroidEffectResult.transient when listKeystoreAliases returns
|
|
73
|
+
* { ok: false, reason: 'wrong-password' }. The driver (app.tsx) maps this to
|
|
74
|
+
* the original UX: setError + setRetryStep('keystore-existing-store-password')
|
|
75
|
+
* + setStep('error') WITHOUT calling handleError (no retryCount bump).
|
|
76
|
+
*/
|
|
77
|
+
wrongPassword?: boolean;
|
|
78
|
+
/** CI-secret entries built at saving-credentials (key/value/masked). */
|
|
79
|
+
ciSecretEntries?: CiSecretEntry[];
|
|
80
|
+
/**
|
|
81
|
+
* Full saved-credential map written at saving-credentials (the 5 build-cred
|
|
82
|
+
* fields, NO CAPGO_TOKEN). The Ink TUI holds the same in its `savedCredentials`
|
|
83
|
+
* React state and the env-export effects write it verbatim. Transient only —
|
|
84
|
+
* never persisted to progress.json (it carries the raw keystore/SA secrets).
|
|
85
|
+
*/
|
|
86
|
+
savedCredentials?: Record<string, string>;
|
|
87
|
+
/** CI-secret destinations discovered at detecting-ci-secrets. */
|
|
88
|
+
ciSecretTargets?: CiSecretTarget[];
|
|
89
|
+
/** Per-destination setup advice surfaced when no target is reachable. */
|
|
90
|
+
ciSecretSetupAdvice?: CiSecretSetupAdvice[];
|
|
91
|
+
/** Resolved owner/repo (GitHub) the CLI will push secrets to. */
|
|
92
|
+
ciSecretRepoLabel?: string | null;
|
|
93
|
+
/** Which secret keys already exist on the remote (checking-ci-secrets). */
|
|
94
|
+
ciSecretExistingKeys?: string[];
|
|
95
|
+
/** Human summary of the upload (uploading-ci-secrets). */
|
|
96
|
+
ciSecretUploadSummary?: string;
|
|
97
|
+
/** Absolute path of the written .env file (exporting-env). */
|
|
98
|
+
envExportPath?: string;
|
|
99
|
+
/** Set when env-export found nothing to write or threw — routed to build-complete, never thrown (exporting-env / overwrite-and-export-env). */
|
|
100
|
+
envExportError?: string;
|
|
101
|
+
/** Absolute path of the written workflow file (writing-workflow-file). */
|
|
102
|
+
workflowFilePath?: string;
|
|
103
|
+
/** The queued build URL (requesting-build). */
|
|
104
|
+
buildUrl?: string;
|
|
105
|
+
/** Streamed build-request log lines (requesting-build). */
|
|
106
|
+
buildOutput?: string[];
|
|
107
|
+
/** Captured AI-analysis job id surfaced on a failed build (requesting-build). */
|
|
108
|
+
aiJobId?: string;
|
|
109
|
+
/** Detected package manager from the project's lockfile (pick-package-manager). */
|
|
110
|
+
detectedPackageManager?: string;
|
|
111
|
+
/** All scripts from package.json (pick-build-script picker). */
|
|
112
|
+
availableScripts?: Record<string, string>;
|
|
113
|
+
/** Project-type recommendation surfaced at the top of pick-build-script. */
|
|
114
|
+
recommendedScript?: string | null;
|
|
115
|
+
/** Default `.env` export path shown at ask-export-env (defaultExportPath). */
|
|
116
|
+
defaultEnvExportPath?: string;
|
|
117
|
+
}
|
|
118
|
+
export declare const KIND_TABLE: Record<AndroidOnboardingStep, AndroidStepKind>;
|
|
119
|
+
/**
|
|
120
|
+
* Pure function: given an explicit step name, persisted progress (or null),
|
|
121
|
+
* and the current runtime context, return a UI-framework-neutral description
|
|
122
|
+
* of the step.
|
|
123
|
+
*
|
|
124
|
+
* This is the primary entry-point for drivers that already know the step
|
|
125
|
+
* (e.g. the MCP bridge). `androidStepView` is a thin wrapper that resolves
|
|
126
|
+
* the step from progress first.
|
|
127
|
+
*
|
|
128
|
+
* Dynamic kind for `android-package-select`:
|
|
129
|
+
* - ctx.detectedPackageIds === undefined → 'auto' (preload not yet done)
|
|
130
|
+
* - ctx.detectedPackageIds.length > 0 → 'choice' (options = the ids)
|
|
131
|
+
* - ctx.detectedPackageIds.length === 0 → 'input' (user must type it)
|
|
132
|
+
*
|
|
133
|
+
* All other steps use KIND_TABLE[step] unchanged.
|
|
134
|
+
*
|
|
135
|
+
* No I/O; no mutation of progress.
|
|
136
|
+
*/
|
|
137
|
+
export declare function androidViewForStep(step: AndroidOnboardingStep, progress: AndroidOnboardingProgress | null, ctx: AndroidStepCtx): AndroidStepView;
|
|
138
|
+
export type AndroidInput = {
|
|
139
|
+
step: 'credentials-exist';
|
|
140
|
+
value: 'backup' | 'cancel';
|
|
141
|
+
} | {
|
|
142
|
+
step: 'keystore-method-select';
|
|
143
|
+
value: 'existing' | 'generate' | 'learn';
|
|
144
|
+
} | {
|
|
145
|
+
step: 'keystore-existing-path';
|
|
146
|
+
path: string;
|
|
147
|
+
} | {
|
|
148
|
+
step: 'keystore-existing-store-password';
|
|
149
|
+
password: string;
|
|
150
|
+
} | {
|
|
151
|
+
step: 'keystore-existing-alias-select';
|
|
152
|
+
alias: string;
|
|
153
|
+
} | {
|
|
154
|
+
step: 'keystore-existing-alias';
|
|
155
|
+
alias: string;
|
|
156
|
+
} | {
|
|
157
|
+
step: 'keystore-existing-key-password';
|
|
158
|
+
password: string;
|
|
159
|
+
} | {
|
|
160
|
+
step: 'keystore-new-alias';
|
|
161
|
+
alias: string;
|
|
162
|
+
} | {
|
|
163
|
+
step: 'keystore-new-password-method';
|
|
164
|
+
value: 'random' | 'manual';
|
|
165
|
+
} | {
|
|
166
|
+
step: 'keystore-new-store-password';
|
|
167
|
+
password: string;
|
|
168
|
+
} | {
|
|
169
|
+
step: 'keystore-new-key-password';
|
|
170
|
+
password: string;
|
|
171
|
+
} | {
|
|
172
|
+
step: 'keystore-new-cn';
|
|
173
|
+
cn: string;
|
|
174
|
+
} | {
|
|
175
|
+
step: 'service-account-method-select';
|
|
176
|
+
value: 'generate' | 'existing';
|
|
177
|
+
} | {
|
|
178
|
+
step: 'sa-json-existing-path';
|
|
179
|
+
path: string;
|
|
180
|
+
} | {
|
|
181
|
+
step: 'sa-json-validation-failed';
|
|
182
|
+
value: 'retry' | 'oauth';
|
|
183
|
+
} | {
|
|
184
|
+
step: 'sa-json-validation-failed';
|
|
185
|
+
value: 'save-anyway';
|
|
186
|
+
serviceAccountKeyBase64: string;
|
|
187
|
+
} | {
|
|
188
|
+
step: 'play-developer-id-input';
|
|
189
|
+
rawDeveloperIdOrUrl: string;
|
|
190
|
+
} | {
|
|
191
|
+
step: 'gcp-projects-select';
|
|
192
|
+
gcpProject: {
|
|
193
|
+
projectId: string;
|
|
194
|
+
name: string;
|
|
195
|
+
projectNumber?: string;
|
|
196
|
+
};
|
|
197
|
+
} | {
|
|
198
|
+
step: 'gcp-project-create-name';
|
|
199
|
+
displayName: string;
|
|
200
|
+
} | {
|
|
201
|
+
step: 'android-package-select';
|
|
202
|
+
packageName: string;
|
|
203
|
+
source: 'gradle' | 'capacitor-config' | 'user-input';
|
|
204
|
+
serviceAccountMethod: 'generate' | 'existing';
|
|
205
|
+
} | {
|
|
206
|
+
step: 'ci-secrets-setup';
|
|
207
|
+
value: 'retry' | 'skip';
|
|
208
|
+
} | {
|
|
209
|
+
step: 'ci-secrets-target-select';
|
|
210
|
+
ciSecretTarget: CiSecretTarget | null;
|
|
211
|
+
} | {
|
|
212
|
+
step: 'ask-ci-secrets';
|
|
213
|
+
value: 'yes' | 'no';
|
|
214
|
+
} | {
|
|
215
|
+
step: 'confirm-ci-secret-overwrite';
|
|
216
|
+
value: 'replace' | 'skip';
|
|
217
|
+
} | {
|
|
218
|
+
step: 'ci-secrets-failed';
|
|
219
|
+
value: 'retry' | 'continue';
|
|
220
|
+
} | {
|
|
221
|
+
step: 'ask-github-actions-setup';
|
|
222
|
+
value: 'with-workflow' | 'secrets-only' | 'declined';
|
|
223
|
+
} | {
|
|
224
|
+
step: 'confirm-secrets-push';
|
|
225
|
+
value: 'confirm' | 'cancel';
|
|
226
|
+
} | {
|
|
227
|
+
step: 'ask-export-env';
|
|
228
|
+
value: 'no';
|
|
229
|
+
} | {
|
|
230
|
+
step: 'ask-export-env';
|
|
231
|
+
value: 'yes';
|
|
232
|
+
envExportTargetPath: string;
|
|
233
|
+
} | {
|
|
234
|
+
step: 'confirm-env-export-overwrite';
|
|
235
|
+
value: 'replace' | 'skip';
|
|
236
|
+
} | {
|
|
237
|
+
step: 'pick-package-manager';
|
|
238
|
+
selectedPackageManager: PackageManager;
|
|
239
|
+
} | {
|
|
240
|
+
step: 'pick-build-script';
|
|
241
|
+
value: '__custom__';
|
|
242
|
+
} | {
|
|
243
|
+
step: 'pick-build-script';
|
|
244
|
+
buildScriptChoice: BuildScriptChoice;
|
|
245
|
+
} | {
|
|
246
|
+
step: 'pick-build-script-custom';
|
|
247
|
+
command: string;
|
|
248
|
+
} | {
|
|
249
|
+
step: 'preview-workflow-file';
|
|
250
|
+
value: 'write' | 'view' | 'cancel';
|
|
251
|
+
} | {
|
|
252
|
+
step: 'view-workflow-diff';
|
|
253
|
+
value: 'close';
|
|
254
|
+
} | {
|
|
255
|
+
step: 'ask-build';
|
|
256
|
+
value: 'yes' | 'no';
|
|
257
|
+
};
|
|
258
|
+
export declare function applyAndroidInput(step: AndroidOnboardingStep, progress: AndroidOnboardingProgress, input: AndroidInput): AndroidOnboardingProgress;
|
|
259
|
+
export interface AndroidEffectDeps {
|
|
260
|
+
generateKeystore: (opts: KeystoreOptions) => KeystoreResult;
|
|
261
|
+
listKeystoreAliases: (bytes: Uint8Array, password: string) => ListAliasesResult;
|
|
262
|
+
tryUnlockPrivateKey: (bytes: Uint8Array, password: string) => ProbeKeyPasswordResult;
|
|
263
|
+
validateServiceAccountJson: (opts: ValidateOptions) => Promise<ValidationResult>;
|
|
264
|
+
updateSavedCredentials: (appId: string, platform: 'ios' | 'android', credentials: Record<string, string>) => Promise<void>;
|
|
265
|
+
loadSavedCredentials: (appId: string) => Promise<unknown>;
|
|
266
|
+
saveAndroidProgress: (appId: string, progress: AndroidOnboardingProgress) => Promise<void>;
|
|
267
|
+
loadAndroidProgress: (appId: string) => Promise<AndroidOnboardingProgress | null>;
|
|
268
|
+
deleteAndroidProgress: (appId: string) => Promise<void>;
|
|
269
|
+
readFile: (path: string) => Promise<Buffer>;
|
|
270
|
+
copyFile: (src: string, dest: string) => Promise<void>;
|
|
271
|
+
/**
|
|
272
|
+
* Run the full browser OAuth flow. The driver pre-binds OAuth client config
|
|
273
|
+
* (clientId, clientSecret, scopes) so the core never sees credentials —
|
|
274
|
+
* config/scope policy lives in the driver.
|
|
275
|
+
*
|
|
276
|
+
* Signature mirrors RunOAuthFlowOptions from oauth-google.ts (minus
|
|
277
|
+
* timeoutMs/signal which the driver controls internally).
|
|
278
|
+
*/
|
|
279
|
+
runOAuthFlow: (callbacks: Pick<RunOAuthFlowOptions, 'onAuthUrl' | 'onStatus'>) => Promise<GoogleOAuthTokens>;
|
|
280
|
+
/**
|
|
281
|
+
* Non-blocking OAuth starter (MCP fire-and-poll). Opens the browser and
|
|
282
|
+
* starts the loopback listener, then returns a PendingOAuthSession immediately
|
|
283
|
+
* without waiting for sign-in to complete. The MCP bridge uses this to
|
|
284
|
+
* avoid blocking a single tool call on the full OAuth round-trip.
|
|
285
|
+
*
|
|
286
|
+
* Optional — only provided by the MCP driver. The Ink driver uses the
|
|
287
|
+
* blocking `runOAuthFlow` instead and does not need this dep.
|
|
288
|
+
*
|
|
289
|
+
* The driver pre-binds OAuth client config (clientId, clientSecret, scopes).
|
|
290
|
+
*/
|
|
291
|
+
startOAuthFlow?: (callbacks?: Pick<RunOAuthFlowOptions, 'onAuthUrl' | 'onStatus'>) => Promise<PendingOAuthSession>;
|
|
292
|
+
/** Fetch the signed-in user's profile (email, sub). */
|
|
293
|
+
fetchUserInfo: (accessToken: string) => Promise<GoogleUserInfo>;
|
|
294
|
+
/**
|
|
295
|
+
* Mint a fresh access token from the stored refresh token. Called before
|
|
296
|
+
* each cloud step that needs one. The driver owns the token cache and
|
|
297
|
+
* handles expiry.
|
|
298
|
+
*/
|
|
299
|
+
getAccessToken: () => Promise<string>;
|
|
300
|
+
/**
|
|
301
|
+
* Revoke a Google OAuth refresh token. Best-effort — the core swallows
|
|
302
|
+
* failures (non-fatal; the token expires on its own).
|
|
303
|
+
*/
|
|
304
|
+
revokeToken: (refreshToken: string) => Promise<void>;
|
|
305
|
+
/**
|
|
306
|
+
* List GCP projects the user has access to.
|
|
307
|
+
* Mirrors gcp-api.ts: listProjects(accessToken).
|
|
308
|
+
*/
|
|
309
|
+
listProjects: (accessToken: string) => Promise<GcpProject[]>;
|
|
310
|
+
/**
|
|
311
|
+
* Create a GCP project and wait for the operation to finish.
|
|
312
|
+
* Mirrors gcp-api.ts: createProject(accessToken, projectId, displayName).
|
|
313
|
+
*/
|
|
314
|
+
createProject: (accessToken: string, projectId: string, displayName: string) => Promise<GcpProject>;
|
|
315
|
+
/**
|
|
316
|
+
* Enable an API on a project (idempotent).
|
|
317
|
+
* Mirrors gcp-api.ts: enableService(accessToken, projectId, serviceName).
|
|
318
|
+
*/
|
|
319
|
+
enableService: (accessToken: string, projectId: string, serviceName: string) => Promise<void>;
|
|
320
|
+
/**
|
|
321
|
+
* Find or create the Capgo service account in a project.
|
|
322
|
+
* Mirrors gcp-api.ts: ensureServiceAccount(args).
|
|
323
|
+
*/
|
|
324
|
+
ensureServiceAccount: (args: {
|
|
325
|
+
accessToken: string;
|
|
326
|
+
projectId: string;
|
|
327
|
+
accountId: string;
|
|
328
|
+
displayName?: string;
|
|
329
|
+
description?: string;
|
|
330
|
+
}) => Promise<{
|
|
331
|
+
account: GcpServiceAccount;
|
|
332
|
+
created: boolean;
|
|
333
|
+
}>;
|
|
334
|
+
/**
|
|
335
|
+
* Create a JSON key for a service account.
|
|
336
|
+
* Mirrors gcp-api.ts: createServiceAccountKey(args).
|
|
337
|
+
*/
|
|
338
|
+
createServiceAccountKey: (args: {
|
|
339
|
+
accessToken: string;
|
|
340
|
+
projectId: string;
|
|
341
|
+
serviceAccountEmail: string;
|
|
342
|
+
}) => Promise<GcpServiceAccountKey>;
|
|
343
|
+
/**
|
|
344
|
+
* Invite the service account into the Play Console developer account.
|
|
345
|
+
* Mirrors play-api.ts: inviteServiceAccount(args).
|
|
346
|
+
*/
|
|
347
|
+
inviteServiceAccount: (args: {
|
|
348
|
+
accessToken: string;
|
|
349
|
+
developerId: string;
|
|
350
|
+
serviceAccountEmail: string;
|
|
351
|
+
developerAccountPermissions?: readonly string[];
|
|
352
|
+
grants?: ReadonlyArray<{
|
|
353
|
+
packageName: string;
|
|
354
|
+
permissions: readonly string[];
|
|
355
|
+
}>;
|
|
356
|
+
}) => Promise<void>;
|
|
357
|
+
/**
|
|
358
|
+
* Find applicationId values in the Android Gradle build files.
|
|
359
|
+
* The driver pre-binds `androidDir` so this dep is argless from the core's
|
|
360
|
+
* perspective. The driver calls findAndroidApplicationIds(androidDir) under
|
|
361
|
+
* the hood.
|
|
362
|
+
*/
|
|
363
|
+
findAndroidApplicationIds: () => Promise<string[]>;
|
|
364
|
+
/**
|
|
365
|
+
* Build the CI-secret entries (key/value/masked) from the saved credentials.
|
|
366
|
+
* Mirrors ci-secrets.ts: createCiSecretEntries(credentials, apiKey?).
|
|
367
|
+
*/
|
|
368
|
+
createCiSecretEntries?: (credentials: Partial<BuildCredentials>, apiKey?: string) => CiSecretEntry[];
|
|
369
|
+
/**
|
|
370
|
+
* Detect which CI-secret destinations (GitHub/GitLab) are reachable.
|
|
371
|
+
* Mirrors ci-secrets.ts: detectCiSecretTargets(runner?). The driver pre-binds
|
|
372
|
+
* the command runner, so the core calls this with no args.
|
|
373
|
+
*/
|
|
374
|
+
detectCiSecretTargets?: (runner?: CommandRunner) => CiSecretDiscovery;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve the concrete `owner/repo` (GitHub) or `group/project` (GitLab) the
|
|
377
|
+
* CLI will target, so the user can confirm before any secret is overwritten.
|
|
378
|
+
* Mirrors ci-secrets.ts: getCiSecretRepoLabelAsync(target, runner?).
|
|
379
|
+
*/
|
|
380
|
+
getCiSecretRepoLabelAsync?: (target: CiSecretTarget, runner?: AsyncCommandRunner) => Promise<string | null>;
|
|
381
|
+
/**
|
|
382
|
+
* List which of `keys` already exist as secrets/variables on the remote.
|
|
383
|
+
* Mirrors ci-secrets.ts: listExistingCiSecretKeysAsync(target, keys, runner?).
|
|
384
|
+
*/
|
|
385
|
+
listExistingCiSecretKeysAsync?: (target: CiSecretTarget, keys: string[], runner?: AsyncCommandRunner) => Promise<string[]>;
|
|
386
|
+
/**
|
|
387
|
+
* Push the CI-secret entries to the target, reporting per-key progress.
|
|
388
|
+
* Mirrors ci-secrets.ts: uploadCiSecretsAsync(target, entries, existingKeys?, runner?, onProgress?).
|
|
389
|
+
*/
|
|
390
|
+
uploadCiSecretsAsync?: (target: CiSecretTarget, entries: CiSecretEntry[], existingKeys?: string[], runner?: AsyncCommandRunner, onProgress?: (current: number, total: number, keyName: string) => void) => Promise<void>;
|
|
391
|
+
/**
|
|
392
|
+
* Write the credentials to a local 0o600 `.env` file (no git operation).
|
|
393
|
+
* Mirrors env-export.ts: exportCredentialsToEnv(opts).
|
|
394
|
+
*/
|
|
395
|
+
exportCredentialsToEnv?: (opts: EnvExportOpts) => EnvExportResult;
|
|
396
|
+
/**
|
|
397
|
+
* Resolve the default `.env` export path for an app + platform (pure).
|
|
398
|
+
* Mirrors env-export.ts: defaultExportPath(appId, platform).
|
|
399
|
+
*/
|
|
400
|
+
defaultExportPath?: (appId: string, platform: 'ios' | 'android') => string;
|
|
401
|
+
/**
|
|
402
|
+
* Generate the GitHub Actions workflow YAML (pure).
|
|
403
|
+
* Mirrors workflow-generator.ts: generateWorkflow(opts).
|
|
404
|
+
*/
|
|
405
|
+
generateWorkflow?: (opts: WorkflowGeneratorOpts) => GeneratedWorkflow;
|
|
406
|
+
/**
|
|
407
|
+
* Generate + write the workflow file to `.github/workflows/capgo-build.yml`.
|
|
408
|
+
* Mirrors workflow-writer.ts: writeWorkflowFile(opts, writeOptions?).
|
|
409
|
+
*/
|
|
410
|
+
writeWorkflowFile?: (opts: WorkflowGeneratorOpts, writeOptions?: WorkflowWriteOptions) => WorkflowWriteResult;
|
|
411
|
+
/**
|
|
412
|
+
* Fire the actual `capgo build request`. The driver pre-binds the logger /
|
|
413
|
+
* silent flag it owns; the core supplies appId + options.
|
|
414
|
+
* Mirrors request.ts: requestBuildInternal(appId, options, silent?, logger?).
|
|
415
|
+
*/
|
|
416
|
+
requestBuildInternal?: (appId: string, options: BuildRequestOptions, silent?: boolean, logger?: BuildLogger) => Promise<BuildRequestResult>;
|
|
417
|
+
/**
|
|
418
|
+
* The streaming BuildLogger the TUI threads into requestBuildInternal (the 4th
|
|
419
|
+
* arg) so every build line streams into `setBuildOutput`. Forwarded verbatim.
|
|
420
|
+
*/
|
|
421
|
+
logger?: BuildLogger;
|
|
422
|
+
/**
|
|
423
|
+
* The build VIEWER sink — the android TUI feeds this into `setBuildOutput` (the
|
|
424
|
+
* dedicated build output pane, DISTINCT from the `onLog` side-log). The shared
|
|
425
|
+
* tail writes the build header / blank+queued / ⚠ failure / no-key UX / catch
|
|
426
|
+
* lines here. Forwarded verbatim through `toTailDeps`. No-op when absent.
|
|
427
|
+
*/
|
|
428
|
+
onBuildOutput?: (line: string) => void;
|
|
429
|
+
/**
|
|
430
|
+
* Resolves the Capgo API key the build request should use, mirroring the
|
|
431
|
+
* android tail's CLI-flag-over-saved precedence. Returns undefined when no key
|
|
432
|
+
* is resolvable (the no-key UX finishes at build-complete). Forwarded verbatim.
|
|
433
|
+
*/
|
|
434
|
+
resolveApikey?: () => string | undefined;
|
|
435
|
+
/**
|
|
436
|
+
* Per-key CI-secret upload progress, forwarded as the 5th arg of
|
|
437
|
+
* uploadCiSecretsAsync. The android tail feeds this into
|
|
438
|
+
* `setCiSecretUploadProgress`. No-op when absent.
|
|
439
|
+
*/
|
|
440
|
+
onCiSecretUploadProgress?: (current: number, total: number, keyName: string) => void;
|
|
441
|
+
/**
|
|
442
|
+
* The 2-phase checking-ci-secrets status text. The android tail feeds this into
|
|
443
|
+
* `setCiSecretCheckPhase`. No-op when absent.
|
|
444
|
+
*/
|
|
445
|
+
onCiSecretCheckPhase?: (phase: string) => void;
|
|
446
|
+
/**
|
|
447
|
+
* The ci-secrets-failed reason (repo-null / catch in checking-ci-secrets). The
|
|
448
|
+
* android tail feeds this into `setCiSecretError` (rendered by the
|
|
449
|
+
* CiSecretsFailedStep). Forwarded verbatim through `toTailDeps`. No-op when absent.
|
|
450
|
+
*/
|
|
451
|
+
onCiSecretError?: (message: string) => void;
|
|
452
|
+
/** Reads the project's package.json scripts map (workflow-builder preload). */
|
|
453
|
+
getPackageScripts?: () => Record<string, string>;
|
|
454
|
+
/** Detects the web-framework project type (best-effort; may resolve null). */
|
|
455
|
+
findProjectType?: (options?: {
|
|
456
|
+
quiet?: boolean;
|
|
457
|
+
}) => Promise<string | null>;
|
|
458
|
+
/** Maps a detected project type to its recommended build script name. */
|
|
459
|
+
findBuildCommandForProjectType?: (projectType: string) => Promise<string | null>;
|
|
460
|
+
/**
|
|
461
|
+
* Workflow-file telemetry hook (e.g. 'workflow-file-written'). The android tail
|
|
462
|
+
* calls `trackWorkflowEvent`. No-op when absent.
|
|
463
|
+
*/
|
|
464
|
+
trackWorkflowEvent?: (event: string, options?: {
|
|
465
|
+
decision?: string;
|
|
466
|
+
}) => void;
|
|
467
|
+
/**
|
|
468
|
+
* DRIVER-HELD transient tail state, threaded back into each post-save tail
|
|
469
|
+
* effect. The Ink TUI resolves these ONCE (at `saving-credentials`) and keeps
|
|
470
|
+
* them in React state (`savedCredentials` / `ciSecretEntries` /
|
|
471
|
+
* `ciSecretExistingKeys`); a headless driver mirrors that by capturing the
|
|
472
|
+
* matching `AndroidEffectResult.transient` from each effect and passing it
|
|
473
|
+
* back here on the NEXT effect. The engine NEVER persists these to
|
|
474
|
+
* progress.json — they are secrets/credentials/entries that must stay in
|
|
475
|
+
* memory only. When a field is absent (e.g. a crash-recovery resume where the
|
|
476
|
+
* driver lost its in-memory state) the effect falls back to a SINGLE lossy
|
|
477
|
+
* re-derivation from progress (rebuildTailCredentials / createCiSecretEntries)
|
|
478
|
+
* rather than resolving the Capgo API key a second time.
|
|
479
|
+
*/
|
|
480
|
+
carried?: {
|
|
481
|
+
/** Full saved credentials map written at saving-credentials (5 fields, no CAPGO_TOKEN). */
|
|
482
|
+
savedCredentials?: Record<string, string>;
|
|
483
|
+
/** CI-secret entries resolved ONCE at saving-credentials (creds + Capgo API key → CAPGO_TOKEN). */
|
|
484
|
+
ciSecretEntries?: CiSecretEntry[];
|
|
485
|
+
/** Which secret keys already exist on the remote, resolved at checking-ci-secrets. */
|
|
486
|
+
ciSecretExistingKeys?: string[];
|
|
487
|
+
/** Whether the workflow file did NOT exist at preview (app.tsx's `previewIsNew`); drives 'Wrote' vs 'Overwrote'. Defaults to NEW when absent. */
|
|
488
|
+
workflowIsNew?: boolean;
|
|
489
|
+
};
|
|
490
|
+
onStatus?: (message: string) => void;
|
|
491
|
+
onLog?: (message: string, color?: string) => void;
|
|
492
|
+
/** Internal-only diagnostic line → the support internal log (main PR #2406). Optional; no-op when absent. */
|
|
493
|
+
onInternalLog?: (line: string) => void;
|
|
494
|
+
onAuthUrl?: (url: string) => void;
|
|
495
|
+
signal?: AbortSignal;
|
|
496
|
+
}
|
|
497
|
+
export interface AndroidEffectResult {
|
|
498
|
+
/** Updated progress after the effect ran (matches what was persisted). */
|
|
499
|
+
progress: AndroidOnboardingProgress;
|
|
500
|
+
/** Explicit next step when not derivable from progress alone (★ transitions). */
|
|
501
|
+
next?: AndroidOnboardingStep;
|
|
502
|
+
/** Transient runtime data that lives in the driver but is NOT persisted. */
|
|
503
|
+
transient?: Partial<AndroidStepCtx>;
|
|
504
|
+
}
|
|
505
|
+
export declare function runAndroidEffect(step: AndroidOnboardingStep, progress: AndroidOnboardingProgress, deps: AndroidEffectDeps): Promise<AndroidEffectResult>;
|
|
@@ -20,6 +20,24 @@ export interface KeystoreResult {
|
|
|
20
20
|
alias: string;
|
|
21
21
|
notAfter: Date;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize a keystore alias for use as an on-disk filename component.
|
|
25
|
+
*
|
|
26
|
+
* The alias originates from user input (e.g. `keystoreNewAlias`). It is used
|
|
27
|
+
* verbatim for the keystore crypto and the saved `KEYSTORE_KEY_ALIAS`, but the
|
|
28
|
+
* value used to build the `<alias>.p12` filename must be sanitized so a value
|
|
29
|
+
* like `../../evil` or `/etc/x` cannot escape the target directory.
|
|
30
|
+
*
|
|
31
|
+
* Rules:
|
|
32
|
+
* - strip any directory components (keep only the basename),
|
|
33
|
+
* - allow only `[A-Za-z0-9._-]`, replacing any other char with `_`,
|
|
34
|
+
* - normalize empty or dot-only results (``, `.`, `..`) to a safe default,
|
|
35
|
+
* - the return value never contains `/`, `\`, or `..`.
|
|
36
|
+
*
|
|
37
|
+
* IMPORTANT: this is ONLY for the filename. Do NOT use it for the crypto alias
|
|
38
|
+
* or the saved key alias — those must stay exactly what the user chose.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sanitizeKeystoreAlias(alias: string): string;
|
|
23
41
|
/**
|
|
24
42
|
* Generate a URL-safe random password suitable for Android keystore use.
|
|
25
43
|
* 24 bytes → 32-char base64url string. Collision-resistant, never written in logs.
|
|
@@ -123,12 +123,43 @@ export interface LoopbackCallbackResult {
|
|
|
123
123
|
*/
|
|
124
124
|
finishResponse: (html: string, statusCode?: number) => void;
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* The shape returned by `startOAuthFlow`. The loopback server is already
|
|
128
|
+
* running and the browser has been opened; the caller awaits `result` at a
|
|
129
|
+
* later point (fire-and-poll pattern for MCP).
|
|
130
|
+
*/
|
|
131
|
+
export interface PendingOAuthSession {
|
|
132
|
+
/** Full Google authorization URL that was opened in the browser. */
|
|
133
|
+
authUrl: string;
|
|
134
|
+
/** The loopback redirect URI embedded in the authUrl. */
|
|
135
|
+
redirectUri: string;
|
|
136
|
+
/**
|
|
137
|
+
* Resolves with validated tokens once the browser callback lands,
|
|
138
|
+
* code is exchanged and scopes pass. Rejects on error/timeout/missing-scopes.
|
|
139
|
+
*/
|
|
140
|
+
result: Promise<GoogleOAuthTokens>;
|
|
141
|
+
/** Force-close the loopback server (safe after result settles). */
|
|
142
|
+
close: () => void;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Non-blocking OAuth starter: opens the browser and starts the loopback
|
|
146
|
+
* listener, then returns IMMEDIATELY without waiting for the sign-in to
|
|
147
|
+
* complete. The caller can `await session.result` later to collect tokens.
|
|
148
|
+
*
|
|
149
|
+
* This is the foundation for the MCP fire-and-poll sign-in model. The Ink
|
|
150
|
+
* wizard uses `runOAuthFlow` (which internally calls this and then awaits
|
|
151
|
+
* `result`) to preserve its blocking behavior.
|
|
152
|
+
*/
|
|
153
|
+
export declare function startOAuthFlow(config: GoogleOAuthConfig, options?: RunOAuthFlowOptions): Promise<PendingOAuthSession>;
|
|
126
154
|
/**
|
|
127
155
|
* Run the full browser-based OAuth flow and return tokens.
|
|
128
156
|
*
|
|
129
157
|
* Side effects:
|
|
130
158
|
* - Opens a browser window at Google's consent screen.
|
|
131
159
|
* - Starts (and later stops) a loopback HTTP server on 127.0.0.1.
|
|
160
|
+
*
|
|
161
|
+
* Delegates to `startOAuthFlow` and awaits the result — preserving the
|
|
162
|
+
* original blocking behavior Ink depends on.
|
|
132
163
|
*/
|
|
133
164
|
export declare function runOAuthFlow(config: GoogleOAuthConfig, options?: RunOAuthFlowOptions): Promise<GoogleOAuthTokens>;
|
|
134
165
|
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
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"];
|