@capgo/cli 7.124.13 → 7.125.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
3
  "type": "module",
4
- "version": "7.124.13",
4
+ "version": "7.125.0",
5
5
  "description": "A CLI to upload to capgo servers",
6
6
  "author": "Martin martin@capgo.app",
7
7
  "license": "Apache 2.0",
@@ -106,8 +106,11 @@
106
106
  "test:macos-signing": "bun test/test-macos-signing.mjs",
107
107
  "test:apple-api-import-helpers": "bun test/test-apple-api-import-helpers.mjs",
108
108
  "test:bundle-id-detector": "bun test/test-bundle-id-detector.mjs",
109
+ "test:apple-api-app-list": "bun test/test-apple-api-app-list.mjs",
110
+ "test:app-verification": "bun test/test-app-verification.mjs",
111
+ "test:pbxproj-parser": "bun test/test-pbxproj-parser.mjs",
109
112
  "test:manifest-path-encoding": "bun test/test-manifest-path-encoding.mjs",
110
- "test": "bun run build && 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: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:ai-log-capture && bun run test:ai-analyze-flow && bun run test:ai-render-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",
113
+ "test": "bun run build && 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: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-render-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",
111
114
  "test:build-platform-selection": "bun test/test-build-platform-selection.mjs",
112
115
  "test:ai-log-capture": "bun test/test-ai-log-capture.mjs",
113
116
  "test:ai-analyze-flow": "bun test/test-ai-analyze-flow.mjs",
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Pure decision logic for the iOS "verify App Store app" onboarding step.
3
+ *
4
+ * No filesystem, network, or React access — every input (the local Release
5
+ * build ID, the remote App Store Connect apps, and the registered bundle IDs)
6
+ * is passed in so the module stays synchronous and unit-testable, mirroring
7
+ * `decideBuilderCtaSurface` / `shouldBlockIncompatibleUpload` in
8
+ * `cli/src/bundle/builder-cta.ts`.
9
+ *
10
+ * The single invariant the step enforces (always `app_store` mode): an App
11
+ * Store Connect app must exist whose `bundleId` equals the Release build ID.
12
+ */
13
+ /**
14
+ * Classification of why (and whether) the verification invariant is met.
15
+ *
16
+ * - `exact-match` — an ASC app's bundle ID == the Release build ID. Pass.
17
+ * - `wrong-build-id` — apps exist but none match → likely a wrong build ID (Path A).
18
+ * - `no-app-identifier-exists` — no apps at all, but the identifier is already
19
+ * registered in the Developer portal (Path B; the
20
+ * ASC new-app form can select the existing id).
21
+ * - `no-app-unregistered` — no apps at all and the identifier is not yet
22
+ * registered (Path B; register first, then create).
23
+ *
24
+ * `no-apps-in-account` is the umbrella for the `apps.length === 0` cases. We
25
+ * keep it in the union because the analytics/step layer surfaces it as a
26
+ * coarse result, but `classifyAppVerification` deliberately returns the finer
27
+ * registered/unregistered split because that distinction is what changes the
28
+ * actionable Path B wording ("identifier already exists" vs "will be
29
+ * registered"). The umbrella value is therefore never returned by the
30
+ * classifier itself.
31
+ */
32
+ export type AppVerifyResult = 'exact-match' | 'wrong-build-id' | 'no-app-identifier-exists' | 'no-app-unregistered' | 'no-apps-in-account';
33
+ /** Minimal shape of an App Store Connect app needed for verification. */
34
+ export interface AscAppLike {
35
+ bundleId: string;
36
+ name: string;
37
+ }
38
+ export interface ClassifyAppVerificationInput {
39
+ /** The authoritative Release `PRODUCT_BUNDLE_IDENTIFIER` from the project. */
40
+ releaseBundleId: string;
41
+ /** Apps that actually exist in the user's App Store Connect account. */
42
+ apps: AscAppLike[];
43
+ /** Bundle IDs registered in the Apple Developer portal (diagnostic only). */
44
+ registeredBundleIds: string[];
45
+ }
46
+ export interface ClassifyAppVerificationResult {
47
+ result: AppVerifyResult;
48
+ /** The matched ASC app when `result === 'exact-match'`, else `null`. */
49
+ matchedApp: AscAppLike | null;
50
+ }
51
+ /**
52
+ * Pure classification of the verification invariant.
53
+ *
54
+ * 1. An app whose `bundleId === releaseBundleId` → `exact-match` (+ that app).
55
+ * 2. Otherwise, if any apps exist → `wrong-build-id` (the build signs an id that
56
+ * matches none of the account's apps).
57
+ * 3. Otherwise (no apps), if `releaseBundleId` is already registered →
58
+ * `no-app-identifier-exists`.
59
+ * 4. Otherwise (no apps, not registered) → `no-app-unregistered`.
60
+ */
61
+ export declare function classifyAppVerification(input: ClassifyAppVerificationInput): ClassifyAppVerificationResult;
62
+ /** Which resolution path the verification gate is enforcing. */
63
+ export type GatePath = 'fix-build-id' | 'create-app';
64
+ export interface EvaluateGateInput {
65
+ /** Whether the invariant now holds (re-checked live on each Continue). */
66
+ satisfied: boolean;
67
+ /** 1-based count of blocked Continue attempts so far. */
68
+ attempt: number;
69
+ }
70
+ export interface EvaluateGateResult {
71
+ /** Whether the user may proceed past the step. */
72
+ proceed: boolean;
73
+ /**
74
+ * How loud the (still-blocked) warning box should be. `0` when satisfied;
75
+ * otherwise the attempt count clamped to `3` so the escalation tops out
76
+ * instead of growing unbounded.
77
+ */
78
+ escalationLevel: number;
79
+ }
80
+ /**
81
+ * Pure gate decision. When the invariant is satisfied the user proceeds with no
82
+ * escalation; otherwise they are blocked and the escalation level is the attempt
83
+ * count capped at {@link MAX_ESCALATION_LEVEL} so the warning box can ramp its
84
+ * treatment without overflowing.
85
+ */
86
+ export declare function evaluateGate(input: EvaluateGateInput): EvaluateGateResult;
@@ -159,6 +159,37 @@ export declare function createCertificate(token: string, csrPem: string): Promis
159
159
  export declare function ensureBundleId(token: string, identifier: string): Promise<{
160
160
  bundleIdResourceId: string;
161
161
  }>;
162
+ /**
163
+ * An App Store Connect app record. Used by the iOS app-verification step to
164
+ * check whether an app exists whose `bundleId` matches the project's Release
165
+ * `PRODUCT_BUNDLE_IDENTIFIER`.
166
+ */
167
+ export interface AscApp {
168
+ id: string;
169
+ bundleId: string;
170
+ name: string;
171
+ }
172
+ /**
173
+ * Parse a `GET /v1/apps` response into {@link AscApp} records. Tolerant of
174
+ * missing `data`, missing `attributes`, and missing individual fields — Apple
175
+ * omits attributes the API key isn't entitled to see rather than nulling them.
176
+ */
177
+ export declare function parseAppsResponse(json: any): AscApp[];
178
+ /**
179
+ * Parse a `GET /v1/bundleIds` response into the list of registered identifier
180
+ * strings, dropping any falsy entries (missing `attributes`/`identifier`).
181
+ */
182
+ export declare function parseBundleIdsResponse(json: any): string[];
183
+ /**
184
+ * List every App Store Connect app visible to the API key, following
185
+ * pagination. Uses the existing {@link ascFetch} — no separate fetch path.
186
+ */
187
+ export declare function listApps(token: string): Promise<AscApp[]>;
188
+ /**
189
+ * List every registered bundle ID identifier visible to the API key, following
190
+ * pagination. Uses the existing {@link ascFetch} — no separate fetch path.
191
+ */
192
+ export declare function listBundleIds(token: string): Promise<string[]>;
162
193
  /**
163
194
  * Get the profile name we use for a given appId.
164
195
  */
@@ -1,4 +1,4 @@
1
- export type BundleIdSource = 'pbxproj-release' | 'pbxproj-fallback' | 'plist' | 'capacitor-config';
1
+ export type BundleIdSource = 'pbxproj-release' | 'pbxproj-debug' | 'pbxproj-fallback' | 'plist' | 'capacitor-config';
2
2
  export interface BundleIdCandidate {
3
3
  value: string;
4
4
  source: BundleIdSource;
@@ -8,6 +8,12 @@ export interface BundleIdCandidate {
8
8
  export interface DetectedBundleIds {
9
9
  /** PRODUCT_BUNDLE_IDENTIFIER from project.pbxproj, preferring Release config. */
10
10
  pbxproj: BundleIdCandidate | null;
11
+ /**
12
+ * The Debug-config PRODUCT_BUNDLE_IDENTIFIER from project.pbxproj, when a
13
+ * literal value exists. Exposed for the awareness note only — never used to
14
+ * gate. Null when no Debug-config literal value is present.
15
+ */
16
+ debug: BundleIdCandidate | null;
11
17
  /**
12
18
  * Info.plist CFBundleIdentifier when it's a literal value (not the common
13
19
  * `$(PRODUCT_BUNDLE_IDENTIFIER)` placeholder, which we drop because it
@@ -24,20 +30,57 @@ export interface DetectedBundleIds {
24
30
  recommended: BundleIdCandidate;
25
31
  /**
26
32
  * True when the recommended value differs from capacitor.config.appId.
27
- * Used by the confirm-app-id step to decide whether to surface a question
28
- * at all — when they match, nothing's worth asking about.
33
+ * Used by redirectIfMismatch to decide whether to adopt the Release id —
34
+ * when they match, capacitor.config.appId is already the build id.
29
35
  */
30
36
  mismatch: boolean;
37
+ /**
38
+ * True only when BOTH a Release-config and a Debug-config literal bundle id
39
+ * were found AND they differ. Drives the "Debug ≠ Release" awareness note;
40
+ * never gates. False when either value is missing or they match.
41
+ */
42
+ debugReleaseDiffer: boolean;
43
+ /**
44
+ * True when a Release-config PRODUCT_BUNDLE_IDENTIFIER was resolved from
45
+ * pbxproj. When false, the authoritative build ID could not be determined
46
+ * from Release and callers should warn/skip gating rather than gate on a
47
+ * Debug or plist fallback.
48
+ */
49
+ releaseResolved: boolean;
31
50
  /**
32
51
  * Deduplicated, ordered list of candidates ready to render as Select
33
52
  * options. Empty list is impossible (capacitor is always included).
34
53
  */
35
54
  candidates: BundleIdCandidate[];
36
55
  }
56
+ /**
57
+ * Parse `PRODUCT_BUNDLE_IDENTIFIER = "..."` lines from pbxproj content,
58
+ * returning the Release and Debug candidates separately.
59
+ *
60
+ * Release is authoritative: when ANY Release-config value exists, `release`
61
+ * is populated and `releaseResolved` is true. The Debug value (when present)
62
+ * is returned alongside via `debug` for the awareness note — it is never
63
+ * promoted to `release`.
64
+ *
65
+ * When no Release config exists, `release` is null and `releaseResolved` is
66
+ * false so callers can detect the no-Release case.
67
+ */
68
+ export declare function parsePbxprojBundleIds(pbxprojContent: string): {
69
+ release: BundleIdCandidate | null;
70
+ debug: BundleIdCandidate | null;
71
+ releaseResolved: boolean;
72
+ };
37
73
  /**
38
74
  * Parse `PRODUCT_BUNDLE_IDENTIFIER = "..."` lines from pbxproj content.
39
- * Returns the Release-config value if present, else the first non-Release
40
- * value. Returns null when no bundle id can be extracted.
75
+ * Returns the Release-config value if present, else the shortest non-Release
76
+ * value as a `pbxproj-fallback`. Returns null when no bundle id can be
77
+ * extracted.
78
+ *
79
+ * Release stays authoritative here: a Release value is never overridden by a
80
+ * Debug value. The no-Release fallback is preserved for backward
81
+ * compatibility — callers that need to distinguish "Release resolved" from
82
+ * "fell back to Debug" should use `parsePbxprojBundleIds` (or the
83
+ * `releaseResolved` flag on `detectIosBundleIds`).
41
84
  *
42
85
  * Looks like a re-implementation of pbxproj-parser.ts's resolveBundleId, but
43
86
  * that one needs an XCConfigurationList id (it walks from a target). This
@@ -16,7 +16,7 @@ export interface OnboardingResult {
16
16
  /** Present only when outcome === 'completed'. */
17
17
  summary?: OnboardingCompletionSummary;
18
18
  }
19
- export type OnboardingStep = 'welcome' | 'resume-prompt' | 'platform-select' | 'adding-platform' | 'credentials-exist' | 'backing-up' | 'setup-method-select' | 'confirm-app-id' | 'import-scanning' | 'import-distribution-mode' | 'import-pick-identity' | 'import-pick-profile' | 'import-validating-all-certs' | 'import-checking-apple-cert' | 'import-no-match-recovery' | 'import-portal-explanation' | 'import-provide-profile-path' | 'import-create-profile-only' | 'import-export-warning' | 'import-compiling-helper' | 'import-exporting' | 'api-key-instructions' | 'p8-method-select' | 'input-p8-path' | 'input-key-id' | 'input-issuer-id' | 'verifying-key' | 'creating-certificate' | 'cert-limit-prompt' | 'revoking-certificate' | 'creating-profile' | 'duplicate-profile-prompt' | 'deleting-duplicate-profiles' | 'saving-credentials' | 'detecting-ci-secrets' | 'ci-secrets-setup' | 'ci-secrets-target-select' | 'ask-ci-secrets' | 'checking-ci-secrets' | 'confirm-ci-secret-overwrite' | 'uploading-ci-secrets' | 'ci-secrets-failed' | 'ask-github-actions-setup' | 'confirm-secrets-push' | 'ask-export-env' | 'exporting-env' | 'confirm-env-export-overwrite' | 'overwrite-and-export-env' | 'pick-package-manager' | 'pick-build-script' | 'pick-build-script-custom' | 'preview-workflow-file' | 'view-workflow-diff' | 'writing-workflow-file' | 'ask-build' | 'requesting-build' | 'ai-analysis-prompt' | 'ai-analysis-running' | 'ai-analysis-result' | 'ai-analysis-result-scroll' | 'build-complete' | 'no-platform' | 'error';
19
+ export type OnboardingStep = 'welcome' | 'resume-prompt' | 'platform-select' | 'adding-platform' | 'credentials-exist' | 'backing-up' | 'setup-method-select' | 'import-scanning' | 'import-distribution-mode' | 'import-pick-identity' | 'import-pick-profile' | 'import-validating-all-certs' | 'import-checking-apple-cert' | 'import-no-match-recovery' | 'import-portal-explanation' | 'import-provide-profile-path' | 'import-create-profile-only' | 'import-export-warning' | 'import-compiling-helper' | 'import-exporting' | 'api-key-instructions' | 'p8-method-select' | 'input-p8-path' | 'input-key-id' | 'input-issuer-id' | 'verifying-key' | 'verify-app' | 'creating-certificate' | 'cert-limit-prompt' | 'revoking-certificate' | 'creating-profile' | 'duplicate-profile-prompt' | 'deleting-duplicate-profiles' | 'saving-credentials' | 'detecting-ci-secrets' | 'ci-secrets-setup' | 'ci-secrets-target-select' | 'ask-ci-secrets' | 'checking-ci-secrets' | 'confirm-ci-secret-overwrite' | 'uploading-ci-secrets' | 'ci-secrets-failed' | 'ask-github-actions-setup' | 'confirm-secrets-push' | 'ask-export-env' | 'exporting-env' | 'confirm-env-export-overwrite' | 'overwrite-and-export-env' | 'pick-package-manager' | 'pick-build-script' | 'pick-build-script-custom' | 'preview-workflow-file' | 'view-workflow-diff' | 'writing-workflow-file' | 'ask-build' | 'requesting-build' | 'ai-analysis-prompt' | 'ai-analysis-running' | 'ai-analysis-result' | 'ai-analysis-result-scroll' | 'build-complete' | 'no-platform' | 'error';
20
20
  export type OnboardingErrorCategory = 'apple_api_unauthorized' | 'apple_api_rate_limited' | 'cert_limit_reached' | 'profile_creation_failed' | 'p8_invalid' | 'keychain_no_identities' | 'keychain_export_failed' | 'keychain_helper_compile_failed' | 'profile_no_match' | 'profile_read_failed' | 'unknown';
21
21
  export interface ApiKeyData {
22
22
  keyId: string;
@@ -105,25 +105,26 @@ export interface OnboardingProgress {
105
105
  */
106
106
  importDistribution?: 'app_store' | 'ad_hoc';
107
107
  /**
108
- * When set, the user explicitly confirmed an iOS bundle id different from
109
- * `capacitor.config.appId`. Used for Apple-side operations (cert lookup,
110
- * profile filtering, `ensureBundleId`, `createProfile`) and as the key in
111
- * the provisioning_map. The progress-file key and Capgo SaaS API calls
112
- * still use `appId` so existing build commands continue to find these
113
- * credentials without forcing the user to edit `capacitor.config`.
108
+ * The resolved iOS bundle id (the authoritative Release
109
+ * `PRODUCT_BUNDLE_IDENTIFIER`) when it differs from `capacitor.config.appId`.
110
+ * Used for Apple-side operations (cert lookup, profile filtering,
111
+ * `ensureBundleId`, `createProfile`) and as the key in the provisioning_map.
112
+ * The progress-file key and Capgo SaaS API calls still use `appId` so existing
113
+ * build commands keep finding these credentials without editing
114
+ * `capacitor.config`.
114
115
  *
115
- * Persisted so the confirm-app-id step doesn't re-ask on resume — once
116
- * confirmed, the override sticks unless the configuration context (see
116
+ * Persisted so verify-app / redirectIfMismatch don't re-resolve on resume —
117
+ * once set, the override sticks unless the configuration context (see
117
118
  * `iosBundleIdContextAppId`) changes between CLI runs.
118
119
  */
119
120
  iosBundleIdOverride?: string;
120
121
  /**
121
- * Snapshot of `config.appId` at the time the user confirmed the
122
- * `iosBundleIdOverride`. On the next run we compare this to the current
123
- * `config.appId`; if it changed (user renamed the app, added/removed a
124
- * dev-tunnel suffix, etc.) the saved override is stale and we re-ask
125
- * via the confirm-app-id step. Without this we'd silently keep using a
126
- * bundle id the user already moved on from.
122
+ * Snapshot of `config.appId` at the time the `iosBundleIdOverride` was
123
+ * resolved. On the next run we compare this to the current `config.appId`;
124
+ * if it changed (user renamed the app, added/removed a dev-tunnel suffix,
125
+ * etc.) the saved override is stale and we re-resolve / re-verify via the
126
+ * verify-app step. Without this we'd silently keep using a bundle id the
127
+ * user already moved on from.
127
128
  */
128
129
  iosBundleIdContextAppId?: string;
129
130
  completedSteps: {
@@ -15,9 +15,10 @@ interface AppProps {
15
15
  * (cert lookup, profile filtering, ensureBundleId, createProfile, and the
16
16
  * provisioning_map key). Sourced from `config.appId` directly — what
17
17
  * `cap sync` writes into project.pbxproj's PRODUCT_BUNDLE_IDENTIFIER.
18
- * The user can override at the `confirm-app-id` step when pbxproj and
19
- * config.appId disagree. command.ts falls back to `appId` if config.appId
20
- * is missing, so this prop is always a valid string.
18
+ * When pbxproj's Release id and config.appId disagree, the wizard adopts the
19
+ * authoritative Release id (verify-app confirms it remotely). command.ts
20
+ * falls back to `appId` if config.appId is missing, so this prop is always a
21
+ * valid string.
21
22
  */
22
23
  iosBundleIdInitial: string;
23
24
  initialProgress: OnboardingProgress | null;
@@ -20,3 +20,29 @@ export declare function findXcodeProject(searchDir: string): string | null;
20
20
  * Returns null if no project is found.
21
21
  */
22
22
  export declare function readPbxproj(projectDir: string): string | null;
23
+ /**
24
+ * Replace every `PRODUCT_BUNDLE_IDENTIFIER = <fromId>;` assignment in pbxproj
25
+ * content with `<toId>` (tolerates optional quotes and surrounding whitespace).
26
+ * Pure — returns the new content and the number of replacements made.
27
+ *
28
+ * Matching by exact value (rather than by config block) is deliberate: callers
29
+ * pass the resolved Release build id as `fromId`, and real Capacitor/RN projects
30
+ * give extensions a SUFFIXED id (com.app.ext), so only the main target's
31
+ * assignment(s) match. A Debug config that shares the exact same value is
32
+ * updated too (keeping Debug == Release); a Debug config with a different value
33
+ * is left untouched.
34
+ */
35
+ export declare function replaceBundleIdInPbxproj(content: string, fromId: string, toId: string): {
36
+ content: string;
37
+ changed: number;
38
+ };
39
+ /**
40
+ * Locate the project's pbxproj (same search order as detectIosBundleIds) and
41
+ * rewrite its `PRODUCT_BUNDLE_IDENTIFIER = <fromId>;` assignments to `<toId>`,
42
+ * writing the file back only when something changed. Returns the number of
43
+ * replacements (0 when no project or no matching assignment was found). Throws
44
+ * only on a filesystem read/write error.
45
+ */
46
+ export declare function writeReleaseBundleId(cwd: string, iosDir: string, fromId: string, toId: string): {
47
+ changed: number;
48
+ };