codex-multi-auth 1.2.7 → 1.3.1

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 (160) hide show
  1. package/README.md +5 -3
  2. package/config/codex-legacy.json +85 -10
  3. package/config/codex-modern.json +35 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +49 -22
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/accounts.d.ts +47 -0
  8. package/dist/lib/accounts.d.ts.map +1 -1
  9. package/dist/lib/accounts.js +277 -33
  10. package/dist/lib/accounts.js.map +1 -1
  11. package/dist/lib/auth/auth.d.ts +35 -0
  12. package/dist/lib/auth/auth.d.ts.map +1 -1
  13. package/dist/lib/auth/auth.js +140 -8
  14. package/dist/lib/auth/auth.js.map +1 -1
  15. package/dist/lib/auth/server.d.ts +1 -1
  16. package/dist/lib/auth/server.d.ts.map +1 -1
  17. package/dist/lib/auth/server.js +19 -11
  18. package/dist/lib/auth/server.js.map +1 -1
  19. package/dist/lib/capability-policy.d.ts.map +1 -1
  20. package/dist/lib/capability-policy.js +7 -2
  21. package/dist/lib/capability-policy.js.map +1 -1
  22. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  23. package/dist/lib/codex-manager/commands/status.js +13 -5
  24. package/dist/lib/codex-manager/commands/status.js.map +1 -1
  25. package/dist/lib/codex-manager/commands/verify.d.ts +59 -0
  26. package/dist/lib/codex-manager/commands/verify.d.ts.map +1 -0
  27. package/dist/lib/codex-manager/commands/verify.js +332 -0
  28. package/dist/lib/codex-manager/commands/verify.js.map +1 -0
  29. package/dist/lib/codex-manager/commands/why-selected.d.ts +57 -0
  30. package/dist/lib/codex-manager/commands/why-selected.d.ts.map +1 -0
  31. package/dist/lib/codex-manager/commands/why-selected.js +198 -0
  32. package/dist/lib/codex-manager/commands/why-selected.js.map +1 -0
  33. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  34. package/dist/lib/codex-manager/help.js +4 -0
  35. package/dist/lib/codex-manager/help.js.map +1 -1
  36. package/dist/lib/codex-manager/settings-hub/backend.d.ts +9 -0
  37. package/dist/lib/codex-manager/settings-hub/backend.d.ts.map +1 -0
  38. package/dist/lib/codex-manager/settings-hub/backend.js +79 -0
  39. package/dist/lib/codex-manager/settings-hub/backend.js.map +1 -0
  40. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts +24 -0
  41. package/dist/lib/codex-manager/settings-hub/dashboard.d.ts.map +1 -0
  42. package/dist/lib/codex-manager/settings-hub/dashboard.js +223 -0
  43. package/dist/lib/codex-manager/settings-hub/dashboard.js.map +1 -0
  44. package/dist/lib/codex-manager/settings-hub/experimental.d.ts +18 -0
  45. package/dist/lib/codex-manager/settings-hub/experimental.d.ts.map +1 -0
  46. package/dist/lib/codex-manager/settings-hub/experimental.js +78 -0
  47. package/dist/lib/codex-manager/settings-hub/experimental.js.map +1 -0
  48. package/dist/lib/codex-manager/settings-hub/index.d.ts +34 -0
  49. package/dist/lib/codex-manager/settings-hub/index.d.ts.map +1 -0
  50. package/dist/lib/codex-manager/settings-hub/index.js +81 -0
  51. package/dist/lib/codex-manager/settings-hub/index.js.map +1 -0
  52. package/dist/lib/codex-manager/settings-hub/shared.d.ts +40 -0
  53. package/dist/lib/codex-manager/settings-hub/shared.d.ts.map +1 -0
  54. package/dist/lib/codex-manager/settings-hub/shared.js +103 -0
  55. package/dist/lib/codex-manager/settings-hub/shared.js.map +1 -0
  56. package/dist/lib/codex-manager/settings-hub.d.ts +1 -64
  57. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  58. package/dist/lib/codex-manager/settings-hub.js +4 -515
  59. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  60. package/dist/lib/codex-manager.d.ts.map +1 -1
  61. package/dist/lib/codex-manager.js +97 -15
  62. package/dist/lib/codex-manager.js.map +1 -1
  63. package/dist/lib/config.d.ts +14 -0
  64. package/dist/lib/config.d.ts.map +1 -1
  65. package/dist/lib/config.js +21 -9
  66. package/dist/lib/config.js.map +1 -1
  67. package/dist/lib/health.d.ts.map +1 -1
  68. package/dist/lib/health.js.map +1 -1
  69. package/dist/lib/prompts/codex.d.ts +2 -2
  70. package/dist/lib/prompts/codex.js +2 -2
  71. package/dist/lib/recovery/storage.d.ts +13 -0
  72. package/dist/lib/recovery/storage.d.ts.map +1 -1
  73. package/dist/lib/recovery/storage.js +144 -9
  74. package/dist/lib/recovery/storage.js.map +1 -1
  75. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  76. package/dist/lib/request/fetch-helpers.js +15 -2
  77. package/dist/lib/request/fetch-helpers.js.map +1 -1
  78. package/dist/lib/request/helpers/model-map.d.ts +3 -3
  79. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  80. package/dist/lib/request/helpers/model-map.js +146 -70
  81. package/dist/lib/request/helpers/model-map.js.map +1 -1
  82. package/dist/lib/request/rate-limit-backoff.d.ts +14 -1
  83. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -1
  84. package/dist/lib/request/rate-limit-backoff.js +26 -5
  85. package/dist/lib/request/rate-limit-backoff.js.map +1 -1
  86. package/dist/lib/request/response-handler.d.ts.map +1 -1
  87. package/dist/lib/request/response-handler.js +82 -25
  88. package/dist/lib/request/response-handler.js.map +1 -1
  89. package/dist/lib/rotation.d.ts +55 -1
  90. package/dist/lib/rotation.d.ts.map +1 -1
  91. package/dist/lib/rotation.js +161 -23
  92. package/dist/lib/rotation.js.map +1 -1
  93. package/dist/lib/routing-mutex.d.ts +107 -0
  94. package/dist/lib/routing-mutex.d.ts.map +1 -0
  95. package/dist/lib/routing-mutex.js +104 -0
  96. package/dist/lib/routing-mutex.js.map +1 -0
  97. package/dist/lib/runtime/storage-scope.d.ts.map +1 -1
  98. package/dist/lib/runtime/storage-scope.js +17 -1
  99. package/dist/lib/runtime/storage-scope.js.map +1 -1
  100. package/dist/lib/schemas.d.ts +125 -0
  101. package/dist/lib/schemas.d.ts.map +1 -1
  102. package/dist/lib/schemas.js +139 -5
  103. package/dist/lib/schemas.js.map +1 -1
  104. package/dist/lib/shutdown.d.ts.map +1 -1
  105. package/dist/lib/shutdown.js +19 -10
  106. package/dist/lib/shutdown.js.map +1 -1
  107. package/dist/lib/storage/account-clear.d.ts.map +1 -1
  108. package/dist/lib/storage/account-clear.js +7 -1
  109. package/dist/lib/storage/account-clear.js.map +1 -1
  110. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -1
  111. package/dist/lib/storage/flagged-storage-file.js +17 -1
  112. package/dist/lib/storage/flagged-storage-file.js.map +1 -1
  113. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -1
  114. package/dist/lib/storage/flagged-storage-io.js +24 -10
  115. package/dist/lib/storage/flagged-storage-io.js.map +1 -1
  116. package/dist/lib/storage/import-export.d.ts.map +1 -1
  117. package/dist/lib/storage/import-export.js +31 -5
  118. package/dist/lib/storage/import-export.js.map +1 -1
  119. package/dist/lib/storage/paths.d.ts.map +1 -1
  120. package/dist/lib/storage/paths.js +62 -7
  121. package/dist/lib/storage/paths.js.map +1 -1
  122. package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -1
  123. package/dist/lib/storage/snapshot-inspectors.js +23 -16
  124. package/dist/lib/storage/snapshot-inspectors.js.map +1 -1
  125. package/dist/lib/storage/storage-parser.d.ts +17 -0
  126. package/dist/lib/storage/storage-parser.d.ts.map +1 -1
  127. package/dist/lib/storage/storage-parser.js +30 -1
  128. package/dist/lib/storage/storage-parser.js.map +1 -1
  129. package/dist/lib/storage.d.ts +1 -1
  130. package/dist/lib/storage.d.ts.map +1 -1
  131. package/dist/lib/storage.js +45 -26
  132. package/dist/lib/storage.js.map +1 -1
  133. package/dist/lib/ui/copy.d.ts +1 -1
  134. package/dist/lib/ui/copy.d.ts.map +1 -1
  135. package/dist/lib/ui/copy.js +2 -1
  136. package/dist/lib/ui/copy.js.map +1 -1
  137. package/dist/lib/ui/select.d.ts.map +1 -1
  138. package/dist/lib/ui/select.js +5 -1
  139. package/dist/lib/ui/select.js.map +1 -1
  140. package/dist/lib/unified-settings.d.ts.map +1 -1
  141. package/dist/lib/unified-settings.js +69 -14
  142. package/dist/lib/unified-settings.js.map +1 -1
  143. package/package.json +5 -5
  144. package/scripts/codex.js +330 -65
  145. package/dist/lib/codex-manager/auth-commands.d.ts +0 -72
  146. package/dist/lib/codex-manager/auth-commands.d.ts.map +0 -1
  147. package/dist/lib/codex-manager/auth-commands.js +0 -643
  148. package/dist/lib/codex-manager/auth-commands.js.map +0 -1
  149. package/dist/lib/codex-manager/commands/doctor.d.ts +0 -85
  150. package/dist/lib/codex-manager/commands/doctor.d.ts.map +0 -1
  151. package/dist/lib/codex-manager/commands/doctor.js +0 -424
  152. package/dist/lib/codex-manager/commands/doctor.js.map +0 -1
  153. package/dist/lib/codex-manager/commands/fix.d.ts +0 -92
  154. package/dist/lib/codex-manager/commands/fix.d.ts.map +0 -1
  155. package/dist/lib/codex-manager/commands/fix.js +0 -347
  156. package/dist/lib/codex-manager/commands/fix.js.map +0 -1
  157. package/dist/lib/codex-manager/commands/verify-flagged.d.ts +0 -69
  158. package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +0 -1
  159. package/dist/lib/codex-manager/commands/verify-flagged.js +0 -251
  160. package/dist/lib/codex-manager/commands/verify-flagged.js.map +0 -1
@@ -1,643 +0,0 @@
1
- import { isBrowserLaunchSuppressed } from "../auth/browser.js";
2
- import { extractAccountEmail, extractAccountId, formatAccountLabel, sanitizeEmail, } from "../accounts.js";
3
- import { promptAddAnotherAccount, promptLoginMode } from "../cli.js";
4
- import { ACCOUNT_LIMITS } from "../constants.js";
5
- import { loadDashboardDisplaySettings, } from "../dashboard-settings.js";
6
- import { evaluateForecastAccounts, recommendForecastAccount, } from "../forecast.js";
7
- import { loadQuotaCache } from "../quota-cache.js";
8
- import { fetchCodexQuotaSnapshot } from "../quota-probe.js";
9
- import { queuedRefresh } from "../refresh-queue.js";
10
- import { getNamedBackups, formatStorageErrorHint, getStoragePath, loadAccounts, loadFlaggedAccounts, restoreAccountsFromBackup, saveAccounts, setStoragePath, StorageError, } from "../storage.js";
11
- import { setCodexCliActiveSelection } from "../codex-cli/writer.js";
12
- import { MODEL_FAMILIES } from "../prompts/codex.js";
13
- import { RefreshLeaseCoordinator } from "../refresh-lease.js";
14
- import { UI_COPY } from "../ui/copy.js";
15
- import { confirm } from "../ui/confirm.js";
16
- import { applyUiThemeFromDashboardSettings, configureUnifiedSettings, } from "./settings-hub.js";
17
- import { parseAuthLoginArgs, parseBestArgs, printBestUsage, printUsage, } from "./help.js";
18
- const liveBestLeaseCoordinator = RefreshLeaseCoordinator.fromEnvironment();
19
- const serializedLiveBestByStorage = new Map();
20
- async function withSerializedBestLiveRun(storagePath, action) {
21
- const queueEntry = serializedLiveBestByStorage.get(storagePath) ?? { tail: Promise.resolve() };
22
- serializedLiveBestByStorage.set(storagePath, queueEntry);
23
- const waitForPrior = queueEntry.tail.catch(() => undefined);
24
- let releaseQueue = () => undefined;
25
- const currentTail = new Promise((resolve) => {
26
- releaseQueue = resolve;
27
- });
28
- queueEntry.tail = currentTail;
29
- await waitForPrior;
30
- let lease = null;
31
- try {
32
- lease = await liveBestLeaseCoordinator.acquire(`codex-auth-best-live:${storagePath}`);
33
- return await action();
34
- }
35
- finally {
36
- try {
37
- if (lease) {
38
- await lease.release();
39
- }
40
- }
41
- finally {
42
- releaseQueue();
43
- if (serializedLiveBestByStorage.get(storagePath) === queueEntry &&
44
- queueEntry.tail === currentTail) {
45
- serializedLiveBestByStorage.delete(storagePath);
46
- }
47
- }
48
- }
49
- }
50
- function clampPreservedActiveIndexByFamily(storage, targetIndex) {
51
- const maxIndex = Math.max(0, storage.accounts.length - 1);
52
- const existingByFamily = storage.activeIndexByFamily ?? {};
53
- const nextByFamily = {};
54
- for (const family of MODEL_FAMILIES) {
55
- const candidate = existingByFamily[family];
56
- if (typeof candidate !== "number" || !Number.isInteger(candidate))
57
- continue;
58
- nextByFamily[family] = Math.min(maxIndex, Math.max(0, candidate));
59
- }
60
- nextByFamily.codex = targetIndex;
61
- storage.activeIndexByFamily = nextByFamily;
62
- }
63
- export async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, switchReason, initialSyncIdToken, preserveActiveIndexByFamily = false, helpers, }) {
64
- const account = storage.accounts[targetIndex];
65
- if (!account) {
66
- throw new Error(`Account ${parsed} not found.`);
67
- }
68
- const wasDisabled = account.enabled === false;
69
- if (wasDisabled) {
70
- account.enabled = true;
71
- }
72
- storage.activeIndex = targetIndex;
73
- if (storage.activeIndexByFamily && preserveActiveIndexByFamily) {
74
- clampPreservedActiveIndexByFamily(storage, targetIndex);
75
- }
76
- else {
77
- storage.activeIndexByFamily = {};
78
- storage.activeIndexByFamily.codex = targetIndex;
79
- }
80
- const switchNow = Date.now();
81
- let syncAccessToken = account.accessToken;
82
- let syncRefreshToken = account.refreshToken;
83
- let syncExpiresAt = account.expiresAt;
84
- let syncIdToken = initialSyncIdToken;
85
- if (!helpers.hasUsableAccessToken(account, switchNow)) {
86
- const refreshResult = await queuedRefresh(account.refreshToken);
87
- if (refreshResult.type === "success") {
88
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
89
- const tokenAccountId = extractAccountId(refreshResult.access);
90
- account.refreshToken = refreshResult.refresh;
91
- account.accessToken = refreshResult.access;
92
- account.expiresAt = refreshResult.expires;
93
- if (refreshedEmail)
94
- account.email = refreshedEmail;
95
- helpers.applyTokenAccountIdentity(account, tokenAccountId);
96
- syncAccessToken = refreshResult.access;
97
- syncRefreshToken = refreshResult.refresh;
98
- syncExpiresAt = refreshResult.expires;
99
- syncIdToken = refreshResult.idToken;
100
- }
101
- else {
102
- console.warn(`Switch validation refresh failed for account ${parsed}: ${helpers.normalizeFailureDetail(refreshResult.message, refreshResult.reason)}.`);
103
- }
104
- }
105
- account.lastUsed = switchNow;
106
- account.lastSwitchReason = switchReason;
107
- await saveAccounts(storage);
108
- const synced = await setCodexCliActiveSelection({
109
- accountId: account.accountId,
110
- email: account.email,
111
- accessToken: syncAccessToken,
112
- refreshToken: syncRefreshToken,
113
- expiresAt: syncExpiresAt,
114
- ...(syncIdToken ? { idToken: syncIdToken } : {}),
115
- });
116
- return { synced, wasDisabled };
117
- }
118
- export async function runSwitch(args, helpers) {
119
- setStoragePath(null);
120
- const indexArg = args[0];
121
- if (!indexArg) {
122
- console.error("Missing index. Usage: codex auth switch <index>");
123
- return 1;
124
- }
125
- const parsed = Number.parseInt(indexArg, 10);
126
- if (!Number.isFinite(parsed) || parsed < 1) {
127
- console.error(`Invalid index: ${indexArg}`);
128
- return 1;
129
- }
130
- const targetIndex = parsed - 1;
131
- const storage = await loadAccounts();
132
- if (!storage || storage.accounts.length === 0) {
133
- console.error("No accounts configured.");
134
- return 1;
135
- }
136
- if (targetIndex < 0 || targetIndex >= storage.accounts.length) {
137
- console.error(`Index out of range. Valid range: 1-${storage.accounts.length}`);
138
- return 1;
139
- }
140
- const account = storage.accounts[targetIndex];
141
- if (!account) {
142
- console.error(`Account ${parsed} not found.`);
143
- return 1;
144
- }
145
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
146
- storage,
147
- targetIndex,
148
- parsed,
149
- switchReason: "rotation",
150
- helpers,
151
- });
152
- if (!synced) {
153
- console.warn(`Switched account ${parsed} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`);
154
- }
155
- console.log(`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
156
- return 0;
157
- }
158
- export async function runBest(args, helpers) {
159
- const parsedArgs = parseBestArgs(args);
160
- if (!parsedArgs.ok) {
161
- if (parsedArgs.reason === "help") {
162
- printBestUsage();
163
- return 0;
164
- }
165
- console.error(parsedArgs.message);
166
- printBestUsage();
167
- return 1;
168
- }
169
- const options = parsedArgs.options;
170
- if (options.modelProvided && !options.live) {
171
- console.error("--model requires --live for codex auth best");
172
- printBestUsage();
173
- return 1;
174
- }
175
- setStoragePath(null);
176
- const execute = async () => {
177
- const storage = await loadAccounts();
178
- if (!storage || storage.accounts.length === 0) {
179
- if (options.json) {
180
- console.log(JSON.stringify({ error: "No accounts configured." }, null, 2));
181
- }
182
- else {
183
- console.log("No accounts configured.");
184
- }
185
- return 1;
186
- }
187
- const now = Date.now();
188
- const refreshFailures = new Map();
189
- const liveQuotaByIndex = new Map();
190
- const probeIdTokenByIndex = new Map();
191
- const probeRefreshedIndices = new Set();
192
- const probeErrors = [];
193
- let changed = false;
194
- const printProbeNotes = () => {
195
- if (probeErrors.length === 0)
196
- return;
197
- console.log(`Live check notes (${probeErrors.length}):`);
198
- for (const error of probeErrors) {
199
- console.log(` - ${error}`);
200
- }
201
- };
202
- const persistProbeChangesIfNeeded = async () => {
203
- if (!changed)
204
- return;
205
- await saveAccounts(storage);
206
- changed = false;
207
- };
208
- for (let i = 0; i < storage.accounts.length; i += 1) {
209
- const account = storage.accounts[i];
210
- if (!account || !options.live)
211
- continue;
212
- if (account.enabled === false)
213
- continue;
214
- let probeAccessToken = account.accessToken;
215
- let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
216
- if (!helpers.hasUsableAccessToken(account, now)) {
217
- const refreshResult = await queuedRefresh(account.refreshToken);
218
- if (refreshResult.type !== "success") {
219
- refreshFailures.set(i, {
220
- ...refreshResult,
221
- message: helpers.normalizeFailureDetail(refreshResult.message, refreshResult.reason),
222
- });
223
- continue;
224
- }
225
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
226
- const refreshedAccountId = extractAccountId(refreshResult.access);
227
- if (account.refreshToken !== refreshResult.refresh) {
228
- account.refreshToken = refreshResult.refresh;
229
- changed = true;
230
- }
231
- if (account.accessToken !== refreshResult.access) {
232
- account.accessToken = refreshResult.access;
233
- changed = true;
234
- }
235
- if (account.expiresAt !== refreshResult.expires) {
236
- account.expiresAt = refreshResult.expires;
237
- changed = true;
238
- }
239
- if (refreshedEmail && refreshedEmail !== account.email) {
240
- account.email = refreshedEmail;
241
- changed = true;
242
- }
243
- if (refreshedAccountId && refreshedAccountId !== account.accountId) {
244
- account.accountId = refreshedAccountId;
245
- account.accountIdSource = "token";
246
- changed = true;
247
- }
248
- if (refreshResult.idToken) {
249
- probeIdTokenByIndex.set(i, refreshResult.idToken);
250
- }
251
- probeRefreshedIndices.add(i);
252
- probeAccessToken = account.accessToken;
253
- probeAccountId = account.accountId ?? refreshedAccountId;
254
- }
255
- if (!probeAccessToken || !probeAccountId) {
256
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
257
- continue;
258
- }
259
- try {
260
- const liveQuota = await fetchCodexQuotaSnapshot({
261
- accountId: probeAccountId,
262
- accessToken: probeAccessToken,
263
- model: options.model,
264
- });
265
- liveQuotaByIndex.set(i, liveQuota);
266
- }
267
- catch (error) {
268
- const message = helpers.normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
269
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
270
- }
271
- }
272
- const forecastInputs = storage.accounts.map((account, index) => ({
273
- index,
274
- account,
275
- isCurrent: index === helpers.resolveActiveIndex(storage, "codex"),
276
- now,
277
- refreshFailure: refreshFailures.get(index),
278
- liveQuota: liveQuotaByIndex.get(index),
279
- }));
280
- const forecastResults = evaluateForecastAccounts(forecastInputs);
281
- const recommendation = recommendForecastAccount(forecastResults);
282
- if (recommendation.recommendedIndex === null) {
283
- await persistProbeChangesIfNeeded();
284
- if (options.json) {
285
- console.log(JSON.stringify({
286
- error: recommendation.reason,
287
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
288
- }, null, 2));
289
- }
290
- else {
291
- console.log(`No best account available: ${recommendation.reason}`);
292
- printProbeNotes();
293
- }
294
- return 1;
295
- }
296
- const bestIndex = recommendation.recommendedIndex;
297
- const bestAccount = storage.accounts[bestIndex];
298
- if (!bestAccount) {
299
- await persistProbeChangesIfNeeded();
300
- if (options.json) {
301
- console.log(JSON.stringify({ error: "Best account not found." }, null, 2));
302
- }
303
- else {
304
- console.log("Best account not found.");
305
- }
306
- return 1;
307
- }
308
- const currentIndex = helpers.resolveActiveIndex(storage, "codex");
309
- if (currentIndex === bestIndex) {
310
- const shouldSyncCurrentBest = probeRefreshedIndices.has(bestIndex) || probeIdTokenByIndex.has(bestIndex);
311
- let alreadyBestSynced;
312
- if (changed) {
313
- bestAccount.lastUsed = now;
314
- await persistProbeChangesIfNeeded();
315
- }
316
- if (shouldSyncCurrentBest) {
317
- alreadyBestSynced = await setCodexCliActiveSelection({
318
- accountId: bestAccount.accountId,
319
- email: bestAccount.email,
320
- accessToken: bestAccount.accessToken,
321
- refreshToken: bestAccount.refreshToken,
322
- expiresAt: bestAccount.expiresAt,
323
- ...(probeIdTokenByIndex.has(bestIndex)
324
- ? { idToken: probeIdTokenByIndex.get(bestIndex) }
325
- : {}),
326
- });
327
- if (!alreadyBestSynced && !options.json) {
328
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
329
- }
330
- }
331
- if (options.json) {
332
- console.log(JSON.stringify({
333
- message: `Already on best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
334
- accountIndex: bestIndex + 1,
335
- reason: recommendation.reason,
336
- ...(alreadyBestSynced !== undefined ? { synced: alreadyBestSynced } : {}),
337
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
338
- }, null, 2));
339
- }
340
- else {
341
- console.log(`Already on best account ${bestIndex + 1}: ${formatAccountLabel(bestAccount, bestIndex)}`);
342
- console.log(`Reason: ${recommendation.reason}`);
343
- printProbeNotes();
344
- }
345
- return 0;
346
- }
347
- const parsed = bestIndex + 1;
348
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
349
- storage,
350
- targetIndex: bestIndex,
351
- parsed,
352
- switchReason: "best",
353
- initialSyncIdToken: probeIdTokenByIndex.get(bestIndex),
354
- helpers,
355
- });
356
- if (options.json) {
357
- console.log(JSON.stringify({
358
- message: `Switched to best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
359
- accountIndex: parsed,
360
- reason: recommendation.reason,
361
- synced,
362
- wasDisabled,
363
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
364
- }, null, 2));
365
- }
366
- else {
367
- console.log(`Switched to best account ${parsed}: ${formatAccountLabel(bestAccount, bestIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
368
- console.log(`Reason: ${recommendation.reason}`);
369
- printProbeNotes();
370
- if (!synced) {
371
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
372
- }
373
- }
374
- return 0;
375
- };
376
- if (options.live) {
377
- return withSerializedBestLiveRun(getStoragePath(), execute);
378
- }
379
- return execute();
380
- }
381
- export async function runAuthLogin(args, deps) {
382
- const parsedArgs = parseAuthLoginArgs(args);
383
- if (!parsedArgs.ok) {
384
- if (parsedArgs.reason === "error") {
385
- console.error(parsedArgs.message);
386
- printUsage();
387
- return 1;
388
- }
389
- printUsage();
390
- return 0;
391
- }
392
- const loginOptions = parsedArgs.options;
393
- setStoragePath(null);
394
- let pendingMenuQuotaRefresh = null;
395
- let menuQuotaRefreshStatus;
396
- loginFlow: while (true) {
397
- let existingStorage = await loadAccounts();
398
- if (existingStorage && existingStorage.accounts.length > 0) {
399
- while (true) {
400
- existingStorage = await loadAccounts();
401
- if (!existingStorage || existingStorage.accounts.length === 0) {
402
- break;
403
- }
404
- const currentStorage = existingStorage;
405
- const displaySettings = await loadDashboardDisplaySettings();
406
- applyUiThemeFromDashboardSettings(displaySettings);
407
- const quotaCache = await loadQuotaCache();
408
- const shouldAutoFetchLimits = displaySettings.menuAutoFetchLimits ?? true;
409
- const showFetchStatus = displaySettings.menuShowFetchStatus ?? true;
410
- const quotaTtlMs = displaySettings.menuQuotaTtlMs ?? deps.defaultMenuQuotaRefreshTtlMs;
411
- if (shouldAutoFetchLimits && !pendingMenuQuotaRefresh) {
412
- const staleCount = deps.countMenuQuotaRefreshTargets(currentStorage, quotaCache, quotaTtlMs);
413
- if (staleCount > 0) {
414
- if (showFetchStatus) {
415
- menuQuotaRefreshStatus = `${UI_COPY.mainMenu.loadingLimits} [0/${staleCount}]`;
416
- }
417
- pendingMenuQuotaRefresh = deps.refreshQuotaCacheForMenu(currentStorage, quotaCache, quotaTtlMs, (current, total) => {
418
- if (!showFetchStatus)
419
- return;
420
- menuQuotaRefreshStatus = `${UI_COPY.mainMenu.loadingLimits} [${current}/${total}]`;
421
- })
422
- .then(() => undefined)
423
- .catch(() => undefined)
424
- .finally(() => {
425
- menuQuotaRefreshStatus = undefined;
426
- pendingMenuQuotaRefresh = null;
427
- });
428
- }
429
- }
430
- const flaggedStorage = await loadFlaggedAccounts();
431
- const menuResult = await promptLoginMode(deps.toExistingAccountInfo(currentStorage, quotaCache, displaySettings), {
432
- flaggedCount: flaggedStorage.accounts.length,
433
- statusMessage: showFetchStatus ? () => menuQuotaRefreshStatus : undefined,
434
- });
435
- if (menuResult.mode === "cancel") {
436
- console.log("Cancelled.");
437
- return 0;
438
- }
439
- if (menuResult.mode === "check") {
440
- await deps.runActionPanel("Quick Check", "Checking local session + live status", async () => {
441
- await deps.runHealthCheck({ forceRefresh: false, liveProbe: true });
442
- }, displaySettings);
443
- continue;
444
- }
445
- if (menuResult.mode === "deep-check") {
446
- await deps.runActionPanel("Deep Check", "Refreshing and testing all accounts", async () => {
447
- await deps.runHealthCheck({ forceRefresh: true, liveProbe: true });
448
- }, displaySettings);
449
- continue;
450
- }
451
- if (menuResult.mode === "forecast") {
452
- await deps.runActionPanel("Best Account", "Comparing accounts", async () => {
453
- await deps.runForecast(["--live"]);
454
- }, displaySettings);
455
- continue;
456
- }
457
- if (menuResult.mode === "fix") {
458
- await deps.runActionPanel("Auto-Fix", "Checking and fixing common issues", async () => {
459
- await deps.runFix(["--live"]);
460
- }, displaySettings);
461
- continue;
462
- }
463
- if (menuResult.mode === "settings") {
464
- await configureUnifiedSettings(displaySettings);
465
- continue;
466
- }
467
- if (menuResult.mode === "verify-flagged") {
468
- await deps.runActionPanel("Problem Account Check", "Checking problem accounts", async () => {
469
- await deps.runVerifyFlagged([]);
470
- }, displaySettings);
471
- continue;
472
- }
473
- if (menuResult.mode === "fresh" && menuResult.deleteAll) {
474
- await deps.runActionPanel("Reset Accounts", "Deleting all saved accounts", async () => {
475
- await deps.clearAccountsAndReset();
476
- console.log("Cleared saved accounts from active storage. Recovery snapshots remain available.");
477
- }, displaySettings);
478
- continue;
479
- }
480
- if (menuResult.mode === "manage") {
481
- const requiresInteractiveOAuth = typeof menuResult.refreshAccountIndex === "number";
482
- if (requiresInteractiveOAuth) {
483
- await deps.handleManageAction(currentStorage, menuResult);
484
- continue;
485
- }
486
- await deps.runActionPanel("Applying Change", "Updating selected account", async () => {
487
- await deps.handleManageAction(currentStorage, menuResult);
488
- }, displaySettings);
489
- continue;
490
- }
491
- if (menuResult.mode === "add") {
492
- break;
493
- }
494
- }
495
- }
496
- const refreshedStorage = await loadAccounts();
497
- let existingCount = refreshedStorage?.accounts.length ?? 0;
498
- let forceNewLogin = existingCount > 0;
499
- let onboardingBackupDiscoveryWarning = null;
500
- const loadNamedBackupsForOnboarding = async () => {
501
- if (existingCount > 0) {
502
- onboardingBackupDiscoveryWarning = null;
503
- return [];
504
- }
505
- try {
506
- onboardingBackupDiscoveryWarning = null;
507
- return await getNamedBackups();
508
- }
509
- catch (error) {
510
- const code = error.code;
511
- deps.log.debug("getNamedBackups failed, skipping restore option", {
512
- code,
513
- error: error instanceof Error ? error.message : String(error),
514
- });
515
- if (code && code !== "ENOENT") {
516
- onboardingBackupDiscoveryWarning =
517
- "Named backup discovery failed. Continuing with browser or manual sign-in only.";
518
- console.warn(onboardingBackupDiscoveryWarning);
519
- }
520
- else {
521
- onboardingBackupDiscoveryWarning = null;
522
- }
523
- return [];
524
- }
525
- };
526
- let namedBackups = await loadNamedBackupsForOnboarding();
527
- while (true) {
528
- const latestNamedBackup = namedBackups[0] ?? null;
529
- const preferManualMode = loginOptions.manual || isBrowserLaunchSuppressed();
530
- const signInMode = preferManualMode
531
- ? "manual"
532
- : await deps.promptOAuthSignInMode(latestNamedBackup, onboardingBackupDiscoveryWarning);
533
- if (signInMode === "cancel") {
534
- if (existingCount > 0) {
535
- console.log(deps.stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
536
- continue loginFlow;
537
- }
538
- console.log("Cancelled.");
539
- return 0;
540
- }
541
- if (signInMode === "restore-backup") {
542
- const latestAvailableBackup = namedBackups[0] ?? null;
543
- if (!latestAvailableBackup) {
544
- namedBackups = await loadNamedBackupsForOnboarding();
545
- continue;
546
- }
547
- const restoreMode = await deps.promptBackupRestoreMode(latestAvailableBackup);
548
- if (restoreMode === "back") {
549
- namedBackups = await loadNamedBackupsForOnboarding();
550
- continue;
551
- }
552
- const selectedBackup = restoreMode === "manual"
553
- ? await deps.promptManualBackupSelection(namedBackups)
554
- : latestAvailableBackup;
555
- if (!selectedBackup) {
556
- namedBackups = await loadNamedBackupsForOnboarding();
557
- continue;
558
- }
559
- const confirmed = await confirm(UI_COPY.oauth.restoreBackupConfirm(selectedBackup.fileName, selectedBackup.accountCount));
560
- if (!confirmed) {
561
- namedBackups = await loadNamedBackupsForOnboarding();
562
- continue;
563
- }
564
- const displaySettings = await loadDashboardDisplaySettings();
565
- applyUiThemeFromDashboardSettings(displaySettings);
566
- try {
567
- await deps.runActionPanel("Load Backup", `Loading ${selectedBackup.fileName}`, async () => {
568
- const restoredStorage = await restoreAccountsFromBackup(selectedBackup.path, { persist: false });
569
- const targetIndex = deps.resolveActiveIndex(restoredStorage);
570
- const { synced } = await persistAndSyncSelectedAccount({
571
- storage: restoredStorage,
572
- targetIndex,
573
- parsed: targetIndex + 1,
574
- switchReason: "restore",
575
- preserveActiveIndexByFamily: true,
576
- helpers: deps,
577
- });
578
- console.log(UI_COPY.oauth.restoreBackupLoaded(selectedBackup.fileName, restoredStorage.accounts.length));
579
- if (!synced) {
580
- console.warn(UI_COPY.oauth.restoreBackupSyncWarning);
581
- }
582
- }, displaySettings);
583
- }
584
- catch (error) {
585
- const message = error instanceof Error ? error.message : String(error);
586
- if (error instanceof StorageError) {
587
- console.error(formatStorageErrorHint(error, selectedBackup.path));
588
- }
589
- else {
590
- console.error(`Backup restore failed: ${message}`);
591
- }
592
- const storageAfterRestoreAttempt = await loadAccounts().catch(() => null);
593
- if ((storageAfterRestoreAttempt?.accounts.length ?? 0) > 0) {
594
- continue loginFlow;
595
- }
596
- namedBackups = await loadNamedBackupsForOnboarding();
597
- continue;
598
- }
599
- continue loginFlow;
600
- }
601
- if (signInMode !== "browser" && signInMode !== "manual") {
602
- continue;
603
- }
604
- const tokenResult = await deps.runOAuthFlow(forceNewLogin, signInMode);
605
- if (tokenResult.type !== "success") {
606
- const message = tokenResult.message ?? tokenResult.reason ?? "unknown error";
607
- if (message.toLowerCase().includes("cancelled")) {
608
- if (existingCount > 0) {
609
- console.log(deps.stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
610
- continue loginFlow;
611
- }
612
- console.log("Cancelled.");
613
- return 0;
614
- }
615
- console.error(`Login failed: ${message}`);
616
- return 1;
617
- }
618
- const resolved = deps.resolveAccountSelection(tokenResult);
619
- await deps.persistAccountPool([resolved], false);
620
- await deps.syncSelectionToCodex(resolved);
621
- const latestStorage = await loadAccounts();
622
- const count = latestStorage?.accounts.length ?? 1;
623
- existingCount = count;
624
- namedBackups = [];
625
- onboardingBackupDiscoveryWarning = null;
626
- console.log(`Added account. Total: ${count}`);
627
- console.log("Next steps:");
628
- console.log(" codex auth status Check that the wrapper is active.");
629
- console.log(" codex auth check Confirm your saved accounts look healthy.");
630
- console.log(" codex auth list Review saved accounts before switching.");
631
- if (count >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
632
- console.log(`Reached maximum account limit (${ACCOUNT_LIMITS.MAX_ACCOUNTS}).`);
633
- break;
634
- }
635
- const addAnother = await promptAddAnotherAccount(count);
636
- if (!addAnother)
637
- break;
638
- forceNewLogin = true;
639
- }
640
- continue loginFlow;
641
- }
642
- }
643
- //# sourceMappingURL=auth-commands.js.map