oc-codex-multi-auth 6.0.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 (248) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/assets/opencode-logo-ornate-dark.svg +18 -0
  4. package/assets/readme-hero.svg +31 -0
  5. package/config/README.md +96 -0
  6. package/config/minimal-opencode.json +13 -0
  7. package/config/opencode-legacy.json +871 -0
  8. package/config/opencode-modern.json +358 -0
  9. package/dist/index.d.ts +45 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +5008 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/accounts/rate-limits.d.ts +22 -0
  14. package/dist/lib/accounts/rate-limits.d.ts.map +1 -0
  15. package/dist/lib/accounts/rate-limits.js +63 -0
  16. package/dist/lib/accounts/rate-limits.js.map +1 -0
  17. package/dist/lib/accounts.d.ts +134 -0
  18. package/dist/lib/accounts.d.ts.map +1 -0
  19. package/dist/lib/accounts.js +795 -0
  20. package/dist/lib/accounts.js.map +1 -0
  21. package/dist/lib/audit.d.ts +45 -0
  22. package/dist/lib/audit.d.ts.map +1 -0
  23. package/dist/lib/audit.js +154 -0
  24. package/dist/lib/audit.js.map +1 -0
  25. package/dist/lib/auth/auth.d.ts +51 -0
  26. package/dist/lib/auth/auth.d.ts.map +1 -0
  27. package/dist/lib/auth/auth.js +191 -0
  28. package/dist/lib/auth/auth.js.map +1 -0
  29. package/dist/lib/auth/browser.d.ts +17 -0
  30. package/dist/lib/auth/browser.d.ts.map +1 -0
  31. package/dist/lib/auth/browser.js +90 -0
  32. package/dist/lib/auth/browser.js.map +1 -0
  33. package/dist/lib/auth/device-code.d.ts +28 -0
  34. package/dist/lib/auth/device-code.d.ts.map +1 -0
  35. package/dist/lib/auth/device-code.js +215 -0
  36. package/dist/lib/auth/device-code.js.map +1 -0
  37. package/dist/lib/auth/login-runner.d.ts +55 -0
  38. package/dist/lib/auth/login-runner.d.ts.map +1 -0
  39. package/dist/lib/auth/login-runner.js +631 -0
  40. package/dist/lib/auth/login-runner.js.map +1 -0
  41. package/dist/lib/auth/server.d.ts +10 -0
  42. package/dist/lib/auth/server.d.ts.map +1 -0
  43. package/dist/lib/auth/server.js +98 -0
  44. package/dist/lib/auth/server.js.map +1 -0
  45. package/dist/lib/auth/token-utils.d.ts +60 -0
  46. package/dist/lib/auth/token-utils.d.ts.map +1 -0
  47. package/dist/lib/auth/token-utils.js +416 -0
  48. package/dist/lib/auth/token-utils.js.map +1 -0
  49. package/dist/lib/auth-rate-limit.d.ts +20 -0
  50. package/dist/lib/auth-rate-limit.d.ts.map +1 -0
  51. package/dist/lib/auth-rate-limit.js +91 -0
  52. package/dist/lib/auth-rate-limit.js.map +1 -0
  53. package/dist/lib/auto-update-checker.d.ts +10 -0
  54. package/dist/lib/auto-update-checker.d.ts.map +1 -0
  55. package/dist/lib/auto-update-checker.js +129 -0
  56. package/dist/lib/auto-update-checker.js.map +1 -0
  57. package/dist/lib/circuit-breaker.d.ts +34 -0
  58. package/dist/lib/circuit-breaker.d.ts.map +1 -0
  59. package/dist/lib/circuit-breaker.js +124 -0
  60. package/dist/lib/circuit-breaker.js.map +1 -0
  61. package/dist/lib/cli.d.ts +46 -0
  62. package/dist/lib/cli.d.ts.map +1 -0
  63. package/dist/lib/cli.js +192 -0
  64. package/dist/lib/cli.js.map +1 -0
  65. package/dist/lib/config.d.ts +42 -0
  66. package/dist/lib/config.d.ts.map +1 -0
  67. package/dist/lib/config.js +314 -0
  68. package/dist/lib/config.js.map +1 -0
  69. package/dist/lib/constants.d.ts +90 -0
  70. package/dist/lib/constants.d.ts.map +1 -0
  71. package/dist/lib/constants.js +90 -0
  72. package/dist/lib/constants.js.map +1 -0
  73. package/dist/lib/context-overflow.d.ts +27 -0
  74. package/dist/lib/context-overflow.d.ts.map +1 -0
  75. package/dist/lib/context-overflow.js +124 -0
  76. package/dist/lib/context-overflow.js.map +1 -0
  77. package/dist/lib/errors.d.ts +113 -0
  78. package/dist/lib/errors.d.ts.map +1 -0
  79. package/dist/lib/errors.js +103 -0
  80. package/dist/lib/errors.js.map +1 -0
  81. package/dist/lib/health.d.ts +33 -0
  82. package/dist/lib/health.d.ts.map +1 -0
  83. package/dist/lib/health.js +70 -0
  84. package/dist/lib/health.js.map +1 -0
  85. package/dist/lib/index.d.ts +20 -0
  86. package/dist/lib/index.d.ts.map +1 -0
  87. package/dist/lib/index.js +20 -0
  88. package/dist/lib/index.js.map +1 -0
  89. package/dist/lib/logger.d.ts +40 -0
  90. package/dist/lib/logger.d.ts.map +1 -0
  91. package/dist/lib/logger.js +337 -0
  92. package/dist/lib/logger.js.map +1 -0
  93. package/dist/lib/oauth-success.html +712 -0
  94. package/dist/lib/parallel-probe.d.ts +28 -0
  95. package/dist/lib/parallel-probe.d.ts.map +1 -0
  96. package/dist/lib/parallel-probe.js +97 -0
  97. package/dist/lib/parallel-probe.js.map +1 -0
  98. package/dist/lib/proactive-refresh.d.ts +67 -0
  99. package/dist/lib/proactive-refresh.d.ts.map +1 -0
  100. package/dist/lib/proactive-refresh.js +161 -0
  101. package/dist/lib/proactive-refresh.js.map +1 -0
  102. package/dist/lib/prompts/codex-opencode-bridge.d.ts +20 -0
  103. package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
  104. package/dist/lib/prompts/codex-opencode-bridge.js +126 -0
  105. package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
  106. package/dist/lib/prompts/codex.d.ts +41 -0
  107. package/dist/lib/prompts/codex.d.ts.map +1 -0
  108. package/dist/lib/prompts/codex.js +400 -0
  109. package/dist/lib/prompts/codex.js.map +1 -0
  110. package/dist/lib/prompts/opencode-codex.d.ts +25 -0
  111. package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
  112. package/dist/lib/prompts/opencode-codex.js +223 -0
  113. package/dist/lib/prompts/opencode-codex.js.map +1 -0
  114. package/dist/lib/recovery/constants.d.ts +12 -0
  115. package/dist/lib/recovery/constants.d.ts.map +1 -0
  116. package/dist/lib/recovery/constants.js +25 -0
  117. package/dist/lib/recovery/constants.js.map +1 -0
  118. package/dist/lib/recovery/index.d.ts +12 -0
  119. package/dist/lib/recovery/index.d.ts.map +1 -0
  120. package/dist/lib/recovery/index.js +12 -0
  121. package/dist/lib/recovery/index.js.map +1 -0
  122. package/dist/lib/recovery/storage.d.ts +24 -0
  123. package/dist/lib/recovery/storage.d.ts.map +1 -0
  124. package/dist/lib/recovery/storage.js +362 -0
  125. package/dist/lib/recovery/storage.js.map +1 -0
  126. package/dist/lib/recovery/types.d.ts +116 -0
  127. package/dist/lib/recovery/types.d.ts.map +1 -0
  128. package/dist/lib/recovery/types.js +7 -0
  129. package/dist/lib/recovery/types.js.map +1 -0
  130. package/dist/lib/recovery.d.ts +31 -0
  131. package/dist/lib/recovery.d.ts.map +1 -0
  132. package/dist/lib/recovery.js +313 -0
  133. package/dist/lib/recovery.js.map +1 -0
  134. package/dist/lib/refresh-queue.d.ts +131 -0
  135. package/dist/lib/refresh-queue.d.ts.map +1 -0
  136. package/dist/lib/refresh-queue.js +297 -0
  137. package/dist/lib/refresh-queue.js.map +1 -0
  138. package/dist/lib/request/fetch-helpers.d.ts +142 -0
  139. package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
  140. package/dist/lib/request/fetch-helpers.js +749 -0
  141. package/dist/lib/request/fetch-helpers.js.map +1 -0
  142. package/dist/lib/request/helpers/input-utils.d.ts +7 -0
  143. package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
  144. package/dist/lib/request/helpers/input-utils.js +214 -0
  145. package/dist/lib/request/helpers/input-utils.js.map +1 -0
  146. package/dist/lib/request/helpers/model-map.d.ts +22 -0
  147. package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
  148. package/dist/lib/request/helpers/model-map.js +183 -0
  149. package/dist/lib/request/helpers/model-map.js.map +1 -0
  150. package/dist/lib/request/helpers/tool-utils.d.ts +29 -0
  151. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -0
  152. package/dist/lib/request/helpers/tool-utils.js +117 -0
  153. package/dist/lib/request/helpers/tool-utils.js.map +1 -0
  154. package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
  155. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
  156. package/dist/lib/request/rate-limit-backoff.js +83 -0
  157. package/dist/lib/request/rate-limit-backoff.js.map +1 -0
  158. package/dist/lib/request/request-transformer.d.ts +107 -0
  159. package/dist/lib/request/request-transformer.d.ts.map +1 -0
  160. package/dist/lib/request/request-transformer.js +930 -0
  161. package/dist/lib/request/request-transformer.js.map +1 -0
  162. package/dist/lib/request/response-handler.d.ts +23 -0
  163. package/dist/lib/request/response-handler.d.ts.map +1 -0
  164. package/dist/lib/request/response-handler.js +245 -0
  165. package/dist/lib/request/response-handler.js.map +1 -0
  166. package/dist/lib/request/retry-budget.d.ts +19 -0
  167. package/dist/lib/request/retry-budget.d.ts.map +1 -0
  168. package/dist/lib/request/retry-budget.js +99 -0
  169. package/dist/lib/request/retry-budget.js.map +1 -0
  170. package/dist/lib/rotation.d.ts +130 -0
  171. package/dist/lib/rotation.d.ts.map +1 -0
  172. package/dist/lib/rotation.js +301 -0
  173. package/dist/lib/rotation.js.map +1 -0
  174. package/dist/lib/runtime-contracts.d.ts +16 -0
  175. package/dist/lib/runtime-contracts.d.ts.map +1 -0
  176. package/dist/lib/runtime-contracts.js +24 -0
  177. package/dist/lib/runtime-contracts.js.map +1 -0
  178. package/dist/lib/schemas.d.ts +457 -0
  179. package/dist/lib/schemas.d.ts.map +1 -0
  180. package/dist/lib/schemas.js +283 -0
  181. package/dist/lib/schemas.js.map +1 -0
  182. package/dist/lib/shutdown.d.ts +7 -0
  183. package/dist/lib/shutdown.d.ts.map +1 -0
  184. package/dist/lib/shutdown.js +43 -0
  185. package/dist/lib/shutdown.js.map +1 -0
  186. package/dist/lib/storage/migrations.d.ts +65 -0
  187. package/dist/lib/storage/migrations.d.ts.map +1 -0
  188. package/dist/lib/storage/migrations.js +44 -0
  189. package/dist/lib/storage/migrations.js.map +1 -0
  190. package/dist/lib/storage/paths.d.ts +16 -0
  191. package/dist/lib/storage/paths.d.ts.map +1 -0
  192. package/dist/lib/storage/paths.js +92 -0
  193. package/dist/lib/storage/paths.js.map +1 -0
  194. package/dist/lib/storage.d.ts +157 -0
  195. package/dist/lib/storage.d.ts.map +1 -0
  196. package/dist/lib/storage.js +1169 -0
  197. package/dist/lib/storage.js.map +1 -0
  198. package/dist/lib/table-formatter.d.ts +32 -0
  199. package/dist/lib/table-formatter.d.ts.map +1 -0
  200. package/dist/lib/table-formatter.js +44 -0
  201. package/dist/lib/table-formatter.js.map +1 -0
  202. package/dist/lib/types.d.ts +131 -0
  203. package/dist/lib/types.d.ts.map +1 -0
  204. package/dist/lib/types.js +2 -0
  205. package/dist/lib/types.js.map +1 -0
  206. package/dist/lib/ui/ansi.d.ts +22 -0
  207. package/dist/lib/ui/ansi.d.ts.map +1 -0
  208. package/dist/lib/ui/ansi.js +38 -0
  209. package/dist/lib/ui/ansi.js.map +1 -0
  210. package/dist/lib/ui/auth-menu.d.ts +39 -0
  211. package/dist/lib/ui/auth-menu.d.ts.map +1 -0
  212. package/dist/lib/ui/auth-menu.js +186 -0
  213. package/dist/lib/ui/auth-menu.js.map +1 -0
  214. package/dist/lib/ui/beginner.d.ts +62 -0
  215. package/dist/lib/ui/beginner.d.ts.map +1 -0
  216. package/dist/lib/ui/beginner.js +259 -0
  217. package/dist/lib/ui/beginner.js.map +1 -0
  218. package/dist/lib/ui/confirm.d.ts +2 -0
  219. package/dist/lib/ui/confirm.d.ts.map +1 -0
  220. package/dist/lib/ui/confirm.js +21 -0
  221. package/dist/lib/ui/confirm.js.map +1 -0
  222. package/dist/lib/ui/format.d.ts +9 -0
  223. package/dist/lib/ui/format.d.ts.map +1 -0
  224. package/dist/lib/ui/format.js +49 -0
  225. package/dist/lib/ui/format.js.map +1 -0
  226. package/dist/lib/ui/runtime.d.ts +11 -0
  227. package/dist/lib/ui/runtime.d.ts.map +1 -0
  228. package/dist/lib/ui/runtime.js +31 -0
  229. package/dist/lib/ui/runtime.js.map +1 -0
  230. package/dist/lib/ui/select.d.ts +20 -0
  231. package/dist/lib/ui/select.d.ts.map +1 -0
  232. package/dist/lib/ui/select.js +343 -0
  233. package/dist/lib/ui/select.js.map +1 -0
  234. package/dist/lib/ui/theme.d.ts +34 -0
  235. package/dist/lib/ui/theme.d.ts.map +1 -0
  236. package/dist/lib/ui/theme.js +84 -0
  237. package/dist/lib/ui/theme.js.map +1 -0
  238. package/dist/lib/utils.d.ts +29 -0
  239. package/dist/lib/utils.d.ts.map +1 -0
  240. package/dist/lib/utils.js +54 -0
  241. package/dist/lib/utils.js.map +1 -0
  242. package/package.json +113 -0
  243. package/scripts/audit-dev-allowlist.js +114 -0
  244. package/scripts/copy-oauth-success.js +37 -0
  245. package/scripts/install-oc-codex-multi-auth-core.js +381 -0
  246. package/scripts/install-oc-codex-multi-auth.js +15 -0
  247. package/scripts/test-all-models.sh +260 -0
  248. package/scripts/validate-model-map.sh +97 -0
@@ -0,0 +1,631 @@
1
+ import { extractAccountEmail, extractAccountId, getAccountIdCandidates, sanitizeEmail, selectBestAccountCandidate, } from "../accounts.js";
2
+ import { logInfo } from "../logger.js";
3
+ import { MODEL_FAMILIES } from "../prompts/codex.js";
4
+ import { withAccountStorageTransaction } from "../storage.js";
5
+ const PERSIST_AUTHENTICATED_SELECTIONS_ERROR = "Failed to persist authenticated account selections.";
6
+ const createSelectionVariant = (tokens, candidate) => ({
7
+ ...tokens,
8
+ accountIdOverride: candidate.accountId,
9
+ organizationIdOverride: candidate.organizationId,
10
+ accountIdSource: candidate.source,
11
+ accountLabel: candidate.label,
12
+ });
13
+ export function resolveAccountSelection(tokens) {
14
+ const override = (process.env.CODEX_AUTH_ACCOUNT_ID ?? "").trim();
15
+ if (override) {
16
+ const suffix = override.length > 6 ? override.slice(-6) : override;
17
+ logInfo(`Using account override from CODEX_AUTH_ACCOUNT_ID (id:${suffix}).`);
18
+ const primary = {
19
+ ...tokens,
20
+ accountIdOverride: override,
21
+ accountIdSource: "manual",
22
+ accountLabel: `Override [id:${suffix}]`,
23
+ };
24
+ return {
25
+ primary,
26
+ variantsForPersistence: [primary],
27
+ };
28
+ }
29
+ const candidates = getAccountIdCandidates(tokens.access, tokens.idToken);
30
+ if (candidates.length === 0) {
31
+ return {
32
+ primary: tokens,
33
+ variantsForPersistence: [tokens],
34
+ };
35
+ }
36
+ const choice = selectBestAccountCandidate(candidates);
37
+ if (!choice) {
38
+ return {
39
+ primary: tokens,
40
+ variantsForPersistence: [tokens],
41
+ };
42
+ }
43
+ const primary = createSelectionVariant(tokens, {
44
+ accountId: choice.accountId,
45
+ organizationId: choice.organizationId,
46
+ source: choice.source ?? "token",
47
+ label: choice.label,
48
+ });
49
+ const variantsForPersistence = [primary];
50
+ for (const candidate of candidates) {
51
+ if (candidate.accountId === primary.accountIdOverride &&
52
+ (candidate.organizationId ?? "") === (primary.organizationIdOverride ?? "")) {
53
+ continue;
54
+ }
55
+ variantsForPersistence.push(createSelectionVariant(tokens, {
56
+ accountId: candidate.accountId,
57
+ organizationId: candidate.organizationId,
58
+ source: candidate.source,
59
+ label: candidate.label,
60
+ }));
61
+ }
62
+ return {
63
+ primary,
64
+ variantsForPersistence,
65
+ };
66
+ }
67
+ export function applyAccountSelectionFallbacks(selection, fallbacks) {
68
+ const primary = { ...selection.primary };
69
+ const primaryAccountId = selection.primary.accountIdOverride?.trim();
70
+ const primaryOrganizationId = selection.primary.organizationIdOverride?.trim();
71
+ const shouldReusePrimaryVariant = (primaryAccountId?.length ?? 0) > 0 || (primaryOrganizationId?.length ?? 0) > 0;
72
+ // Callers may deep-clone `selection.primary`, so reuse the updated primary by
73
+ // persisted account identity instead of relying on object aliasing.
74
+ let variantsForPersistence = selection.variantsForPersistence.map((variant) => variant === selection.primary ||
75
+ (shouldReusePrimaryVariant &&
76
+ (variant.accountIdOverride?.trim() ?? "") === (primaryAccountId ?? "") &&
77
+ (variant.organizationIdOverride?.trim() ?? "") === (primaryOrganizationId ?? ""))
78
+ ? primary
79
+ : { ...variant });
80
+ const accountIdOverride = fallbacks.accountIdOverride?.trim();
81
+ if (!primary.accountIdOverride && accountIdOverride) {
82
+ primary.accountIdOverride = accountIdOverride;
83
+ primary.accountIdSource = fallbacks.accountIdSource ?? "manual";
84
+ variantsForPersistence = [primary];
85
+ }
86
+ const organizationIdOverride = fallbacks.organizationIdOverride?.trim();
87
+ if (!primary.organizationIdOverride && organizationIdOverride) {
88
+ primary.organizationIdOverride = organizationIdOverride;
89
+ }
90
+ const accountLabel = fallbacks.accountLabel?.trim();
91
+ if (!primary.accountLabel && accountLabel) {
92
+ primary.accountLabel = accountLabel;
93
+ }
94
+ return {
95
+ primary,
96
+ variantsForPersistence,
97
+ };
98
+ }
99
+ /**
100
+ * Persists the already-resolved selection through the caller's
101
+ * `persistSelections` callback. Windows filesystem safety remains delegated to
102
+ * that callback, so callers should use `persistAccountPool` or
103
+ * `withAccountStorageTransaction` to keep the rename retry and serialized
104
+ * read-modify-write behavior covered by `test/login-runner.test.ts`.
105
+ * Callback failures are rethrown with a redacted message so callers can log the
106
+ * wrapper safely without leaking token-file paths or account identifiers.
107
+ */
108
+ export async function persistResolvedAccountSelection(selection, options) {
109
+ if (!options?.persistSelections) {
110
+ return selection;
111
+ }
112
+ try {
113
+ await options.persistSelections(selection.variantsForPersistence, options.replaceAll ?? false);
114
+ }
115
+ catch (error) {
116
+ throw new Error(PERSIST_AUTHENTICATED_SELECTIONS_ERROR, {
117
+ cause: error,
118
+ });
119
+ }
120
+ return selection;
121
+ }
122
+ /**
123
+ * Finalizes a resolved selection by delegating persistence to the caller's
124
+ * `persistSelections` callback. Windows lock-retry and read-modify-write
125
+ * serialization remain the callback's responsibility, so callers should route
126
+ * through `persistAccountPool` or `withAccountStorageTransaction` to preserve
127
+ * the guarantees covered by `test/login-runner.test.ts`.
128
+ * Persistence callback failures are redacted inside
129
+ * `persistResolvedAccountSelection()` before they propagate back to callers.
130
+ */
131
+ export async function resolveAndPersistAccountSelection(tokens, options) {
132
+ let selection = resolveAccountSelection(tokens);
133
+ if (options?.fallbacks) {
134
+ selection = applyAccountSelectionFallbacks(selection, options.fallbacks);
135
+ }
136
+ return persistResolvedAccountSelection(selection, options);
137
+ }
138
+ /**
139
+ * Persists login results through the shared storage transaction so overlapping
140
+ * login retries serialize their read-modify-write cycle instead of racing stale
141
+ * snapshots. `withAccountStorageTransaction` also routes the final rename
142
+ * through the Windows lock retry path in `lib/storage.ts`; see
143
+ * `test/login-runner.test.ts` for the concurrent persist regression coverage.
144
+ */
145
+ export async function persistAccountPool(results, replaceAll = false) {
146
+ if (results.length === 0)
147
+ return;
148
+ await withAccountStorageTransaction(async (loadedStorage, persist) => {
149
+ const now = Date.now();
150
+ const stored = replaceAll ? null : loadedStorage;
151
+ let accounts = stored?.accounts ? [...stored.accounts] : [];
152
+ const pushIndex = (map, key, index) => {
153
+ const existing = map.get(key);
154
+ if (existing) {
155
+ existing.push(index);
156
+ return;
157
+ }
158
+ map.set(key, [index]);
159
+ };
160
+ const asUniqueIndex = (indices) => {
161
+ if (!indices || indices.length !== 1)
162
+ return undefined;
163
+ const [onlyIndex] = indices;
164
+ return typeof onlyIndex === "number" ? onlyIndex : undefined;
165
+ };
166
+ const pickNewestAccountIndex = (existingIndex, candidateIndex) => {
167
+ const existing = accounts[existingIndex];
168
+ const candidate = accounts[candidateIndex];
169
+ if (!existing)
170
+ return candidateIndex;
171
+ if (!candidate)
172
+ return existingIndex;
173
+ const existingLastUsed = existing.lastUsed ?? 0;
174
+ const candidateLastUsed = candidate.lastUsed ?? 0;
175
+ if (candidateLastUsed > existingLastUsed)
176
+ return candidateIndex;
177
+ if (candidateLastUsed < existingLastUsed)
178
+ return existingIndex;
179
+ const existingAddedAt = existing.addedAt ?? 0;
180
+ const candidateAddedAt = candidate.addedAt ?? 0;
181
+ return candidateAddedAt >= existingAddedAt ? candidateIndex : existingIndex;
182
+ };
183
+ const mergeAccountRecords = (targetIndex, sourceIndex) => {
184
+ const target = accounts[targetIndex];
185
+ const source = accounts[sourceIndex];
186
+ if (!target || !source)
187
+ return;
188
+ const targetLastUsed = target.lastUsed ?? 0;
189
+ const sourceLastUsed = source.lastUsed ?? 0;
190
+ const targetAddedAt = target.addedAt ?? 0;
191
+ const sourceAddedAt = source.addedAt ?? 0;
192
+ const sourceIsNewer = sourceLastUsed > targetLastUsed ||
193
+ (sourceLastUsed === targetLastUsed && sourceAddedAt > targetAddedAt);
194
+ const newer = sourceIsNewer ? source : target;
195
+ const older = sourceIsNewer ? target : source;
196
+ const mergedRateLimitResetTimes = {};
197
+ const rateLimitResetKeys = new Set([
198
+ ...Object.keys(older.rateLimitResetTimes ?? {}),
199
+ ...Object.keys(newer.rateLimitResetTimes ?? {}),
200
+ ]);
201
+ for (const key of rateLimitResetKeys) {
202
+ const olderRaw = older.rateLimitResetTimes?.[key];
203
+ const newerRaw = newer.rateLimitResetTimes?.[key];
204
+ const olderValue = typeof olderRaw === "number" && Number.isFinite(olderRaw) ? olderRaw : 0;
205
+ const newerValue = typeof newerRaw === "number" && Number.isFinite(newerRaw) ? newerRaw : 0;
206
+ const resolved = Math.max(olderValue, newerValue);
207
+ if (resolved > 0) {
208
+ mergedRateLimitResetTimes[key] = resolved;
209
+ }
210
+ }
211
+ const mergedEnabled = target.enabled === false || source.enabled === false
212
+ ? false
213
+ : target.enabled ?? source.enabled;
214
+ const targetCoolingDownUntil = typeof target.coolingDownUntil === "number" && Number.isFinite(target.coolingDownUntil)
215
+ ? target.coolingDownUntil
216
+ : 0;
217
+ const sourceCoolingDownUntil = typeof source.coolingDownUntil === "number" && Number.isFinite(source.coolingDownUntil)
218
+ ? source.coolingDownUntil
219
+ : 0;
220
+ const mergedCoolingDownUntilValue = Math.max(targetCoolingDownUntil, sourceCoolingDownUntil);
221
+ const mergedCoolingDownUntil = mergedCoolingDownUntilValue > 0 ? mergedCoolingDownUntilValue : undefined;
222
+ const mergedCooldownReason = (() => {
223
+ if (mergedCoolingDownUntilValue <= 0) {
224
+ return target.cooldownReason ?? source.cooldownReason;
225
+ }
226
+ if (sourceCoolingDownUntil > targetCoolingDownUntil) {
227
+ return source.cooldownReason ?? target.cooldownReason;
228
+ }
229
+ if (targetCoolingDownUntil > sourceCoolingDownUntil) {
230
+ return target.cooldownReason ?? source.cooldownReason;
231
+ }
232
+ return source.cooldownReason ?? target.cooldownReason;
233
+ })();
234
+ accounts[targetIndex] = {
235
+ ...target,
236
+ accountId: target.accountId ?? source.accountId,
237
+ organizationId: target.organizationId ?? source.organizationId,
238
+ accountIdSource: target.accountIdSource ?? source.accountIdSource,
239
+ accountLabel: target.accountLabel ?? source.accountLabel,
240
+ email: target.email ?? source.email,
241
+ refreshToken: newer.refreshToken || older.refreshToken,
242
+ accessToken: newer.accessToken || older.accessToken,
243
+ expiresAt: newer.expiresAt ?? older.expiresAt,
244
+ enabled: mergedEnabled,
245
+ addedAt: Math.max(target.addedAt ?? 0, source.addedAt ?? 0),
246
+ lastUsed: Math.max(target.lastUsed ?? 0, source.lastUsed ?? 0),
247
+ lastSwitchReason: target.lastSwitchReason ?? source.lastSwitchReason,
248
+ rateLimitResetTimes: mergedRateLimitResetTimes,
249
+ coolingDownUntil: mergedCoolingDownUntil,
250
+ cooldownReason: mergedCooldownReason,
251
+ };
252
+ };
253
+ const normalizeStoredAccountId = (account) => {
254
+ const accountId = account?.accountId?.trim();
255
+ return accountId && accountId.length > 0 ? accountId : undefined;
256
+ };
257
+ const canCollapseWithCandidateAccountId = (existing, candidateAccountId) => {
258
+ const existingAccountId = normalizeStoredAccountId(existing);
259
+ const normalizedCandidate = candidateAccountId?.trim() || undefined;
260
+ if (!existingAccountId || !normalizedCandidate) {
261
+ return true;
262
+ }
263
+ return existingAccountId === normalizedCandidate;
264
+ };
265
+ const resolveOrganizationMatch = (indexes, organizationId, candidateAccountId) => {
266
+ const matches = indexes.byOrganizationId.get(organizationId);
267
+ if (!matches || matches.length === 0)
268
+ return undefined;
269
+ const candidateId = candidateAccountId?.trim() || undefined;
270
+ let newestNoAccountId;
271
+ let newestExactAccountId;
272
+ let newestAnyNonEmptyAccountId;
273
+ const distinctNonEmptyAccountIds = new Set();
274
+ for (const index of matches) {
275
+ const existing = accounts[index];
276
+ if (!existing)
277
+ continue;
278
+ const existingAccountId = normalizeStoredAccountId(existing);
279
+ if (!existingAccountId) {
280
+ newestNoAccountId =
281
+ typeof newestNoAccountId === "number"
282
+ ? pickNewestAccountIndex(newestNoAccountId, index)
283
+ : index;
284
+ continue;
285
+ }
286
+ distinctNonEmptyAccountIds.add(existingAccountId);
287
+ newestAnyNonEmptyAccountId =
288
+ typeof newestAnyNonEmptyAccountId === "number"
289
+ ? pickNewestAccountIndex(newestAnyNonEmptyAccountId, index)
290
+ : index;
291
+ if (candidateId && existingAccountId === candidateId) {
292
+ newestExactAccountId =
293
+ typeof newestExactAccountId === "number"
294
+ ? pickNewestAccountIndex(newestExactAccountId, index)
295
+ : index;
296
+ }
297
+ }
298
+ if (candidateId) {
299
+ return newestExactAccountId ?? newestNoAccountId;
300
+ }
301
+ if (typeof newestNoAccountId === "number") {
302
+ return newestNoAccountId;
303
+ }
304
+ if (distinctNonEmptyAccountIds.size === 1) {
305
+ return newestAnyNonEmptyAccountId;
306
+ }
307
+ return undefined;
308
+ };
309
+ const resolveNoOrgRefreshMatch = (indexes, refreshToken, candidateAccountId) => {
310
+ const candidateId = candidateAccountId?.trim() || undefined;
311
+ const matches = indexes.byRefreshTokenNoOrg.get(refreshToken);
312
+ if (!matches || matches.length === 0)
313
+ return undefined;
314
+ let newestNoAccountId;
315
+ let newestExactAccountId;
316
+ for (const index of matches) {
317
+ const existing = accounts[index];
318
+ const existingAccountId = normalizeStoredAccountId(existing);
319
+ if (!existingAccountId) {
320
+ newestNoAccountId =
321
+ typeof newestNoAccountId === "number"
322
+ ? pickNewestAccountIndex(newestNoAccountId, index)
323
+ : index;
324
+ continue;
325
+ }
326
+ if (candidateId && existingAccountId === candidateId) {
327
+ newestExactAccountId =
328
+ typeof newestExactAccountId === "number"
329
+ ? pickNewestAccountIndex(newestExactAccountId, index)
330
+ : index;
331
+ }
332
+ }
333
+ return newestExactAccountId ?? newestNoAccountId;
334
+ };
335
+ const resolveUniqueOrgScopedMatch = (indexes, accountId, refreshToken) => {
336
+ const byAccountId = accountId
337
+ ? asUniqueIndex(indexes.byAccountIdOrgScoped.get(accountId))
338
+ : undefined;
339
+ if (byAccountId !== undefined)
340
+ return byAccountId;
341
+ if (accountId) {
342
+ const accountMatches = indexes.byAccountIdOrgScoped.get(accountId);
343
+ if (accountMatches && accountMatches.length > 1) {
344
+ let newestRefreshMatch;
345
+ for (const index of accountMatches) {
346
+ const existing = accounts[index];
347
+ if (!existing)
348
+ continue;
349
+ const existingRefresh = existing.refreshToken?.trim();
350
+ if (!existingRefresh || existingRefresh !== refreshToken) {
351
+ continue;
352
+ }
353
+ newestRefreshMatch =
354
+ typeof newestRefreshMatch === "number"
355
+ ? pickNewestAccountIndex(newestRefreshMatch, index)
356
+ : index;
357
+ }
358
+ if (typeof newestRefreshMatch === "number") {
359
+ return newestRefreshMatch;
360
+ }
361
+ }
362
+ }
363
+ if (accountId)
364
+ return undefined;
365
+ return asUniqueIndex(indexes.byRefreshTokenOrgScoped.get(refreshToken));
366
+ };
367
+ const buildIdentityIndexes = () => {
368
+ const byOrganizationId = new Map();
369
+ const byAccountIdNoOrg = new Map();
370
+ const byRefreshTokenNoOrg = new Map();
371
+ const byEmailNoOrg = new Map();
372
+ const byAccountIdOrgScoped = new Map();
373
+ const byRefreshTokenOrgScoped = new Map();
374
+ const byRefreshTokenGlobal = new Map();
375
+ for (let i = 0; i < accounts.length; i += 1) {
376
+ const account = accounts[i];
377
+ if (!account)
378
+ continue;
379
+ const organizationId = account.organizationId?.trim();
380
+ const accountId = account.accountId?.trim();
381
+ const refreshToken = account.refreshToken?.trim();
382
+ const email = account.email?.trim();
383
+ if (refreshToken) {
384
+ pushIndex(byRefreshTokenGlobal, refreshToken, i);
385
+ }
386
+ if (organizationId) {
387
+ pushIndex(byOrganizationId, organizationId, i);
388
+ if (accountId) {
389
+ pushIndex(byAccountIdOrgScoped, accountId, i);
390
+ }
391
+ if (refreshToken) {
392
+ pushIndex(byRefreshTokenOrgScoped, refreshToken, i);
393
+ }
394
+ continue;
395
+ }
396
+ if (accountId) {
397
+ byAccountIdNoOrg.set(accountId, i);
398
+ }
399
+ if (refreshToken) {
400
+ pushIndex(byRefreshTokenNoOrg, refreshToken, i);
401
+ }
402
+ if (email) {
403
+ byEmailNoOrg.set(email, i);
404
+ }
405
+ }
406
+ return {
407
+ byOrganizationId,
408
+ byAccountIdNoOrg,
409
+ byRefreshTokenNoOrg,
410
+ byEmailNoOrg,
411
+ byAccountIdOrgScoped,
412
+ byRefreshTokenOrgScoped,
413
+ byRefreshTokenGlobal,
414
+ };
415
+ };
416
+ let identityIndexes = buildIdentityIndexes();
417
+ for (const result of results) {
418
+ const accountId = result.accountIdOverride ?? extractAccountId(result.access);
419
+ const normalizedAccountId = accountId?.trim() || undefined;
420
+ const organizationId = result.organizationIdOverride?.trim() || undefined;
421
+ const accountIdSource = normalizedAccountId
422
+ ? result.accountIdSource ??
423
+ (result.accountIdOverride ? "manual" : "token")
424
+ : undefined;
425
+ const accountLabel = result.accountLabel;
426
+ const accountEmail = sanitizeEmail(extractAccountEmail(result.access, result.idToken));
427
+ const existingIndex = (() => {
428
+ if (organizationId) {
429
+ return resolveOrganizationMatch(identityIndexes, organizationId, normalizedAccountId);
430
+ }
431
+ if (normalizedAccountId) {
432
+ const byAccountId = identityIndexes.byAccountIdNoOrg.get(normalizedAccountId);
433
+ if (byAccountId !== undefined) {
434
+ return byAccountId;
435
+ }
436
+ }
437
+ const byRefreshToken = resolveNoOrgRefreshMatch(identityIndexes, result.refresh, normalizedAccountId);
438
+ if (byRefreshToken !== undefined) {
439
+ return byRefreshToken;
440
+ }
441
+ if (accountEmail && !normalizedAccountId) {
442
+ const byEmail = identityIndexes.byEmailNoOrg.get(accountEmail);
443
+ if (byEmail !== undefined) {
444
+ return byEmail;
445
+ }
446
+ }
447
+ const orgScoped = resolveUniqueOrgScopedMatch(identityIndexes, normalizedAccountId, result.refresh);
448
+ if (orgScoped !== undefined)
449
+ return orgScoped;
450
+ const globalRefreshMatch = asUniqueIndex(identityIndexes.byRefreshTokenGlobal.get(result.refresh));
451
+ if (globalRefreshMatch === undefined) {
452
+ return undefined;
453
+ }
454
+ const existing = accounts[globalRefreshMatch];
455
+ if (!canCollapseWithCandidateAccountId(existing, normalizedAccountId)) {
456
+ return undefined;
457
+ }
458
+ return globalRefreshMatch;
459
+ })();
460
+ if (existingIndex === undefined) {
461
+ accounts.push({
462
+ accountId: normalizedAccountId,
463
+ organizationId,
464
+ accountIdSource,
465
+ accountLabel,
466
+ email: accountEmail,
467
+ refreshToken: result.refresh,
468
+ accessToken: result.access,
469
+ expiresAt: result.expires,
470
+ addedAt: now,
471
+ lastUsed: now,
472
+ });
473
+ identityIndexes = buildIdentityIndexes();
474
+ continue;
475
+ }
476
+ const existing = accounts[existingIndex];
477
+ if (!existing)
478
+ continue;
479
+ const nextEmail = accountEmail ?? existing.email;
480
+ const nextOrganizationId = organizationId ?? existing.organizationId;
481
+ const preserveOrgIdentity = typeof existing.organizationId === "string" &&
482
+ existing.organizationId.trim().length > 0 &&
483
+ !organizationId;
484
+ const nextAccountId = preserveOrgIdentity
485
+ ? existing.accountId ?? normalizedAccountId
486
+ : normalizedAccountId ?? existing.accountId;
487
+ const nextAccountIdSource = preserveOrgIdentity
488
+ ? existing.accountIdSource ?? accountIdSource
489
+ : normalizedAccountId
490
+ ? accountIdSource ?? existing.accountIdSource
491
+ : existing.accountIdSource;
492
+ const nextAccountLabel = preserveOrgIdentity
493
+ ? existing.accountLabel ?? accountLabel
494
+ : accountLabel ?? existing.accountLabel;
495
+ accounts[existingIndex] = {
496
+ ...existing,
497
+ accountId: nextAccountId,
498
+ organizationId: nextOrganizationId,
499
+ accountIdSource: nextAccountIdSource,
500
+ accountLabel: nextAccountLabel,
501
+ email: nextEmail,
502
+ refreshToken: result.refresh,
503
+ accessToken: result.access,
504
+ expiresAt: result.expires,
505
+ lastUsed: now,
506
+ };
507
+ identityIndexes = buildIdentityIndexes();
508
+ }
509
+ const pruneRefreshTokenCollisions = () => {
510
+ const indicesToRemove = new Set();
511
+ const exactIdentityToIndex = new Map();
512
+ const getExactIdentityKey = (account) => {
513
+ const organizationId = account?.organizationId?.trim() ?? "";
514
+ const accountId = normalizeStoredAccountId(account) ?? "";
515
+ const email = account?.email?.trim().toLowerCase() ?? "";
516
+ const refreshToken = account?.refreshToken?.trim() ?? "";
517
+ if (organizationId || accountId) {
518
+ return `org:${organizationId}|account:${accountId}|refresh:${refreshToken}`;
519
+ }
520
+ return `email:${email}|refresh:${refreshToken}`;
521
+ };
522
+ for (let i = 0; i < accounts.length; i += 1) {
523
+ const account = accounts[i];
524
+ if (!account)
525
+ continue;
526
+ const identityKey = getExactIdentityKey(account);
527
+ const existingIndex = exactIdentityToIndex.get(identityKey);
528
+ if (existingIndex === undefined) {
529
+ exactIdentityToIndex.set(identityKey, i);
530
+ continue;
531
+ }
532
+ const newestIndex = pickNewestAccountIndex(existingIndex, i);
533
+ const obsoleteIndex = newestIndex === existingIndex ? i : existingIndex;
534
+ mergeAccountRecords(newestIndex, obsoleteIndex);
535
+ indicesToRemove.add(obsoleteIndex);
536
+ exactIdentityToIndex.set(identityKey, newestIndex);
537
+ }
538
+ if (indicesToRemove.size > 0) {
539
+ accounts = accounts.filter((_, index) => !indicesToRemove.has(index));
540
+ }
541
+ };
542
+ const collectIdentityKeys = (account) => {
543
+ const keys = [];
544
+ const organizationId = account?.organizationId?.trim();
545
+ if (organizationId)
546
+ keys.push(`org:${organizationId}`);
547
+ const accountId = account?.accountId?.trim();
548
+ if (accountId)
549
+ keys.push(`account:${accountId}`);
550
+ const refreshToken = account?.refreshToken?.trim();
551
+ if (refreshToken)
552
+ keys.push(`refresh:${refreshToken}`);
553
+ return keys;
554
+ };
555
+ const getStoredAccountAtIndex = (rawIndex) => {
556
+ const storedAccounts = stored?.accounts;
557
+ if (!storedAccounts)
558
+ return undefined;
559
+ if (typeof rawIndex !== "number" || !Number.isFinite(rawIndex))
560
+ return undefined;
561
+ const candidate = Math.floor(rawIndex);
562
+ if (candidate < 0 || candidate >= storedAccounts.length)
563
+ return undefined;
564
+ return storedAccounts[candidate];
565
+ };
566
+ const storedActiveKeys = replaceAll
567
+ ? []
568
+ : collectIdentityKeys(getStoredAccountAtIndex(stored?.activeIndex));
569
+ const storedActiveKeysByFamily = {};
570
+ if (!replaceAll) {
571
+ for (const family of MODEL_FAMILIES) {
572
+ const familyKeys = collectIdentityKeys(getStoredAccountAtIndex(stored?.activeIndexByFamily?.[family]));
573
+ if (familyKeys.length > 0) {
574
+ storedActiveKeysByFamily[family] = familyKeys;
575
+ }
576
+ }
577
+ }
578
+ pruneRefreshTokenCollisions();
579
+ if (accounts.length === 0)
580
+ return;
581
+ const resolveIndexByIdentityKeys = (identityKeys) => {
582
+ if (!identityKeys || identityKeys.length === 0)
583
+ return undefined;
584
+ for (const identityKey of identityKeys) {
585
+ const index = accounts.findIndex((account) => collectIdentityKeys(account).includes(identityKey));
586
+ if (index >= 0) {
587
+ return index;
588
+ }
589
+ }
590
+ return undefined;
591
+ };
592
+ const fallbackActiveIndex = replaceAll
593
+ ? 0
594
+ : typeof stored?.activeIndex === "number" && Number.isFinite(stored.activeIndex)
595
+ ? stored.activeIndex
596
+ : 0;
597
+ const remappedActiveIndex = replaceAll
598
+ ? undefined
599
+ : resolveIndexByIdentityKeys(storedActiveKeys);
600
+ const activeIndex = remappedActiveIndex ?? fallbackActiveIndex;
601
+ const clampedActiveIndex = Math.max(0, Math.min(Math.floor(activeIndex), accounts.length - 1));
602
+ const activeIndexByFamily = {};
603
+ const familiesToPersist = replaceAll
604
+ ? []
605
+ : MODEL_FAMILIES.filter((family) => {
606
+ const storedFamilyIndex = stored?.activeIndexByFamily?.[family];
607
+ return typeof storedFamilyIndex === "number" && Number.isFinite(storedFamilyIndex);
608
+ });
609
+ for (const family of familiesToPersist) {
610
+ const storedFamilyIndex = stored?.activeIndexByFamily?.[family];
611
+ const remappedFamilyIndex = replaceAll
612
+ ? undefined
613
+ : resolveIndexByIdentityKeys(storedActiveKeysByFamily[family]);
614
+ const rawFamilyIndex = replaceAll
615
+ ? 0
616
+ : typeof remappedFamilyIndex === "number"
617
+ ? remappedFamilyIndex
618
+ : typeof storedFamilyIndex === "number" && Number.isFinite(storedFamilyIndex)
619
+ ? storedFamilyIndex
620
+ : clampedActiveIndex;
621
+ activeIndexByFamily[family] = Math.max(0, Math.min(Math.floor(rawFamilyIndex), accounts.length - 1));
622
+ }
623
+ await persist({
624
+ version: 3,
625
+ accounts,
626
+ activeIndex: clampedActiveIndex,
627
+ activeIndexByFamily,
628
+ });
629
+ });
630
+ }
631
+ //# sourceMappingURL=login-runner.js.map