codex-multi-auth 2.7.0 → 2.8.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.
Files changed (56) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/README.md +435 -433
  3. package/dist/lib/cli.d.ts +2 -0
  4. package/dist/lib/cli.d.ts.map +1 -1
  5. package/dist/lib/codex-cli/writer.d.ts +51 -0
  6. package/dist/lib/codex-cli/writer.d.ts.map +1 -1
  7. package/dist/lib/codex-cli/writer.js +89 -8
  8. package/dist/lib/codex-manager/login-menu-actions.d.ts.map +1 -1
  9. package/dist/lib/codex-manager/login-menu-actions.js +12 -1
  10. package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
  11. package/dist/lib/codex-manager/login-menu-data.js +19 -8
  12. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  13. package/dist/lib/codex-manager/repair-commands.js +57 -8
  14. package/dist/lib/forecast.d.ts.map +1 -1
  15. package/dist/lib/forecast.js +30 -4
  16. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  17. package/dist/lib/policy/runtime-policy.js +12 -3
  18. package/dist/lib/quota-readiness.d.ts +1 -0
  19. package/dist/lib/quota-readiness.d.ts.map +1 -1
  20. package/dist/lib/quota-readiness.js +12 -2
  21. package/dist/lib/refresh-lease.d.ts +12 -0
  22. package/dist/lib/refresh-lease.d.ts.map +1 -1
  23. package/dist/lib/refresh-lease.js +24 -2
  24. package/dist/lib/refresh-queue.d.ts.map +1 -1
  25. package/dist/lib/refresh-queue.js +29 -3
  26. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  27. package/dist/lib/request/request-transformer.js +13 -2
  28. package/dist/lib/request/response-handler.d.ts.map +1 -1
  29. package/dist/lib/request/response-handler.js +10 -1
  30. package/dist/lib/rotation.d.ts.map +1 -1
  31. package/dist/lib/rotation.js +8 -1
  32. package/dist/lib/runtime/account-check.d.ts +1 -1
  33. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  34. package/dist/lib/runtime/account-check.js +8 -0
  35. package/dist/lib/runtime/first-run.d.ts +12 -6
  36. package/dist/lib/runtime/first-run.d.ts.map +1 -1
  37. package/dist/lib/runtime/first-run.js +140 -9
  38. package/dist/lib/runtime/rotation-storage-meta.d.ts.map +1 -1
  39. package/dist/lib/runtime/rotation-storage-meta.js +8 -2
  40. package/dist/lib/storage/account-port.d.ts +2 -0
  41. package/dist/lib/storage/account-port.d.ts.map +1 -1
  42. package/dist/lib/storage/account-port.js +1 -0
  43. package/dist/lib/storage/import-export.d.ts +1 -0
  44. package/dist/lib/storage/import-export.d.ts.map +1 -1
  45. package/dist/lib/storage/import-export.js +25 -0
  46. package/dist/lib/storage/project-migration.d.ts +1 -1
  47. package/dist/lib/storage/project-migration.d.ts.map +1 -1
  48. package/dist/lib/storage/project-migration.js +28 -2
  49. package/dist/lib/storage.d.ts +10 -0
  50. package/dist/lib/storage.d.ts.map +1 -1
  51. package/dist/lib/storage.js +16 -1
  52. package/dist/lib/ui/auth-menu-builder.d.ts +10 -0
  53. package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
  54. package/dist/lib/ui/auth-menu-builder.js +29 -4
  55. package/package.json +1 -1
  56. package/scripts/codex.js +126 -13
@@ -24,6 +24,7 @@ export async function importAccountsSnapshot(params) {
24
24
  imported: normalized,
25
25
  maxAccounts: params.maxAccounts,
26
26
  deduplicateAccounts: params.deduplicateAccounts,
27
+ findMatchingAccountIndex: params.findMatchingAccountIndex,
27
28
  });
28
29
  await persist(merged.newStorage);
29
30
  return {
@@ -15,6 +15,7 @@ export declare function mergeImportedAccounts(params: {
15
15
  imported: AccountStorageV3;
16
16
  maxAccounts: number;
17
17
  deduplicateAccounts: (accounts: AccountStorageV3["accounts"]) => AccountStorageV3["accounts"];
18
+ findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined;
18
19
  }): {
19
20
  newStorage: AccountStorageV3;
20
21
  imported: number;
@@ -1 +1 @@
1
- {"version":3,"file":"import-export.d.ts","sourceRoot":"","sources":["../../../lib/storage/import-export.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAsD1D,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8C5B;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;CAClC,GAAG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB,CAgCA"}
1
+ {"version":3,"file":"import-export.d.ts","sourceRoot":"","sources":["../../../lib/storage/import-export.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAsD1D,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChB;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,CAAC;CACrE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8C5B;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CACpB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,KAClC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAGlC,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,CAAC;CACxB,GAAG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB,CA6DA"}
@@ -127,6 +127,14 @@ export async function readImportFile(params) {
127
127
  export function mergeImportedAccounts(params) {
128
128
  const existingAccounts = params.existing?.accounts ?? [];
129
129
  const existingActiveIndex = params.existing?.activeIndex ?? 0;
130
+ // Resolve the pinned account by IDENTITY before merging. The merged list is
131
+ // deduplicated below, which can move accounts to different positions, so a raw
132
+ // positional pin can end up selecting a DIFFERENT account (in range, wrong
133
+ // account) instead of the one the user pinned.
134
+ const existingPinnedIndex = params.existing?.pinnedAccountIndex;
135
+ const pinnedAccount = typeof existingPinnedIndex === "number"
136
+ ? existingAccounts[existingPinnedIndex]
137
+ : undefined;
130
138
  const merged = [...existingAccounts, ...params.imported.accounts];
131
139
  if (merged.length > params.maxAccounts) {
132
140
  const deduped = params.deduplicateAccounts(merged);
@@ -142,6 +150,23 @@ export function mergeImportedAccounts(params) {
142
150
  activeIndex: existingActiveIndex,
143
151
  activeIndexByFamily: params.existing?.activeIndexByFamily,
144
152
  };
153
+ // Preserve the user's manual pin (#474) and affinity generation across import.
154
+ // Rebuilding storage from scratch here previously dropped a `switch <n>` pin
155
+ // and reset affinityGeneration to 0, which lets a running proxy holding a
156
+ // higher in-memory generation clobber a newer CLI pin. The pin is re-resolved
157
+ // against the deduplicated list by identity — a range check alone would happily
158
+ // keep an in-range index that dedupe has repointed at another account. When the
159
+ // pinned account no longer resolves (removed or ambiguous) the pin is dropped
160
+ // rather than left pointing somewhere arbitrary.
161
+ if (pinnedAccount) {
162
+ const remappedPinnedIndex = params.findMatchingAccountIndex(deduplicatedAccounts, pinnedAccount);
163
+ if (remappedPinnedIndex !== undefined) {
164
+ newStorage.pinnedAccountIndex = remappedPinnedIndex;
165
+ }
166
+ }
167
+ if (typeof params.existing?.affinityGeneration === "number") {
168
+ newStorage.affinityGeneration = params.existing.affinityGeneration;
169
+ }
145
170
  const importedCount = deduplicatedAccounts.length - deduplicatedExistingAccounts.length;
146
171
  const skippedCount = params.imported.accounts.length - importedCount;
147
172
  return {
@@ -6,5 +6,5 @@ export declare function loadNormalizedStorageFromPath(path: string, label: strin
6
6
  }>;
7
7
  logWarn: (message: string, details: Record<string, unknown>) => void;
8
8
  }): Promise<AccountStorageV3 | null>;
9
- export declare function mergeStorageForMigration(current: AccountStorageV3 | null, incoming: AccountStorageV3, normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null): AccountStorageV3;
9
+ export declare function mergeStorageForMigration(current: AccountStorageV3 | null, incoming: AccountStorageV3, normalizeAccountStorage: (value: unknown) => AccountStorageV3 | null, findMatchingAccountIndex: (accounts: AccountStorageV3["accounts"], candidate: AccountStorageV3["accounts"][number]) => number | undefined): AccountStorageV3;
10
10
  //# sourceMappingURL=project-migration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-migration.d.ts","sourceRoot":"","sources":["../../../lib/storage/project-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,6BAA6B,CAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACL,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAC/C,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACpC,YAAY,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GACC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBlC;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,GAClE,gBAAgB,CAelB"}
1
+ {"version":3,"file":"project-migration.d.ts","sourceRoot":"","sources":["../../../lib/storage/project-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,wBAAsB,6BAA6B,CAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACL,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAC/C,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACpC,YAAY,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC,CAAC;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACrE,GACC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBlC;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,uBAAuB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,gBAAgB,GAAG,IAAI,EAIpE,wBAAwB,EAAE,CACzB,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAC3C,MAAM,GAAG,SAAS,GACrB,gBAAgB,CAuClB"}
@@ -20,18 +20,44 @@ export async function loadNormalizedStorageFromPath(path, label, deps) {
20
20
  return null;
21
21
  }
22
22
  }
23
- export function mergeStorageForMigration(current, incoming, normalizeAccountStorage) {
23
+ export function mergeStorageForMigration(current, incoming, normalizeAccountStorage,
24
+ // Injected like `normalizeAccountStorage` rather than imported from
25
+ // ../storage.js, which would put this leaf module in a dependency cycle with
26
+ // the storage barrel.
27
+ findMatchingAccountIndex) {
24
28
  if (!current) {
25
29
  return incoming;
26
30
  }
31
+ // Resolve the pinned account by IDENTITY before merging. normalizeAccountStorage
32
+ // deduplicates the merged list but only RANGE-validates the pin, so a raw
33
+ // positional pin that survives the range check can still end up pointing at a
34
+ // different account once dedupe shifts positions.
35
+ const pinnedAccount = typeof current.pinnedAccountIndex === "number"
36
+ ? current.accounts[current.pinnedAccountIndex]
37
+ : undefined;
27
38
  const merged = normalizeAccountStorage({
28
39
  version: 3,
29
40
  activeIndex: current.activeIndex,
30
41
  activeIndexByFamily: current.activeIndexByFamily,
31
42
  accounts: [...current.accounts, ...incoming.accounts],
43
+ // Carry the manual pin (#474) and affinity generation from the current
44
+ // storage through legacy-project migration; normalizeAccountStorage
45
+ // validates/clamps them against the merged account list. Omitting them
46
+ // dropped the pin and reset affinityGeneration to 0, which let a running
47
+ // proxy clobber a newer CLI pin.
48
+ pinnedAccountIndex: current.pinnedAccountIndex,
49
+ affinityGeneration: current.affinityGeneration,
32
50
  });
33
51
  if (!merged) {
34
52
  return current;
35
53
  }
36
- return merged;
54
+ if (!pinnedAccount) {
55
+ return merged;
56
+ }
57
+ // Re-point the pin at the account the user actually pinned; clear it when that
58
+ // account no longer resolves in the normalized list.
59
+ return {
60
+ ...merged,
61
+ pinnedAccountIndex: findMatchingAccountIndex(merged.accounts, pinnedAccount),
62
+ };
37
63
  }
@@ -35,6 +35,16 @@ type AccountMatchOptions = {
35
35
  allowUniqueAccountIdFallbackWithoutEmail?: boolean;
36
36
  };
37
37
  export declare function findMatchingAccountIndex<T extends Pick<AccountLike, "accountId" | "email" | "refreshToken">>(accounts: readonly T[], candidate: Pick<AccountLike, "accountId" | "email" | "refreshToken">, options?: AccountMatchOptions): number | undefined;
38
+ /**
39
+ * Re-resolve a manual pin after the account list changes (deletion / auto-removal).
40
+ * `pinnedAccountIndex` is a POSITION, so once accounts are removed or reordered the
41
+ * raw index silently points at a DIFFERENT account — or out of range, which wedges
42
+ * the pool. Capture the pinned account BEFORE the change and pass it here to follow
43
+ * it by identity: returns its new index, or `undefined` when it is no longer present
44
+ * (the pin is cleared, never left dangling at a stale slot). See #474 — a manual pin
45
+ * must keep pointing at the account the user chose, or be dropped.
46
+ */
47
+ export declare function reconcilePinnedAccountIndex(pinnedAccount: Pick<AccountLike, "accountId" | "email" | "refreshToken"> | undefined, nextAccounts: readonly Pick<AccountLike, "accountId" | "email" | "refreshToken">[]): number | undefined;
38
48
  export declare function resolveAccountSelectionIndex<T extends Pick<AccountLike, "accountId" | "email" | "refreshToken">>(accounts: readonly T[], candidate: Pick<AccountLike, "accountId" | "email" | "refreshToken">, fallbackIndex?: number): number;
39
49
  /**
40
50
  * Removes duplicate accounts, keeping the most recently used entry for each
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../lib/storage.ts"],"names":[],"mappings":"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"}
@@ -558,7 +558,7 @@ async function migrateLegacyProjectStorageIfNeeded(options) {
558
558
  if (liveCurrentStorageAfterLegacyRead.exists) {
559
559
  return liveCurrentStorageAfterLegacyRead.storage;
560
560
  }
561
- const mergedStorage = mergeStorageForMigration(targetStorage, legacyStorage, normalizeAccountStorage);
561
+ const mergedStorage = mergeStorageForMigration(targetStorage, legacyStorage, normalizeAccountStorage, findMatchingAccountIndex);
562
562
  const fallbackStorage = targetStorage ?? legacyStorage;
563
563
  if (commit) {
564
564
  try {
@@ -722,6 +722,20 @@ export function findMatchingAccountIndex(accounts, candidate, options = {}) {
722
722
  }
723
723
  return findUniqueAccountIdMatchIndex(accounts, candidateRef, options);
724
724
  }
725
+ /**
726
+ * Re-resolve a manual pin after the account list changes (deletion / auto-removal).
727
+ * `pinnedAccountIndex` is a POSITION, so once accounts are removed or reordered the
728
+ * raw index silently points at a DIFFERENT account — or out of range, which wedges
729
+ * the pool. Capture the pinned account BEFORE the change and pass it here to follow
730
+ * it by identity: returns its new index, or `undefined` when it is no longer present
731
+ * (the pin is cleared, never left dangling at a stale slot). See #474 — a manual pin
732
+ * must keep pointing at the account the user chose, or be dropped.
733
+ */
734
+ export function reconcilePinnedAccountIndex(pinnedAccount, nextAccounts) {
735
+ if (!pinnedAccount)
736
+ return undefined;
737
+ return findMatchingAccountIndex(nextAccounts, pinnedAccount);
738
+ }
725
739
  export function resolveAccountSelectionIndex(accounts, candidate, fallbackIndex = 0) {
726
740
  if (accounts.length === 0)
727
741
  return 0;
@@ -1707,6 +1721,7 @@ export async function importAccounts(filePath) {
1707
1721
  mergeImportedAccounts,
1708
1722
  maxAccounts: ACCOUNT_LIMITS.MAX_ACCOUNTS,
1709
1723
  deduplicateAccounts,
1724
+ findMatchingAccountIndex,
1710
1725
  logInfo: (message, details) => {
1711
1726
  log.info(message, details);
1712
1727
  },
@@ -17,6 +17,8 @@ export interface AccountInfo {
17
17
  quota5hResetAtMs?: number;
18
18
  quota7dLeftPercent?: number;
19
19
  quota7dResetAtMs?: number;
20
+ quotaPrimaryWindowMinutes?: number;
21
+ quotaSecondaryWindowMinutes?: number;
20
22
  quotaRateLimited?: boolean;
21
23
  quotaExhausted?: boolean;
22
24
  isCurrentAccount?: boolean;
@@ -84,6 +86,14 @@ export declare function accountTitle(account: AccountInfo): string;
84
86
  export declare function accountSearchText(account: AccountInfo): string;
85
87
  export declare function accountRowColor(account: AccountInfo): MenuItem<AuthMenuAction>["color"];
86
88
  export declare function currentMarkerLabel(marker: AccountCurrentMarker): string;
89
+ /**
90
+ * Resolve a quota-window label from its duration in minutes, matching the
91
+ * data-driven labels used by the compact quota summary (`Nd`/`Nh`/`Nm`). Falls
92
+ * back to the positional label when the duration is unknown (e.g. quota entries
93
+ * cached before window durations were persisted), so Plus/Pro rows keep their
94
+ * familiar "5h"/"7d" labels while a Codex Business monthly window renders "30d".
95
+ */
96
+ export declare function resolveQuotaWindowLabel(windowMinutes: number | undefined, fallbackLabel: string): string;
87
97
  export declare function formatAccountHint(account: AccountInfo, ui: ReturnType<typeof getUiRuntimeOptions>): string;
88
98
  export declare function authMenuFocusKey(action: AuthMenuAction): string;
89
99
  //# sourceMappingURL=auth-menu-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-menu-builder.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu-builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth-menu-builder.d.ts","sourceRoot":"","sources":["../../../lib/ui/auth-menu-builder.ts"],"names":[],"mappings":"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"}
@@ -243,6 +243,25 @@ function formatQuotaPercent(leftPercent, ui) {
243
243
  const tone = quotaToneFromLeftPercent(leftPercent);
244
244
  return paintUiText(ui, percentText, tone);
245
245
  }
246
+ /**
247
+ * Resolve a quota-window label from its duration in minutes, matching the
248
+ * data-driven labels used by the compact quota summary (`Nd`/`Nh`/`Nm`). Falls
249
+ * back to the positional label when the duration is unknown (e.g. quota entries
250
+ * cached before window durations were persisted), so Plus/Pro rows keep their
251
+ * familiar "5h"/"7d" labels while a Codex Business monthly window renders "30d".
252
+ */
253
+ export function resolveQuotaWindowLabel(windowMinutes, fallbackLabel) {
254
+ if (typeof windowMinutes !== "number" ||
255
+ !Number.isFinite(windowMinutes) ||
256
+ windowMinutes <= 0) {
257
+ return fallbackLabel;
258
+ }
259
+ if (windowMinutes % 1440 === 0)
260
+ return `${windowMinutes / 1440}d`;
261
+ if (windowMinutes % 60 === 0)
262
+ return `${windowMinutes / 60}h`;
263
+ return `${windowMinutes}m`;
264
+ }
246
265
  function formatQuotaWindow(label, leftPercent, resetAtMs, showCooldown, ui) {
247
266
  const labelText = ui.v2Enabled ? paintUiText(ui, label, "muted") : label;
248
267
  const bar = formatQuotaBar(leftPercent, ui);
@@ -263,14 +282,20 @@ function formatQuotaWindow(label, leftPercent, resetAtMs, showCooldown, ui) {
263
282
  function formatQuotaSummary(account, ui) {
264
283
  const summary = account.quotaSummary ?? "";
265
284
  const showCooldown = account.showQuotaCooldown !== false;
266
- const left5h = normalizeQuotaPercent(account.quota5hLeftPercent) ?? parseLeftPercentFromSummary(summary, "5h");
267
- const left7d = normalizeQuotaPercent(account.quota7dLeftPercent) ?? parseLeftPercentFromSummary(summary, "7d");
285
+ // Resolve the window labels before parsing the summary: the summary string is
286
+ // produced by `formatAccountQuotaSummary`, which already labels its segments by
287
+ // duration, so a Business row reads "30d 18%, ..." and never "5h ...". Searching
288
+ // it for a literal "5h"/"7d" would find nothing and silently drop the bar.
289
+ const primaryLabel = resolveQuotaWindowLabel(account.quotaPrimaryWindowMinutes, "5h");
290
+ const secondaryLabel = resolveQuotaWindowLabel(account.quotaSecondaryWindowMinutes, "7d");
291
+ const left5h = normalizeQuotaPercent(account.quota5hLeftPercent) ?? parseLeftPercentFromSummary(summary, primaryLabel);
292
+ const left7d = normalizeQuotaPercent(account.quota7dLeftPercent) ?? parseLeftPercentFromSummary(summary, secondaryLabel);
268
293
  const segments = [];
269
294
  if (left5h !== null || typeof account.quota5hResetAtMs === "number") {
270
- segments.push(formatQuotaWindow("5h", left5h, account.quota5hResetAtMs, showCooldown, ui));
295
+ segments.push(formatQuotaWindow(primaryLabel, left5h, account.quota5hResetAtMs, showCooldown, ui));
271
296
  }
272
297
  if (left7d !== null || typeof account.quota7dResetAtMs === "number") {
273
- segments.push(formatQuotaWindow("7d", left7d, account.quota7dResetAtMs, showCooldown, ui));
298
+ segments.push(formatQuotaWindow(secondaryLabel, left7d, account.quota7dResetAtMs, showCooldown, ui));
274
299
  }
275
300
  if (account.quotaRateLimited || summary.toLowerCase().includes("rate-limited")) {
276
301
  segments.push(ui.v2Enabled ? paintUiText(ui, "rate-limited", "danger") : `${ANSI.red}rate-limited${ANSI.reset}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-multi-auth",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "Codex CLI multi-account OAuth manager with account switching, health checks, runtime rotation, diagnostics, and recovery tools for @openai/codex",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/scripts/codex.js CHANGED
@@ -74,6 +74,10 @@ const APP_RUNTIME_HELPER_OWNER_PID_ENV =
74
74
  "CODEX_MULTI_AUTH_APP_ROTATION_OWNER_PID";
75
75
  const APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV =
76
76
  "CODEX_MULTI_AUTH_REAL_CODEX_HOME";
77
+ const APP_RUNTIME_HELPER_USE_CANONICAL_HOME_ENV =
78
+ "CODEX_MULTI_AUTH_APP_ROTATION_USE_CANONICAL_HOME";
79
+ const APP_SERVER_CONFIG_ARGS_ENV =
80
+ "CODEX_MULTI_AUTH_APP_SERVER_CONFIG_ARGS_JSON";
77
81
  const APP_RUNTIME_HELPER_STATUS_FILE =
78
82
  RUNTIME_CONSTANTS.APP_RUNTIME_HELPER_STATUS_FILE;
79
83
  const DEFAULT_APP_RUNTIME_HELPER_IDLE_MS = 12 * 60 * 60 * 1000;
@@ -1083,6 +1087,34 @@ async function loadRuntimeConfigTomlModule() {
1083
1087
  }
1084
1088
  }
1085
1089
 
1090
+ // Reconciles the persisted `cli_auth_credentials_store` in ~/.codex/config.toml
1091
+ // to "file" before forwarding.
1092
+ //
1093
+ // `buildForwardArgs` already appends `-c cli_auth_credentials_store="file"`, but
1094
+ // that only covers the process this wrapper spawns. Third-party front-ends
1095
+ // (CodexBar and friends) exec the official binary directly and read config.toml
1096
+ // instead, so a config still pointing at "keychain" keeps producing macOS login
1097
+ // keychain prompts (issue #641). Running the reconcile here self-heals a config
1098
+ // that something else flipped back — e.g. a fresh official `codex login`.
1099
+ //
1100
+ // Idempotent: the lib helper returns without writing when the value is already
1101
+ // "file", so this adds no write amplification on the common path.
1102
+ async function ensurePersistedCodexFileAuthStore() {
1103
+ const forceFileAuthStore = (process.env.CODEX_MULTI_AUTH_FORCE_FILE_AUTH_STORE ?? "1").trim() !== "0";
1104
+ if (!forceFileAuthStore) return;
1105
+
1106
+ try {
1107
+ const mod = await import("../dist/lib/codex-cli/writer.js");
1108
+ if (typeof mod.ensureCodexCliFileAuthStore !== "function") return;
1109
+ await mod.ensureCodexCliFileAuthStore();
1110
+ } catch (error) {
1111
+ if (isModuleNotFoundError(error)) return;
1112
+ // Best effort only. A read-only or locked config.toml (Windows
1113
+ // EPERM/EBUSY) must never block the wrapped command: the per-invocation
1114
+ // `-c` override still protects this run.
1115
+ }
1116
+ }
1117
+
1086
1118
  async function autoSyncManagerActiveSelectionIfEnabled() {
1087
1119
  const enabled = (process.env.CODEX_MULTI_AUTH_AUTO_SYNC_ON_STARTUP ?? "1").trim() !== "0";
1088
1120
  if (!enabled) return;
@@ -3490,6 +3522,38 @@ function createRuntimeRotationProxyCodexHome(
3490
3522
  };
3491
3523
  }
3492
3524
 
3525
+ function createRuntimeRotationProxyCanonicalCodexHome(
3526
+ baseEnv,
3527
+ proxyBaseUrl,
3528
+ clientApiKey,
3529
+ configTomlModule,
3530
+ ) {
3531
+ const originalCodexHome = resolveRuntimeRotationProxyOriginalCodexHome(baseEnv);
3532
+ const providerTable = `model_providers.${RUNTIME_ROTATION_PROXY_PROVIDER_ID}`;
3533
+ const configArgs = [
3534
+ `${providerTable}.name=${configTomlModule.tomlStringLiteral(APP_SERVER_ACCOUNT_DISPLAY_NAME)}`,
3535
+ `${providerTable}.base_url=${configTomlModule.tomlStringLiteral(proxyBaseUrl)}`,
3536
+ `${providerTable}.env_key=${configTomlModule.tomlStringLiteral("OPENAI_API_KEY")}`,
3537
+ `${providerTable}.requires_openai_auth=false`,
3538
+ `${providerTable}.wire_api=${configTomlModule.tomlStringLiteral("responses")}`,
3539
+ "disable_response_storage=false",
3540
+ ].flatMap((assignment) => ["-c", assignment]);
3541
+
3542
+ return {
3543
+ args: configArgs,
3544
+ env: {
3545
+ ...baseEnv,
3546
+ CODEX_HOME: originalCodexHome,
3547
+ OPENAI_API_KEY: clientApiKey,
3548
+ CODEX_MULTI_AUTH_DIR: resolveRuntimeRotationOriginalMultiAuthDir(
3549
+ originalCodexHome,
3550
+ baseEnv,
3551
+ ),
3552
+ },
3553
+ cleanup: () => {},
3554
+ };
3555
+ }
3556
+
3493
3557
  function appendNodeImportOption(nodeOptions, preloadPath) {
3494
3558
  const importOption = `--import=${pathToFileURL(preloadPath).href}`;
3495
3559
  const trimmed = (nodeOptions ?? "").trim();
@@ -3504,15 +3568,26 @@ function createRuntimeRotationAppServerPreloadSource(wrapperScriptPath) {
3504
3568
  "",
3505
3569
  `const wrapperScriptPath = ${JSON.stringify(wrapperScriptPath)};`,
3506
3570
  `const accountLabelEnv = ${JSON.stringify(APP_SERVER_ACCOUNT_LABEL_ENV)};`,
3571
+ `const configArgsEnv = ${JSON.stringify(APP_SERVER_CONFIG_ARGS_ENV)};`,
3572
+ "let configArgs = [];",
3573
+ "try {",
3574
+ ' const parsed = JSON.parse(process.env[configArgsEnv] ?? "[]");',
3575
+ " if (Array.isArray(parsed)) {",
3576
+ ' configArgs = parsed.filter((arg) => typeof arg === "string");',
3577
+ " }",
3578
+ "} catch {",
3579
+ " // Ignore malformed internal metadata and preserve app-server startup.",
3580
+ "}",
3507
3581
  "const rawArgs = process.argv.slice(1);",
3508
3582
  "const firstArg = rawArgs[0] ?? \"\";",
3509
3583
  'if (basename(firstArg).toLowerCase() === "app-server") {',
3510
- ' const args = ["app-server", ...rawArgs.slice(1)];',
3584
+ ' const args = ["app-server", ...rawArgs.slice(1), ...configArgs];',
3511
3585
  " const env = {",
3512
3586
  " ...process.env,",
3513
3587
  ' CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY: "0",',
3514
3588
  " [accountLabelEnv]: \"1\",",
3515
3589
  " };",
3590
+ " delete env[configArgsEnv];",
3516
3591
  " const child = spawn(process.execPath, [wrapperScriptPath, ...args], {",
3517
3592
  " stdio: \"inherit\",",
3518
3593
  " env,",
@@ -3558,7 +3633,7 @@ function sweepStaleRuntimeRotationAppServerShimDirs(shimRootDir) {
3558
3633
  }
3559
3634
  }
3560
3635
 
3561
- function installRuntimeRotationAppServerCliShim(forwardedEnv) {
3636
+ function installRuntimeRotationAppServerCliShim(forwardedEnv, configArgs = []) {
3562
3637
  const shadowCodexHome = forwardedEnv.CODEX_HOME;
3563
3638
  if (!shadowCodexHome) {
3564
3639
  throw new Error("runtime app-server shim requires CODEX_HOME");
@@ -3615,6 +3690,11 @@ function installRuntimeRotationAppServerCliShim(forwardedEnv) {
3615
3690
  );
3616
3691
  forwardedEnv.CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY = "0";
3617
3692
  forwardedEnv[APP_SERVER_ACCOUNT_LABEL_ENV] = "1";
3693
+ if (configArgs.length > 0) {
3694
+ forwardedEnv[APP_SERVER_CONFIG_ARGS_ENV] = JSON.stringify(configArgs);
3695
+ } else {
3696
+ delete forwardedEnv[APP_SERVER_CONFIG_ARGS_ENV];
3697
+ }
3618
3698
  return shimDir;
3619
3699
  }
3620
3700
 
@@ -3715,6 +3795,7 @@ function pickRuntimeRotationAppHelperEnv(env) {
3715
3795
  "CODEX_MULTI_AUTH_REAL_CODEX_BIN",
3716
3796
  "CODEX_MULTI_AUTH_RUNTIME_ROTATION_PROXY",
3717
3797
  APP_SERVER_ACCOUNT_LABEL_ENV,
3798
+ APP_SERVER_CONFIG_ARGS_ENV,
3718
3799
  ]) {
3719
3800
  if (env[name]) {
3720
3801
  picked[name] = env[name];
@@ -3776,7 +3857,7 @@ function createRuntimeRotationAppHelperStatus({
3776
3857
 
3777
3858
  async function runRuntimeRotationAppHelper() {
3778
3859
  let proxyServer = null;
3779
- let shadowContext = null;
3860
+ let runtimeContext = null;
3780
3861
  let appServerShimDir = null;
3781
3862
  let statusTimer = null;
3782
3863
  let closing = false;
@@ -3813,7 +3894,7 @@ async function runRuntimeRotationAppHelper() {
3813
3894
  }
3814
3895
  appServerShimDir = null;
3815
3896
  }
3816
- shadowContext?.cleanup?.();
3897
+ runtimeContext?.cleanup?.();
3817
3898
  } finally {
3818
3899
  try {
3819
3900
  await proxyServer?.close?.();
@@ -3844,13 +3925,33 @@ async function runRuntimeRotationAppHelper() {
3844
3925
  }
3845
3926
  const clientApiKey = createRuntimeRotationProxyClientApiKey();
3846
3927
  proxyServer = await proxyModule.startRuntimeRotationProxy({ clientApiKey });
3847
- shadowContext = createRuntimeRotationProxyCodexHome(
3848
- process.env,
3849
- proxyServer.baseUrl,
3850
- clientApiKey,
3851
- configTomlModule,
3928
+ const useCanonicalHome =
3929
+ (process.env[APP_RUNTIME_HELPER_USE_CANONICAL_HOME_ENV] ?? "").trim() ===
3930
+ "1";
3931
+ runtimeContext = useCanonicalHome
3932
+ ? createRuntimeRotationProxyCanonicalCodexHome(
3933
+ process.env,
3934
+ proxyServer.baseUrl,
3935
+ clientApiKey,
3936
+ configTomlModule,
3937
+ )
3938
+ : createRuntimeRotationProxyCodexHome(
3939
+ process.env,
3940
+ proxyServer.baseUrl,
3941
+ clientApiKey,
3942
+ configTomlModule,
3943
+ );
3944
+ const appServerConfigArgs = useCanonicalHome
3945
+ ? [
3946
+ ...(runtimeContext.args ?? []),
3947
+ "-c",
3948
+ `model_provider=${configTomlModule.tomlStringLiteral(RUNTIME_ROTATION_PROXY_PROVIDER_ID)}`,
3949
+ ]
3950
+ : [];
3951
+ appServerShimDir = installRuntimeRotationAppServerCliShim(
3952
+ runtimeContext.env,
3953
+ appServerConfigArgs,
3852
3954
  );
3853
- appServerShimDir = installRuntimeRotationAppServerCliShim(shadowContext.env);
3854
3955
  lastRequestCount = proxyServer.getStatus?.().totalRequests ?? 0;
3855
3956
  publishStatus("running");
3856
3957
  process.stdout.write(
@@ -3859,7 +3960,8 @@ async function runRuntimeRotationAppHelper() {
3859
3960
  pid: process.pid,
3860
3961
  baseUrl: proxyServer.baseUrl,
3861
3962
  statusPath: resolveRuntimeRotationAppHelperStatusPath(),
3862
- env: pickRuntimeRotationAppHelperEnv(shadowContext.env),
3963
+ args: runtimeContext.args ?? [],
3964
+ env: pickRuntimeRotationAppHelperEnv(runtimeContext.env),
3863
3965
  })}\n`,
3864
3966
  );
3865
3967
 
@@ -3920,7 +4022,7 @@ function stopRuntimeRotationAppHelper(helper) {
3920
4022
  return waitForRuntimeRotationAppHelperExit(helper);
3921
4023
  }
3922
4024
 
3923
- function startRuntimeRotationAppHelper(baseContext) {
4025
+ function startRuntimeRotationAppHelper(baseContext, options = {}) {
3924
4026
  const realCodexHome =
3925
4027
  baseContext.originalCodexHome ??
3926
4028
  resolveRuntimeRotationProxyOriginalCodexHome(baseContext.env);
@@ -3940,6 +4042,8 @@ function startRuntimeRotationAppHelper(baseContext) {
3940
4042
  ),
3941
4043
  [APP_RUNTIME_HELPER_OWNER_PID_ENV]: String(process.pid),
3942
4044
  [APP_RUNTIME_HELPER_REAL_CODEX_HOME_ENV]: realCodexHome,
4045
+ [APP_RUNTIME_HELPER_USE_CANONICAL_HOME_ENV]:
4046
+ options.useCanonicalHome === true ? "1" : "0",
3943
4047
  },
3944
4048
  stdio: ["ignore", "pipe", "pipe"],
3945
4049
  detached: true,
@@ -4005,8 +4109,14 @@ async function createRuntimeRotationAppHelperContext(
4005
4109
  options = {},
4006
4110
  ) {
4007
4111
  const startedAt = Date.now();
4008
- const { helper, message } = await startRuntimeRotationAppHelper(baseContext);
4112
+ const { helper, message } = await startRuntimeRotationAppHelper(
4113
+ baseContext,
4114
+ options,
4115
+ );
4009
4116
  const helperEnv = message.env ?? {};
4117
+ const helperArgs = Array.isArray(message.args)
4118
+ ? message.args.filter((arg) => typeof arg === "string")
4119
+ : [];
4010
4120
  const detachGraceMs = resolveRuntimeRotationAppHelperDetachGraceMs(baseContext.env);
4011
4121
 
4012
4122
  const cleanup = async ({ exitCode } = {}) => {
@@ -4023,6 +4133,7 @@ async function createRuntimeRotationAppHelperContext(
4023
4133
  return {
4024
4134
  args: [
4025
4135
  ...baseContext.args,
4136
+ ...helperArgs,
4026
4137
  "-c",
4027
4138
  `model_provider=${configTomlModule.tomlStringLiteral(RUNTIME_ROTATION_PROXY_PROVIDER_ID)}`,
4028
4139
  ],
@@ -4063,6 +4174,7 @@ async function createRuntimeRotationProxyContextIfEnabled(
4063
4174
  if (isCodexInteractiveTuiCommand(rawArgs)) {
4064
4175
  return createRuntimeRotationAppHelperContext(baseContext, configTomlModule, {
4065
4176
  detachOnExit: true,
4177
+ useCanonicalHome: true,
4066
4178
  });
4067
4179
  }
4068
4180
 
@@ -5045,6 +5157,7 @@ async function main() {
5045
5157
  }
5046
5158
  const forwardArgs = forcedAccount.forwardArgs;
5047
5159
 
5160
+ await ensurePersistedCodexFileAuthStore();
5048
5161
  await autoSyncManagerActiveSelectionIfEnabled();
5049
5162
  await maybePrintForwardStatusLine(forwardArgs);
5050
5163
  maybeRefreshQuotaCacheInBackground();