@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
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import type { Buffer } from 'node:buffer';
|
|
2
|
+
import type { AscApp, AscDistributionCert, AscProfileSummary } from '../apple-api.js';
|
|
3
|
+
import type { ApiKeyData, CertificateData, EnrichedIdentityAvailability, OnboardingProgress, OnboardingStep, ProfileData } from '../types.js';
|
|
4
|
+
import type { AsyncCommandRunner, CiSecretDiscovery, CiSecretEntry, CiSecretTarget, CommandRunner } from '../ci-secrets.js';
|
|
5
|
+
import type { DiscoveredProfile, ExportedP12, IdentityProfileMatch, SigningIdentity } from '../macos-signing.js';
|
|
6
|
+
import type { MobileprovisionDetail } from '../../mobileprovision-parser.js';
|
|
7
|
+
import type { BuildCredentials } from '../../../schemas/build.js';
|
|
8
|
+
import type { BuildLogger, BuildRequestOptions, BuildRequestResult } from '../../request.js';
|
|
9
|
+
import type { EnvExportOpts, EnvExportResult } from '../env-export.js';
|
|
10
|
+
import type { GeneratedWorkflow, WorkflowGeneratorOpts } from '../workflow-generator.js';
|
|
11
|
+
import type { WorkflowWriteOptions, WorkflowWriteResult } from '../workflow-writer.js';
|
|
12
|
+
import type { TailTransient } from '../tail/flow.js';
|
|
13
|
+
import type { AppVerifyResult, AscAppLike, GatePath } from '../app-verification.js';
|
|
14
|
+
import type { DetectedBundleIds } from '../bundle-id-detector.js';
|
|
15
|
+
/**
|
|
16
|
+
* Stable reason an identity has no usable matching profile. Drives the
|
|
17
|
+
* `import-no-match-recovery` menu variant. (Mirrors the `noMatchReason` enum.)
|
|
18
|
+
*/
|
|
19
|
+
export type IosNoMatchReason = 'apple-no-cert-match' | 'apple-no-profiles-linked' | 'apple-bundle-mismatch' | 'apple-distribution-mismatch' | 'apple-other' | 'no-profile-on-disk';
|
|
20
|
+
/**
|
|
21
|
+
* A duplicate Capgo provisioning profile (creating-profile / import-create).
|
|
22
|
+
* Matches the `{ id, name, profileType }` triple returned by apple-api's
|
|
23
|
+
* findCapgoProfiles() and carried on DuplicateProfileError.profiles. Derived
|
|
24
|
+
* from the real AscProfileSummary so it tracks any future field additions.
|
|
25
|
+
*/
|
|
26
|
+
export type IosDuplicateProfile = Pick<AscProfileSummary, 'id' | 'name' | 'profileType'>;
|
|
27
|
+
export type IosStepKind = 'auto' | 'input' | 'choice' | 'done' | 'error';
|
|
28
|
+
export interface IosStepOption {
|
|
29
|
+
value: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
note?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface IosStepView {
|
|
34
|
+
step: OnboardingStep;
|
|
35
|
+
kind: IosStepKind;
|
|
36
|
+
title?: string;
|
|
37
|
+
prompt?: string;
|
|
38
|
+
collect?: string[];
|
|
39
|
+
options?: IosStepOption[];
|
|
40
|
+
message?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Per-step runtime context the driver supplies to the view builder AND threads
|
|
44
|
+
* back through `IosEffectResult.transient` between effects. EVERY field is
|
|
45
|
+
* OPTIONAL so a caller that only passes `{ appId }` still gets a usable view.
|
|
46
|
+
*
|
|
47
|
+
* This is the iOS "ephemeral inventory" — driver-held transient state that is
|
|
48
|
+
* NEVER persisted to progress.json (it carries Apple-side selections + raw
|
|
49
|
+
* cert/profile/keychain payloads). The total resume function (getIosResumeStep)
|
|
50
|
+
* NEVER produces a step that depends on these — on resume the driver re-runs the
|
|
51
|
+
* silent inventory (import-scanning) and re-renders the picker. See the audit's
|
|
52
|
+
* "Ephemeral inventory" section for the producer/consumer map.
|
|
53
|
+
*/
|
|
54
|
+
export interface IosStepCtx {
|
|
55
|
+
appId?: string;
|
|
56
|
+
/** Selected signing identity (import-pick-identity). REQUIRED by import-exporting. */
|
|
57
|
+
chosenIdentity?: SigningIdentity;
|
|
58
|
+
/** Selected provisioning profile (import-pick-profile). REQUIRED by import-exporting. */
|
|
59
|
+
chosenProfile?: DiscoveredProfile;
|
|
60
|
+
/** Discovery result list from import-scanning (identities + on-disk profiles). */
|
|
61
|
+
importMatches?: IdentityProfileMatch[];
|
|
62
|
+
/** Scanned on-disk profiles (paired with importMatches). */
|
|
63
|
+
importProfiles?: DiscoveredProfile[];
|
|
64
|
+
/** Per-identity Apple-side availability (import-validating-all-certs). */
|
|
65
|
+
identityAvailability?: Record<string, EnrichedIdentityAvailability>;
|
|
66
|
+
/** Per-identity prefetched Apple profiles (parallel prefetch after validation). */
|
|
67
|
+
profilePrefetch?: Record<string, DiscoveredProfile[]>;
|
|
68
|
+
/** Apple cert resource id for the chosen identity (import-checking-apple-cert). */
|
|
69
|
+
_appleCertIdForChosen?: string;
|
|
70
|
+
/** Why the chosen identity has no usable profile — drives the recovery menu. */
|
|
71
|
+
noMatchReason?: IosNoMatchReason;
|
|
72
|
+
/** Duplicate Capgo profiles (creating-profile / import-create-profile-only). */
|
|
73
|
+
duplicateProfiles?: IosDuplicateProfile[];
|
|
74
|
+
/** Existing Apple certs offered for revocation when the cert limit is hit. */
|
|
75
|
+
existingCerts?: AscDistributionCert[];
|
|
76
|
+
/** The user's revoke selection (cert-limit-prompt → revoking-certificate). */
|
|
77
|
+
certToRevoke?: AscDistributionCert;
|
|
78
|
+
/**
|
|
79
|
+
* Whether the host can show a native file picker — gates the
|
|
80
|
+
* import-no-match-recovery / import-portal-explanation "use a .mobileprovision
|
|
81
|
+
* from disk" option (the TUI's canUseFilePicker(), app.tsx:3570/3733). The
|
|
82
|
+
* DRIVER threads the host capability here; the view defaults it to true (the
|
|
83
|
+
* macOS-first onboarding target) so a caller that only passes { appId } still
|
|
84
|
+
* gets the file-picker recovery option.
|
|
85
|
+
*/
|
|
86
|
+
canUseFilePicker?: boolean;
|
|
87
|
+
/** Resolved certificate data (creating-profile create-new / import-exporting). */
|
|
88
|
+
certData?: CertificateData;
|
|
89
|
+
/** Resolved profile data (creating-profile create-new / import-exporting). */
|
|
90
|
+
profileData?: ProfileData;
|
|
91
|
+
/** Apple team id resolved alongside certData/profileData. */
|
|
92
|
+
teamId?: string;
|
|
93
|
+
/** Keychain export password (import-exporting). Transient only. */
|
|
94
|
+
importedP12Password?: string;
|
|
95
|
+
/** Buffer of .p8 file content during validation (only the PATH is persisted). */
|
|
96
|
+
p8Content?: Buffer;
|
|
97
|
+
/**
|
|
98
|
+
* True once the p8-method-select file-picker effect has opened the native
|
|
99
|
+
* dialog this drive — the engine returns it in transient and the driver
|
|
100
|
+
* threads it back as `deps.carried.pickerOpened` so a re-render does NOT
|
|
101
|
+
* re-open the picker. Mirrors the TUI's `pickerOpenedRef` guard.
|
|
102
|
+
*/
|
|
103
|
+
pickerOpened?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* True once the import-provide-profile-path .mobileprovision picker has opened
|
|
106
|
+
* the native dialog this drive — returned in transient and threaded back as
|
|
107
|
+
* `deps.carried.profilePickerOpened` so a re-render does NOT re-open the
|
|
108
|
+
* picker. SEPARATE from `pickerOpened` (the .p8 picker). Mirrors the TUI's
|
|
109
|
+
* `mobileprovisionPickerOpenedRef` guard (app.tsx:1689).
|
|
110
|
+
*/
|
|
111
|
+
profilePickerOpened?: boolean;
|
|
112
|
+
/** Verified key id + issuer id (mirror of completedSteps.apiKeyVerified). */
|
|
113
|
+
apiKey?: ApiKeyData;
|
|
114
|
+
/** ASC apps fetched by the verify-app effect (picker source + Path B re-poll). */
|
|
115
|
+
verifyApps?: AscApp[];
|
|
116
|
+
/** Registered Developer-portal bundle ids (diagnostic — sharpens Path B wording). */
|
|
117
|
+
verifyRegisteredIds?: string[];
|
|
118
|
+
/** The authoritative Release build id, re-detected FRESH from disk ('' = unresolved). */
|
|
119
|
+
verifyReleaseBundleId?: string;
|
|
120
|
+
/** The Debug-config bundle id when it differs from Release (else ''). */
|
|
121
|
+
verifyDebugBundleId?: string;
|
|
122
|
+
/** True when Debug + Release literal ids both exist AND differ (awareness note + telemetry). */
|
|
123
|
+
verifyDebugReleaseDiffer?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* The verify-app classification (the pure classifyAppVerification result),
|
|
126
|
+
* widened with the two pass-through outcomes ('fetch-failed' /
|
|
127
|
+
* 'no-release-config') so the driver's Result telemetry mirrors the TUI's.
|
|
128
|
+
* Present once the initial fetch has run — its absence is what makes
|
|
129
|
+
* iosViewForStep render verify-app as an AUTO effect instead of the gate.
|
|
130
|
+
*/
|
|
131
|
+
verifyResult?: AppVerifyResult | 'fetch-failed' | 'no-release-config';
|
|
132
|
+
/** Which gate path the user is on (null = the picker). */
|
|
133
|
+
verifyPath?: GatePath | null;
|
|
134
|
+
/** The existing app picked in Path A (its bundleId is the target to match). */
|
|
135
|
+
verifyChosenApp?: AscAppLike | null;
|
|
136
|
+
/** 1-based count of blocked Continue attempts (drives the escalating warning). */
|
|
137
|
+
verifyAttempt?: number;
|
|
138
|
+
/** Path B: ask before re-opening the browser after a blocked re-poll. */
|
|
139
|
+
verifyAskReopen?: boolean;
|
|
140
|
+
/** Where verify-app routes on pass/pass-through (set by verifying-key on import). */
|
|
141
|
+
pendingVerifyNext?: OnboardingStep;
|
|
142
|
+
/** Human-readable error message the error view renders (failing step's message). */
|
|
143
|
+
error?: string;
|
|
144
|
+
/** The step to re-run when the user picks "Try again" (absent = no retry offered). */
|
|
145
|
+
retryStep?: OnboardingStep;
|
|
146
|
+
ciSecretEntries?: TailTransient['ciSecretEntries'];
|
|
147
|
+
savedCredentials?: TailTransient['savedCredentials'];
|
|
148
|
+
ciSecretTargets?: TailTransient['ciSecretTargets'];
|
|
149
|
+
ciSecretSetupAdvice?: TailTransient['ciSecretSetupAdvice'];
|
|
150
|
+
ciSecretRepoLabel?: TailTransient['ciSecretRepoLabel'];
|
|
151
|
+
ciSecretExistingKeys?: TailTransient['ciSecretExistingKeys'];
|
|
152
|
+
ciSecretUploadSummary?: TailTransient['ciSecretUploadSummary'];
|
|
153
|
+
envExportPath?: TailTransient['envExportPath'];
|
|
154
|
+
workflowFilePath?: TailTransient['workflowFilePath'];
|
|
155
|
+
buildUrl?: TailTransient['buildUrl'];
|
|
156
|
+
buildOutput?: TailTransient['buildOutput'];
|
|
157
|
+
aiJobId?: TailTransient['aiJobId'];
|
|
158
|
+
availableScripts?: TailTransient['availableScripts'];
|
|
159
|
+
recommendedScript?: TailTransient['recommendedScript'];
|
|
160
|
+
envExportError?: TailTransient['envExportError'];
|
|
161
|
+
ciSecretError?: TailTransient['ciSecretError'];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Async dependencies the iOS effects need (Apple API client, CSR + keychain
|
|
165
|
+
* export, mobileprovision parsing, persistence, the shared tail helpers, and
|
|
166
|
+
* status/log callbacks). EVERY helper is OPTIONAL and ADDITIVE so a driver can
|
|
167
|
+
* inject only what the path it drives needs and the skeleton's stubs keep
|
|
168
|
+
* type-checking. Data types are the REAL exports from apple-api / macos-signing /
|
|
169
|
+
* mobileprovision-parser / csr; only the call-shape envelopes are engine-local.
|
|
170
|
+
*/
|
|
171
|
+
export interface IosEffectDeps {
|
|
172
|
+
appId?: string;
|
|
173
|
+
/** Verify an ASC API key (keyId + issuerId via the .p8). */
|
|
174
|
+
verifyApiKey?: (args: {
|
|
175
|
+
keyId: string;
|
|
176
|
+
issuerId: string;
|
|
177
|
+
p8Content: Buffer;
|
|
178
|
+
}) => Promise<{
|
|
179
|
+
teamId?: string;
|
|
180
|
+
}>;
|
|
181
|
+
/**
|
|
182
|
+
* Create a distribution certificate from a CSR. Returns the RAW Apple cert
|
|
183
|
+
* response (mirrors the real apple-api `createCertificate` helper): the cert
|
|
184
|
+
* resource id, the base64 DER `certificateContent`, the expiry, and the team
|
|
185
|
+
* id. The engine pairs `certificateContent` + the CSR private key via
|
|
186
|
+
* `createP12` to produce the final .p12 — keeping the IO-free engine in charge
|
|
187
|
+
* of assembling the CertificateData credential. Throws CertificateLimitError
|
|
188
|
+
* (carrying the existing certs) when Apple's per-team cert limit is hit.
|
|
189
|
+
*/
|
|
190
|
+
createCertificate?: (args: {
|
|
191
|
+
csr: string;
|
|
192
|
+
accessToken?: string;
|
|
193
|
+
}) => Promise<{
|
|
194
|
+
certificateId: string;
|
|
195
|
+
certificateContent: string;
|
|
196
|
+
expirationDate: string;
|
|
197
|
+
teamId: string;
|
|
198
|
+
}>;
|
|
199
|
+
/** Revoke an existing certificate (cert-limit recovery). */
|
|
200
|
+
revokeCertificate?: (certificateId: string) => Promise<void>;
|
|
201
|
+
/** Create a provisioning profile for a bundle id + cert. */
|
|
202
|
+
createProfile?: (args: {
|
|
203
|
+
bundleId: string;
|
|
204
|
+
certificateId: string;
|
|
205
|
+
distribution?: string;
|
|
206
|
+
}) => Promise<ProfileData>;
|
|
207
|
+
/** Delete a provisioning profile (duplicate-profile recovery). */
|
|
208
|
+
deleteProfile?: (profileId: string) => Promise<void>;
|
|
209
|
+
/** Resolve the Apple cert resource id from a local cert SHA-1. */
|
|
210
|
+
findCertIdBySha1?: (sha1: string) => Promise<string | null>;
|
|
211
|
+
/** Classify a cert's Apple-side availability (import-validating-all-certs). */
|
|
212
|
+
classifyCertAvailability?: (identity: SigningIdentity) => Promise<EnrichedIdentityAvailability>;
|
|
213
|
+
/** List the team's distribution certificates (cert-limit prompt). */
|
|
214
|
+
listCertificates?: () => Promise<AscDistributionCert[]>;
|
|
215
|
+
/** Check for duplicate Capgo profiles for a bundle id. */
|
|
216
|
+
checkDuplicateProfiles?: (bundleId: string) => Promise<IosDuplicateProfile[]>;
|
|
217
|
+
/** Ensure the bundle id exists on Apple (import-create-profile-only). */
|
|
218
|
+
ensureBundleId?: (bundleId: string) => Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* List the Apple profiles linked to a cert (import-checking-apple-cert).
|
|
221
|
+
*
|
|
222
|
+
* Returns the RAW Apple shape (AscProfileSummary[]) exactly as the real
|
|
223
|
+
* apple-api `listProfilesForCert` helper does — id / name / profileType /
|
|
224
|
+
* profileContent / expirationDate / bundleIdentifier. The engine itself
|
|
225
|
+
* synthesizes each summary into a DiscoveredProfile (populating profileBase64
|
|
226
|
+
* + certificateSha1s=[identity.sha1]) via `synthesizeProfileFromAscSummary`,
|
|
227
|
+
* byte-for-byte mirroring the TUI's inline mapping at app.tsx:1556 / :1460.
|
|
228
|
+
* Keeping the dep at the raw Apple shape means the driver pre-binds nothing
|
|
229
|
+
* more than the real helper.
|
|
230
|
+
*/
|
|
231
|
+
listProfilesForCert?: (certificateId: string) => Promise<AscProfileSummary[]>;
|
|
232
|
+
/** List every ASC app visible to the API key (verify-app fetch + Path B re-poll). */
|
|
233
|
+
listApps?: () => Promise<AscApp[]>;
|
|
234
|
+
/** List every registered bundle-id identifier (verify-app diagnostics). */
|
|
235
|
+
listBundleIds?: () => Promise<string[]>;
|
|
236
|
+
/**
|
|
237
|
+
* FRESH bundle-id detection from disk (verify-app + the Path-A re-check). The
|
|
238
|
+
* driver pre-binds the real `detectIosBundleIds({ cwd, iosDir, capacitorAppId })`
|
|
239
|
+
* — the engine reads `releaseResolved`/`pbxproj` for the authoritative Release
|
|
240
|
+
* id, `debug`/`debugReleaseDiffer` for the awareness note, and `capacitor` for
|
|
241
|
+
* the persisted iosBundleIdContextAppId snapshot. Called PER CHECK so an edit
|
|
242
|
+
* the user made since the wizard started is picked up (the TUI bypasses its
|
|
243
|
+
* memo the same way, app.tsx:1522/3088).
|
|
244
|
+
*/
|
|
245
|
+
detectBundleIds?: () => DetectedBundleIds;
|
|
246
|
+
/**
|
|
247
|
+
* Rewrite the Release PRODUCT_BUNDLE_IDENTIFIER assignments equal to `fromId`
|
|
248
|
+
* to `toId` in the Xcode project (the Path-A auto-fix). The driver pre-binds
|
|
249
|
+
* the real `writeReleaseBundleId(cwd, iosDir, …)`; returns the number of
|
|
250
|
+
* replaced assignments (0 = nothing matched). Throws only on an FS error.
|
|
251
|
+
*/
|
|
252
|
+
writeReleaseBundleId?: (fromId: string, toId: string) => {
|
|
253
|
+
changed: number;
|
|
254
|
+
};
|
|
255
|
+
/** Generate a CSR + private key PEM. */
|
|
256
|
+
generateCsr?: (args?: {
|
|
257
|
+
commonName?: string;
|
|
258
|
+
}) => {
|
|
259
|
+
csr: string;
|
|
260
|
+
privateKeyPem: string;
|
|
261
|
+
};
|
|
262
|
+
/** Build a .p12 from a cert + private key. Returns base64. */
|
|
263
|
+
createP12?: (args: {
|
|
264
|
+
certificatePem: string;
|
|
265
|
+
privateKeyPem: string;
|
|
266
|
+
password: string;
|
|
267
|
+
}) => string;
|
|
268
|
+
/** List the Mac's code-signing identities (import-scanning). */
|
|
269
|
+
listSigningIdentities?: () => Promise<SigningIdentity[]>;
|
|
270
|
+
/** Scan the Mac's on-disk provisioning profiles (import-scanning). */
|
|
271
|
+
scanProvisioningProfiles?: () => Promise<DiscoveredProfile[]>;
|
|
272
|
+
/**
|
|
273
|
+
* Export a .p12 (cert + key) from the Keychain for the chosen identity
|
|
274
|
+
* (import-exporting). Signature mirrors the REAL macos-signing helper VERBATIM:
|
|
275
|
+
* takes the identity's SHA-1 and resolves to { base64, passphrase } (the
|
|
276
|
+
* auto-generated wrap passphrase becomes the transient importedP12Password the
|
|
277
|
+
* saving-credentials handoff reads — NEVER persisted, risk #2 / D-iOS-3).
|
|
278
|
+
*/
|
|
279
|
+
exportP12FromKeychain?: (targetSha1: string) => Promise<ExportedP12>;
|
|
280
|
+
/** Parse a `.mobileprovision` file in detail (import-provide-profile-path). */
|
|
281
|
+
parseMobileprovisionDetailed?: (bytes: Buffer) => MobileprovisionDetail;
|
|
282
|
+
loadProgress?: (appId: string) => Promise<OnboardingProgress | null>;
|
|
283
|
+
saveProgress?: (appId: string, progress: OnboardingProgress) => Promise<void>;
|
|
284
|
+
deleteProgress?: (appId: string) => Promise<void>;
|
|
285
|
+
/** Persist the saved build-credential map (saving-credentials). */
|
|
286
|
+
updateSavedCredentials?: (appId: string, platform: 'ios' | 'android', credentials: Record<string, string>) => Promise<void>;
|
|
287
|
+
loadSavedCredentials?: (appId: string) => Promise<unknown>;
|
|
288
|
+
readFile?: (path: string) => Promise<Buffer>;
|
|
289
|
+
copyFile?: (src: string, dest: string) => Promise<void>;
|
|
290
|
+
/**
|
|
291
|
+
* Open the native .p8 file picker (p8-method-select). Resolves to the chosen
|
|
292
|
+
* absolute path, or null when the user cancels. The driver pre-binds the real
|
|
293
|
+
* `openFilePicker` here; tests inject a canned path/null. Mirrors the TUI's
|
|
294
|
+
* `openFilePicker()` call inside the p8-method-select effect.
|
|
295
|
+
*/
|
|
296
|
+
openP8FilePicker?: () => Promise<string | null>;
|
|
297
|
+
/**
|
|
298
|
+
* Open the native .mobileprovision file picker (import-provide-profile-path).
|
|
299
|
+
* Resolves to the chosen absolute path, or null when the user cancels. The
|
|
300
|
+
* driver pre-binds the real `openMobileprovisionPicker` here; tests inject a
|
|
301
|
+
* canned path/null. Mirrors the TUI's `openMobileprovisionPicker()` call
|
|
302
|
+
* inside the import-provide-profile-path effect (app.tsx:1696). The bytes are
|
|
303
|
+
* then read via `deps.readFile` and parsed via `deps.parseMobileprovisionDetailed`.
|
|
304
|
+
*/
|
|
305
|
+
openProfilePicker?: () => Promise<string | null>;
|
|
306
|
+
/**
|
|
307
|
+
* Whether the host can show a native file picker. Gates the
|
|
308
|
+
* import-no-match-recovery / import-portal-explanation "use a .mobileprovision
|
|
309
|
+
* from disk" option exactly as the TUI's `canUseFilePicker()` does
|
|
310
|
+
* (app.tsx:3570/3733). Defaults to true when omitted (the macOS-first target).
|
|
311
|
+
*/
|
|
312
|
+
canUseFilePicker?: () => boolean;
|
|
313
|
+
/**
|
|
314
|
+
* Open a URL in the host's default browser (import-portal-explanation's
|
|
315
|
+
* "open the portal anyway" branch). Best-effort — the driver pre-binds the
|
|
316
|
+
* real `open` helper; tests inject a recorder/no-op. Mirrors the TUI's
|
|
317
|
+
* `open(...)` call at app.tsx:3749. A failure must NOT abort recovery.
|
|
318
|
+
*/
|
|
319
|
+
openExternal?: (url: string) => Promise<void> | void;
|
|
320
|
+
/**
|
|
321
|
+
* Whether the host is macOS. Gates the post-backup fork: on macOS the user is
|
|
322
|
+
* offered import-vs-create at `setup-method-select`; off-macOS the import
|
|
323
|
+
* sub-flow is unavailable so backing-up routes straight to the create-new
|
|
324
|
+
* `api-key-instructions`. Mirrors the TUI's `isMacOS()` branch. Defaults to
|
|
325
|
+
* true when omitted (the macOS-first onboarding target).
|
|
326
|
+
*/
|
|
327
|
+
isMacOS?: () => boolean;
|
|
328
|
+
createCiSecretEntries?: (credentials: Partial<BuildCredentials>, apiKey?: string) => CiSecretEntry[];
|
|
329
|
+
detectCiSecretTargets?: (runner?: CommandRunner) => CiSecretDiscovery;
|
|
330
|
+
getCiSecretRepoLabelAsync?: (target: CiSecretTarget, runner?: AsyncCommandRunner) => Promise<string | null>;
|
|
331
|
+
listExistingCiSecretKeysAsync?: (target: CiSecretTarget, keys: string[], runner?: AsyncCommandRunner) => Promise<string[]>;
|
|
332
|
+
uploadCiSecretsAsync?: (target: CiSecretTarget, entries: CiSecretEntry[], existingKeys?: string[], runner?: AsyncCommandRunner, onProgress?: (current: number, total: number, keyName: string) => void) => Promise<void>;
|
|
333
|
+
exportCredentialsToEnv?: (opts: EnvExportOpts) => EnvExportResult;
|
|
334
|
+
defaultExportPath?: (appId: string, platform: 'ios' | 'android') => string;
|
|
335
|
+
generateWorkflow?: (opts: WorkflowGeneratorOpts) => GeneratedWorkflow;
|
|
336
|
+
writeWorkflowFile?: (opts: WorkflowGeneratorOpts, writeOptions?: WorkflowWriteOptions) => WorkflowWriteResult;
|
|
337
|
+
requestBuildInternal?: (appId: string, options: BuildRequestOptions, silent?: boolean, logger?: BuildLogger) => Promise<BuildRequestResult>;
|
|
338
|
+
/** The streaming BuildLogger threaded into requestBuildInternal (4th arg). */
|
|
339
|
+
logger?: BuildLogger;
|
|
340
|
+
/** The build VIEWER sink (FullscreenBuildOutput), distinct from onLog. */
|
|
341
|
+
onBuildOutput?: (line: string) => void;
|
|
342
|
+
/** Resolves the Capgo API key for the build request (CLI-flag-over-saved). */
|
|
343
|
+
resolveApikey?: () => string | undefined;
|
|
344
|
+
/** Per-key CI-secret upload progress (uploadCiSecretsAsync 5th arg). */
|
|
345
|
+
onCiSecretUploadProgress?: (current: number, total: number, keyName: string) => void;
|
|
346
|
+
/** The 2-phase checking-ci-secrets status text. */
|
|
347
|
+
onCiSecretCheckPhase?: (phase: string) => void;
|
|
348
|
+
/** The ci-secrets-failed reason. */
|
|
349
|
+
onCiSecretError?: (message: string) => void;
|
|
350
|
+
/** Reads the project's package.json scripts map (with-workflow preload). */
|
|
351
|
+
getPackageScripts?: () => Record<string, string>;
|
|
352
|
+
/** Detects the web-framework project type (best-effort; may resolve null). */
|
|
353
|
+
findProjectType?: (options?: {
|
|
354
|
+
quiet?: boolean;
|
|
355
|
+
}) => Promise<string | null>;
|
|
356
|
+
/** Maps a detected project type to its recommended build script name. */
|
|
357
|
+
findBuildCommandForProjectType?: (projectType: string) => Promise<string | null>;
|
|
358
|
+
/** Workflow-file telemetry hook (e.g. 'workflow-file-written'). */
|
|
359
|
+
trackWorkflowEvent?: (event: string, options?: {
|
|
360
|
+
decision?: string;
|
|
361
|
+
}) => void;
|
|
362
|
+
/**
|
|
363
|
+
* DRIVER-HELD transient tail state threaded back into each post-save effect.
|
|
364
|
+
* The TUI resolves these ONCE (at saving-credentials) and keeps them in React
|
|
365
|
+
* state; a headless driver mirrors that by capturing the matching
|
|
366
|
+
* IosEffectResult.transient and passing it back here on the NEXT effect.
|
|
367
|
+
* NEVER persisted to progress.json. When absent (crash-recovery resume) the
|
|
368
|
+
* effect falls back to a single lossy re-derivation from progress.
|
|
369
|
+
*/
|
|
370
|
+
carried?: {
|
|
371
|
+
savedCredentials?: Record<string, string>;
|
|
372
|
+
ciSecretEntries?: CiSecretEntry[];
|
|
373
|
+
ciSecretExistingKeys?: string[];
|
|
374
|
+
/**
|
|
375
|
+
* Whether the workflow file did NOT exist when previewed (the TUI's
|
|
376
|
+
* `previewIsNew`, resolved at preview-workflow-file via existsSync). The
|
|
377
|
+
* writing-workflow-file effect logs '✔ Wrote' vs '✔ Overwrote' from it.
|
|
378
|
+
* Absent defaults to NEW ('Wrote'). EPHEMERAL — never persisted.
|
|
379
|
+
*/
|
|
380
|
+
workflowIsNew?: boolean;
|
|
381
|
+
/** The chosen signing identity (lossy re-scan source on resume). */
|
|
382
|
+
chosenIdentity?: SigningIdentity;
|
|
383
|
+
/** The chosen provisioning profile (lossy re-scan source on resume). */
|
|
384
|
+
chosenProfile?: DiscoveredProfile;
|
|
385
|
+
/**
|
|
386
|
+
* The import-scanning discovery inventory (identity↔on-disk-profile matches +
|
|
387
|
+
* the raw scanned profiles), threaded forward so the NEXT import effect can
|
|
388
|
+
* read it without a re-scan. Produced by import-scanning into transient; the
|
|
389
|
+
* driver mirrors it back here for import-validating-all-certs (which batches
|
|
390
|
+
* classifyCertAvailability over importMatches) and the pickers. EPHEMERAL —
|
|
391
|
+
* never persisted; on a crash-recovery resume the engine re-lands on
|
|
392
|
+
* import-scanning and re-populates it.
|
|
393
|
+
*/
|
|
394
|
+
importMatches?: IdentityProfileMatch[];
|
|
395
|
+
importProfiles?: DiscoveredProfile[];
|
|
396
|
+
/** Resolved cert/profile/team export payloads carried into saving-credentials. */
|
|
397
|
+
certData?: CertificateData;
|
|
398
|
+
profileData?: ProfileData;
|
|
399
|
+
teamId?: string;
|
|
400
|
+
/**
|
|
401
|
+
* The validated .p8 file content (ASC private key) the driver carries
|
|
402
|
+
* between the .p8 input chain and `verifying-key`. ONLY the p8Path is
|
|
403
|
+
* persisted to progress.json — the raw key bytes ride this transient
|
|
404
|
+
* channel, mirroring the TUI's `p8ContentRef`. The verifying-key effect
|
|
405
|
+
* reads it from here; when absent (crash-recovery resume) it falls back to
|
|
406
|
+
* re-reading the file at `progress.p8Path` via `deps.readFile`.
|
|
407
|
+
*/
|
|
408
|
+
p8Content?: Buffer;
|
|
409
|
+
/**
|
|
410
|
+
* Tracks that the p8-method-select file-picker effect already ran, so a
|
|
411
|
+
* re-render does NOT re-open the native picker. Mirrors the TUI's
|
|
412
|
+
* `pickerOpenedRef`. The driver threads the returned `pickerOpened: true`
|
|
413
|
+
* transient back here on the next call.
|
|
414
|
+
*/
|
|
415
|
+
pickerOpened?: boolean;
|
|
416
|
+
/**
|
|
417
|
+
* Tracks that the import-provide-profile-path .mobileprovision file-picker
|
|
418
|
+
* effect already ran this attempt, so a re-render / re-drive does NOT re-open
|
|
419
|
+
* the native picker. SEPARATE from `pickerOpened` (the .p8 picker guard) so
|
|
420
|
+
* the two file pickers never cross-suppress each other — mirrors the TUI's
|
|
421
|
+
* distinct `mobileprovisionPickerOpenedRef` (app.tsx:1689). The driver threads
|
|
422
|
+
* the returned `profilePickerOpened: true` transient back here; it RESETS the
|
|
423
|
+
* flag (to false) before routing into import-provide-profile-path from the
|
|
424
|
+
* recovery menu, exactly as the TUI clears the ref at app.tsx:3593.
|
|
425
|
+
*/
|
|
426
|
+
profilePickerOpened?: boolean;
|
|
427
|
+
/**
|
|
428
|
+
* Keychain export passphrase for the IMPORT path's .p12 (import-exporting).
|
|
429
|
+
* Transient only — the import-exporting effect never persists it, so the
|
|
430
|
+
* saving-credentials handoff reads it from carried. Absent on the create-new
|
|
431
|
+
* path (which uses the well-known DEFAULT_P12_PASSWORD) and on a crash-recovery
|
|
432
|
+
* resume that lost the in-memory state.
|
|
433
|
+
*/
|
|
434
|
+
importedP12Password?: string;
|
|
435
|
+
/**
|
|
436
|
+
* The cert the user picked at `cert-limit-prompt` (cert-limit recovery). The
|
|
437
|
+
* choice is EPHEMERAL — `applyIosInput` persists nothing; the driver records
|
|
438
|
+
* the picked AscDistributionCert here and re-drives the prompt as a resolver
|
|
439
|
+
* effect, exactly as the TUI stashes `certToRevoke` in React state before
|
|
440
|
+
* advancing to `revoking-certificate` (app.tsx:3923). The resolver returns
|
|
441
|
+
* `revoking-certificate` when present, `error` when absent (the user exited).
|
|
442
|
+
* Mirrors the BATCH 2 ephemeral-branching mechanism (`pickerOpened` /
|
|
443
|
+
* `chosenIdentity`): the selection lives in carried, never in progress.json.
|
|
444
|
+
*/
|
|
445
|
+
certToRevoke?: AscDistributionCert;
|
|
446
|
+
/**
|
|
447
|
+
* The duplicate Capgo profiles surfaced at `duplicate-profile-prompt`
|
|
448
|
+
* (duplicate-profile recovery). Produced by `creating-profile` /
|
|
449
|
+
* `import-create-profile-only` into transient; the driver threads the list
|
|
450
|
+
* back here so `deleting-duplicate-profiles` knows which profiles to delete.
|
|
451
|
+
* NEVER persisted (only `duplicateProfileOrigin` is — see types.ts).
|
|
452
|
+
*/
|
|
453
|
+
duplicateProfiles?: IosDuplicateProfile[];
|
|
454
|
+
/**
|
|
455
|
+
* The user's confirm/exit decision at `duplicate-profile-prompt`. EPHEMERAL —
|
|
456
|
+
* `applyIosInput` persists nothing (per the audit's sequencing model); the
|
|
457
|
+
* driver records the choice here and re-drives the prompt as a resolver
|
|
458
|
+
* effect. `true` → `deleting-duplicate-profiles`; falsy (the user exited) →
|
|
459
|
+
* `error` (mirroring app.tsx:3942's delete-vs-exitOnboarding branch).
|
|
460
|
+
*/
|
|
461
|
+
confirmDeleteDuplicates?: boolean;
|
|
462
|
+
/**
|
|
463
|
+
* The user's pick at `import-no-match-recovery` (the 5-way HUB). EPHEMERAL —
|
|
464
|
+
* `applyIosInput` persists nothing; the driver records the choice here and
|
|
465
|
+
* re-drives the prompt as a resolver effect. 'create' →
|
|
466
|
+
* import-create-profile-only (with an ASC key) or api-key-instructions
|
|
467
|
+
* (without); 'provide-profile-path' → import-provide-profile-path; 'browser'
|
|
468
|
+
* → import-portal-explanation; 'back' → import-pick-identity. Mirrors the
|
|
469
|
+
* TUI's recovery-menu onChange (app.tsx:3579).
|
|
470
|
+
*/
|
|
471
|
+
recoveryAction?: 'create' | 'provide-profile-path' | 'browser' | 'back';
|
|
472
|
+
/**
|
|
473
|
+
* The user's pick at `import-portal-explanation` (the manual-portal
|
|
474
|
+
* walkthrough). EPHEMERAL — the driver records the choice here and re-drives
|
|
475
|
+
* the step as a resolver. 'use-create' → import-create-profile-only;
|
|
476
|
+
* 'use-file' → import-provide-profile-path; 'open-anyway' / 'back' →
|
|
477
|
+
* import-no-match-recovery. Mirrors app.tsx:3738.
|
|
478
|
+
*/
|
|
479
|
+
portalAction?: 'use-create' | 'open-anyway' | 'use-file' | 'back';
|
|
480
|
+
/**
|
|
481
|
+
* The user's pick at `import-export-warning` (the heads-up before the one
|
|
482
|
+
* Keychain dialog). EPHEMERAL — `applyIosInput` persists nothing; the driver
|
|
483
|
+
* records the choice here and re-drives the step as a resolver. 'go' →
|
|
484
|
+
* import-exporting (the precompiled signed helper is resolved + verified in
|
|
485
|
+
* the export step itself — PR #2458 removed the swiftc compile step);
|
|
486
|
+
* 'back' → import-pick-profile; 'exit'/absent → exit onboarding. Mirrors
|
|
487
|
+
* app.tsx:3769 onChange.
|
|
488
|
+
*/
|
|
489
|
+
exportWarningAction?: 'go' | 'back' | 'exit';
|
|
490
|
+
/**
|
|
491
|
+
* The STICKY no-match reason set by the step that ROUTED into recovery
|
|
492
|
+
* (import-pick-identity / import-checking-apple-cert). The recovery resolver
|
|
493
|
+
* + the import-provide-profile-path cancel branch thread it back so a
|
|
494
|
+
* re-entry from a file-picker cancel / portal "open anyway" does NOT
|
|
495
|
+
* recompute or overwrite it (risk #8) — the menu keeps showing the SAME
|
|
496
|
+
* variant. Mirrors the TUI leaving `noMatchReason` untouched on back-nav.
|
|
497
|
+
*/
|
|
498
|
+
noMatchReason?: IosNoMatchReason;
|
|
499
|
+
/**
|
|
500
|
+
* The step to re-run when the user picks "Try again" on the error screen
|
|
501
|
+
* (BATCH 8). EPHEMERAL — set by the failing effect into transient.retryStep,
|
|
502
|
+
* threaded back here by the driver, and read by the error RESOLVER
|
|
503
|
+
* (runIosEffect('error')) to route a retry. NEVER persisted: an error is
|
|
504
|
+
* transient runtime state, so a crash-recovery resume re-enters the failing
|
|
505
|
+
* phase fresh (getIosResumeStep never returns 'error'). Mirrors the TUI's
|
|
506
|
+
* setRetryStep + the ErrorStep 'retry' branch (app.tsx:1116 / 4468).
|
|
507
|
+
*/
|
|
508
|
+
retryStep?: OnboardingStep;
|
|
509
|
+
/**
|
|
510
|
+
* The user's pick on the error screen (BATCH 8). EPHEMERAL — `applyIosInput`
|
|
511
|
+
* persists nothing; the driver records the choice here and re-drives the step
|
|
512
|
+
* as a resolver. 'retry' → re-run carried.retryStep (the failing step);
|
|
513
|
+
* 'restart' → welcome (a fresh reset); 'exit'/absent → stay on 'error' (the
|
|
514
|
+
* terminal exit sink — the driver leaves onboarding, mirroring the TUI's
|
|
515
|
+
* exitOnboarding at app.tsx:4482). NEVER persisted.
|
|
516
|
+
*/
|
|
517
|
+
errorAction?: 'retry' | 'restart' | 'exit';
|
|
518
|
+
/**
|
|
519
|
+
* Where verify-app routes once the invariant holds (or on a pass-through
|
|
520
|
+
* exit): the import continuation (import-validating-all-certs /
|
|
521
|
+
* import-pick-identity) on the import app_store path, absent on create-new
|
|
522
|
+
* (verify-app falls back to 'creating-certificate'). Produced by
|
|
523
|
+
* verifying-key into transient.pendingVerifyNext; the driver threads it back
|
|
524
|
+
* here. NEVER persisted — a fresh mount has none, so a resume re-entering
|
|
525
|
+
* verify-app always falls back to creating-certificate (matching the TUI's
|
|
526
|
+
* pendingVerifyNext React state + getResumeStep's verify-app comment).
|
|
527
|
+
*/
|
|
528
|
+
pendingVerifyNext?: OnboardingStep;
|
|
529
|
+
/**
|
|
530
|
+
* The user's pick on the PARKED verify-app step (the picker or one of the
|
|
531
|
+
* two gates). EPHEMERAL — `applyIosInput` persists nothing; the driver
|
|
532
|
+
* records the pick here and re-drives verify-app as a resolver effect:
|
|
533
|
+
* 'pick' (+ verifyChosenApp) / 'create-new' route the picker; 'autofix' /
|
|
534
|
+
* 'continue' drive the Path-A fix-build-id gate; 'recheck' / 'open' /
|
|
535
|
+
* 'reopen' drive the Path-B create-app gate; 'back' resets to the picker;
|
|
536
|
+
* 'cancel' exits via the error sink. Mirrors the TUI Select onChange values
|
|
537
|
+
* (app.tsx:3246/3283/3323/3360). The driver MUST clear it after each
|
|
538
|
+
* resolver run so a later re-entry runs the initial fetch.
|
|
539
|
+
*/
|
|
540
|
+
verifyAction?: 'pick' | 'create-new' | 'autofix' | 'continue' | 'recheck' | 'open' | 'reopen' | 'back' | 'cancel';
|
|
541
|
+
/** The existing ASC app picked in the verify-app picker (Path A target). */
|
|
542
|
+
verifyChosenApp?: AscAppLike | null;
|
|
543
|
+
/** The ASC apps fetched by the initial verify-app effect (picker source + re-poll). */
|
|
544
|
+
verifyApps?: AscApp[];
|
|
545
|
+
/** Registered Developer-portal bundle ids (Path B wording sharpener). */
|
|
546
|
+
verifyRegisteredIds?: string[];
|
|
547
|
+
/** The authoritative Release build id resolved by the verify-app fresh detect. */
|
|
548
|
+
verifyReleaseBundleId?: string;
|
|
549
|
+
/** The Debug-config bundle id when it differs from Release (else ''). */
|
|
550
|
+
verifyDebugBundleId?: string;
|
|
551
|
+
/** Which gate path the user is on (null = the picker). */
|
|
552
|
+
verifyPath?: GatePath | null;
|
|
553
|
+
/** 1-based count of blocked Continue attempts (the escalation driver). */
|
|
554
|
+
verifyAttempt?: number;
|
|
555
|
+
/** Path B: ask before re-opening the browser after a blocked re-poll. */
|
|
556
|
+
verifyAskReopen?: boolean;
|
|
557
|
+
};
|
|
558
|
+
onStatus?: (message: string) => void;
|
|
559
|
+
onLog?: (message: string, color?: string) => void;
|
|
560
|
+
/** Internal-only diagnostic line → the support internal log (main PR #2406). Optional; no-op when absent. */
|
|
561
|
+
onInternalLog?: (line: string) => void;
|
|
562
|
+
signal?: AbortSignal;
|
|
563
|
+
}
|
|
564
|
+
export interface IosEffectResult {
|
|
565
|
+
/** Updated progress after the effect ran (matches what was persisted). */
|
|
566
|
+
progress: OnboardingProgress;
|
|
567
|
+
/** Explicit next step when not derivable from progress alone (★ transitions). */
|
|
568
|
+
next?: OnboardingStep;
|
|
569
|
+
/** Transient runtime data that lives in the driver but is NOT persisted. */
|
|
570
|
+
transient?: Partial<IosStepCtx>;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The create-new choice/input vocabulary. Mirrors android's `AndroidInput`:
|
|
574
|
+
* one variant per choice/input step that records (or routes) state. The iOS
|
|
575
|
+
* `applyIosInput` signature still accepts `unknown`, so callers cast to this.
|
|
576
|
+
* Navigation-only choices (api-key-instructions) are included for completeness
|
|
577
|
+
* but return progress unchanged.
|
|
578
|
+
*/
|
|
579
|
+
export type IosInput = {
|
|
580
|
+
step: 'setup-method-select';
|
|
581
|
+
value: 'create' | 'import';
|
|
582
|
+
} | {
|
|
583
|
+
step: 'api-key-instructions';
|
|
584
|
+
value: 'picker' | 'manual';
|
|
585
|
+
} | {
|
|
586
|
+
step: 'input-p8-path';
|
|
587
|
+
value: string;
|
|
588
|
+
} | {
|
|
589
|
+
step: 'input-key-id';
|
|
590
|
+
value: string;
|
|
591
|
+
} | {
|
|
592
|
+
step: 'input-issuer-id';
|
|
593
|
+
value: string;
|
|
594
|
+
} | {
|
|
595
|
+
step: 'cert-limit-prompt';
|
|
596
|
+
value: string;
|
|
597
|
+
} | {
|
|
598
|
+
step: 'duplicate-profile-prompt';
|
|
599
|
+
value: 'delete' | 'exit';
|
|
600
|
+
} | {
|
|
601
|
+
step: 'verify-app';
|
|
602
|
+
value: string;
|
|
603
|
+
} | {
|
|
604
|
+
step: 'import-distribution-mode';
|
|
605
|
+
value: 'app_store' | 'ad_hoc' | '__cancel__';
|
|
606
|
+
} | {
|
|
607
|
+
step: 'import-pick-identity';
|
|
608
|
+
value: string;
|
|
609
|
+
} | {
|
|
610
|
+
step: 'import-pick-profile';
|
|
611
|
+
value: string;
|
|
612
|
+
} | {
|
|
613
|
+
step: 'import-no-match-recovery';
|
|
614
|
+
value: 'create' | 'provide-profile-path' | 'browser' | 'back';
|
|
615
|
+
} | {
|
|
616
|
+
step: 'import-portal-explanation';
|
|
617
|
+
value: 'use-create' | 'open-anyway' | 'use-file' | 'back';
|
|
618
|
+
} | {
|
|
619
|
+
step: 'import-export-warning';
|
|
620
|
+
value: 'go' | 'back' | 'exit';
|
|
621
|
+
};
|
|
622
|
+
/**
|
|
623
|
+
* Build the view-model for a given step. Post-save tail steps delegate to the
|
|
624
|
+
* shared neutral view (adapted back to IosStepView). The create-new choice/input
|
|
625
|
+
* steps (setup-method fork + .p8 chain) return real per-step views mirroring the
|
|
626
|
+
* TUI prompts/options (ui/steps/ios-credentials.tsx). All other steps return a
|
|
627
|
+
* minimal placeholder 'auto' view echoing the step (real per-step views land in
|
|
628
|
+
* later batches).
|
|
629
|
+
*/
|
|
630
|
+
export declare function iosViewForStep(step: OnboardingStep, progress: OnboardingProgress, ctx?: IosStepCtx): IosStepView;
|
|
631
|
+
/**
|
|
632
|
+
* Apply a user input to progress. Post-save tail choice/input steps delegate
|
|
633
|
+
* the reducer to the shared neutral module. The create-new choice/input steps
|
|
634
|
+
* persist their field(s) exactly as the TUI's onSubmit/onChange handlers do
|
|
635
|
+
* (ui/app.tsx). All other steps return progress unchanged (real per-step
|
|
636
|
+
* mutations land in later batches).
|
|
637
|
+
*
|
|
638
|
+
* PURE — no IO. The .p8 file read + keyId extraction + Apple verification are
|
|
639
|
+
* effect-boundary concerns (p8-method-select / verifying-key); the reducers here
|
|
640
|
+
* only record the raw user input into progress.
|
|
641
|
+
*/
|
|
642
|
+
export declare function applyIosInput(step: OnboardingStep, progress: OnboardingProgress, input: unknown): OnboardingProgress;
|
|
643
|
+
/**
|
|
644
|
+
* Run the async side-effect for a step. Post-save tail steps (incl.
|
|
645
|
+
* saving-credentials) delegate to the shared neutral module via toTailDeps; the
|
|
646
|
+
* neutral result maps 1:1 onto IosEffectResult (next is a wider OnboardingStep;
|
|
647
|
+
* transient is a subset of IosStepCtx). All other steps are not implemented yet
|
|
648
|
+
* — the real Apple-API / keychain / build effects land in later batches.
|
|
649
|
+
*/
|
|
650
|
+
export declare function runIosEffect(step: OnboardingStep, progress: OnboardingProgress, deps: IosEffectDeps): Promise<IosEffectResult>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true only when `appId` is a safe reverse-domain package identifier
|
|
3
|
+
* that can be embedded in a shell command string without risk of injection.
|
|
4
|
+
*
|
|
5
|
+
* Valid examples: com.example.app io.capgo.app_1 com.acme.my-app
|
|
6
|
+
* Invalid examples: com.x; rm -rf ~ com.x$(cmd) nodots ""
|
|
7
|
+
*/
|
|
8
|
+
export declare function isSafeAppIdForCommand(appId: string): boolean;
|