@better-update/cli 0.44.1 → 0.46.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.mjs CHANGED
@@ -35,7 +35,7 @@ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
35
35
 
36
36
  //#endregion
37
37
  //#region package.json
38
- var version = "0.44.1";
38
+ var version = "0.46.0";
39
39
 
40
40
  //#endregion
41
41
  //#region src/lib/interactive-mode.ts
@@ -5055,6 +5055,27 @@ const writeEasJsonPatch = (projectRoot, patch) => Effect.gen(function* () {
5055
5055
  return filePath;
5056
5056
  });
5057
5057
  /**
5058
+ * Set `submit.<profileName>.ios.ascApiKeyId` in `eas.json`, preserving every
5059
+ * other submit profile and key. Used after auto-resolving/creating an ASC API
5060
+ * key during `submit` so the next run reuses it instead of creating another.
5061
+ */
5062
+ const setSubmitProfileAscApiKeyId = (projectRoot, profileName, ascApiKeyId) => Effect.gen(function* () {
5063
+ const existing = (yield* readEasJsonRaw(projectRoot)) ?? {};
5064
+ const submit = isRecord$1(existing["submit"]) ? existing["submit"] : {};
5065
+ const profile = isRecord$1(submit[profileName]) ? submit[profileName] : {};
5066
+ const ios = isRecord$1(profile["ios"]) ? profile["ios"] : {};
5067
+ return yield* writeEasJsonPatch(projectRoot, { submit: {
5068
+ ...submit,
5069
+ [profileName]: {
5070
+ ...profile,
5071
+ ios: {
5072
+ ...ios,
5073
+ ascApiKeyId
5074
+ }
5075
+ }
5076
+ } });
5077
+ });
5078
+ /**
5058
5079
  * Default `build` profiles scaffolded by `init` / `build configure`. Mirrors the
5059
5080
  * EAS three-tier convention: `development` (dev-client, internal), `preview`
5060
5081
  * (internal QA) and `production` (store). Keep in sync with the build-profile
@@ -23861,7 +23882,7 @@ const wrapKeyCreate = (run) => Effect.tryPromise({
23861
23882
  });
23862
23883
  }
23863
23884
  });
23864
- const writeRescueP8 = (keyId, p8Pem) => Effect.gen(function* () {
23885
+ const writeRescueP8$1 = (keyId, p8Pem) => Effect.gen(function* () {
23865
23886
  const fs = yield* FileSystem.FileSystem;
23866
23887
  const filePath = `AuthKey_${keyId}.p8`;
23867
23888
  yield* fs.writeFileString(filePath, p8Pem, { mode: 384 });
@@ -23892,7 +23913,7 @@ const generateAndUploadApnsKeyViaAppleId = (api, input) => Effect.gen(function*
23892
23913
  ...compact({ appleTeamName: toOptional(input.appleTeamName) })
23893
23914
  } });
23894
23915
  }).pipe(Effect.catchAll((cause) => Effect.gen(function* () {
23895
- const rescuePath = yield* writeRescueP8(key.id, p8Pem).pipe(Effect.catchAll(() => Effect.succeed(null)));
23916
+ const rescuePath = yield* writeRescueP8$1(key.id, p8Pem).pipe(Effect.catchAll(() => Effect.succeed(null)));
23896
23917
  const where = rescuePath === null ? "could not be saved locally and is now unrecoverable" : `was saved to ${rescuePath} — re-import with \`credentials generate push-key --p8 ${rescuePath} --key-id ${key.id} --apple-team-id ${input.appleTeamIdentifier}\``;
23897
23918
  return yield* new AppleIdGenerateFailedError({
23898
23919
  step: "store-apns-key",
@@ -28373,6 +28394,107 @@ const androidMenu = (ctx) => Effect.gen(function* () {
28373
28394
  yield* androidMenu(ctx);
28374
28395
  });
28375
28396
 
28397
+ //#endregion
28398
+ //#region src/lib/credentials-generator-asc-key.ts
28399
+ const toUserRole = (role) => role === "APP_MANAGER" ? AppleUtils.UserRole.APP_MANAGER : AppleUtils.UserRole.ADMIN;
28400
+ /** Default nickname shown in App Store Connect → Users and Access → Integrations. */
28401
+ const defaultAscApiKeyNickname = () => `[better-update] ${(/* @__PURE__ */ new Date()).toISOString()}`;
28402
+ const ASC_KEY_NOT_READY_PATTERN = /no resource of type|resource does not exist/iu;
28403
+ const ASC_KEY_DOWNLOAD_RETRY = Schedule.exponential("1 second", 2).pipe(Schedule.intersect(Schedule.recurs(6)));
28404
+ const downloadAscKeyWithRetry = (key) => Effect.tryPromise({
28405
+ try: async () => key.downloadAsync(),
28406
+ catch: (cause) => new AppleIdGenerateFailedError({
28407
+ step: "apple-download-asc-key",
28408
+ message: messageOf(cause)
28409
+ })
28410
+ }).pipe(Effect.flatMap((pem) => pem === null || pem.length === 0 ? Effect.fail(new AppleIdGenerateFailedError({
28411
+ step: "apple-download-asc-key",
28412
+ message: "App Store Connect returned no private key for the new API key — it may already have been downloaded. A key can only be downloaded once; create a new one or upload the .p8 manually with `credentials upload-asc-key`."
28413
+ })) : Effect.succeed(pem)), Effect.retry({
28414
+ while: (error) => ASC_KEY_NOT_READY_PATTERN.test(error.message),
28415
+ schedule: ASC_KEY_DOWNLOAD_RETRY
28416
+ }), Effect.catchIf((error) => ASC_KEY_NOT_READY_PATTERN.test(error.message), () => Effect.fail(new AppleIdGenerateFailedError({
28417
+ step: "apple-download-asc-key",
28418
+ message: "App Store Connect is still provisioning the new API key (this can take up to a minute). The key was created — re-run shortly, or download the .p8 from App Store Connect → Users and Access → Integrations and import it with `credentials upload-asc-key`."
28419
+ }))));
28420
+ const writeRescueP8 = (keyId, p8Pem) => Effect.gen(function* () {
28421
+ const fs = yield* FileSystem.FileSystem;
28422
+ const filePath = `AuthKey_${keyId}.p8`;
28423
+ yield* fs.writeFileString(filePath, p8Pem, { mode: 384 });
28424
+ return filePath;
28425
+ });
28426
+ /**
28427
+ * Create an App Store Connect API key from the Apple ID cookie session, download
28428
+ * its one-shot `.p8`, resolve the issuer id, and store the sealed envelope in the
28429
+ * vault — the zero-knowledge equivalent of downloading a key from App Store Connect
28430
+ * and running `credentials upload-asc-key`, but without the manual round-trip.
28431
+ */
28432
+ const generateAndUploadAscApiKeyViaAppleId = (api, input) => Effect.gen(function* () {
28433
+ const ctx = input.context;
28434
+ const key = yield* wrap("apple-create-asc-key", async () => AppleUtils.ApiKey.createAsync(ctx, {
28435
+ nickname: input.nickname,
28436
+ allAppsVisible: true,
28437
+ roles: [toUserRole(input.role)],
28438
+ keyType: AppleUtils.ApiKeyType.PUBLIC_API
28439
+ }));
28440
+ const p8Pem = yield* downloadAscKeyWithRetry(key);
28441
+ const displayName = input.name ?? key.id;
28442
+ const stored = yield* Effect.gen(function* () {
28443
+ const issuerId = (yield* wrap("apple-fetch-asc-key", async () => AppleUtils.ApiKey.infoAsync(ctx, { id: key.id }))).attributes.provider?.id;
28444
+ if (issuerId === void 0 || issuerId.length === 0) return yield* new AppleIdGenerateFailedError({
28445
+ step: "resolve-issuer-id",
28446
+ message: "App Store Connect did not return an issuer ID for the new key. Find it under Users and Access → Integrations and import the .p8 with `credentials upload-asc-key`."
28447
+ });
28448
+ const metadata = compact({
28449
+ name: displayName,
28450
+ keyId: key.id,
28451
+ issuerId,
28452
+ appleTeamIdentifier: input.appleTeamIdentifier
28453
+ });
28454
+ const envelope = yield* sealForUpload({
28455
+ session: yield* openVaultSessionInteractive(api),
28456
+ credentialType: "asc-api-key",
28457
+ metadata,
28458
+ secret: { p8Pem }
28459
+ });
28460
+ return {
28461
+ id: (yield* api.ascApiKeys.upload({ payload: {
28462
+ ...toUploadEnvelope(envelope),
28463
+ ...metadata
28464
+ } })).id,
28465
+ issuerId
28466
+ };
28467
+ }).pipe(Effect.catchAll((cause) => Effect.gen(function* () {
28468
+ const rescuePath = yield* writeRescueP8(key.id, p8Pem).pipe(Effect.catchAll(() => Effect.succeed(null)));
28469
+ const where = rescuePath === null ? "could not be saved locally and is now unrecoverable" : `was saved to ${rescuePath} — re-import with \`credentials upload-asc-key --p8 ${rescuePath} --key-id ${key.id}\` (find the issuer ID under App Store Connect → Users and Access → Integrations)`;
28470
+ return yield* new AppleIdGenerateFailedError({
28471
+ step: "store-asc-key",
28472
+ message: `Created App Store Connect API key ${key.id} on Apple but failed to store it (${messageOf(cause)}). The downloaded .p8 ${where}.`
28473
+ });
28474
+ })));
28475
+ return {
28476
+ id: stored.id,
28477
+ keyId: key.id,
28478
+ issuerId: stored.issuerId,
28479
+ name: displayName,
28480
+ role: input.role
28481
+ };
28482
+ });
28483
+ /**
28484
+ * List the team's active App Store Connect API keys as seen on Apple (via the
28485
+ * cookie session). Used before auto-creating a key to avoid making a redundant
28486
+ * one — note a key's `.p8` is downloadable only once, so a key listed here is
28487
+ * usable for publishing only if its `.p8` was captured at creation (i.e. it is
28488
+ * already in the vault). Surfacing them lets the caller warn + respect Apple's
28489
+ * per-team key cap rather than blindly creating another.
28490
+ */
28491
+ const listAscApiKeysViaAppleId = (ctx) => Effect.gen(function* () {
28492
+ return (yield* wrap("apple-list-asc-keys", async () => AppleUtils.ApiKey.getAsync(ctx))).filter((key) => key.attributes.isActive).map((key) => ({
28493
+ keyId: key.id,
28494
+ nickname: key.attributes.nickname
28495
+ }));
28496
+ });
28497
+
28376
28498
  //#endregion
28377
28499
  //#region src/application/credentials-manager-ios-asc.ts
28378
28500
  const uploadIosAscKey = (ctx) => Effect.gen(function* () {
@@ -28392,6 +28514,33 @@ const uploadIosAscKey = (ctx) => Effect.gen(function* () {
28392
28514
  yield* Console.log("ASC API key uploaded.");
28393
28515
  yield* printKeyValue([["ID", created.id], ["Key ID", keyId]]);
28394
28516
  });
28517
+ const generateAscKeyViaAppleId = (ctx) => Effect.gen(function* () {
28518
+ const auth = yield* AppleAuth;
28519
+ const session = yield* auth.ensureLoggedIn();
28520
+ const role = yield* promptSelect("Select a role for the generated API key", [{
28521
+ value: "ADMIN",
28522
+ label: "ADMIN (default)"
28523
+ }, {
28524
+ value: "APP_MANAGER",
28525
+ label: "APP_MANAGER (least privilege for app management)"
28526
+ }]);
28527
+ yield* Console.log("Creating an App Store Connect API key via your Apple ID...");
28528
+ return yield* generateAndUploadAscApiKeyViaAppleId(ctx.api, {
28529
+ context: auth.buildRequestContext(session),
28530
+ appleTeamIdentifier: session.teamId,
28531
+ nickname: defaultAscApiKeyNickname(),
28532
+ role
28533
+ });
28534
+ });
28535
+ const createIosAscKeyViaAppleId = (ctx) => Effect.gen(function* () {
28536
+ const created = yield* generateAscKeyViaAppleId(ctx);
28537
+ yield* Console.log(`Created and stored ASC API key ${created.keyId}.`);
28538
+ yield* printKeyValue([
28539
+ ["ID", created.id],
28540
+ ["Key ID", created.keyId],
28541
+ ["Issuer ID", created.issuerId]
28542
+ ]);
28543
+ });
28395
28544
  const bindIosAscKey = (ctx) => Effect.gen(function* () {
28396
28545
  const keys = yield* ctx.api.ascApiKeys.list();
28397
28546
  if (keys.items.length === 0) return yield* new MissingCredentialsError({
@@ -28428,18 +28577,26 @@ const uploadNewAscKey = (ctx) => Effect.gen(function* () {
28428
28577
  });
28429
28578
  const setupProjectAscApiKey = (ctx) => Effect.gen(function* () {
28430
28579
  const keys = yield* ctx.api.ascApiKeys.list();
28431
- const choice = keys.items.length === 0 ? "upload" : yield* promptSelect("How would you like to set up the ASC key?", [{
28432
- value: "existing",
28433
- label: `Use an existing ASC API key (${String(keys.items.length)})`
28580
+ const baseChoices = [{
28581
+ value: "generate",
28582
+ label: "Create a new ASC API key from your Apple ID (no .p8 needed)"
28434
28583
  }, {
28435
28584
  value: "upload",
28436
- label: "Upload a new ASC API key"
28437
- }]);
28585
+ label: "Upload an existing .p8 key"
28586
+ }];
28587
+ const choice = keys.items.length === 0 ? yield* promptSelect("How would you like to set up the ASC key?", baseChoices) : yield* promptSelect("How would you like to set up the ASC key?", [{
28588
+ value: "existing",
28589
+ label: `Use an existing ASC API key (${String(keys.items.length)})`
28590
+ }, ...baseChoices]);
28438
28591
  const config = yield* promptForBundleConfig(ctx);
28439
- const ascKeyId = choice === "upload" ? yield* uploadNewAscKey(ctx) : yield* promptSelect("Select an ASC API key to bind", keys.items.map((key) => ({
28440
- value: key.id,
28441
- label: `${key.name} (${key.keyId})`
28442
- })));
28592
+ const ascKeyId = yield* Effect.gen(function* () {
28593
+ if (choice === "generate") return (yield* generateAscKeyViaAppleId(ctx)).id;
28594
+ if (choice === "upload") return yield* uploadNewAscKey(ctx);
28595
+ return yield* promptSelect("Select an ASC API key to bind", keys.items.map((key) => ({
28596
+ value: key.id,
28597
+ label: `${key.name} (${key.keyId})`
28598
+ })));
28599
+ });
28443
28600
  yield* ctx.api.iosBundleConfigurations.update({
28444
28601
  path: { id: config.id },
28445
28602
  payload: { ascApiKeyId: ascKeyId }
@@ -28453,6 +28610,10 @@ const iosAscKeysMenu = (ctx) => Effect.gen(function* () {
28453
28610
  value: "setup",
28454
28611
  label: "Set up your project to use an ASC API Key"
28455
28612
  },
28613
+ {
28614
+ value: "create",
28615
+ label: "Create a new ASC API key from your Apple ID (no .p8 needed)"
28616
+ },
28456
28617
  {
28457
28618
  value: "upload",
28458
28619
  label: "Add a new ASC API key"
@@ -28472,6 +28633,7 @@ const iosAscKeysMenu = (ctx) => Effect.gen(function* () {
28472
28633
  ]));
28473
28634
  if (choice === "__back__") return;
28474
28635
  if (choice === "setup") yield* safely("set up ASC key", setupProjectAscApiKey(ctx));
28636
+ else if (choice === "create") yield* safely("create ASC key", createIosAscKeyViaAppleId(ctx));
28475
28637
  else if (choice === "upload") yield* safely("upload ASC key", uploadIosAscKey(ctx));
28476
28638
  else if (choice === "bind") yield* safely("bind ASC key", bindIosAscKey(ctx));
28477
28639
  else if (choice === "delete") yield* safely("delete ASC key", pickAndDelete(ctx, "asc-api-key", "ASC API key"));
@@ -30353,6 +30515,66 @@ const envVaultCommand = defineCommand({
30353
30515
  default: "status"
30354
30516
  });
30355
30517
 
30518
+ //#endregion
30519
+ //#region src/commands/credentials/generate-asc-key.ts
30520
+ const ASC_KEY_EXIT_EXTRAS = {
30521
+ CredentialValidationError: 2,
30522
+ AppleIdGenerateFailedError: 6,
30523
+ AppleAuthError: 4,
30524
+ InteractiveProhibitedError: 4
30525
+ };
30526
+ const normalizeRole = (raw) => {
30527
+ if (raw === void 0) return Effect.succeed("ADMIN");
30528
+ const upper = raw.trim().toUpperCase();
30529
+ if (upper === "ADMIN" || upper === "APP_MANAGER") return Effect.succeed(upper);
30530
+ return Effect.fail(new CredentialValidationError({ message: `Unknown ASC API key role "${raw}". Use ADMIN or APP_MANAGER.` }));
30531
+ };
30532
+ const ascKeyCommand = defineCommand({
30533
+ meta: {
30534
+ name: "asc-key",
30535
+ description: "Create an App Store Connect API key (.p8) directly from your Apple ID login — no manual download. Stored encrypted in your vault, it can issue certificates, sync devices, and upload builds. Requires the Account Holder to have agreed to the API Terms once under Users and Access → Integrations."
30536
+ },
30537
+ args: {
30538
+ role: {
30539
+ type: "string",
30540
+ description: "ADMIN (default) or APP_MANAGER (least privilege)"
30541
+ },
30542
+ name: {
30543
+ type: "string",
30544
+ description: "Display name for the stored key (defaults to the key ID)"
30545
+ },
30546
+ nickname: {
30547
+ type: "string",
30548
+ description: "Nickname shown in App Store Connect (defaults to a timestamped name)"
30549
+ }
30550
+ },
30551
+ run: async ({ args }) => runEffect(Effect.gen(function* () {
30552
+ const role = yield* normalizeRole(args.role);
30553
+ const api = yield* apiClient;
30554
+ const auth = yield* AppleAuth;
30555
+ const session = yield* auth.ensureLoggedIn();
30556
+ yield* printHuman("Creating an App Store Connect API key via your Apple ID...");
30557
+ const created = yield* generateAndUploadAscApiKeyViaAppleId(api, {
30558
+ context: auth.buildRequestContext(session),
30559
+ appleTeamIdentifier: session.teamId,
30560
+ nickname: args.nickname ?? defaultAscApiKeyNickname(),
30561
+ role,
30562
+ ...compact({ name: args.name })
30563
+ });
30564
+ yield* printHuman("App Store Connect API key created and stored.");
30565
+ yield* printHumanKeyValue([
30566
+ ["ID", created.id],
30567
+ ["Key ID", created.keyId],
30568
+ ["Issuer ID", created.issuerId],
30569
+ ["Role", created.role]
30570
+ ]);
30571
+ return created;
30572
+ }), {
30573
+ exits: ASC_KEY_EXIT_EXTRAS,
30574
+ json: "value"
30575
+ })
30576
+ });
30577
+
30356
30578
  //#endregion
30357
30579
  //#region src/lib/credentials-generator-merchant.ts
30358
30580
  /**
@@ -30856,6 +31078,7 @@ const generateCommand$1 = defineCommand({
30856
31078
  "provisioning-profile": provisioningProfileCommand,
30857
31079
  "push-key": pushKeyCommand$1,
30858
31080
  "merchant-id": merchantIdCommand,
31081
+ "asc-key": ascKeyCommand,
30859
31082
  "gsa-key": gsaKeyCommand
30860
31083
  }
30861
31084
  });
@@ -35330,6 +35553,78 @@ const statusCommand = defineCommand({
35330
35553
  }), { json: "value" })
35331
35554
  });
35332
35555
 
35556
+ //#endregion
35557
+ //#region src/application/submit-asc-key.ts
35558
+ const ROLE_CHOICES = [{
35559
+ value: "ADMIN",
35560
+ label: "ADMIN (default)"
35561
+ }, {
35562
+ value: "APP_MANAGER",
35563
+ label: "APP_MANAGER (least privilege for app management)"
35564
+ }];
35565
+ const CREATE_CHOICE = "__create__";
35566
+ /** Best-effort: write the resolved id back to eas.json so the next run reuses it. */
35567
+ const persist = (input, keyId) => setSubmitProfileAscApiKeyId(input.projectRoot, input.profileName, keyId).pipe(Effect.flatMap((path) => printHuman(`Saved ascApiKeyId to ${path} (submit profile "${input.profileName}") for reuse.`)), Effect.catchAll((error) => printHuman(`Note: could not write ascApiKeyId to eas.json (${error.message}). Add it manually to reuse this key.`)));
35568
+ /** Log in, warn about any existing team keys, then (with consent) create + persist. */
35569
+ const createAndPersist = (input) => Effect.gen(function* () {
35570
+ const auth = yield* AppleAuth;
35571
+ const session = yield* auth.ensureLoggedIn();
35572
+ const ctx = auth.buildRequestContext(session);
35573
+ const teamKeys = yield* listAscApiKeysViaAppleId(ctx).pipe(Effect.orElseSucceed(() => []));
35574
+ const hasTeamKeys = teamKeys.length > 0;
35575
+ if (hasTeamKeys) {
35576
+ yield* printHuman(`Your Apple team already has ${String(teamKeys.length)} App Store Connect API key(s): ${teamKeys.map((key) => key.nickname).join(", ")}.`);
35577
+ yield* printHuman("A key's .p8 is downloadable only once at creation, so an existing key is reusable only if you still have its .p8 (import it with `credentials upload-asc-key`). Apple also caps the number of keys per team.");
35578
+ }
35579
+ if (!(yield* promptConfirm(hasTeamKeys ? "Create a new ASC API key anyway?" : "No App Store Connect API key found. Create one now from your Apple ID?", { initialValue: !hasTeamKeys }))) return null;
35580
+ const role = yield* promptSelect("Select a role for the generated API key", ROLE_CHOICES);
35581
+ yield* printHuman("Creating an App Store Connect API key via your Apple ID...");
35582
+ const created = yield* generateAndUploadAscApiKeyViaAppleId(input.api, {
35583
+ context: ctx,
35584
+ appleTeamIdentifier: session.teamId,
35585
+ nickname: defaultAscApiKeyNickname(),
35586
+ role
35587
+ });
35588
+ yield* printHuman(`Created and stored ASC API key ${created.keyId}.`);
35589
+ yield* persist(input, created.id);
35590
+ return created.id;
35591
+ });
35592
+ /**
35593
+ * Resolve an ASC API key id to upload a `submit` build with when none is set in
35594
+ * the submit profile. Reuses a stored vault key when possible (the only keys we
35595
+ * hold a usable `.p8` for), else offers to create one from the Apple ID session.
35596
+ * Returns the resolved id, or `null` when none could be resolved — non-interactive
35597
+ * runs, a declined prompt, or any failure (login/create/network) degrade to `null`
35598
+ * so the caller falls back to queuing the submission with guidance rather than
35599
+ * crashing. Persists the resolved id to `eas.json` for reuse.
35600
+ */
35601
+ const ensureAscApiKeyForSubmit = (input) => Effect.gen(function* () {
35602
+ if (!(yield* InteractiveMode).allow) return null;
35603
+ const stored = yield* input.api.ascApiKeys.list();
35604
+ if (stored.items.length === 1) {
35605
+ const [only] = stored.items;
35606
+ if (only !== void 0) {
35607
+ yield* printHuman(`Using your stored ASC API key "${only.name}" (${only.keyId}).`);
35608
+ yield* persist(input, only.id);
35609
+ return only.id;
35610
+ }
35611
+ }
35612
+ if (stored.items.length > 1) {
35613
+ const picked = yield* promptSelect("No ASC API key in this submit profile. Pick one to use, or create a new one:", [...stored.items.map((key) => ({
35614
+ value: key.id,
35615
+ label: `${key.name} (${key.keyId})`
35616
+ })), {
35617
+ value: CREATE_CHOICE,
35618
+ label: "Create a new ASC API key from my Apple ID"
35619
+ }]);
35620
+ if (picked !== CREATE_CHOICE) {
35621
+ yield* persist(input, picked);
35622
+ return picked;
35623
+ }
35624
+ }
35625
+ return yield* createAndPersist(input);
35626
+ }).pipe(Effect.catchAll((error) => printHuman(`Could not set up an App Store Connect API key (${messageOf(error)}). The submission was queued — create one with \`credentials generate asc-key\` and re-run.`).pipe(Effect.as(null))));
35627
+
35333
35628
  //#endregion
35334
35629
  //#region src/commands/submit/index.ts
35335
35630
  const PLATFORMS = ["ios", "android"];
@@ -35416,7 +35711,17 @@ const runFlow = (api, projectId, args) => Effect.gen(function* () {
35416
35711
  appleId: iosProfile?.appleId,
35417
35712
  ascApiKeyId: iosProfile?.ascApiKeyId,
35418
35713
  hasAppSpecificPassword: hasAppleAppSpecificPassword()
35419
- });
35714
+ }) ?? (yield* Effect.gen(function* () {
35715
+ const resolvedKeyId = yield* ensureAscApiKeyForSubmit({
35716
+ api,
35717
+ projectRoot: args.projectRoot,
35718
+ profileName: args.profile
35719
+ });
35720
+ return resolvedKeyId === null ? null : {
35721
+ kind: "asc-api-key",
35722
+ ascApiKeyId: resolvedKeyId
35723
+ };
35724
+ }));
35420
35725
  if (auth === null) {
35421
35726
  yield* printHuman("iOS submission queued. Add ascApiKeyId to the eas.json submit profile, or set appleId + the EXPO_APPLE_APP_SPECIFIC_PASSWORD env var, to enable client-side altool upload.");
35422
35727
  return submission;
@@ -35430,7 +35735,7 @@ const runFlow = (api, projectId, args) => Effect.gen(function* () {
35430
35735
  value: args.archive.archiveUrl
35431
35736
  },
35432
35737
  auth,
35433
- ascApiKeyId: iosProfile?.ascApiKeyId,
35738
+ ascApiKeyId: auth.kind === "asc-api-key" ? auth.ascApiKeyId : iosProfile?.ascApiKeyId,
35434
35739
  config: {
35435
35740
  bundleIdentifier: iosConfig.bundleIdentifier,
35436
35741
  ascAppId: iosProfile?.ascAppId,
@@ -35514,7 +35819,8 @@ const submitCommand = defineCommand({
35514
35819
  }
35515
35820
  const projectId = yield* readProjectId;
35516
35821
  const api = yield* apiClient;
35517
- const easProfile = yield* readSubmitProfile(yield* (yield* CliRuntime).cwd, args.profile);
35822
+ const projectRoot = yield* (yield* CliRuntime).cwd;
35823
+ const easProfile = yield* readSubmitProfile(projectRoot, args.profile);
35518
35824
  const archive = yield* resolveArchive(api, projectId, platform, {
35519
35825
  id: args.id,
35520
35826
  path: args.path,
@@ -35528,6 +35834,7 @@ const submitCommand = defineCommand({
35528
35834
  yield* runFlow(api, projectId, {
35529
35835
  platform,
35530
35836
  profile: args.profile,
35837
+ projectRoot,
35531
35838
  easProfile,
35532
35839
  archive,
35533
35840
  wait: args.wait,