codex-multi-auth 2.16.0 → 2.17.2

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 (303) hide show
  1. package/.codex-plugin/plugin.json +9 -9
  2. package/LICENSE +21 -21
  3. package/README.md +487 -463
  4. package/assets/codex-multi-auth-icon.svg +39 -39
  5. package/assets/readme-hero.svg +31 -31
  6. package/config/README.md +31 -31
  7. package/config/codex-legacy.json +1221 -1221
  8. package/config/codex-modern.json +423 -423
  9. package/config/minimal-codex.json +15 -15
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +8 -3
  12. package/dist/lib/account-policy.d.ts +4 -0
  13. package/dist/lib/account-policy.d.ts.map +1 -1
  14. package/dist/lib/account-policy.js +8 -0
  15. package/dist/lib/accounts/rate-limits.d.ts.map +1 -1
  16. package/dist/lib/accounts/rate-limits.js +2 -0
  17. package/dist/lib/accounts.d.ts +15 -3
  18. package/dist/lib/accounts.d.ts.map +1 -1
  19. package/dist/lib/accounts.js +231 -30
  20. package/dist/lib/api-route-store.d.ts +24 -0
  21. package/dist/lib/api-route-store.d.ts.map +1 -0
  22. package/dist/lib/api-route-store.js +145 -0
  23. package/dist/lib/auth/account-access.d.ts +115 -0
  24. package/dist/lib/auth/account-access.d.ts.map +1 -0
  25. package/dist/lib/auth/account-access.js +214 -0
  26. package/dist/lib/auth/token-utils.d.ts +54 -1
  27. package/dist/lib/auth/token-utils.d.ts.map +1 -1
  28. package/dist/lib/auth/token-utils.js +92 -7
  29. package/dist/lib/cli.d.ts +1 -1
  30. package/dist/lib/cli.d.ts.map +1 -1
  31. package/dist/lib/cli.js +4 -0
  32. package/dist/lib/codex-cli/state.d.ts +7 -0
  33. package/dist/lib/codex-cli/state.d.ts.map +1 -1
  34. package/dist/lib/codex-cli/state.js +18 -0
  35. package/dist/lib/codex-cli/writer.d.ts.map +1 -1
  36. package/dist/lib/codex-cli/writer.js +60 -8
  37. package/dist/lib/codex-manager/account-manager-commands.d.ts.map +1 -1
  38. package/dist/lib/codex-manager/account-manager-commands.js +1 -0
  39. package/dist/lib/codex-manager/account-pool-write.d.ts +3 -1
  40. package/dist/lib/codex-manager/account-pool-write.d.ts.map +1 -1
  41. package/dist/lib/codex-manager/account-pool-write.js +4 -0
  42. package/dist/lib/codex-manager/api-login-menu.d.ts +14 -0
  43. package/dist/lib/codex-manager/api-login-menu.d.ts.map +1 -0
  44. package/dist/lib/codex-manager/api-login-menu.js +224 -0
  45. package/dist/lib/codex-manager/commands/account.d.ts.map +1 -1
  46. package/dist/lib/codex-manager/commands/account.js +27 -0
  47. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -1
  48. package/dist/lib/codex-manager/commands/best.js +6 -2
  49. package/dist/lib/codex-manager/commands/check.d.ts +7 -1
  50. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -1
  51. package/dist/lib/codex-manager/commands/check.js +24 -3
  52. package/dist/lib/codex-manager/commands/forecast.d.ts +1 -1
  53. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -1
  54. package/dist/lib/codex-manager/commands/forecast.js +7 -3
  55. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -1
  56. package/dist/lib/codex-manager/commands/report.js +5 -1
  57. package/dist/lib/codex-manager/commands/resets.d.ts +2 -0
  58. package/dist/lib/codex-manager/commands/resets.d.ts.map +1 -0
  59. package/dist/lib/codex-manager/commands/resets.js +67 -0
  60. package/dist/lib/codex-manager/commands/rotation.d.ts +2 -2
  61. package/dist/lib/codex-manager/commands/rotation.d.ts.map +1 -1
  62. package/dist/lib/codex-manager/commands/rotation.js +63 -12
  63. package/dist/lib/codex-manager/commands/status.d.ts +12 -1
  64. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -1
  65. package/dist/lib/codex-manager/commands/status.js +166 -22
  66. package/dist/lib/codex-manager/commands/uninstall.js +1 -1
  67. package/dist/lib/codex-manager/formatters/quota-formatters.d.ts.map +1 -1
  68. package/dist/lib/codex-manager/formatters/quota-formatters.js +3 -2
  69. package/dist/lib/codex-manager/health-check.d.ts +3 -0
  70. package/dist/lib/codex-manager/health-check.d.ts.map +1 -1
  71. package/dist/lib/codex-manager/health-check.js +57 -7
  72. package/dist/lib/codex-manager/help.d.ts.map +1 -1
  73. package/dist/lib/codex-manager/help.js +4 -2
  74. package/dist/lib/codex-manager/login-flow.d.ts.map +1 -1
  75. package/dist/lib/codex-manager/login-flow.js +69 -7
  76. package/dist/lib/codex-manager/login-menu-data.d.ts.map +1 -1
  77. package/dist/lib/codex-manager/login-menu-data.js +21 -8
  78. package/dist/lib/codex-manager/login-oauth.d.ts +3 -0
  79. package/dist/lib/codex-manager/login-oauth.d.ts.map +1 -1
  80. package/dist/lib/codex-manager/login-oauth.js +6 -1
  81. package/dist/lib/codex-manager/login-workspace-choice.d.ts +11 -0
  82. package/dist/lib/codex-manager/login-workspace-choice.d.ts.map +1 -0
  83. package/dist/lib/codex-manager/login-workspace-choice.js +37 -0
  84. package/dist/lib/codex-manager/persist-selected-account.d.ts.map +1 -1
  85. package/dist/lib/codex-manager/persist-selected-account.js +2 -1
  86. package/dist/lib/codex-manager/quota-cache-helpers.d.ts +7 -1
  87. package/dist/lib/codex-manager/quota-cache-helpers.d.ts.map +1 -1
  88. package/dist/lib/codex-manager/quota-cache-helpers.js +34 -19
  89. package/dist/lib/codex-manager/repair-commands.d.ts +13 -0
  90. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -1
  91. package/dist/lib/codex-manager/repair-commands.js +144 -9
  92. package/dist/lib/codex-manager.d.ts.map +1 -1
  93. package/dist/lib/codex-manager.js +25 -6
  94. package/dist/lib/concurrency.d.ts +3 -0
  95. package/dist/lib/concurrency.d.ts.map +1 -0
  96. package/dist/lib/concurrency.js +31 -0
  97. package/dist/lib/context-budget-response.js +8 -8
  98. package/dist/lib/context-overflow.js +10 -10
  99. package/dist/lib/model-route-policy.d.ts +52 -0
  100. package/dist/lib/model-route-policy.d.ts.map +1 -0
  101. package/dist/lib/model-route-policy.js +159 -0
  102. package/dist/lib/oauth-success.html +125 -125
  103. package/dist/lib/policy/runtime-policy.d.ts +1 -0
  104. package/dist/lib/policy/runtime-policy.d.ts.map +1 -1
  105. package/dist/lib/policy/runtime-policy.js +3 -0
  106. package/dist/lib/preemptive-quota-scheduler.d.ts +2 -0
  107. package/dist/lib/preemptive-quota-scheduler.d.ts.map +1 -1
  108. package/dist/lib/preemptive-quota-scheduler.js +4 -0
  109. package/dist/lib/prompts/codex-host-bridge.js +138 -138
  110. package/dist/lib/prompts/codex.js +79 -79
  111. package/dist/lib/quota-cache.d.ts +7 -3
  112. package/dist/lib/quota-cache.d.ts.map +1 -1
  113. package/dist/lib/quota-cache.js +91 -59
  114. package/dist/lib/quota-probe.d.ts +5 -0
  115. package/dist/lib/quota-probe.d.ts.map +1 -1
  116. package/dist/lib/quota-probe.js +26 -2
  117. package/dist/lib/quota-readiness.d.ts +7 -2
  118. package/dist/lib/quota-readiness.d.ts.map +1 -1
  119. package/dist/lib/quota-readiness.js +25 -4
  120. package/dist/lib/request/client-cancellation.d.ts +5 -0
  121. package/dist/lib/request/client-cancellation.d.ts.map +1 -0
  122. package/dist/lib/request/client-cancellation.js +7 -0
  123. package/dist/lib/request/response-outcome.d.ts +25 -0
  124. package/dist/lib/request/response-outcome.d.ts.map +1 -0
  125. package/dist/lib/request/response-outcome.js +49 -0
  126. package/dist/lib/request/stream-failover-runtime.d.ts +2 -1
  127. package/dist/lib/request/stream-failover-runtime.d.ts.map +1 -1
  128. package/dist/lib/request/stream-failover-runtime.js +29 -10
  129. package/dist/lib/runtime/account-check.d.ts.map +1 -1
  130. package/dist/lib/runtime/account-check.js +2 -8
  131. package/dist/lib/runtime/account-model-catalog.d.ts +64 -0
  132. package/dist/lib/runtime/account-model-catalog.d.ts.map +1 -0
  133. package/dist/lib/runtime/account-model-catalog.js +154 -0
  134. package/dist/lib/runtime/account-pool.d.ts +3 -1
  135. package/dist/lib/runtime/account-pool.d.ts.map +1 -1
  136. package/dist/lib/runtime/account-pool.js +4 -0
  137. package/dist/lib/runtime/account-reset-credits.d.ts +42 -0
  138. package/dist/lib/runtime/account-reset-credits.d.ts.map +1 -0
  139. package/dist/lib/runtime/account-reset-credits.js +82 -0
  140. package/dist/lib/runtime/api-model-capabilities.d.ts +30 -0
  141. package/dist/lib/runtime/api-model-capabilities.d.ts.map +1 -0
  142. package/dist/lib/runtime/api-model-capabilities.js +434 -0
  143. package/dist/lib/runtime/api-model-runtime.d.ts +42 -0
  144. package/dist/lib/runtime/api-model-runtime.d.ts.map +1 -0
  145. package/dist/lib/runtime/api-model-runtime.js +358 -0
  146. package/dist/lib/runtime/app-bind.d.ts +19 -1
  147. package/dist/lib/runtime/app-bind.d.ts.map +1 -1
  148. package/dist/lib/runtime/app-bind.js +89 -11
  149. package/dist/lib/runtime/automatic-account-checks.d.ts +35 -0
  150. package/dist/lib/runtime/automatic-account-checks.d.ts.map +1 -0
  151. package/dist/lib/runtime/automatic-account-checks.js +140 -0
  152. package/dist/lib/runtime/automatic-subscription-checks.d.ts +7 -0
  153. package/dist/lib/runtime/automatic-subscription-checks.d.ts.map +1 -0
  154. package/dist/lib/runtime/automatic-subscription-checks.js +54 -0
  155. package/dist/lib/runtime/catalog-capabilities.d.ts +8 -0
  156. package/dist/lib/runtime/catalog-capabilities.d.ts.map +1 -0
  157. package/dist/lib/runtime/catalog-capabilities.js +61 -0
  158. package/dist/lib/runtime/inference-activity.d.ts +15 -0
  159. package/dist/lib/runtime/inference-activity.d.ts.map +1 -0
  160. package/dist/lib/runtime/inference-activity.js +95 -0
  161. package/dist/lib/runtime/model-discovery-status.d.ts +100 -0
  162. package/dist/lib/runtime/model-discovery-status.d.ts.map +1 -0
  163. package/dist/lib/runtime/model-discovery-status.js +387 -0
  164. package/dist/lib/runtime/native-account-storage.d.ts +11 -0
  165. package/dist/lib/runtime/native-account-storage.d.ts.map +1 -0
  166. package/dist/lib/runtime/native-account-storage.js +50 -0
  167. package/dist/lib/runtime/native-account-sync.d.ts +19 -0
  168. package/dist/lib/runtime/native-account-sync.d.ts.map +1 -0
  169. package/dist/lib/runtime/native-account-sync.js +62 -0
  170. package/dist/lib/runtime/native-binding-lock.d.ts +3 -0
  171. package/dist/lib/runtime/native-binding-lock.d.ts.map +1 -0
  172. package/dist/lib/runtime/native-binding-lock.js +5 -0
  173. package/dist/lib/runtime/native-client-auth.d.ts +3 -0
  174. package/dist/lib/runtime/native-client-auth.d.ts.map +1 -0
  175. package/dist/lib/runtime/native-client-auth.js +42 -0
  176. package/dist/lib/runtime/native-provider-config.d.ts +4 -0
  177. package/dist/lib/runtime/native-provider-config.d.ts.map +1 -0
  178. package/dist/lib/runtime/native-provider-config.js +52 -0
  179. package/dist/lib/runtime/native-rate-limits.d.ts +18 -0
  180. package/dist/lib/runtime/native-rate-limits.d.ts.map +1 -0
  181. package/dist/lib/runtime/native-rate-limits.js +135 -0
  182. package/dist/lib/runtime/reset-credit-routing.d.ts +41 -0
  183. package/dist/lib/runtime/reset-credit-routing.d.ts.map +1 -0
  184. package/dist/lib/runtime/reset-credit-routing.js +66 -0
  185. package/dist/lib/runtime/reset-credits.d.ts +101 -0
  186. package/dist/lib/runtime/reset-credits.d.ts.map +1 -0
  187. package/dist/lib/runtime/reset-credits.js +162 -0
  188. package/dist/lib/runtime/response-output-history.d.ts +17 -0
  189. package/dist/lib/runtime/response-output-history.d.ts.map +1 -0
  190. package/dist/lib/runtime/response-output-history.js +85 -0
  191. package/dist/lib/runtime/responses-websocket.d.ts +31 -0
  192. package/dist/lib/runtime/responses-websocket.d.ts.map +1 -0
  193. package/dist/lib/runtime/responses-websocket.js +636 -0
  194. package/dist/lib/runtime/resume-picker.d.ts +41 -0
  195. package/dist/lib/runtime/resume-picker.d.ts.map +1 -0
  196. package/dist/lib/runtime/resume-picker.js +169 -0
  197. package/dist/lib/runtime/rotation-account-selection.d.ts +6 -0
  198. package/dist/lib/runtime/rotation-account-selection.d.ts.map +1 -1
  199. package/dist/lib/runtime/rotation-account-selection.js +63 -0
  200. package/dist/lib/runtime/rotation-proxy-state.d.ts +33 -0
  201. package/dist/lib/runtime/rotation-proxy-state.d.ts.map +1 -1
  202. package/dist/lib/runtime/rotation-server-types.d.ts +18 -0
  203. package/dist/lib/runtime/rotation-server-types.d.ts.map +1 -1
  204. package/dist/lib/runtime/runtime-capability-failures.d.ts +14 -0
  205. package/dist/lib/runtime/runtime-capability-failures.d.ts.map +1 -0
  206. package/dist/lib/runtime/runtime-capability-failures.js +76 -0
  207. package/dist/lib/runtime/runtime-observability.d.ts +7 -0
  208. package/dist/lib/runtime/runtime-observability.d.ts.map +1 -1
  209. package/dist/lib/runtime/runtime-observability.js +42 -2
  210. package/dist/lib/runtime/subscription-first-use.d.ts +10 -0
  211. package/dist/lib/runtime/subscription-first-use.d.ts.map +1 -0
  212. package/dist/lib/runtime/subscription-first-use.js +64 -0
  213. package/dist/lib/runtime/subscription-quota-event.d.ts +6 -0
  214. package/dist/lib/runtime/subscription-quota-event.d.ts.map +1 -0
  215. package/dist/lib/runtime/subscription-quota-event.js +31 -0
  216. package/dist/lib/runtime/subscription-quota-order.d.ts +17 -0
  217. package/dist/lib/runtime/subscription-quota-order.d.ts.map +1 -0
  218. package/dist/lib/runtime/subscription-quota-order.js +31 -0
  219. package/dist/lib/runtime/workspace-model-scopes.d.ts +15 -0
  220. package/dist/lib/runtime/workspace-model-scopes.d.ts.map +1 -0
  221. package/dist/lib/runtime/workspace-model-scopes.js +34 -0
  222. package/dist/lib/runtime-constants.d.ts +3 -0
  223. package/dist/lib/runtime-constants.d.ts.map +1 -1
  224. package/dist/lib/runtime-constants.js +5 -0
  225. package/dist/lib/runtime-paths.d.ts.map +1 -1
  226. package/dist/lib/runtime-paths.js +4 -1
  227. package/dist/lib/runtime-rotation-proxy.d.ts.map +1 -1
  228. package/dist/lib/runtime-rotation-proxy.js +852 -42
  229. package/dist/lib/schemas.d.ts +20 -0
  230. package/dist/lib/schemas.d.ts.map +1 -1
  231. package/dist/lib/schemas.js +3 -0
  232. package/dist/lib/storage/credential-sidecars.d.ts +13 -0
  233. package/dist/lib/storage/credential-sidecars.d.ts.map +1 -0
  234. package/dist/lib/storage/credential-sidecars.js +52 -0
  235. package/dist/lib/storage/file-lock.d.ts +9 -0
  236. package/dist/lib/storage/file-lock.d.ts.map +1 -0
  237. package/dist/lib/storage/file-lock.js +186 -0
  238. package/dist/lib/storage/flagged-storage.d.ts.map +1 -1
  239. package/dist/lib/storage/flagged-storage.js +12 -0
  240. package/dist/lib/storage/path-state.d.ts +2 -0
  241. package/dist/lib/storage/path-state.d.ts.map +1 -1
  242. package/dist/lib/storage/path-state.js +9 -0
  243. package/dist/lib/storage/paths.js +4 -4
  244. package/dist/lib/storage/pending-auth.d.ts +18 -0
  245. package/dist/lib/storage/pending-auth.d.ts.map +1 -0
  246. package/dist/lib/storage/pending-auth.js +155 -0
  247. package/dist/lib/storage/public-types.d.ts +13 -0
  248. package/dist/lib/storage/public-types.d.ts.map +1 -1
  249. package/dist/lib/storage/record-identity.d.ts +16 -0
  250. package/dist/lib/storage/record-identity.d.ts.map +1 -0
  251. package/dist/lib/storage/record-identity.js +16 -0
  252. package/dist/lib/storage/snapshot-merge.d.ts +16 -0
  253. package/dist/lib/storage/snapshot-merge.d.ts.map +1 -0
  254. package/dist/lib/storage/snapshot-merge.js +206 -0
  255. package/dist/lib/storage/transactions.d.ts.map +1 -1
  256. package/dist/lib/storage/transactions.js +5 -4
  257. package/dist/lib/storage.d.ts +6 -0
  258. package/dist/lib/storage.d.ts.map +1 -1
  259. package/dist/lib/storage.js +77 -7
  260. package/dist/lib/ui/auth-menu-builder.d.ts +2 -0
  261. package/dist/lib/ui/auth-menu-builder.d.ts.map +1 -1
  262. package/dist/lib/ui/auth-menu-builder.js +1 -0
  263. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  264. package/dist/lib/ui/auth-menu.js +1 -0
  265. package/dist/lib/ui/check-progress.d.ts +8 -0
  266. package/dist/lib/ui/check-progress.d.ts.map +1 -0
  267. package/dist/lib/ui/check-progress.js +30 -0
  268. package/dist/lib/ui/format.d.ts +3 -1
  269. package/dist/lib/ui/format.d.ts.map +1 -1
  270. package/dist/lib/ui/format.js +8 -0
  271. package/dist/lib/ui/ui-copy.d.ts +2 -2
  272. package/dist/lib/ui/ui-copy.js +2 -2
  273. package/dist/lib/usage/pricing.js +2 -2
  274. package/dist/lib/usage/usage-extraction.d.ts +3 -1
  275. package/dist/lib/usage/usage-extraction.d.ts.map +1 -1
  276. package/dist/lib/usage/usage-extraction.js +46 -9
  277. package/node_modules/@codex-ai/plugin/dist/index.d.ts +2 -2
  278. package/node_modules/@codex-ai/plugin/dist/index.js +2 -2
  279. package/node_modules/@codex-ai/plugin/dist/tool.d.ts +42 -42
  280. package/node_modules/@codex-ai/plugin/dist/tool.js +29 -29
  281. package/node_modules/@codex-ai/plugin/package.json +9 -9
  282. package/node_modules/@codex-ai/sdk/dist/index.d.ts +4 -4
  283. package/node_modules/@codex-ai/sdk/dist/index.js +2 -2
  284. package/node_modules/@codex-ai/sdk/package.json +8 -8
  285. package/package.json +200 -197
  286. package/scripts/codex-app-launcher.js +653 -653
  287. package/scripts/codex-app-router.js +373 -366
  288. package/scripts/codex-bin-resolver.js +313 -281
  289. package/scripts/codex-multi-auth.js +59 -59
  290. package/scripts/codex-routing.js +62 -61
  291. package/scripts/codex.js +6642 -6564
  292. package/scripts/install-codex-auth-utils.js +213 -213
  293. package/scripts/mcodex.js +367 -367
  294. package/scripts/postinstall.js +114 -114
  295. package/scripts/preuninstall.js +265 -265
  296. package/vendor/codex-ai-plugin/dist/index.d.ts +2 -2
  297. package/vendor/codex-ai-plugin/dist/index.js +2 -2
  298. package/vendor/codex-ai-plugin/dist/tool.d.ts +42 -42
  299. package/vendor/codex-ai-plugin/dist/tool.js +29 -29
  300. package/vendor/codex-ai-plugin/package.json +9 -9
  301. package/vendor/codex-ai-sdk/dist/index.d.ts +4 -4
  302. package/vendor/codex-ai-sdk/dist/index.js +2 -2
  303. package/vendor/codex-ai-sdk/package.json +8 -8
@@ -0,0 +1,115 @@
1
+ import type { CodexCliMirror, Workspace } from "../storage/public-types.js";
2
+ import type { AccountIdSource } from "../types.js";
3
+ /**
4
+ * Which accounts the backend will actually let the current credentials act as.
5
+ *
6
+ * Codex CLI >= 0.156.0 resolves this itself through
7
+ * `GET /backend-api/wham/accounts/check` before every request and refuses to run
8
+ * when the account it was told to use is absent from the answer
9
+ * ("selected workspace missing from routing discovery"). The id/organization
10
+ * lists carried inside the token claims are NOT the same thing: a token can list
11
+ * an organization it has no live authorization for, so selecting a workspace from
12
+ * the claims alone can persist an account id that every later request rejects.
13
+ */
14
+ export interface AuthorizedAccounts {
15
+ /** Every account id the response listed, in the order it listed them. */
16
+ accountIds: string[];
17
+ /** The id the backend itself considers the default, when it named one. */
18
+ defaultAccountId?: string;
19
+ }
20
+ export interface FetchAuthorizedAccountsOptions {
21
+ /** Injection seam for tests; defaults to the global fetch. */
22
+ fetch?: typeof globalThis.fetch;
23
+ signal?: AbortSignal;
24
+ }
25
+ /**
26
+ * Reads the account ids out of a `wham/accounts/check` payload.
27
+ *
28
+ * Returns null when the payload carries no usable list, which the caller must
29
+ * treat as "unknown" rather than "nothing is authorized" — refusing every
30
+ * account on a shape we failed to parse would break logins for a response format
31
+ * change alone.
32
+ */
33
+ export declare function parseAuthorizedAccounts(payload: unknown): AuthorizedAccounts | null;
34
+ /**
35
+ * Asks the backend which accounts these credentials may act as.
36
+ *
37
+ * Fails open (resolves null) for every error: this check exists to catch a
38
+ * selection that would not work, so an unreachable or unparseable answer must
39
+ * leave the caller's existing behavior untouched rather than block a login.
40
+ */
41
+ export declare function fetchAuthorizedAccounts(accessToken: string, options?: FetchAuthorizedAccountsOptions): Promise<AuthorizedAccounts | null>;
42
+ export interface ConstrainedSelection {
43
+ /** The account id to actually persist. */
44
+ accountId: string;
45
+ /** True when the backend's answer forced a different id than requested. */
46
+ changed: boolean;
47
+ /** The id that was rejected, present only when `changed` is true. */
48
+ rejected?: string;
49
+ }
50
+ /**
51
+ * Narrows a chosen account id to one the backend authorizes.
52
+ *
53
+ * Only ever narrows: with no authorization data, no default to fall back to, or
54
+ * an id that is already authorized, the caller's own choice is returned
55
+ * unchanged. That keeps this a guard against a known-bad write rather than a new
56
+ * way for a login to end up somewhere the user did not ask for.
57
+ */
58
+ export declare function constrainSelectionToAuthorized(accountId: string, authorized: AuthorizedAccounts | null): ConstrainedSelection;
59
+ /** The slice of a resolved login selection this guard reads and rewrites. */
60
+ export interface AccountSelectionLike {
61
+ accountIdOverride?: string;
62
+ accountIdSource?: AccountIdSource;
63
+ accountLabel?: string;
64
+ workspaces?: Workspace[];
65
+ }
66
+ /**
67
+ * Applies {@link constrainSelectionToAuthorized} to a resolved login selection.
68
+ *
69
+ * On a rewrite the source becomes `token`: the backend's default account IS the
70
+ * account these credentials are, so it must auto-follow later token refreshes
71
+ * (see `shouldUpdateAccountIdFromToken`) instead of being pinned the way an
72
+ * explicit org/manual choice is. The label follows the new id too; an empty
73
+ * label (not undefined) is deliberate, because the account-pool merge keeps
74
+ * the saved label on undefined and would go on naming the rejected workspace.
75
+ *
76
+ * The workspace list is narrowed with {@link toAuthorizedWorkspaces}.
77
+ */
78
+ export declare function applyAuthorizedAccountConstraint<T extends AccountSelectionLike>(selection: T, authorized: AuthorizedAccounts | null): {
79
+ selection: T;
80
+ result: ConstrainedSelection | null;
81
+ };
82
+ /** The outcome of {@link refreshCodexCliMirror}. */
83
+ export type CodexCliMirrorUpdate = "set" | "cleared";
84
+ /**
85
+ * Re-checks an explicit (`manual`) binding and keeps its CodexCliMirror in
86
+ * step, mutating the record in place: set to the backend default while the
87
+ * explicit id is unauthorized, removed once it is authorized again. The
88
+ * explicit id itself is never changed. Fails open: no answer, no change.
89
+ */
90
+ export declare function refreshCodexCliMirror(account: {
91
+ accountId?: string;
92
+ accountIdSource?: AccountIdSource;
93
+ codexCliMirror?: CodexCliMirror;
94
+ }, accessToken: string, options?: FetchAuthorizedAccountsOptions): Promise<CodexCliMirrorUpdate | null>;
95
+ /** The slice of a saved account record this migration reads and rewrites. */
96
+ export interface StoredAccountIdentity {
97
+ accountId?: string;
98
+ accountIdSource?: AccountIdSource;
99
+ accountLabel?: string;
100
+ workspaces?: Workspace[];
101
+ currentWorkspaceIndex?: number;
102
+ }
103
+ /**
104
+ * Rebinds a saved account's id when it is not one the backend currently
105
+ * authorizes, mutating the record in place. This is the migration path for
106
+ * accounts saved before this guard existed (or by `codex-multi-auth workspace
107
+ * <account> <workspace>`, which has no live check of its own).
108
+ *
109
+ * Scoped to `accountIdSource === "org"` — the one source that never
110
+ * auto-follows the token (`shouldUpdateAccountIdFromToken`). "token" /
111
+ * "id_token" already self-correct through `applyTokenAccountIdentity`, and
112
+ * "manual" is an explicit `login --org` binding this must not override.
113
+ */
114
+ export declare function reboundUnauthorizedAccountIdentity(account: StoredAccountIdentity, accessToken: string, options?: FetchAuthorizedAccountsOptions): Promise<ConstrainedSelection | null>;
115
+ //# sourceMappingURL=account-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-access.d.ts","sourceRoot":"","sources":["../../../lib/auth/account-access.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IAClC,yEAAyE;IACzE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,8BAA8B;IAC9C,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAeD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,EAAE,OAAO,GACd,kBAAkB,GAAG,IAAI,CAgB3B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC5C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,8BAAmC,GAC1C,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA0BpC;AAED,MAAM,WAAW,oBAAoB;IACpC,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC7C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,kBAAkB,GAAG,IAAI,GACnC,oBAAoB,CAUtB;AAED,6EAA6E;AAC7E,MAAM,WAAW,oBAAoB;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,SAAS,oBAAoB,EAC9E,SAAS,EAAE,CAAC,EACZ,UAAU,EAAE,kBAAkB,GAAG,IAAI,GACnC;IAAE,SAAS,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAA;CAAE,CAsBvD;AA6BD,oDAAoD;AACpD,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC,EACD,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,8BAAmC,GAC1C,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAoBtC;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kCAAkC,CACvD,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,8BAAmC,GAC1C,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CA4BtC"}
@@ -0,0 +1,214 @@
1
+ import { CODEX_BASE_URL } from "../constants.js";
2
+ const ACCOUNTS_CHECK_PATH = "/wham/accounts/check";
3
+ const ACCOUNTS_CHECK_TIMEOUT_MS = 10_000;
4
+ function readString(value) {
5
+ if (typeof value !== "string")
6
+ return undefined;
7
+ const trimmed = value.trim();
8
+ return trimmed.length > 0 ? trimmed : undefined;
9
+ }
10
+ function isRecord(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ /**
14
+ * Reads the account ids out of a `wham/accounts/check` payload.
15
+ *
16
+ * Returns null when the payload carries no usable list, which the caller must
17
+ * treat as "unknown" rather than "nothing is authorized" — refusing every
18
+ * account on a shape we failed to parse would break logins for a response format
19
+ * change alone.
20
+ */
21
+ export function parseAuthorizedAccounts(payload) {
22
+ if (!isRecord(payload))
23
+ return null;
24
+ const accounts = payload.accounts;
25
+ if (!Array.isArray(accounts))
26
+ return null;
27
+ const accountIds = [];
28
+ for (const entry of accounts) {
29
+ if (!isRecord(entry))
30
+ continue;
31
+ const id = readString(entry.id) ?? readString(entry.account_id);
32
+ if (id && !accountIds.includes(id))
33
+ accountIds.push(id);
34
+ }
35
+ if (accountIds.length === 0)
36
+ return null;
37
+ const defaultAccountId = readString(payload.default_account_id);
38
+ return defaultAccountId ? { accountIds, defaultAccountId } : { accountIds };
39
+ }
40
+ /**
41
+ * Asks the backend which accounts these credentials may act as.
42
+ *
43
+ * Fails open (resolves null) for every error: this check exists to catch a
44
+ * selection that would not work, so an unreachable or unparseable answer must
45
+ * leave the caller's existing behavior untouched rather than block a login.
46
+ */
47
+ export async function fetchAuthorizedAccounts(accessToken, options = {}) {
48
+ const token = readString(accessToken);
49
+ if (!token)
50
+ return null;
51
+ const doFetch = options.fetch ?? globalThis.fetch;
52
+ const timeout = AbortSignal.timeout(ACCOUNTS_CHECK_TIMEOUT_MS);
53
+ const signal = options.signal
54
+ ? AbortSignal.any([options.signal, timeout])
55
+ : timeout;
56
+ try {
57
+ const response = await doFetch(`${CODEX_BASE_URL}${ACCOUNTS_CHECK_PATH}`, {
58
+ method: "GET",
59
+ headers: {
60
+ authorization: `Bearer ${token}`,
61
+ accept: "application/json",
62
+ },
63
+ signal,
64
+ });
65
+ if (!response.ok)
66
+ return null;
67
+ return parseAuthorizedAccounts(await response.json());
68
+ }
69
+ catch {
70
+ // Deliberately silent about the cause: the token is in scope here and the
71
+ // failure is advisory, so there is nothing safe or useful to log.
72
+ return null;
73
+ }
74
+ }
75
+ /**
76
+ * Narrows a chosen account id to one the backend authorizes.
77
+ *
78
+ * Only ever narrows: with no authorization data, no default to fall back to, or
79
+ * an id that is already authorized, the caller's own choice is returned
80
+ * unchanged. That keeps this a guard against a known-bad write rather than a new
81
+ * way for a login to end up somewhere the user did not ask for.
82
+ */
83
+ export function constrainSelectionToAuthorized(accountId, authorized) {
84
+ if (!authorized)
85
+ return { accountId, changed: false };
86
+ if (authorized.accountIds.includes(accountId)) {
87
+ return { accountId, changed: false };
88
+ }
89
+ const fallback = authorized.defaultAccountId;
90
+ if (!fallback || fallback === accountId) {
91
+ return { accountId, changed: false };
92
+ }
93
+ return { accountId: fallback, changed: true, rejected: accountId };
94
+ }
95
+ /**
96
+ * Applies {@link constrainSelectionToAuthorized} to a resolved login selection.
97
+ *
98
+ * On a rewrite the source becomes `token`: the backend's default account IS the
99
+ * account these credentials are, so it must auto-follow later token refreshes
100
+ * (see `shouldUpdateAccountIdFromToken`) instead of being pinned the way an
101
+ * explicit org/manual choice is. The label follows the new id too; an empty
102
+ * label (not undefined) is deliberate, because the account-pool merge keeps
103
+ * the saved label on undefined and would go on naming the rejected workspace.
104
+ *
105
+ * The workspace list is narrowed with {@link toAuthorizedWorkspaces}.
106
+ */
107
+ export function applyAuthorizedAccountConstraint(selection, authorized) {
108
+ const current = readString(selection.accountIdOverride);
109
+ if (!current || !authorized)
110
+ return { selection, result: null };
111
+ const result = constrainSelectionToAuthorized(current, authorized);
112
+ if (!result.changed)
113
+ return { selection, result };
114
+ const workspaces = selection.workspaces
115
+ ? toAuthorizedWorkspaces(selection.workspaces, authorized, result.accountId)
116
+ : undefined;
117
+ return {
118
+ selection: {
119
+ ...selection,
120
+ accountIdOverride: result.accountId,
121
+ accountIdSource: "token",
122
+ accountLabel: workspaces?.find((workspace) => workspace.id === result.accountId)
123
+ ?.name ?? "",
124
+ ...(workspaces ? { workspaces } : {}),
125
+ },
126
+ result,
127
+ };
128
+ }
129
+ /**
130
+ * Keeps only workspaces the backend authorizes and makes `accountId` the one
131
+ * default, adding it when the token claims never listed it.
132
+ *
133
+ * The plugin host sends workspaces[currentWorkspaceIndex].id ahead of
134
+ * accountId, and the account-pool merge keeps a saved pointer while its id
135
+ * survives, else falls back to the default. With every unauthorized
136
+ * workspace gone and `accountId` the only default, neither path can land on
137
+ * a workspace the backend refuses.
138
+ */
139
+ function toAuthorizedWorkspaces(workspaces, authorized, accountId) {
140
+ const kept = workspaces
141
+ .filter((workspace) => workspace.id === accountId || authorized.accountIds.includes(workspace.id))
142
+ .map((workspace) => ({ ...workspace, isDefault: workspace.id === accountId }));
143
+ if (!kept.some((workspace) => workspace.id === accountId)) {
144
+ kept.push({ id: accountId, enabled: true, isDefault: true });
145
+ }
146
+ return kept;
147
+ }
148
+ /**
149
+ * Re-checks an explicit (`manual`) binding and keeps its CodexCliMirror in
150
+ * step, mutating the record in place: set to the backend default while the
151
+ * explicit id is unauthorized, removed once it is authorized again. The
152
+ * explicit id itself is never changed. Fails open: no answer, no change.
153
+ */
154
+ export async function refreshCodexCliMirror(account, accessToken, options = {}) {
155
+ if (account.accountIdSource !== "manual")
156
+ return null;
157
+ const currentId = readString(account.accountId);
158
+ if (!currentId)
159
+ return null;
160
+ const authorized = await fetchAuthorizedAccounts(accessToken, options);
161
+ if (!authorized)
162
+ return null;
163
+ if (authorized.accountIds.includes(currentId)) {
164
+ if (!account.codexCliMirror)
165
+ return null;
166
+ delete account.codexCliMirror;
167
+ return "cleared";
168
+ }
169
+ const result = constrainSelectionToAuthorized(currentId, authorized);
170
+ if (!result.changed)
171
+ return null;
172
+ const mirror = account.codexCliMirror;
173
+ if (mirror?.forAccountId === currentId && mirror.accountId === result.accountId) {
174
+ return null;
175
+ }
176
+ account.codexCliMirror = { forAccountId: currentId, accountId: result.accountId };
177
+ return "set";
178
+ }
179
+ /**
180
+ * Rebinds a saved account's id when it is not one the backend currently
181
+ * authorizes, mutating the record in place. This is the migration path for
182
+ * accounts saved before this guard existed (or by `codex-multi-auth workspace
183
+ * <account> <workspace>`, which has no live check of its own).
184
+ *
185
+ * Scoped to `accountIdSource === "org"` — the one source that never
186
+ * auto-follows the token (`shouldUpdateAccountIdFromToken`). "token" /
187
+ * "id_token" already self-correct through `applyTokenAccountIdentity`, and
188
+ * "manual" is an explicit `login --org` binding this must not override.
189
+ */
190
+ export async function reboundUnauthorizedAccountIdentity(account, accessToken, options = {}) {
191
+ if (account.accountIdSource !== "org")
192
+ return null;
193
+ const currentId = readString(account.accountId);
194
+ if (!currentId)
195
+ return null;
196
+ const authorized = await fetchAuthorizedAccounts(accessToken, options);
197
+ const result = constrainSelectionToAuthorized(currentId, authorized);
198
+ if (!authorized || !result.changed)
199
+ return null;
200
+ account.accountId = result.accountId;
201
+ account.accountIdSource = "token";
202
+ // The workspace pointer has to follow the rebind: the plugin host sends
203
+ // the pointed-at workspace id ahead of accountId.
204
+ if (account.workspaces) {
205
+ account.workspaces = toAuthorizedWorkspaces(account.workspaces, authorized, result.accountId);
206
+ const workspaceIndex = account.workspaces.findIndex((workspace) => workspace.id === result.accountId);
207
+ account.currentWorkspaceIndex = workspaceIndex;
208
+ account.accountLabel = account.workspaces[workspaceIndex]?.name ?? "";
209
+ }
210
+ else if (account.accountLabel !== undefined) {
211
+ account.accountLabel = "";
212
+ }
213
+ return result;
214
+ }
@@ -2,6 +2,7 @@
2
2
  * Token utility functions for JWT parsing and account ID extraction.
3
3
  * Extracted from accounts.ts to reduce module size and improve cohesion.
4
4
  */
5
+ import type { CodexCliMirror } from "../storage/public-types.js";
5
6
  import type { AccountIdSource } from "../types.js";
6
7
  /**
7
8
  * Account ID candidate from token or organization data.
@@ -13,9 +14,13 @@ export interface AccountIdCandidate {
13
14
  isDefault?: boolean;
14
15
  isPersonal?: boolean;
15
16
  }
17
+ /** Explicit metadata wins over the conventional Personal display name. */
18
+ export declare function isPersonalAccountCandidate(candidate: AccountIdCandidate): boolean;
16
19
  /**
17
20
  * Select the best workspace candidate for OAuth account binding.
18
- * Preference order:
21
+ * Prefer a uniquely identifiable Personal workspace. Ambiguous CLI logins
22
+ * require an explicit choice before reaching this fallback selector.
23
+ * Fallback order when no unique Personal candidate exists:
19
24
  * 1) org default that is not personal
20
25
  * 2) org default (any)
21
26
  * 3) id_token candidate
@@ -30,6 +35,53 @@ export declare function selectBestAccountCandidate(candidates: AccountIdCandidat
30
35
  * @returns Account ID string or undefined if not found
31
36
  */
32
37
  export declare function extractAccountId(accessToken?: string): string | undefined;
38
+ /**
39
+ * Maps a stored account id to the value Codex CLI accepts as tokens.account_id.
40
+ * Codex CLI >= 0.156 checks that field against the token's ChatGPT workspaces;
41
+ * an OpenAI platform org id ("org-...") is never one of them, so it resolves to
42
+ * the token's chatgpt_account_id claim instead, or undefined without a claim.
43
+ * The auth.json writer and every stored-vs-auth.json comparison must share
44
+ * this mapping, or org-sourced accounts read as permanently drifted.
45
+ */
46
+ /** Whether an id is an OpenAI platform org id ("org-..."), never a ChatGPT workspace. */
47
+ export declare function isOpenAiOrgId(accountId: string | undefined): boolean;
48
+ export declare function resolveCodexAuthAccountId(accountId: string | undefined, accessToken?: string, idToken?: string): string | undefined;
49
+ /**
50
+ * The account id to hand the ~/.codex/auth.json writer for a saved account,
51
+ * and to compare against that file. Applies the account's live
52
+ * {@link CodexCliMirror} first, then resolveCodexAuthAccountId's org-id
53
+ * substitution. An org id without a token claim is returned unchanged so the
54
+ * writer can still drop it. Every auth.json writer and drift check goes
55
+ * through this; a raw `account.accountId` there writes back an id Codex CLI
56
+ * refuses.
57
+ */
58
+ export declare function codexCliAccountIdFor(account: {
59
+ accountId?: string;
60
+ codexCliMirror?: CodexCliMirror;
61
+ }, accessToken?: string, idToken?: string): string | undefined;
62
+ interface CodexAccountIdentity {
63
+ accountId?: string;
64
+ accessToken?: string;
65
+ }
66
+ /**
67
+ * The Codex CLI state's active account id with the access token of the
68
+ * snapshot it came from, so an "org-..." id (the legacy accounts.json keeps
69
+ * the raw id) can be resolved against that account's own token.
70
+ */
71
+ export declare function codexCliActiveIdentity(state: {
72
+ activeAccountId?: string;
73
+ accounts?: ReadonlyArray<CodexAccountIdentity & {
74
+ isActive?: boolean;
75
+ }>;
76
+ }): CodexAccountIdentity;
77
+ /**
78
+ * Whether two account ids name the same Codex account. Equal raw ids match;
79
+ * otherwise each side goes through resolveCodexAuthAccountId with its own
80
+ * token, so a stored "org-..." id matches the workspace id the writer put in
81
+ * auth.json. Unresolvable ids (org id, no claim) never match a different id.
82
+ * Equal org ids still differ when both tokens name different workspaces.
83
+ */
84
+ export declare function codexAuthAccountIdsMatch(left: CodexAccountIdentity, right: CodexAccountIdentity): boolean;
33
85
  /**
34
86
  * Extracts the email address from OAuth tokens.
35
87
  * Checks id_token first (where OpenAI puts email), then falls back to access_token.
@@ -72,4 +124,5 @@ export declare function resolveRuntimeRequestIdentity(input: {
72
124
  * @returns Sanitized email or undefined if invalid
73
125
  */
74
126
  export declare function sanitizeEmail(email: string | undefined): string | undefined;
127
+ export {};
75
128
  //# sourceMappingURL=token-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../../lib/auth/token-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,aAAa,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAoND;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,kBAAkB,EAAE,GAC9B,kBAAkB,GAAG,SAAS,CA8BhC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuB9F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,GACd,kBAAkB,EAAE,CA+BtB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC7C,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAIT;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,cAAc,EAAE,MAAM,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAMpB;AAED,MAAM,WAAW,sBAAsB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,sBAAsB,CAazB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAK3E"}
1
+ {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../../lib/auth/token-utils.ts"],"names":[],"mappings":"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"}
@@ -180,19 +180,33 @@ function collectCandidatesFromPayload(payload, source) {
180
180
  * Removes duplicate candidates by accountId.
181
181
  */
182
182
  function uniqueCandidates(candidates) {
183
- const seen = new Set();
184
- const result = [];
183
+ const byId = new Map();
185
184
  for (const candidate of candidates) {
186
- if (seen.has(candidate.accountId))
185
+ const kept = byId.get(candidate.accountId);
186
+ if (!kept) {
187
+ byId.set(candidate.accountId, { ...candidate });
187
188
  continue;
188
- seen.add(candidate.accountId);
189
- result.push(candidate);
189
+ }
190
+ const wasPersonal = isPersonalAccountCandidate(kept);
191
+ if (kept.isPersonal === undefined && candidate.isPersonal !== undefined) {
192
+ kept.isPersonal = candidate.isPersonal;
193
+ }
194
+ if (!wasPersonal && isPersonalAccountCandidate(candidate) && kept.isPersonal !== false) {
195
+ kept.label = candidate.label;
196
+ }
190
197
  }
191
- return result;
198
+ return [...byId.values()];
199
+ }
200
+ /** Explicit metadata wins over the conventional Personal display name. */
201
+ export function isPersonalAccountCandidate(candidate) {
202
+ return candidate.isPersonal === true ||
203
+ (candidate.isPersonal !== false && /^Personal(?:\s*\(|\s*\[|$)/i.test(candidate.label.trim()));
192
204
  }
193
205
  /**
194
206
  * Select the best workspace candidate for OAuth account binding.
195
- * Preference order:
207
+ * Prefer a uniquely identifiable Personal workspace. Ambiguous CLI logins
208
+ * require an explicit choice before reaching this fallback selector.
209
+ * Fallback order when no unique Personal candidate exists:
196
210
  * 1) org default that is not personal
197
211
  * 2) org default (any)
198
212
  * 3) id_token candidate
@@ -203,6 +217,9 @@ function uniqueCandidates(candidates) {
203
217
  export function selectBestAccountCandidate(candidates) {
204
218
  if (candidates.length === 0)
205
219
  return undefined;
220
+ const personal = candidates.filter(isPersonalAccountCandidate);
221
+ if (personal.length === 1)
222
+ return personal[0];
206
223
  const orgDefaultNonPersonal = candidates.find((candidate) => candidate.source === "org" && candidate.isDefault === true && candidate.isPersonal !== true);
207
224
  if (orgDefaultNonPersonal)
208
225
  return orgDefaultNonPersonal;
@@ -232,6 +249,74 @@ export function extractAccountId(accessToken) {
232
249
  const accountId = decoded?.[JWT_CLAIM_PATH]?.chatgpt_account_id;
233
250
  return typeof accountId === "string" && accountId.trim() ? accountId : undefined;
234
251
  }
252
+ /**
253
+ * Maps a stored account id to the value Codex CLI accepts as tokens.account_id.
254
+ * Codex CLI >= 0.156 checks that field against the token's ChatGPT workspaces;
255
+ * an OpenAI platform org id ("org-...") is never one of them, so it resolves to
256
+ * the token's chatgpt_account_id claim instead, or undefined without a claim.
257
+ * The auth.json writer and every stored-vs-auth.json comparison must share
258
+ * this mapping, or org-sourced accounts read as permanently drifted.
259
+ */
260
+ /** Whether an id is an OpenAI platform org id ("org-..."), never a ChatGPT workspace. */
261
+ export function isOpenAiOrgId(accountId) {
262
+ return /^org-/i.test(accountId?.trim() ?? "");
263
+ }
264
+ export function resolveCodexAuthAccountId(accountId, accessToken, idToken) {
265
+ const trimmed = accountId?.trim();
266
+ if (!trimmed)
267
+ return undefined;
268
+ if (!isOpenAiOrgId(trimmed))
269
+ return trimmed;
270
+ return extractAccountId(accessToken) ?? extractAccountId(idToken);
271
+ }
272
+ /**
273
+ * The account id to hand the ~/.codex/auth.json writer for a saved account,
274
+ * and to compare against that file. Applies the account's live
275
+ * {@link CodexCliMirror} first, then resolveCodexAuthAccountId's org-id
276
+ * substitution. An org id without a token claim is returned unchanged so the
277
+ * writer can still drop it. Every auth.json writer and drift check goes
278
+ * through this; a raw `account.accountId` there writes back an id Codex CLI
279
+ * refuses.
280
+ */
281
+ export function codexCliAccountIdFor(account, accessToken, idToken) {
282
+ const accountId = account.accountId?.trim();
283
+ const mirror = account.codexCliMirror;
284
+ const selected = accountId && mirror && mirror.forAccountId === accountId
285
+ ? mirror.accountId
286
+ : account.accountId;
287
+ return resolveCodexAuthAccountId(selected, accessToken, idToken) ?? selected;
288
+ }
289
+ /**
290
+ * The Codex CLI state's active account id with the access token of the
291
+ * snapshot it came from, so an "org-..." id (the legacy accounts.json keeps
292
+ * the raw id) can be resolved against that account's own token.
293
+ */
294
+ export function codexCliActiveIdentity(state) {
295
+ const accountId = state.activeAccountId?.trim();
296
+ const accounts = state.accounts ?? [];
297
+ const snapshot = accounts.find((entry) => !!accountId && entry.accountId?.trim() === accountId) ??
298
+ accounts.find((entry) => entry.isActive);
299
+ return { accountId, accessToken: snapshot?.accessToken };
300
+ }
301
+ /**
302
+ * Whether two account ids name the same Codex account. Equal raw ids match;
303
+ * otherwise each side goes through resolveCodexAuthAccountId with its own
304
+ * token, so a stored "org-..." id matches the workspace id the writer put in
305
+ * auth.json. Unresolvable ids (org id, no claim) never match a different id.
306
+ * Equal org ids still differ when both tokens name different workspaces.
307
+ */
308
+ export function codexAuthAccountIdsMatch(left, right) {
309
+ const leftId = left.accountId?.trim();
310
+ const rightId = right.accountId?.trim();
311
+ if (!leftId || !rightId)
312
+ return false;
313
+ const leftResolved = resolveCodexAuthAccountId(leftId, left.accessToken);
314
+ const rightResolved = resolveCodexAuthAccountId(rightId, right.accessToken);
315
+ if (leftId === rightId) {
316
+ return !leftResolved || !rightResolved || leftResolved === rightResolved;
317
+ }
318
+ return !!leftResolved && leftResolved === rightResolved;
319
+ }
235
320
  /**
236
321
  * Extracts the email address from OAuth tokens.
237
322
  * Checks id_token first (where OpenAI puts email), then falls back to access_token.
package/dist/lib/cli.d.ts CHANGED
@@ -8,7 +8,7 @@ import type { AccountCurrentMarker } from "./runtime/runtime-current-account.js"
8
8
  */
9
9
  export declare function isNonInteractiveMode(): boolean;
10
10
  export declare function promptAddAnotherAccount(currentCount: number): Promise<boolean>;
11
- export type LoginMode = "add" | "forecast" | "fix" | "settings" | "fresh" | "manage" | "check" | "deep-check" | "verify-flagged" | "cancel";
11
+ export type LoginMode = "add" | "forecast" | "fix" | "settings" | "api-models" | "fresh" | "manage" | "check" | "deep-check" | "verify-flagged" | "cancel";
12
12
  export interface ExistingAccountInfo {
13
13
  accountId?: string;
14
14
  accountLabel?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"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"}
package/dist/lib/cli.js CHANGED
@@ -90,6 +90,8 @@ async function promptLoginModeFallback(existingAccounts) {
90
90
  while (true) {
91
91
  const answer = await rl.question(UI_COPY.fallback.selectModePrompt);
92
92
  const normalized = answer.trim().toLowerCase();
93
+ if (normalized === "api")
94
+ return { mode: "api-models" };
93
95
  if (normalized === "a" || normalized === "add")
94
96
  return { mode: "add" };
95
97
  if (normalized === "b" || normalized === "p" || normalized === "forecast") {
@@ -142,6 +144,8 @@ export async function promptLoginMode(existingAccounts, options = {}) {
142
144
  return { mode: "forecast" };
143
145
  case "fix":
144
146
  return { mode: "fix" };
147
+ case "api-models":
148
+ return { mode: "api-models" };
145
149
  case "settings":
146
150
  return { mode: "settings" };
147
151
  case "fresh":
@@ -13,6 +13,13 @@ export interface CodexCliState {
13
13
  accounts: CodexCliAccountSnapshot[];
14
14
  activeAccountId?: string;
15
15
  activeEmail?: string;
16
+ /**
17
+ * The raw tokens.account_id of auth.json whenever auth.json exists, also
18
+ * when the state itself came from the legacy accounts.json. Codex CLI
19
+ * 0.156+ rejects an "org-..." value there (#700), unlike accounts.json,
20
+ * which keeps raw org ids.
21
+ */
22
+ authFileAccountId?: string;
16
23
  syncVersion?: number;
17
24
  sourceUpdatedAtMs?: number;
18
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../lib/codex-cli/state.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,uBAAuB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAsID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAgB/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AAwHD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAwJ/B;AAED,wBAAsB,2BAA2B,CAChD,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAgBzC;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../lib/codex-cli/state.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,uBAAuB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAsID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAgB/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAIhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C;AA0HD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA2K/B;AAED,wBAAsB,2BAA2B,CAChD,KAAK,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAgBzC;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D"}
@@ -299,6 +299,7 @@ function parseCodexCliAuthState(path, parsed, sourceUpdatedAtMs) {
299
299
  accounts: [snapshot],
300
300
  activeAccountId: accountId,
301
301
  activeEmail: email,
302
+ authFileAccountId: readTrimmedString(tokens.account_id) ?? readTrimmedString(tokens.accountId),
302
303
  syncVersion: readNumber(parsed.codexMultiAuthSyncVersion),
303
304
  sourceUpdatedAtMs,
304
305
  };
@@ -372,6 +373,23 @@ export async function loadCodexCliState(options) {
372
373
  }
373
374
  const state = parseCodexCliState(accountsPath, parsed, sourceUpdatedAtMs);
374
375
  if (state) {
376
+ if (hasAuthPath) {
377
+ // Codex CLI itself reads auth.json, so a stale org account_id
378
+ // there must stay visible behind a valid accounts.json.
379
+ try {
380
+ const authParsed = JSON.parse(await retryFsOperation(() => fs.readFile(authPath, "utf-8")));
381
+ const authTokens = isRecord(authParsed) && isRecord(authParsed.tokens)
382
+ ? authParsed.tokens
383
+ : undefined;
384
+ state.authFileAccountId = authTokens
385
+ ? (readTrimmedString(authTokens.account_id) ??
386
+ readTrimmedString(authTokens.accountId))
387
+ : undefined;
388
+ }
389
+ catch {
390
+ // Unreadable auth.json: nothing to flag here.
391
+ }
392
+ }
375
393
  incrementCodexCliMetric("readSuccesses");
376
394
  log.debug("Loaded Codex CLI state", {
377
395
  operation: "read-state",
@@ -1 +1 @@
1
- {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../lib/codex-cli/writer.ts"],"names":[],"mappings":"AAoBA,UAAU,eAAe;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AA8ND;;;;;;GAMG;AACH,wBAAgB,kCAAkC,IAAI,OAAO,CAG5D;AAiBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,2BAA2B,CAChD,UAAU,GAAE,MAAgC,GAC1C,OAAO,CAAC,OAAO,CAAC,CAgDlB;AAmHD,wBAAsB,0BAA0B,CAC/C,SAAS,EAAE,eAAe,GACxB,OAAO,CAAC,OAAO,CAAC,CAqMlB;AAED,wBAAgB,sCAAsC,IAAI,MAAM,CAE/D"}
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../lib/codex-cli/writer.ts"],"names":[],"mappings":"AA4BA,UAAU,eAAe;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AA8ND;;;;;;GAMG;AACH,wBAAgB,kCAAkC,IAAI,OAAO,CAG5D;AAiBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,2BAA2B,CAChD,UAAU,GAAE,MAAgC,GAC1C,OAAO,CAAC,OAAO,CAAC,CAgDlB;AA4JD,wBAAsB,0BAA0B,CAC/C,SAAS,EAAE,eAAe,GACxB,OAAO,CAAC,OAAO,CAAC,CAkNlB;AAED,wBAAgB,sCAAsC,IAAI,MAAM,CAE/D"}