codex-multi-auth 1.1.11 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (695) hide show
  1. package/README.md +52 -20
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1023 -1661
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/accounts.d.ts +22 -2
  6. package/dist/lib/accounts.d.ts.map +1 -1
  7. package/dist/lib/accounts.js +84 -1
  8. package/dist/lib/accounts.js.map +1 -1
  9. package/dist/lib/auth/browser.d.ts +1 -0
  10. package/dist/lib/auth/browser.d.ts.map +1 -1
  11. package/dist/lib/auth/browser.js +13 -0
  12. package/dist/lib/auth/browser.js.map +1 -1
  13. package/dist/lib/auth/index.d.ts +2 -0
  14. package/dist/lib/auth/index.d.ts.map +1 -0
  15. package/dist/lib/auth/index.js +2 -0
  16. package/dist/lib/auth/index.js.map +1 -0
  17. package/dist/lib/auth/token-utils.d.ts +16 -0
  18. package/dist/lib/auth/token-utils.d.ts.map +1 -1
  19. package/dist/lib/auth/token-utils.js +13 -0
  20. package/dist/lib/auth/token-utils.js.map +1 -1
  21. package/dist/lib/codex-cli/index.d.ts +5 -0
  22. package/dist/lib/codex-cli/index.d.ts.map +1 -0
  23. package/dist/lib/codex-cli/index.js +5 -0
  24. package/dist/lib/codex-cli/index.js.map +1 -0
  25. package/dist/lib/codex-manager/auth-commands.d.ts +72 -0
  26. package/dist/lib/codex-manager/auth-commands.d.ts.map +1 -0
  27. package/dist/lib/codex-manager/auth-commands.js +643 -0
  28. package/dist/lib/codex-manager/auth-commands.js.map +1 -0
  29. package/dist/lib/codex-manager/backend-category-entry.d.ts +103 -0
  30. package/dist/lib/codex-manager/backend-category-entry.d.ts.map +1 -0
  31. package/dist/lib/codex-manager/backend-category-entry.js +23 -0
  32. package/dist/lib/codex-manager/backend-category-entry.js.map +1 -0
  33. package/dist/lib/codex-manager/backend-category-helpers.d.ts +10 -0
  34. package/dist/lib/codex-manager/backend-category-helpers.d.ts.map +1 -0
  35. package/dist/lib/codex-manager/backend-category-helpers.js +29 -0
  36. package/dist/lib/codex-manager/backend-category-helpers.js.map +1 -0
  37. package/dist/lib/codex-manager/backend-category-prompt.d.ts +55 -0
  38. package/dist/lib/codex-manager/backend-category-prompt.d.ts.map +1 -0
  39. package/dist/lib/codex-manager/backend-category-prompt.js +178 -0
  40. package/dist/lib/codex-manager/backend-category-prompt.js.map +1 -0
  41. package/dist/lib/codex-manager/backend-settings-controller.d.ts +11 -0
  42. package/dist/lib/codex-manager/backend-settings-controller.d.ts.map +1 -0
  43. package/dist/lib/codex-manager/backend-settings-controller.js +14 -0
  44. package/dist/lib/codex-manager/backend-settings-controller.js.map +1 -0
  45. package/dist/lib/codex-manager/backend-settings-entry.d.ts +20 -0
  46. package/dist/lib/codex-manager/backend-settings-entry.d.ts.map +1 -0
  47. package/dist/lib/codex-manager/backend-settings-entry.js +12 -0
  48. package/dist/lib/codex-manager/backend-settings-entry.js.map +1 -0
  49. package/dist/lib/codex-manager/backend-settings-helpers.d.ts +16 -0
  50. package/dist/lib/codex-manager/backend-settings-helpers.d.ts.map +1 -0
  51. package/dist/lib/codex-manager/backend-settings-helpers.js +102 -0
  52. package/dist/lib/codex-manager/backend-settings-helpers.js.map +1 -0
  53. package/dist/lib/codex-manager/backend-settings-prompt.d.ts +49 -0
  54. package/dist/lib/codex-manager/backend-settings-prompt.d.ts.map +1 -0
  55. package/dist/lib/codex-manager/backend-settings-prompt.js +121 -0
  56. package/dist/lib/codex-manager/backend-settings-prompt.js.map +1 -0
  57. package/dist/lib/codex-manager/backend-settings-schema.d.ts +55 -0
  58. package/dist/lib/codex-manager/backend-settings-schema.d.ts.map +1 -0
  59. package/dist/lib/codex-manager/backend-settings-schema.js +264 -0
  60. package/dist/lib/codex-manager/backend-settings-schema.js.map +1 -0
  61. package/dist/lib/codex-manager/behavior-settings-panel.d.ts +33 -0
  62. package/dist/lib/codex-manager/behavior-settings-panel.d.ts.map +1 -0
  63. package/dist/lib/codex-manager/behavior-settings-panel.js +180 -0
  64. package/dist/lib/codex-manager/behavior-settings-panel.js.map +1 -0
  65. package/dist/lib/codex-manager/best.d.ts +26 -0
  66. package/dist/lib/codex-manager/best.d.ts.map +1 -0
  67. package/dist/lib/codex-manager/best.js +249 -0
  68. package/dist/lib/codex-manager/best.js.map +1 -0
  69. package/dist/lib/codex-manager/commands/best.d.ts +81 -0
  70. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -0
  71. package/dist/lib/codex-manager/commands/best.js +223 -0
  72. package/dist/lib/codex-manager/commands/best.js.map +1 -0
  73. package/dist/lib/codex-manager/commands/check.d.ts +7 -0
  74. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -0
  75. package/dist/lib/codex-manager/commands/check.js +5 -0
  76. package/dist/lib/codex-manager/commands/check.js.map +1 -0
  77. package/dist/lib/codex-manager/commands/config-explain.d.ts +7 -0
  78. package/dist/lib/codex-manager/commands/config-explain.d.ts.map +1 -0
  79. package/dist/lib/codex-manager/commands/config-explain.js +31 -0
  80. package/dist/lib/codex-manager/commands/config-explain.js.map +1 -0
  81. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +28 -0
  82. package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -0
  83. package/dist/lib/codex-manager/commands/debug-bundle.js +62 -0
  84. package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -0
  85. package/dist/lib/codex-manager/commands/doctor.d.ts +85 -0
  86. package/dist/lib/codex-manager/commands/doctor.d.ts.map +1 -0
  87. package/dist/lib/codex-manager/commands/doctor.js +424 -0
  88. package/dist/lib/codex-manager/commands/doctor.js.map +1 -0
  89. package/dist/lib/codex-manager/commands/fix.d.ts +92 -0
  90. package/dist/lib/codex-manager/commands/fix.d.ts.map +1 -0
  91. package/dist/lib/codex-manager/commands/fix.js +347 -0
  92. package/dist/lib/codex-manager/commands/fix.js.map +1 -0
  93. package/dist/lib/codex-manager/commands/forecast.d.ts +70 -0
  94. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -0
  95. package/dist/lib/codex-manager/commands/forecast.js +281 -0
  96. package/dist/lib/codex-manager/commands/forecast.js.map +1 -0
  97. package/dist/lib/codex-manager/commands/init-config.d.ts +15 -0
  98. package/dist/lib/codex-manager/commands/init-config.d.ts.map +1 -0
  99. package/dist/lib/codex-manager/commands/init-config.js +95 -0
  100. package/dist/lib/codex-manager/commands/init-config.js.map +1 -0
  101. package/dist/lib/codex-manager/commands/report.d.ts +24 -0
  102. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -0
  103. package/dist/lib/codex-manager/commands/report.js +314 -0
  104. package/dist/lib/codex-manager/commands/report.js.map +1 -0
  105. package/dist/lib/codex-manager/commands/status.d.ts +23 -0
  106. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -0
  107. package/dist/lib/codex-manager/commands/status.js +50 -0
  108. package/dist/lib/codex-manager/commands/status.js.map +1 -0
  109. package/dist/lib/codex-manager/commands/switch.d.ts +22 -0
  110. package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -0
  111. package/dist/lib/codex-manager/commands/switch.js +41 -0
  112. package/dist/lib/codex-manager/commands/switch.js.map +1 -0
  113. package/dist/lib/codex-manager/commands/verify-flagged.d.ts +69 -0
  114. package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +1 -0
  115. package/dist/lib/codex-manager/commands/verify-flagged.js +251 -0
  116. package/dist/lib/codex-manager/commands/verify-flagged.js.map +1 -0
  117. package/dist/lib/codex-manager/config-explain.d.ts +2 -0
  118. package/dist/lib/codex-manager/config-explain.d.ts.map +1 -0
  119. package/dist/lib/codex-manager/config-explain.js +62 -0
  120. package/dist/lib/codex-manager/config-explain.js.map +1 -0
  121. package/dist/lib/codex-manager/config-template.d.ts +2 -0
  122. package/dist/lib/codex-manager/config-template.d.ts.map +1 -0
  123. package/dist/lib/codex-manager/config-template.js +112 -0
  124. package/dist/lib/codex-manager/config-template.js.map +1 -0
  125. package/dist/lib/codex-manager/dashboard-display-panel.d.ts +40 -0
  126. package/dist/lib/codex-manager/dashboard-display-panel.d.ts.map +1 -0
  127. package/dist/lib/codex-manager/dashboard-display-panel.js +187 -0
  128. package/dist/lib/codex-manager/dashboard-display-panel.js.map +1 -0
  129. package/dist/lib/codex-manager/dashboard-formatters.d.ts +6 -0
  130. package/dist/lib/codex-manager/dashboard-formatters.d.ts.map +1 -0
  131. package/dist/lib/codex-manager/dashboard-formatters.js +19 -0
  132. package/dist/lib/codex-manager/dashboard-formatters.js.map +1 -0
  133. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts +12 -0
  134. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts.map +1 -0
  135. package/dist/lib/codex-manager/dashboard-settings-controller.js +17 -0
  136. package/dist/lib/codex-manager/dashboard-settings-controller.js.map +1 -0
  137. package/dist/lib/codex-manager/dashboard-settings-data.d.ts +10 -0
  138. package/dist/lib/codex-manager/dashboard-settings-data.d.ts.map +1 -0
  139. package/dist/lib/codex-manager/dashboard-settings-data.js +95 -0
  140. package/dist/lib/codex-manager/dashboard-settings-data.js.map +1 -0
  141. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts +22 -0
  142. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts.map +1 -0
  143. package/dist/lib/codex-manager/dashboard-settings-entry.js +13 -0
  144. package/dist/lib/codex-manager/dashboard-settings-entry.js.map +1 -0
  145. package/dist/lib/codex-manager/debug-bundle.d.ts +2 -0
  146. package/dist/lib/codex-manager/debug-bundle.d.ts.map +1 -0
  147. package/dist/lib/codex-manager/debug-bundle.js +128 -0
  148. package/dist/lib/codex-manager/debug-bundle.js.map +1 -0
  149. package/dist/lib/codex-manager/doctor.d.ts +20 -0
  150. package/dist/lib/codex-manager/doctor.d.ts.map +1 -0
  151. package/dist/lib/codex-manager/doctor.js +546 -0
  152. package/dist/lib/codex-manager/doctor.js.map +1 -0
  153. package/dist/lib/codex-manager/experimental-settings-entry.d.ts +7 -0
  154. package/dist/lib/codex-manager/experimental-settings-entry.d.ts.map +1 -0
  155. package/dist/lib/codex-manager/experimental-settings-entry.js +30 -0
  156. package/dist/lib/codex-manager/experimental-settings-entry.js.map +1 -0
  157. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts +66 -0
  158. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -0
  159. package/dist/lib/codex-manager/experimental-settings-prompt.js +221 -0
  160. package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -0
  161. package/dist/lib/codex-manager/experimental-settings-schema.d.ts +23 -0
  162. package/dist/lib/codex-manager/experimental-settings-schema.d.ts.map +1 -0
  163. package/dist/lib/codex-manager/experimental-settings-schema.js +34 -0
  164. package/dist/lib/codex-manager/experimental-settings-schema.js.map +1 -0
  165. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts +30 -0
  166. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts.map +1 -0
  167. package/dist/lib/codex-manager/experimental-sync-target-entry.js +16 -0
  168. package/dist/lib/codex-manager/experimental-sync-target-entry.js.map +1 -0
  169. package/dist/lib/codex-manager/experimental-sync-target.d.ts +23 -0
  170. package/dist/lib/codex-manager/experimental-sync-target.d.ts.map +1 -0
  171. package/dist/lib/codex-manager/experimental-sync-target.js +39 -0
  172. package/dist/lib/codex-manager/experimental-sync-target.js.map +1 -0
  173. package/dist/lib/codex-manager/fix.d.ts +26 -0
  174. package/dist/lib/codex-manager/fix.d.ts.map +1 -0
  175. package/dist/lib/codex-manager/fix.js +366 -0
  176. package/dist/lib/codex-manager/fix.js.map +1 -0
  177. package/dist/lib/codex-manager/forecast-report-commands.d.ts +54 -0
  178. package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -0
  179. package/dist/lib/codex-manager/forecast-report-commands.js +443 -0
  180. package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -0
  181. package/dist/lib/codex-manager/forecast.d.ts +26 -0
  182. package/dist/lib/codex-manager/forecast.d.ts.map +1 -0
  183. package/dist/lib/codex-manager/forecast.js +218 -0
  184. package/dist/lib/codex-manager/forecast.js.map +1 -0
  185. package/dist/lib/codex-manager/help.d.ts +36 -0
  186. package/dist/lib/codex-manager/help.d.ts.map +1 -0
  187. package/dist/lib/codex-manager/help.js +125 -0
  188. package/dist/lib/codex-manager/help.js.map +1 -0
  189. package/dist/lib/codex-manager/info.d.ts +11 -0
  190. package/dist/lib/codex-manager/info.d.ts.map +1 -0
  191. package/dist/lib/codex-manager/info.js +91 -0
  192. package/dist/lib/codex-manager/info.js.map +1 -0
  193. package/dist/lib/codex-manager/login.d.ts +40 -0
  194. package/dist/lib/codex-manager/login.d.ts.map +1 -0
  195. package/dist/lib/codex-manager/login.js +1041 -0
  196. package/dist/lib/codex-manager/login.js.map +1 -0
  197. package/dist/lib/codex-manager/repair-commands.d.ts +70 -0
  198. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -0
  199. package/dist/lib/codex-manager/repair-commands.js +1671 -0
  200. package/dist/lib/codex-manager/repair-commands.js.map +1 -0
  201. package/dist/lib/codex-manager/report.d.ts +39 -0
  202. package/dist/lib/codex-manager/report.d.ts.map +1 -0
  203. package/dist/lib/codex-manager/report.js +243 -0
  204. package/dist/lib/codex-manager/report.js.map +1 -0
  205. package/dist/lib/codex-manager/settings-hub-entry.d.ts +30 -0
  206. package/dist/lib/codex-manager/settings-hub-entry.d.ts.map +1 -0
  207. package/dist/lib/codex-manager/settings-hub-entry.js +11 -0
  208. package/dist/lib/codex-manager/settings-hub-entry.js.map +1 -0
  209. package/dist/lib/codex-manager/settings-hub-menu.d.ts +30 -0
  210. package/dist/lib/codex-manager/settings-hub-menu.d.ts.map +1 -0
  211. package/dist/lib/codex-manager/settings-hub-menu.js +37 -0
  212. package/dist/lib/codex-manager/settings-hub-menu.js.map +1 -0
  213. package/dist/lib/codex-manager/settings-hub-prompt.d.ts +25 -0
  214. package/dist/lib/codex-manager/settings-hub-prompt.d.ts.map +1 -0
  215. package/dist/lib/codex-manager/settings-hub-prompt.js +23 -0
  216. package/dist/lib/codex-manager/settings-hub-prompt.js.map +1 -0
  217. package/dist/lib/codex-manager/settings-hub.d.ts +12 -10
  218. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  219. package/dist/lib/codex-manager/settings-hub.js +262 -2187
  220. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  221. package/dist/lib/codex-manager/settings-panels.d.ts +56 -0
  222. package/dist/lib/codex-manager/settings-panels.d.ts.map +1 -0
  223. package/dist/lib/codex-manager/settings-panels.js +73 -0
  224. package/dist/lib/codex-manager/settings-panels.js.map +1 -0
  225. package/dist/lib/codex-manager/settings-persist-utils.d.ts +9 -0
  226. package/dist/lib/codex-manager/settings-persist-utils.d.ts.map +1 -0
  227. package/dist/lib/codex-manager/settings-persist-utils.js +33 -0
  228. package/dist/lib/codex-manager/settings-persist-utils.js.map +1 -0
  229. package/dist/lib/codex-manager/settings-preview.d.ts +12 -0
  230. package/dist/lib/codex-manager/settings-preview.d.ts.map +1 -0
  231. package/dist/lib/codex-manager/settings-preview.js +118 -0
  232. package/dist/lib/codex-manager/settings-preview.js.map +1 -0
  233. package/dist/lib/codex-manager/settings-write-queue.d.ts +14 -0
  234. package/dist/lib/codex-manager/settings-write-queue.d.ts.map +1 -0
  235. package/dist/lib/codex-manager/settings-write-queue.js +88 -0
  236. package/dist/lib/codex-manager/settings-write-queue.js.map +1 -0
  237. package/dist/lib/codex-manager/statusline-order.d.ts +3 -0
  238. package/dist/lib/codex-manager/statusline-order.d.ts.map +1 -0
  239. package/dist/lib/codex-manager/statusline-order.js +17 -0
  240. package/dist/lib/codex-manager/statusline-order.js.map +1 -0
  241. package/dist/lib/codex-manager/statusline-settings-panel.d.ts +40 -0
  242. package/dist/lib/codex-manager/statusline-settings-panel.d.ts.map +1 -0
  243. package/dist/lib/codex-manager/statusline-settings-panel.js +167 -0
  244. package/dist/lib/codex-manager/statusline-settings-panel.js.map +1 -0
  245. package/dist/lib/codex-manager/switch.d.ts +31 -0
  246. package/dist/lib/codex-manager/switch.d.ts.map +1 -0
  247. package/dist/lib/codex-manager/switch.js +103 -0
  248. package/dist/lib/codex-manager/switch.js.map +1 -0
  249. package/dist/lib/codex-manager/theme-settings-panel.d.ts +26 -0
  250. package/dist/lib/codex-manager/theme-settings-panel.d.ts.map +1 -0
  251. package/dist/lib/codex-manager/theme-settings-panel.js +131 -0
  252. package/dist/lib/codex-manager/theme-settings-panel.js.map +1 -0
  253. package/dist/lib/codex-manager/unified-settings-controller.d.ts +27 -0
  254. package/dist/lib/codex-manager/unified-settings-controller.d.ts.map +1 -0
  255. package/dist/lib/codex-manager/unified-settings-controller.js +50 -0
  256. package/dist/lib/codex-manager/unified-settings-controller.js.map +1 -0
  257. package/dist/lib/codex-manager/unified-settings-entry.d.ts +6 -0
  258. package/dist/lib/codex-manager/unified-settings-entry.d.ts.map +1 -0
  259. package/dist/lib/codex-manager/unified-settings-entry.js +23 -0
  260. package/dist/lib/codex-manager/unified-settings-entry.js.map +1 -0
  261. package/dist/lib/codex-manager/verify-flagged.d.ts +20 -0
  262. package/dist/lib/codex-manager/verify-flagged.d.ts.map +1 -0
  263. package/dist/lib/codex-manager/verify-flagged.js +316 -0
  264. package/dist/lib/codex-manager/verify-flagged.js.map +1 -0
  265. package/dist/lib/codex-manager.d.ts +1 -0
  266. package/dist/lib/codex-manager.d.ts.map +1 -1
  267. package/dist/lib/codex-manager.js +767 -2195
  268. package/dist/lib/codex-manager.js.map +1 -1
  269. package/dist/lib/config.d.ts +38 -0
  270. package/dist/lib/config.d.ts.map +1 -1
  271. package/dist/lib/config.js +371 -9
  272. package/dist/lib/config.js.map +1 -1
  273. package/dist/lib/errors.d.ts +9 -0
  274. package/dist/lib/errors.d.ts.map +1 -1
  275. package/dist/lib/errors.js +13 -0
  276. package/dist/lib/errors.js.map +1 -1
  277. package/dist/lib/forecast.d.ts +17 -0
  278. package/dist/lib/forecast.d.ts.map +1 -1
  279. package/dist/lib/forecast.js +33 -6
  280. package/dist/lib/forecast.js.map +1 -1
  281. package/dist/lib/named-backup-export.js +2 -2
  282. package/dist/lib/named-backup-export.js.map +1 -1
  283. package/dist/lib/prompts/codex.d.ts +8 -3
  284. package/dist/lib/prompts/codex.d.ts.map +1 -1
  285. package/dist/lib/prompts/codex.js +16 -26
  286. package/dist/lib/prompts/codex.js.map +1 -1
  287. package/dist/lib/request/failover-config.d.ts +4 -0
  288. package/dist/lib/request/failover-config.d.ts.map +1 -0
  289. package/dist/lib/request/failover-config.js +15 -0
  290. package/dist/lib/request/failover-config.js.map +1 -0
  291. package/dist/lib/request/fetch-helpers.d.ts +28 -4
  292. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  293. package/dist/lib/request/fetch-helpers.js +170 -8
  294. package/dist/lib/request/fetch-helpers.js.map +1 -1
  295. package/dist/lib/request/helpers/model-map.d.ts +49 -12
  296. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  297. package/dist/lib/request/helpers/model-map.js +303 -108
  298. package/dist/lib/request/helpers/model-map.js.map +1 -1
  299. package/dist/lib/request/helpers/tool-utils.d.ts +2 -15
  300. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
  301. package/dist/lib/request/helpers/tool-utils.js +40 -27
  302. package/dist/lib/request/helpers/tool-utils.js.map +1 -1
  303. package/dist/lib/request/index.d.ts +5 -0
  304. package/dist/lib/request/index.d.ts.map +1 -0
  305. package/dist/lib/request/index.js +5 -0
  306. package/dist/lib/request/index.js.map +1 -0
  307. package/dist/lib/request/request-init.d.ts +3 -0
  308. package/dist/lib/request/request-init.d.ts.map +1 -0
  309. package/dist/lib/request/request-init.js +50 -0
  310. package/dist/lib/request/request-init.js.map +1 -0
  311. package/dist/lib/request/request-transformer.d.ts +19 -6
  312. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  313. package/dist/lib/request/request-transformer.js +210 -193
  314. package/dist/lib/request/request-transformer.js.map +1 -1
  315. package/dist/lib/request/response-compaction.d.ts +20 -0
  316. package/dist/lib/request/response-compaction.d.ts.map +1 -0
  317. package/dist/lib/request/response-compaction.js +124 -0
  318. package/dist/lib/request/response-compaction.js.map +1 -0
  319. package/dist/lib/request/response-handler.d.ts +2 -0
  320. package/dist/lib/request/response-handler.d.ts.map +1 -1
  321. package/dist/lib/request/response-handler.js +567 -11
  322. package/dist/lib/request/response-handler.js.map +1 -1
  323. package/dist/lib/request/response-metadata.d.ts +3 -0
  324. package/dist/lib/request/response-metadata.d.ts.map +1 -0
  325. package/dist/lib/request/response-metadata.js +61 -0
  326. package/dist/lib/request/response-metadata.js.map +1 -0
  327. package/dist/lib/request/trace-mode.d.ts +9 -0
  328. package/dist/lib/request/trace-mode.d.ts.map +1 -0
  329. package/dist/lib/request/trace-mode.js +14 -0
  330. package/dist/lib/request/trace-mode.js.map +1 -0
  331. package/dist/lib/request/wait-utils.d.ts +14 -0
  332. package/dist/lib/request/wait-utils.d.ts.map +1 -0
  333. package/dist/lib/request/wait-utils.js +44 -0
  334. package/dist/lib/request/wait-utils.js.map +1 -0
  335. package/dist/lib/runtime/account-check-helpers.d.ts +8 -0
  336. package/dist/lib/runtime/account-check-helpers.d.ts.map +1 -0
  337. package/dist/lib/runtime/account-check-helpers.js +30 -0
  338. package/dist/lib/runtime/account-check-helpers.js.map +1 -0
  339. package/dist/lib/runtime/account-check-types.d.ts +18 -0
  340. package/dist/lib/runtime/account-check-types.d.ts.map +1 -0
  341. package/dist/lib/runtime/account-check-types.js +12 -0
  342. package/dist/lib/runtime/account-check-types.js.map +1 -0
  343. package/dist/lib/runtime/account-check.d.ts +48 -0
  344. package/dist/lib/runtime/account-check.d.ts.map +1 -0
  345. package/dist/lib/runtime/account-check.js +202 -0
  346. package/dist/lib/runtime/account-check.js.map +1 -0
  347. package/dist/lib/runtime/account-health-check.d.ts +8 -0
  348. package/dist/lib/runtime/account-health-check.d.ts.map +1 -0
  349. package/dist/lib/runtime/account-health-check.js +30 -0
  350. package/dist/lib/runtime/account-health-check.js.map +1 -0
  351. package/dist/lib/runtime/account-manager-cache-entry.d.ts +25 -0
  352. package/dist/lib/runtime/account-manager-cache-entry.d.ts.map +1 -0
  353. package/dist/lib/runtime/account-manager-cache-entry.js +17 -0
  354. package/dist/lib/runtime/account-manager-cache-entry.js.map +1 -0
  355. package/dist/lib/runtime/account-manager-cache.d.ts +25 -0
  356. package/dist/lib/runtime/account-manager-cache.d.ts.map +1 -0
  357. package/dist/lib/runtime/account-manager-cache.js +44 -0
  358. package/dist/lib/runtime/account-manager-cache.js.map +1 -0
  359. package/dist/lib/runtime/account-pool.d.ts +23 -0
  360. package/dist/lib/runtime/account-pool.d.ts.map +1 -0
  361. package/dist/lib/runtime/account-pool.js +138 -0
  362. package/dist/lib/runtime/account-pool.js.map +1 -0
  363. package/dist/lib/runtime/account-scope.d.ts +10 -0
  364. package/dist/lib/runtime/account-scope.d.ts.map +1 -0
  365. package/dist/lib/runtime/account-scope.js +13 -0
  366. package/dist/lib/runtime/account-scope.js.map +1 -0
  367. package/dist/lib/runtime/account-select-event.d.ts +19 -0
  368. package/dist/lib/runtime/account-select-event.d.ts.map +1 -0
  369. package/dist/lib/runtime/account-select-event.js +53 -0
  370. package/dist/lib/runtime/account-select-event.js.map +1 -0
  371. package/dist/lib/runtime/account-selection.d.ts +28 -0
  372. package/dist/lib/runtime/account-selection.d.ts.map +1 -0
  373. package/dist/lib/runtime/account-selection.js +46 -0
  374. package/dist/lib/runtime/account-selection.js.map +1 -0
  375. package/dist/lib/runtime/account-state.d.ts +13 -0
  376. package/dist/lib/runtime/account-state.d.ts.map +1 -0
  377. package/dist/lib/runtime/account-state.js +38 -0
  378. package/dist/lib/runtime/account-state.js.map +1 -0
  379. package/dist/lib/runtime/account-status.d.ts +13 -0
  380. package/dist/lib/runtime/account-status.d.ts.map +1 -0
  381. package/dist/lib/runtime/account-status.js +37 -0
  382. package/dist/lib/runtime/account-status.js.map +1 -0
  383. package/dist/lib/runtime/account-storage-scope-entry.d.ts +26 -0
  384. package/dist/lib/runtime/account-storage-scope-entry.d.ts.map +1 -0
  385. package/dist/lib/runtime/account-storage-scope-entry.js +15 -0
  386. package/dist/lib/runtime/account-storage-scope-entry.js.map +1 -0
  387. package/dist/lib/runtime/auth-facade.d.ts +51 -0
  388. package/dist/lib/runtime/auth-facade.d.ts.map +1 -0
  389. package/dist/lib/runtime/auth-facade.js +48 -0
  390. package/dist/lib/runtime/auth-facade.js.map +1 -0
  391. package/dist/lib/runtime/browser-oauth-flow.d.ts +32 -0
  392. package/dist/lib/runtime/browser-oauth-flow.d.ts.map +1 -0
  393. package/dist/lib/runtime/browser-oauth-flow.js +31 -0
  394. package/dist/lib/runtime/browser-oauth-flow.js.map +1 -0
  395. package/dist/lib/runtime/capability-boost.d.ts +25 -0
  396. package/dist/lib/runtime/capability-boost.d.ts.map +1 -0
  397. package/dist/lib/runtime/capability-boost.js +26 -0
  398. package/dist/lib/runtime/capability-boost.js.map +1 -0
  399. package/dist/lib/runtime/event-handler.d.ts +53 -0
  400. package/dist/lib/runtime/event-handler.d.ts.map +1 -0
  401. package/dist/lib/runtime/event-handler.js +46 -0
  402. package/dist/lib/runtime/event-handler.js.map +1 -0
  403. package/dist/lib/runtime/flagged-verify-types.d.ts +9 -0
  404. package/dist/lib/runtime/flagged-verify-types.d.ts.map +1 -0
  405. package/dist/lib/runtime/flagged-verify-types.js +7 -0
  406. package/dist/lib/runtime/flagged-verify-types.js.map +1 -0
  407. package/dist/lib/runtime/hydrate-emails.d.ts +13 -0
  408. package/dist/lib/runtime/hydrate-emails.d.ts.map +1 -0
  409. package/dist/lib/runtime/hydrate-emails.js +56 -0
  410. package/dist/lib/runtime/hydrate-emails.js.map +1 -0
  411. package/dist/lib/runtime/live-sync-entry.d.ts +36 -0
  412. package/dist/lib/runtime/live-sync-entry.d.ts.map +1 -0
  413. package/dist/lib/runtime/live-sync-entry.js +14 -0
  414. package/dist/lib/runtime/live-sync-entry.js.map +1 -0
  415. package/dist/lib/runtime/live-sync.d.ts +35 -0
  416. package/dist/lib/runtime/live-sync.d.ts.map +1 -0
  417. package/dist/lib/runtime/live-sync.js +65 -0
  418. package/dist/lib/runtime/live-sync.js.map +1 -0
  419. package/dist/lib/runtime/loader-setup.d.ts +9 -0
  420. package/dist/lib/runtime/loader-setup.d.ts.map +1 -0
  421. package/dist/lib/runtime/loader-setup.js +8 -0
  422. package/dist/lib/runtime/loader-setup.js.map +1 -0
  423. package/dist/lib/runtime/login-menu-accounts.d.ts +29 -0
  424. package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -0
  425. package/dist/lib/runtime/login-menu-accounts.js +33 -0
  426. package/dist/lib/runtime/login-menu-accounts.js.map +1 -0
  427. package/dist/lib/runtime/manual-oauth-flow.d.ts +40 -0
  428. package/dist/lib/runtime/manual-oauth-flow.d.ts.map +1 -0
  429. package/dist/lib/runtime/manual-oauth-flow.js +71 -0
  430. package/dist/lib/runtime/manual-oauth-flow.js.map +1 -0
  431. package/dist/lib/runtime/metrics.d.ts +29 -0
  432. package/dist/lib/runtime/metrics.d.ts.map +1 -0
  433. package/dist/lib/runtime/metrics.js +97 -0
  434. package/dist/lib/runtime/metrics.js.map +1 -0
  435. package/dist/lib/runtime/oauth-browser-flow.d.ts +9 -0
  436. package/dist/lib/runtime/oauth-browser-flow.d.ts.map +1 -0
  437. package/dist/lib/runtime/oauth-browser-flow.js +34 -0
  438. package/dist/lib/runtime/oauth-browser-flow.js.map +1 -0
  439. package/dist/lib/runtime/preemptive-quota.d.ts +13 -0
  440. package/dist/lib/runtime/preemptive-quota.d.ts.map +1 -0
  441. package/dist/lib/runtime/preemptive-quota.js +9 -0
  442. package/dist/lib/runtime/preemptive-quota.js.map +1 -0
  443. package/dist/lib/runtime/quota-headers.d.ts +12 -0
  444. package/dist/lib/runtime/quota-headers.d.ts.map +1 -0
  445. package/dist/lib/runtime/quota-headers.js +131 -0
  446. package/dist/lib/runtime/quota-headers.js.map +1 -0
  447. package/dist/lib/runtime/quota-probe.d.ts +18 -0
  448. package/dist/lib/runtime/quota-probe.d.ts.map +1 -0
  449. package/dist/lib/runtime/quota-probe.js +82 -0
  450. package/dist/lib/runtime/quota-probe.js.map +1 -0
  451. package/dist/lib/runtime/quota-settings.d.ts +14 -0
  452. package/dist/lib/runtime/quota-settings.d.ts.map +1 -0
  453. package/dist/lib/runtime/quota-settings.js +12 -0
  454. package/dist/lib/runtime/quota-settings.js.map +1 -0
  455. package/dist/lib/runtime/refresh-guardian-entry.d.ts +32 -0
  456. package/dist/lib/runtime/refresh-guardian-entry.d.ts.map +1 -0
  457. package/dist/lib/runtime/refresh-guardian-entry.js +12 -0
  458. package/dist/lib/runtime/refresh-guardian-entry.js.map +1 -0
  459. package/dist/lib/runtime/refresh-guardian.d.ts +24 -0
  460. package/dist/lib/runtime/refresh-guardian.d.ts.map +1 -0
  461. package/dist/lib/runtime/refresh-guardian.js +32 -0
  462. package/dist/lib/runtime/refresh-guardian.js.map +1 -0
  463. package/dist/lib/runtime/request-init.d.ts +5 -0
  464. package/dist/lib/runtime/request-init.d.ts.map +1 -0
  465. package/dist/lib/runtime/request-init.js +50 -0
  466. package/dist/lib/runtime/request-init.js.map +1 -0
  467. package/dist/lib/runtime/runtime-services.d.ts +58 -0
  468. package/dist/lib/runtime/runtime-services.d.ts.map +1 -0
  469. package/dist/lib/runtime/runtime-services.js +87 -0
  470. package/dist/lib/runtime/runtime-services.js.map +1 -0
  471. package/dist/lib/runtime/session-affinity-entry.d.ts +30 -0
  472. package/dist/lib/runtime/session-affinity-entry.d.ts.map +1 -0
  473. package/dist/lib/runtime/session-affinity-entry.js +11 -0
  474. package/dist/lib/runtime/session-affinity-entry.js.map +1 -0
  475. package/dist/lib/runtime/session-affinity.d.ts +13 -0
  476. package/dist/lib/runtime/session-affinity.d.ts.map +1 -0
  477. package/dist/lib/runtime/session-affinity.js +17 -0
  478. package/dist/lib/runtime/session-affinity.js.map +1 -0
  479. package/dist/lib/runtime/session-recovery.d.ts +8 -0
  480. package/dist/lib/runtime/session-recovery.d.ts.map +1 -0
  481. package/dist/lib/runtime/session-recovery.js +7 -0
  482. package/dist/lib/runtime/session-recovery.js.map +1 -0
  483. package/dist/lib/runtime/status-marker.d.ts +3 -0
  484. package/dist/lib/runtime/status-marker.d.ts.map +1 -0
  485. package/dist/lib/runtime/status-marker.js +15 -0
  486. package/dist/lib/runtime/status-marker.js.map +1 -0
  487. package/dist/lib/runtime/storage-scope.d.ts +13 -0
  488. package/dist/lib/runtime/storage-scope.d.ts.map +1 -0
  489. package/dist/lib/runtime/storage-scope.js +14 -0
  490. package/dist/lib/runtime/storage-scope.js.map +1 -0
  491. package/dist/lib/runtime/toast.d.ts +16 -0
  492. package/dist/lib/runtime/toast.d.ts.map +1 -0
  493. package/dist/lib/runtime/toast.js +16 -0
  494. package/dist/lib/runtime/toast.js.map +1 -0
  495. package/dist/lib/runtime/ui-runtime-entry.d.ts +7 -0
  496. package/dist/lib/runtime/ui-runtime-entry.d.ts.map +1 -0
  497. package/dist/lib/runtime/ui-runtime-entry.js +4 -0
  498. package/dist/lib/runtime/ui-runtime-entry.js.map +1 -0
  499. package/dist/lib/runtime/ui-runtime.d.ts +9 -0
  500. package/dist/lib/runtime/ui-runtime.d.ts.map +1 -0
  501. package/dist/lib/runtime/ui-runtime.js +23 -0
  502. package/dist/lib/runtime/ui-runtime.js.map +1 -0
  503. package/dist/lib/runtime/verify-flagged.d.ts +44 -0
  504. package/dist/lib/runtime/verify-flagged.d.ts.map +1 -0
  505. package/dist/lib/runtime/verify-flagged.js +84 -0
  506. package/dist/lib/runtime/verify-flagged.js.map +1 -0
  507. package/dist/lib/schemas.d.ts +9 -0
  508. package/dist/lib/schemas.d.ts.map +1 -1
  509. package/dist/lib/schemas.js +9 -1
  510. package/dist/lib/schemas.js.map +1 -1
  511. package/dist/lib/session-affinity.d.ts +10 -0
  512. package/dist/lib/session-affinity.d.ts.map +1 -1
  513. package/dist/lib/session-affinity.js +53 -6
  514. package/dist/lib/session-affinity.js.map +1 -1
  515. package/dist/lib/storage/account-clear-entry.d.ts +16 -0
  516. package/dist/lib/storage/account-clear-entry.d.ts.map +1 -0
  517. package/dist/lib/storage/account-clear-entry.js +12 -0
  518. package/dist/lib/storage/account-clear-entry.js.map +1 -0
  519. package/dist/lib/storage/account-clear.d.ts +8 -0
  520. package/dist/lib/storage/account-clear.d.ts.map +1 -0
  521. package/dist/lib/storage/account-clear.js +29 -0
  522. package/dist/lib/storage/account-clear.js.map +1 -0
  523. package/dist/lib/storage/account-match-utils.d.ts +9 -0
  524. package/dist/lib/storage/account-match-utils.d.ts.map +1 -0
  525. package/dist/lib/storage/account-match-utils.js +45 -0
  526. package/dist/lib/storage/account-match-utils.js.map +1 -0
  527. package/dist/lib/storage/account-persistence.d.ts +3 -0
  528. package/dist/lib/storage/account-persistence.d.ts.map +1 -0
  529. package/dist/lib/storage/account-persistence.js +12 -0
  530. package/dist/lib/storage/account-persistence.js.map +1 -0
  531. package/dist/lib/storage/account-port.d.ts +50 -0
  532. package/dist/lib/storage/account-port.d.ts.map +1 -0
  533. package/dist/lib/storage/account-port.js +43 -0
  534. package/dist/lib/storage/account-port.js.map +1 -0
  535. package/dist/lib/storage/account-save-entry.d.ts +7 -0
  536. package/dist/lib/storage/account-save-entry.d.ts.map +1 -0
  537. package/dist/lib/storage/account-save-entry.js +6 -0
  538. package/dist/lib/storage/account-save-entry.js.map +1 -0
  539. package/dist/lib/storage/account-save.d.ts +30 -0
  540. package/dist/lib/storage/account-save.d.ts.map +1 -0
  541. package/dist/lib/storage/account-save.js +49 -0
  542. package/dist/lib/storage/account-save.js.map +1 -0
  543. package/dist/lib/storage/account-snapshot.d.ts +25 -0
  544. package/dist/lib/storage/account-snapshot.d.ts.map +1 -0
  545. package/dist/lib/storage/account-snapshot.js +78 -0
  546. package/dist/lib/storage/account-snapshot.js.map +1 -0
  547. package/dist/lib/storage/backup-metadata-builder.d.ts +32 -0
  548. package/dist/lib/storage/backup-metadata-builder.d.ts.map +1 -0
  549. package/dist/lib/storage/backup-metadata-builder.js +77 -0
  550. package/dist/lib/storage/backup-metadata-builder.js.map +1 -0
  551. package/dist/lib/storage/backup-metadata.d.ts +29 -0
  552. package/dist/lib/storage/backup-metadata.d.ts.map +1 -0
  553. package/dist/lib/storage/backup-metadata.js +16 -0
  554. package/dist/lib/storage/backup-metadata.js.map +1 -0
  555. package/dist/lib/storage/backup-paths.d.ts +10 -0
  556. package/dist/lib/storage/backup-paths.d.ts.map +1 -0
  557. package/dist/lib/storage/backup-paths.js +27 -0
  558. package/dist/lib/storage/backup-paths.js.map +1 -0
  559. package/dist/lib/storage/backup-restore.d.ts +11 -0
  560. package/dist/lib/storage/backup-restore.d.ts.map +1 -0
  561. package/dist/lib/storage/backup-restore.js +52 -0
  562. package/dist/lib/storage/backup-restore.js.map +1 -0
  563. package/dist/lib/storage/cache-artifacts.d.ts +2 -0
  564. package/dist/lib/storage/cache-artifacts.d.ts.map +1 -0
  565. package/dist/lib/storage/cache-artifacts.js +4 -0
  566. package/dist/lib/storage/cache-artifacts.js.map +1 -0
  567. package/dist/lib/storage/error-hints.d.ts +10 -0
  568. package/dist/lib/storage/error-hints.d.ts.map +1 -0
  569. package/dist/lib/storage/error-hints.js +36 -0
  570. package/dist/lib/storage/error-hints.js.map +1 -0
  571. package/dist/lib/storage/file-paths.d.ts +6 -0
  572. package/dist/lib/storage/file-paths.d.ts.map +1 -0
  573. package/dist/lib/storage/file-paths.js +17 -0
  574. package/dist/lib/storage/file-paths.js.map +1 -0
  575. package/dist/lib/storage/fixture-guards.d.ts +4 -0
  576. package/dist/lib/storage/fixture-guards.d.ts.map +1 -0
  577. package/dist/lib/storage/fixture-guards.js +27 -0
  578. package/dist/lib/storage/fixture-guards.js.map +1 -0
  579. package/dist/lib/storage/flagged-entry.d.ts +15 -0
  580. package/dist/lib/storage/flagged-entry.d.ts.map +1 -0
  581. package/dist/lib/storage/flagged-entry.js +12 -0
  582. package/dist/lib/storage/flagged-entry.js.map +1 -0
  583. package/dist/lib/storage/flagged-load-entry.d.ts +20 -0
  584. package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -0
  585. package/dist/lib/storage/flagged-load-entry.js +13 -0
  586. package/dist/lib/storage/flagged-load-entry.js.map +1 -0
  587. package/dist/lib/storage/flagged-save-entry.d.ts +7 -0
  588. package/dist/lib/storage/flagged-save-entry.d.ts.map +1 -0
  589. package/dist/lib/storage/flagged-save-entry.js +6 -0
  590. package/dist/lib/storage/flagged-save-entry.js.map +1 -0
  591. package/dist/lib/storage/flagged-storage-file.d.ts +7 -0
  592. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -0
  593. package/dist/lib/storage/flagged-storage-file.js +25 -0
  594. package/dist/lib/storage/flagged-storage-file.js.map +1 -0
  595. package/dist/lib/storage/flagged-storage-io.d.ts +28 -0
  596. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -0
  597. package/dist/lib/storage/flagged-storage-io.js +135 -0
  598. package/dist/lib/storage/flagged-storage-io.js.map +1 -0
  599. package/dist/lib/storage/flagged-storage.d.ts +6 -0
  600. package/dist/lib/storage/flagged-storage.d.ts.map +1 -0
  601. package/dist/lib/storage/flagged-storage.js +90 -0
  602. package/dist/lib/storage/flagged-storage.js.map +1 -0
  603. package/dist/lib/storage/gitignore.d.ts +7 -0
  604. package/dist/lib/storage/gitignore.d.ts.map +1 -0
  605. package/dist/lib/storage/gitignore.js +34 -0
  606. package/dist/lib/storage/gitignore.js.map +1 -0
  607. package/dist/lib/storage/hash.d.ts +2 -0
  608. package/dist/lib/storage/hash.d.ts.map +1 -0
  609. package/dist/lib/storage/hash.js +5 -0
  610. package/dist/lib/storage/hash.js.map +1 -0
  611. package/dist/lib/storage/identity.d.ts +15 -0
  612. package/dist/lib/storage/identity.d.ts.map +1 -0
  613. package/dist/lib/storage/identity.js +48 -0
  614. package/dist/lib/storage/identity.js.map +1 -0
  615. package/dist/lib/storage/import-export.d.ts +24 -0
  616. package/dist/lib/storage/import-export.d.ts.map +1 -0
  617. package/dist/lib/storage/import-export.js +108 -0
  618. package/dist/lib/storage/import-export.js.map +1 -0
  619. package/dist/lib/storage/metadata-section.d.ts +16 -0
  620. package/dist/lib/storage/metadata-section.d.ts.map +1 -0
  621. package/dist/lib/storage/metadata-section.js +16 -0
  622. package/dist/lib/storage/metadata-section.js.map +1 -0
  623. package/dist/lib/storage/migrations.d.ts +5 -2
  624. package/dist/lib/storage/migrations.d.ts.map +1 -1
  625. package/dist/lib/storage/migrations.js.map +1 -1
  626. package/dist/lib/storage/named-backup-entry.d.ts +15 -0
  627. package/dist/lib/storage/named-backup-entry.d.ts.map +1 -0
  628. package/dist/lib/storage/named-backup-entry.js +7 -0
  629. package/dist/lib/storage/named-backup-entry.js.map +1 -0
  630. package/dist/lib/storage/named-backups-entry.d.ts +19 -0
  631. package/dist/lib/storage/named-backups-entry.d.ts.map +1 -0
  632. package/dist/lib/storage/named-backups-entry.js +7 -0
  633. package/dist/lib/storage/named-backups-entry.js.map +1 -0
  634. package/dist/lib/storage/named-backups.d.ts +15 -0
  635. package/dist/lib/storage/named-backups.d.ts.map +1 -0
  636. package/dist/lib/storage/named-backups.js +68 -0
  637. package/dist/lib/storage/named-backups.js.map +1 -0
  638. package/dist/lib/storage/path-state.d.ts +9 -0
  639. package/dist/lib/storage/path-state.d.ts.map +1 -0
  640. package/dist/lib/storage/path-state.js +20 -0
  641. package/dist/lib/storage/path-state.js.map +1 -0
  642. package/dist/lib/storage/project-migration.d.ts +10 -0
  643. package/dist/lib/storage/project-migration.d.ts.map +1 -0
  644. package/dist/lib/storage/project-migration.js +38 -0
  645. package/dist/lib/storage/project-migration.js.map +1 -0
  646. package/dist/lib/storage/record-utils.d.ts +3 -0
  647. package/dist/lib/storage/record-utils.d.ts.map +1 -0
  648. package/dist/lib/storage/record-utils.js +9 -0
  649. package/dist/lib/storage/record-utils.js.map +1 -0
  650. package/dist/lib/storage/restore-assessment.d.ts +7 -0
  651. package/dist/lib/storage/restore-assessment.d.ts.map +1 -0
  652. package/dist/lib/storage/restore-assessment.js +42 -0
  653. package/dist/lib/storage/restore-assessment.js.map +1 -0
  654. package/dist/lib/storage/restore-backup-entry.d.ts +24 -0
  655. package/dist/lib/storage/restore-backup-entry.d.ts.map +1 -0
  656. package/dist/lib/storage/restore-backup-entry.js +10 -0
  657. package/dist/lib/storage/restore-backup-entry.js.map +1 -0
  658. package/dist/lib/storage/restore-metadata.d.ts +10 -0
  659. package/dist/lib/storage/restore-metadata.d.ts.map +1 -0
  660. package/dist/lib/storage/restore-metadata.js +18 -0
  661. package/dist/lib/storage/restore-metadata.js.map +1 -0
  662. package/dist/lib/storage/restore.d.ts +14 -0
  663. package/dist/lib/storage/restore.d.ts.map +1 -0
  664. package/dist/lib/storage/restore.js +53 -0
  665. package/dist/lib/storage/restore.js.map +1 -0
  666. package/dist/lib/storage/snapshot-inspectors.d.ts +22 -0
  667. package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -0
  668. package/dist/lib/storage/snapshot-inspectors.js +95 -0
  669. package/dist/lib/storage/snapshot-inspectors.js.map +1 -0
  670. package/dist/lib/storage/storage-parser.d.ts +15 -0
  671. package/dist/lib/storage/storage-parser.d.ts.map +1 -0
  672. package/dist/lib/storage/storage-parser.js +16 -0
  673. package/dist/lib/storage/storage-parser.js.map +1 -0
  674. package/dist/lib/storage/transactions.d.ts +23 -0
  675. package/dist/lib/storage/transactions.d.ts.map +1 -0
  676. package/dist/lib/storage/transactions.js +64 -0
  677. package/dist/lib/storage/transactions.js.map +1 -0
  678. package/dist/lib/storage.d.ts +12 -41
  679. package/dist/lib/storage.d.ts.map +1 -1
  680. package/dist/lib/storage.js +527 -938
  681. package/dist/lib/storage.js.map +1 -1
  682. package/dist/lib/types.d.ts +80 -1
  683. package/dist/lib/types.d.ts.map +1 -1
  684. package/dist/lib/ui/copy.d.ts +24 -4
  685. package/dist/lib/ui/copy.d.ts.map +1 -1
  686. package/dist/lib/ui/copy.js +24 -4
  687. package/dist/lib/ui/copy.js.map +1 -1
  688. package/package.json +44 -6
  689. package/scripts/check-pack-budget-lib.js +114 -0
  690. package/scripts/check-pack-budget.mjs +10 -0
  691. package/scripts/codex-multi-auth.js +34 -10
  692. package/scripts/install-codex-auth-utils.js +54 -14
  693. package/scripts/install-codex-auth.js +62 -33
  694. package/scripts/update-vendor-provenance.mjs +96 -0
  695. package/scripts/verify-vendor-provenance.mjs +53 -0
@@ -1,28 +1,42 @@
1
- import { createInterface } from "node:readline/promises";
2
1
  import { stdin as input, stdout as output } from "node:process";
3
- import { promises as fs, existsSync } from "node:fs";
4
- import { dirname, resolve } from "node:path";
2
+ import { createInterface } from "node:readline/promises";
3
+ import { extractAccountEmail, extractAccountId, formatAccountLabel, formatWaitTime, getAccountIdCandidates, resolveRequestAccountId, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, } from "./accounts.js";
5
4
  import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationInput, REDIRECT_URI, } from "./auth/auth.js";
5
+ import { copyTextToClipboard, isBrowserLaunchSuppressed, openBrowserUrl, } from "./auth/browser.js";
6
6
  import { startLocalOAuthServer } from "./auth/server.js";
7
- import { copyTextToClipboard, openBrowserUrl } from "./auth/browser.js";
8
- import { promptAddAnotherAccount, promptLoginMode } from "./cli.js";
9
- import { extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, getAccountIdCandidates, resolveRequestAccountId, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, } from "./accounts.js";
7
+ import { promptAddAnotherAccount, promptLoginMode, } from "./cli.js";
8
+ import { loadCodexCliState } from "./codex-cli/state.js";
9
+ import { setCodexCliActiveSelection } from "./codex-cli/writer.js";
10
+ import { runBestCommand, } from "./codex-manager/commands/best.js";
11
+ import { runCheckCommand } from "./codex-manager/commands/check.js";
12
+ import { runConfigExplainCommand } from "./codex-manager/commands/config-explain.js";
13
+ import { runDebugBundleCommand } from "./codex-manager/commands/debug-bundle.js";
14
+ import { runDoctor as runRepairDoctor, runFix as runRepairFix, runVerifyFlagged as runRepairVerifyFlagged, } from "./codex-manager/repair-commands.js";
15
+ import { runForecastCommand } from "./codex-manager/commands/forecast.js";
16
+ import { runInitConfigCommand } from "./codex-manager/commands/init-config.js";
17
+ import { runReportCommand } from "./codex-manager/commands/report.js";
18
+ import { runFeaturesCommand, runStatusCommand, } from "./codex-manager/commands/status.js";
19
+ import { runSwitchCommand } from "./codex-manager/commands/switch.js";
20
+ import { parseAuthLoginArgs, printUsage } from "./codex-manager/help.js";
21
+ import { applyUiThemeFromDashboardSettings, configureUnifiedSettings, resolveMenuLayoutMode, } from "./codex-manager/settings-hub.js";
22
+ import { getPluginConfigExplainReport } from "./config.js";
10
23
  import { ACCOUNT_LIMITS } from "./constants.js";
11
- import { loadDashboardDisplaySettings, DEFAULT_DASHBOARD_DISPLAY_SETTINGS, } from "./dashboard-settings.js";
12
- import { evaluateForecastAccounts, isHardRefreshFailure, recommendForecastAccount, summarizeForecast, } from "./forecast.js";
24
+ import { DEFAULT_DASHBOARD_DISPLAY_SETTINGS, loadDashboardDisplaySettings, } from "./dashboard-settings.js";
25
+ import { evaluateForecastAccounts, recommendForecastAccount, summarizeForecast, } from "./forecast.js";
26
+ import { createLogger } from "./logger.js";
13
27
  import { MODEL_FAMILIES } from "./prompts/codex.js";
28
+ import { getModelCapabilities, getModelProfile, resolveNormalizedModel, } from "./request/helpers/model-map.js";
29
+ import { loadQuotaCache, saveQuotaCache, } from "./quota-cache.js";
14
30
  import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./quota-probe.js";
15
31
  import { queuedRefresh } from "./refresh-queue.js";
16
- import { loadQuotaCache, saveQuotaCache, } from "./quota-cache.js";
17
- import { clearAccounts, findMatchingAccountIndex, getStoragePath, loadFlaggedAccounts, loadAccounts, saveFlaggedAccounts, saveAccounts, setStoragePath, withAccountAndFlaggedStorageTransaction, withAccountStorageTransaction, } from "./storage.js";
18
- import { getCodexCliAuthPath, getCodexCliConfigPath, loadCodexCliState, } from "./codex-cli/state.js";
19
- import { setCodexCliActiveSelection } from "./codex-cli/writer.js";
32
+ import { clearAccounts, findMatchingAccountIndex, formatStorageErrorHint, getLastAccountsSaveTimestamp, getNamedBackups, getStoragePath, loadAccounts, loadFlaggedAccounts, restoreAccountsFromBackup, StorageError, saveAccounts, setStoragePath, withAccountStorageTransaction, } from "./storage.js";
20
33
  import { ANSI } from "./ui/ansi.js";
34
+ import { confirm } from "./ui/confirm.js";
21
35
  import { UI_COPY } from "./ui/copy.js";
22
36
  import { paintUiText, quotaToneFromLeftPercent } from "./ui/format.js";
23
37
  import { getUiRuntimeOptions } from "./ui/runtime.js";
24
38
  import { select } from "./ui/select.js";
25
- import { applyUiThemeFromDashboardSettings, configureUnifiedSettings, resolveMenuLayoutMode } from "./codex-manager/settings-hub.js";
39
+ const log = createLogger("codex-manager");
26
40
  function stylePromptText(text, tone) {
27
41
  if (!output.isTTY)
28
42
  return text;
@@ -45,6 +59,28 @@ function stylePromptText(text, tone) {
45
59
  : ANSI.dim;
46
60
  return `${legacyCode}${text}${ANSI.reset}`;
47
61
  }
62
+ function inspectRequestedModel(requestedModel) {
63
+ const normalized = resolveNormalizedModel(requestedModel);
64
+ const profile = getModelProfile(normalized);
65
+ return {
66
+ requested: requestedModel,
67
+ normalized,
68
+ remapped: requestedModel !== normalized,
69
+ promptFamily: profile.promptFamily,
70
+ capabilities: getModelCapabilities(normalized),
71
+ };
72
+ }
73
+ function formatModelInspection(model) {
74
+ const route = model.remapped
75
+ ? `${model.requested} -> ${model.normalized}`
76
+ : model.normalized;
77
+ return [
78
+ route,
79
+ `prompt family ${model.promptFamily}`,
80
+ `tool search ${model.capabilities.toolSearch ? "yes" : "no"}`,
81
+ `computer use ${model.capabilities.computerUse ? "yes" : "no"}`,
82
+ ].join(" | ");
83
+ }
48
84
  function collapseWhitespace(value) {
49
85
  return value.replace(/\s+/g, " ").trim();
50
86
  }
@@ -61,11 +97,10 @@ function extractErrorMessageFromPayload(payload) {
61
97
  const directMessage = typeof record.message === "string"
62
98
  ? collapseWhitespace(record.message)
63
99
  : "";
64
- const directCode = typeof record.code === "string"
65
- ? collapseWhitespace(record.code)
66
- : "";
100
+ const directCode = typeof record.code === "string" ? collapseWhitespace(record.code) : "";
67
101
  if (directMessage) {
68
- if (directCode && !directMessage.toLowerCase().includes(directCode.toLowerCase())) {
102
+ if (directCode &&
103
+ !directMessage.toLowerCase().includes(directCode.toLowerCase())) {
69
104
  return `${directMessage} [${directCode}]`;
70
105
  }
71
106
  return directMessage;
@@ -117,11 +152,36 @@ function formatResultSummary(segments) {
117
152
  const rendered = segments.map((segment) => stylePromptText(segment.text, segment.tone));
118
153
  return `${stylePromptText("Result:", "accent")} ${joinStyledSegments(rendered)}`;
119
154
  }
155
+ function createRepairCommandDeps() {
156
+ return {
157
+ stylePromptText,
158
+ styleAccountDetailText,
159
+ formatResultSummary,
160
+ resolveActiveIndex,
161
+ hasUsableAccessToken,
162
+ hasLikelyInvalidRefreshToken,
163
+ normalizeFailureDetail,
164
+ buildQuotaEmailFallbackState,
165
+ updateQuotaCacheForAccount,
166
+ cloneQuotaCacheData,
167
+ pruneUnsafeQuotaEmailCacheEntry,
168
+ formatCompactQuotaSnapshot,
169
+ resolveStoredAccountIdentity,
170
+ applyTokenAccountIdentity,
171
+ };
172
+ }
173
+ function isOAuthCancellation(result) {
174
+ const message = (result.message ?? result.reason ?? "").trim().toLowerCase();
175
+ return message.includes("cancelled") || message.includes("canceled");
176
+ }
120
177
  function styleQuotaSummary(summary) {
121
178
  const normalized = collapseWhitespace(summary);
122
179
  if (!normalized)
123
180
  return stylePromptText(summary, "muted");
124
- const segments = normalized.split("|").map((segment) => segment.trim()).filter(Boolean);
181
+ const segments = normalized
182
+ .split("|")
183
+ .map((segment) => segment.trim())
184
+ .filter(Boolean);
125
185
  if (segments.length === 0)
126
186
  return stylePromptText(normalized, "muted");
127
187
  const rendered = segments.map((segment) => {
@@ -204,35 +264,6 @@ function isAbortError(error) {
204
264
  const maybe = error;
205
265
  return maybe.name === "AbortError" || maybe.code === "ABORT_ERR";
206
266
  }
207
- function isUserCancelledOAuth(result) {
208
- if (result.type !== "failed")
209
- return false;
210
- const message = (result.message ?? "").toLowerCase();
211
- return message.includes("cancelled");
212
- }
213
- function printUsage() {
214
- console.log([
215
- "Codex Multi-Auth CLI",
216
- "",
217
- "Usage:",
218
- " codex auth login",
219
- " codex auth list",
220
- " codex auth status",
221
- " codex auth switch <index>",
222
- " codex auth best [--live] [--json] [--model <model>]",
223
- " codex auth check",
224
- " codex auth features",
225
- " codex auth verify-flagged [--dry-run] [--json] [--no-restore]",
226
- " codex auth forecast [--live] [--json] [--model <model>]",
227
- " codex auth report [--live] [--json] [--model <model>] [--out <path>]",
228
- " codex auth fix [--dry-run] [--json] [--live] [--model <model>]",
229
- " codex auth doctor [--json] [--fix] [--dry-run]",
230
- "",
231
- "Notes:",
232
- " - Uses ~/.codex/multi-auth/openai-codex-accounts.json",
233
- " - Syncs active account into Codex CLI auth state",
234
- ].join("\n"));
235
- }
236
267
  const IMPLEMENTED_FEATURES = [
237
268
  { id: 1, name: "Multi-account OAuth login dashboard" },
238
269
  { id: 2, name: "Account add/update dedupe by token/id/email" },
@@ -276,14 +307,6 @@ const IMPLEMENTED_FEATURES = [
276
307
  { id: 40, name: "OAuth browser-first flow with manual callback fallback" },
277
308
  { id: 41, name: "Auto-switch to best account command" },
278
309
  ];
279
- function runFeaturesReport() {
280
- console.log(`Implemented features (${IMPLEMENTED_FEATURES.length})`);
281
- console.log("");
282
- for (const feature of IMPLEMENTED_FEATURES) {
283
- console.log(`${feature.id}. ${feature.name}`);
284
- }
285
- return 0;
286
- }
287
310
  function resolveActiveIndex(storage, family = "codex") {
288
311
  const total = storage.accounts.length;
289
312
  if (total === 0)
@@ -614,7 +637,8 @@ const ACCESS_TOKEN_FRESH_WINDOW_MS = 5 * 60 * 1000;
614
637
  function hasUsableAccessToken(account, now) {
615
638
  if (!account.accessToken)
616
639
  return false;
617
- if (typeof account.expiresAt !== "number" || !Number.isFinite(account.expiresAt))
640
+ if (typeof account.expiresAt !== "number" ||
641
+ !Number.isFinite(account.expiresAt))
618
642
  return false;
619
643
  return account.expiresAt - now > ACCESS_TOKEN_FRESH_WINDOW_MS;
620
644
  }
@@ -629,7 +653,8 @@ function hasLikelyInvalidRefreshToken(refreshToken) {
629
653
  function mapAccountStatus(account, index, activeIndex, now) {
630
654
  if (account.enabled === false)
631
655
  return "disabled";
632
- if (typeof account.coolingDownUntil === "number" && account.coolingDownUntil > now) {
656
+ if (typeof account.coolingDownUntil === "number" &&
657
+ account.coolingDownUntil > now) {
633
658
  return "cooldown";
634
659
  }
635
660
  const rateLimit = formatRateLimitEntry(account, now, "codex");
@@ -649,7 +674,9 @@ function parseLeftPercentFromQuotaSummary(summary, windowLabel) {
649
674
  return Math.max(0, Math.min(100, value));
650
675
  }
651
676
  function readQuotaLeftPercent(account, windowLabel) {
652
- const direct = windowLabel === "5h" ? account.quota5hLeftPercent : account.quota7dLeftPercent;
677
+ const direct = windowLabel === "5h"
678
+ ? account.quota5hLeftPercent
679
+ : account.quota7dLeftPercent;
653
680
  if (typeof direct === "number" && Number.isFinite(direct)) {
654
681
  return Math.max(0, Math.min(100, Math.round(direct)));
655
682
  }
@@ -682,7 +709,8 @@ function compareReadyFirstAccounts(left, right) {
682
709
  const right7d = readQuotaLeftPercent(right, "7d");
683
710
  if (left7d !== right7d)
684
711
  return right7d - left7d;
685
- const bucketDelta = accountStatusSortBucket(left.status) - accountStatusSortBucket(right.status);
712
+ const bucketDelta = accountStatusSortBucket(left.status) -
713
+ accountStatusSortBucket(right.status);
686
714
  if (bucketDelta !== 0)
687
715
  return bucketDelta;
688
716
  const leftLastUsed = left.lastUsed ?? 0;
@@ -694,14 +722,19 @@ function compareReadyFirstAccounts(left, right) {
694
722
  return leftSource - rightSource;
695
723
  }
696
724
  function applyAccountMenuOrdering(accounts, displaySettings) {
697
- const sortEnabled = displaySettings.menuSortEnabled ?? (DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ?? true);
698
- const sortMode = displaySettings.menuSortMode ?? (DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ?? "ready-first");
725
+ const sortEnabled = displaySettings.menuSortEnabled ??
726
+ DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ??
727
+ true;
728
+ const sortMode = displaySettings.menuSortMode ??
729
+ DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortMode ??
730
+ "ready-first";
699
731
  if (!sortEnabled || sortMode !== "ready-first") {
700
732
  return [...accounts];
701
733
  }
702
734
  const sorted = [...accounts].sort(compareReadyFirstAccounts);
703
735
  const pinCurrent = displaySettings.menuSortPinCurrent ??
704
- (DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortPinCurrent ?? false);
736
+ DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortPinCurrent ??
737
+ false;
705
738
  if (pinCurrent) {
706
739
  const currentIndex = sorted.findIndex((account) => account.isCurrentAccount);
707
740
  if (currentIndex > 0) {
@@ -752,7 +785,11 @@ function toExistingAccountInfo(storage, quotaCache, displaySettings) {
752
785
  showHintsForUnselectedRows: layoutMode === "expanded-rows",
753
786
  highlightCurrentRow: displaySettings.menuHighlightCurrentRow ?? true,
754
787
  focusStyle: displaySettings.menuFocusStyle ?? "row-invert",
755
- statuslineFields: displaySettings.menuStatuslineFields ?? ["last-used", "limits", "status"],
788
+ statuslineFields: displaySettings.menuStatuslineFields ?? [
789
+ "last-used",
790
+ "limits",
791
+ "status",
792
+ ],
756
793
  };
757
794
  });
758
795
  const orderedAccounts = applyAccountMenuOrdering(baseAccounts, displaySettings);
@@ -821,23 +858,72 @@ function applyTokenAccountIdentity(account, tokenAccountId) {
821
858
  if (!nextIdentity.accountId) {
822
859
  return false;
823
860
  }
824
- if (nextIdentity.accountId === account.accountId
825
- && nextIdentity.accountIdSource === account.accountIdSource) {
861
+ if (nextIdentity.accountId === account.accountId &&
862
+ nextIdentity.accountIdSource === account.accountIdSource) {
826
863
  return false;
827
864
  }
828
865
  account.accountId = nextIdentity.accountId;
829
866
  account.accountIdSource = nextIdentity.accountIdSource;
830
867
  return true;
831
868
  }
832
- async function promptManualCallback(state) {
833
- if (!input.isTTY || !output.isTTY) {
869
+ async function promptManualCallback(state, options = {}) {
870
+ const useInteractivePrompt = input.isTTY && output.isTTY;
871
+ if (!useInteractivePrompt && !options.allowNonTty) {
834
872
  return null;
835
873
  }
836
874
  const rl = createInterface({ input, output });
837
875
  try {
838
- console.log("");
839
- console.log(stylePromptText(UI_COPY.oauth.pastePrompt, "accent"));
840
- const answer = await rl.question("◆ ");
876
+ if (useInteractivePrompt) {
877
+ console.log("");
878
+ console.log(stylePromptText(UI_COPY.oauth.pastePrompt, "accent"));
879
+ }
880
+ const answer = useInteractivePrompt
881
+ ? await rl.question("◆ ")
882
+ : await new Promise((resolve, reject) => {
883
+ if (input.readableEnded || input.destroyed) {
884
+ resolve(null);
885
+ return;
886
+ }
887
+ let settled = false;
888
+ const handleInputClosed = () => {
889
+ if (settled)
890
+ return;
891
+ settled = true;
892
+ input.off("end", handleInputClosed);
893
+ input.off("close", handleInputClosed);
894
+ resolve(null);
895
+ };
896
+ const finish = (value) => {
897
+ if (settled)
898
+ return;
899
+ settled = true;
900
+ input.off("end", handleInputClosed);
901
+ input.off("close", handleInputClosed);
902
+ resolve(value);
903
+ };
904
+ const fail = (error) => {
905
+ if (settled)
906
+ return;
907
+ settled = true;
908
+ input.off("end", handleInputClosed);
909
+ input.off("close", handleInputClosed);
910
+ reject(error);
911
+ };
912
+ rl.question("")
913
+ .then((value) => finish(value))
914
+ .catch((error) => {
915
+ if (isAbortError(error) || isReadlineClosedError(error)) {
916
+ handleInputClosed();
917
+ return;
918
+ }
919
+ fail(error);
920
+ });
921
+ input.once("end", handleInputClosed);
922
+ input.once("close", handleInputClosed);
923
+ });
924
+ if (answer === null) {
925
+ return null;
926
+ }
841
927
  if (answer.includes("\u001b")) {
842
928
  return null;
843
929
  }
@@ -857,7 +943,7 @@ async function promptManualCallback(state) {
857
943
  return parsed.code;
858
944
  }
859
945
  catch (error) {
860
- if (isAbortError(error)) {
946
+ if (isAbortError(error) || isReadlineClosedError(error)) {
861
947
  return null;
862
948
  }
863
949
  throw error;
@@ -866,20 +952,65 @@ async function promptManualCallback(state) {
866
952
  rl.close();
867
953
  }
868
954
  }
869
- async function promptOAuthSignInMode() {
955
+ function isReadlineClosedError(error) {
956
+ if (!(error instanceof Error)) {
957
+ return false;
958
+ }
959
+ const errorCode = typeof error === "object" && error !== null && "code" in error
960
+ ? String(error.code)
961
+ : "";
962
+ return (errorCode === "ERR_USE_AFTER_CLOSE" ||
963
+ /readline was closed/i.test(error.message));
964
+ }
965
+ export function formatBackupSavedAt(mtimeMs) {
966
+ return new Date(mtimeMs).toLocaleString(undefined, {
967
+ month: "short",
968
+ day: "numeric",
969
+ year: "numeric",
970
+ hour: "numeric",
971
+ minute: "2-digit",
972
+ });
973
+ }
974
+ async function promptOAuthSignInMode(backupOption, backupDiscoveryWarning = null) {
870
975
  if (!input.isTTY || !output.isTTY) {
871
976
  return "browser";
872
977
  }
873
978
  const ui = getUiRuntimeOptions();
874
979
  const items = [
980
+ {
981
+ label: UI_COPY.oauth.signInHeading,
982
+ value: "cancel",
983
+ kind: "heading",
984
+ },
875
985
  { label: UI_COPY.oauth.openBrowser, value: "browser", color: "green" },
876
986
  { label: UI_COPY.oauth.manualMode, value: "manual", color: "yellow" },
987
+ ...(backupOption
988
+ ? [
989
+ { separator: true, label: "", value: "cancel" },
990
+ {
991
+ label: UI_COPY.oauth.restoreHeading,
992
+ value: "cancel",
993
+ kind: "heading",
994
+ },
995
+ {
996
+ label: UI_COPY.oauth.restoreSavedBackup,
997
+ value: "restore-backup",
998
+ hint: UI_COPY.oauth.loadLastBackupHint(backupOption.fileName, backupOption.accountCount, formatBackupSavedAt(backupOption.mtimeMs)),
999
+ color: "cyan",
1000
+ },
1001
+ ]
1002
+ : []),
1003
+ { separator: true, label: "", value: "cancel" },
877
1004
  { label: UI_COPY.oauth.back, value: "cancel", color: "red" },
878
1005
  ];
879
1006
  const selected = await select(items, {
880
1007
  message: UI_COPY.oauth.chooseModeTitle,
881
- subtitle: UI_COPY.oauth.chooseModeSubtitle,
882
- help: UI_COPY.oauth.chooseModeHelp,
1008
+ subtitle: backupDiscoveryWarning
1009
+ ? `${UI_COPY.oauth.chooseModeSubtitle} ${backupDiscoveryWarning}`
1010
+ : UI_COPY.oauth.chooseModeSubtitle,
1011
+ help: backupOption
1012
+ ? UI_COPY.oauth.chooseModeHelpWithBackup
1013
+ : UI_COPY.oauth.chooseModeHelp,
883
1014
  clearScreen: true,
884
1015
  theme: ui.theme,
885
1016
  selectedEmphasis: "minimal",
@@ -892,11 +1023,81 @@ async function promptOAuthSignInMode() {
892
1023
  return "browser";
893
1024
  if (lower === "2")
894
1025
  return "manual";
1026
+ if (lower === "3" && backupOption)
1027
+ return "restore-backup";
895
1028
  return undefined;
896
1029
  },
897
1030
  });
898
1031
  return selected ?? "cancel";
899
1032
  }
1033
+ async function promptBackupRestoreMode(latestBackup) {
1034
+ if (!input.isTTY || !output.isTTY) {
1035
+ return "latest";
1036
+ }
1037
+ const ui = getUiRuntimeOptions();
1038
+ const items = [
1039
+ {
1040
+ label: UI_COPY.oauth.loadLastBackup,
1041
+ value: "latest",
1042
+ hint: `${UI_COPY.oauth.restoreBackupLatestHint}\n${UI_COPY.oauth.manualBackupHint(latestBackup.accountCount, formatBackupSavedAt(latestBackup.mtimeMs))}`,
1043
+ color: "cyan",
1044
+ },
1045
+ {
1046
+ label: UI_COPY.oauth.chooseBackupManually,
1047
+ value: "manual",
1048
+ color: "yellow",
1049
+ },
1050
+ { label: UI_COPY.oauth.back, value: "back", color: "red" },
1051
+ ];
1052
+ const selected = await select(items, {
1053
+ message: UI_COPY.oauth.restoreBackupTitle,
1054
+ subtitle: UI_COPY.oauth.restoreBackupSubtitle,
1055
+ help: UI_COPY.oauth.restoreBackupHelp,
1056
+ clearScreen: true,
1057
+ theme: ui.theme,
1058
+ selectedEmphasis: "minimal",
1059
+ allowEscape: false,
1060
+ onInput: (raw) => {
1061
+ const lower = raw.toLowerCase();
1062
+ if (lower === "q")
1063
+ return "back";
1064
+ if (lower === "1")
1065
+ return "latest";
1066
+ if (lower === "2")
1067
+ return "manual";
1068
+ return undefined;
1069
+ },
1070
+ });
1071
+ return selected ?? "back";
1072
+ }
1073
+ async function promptManualBackupSelection(backups) {
1074
+ if (!input.isTTY || !output.isTTY) {
1075
+ return backups[0] ?? null;
1076
+ }
1077
+ const ui = getUiRuntimeOptions();
1078
+ const items = backups.map((backup) => ({
1079
+ label: backup.fileName,
1080
+ value: backup,
1081
+ hint: UI_COPY.oauth.manualBackupHint(backup.accountCount, formatBackupSavedAt(backup.mtimeMs)),
1082
+ color: "cyan",
1083
+ }));
1084
+ items.push({ label: UI_COPY.oauth.back, value: null, color: "red" });
1085
+ const selected = await select(items, {
1086
+ message: UI_COPY.oauth.manualBackupTitle,
1087
+ subtitle: UI_COPY.oauth.manualBackupSubtitle,
1088
+ help: UI_COPY.oauth.manualBackupHelp,
1089
+ clearScreen: true,
1090
+ theme: ui.theme,
1091
+ selectedEmphasis: "minimal",
1092
+ allowEscape: false,
1093
+ onInput: (raw) => {
1094
+ if (raw.toLowerCase() === "q")
1095
+ return null;
1096
+ return undefined;
1097
+ },
1098
+ });
1099
+ return selected;
1100
+ }
900
1101
  async function waitForMenuReturn(options = {}) {
901
1102
  if (!input.isTTY || !output.isTTY) {
902
1103
  return;
@@ -1142,18 +1343,29 @@ async function runActionPanel(title, stage, action, settings) {
1142
1343
  throw failed;
1143
1344
  }
1144
1345
  }
1145
- async function runOAuthFlow(forceNewLogin) {
1346
+ async function runOAuthFlow(forceNewLogin, signInMode) {
1146
1347
  const { pkce, state, url } = await createAuthorizationFlow({ forceNewLogin });
1147
- const oauthServer = await startLocalOAuthServer({ state });
1148
1348
  let code = null;
1349
+ let oauthServer = null;
1149
1350
  try {
1150
- const signInMode = await promptOAuthSignInMode();
1151
- if (signInMode === "cancel") {
1152
- return {
1153
- type: "failed",
1154
- reason: "unknown",
1155
- message: UI_COPY.oauth.cancelled,
1156
- };
1351
+ if (signInMode === "browser") {
1352
+ try {
1353
+ oauthServer = await startLocalOAuthServer({ state });
1354
+ }
1355
+ catch (serverError) {
1356
+ log.warn("Local OAuth callback server unavailable; falling back to manual callback entry.", serverError instanceof Error
1357
+ ? {
1358
+ message: serverError.message,
1359
+ stack: serverError.stack,
1360
+ code: typeof serverError === "object" &&
1361
+ serverError !== null &&
1362
+ "code" in serverError
1363
+ ? String(serverError.code)
1364
+ : undefined,
1365
+ }
1366
+ : { error: String(serverError) });
1367
+ oauthServer = null;
1368
+ }
1157
1369
  }
1158
1370
  if (signInMode === "browser") {
1159
1371
  const opened = openBrowserUrl(url);
@@ -1172,18 +1384,25 @@ async function runOAuthFlow(forceNewLogin) {
1172
1384
  const copied = copyTextToClipboard(url);
1173
1385
  console.log(stylePromptText(copied ? UI_COPY.oauth.copyOk : UI_COPY.oauth.copyFail, copied ? "success" : "warning"));
1174
1386
  }
1175
- if (oauthServer.ready) {
1387
+ const waitingForCallback = signInMode === "browser" && oauthServer?.ready === true;
1388
+ if (waitingForCallback && oauthServer) {
1176
1389
  console.log(stylePromptText(UI_COPY.oauth.waitingCallback, "muted"));
1177
1390
  const callbackResult = await oauthServer.waitForCode(state);
1178
1391
  code = callbackResult?.code ?? null;
1179
1392
  }
1180
1393
  if (!code) {
1181
- console.log(stylePromptText(UI_COPY.oauth.callbackMissed, "warning"));
1182
- code = await promptManualCallback(state);
1394
+ console.log(stylePromptText(waitingForCallback
1395
+ ? UI_COPY.oauth.callbackMissed
1396
+ : signInMode === "manual"
1397
+ ? UI_COPY.oauth.callbackBypassed
1398
+ : UI_COPY.oauth.callbackUnavailable, "warning"));
1399
+ code = await promptManualCallback(state, {
1400
+ allowNonTty: signInMode === "manual",
1401
+ });
1183
1402
  }
1184
1403
  }
1185
1404
  finally {
1186
- oauthServer.close();
1405
+ oauthServer?.close();
1187
1406
  }
1188
1407
  if (!code) {
1189
1408
  return {
@@ -1206,7 +1425,8 @@ async function persistAccountPool(results, replaceAll) {
1206
1425
  const tokenAccountId = extractAccountId(result.access);
1207
1426
  const accountId = resolveRequestAccountId(result.accountIdOverride, result.accountIdSource, tokenAccountId);
1208
1427
  const accountIdSource = accountId
1209
- ? (result.accountIdSource ?? (result.accountIdOverride ? "manual" : "token"))
1428
+ ? (result.accountIdSource ??
1429
+ (result.accountIdOverride ? "manual" : "token"))
1210
1430
  : undefined;
1211
1431
  const accountLabel = result.accountLabel;
1212
1432
  const accountEmail = sanitizeEmail(extractAccountEmail(result.access, result.idToken));
@@ -1289,47 +1509,11 @@ async function syncSelectionToCodex(tokens) {
1289
1509
  idToken: tokens.idToken,
1290
1510
  });
1291
1511
  }
1292
- async function showAccountStatus() {
1293
- setStoragePath(null);
1294
- const storage = await loadAccounts();
1295
- const path = getStoragePath();
1296
- if (!storage || storage.accounts.length === 0) {
1297
- console.log("No accounts configured.");
1298
- console.log(`Storage: ${path}`);
1299
- return;
1300
- }
1301
- const now = Date.now();
1302
- const activeIndex = resolveActiveIndex(storage, "codex");
1303
- console.log(`Accounts (${storage.accounts.length})`);
1304
- console.log(`Storage: ${path}`);
1305
- console.log("");
1306
- for (let i = 0; i < storage.accounts.length; i += 1) {
1307
- const account = storage.accounts[i];
1308
- if (!account)
1309
- continue;
1310
- const label = formatAccountLabel(account, i);
1311
- const markers = [];
1312
- if (i === activeIndex)
1313
- markers.push("current");
1314
- if (account.enabled === false)
1315
- markers.push("disabled");
1316
- const rateLimit = formatRateLimitEntry(account, now, "codex");
1317
- if (rateLimit)
1318
- markers.push("rate-limited");
1319
- const cooldown = formatCooldown(account, now);
1320
- if (cooldown)
1321
- markers.push(`cooldown:${cooldown}`);
1322
- const markerLabel = markers.length > 0 ? ` [${markers.join(", ")}]` : "";
1323
- const lastUsed = typeof account.lastUsed === "number" && account.lastUsed > 0
1324
- ? `used ${formatWaitTime(now - account.lastUsed)} ago`
1325
- : "never used";
1326
- console.log(`${i + 1}. ${label}${markerLabel} ${lastUsed}`);
1327
- }
1328
- }
1329
1512
  async function runHealthCheck(options = {}) {
1330
1513
  const forceRefresh = options.forceRefresh === true;
1331
1514
  const liveProbe = options.liveProbe === true;
1332
1515
  const probeModel = options.model?.trim() || "gpt-5-codex";
1516
+ const modelInspection = inspectRequestedModel(probeModel);
1333
1517
  const display = options.display ?? DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
1334
1518
  const quotaCache = liveProbe ? await loadQuotaCache() : null;
1335
1519
  const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
@@ -1353,6 +1537,9 @@ async function runHealthCheck(options = {}) {
1353
1537
  console.log(stylePromptText(forceRefresh
1354
1538
  ? `Checking ${storage.accounts.length} account(s) with full refresh test...`
1355
1539
  : `Checking ${storage.accounts.length} account(s) with quick check${liveProbe ? " + live check" : ""}...`, "accent"));
1540
+ if (liveProbe) {
1541
+ console.log(stylePromptText(`Model probe: ${formatModelInspection(modelInspection)}`, "muted"));
1542
+ }
1356
1543
  for (let i = 0; i < storage.accounts.length; i += 1) {
1357
1544
  const account = storage.accounts[i];
1358
1545
  if (!account)
@@ -1376,14 +1563,15 @@ async function runHealthCheck(options = {}) {
1376
1563
  : undefined;
1377
1564
  if (!probeAccountId || !currentAccessToken) {
1378
1565
  warnings += 1;
1379
- healthDetail = "signed in and working (live check skipped: missing account ID)";
1566
+ healthDetail =
1567
+ "signed in and working (live check skipped: missing account ID)";
1380
1568
  }
1381
1569
  else {
1382
1570
  try {
1383
1571
  const snapshot = await fetchCodexQuotaSnapshot({
1384
1572
  accountId: probeAccountId,
1385
1573
  accessToken: currentAccessToken,
1386
- model: probeModel,
1574
+ model: modelInspection.normalized,
1387
1575
  });
1388
1576
  if (workingQuotaCache) {
1389
1577
  quotaCacheChanged =
@@ -1453,14 +1641,15 @@ async function runHealthCheck(options = {}) {
1453
1641
  const probeAccountId = account.accountId ?? tokenAccountId;
1454
1642
  if (!probeAccountId) {
1455
1643
  warnings += 1;
1456
- healthyMessage = "working now (live check skipped: missing account ID)";
1644
+ healthyMessage =
1645
+ "working now (live check skipped: missing account ID)";
1457
1646
  }
1458
1647
  else {
1459
1648
  try {
1460
1649
  const snapshot = await fetchCodexQuotaSnapshot({
1461
1650
  accountId: probeAccountId,
1462
1651
  accessToken: result.access,
1463
- model: probeModel,
1652
+ model: modelInspection.normalized,
1464
1653
  });
1465
1654
  if (workingQuotaCache) {
1466
1655
  quotaCacheChanged =
@@ -1504,7 +1693,9 @@ async function runHealthCheck(options = {}) {
1504
1693
  if (changed) {
1505
1694
  await saveAccounts(storage);
1506
1695
  }
1507
- if (activeAccountRefreshed && activeIndex >= 0 && activeIndex < storage.accounts.length) {
1696
+ if (activeAccountRefreshed &&
1697
+ activeIndex >= 0 &&
1698
+ activeIndex < storage.accounts.length) {
1508
1699
  const activeAccount = storage.accounts[activeIndex];
1509
1700
  if (activeAccount) {
1510
1701
  await setCodexCliActiveSelection({
@@ -1519,21 +1710,16 @@ async function runHealthCheck(options = {}) {
1519
1710
  console.log("");
1520
1711
  console.log(formatResultSummary([
1521
1712
  { text: `${ok} working`, tone: "success" },
1522
- { text: `${failed} need re-login`, tone: failed > 0 ? "danger" : "muted" },
1523
- { text: `${warnings} warning${warnings === 1 ? "" : "s"}`, tone: warnings > 0 ? "warning" : "muted" },
1713
+ {
1714
+ text: `${failed} need re-login`,
1715
+ tone: failed > 0 ? "danger" : "muted",
1716
+ },
1717
+ {
1718
+ text: `${warnings} warning${warnings === 1 ? "" : "s"}`,
1719
+ tone: warnings > 0 ? "warning" : "muted",
1720
+ },
1524
1721
  ]));
1525
1722
  }
1526
- function printForecastUsage() {
1527
- console.log([
1528
- "Usage:",
1529
- " codex auth forecast [--live] [--json] [--model <model>]",
1530
- "",
1531
- "Options:",
1532
- " --live, -l Probe live quota headers via Codex backend",
1533
- " --json, -j Print machine-readable JSON output",
1534
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1535
- ].join("\n"));
1536
- }
1537
1723
  function printBestUsage() {
1538
1724
  console.log([
1539
1725
  "Usage:",
@@ -1549,79 +1735,6 @@ function printBestUsage() {
1549
1735
  " - Switches to the recommended account when it is not already active",
1550
1736
  ].join("\n"));
1551
1737
  }
1552
- function printFixUsage() {
1553
- console.log([
1554
- "Usage:",
1555
- " codex auth fix [--dry-run] [--json] [--live] [--model <model>]",
1556
- "",
1557
- "Options:",
1558
- " --dry-run, -n Preview changes without writing storage",
1559
- " --json, -j Print machine-readable JSON output",
1560
- " --live, -l Run live session probe before deciding health",
1561
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1562
- "",
1563
- "Behavior:",
1564
- " - Refreshes tokens for enabled accounts",
1565
- " - Disables hard-failed accounts (never deletes)",
1566
- " - Recommends a better current account when needed",
1567
- ].join("\n"));
1568
- }
1569
- function printVerifyFlaggedUsage() {
1570
- console.log([
1571
- "Usage:",
1572
- " codex auth verify-flagged [--dry-run] [--json] [--no-restore]",
1573
- "",
1574
- "Options:",
1575
- " --dry-run, -n Preview changes without writing storage",
1576
- " --json, -j Print machine-readable JSON output",
1577
- " --no-restore Check flagged accounts without restoring healthy ones",
1578
- "",
1579
- "Behavior:",
1580
- " - Refresh-checks accounts from flagged storage",
1581
- " - Restores healthy accounts back to active storage by default",
1582
- ].join("\n"));
1583
- }
1584
- function parseForecastArgs(args) {
1585
- const options = {
1586
- live: false,
1587
- json: false,
1588
- model: "gpt-5-codex",
1589
- };
1590
- for (let i = 0; i < args.length; i += 1) {
1591
- const arg = args[i];
1592
- if (!arg)
1593
- continue;
1594
- if (!arg)
1595
- continue;
1596
- if (arg === "--live" || arg === "-l") {
1597
- options.live = true;
1598
- continue;
1599
- }
1600
- if (arg === "--json" || arg === "-j") {
1601
- options.json = true;
1602
- continue;
1603
- }
1604
- if (arg === "--model" || arg === "-m") {
1605
- const value = args[i + 1];
1606
- if (!value) {
1607
- return { ok: false, message: "Missing value for --model" };
1608
- }
1609
- options.model = value;
1610
- i += 1;
1611
- continue;
1612
- }
1613
- if (arg.startsWith("--model=")) {
1614
- const value = arg.slice("--model=".length).trim();
1615
- if (!value) {
1616
- return { ok: false, message: "Missing value for --model" };
1617
- }
1618
- options.model = value;
1619
- continue;
1620
- }
1621
- return { ok: false, message: `Unknown option: ${arg}` };
1622
- }
1623
- return { ok: true, options };
1624
- }
1625
1738
  function parseBestArgs(args) {
1626
1739
  const options = {
1627
1740
  live: false,
@@ -1664,1703 +1777,173 @@ function parseBestArgs(args) {
1664
1777
  }
1665
1778
  return { ok: true, options };
1666
1779
  }
1667
- function parseFixArgs(args) {
1668
- const options = {
1669
- dryRun: false,
1670
- json: false,
1671
- live: false,
1672
- model: "gpt-5-codex",
1673
- };
1674
- for (let i = 0; i < args.length; i += 1) {
1675
- const argValue = args[i];
1676
- if (typeof argValue !== "string")
1677
- continue;
1678
- if (argValue === "--dry-run" || argValue === "-n") {
1679
- options.dryRun = true;
1680
- continue;
1681
- }
1682
- if (argValue === "--json" || argValue === "-j") {
1683
- options.json = true;
1684
- continue;
1685
- }
1686
- if (argValue === "--live" || argValue === "-l") {
1687
- options.live = true;
1688
- continue;
1689
- }
1690
- if (argValue === "--model" || argValue === "-m") {
1691
- const value = args[i + 1];
1692
- if (!value) {
1693
- return { ok: false, message: "Missing value for --model" };
1694
- }
1695
- options.model = value;
1696
- i += 1;
1697
- continue;
1698
- }
1699
- if (argValue.startsWith("--model=")) {
1700
- const value = argValue.slice("--model=".length).trim();
1701
- if (!value) {
1702
- return { ok: false, message: "Missing value for --model" };
1703
- }
1704
- options.model = value;
1705
- continue;
1706
- }
1707
- return { ok: false, message: `Unknown option: ${argValue}` };
1780
+ async function runForecast(args) {
1781
+ return runForecastCommand(args, {
1782
+ setStoragePath,
1783
+ loadAccounts,
1784
+ loadDashboardDisplaySettings,
1785
+ resolveActiveIndex,
1786
+ loadQuotaCache,
1787
+ saveQuotaCache,
1788
+ cloneQuotaCacheData,
1789
+ buildQuotaEmailFallbackState,
1790
+ updateQuotaCacheForAccount,
1791
+ hasUsableAccessToken,
1792
+ queuedRefresh,
1793
+ fetchCodexQuotaSnapshot,
1794
+ normalizeFailureDetail,
1795
+ formatAccountLabel,
1796
+ extractAccountId,
1797
+ evaluateForecastAccounts,
1798
+ summarizeForecast,
1799
+ recommendForecastAccount,
1800
+ stylePromptText,
1801
+ formatResultSummary,
1802
+ styleQuotaSummary,
1803
+ formatCompactQuotaSnapshot,
1804
+ availabilityTone,
1805
+ riskTone,
1806
+ formatWaitTime,
1807
+ defaultDisplay: DEFAULT_DASHBOARD_DISPLAY_SETTINGS,
1808
+ formatQuotaSnapshotLine,
1809
+ });
1810
+ }
1811
+ async function clearAccountsAndReset() {
1812
+ await clearAccounts();
1813
+ }
1814
+ function adjustManageActionSelectionIndex(currentIndex, removedIndex, remainingCount) {
1815
+ if (remainingCount <= 0) {
1816
+ return 0;
1708
1817
  }
1709
- return { ok: true, options };
1818
+ if (typeof currentIndex !== "number" || currentIndex < 0) {
1819
+ return 0;
1820
+ }
1821
+ if (currentIndex < removedIndex) {
1822
+ return Math.min(currentIndex, remainingCount - 1);
1823
+ }
1824
+ if (currentIndex > removedIndex) {
1825
+ return currentIndex - 1;
1826
+ }
1827
+ return Math.min(removedIndex, remainingCount - 1);
1710
1828
  }
1711
- function parseVerifyFlaggedArgs(args) {
1712
- const options = {
1713
- dryRun: false,
1714
- json: false,
1715
- restore: true,
1716
- };
1717
- for (const arg of args) {
1718
- if (arg === "--dry-run" || arg === "-n") {
1719
- options.dryRun = true;
1720
- continue;
1721
- }
1722
- if (arg === "--json" || arg === "-j") {
1723
- options.json = true;
1724
- continue;
1725
- }
1726
- if (arg === "--no-restore") {
1727
- options.restore = false;
1728
- continue;
1829
+ function resetManageActionSelection(storage, removedIndex) {
1830
+ const remainingCount = storage.accounts.length;
1831
+ if (remainingCount <= 0) {
1832
+ storage.activeIndex = 0;
1833
+ storage.activeIndexByFamily = {};
1834
+ for (const family of MODEL_FAMILIES) {
1835
+ storage.activeIndexByFamily[family] = 0;
1729
1836
  }
1730
- return { ok: false, message: `Unknown option: ${arg}` };
1837
+ return;
1838
+ }
1839
+ const previousActiveIndex = storage.activeIndex;
1840
+ const previousByFamily = { ...storage.activeIndexByFamily };
1841
+ storage.activeIndex = adjustManageActionSelectionIndex(previousActiveIndex, removedIndex, remainingCount);
1842
+ storage.activeIndexByFamily = {};
1843
+ for (const family of MODEL_FAMILIES) {
1844
+ storage.activeIndexByFamily[family] = adjustManageActionSelectionIndex(previousByFamily[family] ?? previousActiveIndex, removedIndex, remainingCount);
1731
1845
  }
1732
- return { ok: true, options };
1733
1846
  }
1734
- function printDoctorUsage() {
1735
- console.log([
1736
- "Usage:",
1737
- " codex auth doctor [--json] [--fix] [--dry-run]",
1738
- "",
1739
- "Options:",
1740
- " --json, -j Print machine-readable JSON diagnostics",
1741
- " --fix Apply safe auto-fixes to storage",
1742
- " --dry-run, -n Preview --fix changes without writing storage",
1743
- "",
1744
- "Behavior:",
1745
- " - Validates account storage readability",
1746
- " - Checks active index consistency and account duplication",
1747
- " - Flags placeholder/demo accounts and disabled-all scenarios",
1748
- ].join("\n"));
1847
+ function replaceManageActionStorage(target, source) {
1848
+ target.version = source.version;
1849
+ target.accounts = structuredClone(source.accounts);
1850
+ target.activeIndex = source.activeIndex;
1851
+ target.activeIndexByFamily = {
1852
+ ...source.activeIndexByFamily,
1853
+ };
1749
1854
  }
1750
- function parseDoctorArgs(args) {
1751
- const options = { json: false, fix: false, dryRun: false };
1752
- for (const arg of args) {
1753
- if (arg === "--json" || arg === "-j") {
1754
- options.json = true;
1755
- continue;
1756
- }
1757
- if (arg === "--fix") {
1758
- options.fix = true;
1759
- continue;
1760
- }
1761
- if (arg === "--dry-run" || arg === "-n") {
1762
- options.dryRun = true;
1763
- continue;
1855
+ function resolveManageActionAccountIndex(storage, fallbackIndex, account) {
1856
+ if (account) {
1857
+ const matchedIndex = findMatchingAccountIndex(storage.accounts, {
1858
+ accountId: account.accountId,
1859
+ email: account.email,
1860
+ refreshToken: account.refreshToken,
1861
+ }, {
1862
+ allowUniqueAccountIdFallbackWithoutEmail: true,
1863
+ });
1864
+ if (typeof matchedIndex === "number" && matchedIndex >= 0) {
1865
+ return matchedIndex;
1764
1866
  }
1765
- return { ok: false, message: `Unknown option: ${arg}` };
1867
+ return null;
1766
1868
  }
1767
- if (options.dryRun && !options.fix) {
1768
- return { ok: false, message: "--dry-run requires --fix" };
1769
- }
1770
- return { ok: true, options };
1771
- }
1772
- function printReportUsage() {
1773
- console.log([
1774
- "Usage:",
1775
- " codex auth report [--live] [--json] [--model <model>] [--out <path>]",
1776
- "",
1777
- "Options:",
1778
- " --live, -l Probe live quota headers via Codex backend",
1779
- " --json, -j Print machine-readable JSON output",
1780
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1781
- " --out Write JSON report to a file path",
1782
- ].join("\n"));
1783
- }
1784
- function parseReportArgs(args) {
1785
- const options = {
1786
- live: false,
1787
- json: false,
1788
- model: "gpt-5-codex",
1789
- };
1790
- for (let i = 0; i < args.length; i += 1) {
1791
- const arg = args[i];
1792
- if (!arg)
1793
- continue;
1794
- if (arg === "--live" || arg === "-l") {
1795
- options.live = true;
1796
- continue;
1797
- }
1798
- if (arg === "--json" || arg === "-j") {
1799
- options.json = true;
1800
- continue;
1801
- }
1802
- if (arg === "--model" || arg === "-m") {
1803
- const value = args[i + 1];
1804
- if (!value) {
1805
- return { ok: false, message: "Missing value for --model" };
1806
- }
1807
- options.model = value;
1808
- i += 1;
1809
- continue;
1810
- }
1811
- if (arg.startsWith("--model=")) {
1812
- const value = arg.slice("--model=".length).trim();
1813
- if (!value) {
1814
- return { ok: false, message: "Missing value for --model" };
1815
- }
1816
- options.model = value;
1817
- continue;
1818
- }
1819
- if (arg === "--out") {
1820
- const value = args[i + 1];
1821
- if (!value) {
1822
- return { ok: false, message: "Missing value for --out" };
1823
- }
1824
- options.outPath = value;
1825
- i += 1;
1826
- continue;
1827
- }
1828
- if (arg.startsWith("--out=")) {
1829
- const value = arg.slice("--out=".length).trim();
1830
- if (!value) {
1831
- return { ok: false, message: "Missing value for --out" };
1832
- }
1833
- options.outPath = value;
1834
- continue;
1835
- }
1836
- return { ok: false, message: `Unknown option: ${arg}` };
1837
- }
1838
- return { ok: true, options };
1839
- }
1840
- function serializeForecastResults(results, liveQuotaByIndex, refreshFailures) {
1841
- return results.map((result) => {
1842
- const liveQuota = liveQuotaByIndex.get(result.index);
1843
- return {
1844
- index: result.index,
1845
- label: result.label,
1846
- isCurrent: result.isCurrent,
1847
- availability: result.availability,
1848
- riskScore: result.riskScore,
1849
- riskLevel: result.riskLevel,
1850
- waitMs: result.waitMs,
1851
- reasons: result.reasons,
1852
- liveQuota: liveQuota
1853
- ? {
1854
- status: liveQuota.status,
1855
- planType: liveQuota.planType,
1856
- activeLimit: liveQuota.activeLimit,
1857
- model: liveQuota.model,
1858
- summary: formatQuotaSnapshotLine(liveQuota),
1859
- }
1860
- : undefined,
1861
- refreshFailure: refreshFailures.get(result.index),
1862
- };
1863
- });
1864
- }
1865
- async function runForecast(args) {
1866
- if (args.includes("--help") || args.includes("-h")) {
1867
- printForecastUsage();
1868
- return 0;
1869
- }
1870
- const parsedArgs = parseForecastArgs(args);
1871
- if (!parsedArgs.ok) {
1872
- console.error(parsedArgs.message);
1873
- printForecastUsage();
1874
- return 1;
1875
- }
1876
- const options = parsedArgs.options;
1877
- const display = DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
1878
- const quotaCache = options.live ? await loadQuotaCache() : null;
1879
- const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
1880
- let quotaCacheChanged = false;
1881
- setStoragePath(null);
1882
- const storage = await loadAccounts();
1883
- if (!storage || storage.accounts.length === 0) {
1884
- console.log("No accounts configured.");
1885
- return 0;
1886
- }
1887
- const quotaEmailFallbackState = options.live && quotaCache
1888
- ? buildQuotaEmailFallbackState(storage.accounts)
1889
- : null;
1890
- const now = Date.now();
1891
- const activeIndex = resolveActiveIndex(storage, "codex");
1892
- const refreshFailures = new Map();
1893
- const liveQuotaByIndex = new Map();
1894
- const probeErrors = [];
1895
- for (let i = 0; i < storage.accounts.length; i += 1) {
1896
- const account = storage.accounts[i];
1897
- if (!account || !options.live)
1898
- continue;
1899
- if (account.enabled === false)
1900
- continue;
1901
- let probeAccessToken = account.accessToken;
1902
- let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
1903
- if (!hasUsableAccessToken(account, now)) {
1904
- const refreshResult = await queuedRefresh(account.refreshToken);
1905
- if (refreshResult.type !== "success") {
1906
- refreshFailures.set(i, {
1907
- ...refreshResult,
1908
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
1909
- });
1910
- continue;
1911
- }
1912
- probeAccessToken = refreshResult.access;
1913
- probeAccountId = account.accountId ?? extractAccountId(refreshResult.access);
1914
- }
1915
- if (!probeAccessToken || !probeAccountId) {
1916
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
1917
- continue;
1918
- }
1919
- try {
1920
- const liveQuota = await fetchCodexQuotaSnapshot({
1921
- accountId: probeAccountId,
1922
- accessToken: probeAccessToken,
1923
- model: options.model,
1924
- });
1925
- liveQuotaByIndex.set(i, liveQuota);
1926
- if (workingQuotaCache) {
1927
- const account = storage.accounts[i];
1928
- if (account) {
1929
- quotaCacheChanged =
1930
- updateQuotaCacheForAccount(workingQuotaCache, account, liveQuota, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
1931
- }
1932
- }
1933
- }
1934
- catch (error) {
1935
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
1936
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
1937
- }
1938
- }
1939
- const forecastInputs = storage.accounts.map((account, index) => ({
1940
- index,
1941
- account,
1942
- isCurrent: index === activeIndex,
1943
- now,
1944
- refreshFailure: refreshFailures.get(index),
1945
- liveQuota: liveQuotaByIndex.get(index),
1946
- }));
1947
- const forecastResults = evaluateForecastAccounts(forecastInputs);
1948
- const summary = summarizeForecast(forecastResults);
1949
- const recommendation = recommendForecastAccount(forecastResults);
1950
- if (options.json) {
1951
- if (workingQuotaCache && quotaCacheChanged) {
1952
- await saveQuotaCache(workingQuotaCache);
1953
- }
1954
- console.log(JSON.stringify({
1955
- command: "forecast",
1956
- model: options.model,
1957
- liveProbe: options.live,
1958
- summary,
1959
- recommendation,
1960
- probeErrors,
1961
- accounts: serializeForecastResults(forecastResults, liveQuotaByIndex, refreshFailures),
1962
- }, null, 2));
1963
- return 0;
1964
- }
1965
- console.log(stylePromptText(`Best-account preview (${storage.accounts.length} account(s), model ${options.model}, live check ${options.live ? "on" : "off"})`, "accent"));
1966
- console.log(formatResultSummary([
1967
- { text: `${summary.ready} ready now`, tone: "success" },
1968
- { text: `${summary.delayed} waiting`, tone: "warning" },
1969
- { text: `${summary.unavailable} unavailable`, tone: summary.unavailable > 0 ? "danger" : "muted" },
1970
- { text: `${summary.highRisk} high risk`, tone: summary.highRisk > 0 ? "danger" : "muted" },
1971
- ]));
1972
- console.log("");
1973
- for (const result of forecastResults) {
1974
- if (!display.showPerAccountRows) {
1975
- continue;
1976
- }
1977
- const currentTag = result.isCurrent ? " [current]" : "";
1978
- const waitLabel = result.waitMs > 0 ? stylePromptText(`wait ${formatWaitTime(result.waitMs)}`, "muted") : "";
1979
- const indexLabel = stylePromptText(`${result.index + 1}.`, "accent");
1980
- const accountLabel = stylePromptText(`${result.label}${currentTag}`, "accent");
1981
- const riskLabel = stylePromptText(`${result.riskLevel} risk (${result.riskScore})`, riskTone(result.riskLevel));
1982
- const availabilityLabel = stylePromptText(result.availability, availabilityTone(result.availability));
1983
- const rowParts = [availabilityLabel, riskLabel];
1984
- if (waitLabel)
1985
- rowParts.push(waitLabel);
1986
- console.log(`${indexLabel} ${accountLabel} ${stylePromptText("|", "muted")} ${joinStyledSegments(rowParts)}`);
1987
- if (display.showForecastReasons && result.reasons.length > 0) {
1988
- console.log(` ${stylePromptText(result.reasons.slice(0, 3).join("; "), "muted")}`);
1989
- }
1990
- const liveQuota = liveQuotaByIndex.get(result.index);
1991
- if (display.showQuotaDetails && liveQuota) {
1992
- console.log(` ${stylePromptText("quota:", "accent")} ${styleQuotaSummary(formatCompactQuotaSnapshot(liveQuota))}`);
1993
- }
1994
- }
1995
- if (!display.showPerAccountRows) {
1996
- console.log(stylePromptText("Per-account lines are hidden in dashboard settings.", "muted"));
1997
- }
1998
- if (display.showRecommendations) {
1999
- console.log("");
2000
- if (recommendation.recommendedIndex !== null) {
2001
- const index = recommendation.recommendedIndex;
2002
- const account = forecastResults.find((result) => result.index === index);
2003
- if (account) {
2004
- console.log(`${stylePromptText("Best next account:", "accent")} ${stylePromptText(`${index + 1} (${account.label})`, "success")}`);
2005
- console.log(`${stylePromptText("Why:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2006
- if (index !== activeIndex) {
2007
- console.log(`${stylePromptText("Switch now with:", "accent")} codex auth switch ${index + 1}`);
2008
- }
2009
- }
2010
- }
2011
- else {
2012
- console.log(`${stylePromptText("Note:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2013
- }
2014
- }
2015
- if (display.showLiveProbeNotes && probeErrors.length > 0) {
2016
- console.log("");
2017
- console.log(stylePromptText(`Live check notes (${probeErrors.length}):`, "warning"));
2018
- for (const error of probeErrors) {
2019
- console.log(` ${stylePromptText("-", "warning")} ${stylePromptText(error, "muted")}`);
2020
- }
2021
- }
2022
- if (workingQuotaCache && quotaCacheChanged) {
2023
- await saveQuotaCache(workingQuotaCache);
2024
- }
2025
- return 0;
2026
- }
2027
- async function runReport(args) {
2028
- if (args.includes("--help") || args.includes("-h")) {
2029
- printReportUsage();
2030
- return 0;
2031
- }
2032
- const parsedArgs = parseReportArgs(args);
2033
- if (!parsedArgs.ok) {
2034
- console.error(parsedArgs.message);
2035
- printReportUsage();
2036
- return 1;
2037
- }
2038
- const options = parsedArgs.options;
2039
- setStoragePath(null);
2040
- const storagePath = getStoragePath();
2041
- const storage = await loadAccounts();
2042
- const now = Date.now();
2043
- const accountCount = storage?.accounts.length ?? 0;
2044
- const activeIndex = storage ? resolveActiveIndex(storage, "codex") : 0;
2045
- const refreshFailures = new Map();
2046
- const liveQuotaByIndex = new Map();
2047
- const probeErrors = [];
2048
- if (storage && options.live) {
2049
- for (let i = 0; i < storage.accounts.length; i += 1) {
2050
- const account = storage.accounts[i];
2051
- if (!account || account.enabled === false)
2052
- continue;
2053
- const refreshResult = await queuedRefresh(account.refreshToken);
2054
- if (refreshResult.type !== "success") {
2055
- refreshFailures.set(i, {
2056
- ...refreshResult,
2057
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
2058
- });
2059
- continue;
2060
- }
2061
- const accountId = account.accountId ?? extractAccountId(refreshResult.access);
2062
- if (!accountId) {
2063
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
2064
- continue;
2065
- }
2066
- try {
2067
- const liveQuota = await fetchCodexQuotaSnapshot({
2068
- accountId,
2069
- accessToken: refreshResult.access,
2070
- model: options.model,
2071
- });
2072
- liveQuotaByIndex.set(i, liveQuota);
2073
- }
2074
- catch (error) {
2075
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2076
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
2077
- }
2078
- }
2079
- }
2080
- const forecastResults = storage
2081
- ? evaluateForecastAccounts(storage.accounts.map((account, index) => ({
2082
- index,
2083
- account,
2084
- isCurrent: index === activeIndex,
2085
- now,
2086
- refreshFailure: refreshFailures.get(index),
2087
- liveQuota: liveQuotaByIndex.get(index),
2088
- })))
2089
- : [];
2090
- const forecastSummary = summarizeForecast(forecastResults);
2091
- const recommendation = recommendForecastAccount(forecastResults);
2092
- const enabledCount = storage
2093
- ? storage.accounts.filter((account) => account.enabled !== false).length
2094
- : 0;
2095
- const disabledCount = Math.max(0, accountCount - enabledCount);
2096
- const coolingCount = storage
2097
- ? storage.accounts.filter((account) => typeof account.coolingDownUntil === "number" && account.coolingDownUntil > now).length
2098
- : 0;
2099
- const rateLimitedCount = storage
2100
- ? storage.accounts.filter((account) => !!formatRateLimitEntry(account, now, "codex")).length
2101
- : 0;
2102
- const report = {
2103
- command: "report",
2104
- generatedAt: new Date(now).toISOString(),
2105
- storagePath,
2106
- model: options.model,
2107
- liveProbe: options.live,
2108
- accounts: {
2109
- total: accountCount,
2110
- enabled: enabledCount,
2111
- disabled: disabledCount,
2112
- coolingDown: coolingCount,
2113
- rateLimited: rateLimitedCount,
2114
- },
2115
- activeIndex: accountCount > 0 ? activeIndex + 1 : null,
2116
- forecast: {
2117
- summary: forecastSummary,
2118
- recommendation,
2119
- probeErrors,
2120
- accounts: serializeForecastResults(forecastResults, liveQuotaByIndex, refreshFailures),
2121
- },
2122
- };
2123
- if (options.outPath) {
2124
- const outputPath = resolve(process.cwd(), options.outPath);
2125
- await fs.mkdir(dirname(outputPath), { recursive: true });
2126
- await fs.writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`, "utf-8");
2127
- }
2128
- if (options.json) {
2129
- console.log(JSON.stringify(report, null, 2));
2130
- return 0;
2131
- }
2132
- console.log(`Report generated at ${report.generatedAt}`);
2133
- console.log(`Storage: ${report.storagePath}`);
2134
- console.log(`Accounts: ${report.accounts.total} total (${report.accounts.enabled} enabled, ${report.accounts.disabled} disabled, ${report.accounts.coolingDown} cooling, ${report.accounts.rateLimited} rate-limited)`);
2135
- if (report.activeIndex !== null) {
2136
- console.log(`Active account: ${report.activeIndex}`);
2137
- }
2138
- console.log(`Forecast: ${report.forecast.summary.ready} ready, ${report.forecast.summary.delayed} delayed, ${report.forecast.summary.unavailable} unavailable`);
2139
- if (report.forecast.recommendation.recommendedIndex !== null) {
2140
- console.log(`Recommendation: account ${report.forecast.recommendation.recommendedIndex + 1} (${report.forecast.recommendation.reason})`);
2141
- }
2142
- else {
2143
- console.log(`Recommendation: ${report.forecast.recommendation.reason}`);
2144
- }
2145
- if (options.outPath) {
2146
- console.log(`Report written: ${resolve(process.cwd(), options.outPath)}`);
2147
- }
2148
- if (report.forecast.probeErrors.length > 0) {
2149
- console.log(`Probe notes: ${report.forecast.probeErrors.length}`);
2150
- }
2151
- return 0;
2152
- }
2153
- function summarizeFixReports(reports) {
2154
- let healthy = 0;
2155
- let disabled = 0;
2156
- let warnings = 0;
2157
- let skipped = 0;
2158
- for (const report of reports) {
2159
- if (report.outcome === "healthy")
2160
- healthy += 1;
2161
- else if (report.outcome === "disabled-hard-failure")
2162
- disabled += 1;
2163
- else if (report.outcome === "warning-soft-failure")
2164
- warnings += 1;
2165
- else
2166
- skipped += 1;
2167
- }
2168
- return { healthy, disabled, warnings, skipped };
2169
- }
2170
- function createEmptyAccountStorage() {
2171
- const activeIndexByFamily = {};
2172
- for (const family of MODEL_FAMILIES) {
2173
- activeIndexByFamily[family] = 0;
2174
- }
2175
- return {
2176
- version: 3,
2177
- accounts: [],
2178
- activeIndex: 0,
2179
- activeIndexByFamily,
2180
- };
2181
- }
2182
- function findExistingAccountIndexForFlagged(storage, flagged, nextRefreshToken, nextAccountId, nextEmail) {
2183
- const flaggedEmail = sanitizeEmail(flagged.email);
2184
- const candidateAccountId = nextAccountId ?? flagged.accountId;
2185
- const candidateEmail = sanitizeEmail(nextEmail) ?? flaggedEmail;
2186
- const nextMatchIndex = findMatchingAccountIndex(storage.accounts, {
2187
- accountId: candidateAccountId,
2188
- email: candidateEmail,
2189
- refreshToken: nextRefreshToken,
2190
- }, {
2191
- allowUniqueAccountIdFallbackWithoutEmail: true,
2192
- });
2193
- if (nextMatchIndex !== undefined) {
2194
- return nextMatchIndex;
2195
- }
2196
- const flaggedMatchIndex = findMatchingAccountIndex(storage.accounts, {
2197
- accountId: candidateAccountId,
2198
- email: candidateEmail,
2199
- refreshToken: flagged.refreshToken,
2200
- }, {
2201
- allowUniqueAccountIdFallbackWithoutEmail: true,
2202
- });
2203
- return flaggedMatchIndex ?? -1;
2204
- }
2205
- function upsertRecoveredFlaggedAccount(storage, flagged, refreshResult, now) {
2206
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken)) ?? flagged.email;
2207
- const tokenAccountId = extractAccountId(refreshResult.access);
2208
- const { accountId: nextAccountId, accountIdSource: nextAccountIdSource } = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2209
- const existingIndex = findExistingAccountIndexForFlagged(storage, flagged, refreshResult.refresh, nextAccountId, nextEmail);
2210
- if (existingIndex >= 0) {
2211
- const existing = storage.accounts[existingIndex];
2212
- if (!existing) {
2213
- return { restored: false, changed: false, message: "existing account entry is missing" };
2214
- }
2215
- let changed = false;
2216
- if (existing.refreshToken !== refreshResult.refresh) {
2217
- existing.refreshToken = refreshResult.refresh;
2218
- changed = true;
2219
- }
2220
- if (existing.accessToken !== refreshResult.access) {
2221
- existing.accessToken = refreshResult.access;
2222
- changed = true;
2223
- }
2224
- if (existing.expiresAt !== refreshResult.expires) {
2225
- existing.expiresAt = refreshResult.expires;
2226
- changed = true;
2227
- }
2228
- if (nextEmail && nextEmail !== existing.email) {
2229
- existing.email = nextEmail;
2230
- changed = true;
2231
- }
2232
- if (nextAccountId !== undefined &&
2233
- ((nextAccountId !== existing.accountId)
2234
- || (nextAccountIdSource !== existing.accountIdSource))) {
2235
- existing.accountId = nextAccountId;
2236
- existing.accountIdSource = nextAccountIdSource;
2237
- changed = true;
2238
- }
2239
- if (existing.enabled === false) {
2240
- existing.enabled = true;
2241
- changed = true;
2242
- }
2243
- if (existing.accountLabel !== flagged.accountLabel && flagged.accountLabel) {
2244
- existing.accountLabel = flagged.accountLabel;
2245
- changed = true;
2246
- }
2247
- existing.lastUsed = now;
2248
- return {
2249
- restored: true,
2250
- changed,
2251
- message: `restored into existing account ${existingIndex + 1}`,
2252
- };
2253
- }
2254
- if (storage.accounts.length >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
2255
- return {
2256
- restored: false,
2257
- changed: false,
2258
- message: `cannot restore (max ${ACCOUNT_LIMITS.MAX_ACCOUNTS} accounts reached)`,
2259
- };
2260
- }
2261
- storage.accounts.push({
2262
- refreshToken: refreshResult.refresh,
2263
- accessToken: refreshResult.access,
2264
- expiresAt: refreshResult.expires,
2265
- accountId: nextAccountId,
2266
- accountIdSource: nextAccountIdSource,
2267
- accountLabel: flagged.accountLabel,
2268
- email: nextEmail,
2269
- addedAt: flagged.addedAt ?? now,
2270
- lastUsed: now,
2271
- enabled: true,
2272
- });
2273
- return {
2274
- restored: true,
2275
- changed: true,
2276
- message: `restored as account ${storage.accounts.length}`,
2277
- };
2278
- }
2279
- async function runVerifyFlagged(args) {
2280
- if (args.includes("--help") || args.includes("-h")) {
2281
- printVerifyFlaggedUsage();
2282
- return 0;
2283
- }
2284
- const parsedArgs = parseVerifyFlaggedArgs(args);
2285
- if (!parsedArgs.ok) {
2286
- console.error(parsedArgs.message);
2287
- printVerifyFlaggedUsage();
2288
- return 1;
2289
- }
2290
- const options = parsedArgs.options;
2291
- setStoragePath(null);
2292
- const flaggedStorage = await loadFlaggedAccounts();
2293
- if (flaggedStorage.accounts.length === 0) {
2294
- if (options.json) {
2295
- console.log(JSON.stringify({
2296
- command: "verify-flagged",
2297
- total: 0,
2298
- restored: 0,
2299
- healthyFlagged: 0,
2300
- stillFlagged: 0,
2301
- changed: false,
2302
- dryRun: options.dryRun,
2303
- restore: options.restore,
2304
- reports: [],
2305
- }, null, 2));
2306
- return 0;
2307
- }
2308
- console.log("No flagged accounts to check.");
2309
- return 0;
2310
- }
2311
- let storageChanged = false;
2312
- let flaggedChanged = false;
2313
- const reports = [];
2314
- const nextFlaggedAccounts = [];
2315
- const now = Date.now();
2316
- const refreshChecks = [];
2317
- for (let i = 0; i < flaggedStorage.accounts.length; i += 1) {
2318
- const flagged = flaggedStorage.accounts[i];
2319
- if (!flagged)
2320
- continue;
2321
- const label = formatAccountLabel(flagged, i);
2322
- refreshChecks.push({
2323
- index: i,
2324
- flagged,
2325
- label,
2326
- result: await queuedRefresh(flagged.refreshToken),
2327
- });
2328
- }
2329
- const applyRefreshChecks = (storage) => {
2330
- for (const check of refreshChecks) {
2331
- const { index: i, flagged, label, result } = check;
2332
- if (result.type === "success") {
2333
- if (!options.restore) {
2334
- const tokenAccountId = extractAccountId(result.access);
2335
- const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2336
- const nextFlagged = {
2337
- ...flagged,
2338
- refreshToken: result.refresh,
2339
- accessToken: result.access,
2340
- expiresAt: result.expires,
2341
- accountId: nextIdentity.accountId,
2342
- accountIdSource: nextIdentity.accountIdSource,
2343
- email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
2344
- lastUsed: now,
2345
- lastError: undefined,
2346
- };
2347
- nextFlaggedAccounts.push(nextFlagged);
2348
- if (JSON.stringify(nextFlagged) !== JSON.stringify(flagged)) {
2349
- flaggedChanged = true;
2350
- }
2351
- reports.push({
2352
- index: i,
2353
- label,
2354
- outcome: "healthy-flagged",
2355
- message: "session is healthy (left in flagged list due to --no-restore)",
2356
- });
2357
- continue;
2358
- }
2359
- const upsertResult = upsertRecoveredFlaggedAccount(storage, flagged, result, now);
2360
- if (upsertResult.restored) {
2361
- storageChanged = storageChanged || upsertResult.changed;
2362
- flaggedChanged = true;
2363
- reports.push({
2364
- index: i,
2365
- label,
2366
- outcome: "restored",
2367
- message: upsertResult.message,
2368
- });
2369
- continue;
2370
- }
2371
- const tokenAccountId = extractAccountId(result.access);
2372
- const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2373
- const updatedFlagged = {
2374
- ...flagged,
2375
- refreshToken: result.refresh,
2376
- accessToken: result.access,
2377
- expiresAt: result.expires,
2378
- accountId: nextIdentity.accountId,
2379
- accountIdSource: nextIdentity.accountIdSource,
2380
- email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
2381
- lastUsed: now,
2382
- lastError: upsertResult.message,
2383
- };
2384
- nextFlaggedAccounts.push(updatedFlagged);
2385
- if (JSON.stringify(updatedFlagged) !== JSON.stringify(flagged)) {
2386
- flaggedChanged = true;
2387
- }
2388
- reports.push({
2389
- index: i,
2390
- label,
2391
- outcome: "restore-skipped",
2392
- message: upsertResult.message,
2393
- });
2394
- continue;
2395
- }
2396
- const detail = normalizeFailureDetail(result.message, result.reason);
2397
- const failedFlagged = {
2398
- ...flagged,
2399
- lastError: detail,
2400
- };
2401
- nextFlaggedAccounts.push(failedFlagged);
2402
- if ((flagged.lastError ?? "") !== detail) {
2403
- flaggedChanged = true;
2404
- }
2405
- reports.push({
2406
- index: i,
2407
- label,
2408
- outcome: "still-flagged",
2409
- message: detail,
2410
- });
2411
- }
2412
- };
2413
- if (options.restore) {
2414
- if (options.dryRun) {
2415
- applyRefreshChecks((await loadAccounts()) ?? createEmptyAccountStorage());
2416
- }
2417
- else {
2418
- await withAccountAndFlaggedStorageTransaction(async (loadedStorage, persist) => {
2419
- const nextStorage = loadedStorage
2420
- ? structuredClone(loadedStorage)
2421
- : createEmptyAccountStorage();
2422
- applyRefreshChecks(nextStorage);
2423
- if (!storageChanged) {
2424
- return;
2425
- }
2426
- normalizeDoctorIndexes(nextStorage);
2427
- await persist(nextStorage, {
2428
- version: 1,
2429
- accounts: nextFlaggedAccounts,
2430
- });
2431
- });
2432
- }
2433
- }
2434
- else {
2435
- applyRefreshChecks(createEmptyAccountStorage());
2436
- }
2437
- const remainingFlagged = nextFlaggedAccounts.length;
2438
- const restored = reports.filter((report) => report.outcome === "restored").length;
2439
- const healthyFlagged = reports.filter((report) => report.outcome === "healthy-flagged").length;
2440
- const stillFlagged = reports.filter((report) => report.outcome === "still-flagged").length;
2441
- const changed = storageChanged || flaggedChanged;
2442
- if (!options.dryRun && flaggedChanged && (!options.restore || !storageChanged)) {
2443
- await saveFlaggedAccounts({
2444
- version: 1,
2445
- accounts: nextFlaggedAccounts,
2446
- });
2447
- }
2448
- if (options.json) {
2449
- console.log(JSON.stringify({
2450
- command: "verify-flagged",
2451
- total: flaggedStorage.accounts.length,
2452
- restored,
2453
- healthyFlagged,
2454
- stillFlagged,
2455
- remainingFlagged,
2456
- changed,
2457
- dryRun: options.dryRun,
2458
- restore: options.restore,
2459
- reports,
2460
- }, null, 2));
2461
- return 0;
2462
- }
2463
- console.log(stylePromptText(`Checking ${flaggedStorage.accounts.length} flagged account(s)...`, "accent"));
2464
- for (const report of reports) {
2465
- const tone = report.outcome === "restored"
2466
- ? "success"
2467
- : report.outcome === "healthy-flagged"
2468
- ? "warning"
2469
- : report.outcome === "restore-skipped"
2470
- ? "warning"
2471
- : "danger";
2472
- const marker = report.outcome === "restored"
2473
- ? "✓"
2474
- : report.outcome === "healthy-flagged"
2475
- ? "!"
2476
- : report.outcome === "restore-skipped"
2477
- ? "!"
2478
- : "✗";
2479
- console.log(`${stylePromptText(marker, tone)} ${stylePromptText(`${report.index + 1}. ${report.label}`, "accent")} ${stylePromptText("|", "muted")} ${styleAccountDetailText(report.message, tone)}`);
2480
- }
2481
- console.log("");
2482
- console.log(formatResultSummary([
2483
- { text: `${restored} restored`, tone: restored > 0 ? "success" : "muted" },
2484
- { text: `${healthyFlagged} healthy (kept flagged)`, tone: healthyFlagged > 0 ? "warning" : "muted" },
2485
- { text: `${stillFlagged} still flagged`, tone: stillFlagged > 0 ? "danger" : "muted" },
2486
- ]));
2487
- if (options.dryRun) {
2488
- console.log(stylePromptText("Preview only: no changes were saved.", "warning"));
2489
- }
2490
- else if (!changed) {
2491
- console.log(stylePromptText("No storage changes were needed.", "muted"));
2492
- }
2493
- return 0;
2494
- }
2495
- async function runFix(args) {
2496
- if (args.includes("--help") || args.includes("-h")) {
2497
- printFixUsage();
2498
- return 0;
2499
- }
2500
- const parsedArgs = parseFixArgs(args);
2501
- if (!parsedArgs.ok) {
2502
- console.error(parsedArgs.message);
2503
- printFixUsage();
2504
- return 1;
2505
- }
2506
- const options = parsedArgs.options;
2507
- const display = DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
2508
- const quotaCache = options.live ? await loadQuotaCache() : null;
2509
- const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
2510
- let quotaCacheChanged = false;
2511
- setStoragePath(null);
2512
- const storage = await loadAccounts();
2513
- if (!storage || storage.accounts.length === 0) {
2514
- console.log("No accounts configured.");
2515
- return 0;
2516
- }
2517
- let quotaEmailFallbackState = options.live && quotaCache
2518
- ? buildQuotaEmailFallbackState(storage.accounts)
2519
- : null;
2520
- const now = Date.now();
2521
- const activeIndex = resolveActiveIndex(storage, "codex");
2522
- let changed = false;
2523
- const reports = [];
2524
- const refreshFailures = new Map();
2525
- const hardDisabledIndexes = [];
2526
- for (let i = 0; i < storage.accounts.length; i += 1) {
2527
- const account = storage.accounts[i];
2528
- if (!account)
2529
- continue;
2530
- const label = formatAccountLabel(account, i);
2531
- if (account.enabled === false) {
2532
- reports.push({
2533
- index: i,
2534
- label,
2535
- outcome: "already-disabled",
2536
- message: "already disabled",
2537
- });
2538
- continue;
2539
- }
2540
- if (hasUsableAccessToken(account, now)) {
2541
- if (options.live) {
2542
- const currentAccessToken = account.accessToken;
2543
- const probeAccountId = currentAccessToken
2544
- ? (account.accountId ?? extractAccountId(currentAccessToken))
2545
- : undefined;
2546
- if (probeAccountId && currentAccessToken) {
2547
- try {
2548
- const snapshot = await fetchCodexQuotaSnapshot({
2549
- accountId: probeAccountId,
2550
- accessToken: currentAccessToken,
2551
- model: options.model,
2552
- });
2553
- if (workingQuotaCache) {
2554
- quotaCacheChanged =
2555
- updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
2556
- }
2557
- reports.push({
2558
- index: i,
2559
- label,
2560
- outcome: "healthy",
2561
- message: display.showQuotaDetails
2562
- ? `live session OK (${formatCompactQuotaSnapshot(snapshot)})`
2563
- : "live session OK",
2564
- });
2565
- continue;
2566
- }
2567
- catch (error) {
2568
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2569
- reports.push({
2570
- index: i,
2571
- label,
2572
- outcome: "warning-soft-failure",
2573
- message: `live probe failed (${message}), trying refresh fallback`,
2574
- });
2575
- }
2576
- }
2577
- }
2578
- const refreshWarning = hasLikelyInvalidRefreshToken(account.refreshToken)
2579
- ? " (refresh token looks stale; re-login recommended)"
2580
- : "";
2581
- reports.push({
2582
- index: i,
2583
- label,
2584
- outcome: "healthy",
2585
- message: `access token still valid${refreshWarning}`,
2586
- });
2587
- continue;
2588
- }
2589
- const refreshResult = await queuedRefresh(account.refreshToken);
2590
- if (refreshResult.type === "success") {
2591
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
2592
- const nextAccountId = extractAccountId(refreshResult.access);
2593
- const previousEmail = account.email;
2594
- let accountChanged = false;
2595
- let accountIdentityChanged = false;
2596
- if (account.refreshToken !== refreshResult.refresh) {
2597
- account.refreshToken = refreshResult.refresh;
2598
- accountChanged = true;
2599
- }
2600
- if (account.accessToken !== refreshResult.access) {
2601
- account.accessToken = refreshResult.access;
2602
- accountChanged = true;
2603
- }
2604
- if (account.expiresAt !== refreshResult.expires) {
2605
- account.expiresAt = refreshResult.expires;
2606
- accountChanged = true;
2607
- }
2608
- if (nextEmail && nextEmail !== account.email) {
2609
- account.email = nextEmail;
2610
- accountChanged = true;
2611
- accountIdentityChanged = true;
2612
- }
2613
- if (applyTokenAccountIdentity(account, nextAccountId)) {
2614
- accountChanged = true;
2615
- accountIdentityChanged = true;
2616
- }
2617
- if (accountChanged)
2618
- changed = true;
2619
- if (accountIdentityChanged && options.live && workingQuotaCache) {
2620
- quotaEmailFallbackState = buildQuotaEmailFallbackState(storage.accounts);
2621
- quotaCacheChanged =
2622
- pruneUnsafeQuotaEmailCacheEntry(workingQuotaCache, previousEmail, storage.accounts, quotaEmailFallbackState) || quotaCacheChanged;
2623
- }
2624
- if (options.live) {
2625
- const probeAccountId = account.accountId ?? nextAccountId;
2626
- if (probeAccountId) {
2627
- try {
2628
- const snapshot = await fetchCodexQuotaSnapshot({
2629
- accountId: probeAccountId,
2630
- accessToken: refreshResult.access,
2631
- model: options.model,
2632
- });
2633
- if (workingQuotaCache) {
2634
- quotaCacheChanged =
2635
- updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
2636
- }
2637
- reports.push({
2638
- index: i,
2639
- label,
2640
- outcome: "healthy",
2641
- message: display.showQuotaDetails
2642
- ? `refresh + live probe succeeded (${formatCompactQuotaSnapshot(snapshot)})`
2643
- : "refresh + live probe succeeded",
2644
- });
2645
- continue;
2646
- }
2647
- catch (error) {
2648
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2649
- reports.push({
2650
- index: i,
2651
- label,
2652
- outcome: "warning-soft-failure",
2653
- message: `refresh succeeded but live probe failed: ${message}`,
2654
- });
2655
- continue;
2656
- }
2657
- }
2658
- }
2659
- reports.push({
2660
- index: i,
2661
- label,
2662
- outcome: "healthy",
2663
- message: "refresh succeeded",
2664
- });
2665
- continue;
2666
- }
2667
- const detail = normalizeFailureDetail(refreshResult.message, refreshResult.reason);
2668
- refreshFailures.set(i, {
2669
- ...refreshResult,
2670
- message: detail,
2671
- });
2672
- if (isHardRefreshFailure(refreshResult)) {
2673
- account.enabled = false;
2674
- changed = true;
2675
- hardDisabledIndexes.push(i);
2676
- reports.push({
2677
- index: i,
2678
- label,
2679
- outcome: "disabled-hard-failure",
2680
- message: detail,
2681
- });
2682
- }
2683
- else {
2684
- reports.push({
2685
- index: i,
2686
- label,
2687
- outcome: "warning-soft-failure",
2688
- message: detail,
2689
- });
2690
- }
2691
- }
2692
- if (hardDisabledIndexes.length > 0) {
2693
- const enabledCount = storage.accounts.filter((account) => account.enabled !== false).length;
2694
- if (enabledCount === 0) {
2695
- const fallbackIndex = hardDisabledIndexes.includes(activeIndex) ? activeIndex : hardDisabledIndexes[0];
2696
- const fallback = typeof fallbackIndex === "number"
2697
- ? storage.accounts[fallbackIndex]
2698
- : undefined;
2699
- if (fallback && fallback.enabled === false) {
2700
- fallback.enabled = true;
2701
- changed = true;
2702
- const existingReport = reports.find((report) => report.index === fallbackIndex &&
2703
- report.outcome === "disabled-hard-failure");
2704
- if (existingReport) {
2705
- existingReport.outcome = "warning-soft-failure";
2706
- existingReport.message = `${existingReport.message} (kept enabled to avoid lockout; re-login required)`;
2707
- }
2708
- }
2709
- }
2710
- }
2711
- const forecastResults = evaluateForecastAccounts(storage.accounts.map((account, index) => ({
2712
- index,
2713
- account,
2714
- isCurrent: index === activeIndex,
2715
- now,
2716
- refreshFailure: refreshFailures.get(index),
2717
- })));
2718
- const recommendation = recommendForecastAccount(forecastResults);
2719
- const reportSummary = summarizeFixReports(reports);
2720
- if (changed && !options.dryRun) {
2721
- await saveAccounts(storage);
2722
- }
2723
- if (options.json) {
2724
- if (workingQuotaCache && quotaCacheChanged) {
2725
- await saveQuotaCache(workingQuotaCache);
2726
- }
2727
- console.log(JSON.stringify({
2728
- command: "fix",
2729
- dryRun: options.dryRun,
2730
- liveProbe: options.live,
2731
- model: options.model,
2732
- changed,
2733
- summary: reportSummary,
2734
- recommendation,
2735
- recommendedSwitchCommand: recommendation.recommendedIndex !== null &&
2736
- recommendation.recommendedIndex !== activeIndex
2737
- ? `codex auth switch ${recommendation.recommendedIndex + 1}`
2738
- : null,
2739
- reports,
2740
- }, null, 2));
2741
- return 0;
2742
- }
2743
- console.log(stylePromptText(`Auto-fix scan (${options.dryRun ? "preview" : "apply"})`, "accent"));
2744
- console.log(formatResultSummary([
2745
- { text: `${reportSummary.healthy} working`, tone: "success" },
2746
- { text: `${reportSummary.disabled} disabled`, tone: reportSummary.disabled > 0 ? "danger" : "muted" },
2747
- {
2748
- text: `${reportSummary.warnings} warning${reportSummary.warnings === 1 ? "" : "s"}`,
2749
- tone: reportSummary.warnings > 0 ? "warning" : "muted",
2750
- },
2751
- { text: `${reportSummary.skipped} already disabled`, tone: "muted" },
2752
- ]));
2753
- if (display.showPerAccountRows) {
2754
- console.log("");
2755
- for (const report of reports) {
2756
- const prefix = report.outcome === "healthy"
2757
- ? "✓"
2758
- : report.outcome === "disabled-hard-failure"
2759
- ? "✗"
2760
- : report.outcome === "warning-soft-failure"
2761
- ? "!"
2762
- : "-";
2763
- const tone = report.outcome === "healthy"
2764
- ? "success"
2765
- : report.outcome === "disabled-hard-failure"
2766
- ? "danger"
2767
- : report.outcome === "warning-soft-failure"
2768
- ? "warning"
2769
- : "muted";
2770
- console.log(`${stylePromptText(prefix, tone)} ${stylePromptText(`${report.index + 1}. ${report.label}`, "accent")} ${stylePromptText("|", "muted")} ${styleAccountDetailText(report.message, tone === "success" ? "muted" : tone)}`);
2771
- }
2772
- }
2773
- else {
2774
- console.log("");
2775
- console.log(stylePromptText("Per-account lines are hidden in dashboard settings.", "muted"));
2776
- }
2777
- if (display.showRecommendations) {
2778
- console.log("");
2779
- if (recommendation.recommendedIndex !== null) {
2780
- const target = recommendation.recommendedIndex + 1;
2781
- console.log(`${stylePromptText("Best next account:", "accent")} ${stylePromptText(String(target), "success")}`);
2782
- console.log(`${stylePromptText("Why:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2783
- if (recommendation.recommendedIndex !== activeIndex) {
2784
- console.log(`${stylePromptText("Switch now with:", "accent")} codex auth switch ${target}`);
2785
- }
2786
- }
2787
- else {
2788
- console.log(`${stylePromptText("Note:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2789
- }
2790
- }
2791
- if (workingQuotaCache && quotaCacheChanged) {
2792
- await saveQuotaCache(workingQuotaCache);
2793
- }
2794
- if (changed && options.dryRun) {
2795
- console.log(`\n${stylePromptText("Preview only: no changes were saved.", "warning")}`);
2796
- }
2797
- else if (changed) {
2798
- console.log(`\n${stylePromptText("Saved updates.", "success")}`);
2799
- }
2800
- else {
2801
- console.log(`\n${stylePromptText("No changes were needed.", "muted")}`);
2802
- }
2803
- return 0;
2804
- }
2805
- function hasPlaceholderEmail(value) {
2806
- if (!value)
2807
- return false;
2808
- const email = value.trim().toLowerCase();
2809
- if (!email)
2810
- return false;
2811
- return (email.endsWith("@example.com") ||
2812
- email.includes("account1@example.com") ||
2813
- email.includes("account2@example.com") ||
2814
- email.includes("account3@example.com"));
2815
- }
2816
- function normalizeDoctorIndexes(storage) {
2817
- const total = storage.accounts.length;
2818
- const nextActive = total === 0 ? 0 : Math.max(0, Math.min(storage.activeIndex, total - 1));
2819
- let changed = false;
2820
- if (storage.activeIndex !== nextActive) {
2821
- storage.activeIndex = nextActive;
2822
- changed = true;
2823
- }
2824
- storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
2825
- for (const family of MODEL_FAMILIES) {
2826
- const raw = storage.activeIndexByFamily[family];
2827
- const fallback = storage.activeIndex;
2828
- const candidate = typeof raw === "number" && Number.isFinite(raw) ? raw : fallback;
2829
- const clamped = total === 0 ? 0 : Math.max(0, Math.min(candidate, total - 1));
2830
- if (storage.activeIndexByFamily[family] !== clamped) {
2831
- storage.activeIndexByFamily[family] = clamped;
2832
- changed = true;
2833
- }
2834
- }
2835
- return changed;
2836
- }
2837
- function getDoctorRefreshTokenKey(refreshToken) {
2838
- if (typeof refreshToken !== "string")
2839
- return undefined;
2840
- const trimmed = refreshToken.trim();
2841
- return trimmed || undefined;
2842
- }
2843
- function applyDoctorFixes(storage) {
2844
- let changed = false;
2845
- const actions = [];
2846
- if (normalizeDoctorIndexes(storage)) {
2847
- changed = true;
2848
- actions.push({
2849
- key: "active-index",
2850
- message: "Normalized active account indexes",
2851
- });
2852
- }
2853
- const seenRefreshTokens = new Map();
2854
- for (let i = 0; i < storage.accounts.length; i += 1) {
2855
- const account = storage.accounts[i];
2856
- if (!account)
2857
- continue;
2858
- const refreshToken = getDoctorRefreshTokenKey(account.refreshToken);
2859
- if (!refreshToken)
2860
- continue;
2861
- const existingTokenIndex = seenRefreshTokens.get(refreshToken);
2862
- if (typeof existingTokenIndex === "number") {
2863
- if (account.enabled !== false) {
2864
- account.enabled = false;
2865
- changed = true;
2866
- actions.push({
2867
- key: "duplicate-refresh-token",
2868
- message: `Disabled duplicate token entry on account ${i + 1} (kept account ${existingTokenIndex + 1})`,
2869
- });
2870
- }
2871
- }
2872
- else {
2873
- seenRefreshTokens.set(refreshToken, i);
2874
- }
2875
- const tokenEmail = sanitizeEmail(extractAccountEmail(account.accessToken));
2876
- if (tokenEmail &&
2877
- (!sanitizeEmail(account.email) || hasPlaceholderEmail(account.email))) {
2878
- account.email = tokenEmail;
2879
- changed = true;
2880
- actions.push({
2881
- key: "email-from-token",
2882
- message: `Updated account ${i + 1} email from token claims`,
2883
- });
2884
- }
2885
- const tokenAccountId = extractAccountId(account.accessToken);
2886
- if (!account.accountId && tokenAccountId) {
2887
- account.accountId = tokenAccountId;
2888
- account.accountIdSource = "token";
2889
- changed = true;
2890
- actions.push({
2891
- key: "account-id-from-token",
2892
- message: `Filled missing accountId for account ${i + 1}`,
2893
- });
2894
- }
2895
- }
2896
- const enabledCount = storage.accounts.filter((account) => account.enabled !== false).length;
2897
- if (storage.accounts.length > 0 && enabledCount === 0) {
2898
- const index = resolveActiveIndex(storage, "codex");
2899
- const candidate = storage.accounts[index] ?? storage.accounts[0];
2900
- if (candidate) {
2901
- candidate.enabled = true;
2902
- changed = true;
2903
- actions.push({
2904
- key: "enabled-accounts",
2905
- message: `Re-enabled account ${index + 1} to avoid an all-disabled pool`,
2906
- });
2907
- }
2908
- }
2909
- if (normalizeDoctorIndexes(storage)) {
2910
- changed = true;
2911
- }
2912
- return { changed, actions };
2913
- }
2914
- async function runDoctor(args) {
2915
- if (args.includes("--help") || args.includes("-h")) {
2916
- printDoctorUsage();
2917
- return 0;
2918
- }
2919
- const parsedArgs = parseDoctorArgs(args);
2920
- if (!parsedArgs.ok) {
2921
- console.error(parsedArgs.message);
2922
- printDoctorUsage();
2923
- return 1;
2924
- }
2925
- const options = parsedArgs.options;
2926
- setStoragePath(null);
2927
- const storagePath = getStoragePath();
2928
- const checks = [];
2929
- const addCheck = (check) => {
2930
- checks.push(check);
2931
- };
2932
- addCheck({
2933
- key: "storage-file",
2934
- severity: existsSync(storagePath) ? "ok" : "warn",
2935
- message: existsSync(storagePath)
2936
- ? "Account storage file found"
2937
- : "Account storage file does not exist yet (first login pending)",
2938
- details: storagePath,
2939
- });
2940
- if (existsSync(storagePath)) {
2941
- try {
2942
- const stat = await fs.stat(storagePath);
2943
- addCheck({
2944
- key: "storage-readable",
2945
- severity: stat.size > 0 ? "ok" : "warn",
2946
- message: stat.size > 0 ? "Storage file is readable" : "Storage file is empty",
2947
- details: `${stat.size} bytes`,
2948
- });
2949
- }
2950
- catch (error) {
2951
- addCheck({
2952
- key: "storage-readable",
2953
- severity: "error",
2954
- message: "Unable to read storage file metadata",
2955
- details: error instanceof Error ? error.message : String(error),
2956
- });
2957
- }
2958
- }
2959
- const codexAuthPath = getCodexCliAuthPath();
2960
- const codexConfigPath = getCodexCliConfigPath();
2961
- let codexAuthEmail;
2962
- let codexAuthAccountId;
2963
- addCheck({
2964
- key: "codex-auth-file",
2965
- severity: existsSync(codexAuthPath) ? "ok" : "warn",
2966
- message: existsSync(codexAuthPath)
2967
- ? "Codex auth file found"
2968
- : "Codex auth file does not exist",
2969
- details: codexAuthPath,
2970
- });
2971
- if (existsSync(codexAuthPath)) {
2972
- try {
2973
- const raw = await fs.readFile(codexAuthPath, "utf-8");
2974
- const parsed = JSON.parse(raw);
2975
- if (parsed && typeof parsed === "object") {
2976
- const payload = parsed;
2977
- const tokens = payload.tokens && typeof payload.tokens === "object"
2978
- ? payload.tokens
2979
- : null;
2980
- const accessToken = tokens && typeof tokens.access_token === "string"
2981
- ? tokens.access_token
2982
- : undefined;
2983
- const idToken = tokens && typeof tokens.id_token === "string"
2984
- ? tokens.id_token
2985
- : undefined;
2986
- const accountIdFromFile = tokens && typeof tokens.account_id === "string"
2987
- ? tokens.account_id
2988
- : undefined;
2989
- const emailFromFile = typeof payload.email === "string" ? payload.email : undefined;
2990
- codexAuthEmail = sanitizeEmail(emailFromFile ?? extractAccountEmail(accessToken, idToken));
2991
- codexAuthAccountId = accountIdFromFile ?? extractAccountId(accessToken);
2992
- }
2993
- addCheck({
2994
- key: "codex-auth-readable",
2995
- severity: "ok",
2996
- message: "Codex auth file is readable",
2997
- details: codexAuthEmail || codexAuthAccountId
2998
- ? `email=${codexAuthEmail ?? "unknown"}, accountId=${codexAuthAccountId ?? "unknown"}`
2999
- : undefined,
3000
- });
3001
- }
3002
- catch (error) {
3003
- addCheck({
3004
- key: "codex-auth-readable",
3005
- severity: "error",
3006
- message: "Unable to read Codex auth file",
3007
- details: error instanceof Error ? error.message : String(error),
3008
- });
3009
- }
3010
- }
3011
- addCheck({
3012
- key: "codex-config-file",
3013
- severity: existsSync(codexConfigPath) ? "ok" : "warn",
3014
- message: existsSync(codexConfigPath)
3015
- ? "Codex config file found"
3016
- : "Codex config file does not exist",
3017
- details: codexConfigPath,
3018
- });
3019
- let codexAuthStoreMode;
3020
- if (existsSync(codexConfigPath)) {
3021
- try {
3022
- const configRaw = await fs.readFile(codexConfigPath, "utf-8");
3023
- const match = configRaw.match(/^\s*cli_auth_credentials_store\s*=\s*"([^"]+)"\s*$/m);
3024
- if (match?.[1]) {
3025
- codexAuthStoreMode = match[1].trim();
3026
- }
3027
- }
3028
- catch (error) {
3029
- addCheck({
3030
- key: "codex-auth-store",
3031
- severity: "warn",
3032
- message: "Unable to read Codex auth-store config",
3033
- details: error instanceof Error ? error.message : String(error),
3034
- });
3035
- }
3036
- }
3037
- if (!checks.some((check) => check.key === "codex-auth-store")) {
3038
- addCheck({
3039
- key: "codex-auth-store",
3040
- severity: codexAuthStoreMode === "file" ? "ok" : "warn",
3041
- message: codexAuthStoreMode === "file"
3042
- ? "Codex auth storage is set to file"
3043
- : "Codex auth storage is not explicitly set to file",
3044
- details: codexAuthStoreMode ? `mode=${codexAuthStoreMode}` : "mode=unset",
3045
- });
3046
- }
3047
- const codexCliState = await loadCodexCliState({ forceRefresh: true });
3048
- addCheck({
3049
- key: "codex-cli-state",
3050
- severity: codexCliState ? "ok" : "warn",
3051
- message: codexCliState
3052
- ? "Codex CLI state loaded"
3053
- : "Codex CLI state unavailable",
3054
- details: codexCliState?.path,
3055
- });
3056
- const storage = await loadAccounts();
3057
- let fixChanged = false;
3058
- let fixActions = [];
3059
- if (options.fix && storage && storage.accounts.length > 0) {
3060
- const fixed = applyDoctorFixes(storage);
3061
- fixChanged = fixed.changed;
3062
- fixActions = fixed.actions;
3063
- if (fixChanged && !options.dryRun) {
3064
- await saveAccounts(storage);
3065
- }
3066
- addCheck({
3067
- key: "auto-fix",
3068
- severity: fixChanged ? "warn" : "ok",
3069
- message: fixChanged
3070
- ? options.dryRun
3071
- ? `Prepared ${fixActions.length} fix(es) (dry-run)`
3072
- : `Applied ${fixActions.length} fix(es)`
3073
- : "No safe auto-fixes needed",
3074
- });
3075
- }
3076
- if (!storage || storage.accounts.length === 0) {
3077
- addCheck({
3078
- key: "accounts",
3079
- severity: "warn",
3080
- message: "No accounts configured",
3081
- });
3082
- }
3083
- else {
3084
- addCheck({
3085
- key: "accounts",
3086
- severity: "ok",
3087
- message: `Loaded ${storage.accounts.length} account(s)`,
3088
- });
3089
- const activeIndex = resolveActiveIndex(storage, "codex");
3090
- const activeExists = activeIndex >= 0 && activeIndex < storage.accounts.length;
3091
- addCheck({
3092
- key: "active-index",
3093
- severity: activeExists ? "ok" : "error",
3094
- message: activeExists
3095
- ? `Active index is valid (${activeIndex + 1})`
3096
- : "Active index is out of range",
3097
- });
3098
- const disabledCount = storage.accounts.filter((a) => a.enabled === false).length;
3099
- addCheck({
3100
- key: "enabled-accounts",
3101
- severity: disabledCount >= storage.accounts.length ? "error" : "ok",
3102
- message: disabledCount >= storage.accounts.length
3103
- ? "All accounts are disabled"
3104
- : `${storage.accounts.length - disabledCount} enabled / ${disabledCount} disabled`,
3105
- });
3106
- const seenRefreshTokens = new Set();
3107
- let duplicateTokenCount = 0;
3108
- for (const account of storage.accounts) {
3109
- const token = getDoctorRefreshTokenKey(account.refreshToken);
3110
- if (!token)
3111
- continue;
3112
- if (seenRefreshTokens.has(token)) {
3113
- duplicateTokenCount += 1;
3114
- }
3115
- else {
3116
- seenRefreshTokens.add(token);
3117
- }
3118
- }
3119
- addCheck({
3120
- key: "duplicate-refresh-token",
3121
- severity: duplicateTokenCount > 0 ? "warn" : "ok",
3122
- message: duplicateTokenCount > 0
3123
- ? `Detected ${duplicateTokenCount} duplicate refresh token entr${duplicateTokenCount === 1 ? "y" : "ies"}`
3124
- : "No duplicate refresh tokens detected",
3125
- });
3126
- const seenEmails = new Set();
3127
- let duplicateEmailCount = 0;
3128
- let placeholderEmailCount = 0;
3129
- let likelyInvalidRefreshTokenCount = 0;
3130
- for (const account of storage.accounts) {
3131
- const email = sanitizeEmail(account.email);
3132
- if (!email)
3133
- continue;
3134
- if (seenEmails.has(email))
3135
- duplicateEmailCount += 1;
3136
- seenEmails.add(email);
3137
- if (hasPlaceholderEmail(email))
3138
- placeholderEmailCount += 1;
3139
- if (hasLikelyInvalidRefreshToken(account.refreshToken)) {
3140
- likelyInvalidRefreshTokenCount += 1;
3141
- }
3142
- }
3143
- addCheck({
3144
- key: "duplicate-email",
3145
- severity: duplicateEmailCount > 0 ? "warn" : "ok",
3146
- message: duplicateEmailCount > 0
3147
- ? `Detected ${duplicateEmailCount} duplicate email entr${duplicateEmailCount === 1 ? "y" : "ies"}`
3148
- : "No duplicate emails detected",
3149
- });
3150
- addCheck({
3151
- key: "placeholder-email",
3152
- severity: placeholderEmailCount > 0 ? "warn" : "ok",
3153
- message: placeholderEmailCount > 0
3154
- ? `${placeholderEmailCount} account(s) appear to be placeholder/demo entries`
3155
- : "No placeholder emails detected",
3156
- });
3157
- addCheck({
3158
- key: "refresh-token-shape",
3159
- severity: likelyInvalidRefreshTokenCount > 0 ? "warn" : "ok",
3160
- message: likelyInvalidRefreshTokenCount > 0
3161
- ? `${likelyInvalidRefreshTokenCount} account(s) have likely invalid refresh token format`
3162
- : "Refresh token format looks normal",
3163
- });
3164
- const now = Date.now();
3165
- const forecastResults = evaluateForecastAccounts(storage.accounts.map((account, index) => ({
3166
- index,
3167
- account,
3168
- isCurrent: index === activeIndex,
3169
- now,
3170
- })));
3171
- const recommendation = recommendForecastAccount(forecastResults);
3172
- if (recommendation.recommendedIndex !== null && recommendation.recommendedIndex !== activeIndex) {
3173
- addCheck({
3174
- key: "recommended-switch",
3175
- severity: "warn",
3176
- message: `A healthier account is available: switch to ${recommendation.recommendedIndex + 1}`,
3177
- details: recommendation.reason,
3178
- });
3179
- }
3180
- else {
3181
- addCheck({
3182
- key: "recommended-switch",
3183
- severity: "ok",
3184
- message: "Current account aligns with forecast recommendation",
3185
- });
3186
- }
3187
- if (activeExists) {
3188
- const activeAccount = storage.accounts[activeIndex];
3189
- const managerActiveEmail = sanitizeEmail(activeAccount?.email);
3190
- const managerActiveAccountId = activeAccount?.accountId;
3191
- const codexActiveEmail = sanitizeEmail(codexCliState?.activeEmail) ?? codexAuthEmail;
3192
- const codexActiveAccountId = codexCliState?.activeAccountId ?? codexAuthAccountId;
3193
- const isEmailMismatch = !!managerActiveEmail &&
3194
- !!codexActiveEmail &&
3195
- managerActiveEmail !== codexActiveEmail;
3196
- const isAccountIdMismatch = !!managerActiveAccountId &&
3197
- !!codexActiveAccountId &&
3198
- managerActiveAccountId !== codexActiveAccountId;
3199
- addCheck({
3200
- key: "active-selection-sync",
3201
- severity: isEmailMismatch || isAccountIdMismatch ? "warn" : "ok",
3202
- message: isEmailMismatch || isAccountIdMismatch
3203
- ? "Manager active account and Codex active account are not aligned"
3204
- : "Manager active account and Codex active account are aligned",
3205
- details: `manager=${managerActiveEmail ?? managerActiveAccountId ?? "unknown"} | codex=${codexActiveEmail ?? codexActiveAccountId ?? "unknown"}`,
3206
- });
3207
- if (options.fix && activeAccount) {
3208
- let syncAccessToken = activeAccount.accessToken;
3209
- let syncRefreshToken = activeAccount.refreshToken;
3210
- let syncExpiresAt = activeAccount.expiresAt;
3211
- let syncIdToken;
3212
- let storageChangedFromDoctorSync = false;
3213
- if (!hasUsableAccessToken(activeAccount, now)) {
3214
- if (options.dryRun) {
3215
- fixActions.push({
3216
- key: "doctor-refresh",
3217
- message: `Prepared active-account token refresh for account ${activeIndex + 1} (dry-run)`,
3218
- });
3219
- }
3220
- else {
3221
- const refreshResult = await queuedRefresh(activeAccount.refreshToken);
3222
- if (refreshResult.type === "success") {
3223
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
3224
- const refreshedAccountId = extractAccountId(refreshResult.access);
3225
- activeAccount.accessToken = refreshResult.access;
3226
- activeAccount.refreshToken = refreshResult.refresh;
3227
- activeAccount.expiresAt = refreshResult.expires;
3228
- if (refreshedEmail)
3229
- activeAccount.email = refreshedEmail;
3230
- applyTokenAccountIdentity(activeAccount, refreshedAccountId);
3231
- syncAccessToken = refreshResult.access;
3232
- syncRefreshToken = refreshResult.refresh;
3233
- syncExpiresAt = refreshResult.expires;
3234
- syncIdToken = refreshResult.idToken;
3235
- storageChangedFromDoctorSync = true;
3236
- fixActions.push({
3237
- key: "doctor-refresh",
3238
- message: `Refreshed active account tokens for account ${activeIndex + 1}`,
3239
- });
3240
- }
3241
- else {
3242
- addCheck({
3243
- key: "doctor-refresh",
3244
- severity: "warn",
3245
- message: "Unable to refresh active account before Codex sync",
3246
- details: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
3247
- });
3248
- }
3249
- }
3250
- }
3251
- if (storageChangedFromDoctorSync) {
3252
- fixChanged = true;
3253
- if (!options.dryRun) {
3254
- await saveAccounts(storage);
3255
- }
3256
- }
3257
- if (!options.dryRun) {
3258
- const synced = await setCodexCliActiveSelection({
3259
- accountId: activeAccount.accountId,
3260
- email: activeAccount.email,
3261
- accessToken: syncAccessToken,
3262
- refreshToken: syncRefreshToken,
3263
- expiresAt: syncExpiresAt,
3264
- ...(syncIdToken ? { idToken: syncIdToken } : {}),
3265
- });
3266
- if (synced) {
3267
- fixChanged = true;
3268
- fixActions.push({
3269
- key: "codex-active-sync",
3270
- message: "Synced manager active account into Codex auth state",
3271
- });
3272
- }
3273
- else {
3274
- addCheck({
3275
- key: "codex-active-sync",
3276
- severity: "warn",
3277
- message: "Failed to sync manager active account into Codex auth state",
3278
- });
3279
- }
3280
- }
3281
- else {
3282
- fixActions.push({
3283
- key: "codex-active-sync",
3284
- message: "Prepared Codex active-account sync (dry-run)",
3285
- });
3286
- }
3287
- }
3288
- }
1869
+ return fallbackIndex >= 0 && fallbackIndex < storage.accounts.length
1870
+ ? fallbackIndex
1871
+ : null;
1872
+ }
1873
+ function matchesManageActionAccount(account, candidate) {
1874
+ if (!account || !candidate) {
1875
+ return false;
3289
1876
  }
3290
- const summary = checks.reduce((acc, check) => {
3291
- acc[check.severity] += 1;
3292
- return acc;
3293
- }, { ok: 0, warn: 0, error: 0 });
3294
- if (options.json) {
3295
- console.log(JSON.stringify({
3296
- command: "doctor",
3297
- storagePath,
3298
- summary,
3299
- checks,
3300
- fix: {
3301
- enabled: options.fix,
3302
- dryRun: options.dryRun,
3303
- changed: fixChanged,
3304
- actions: fixActions,
3305
- },
3306
- }, null, 2));
3307
- return summary.error > 0 ? 1 : 0;
3308
- }
3309
- console.log("Doctor diagnostics");
3310
- console.log(`Storage: ${storagePath}`);
3311
- console.log(`Summary: ${summary.ok} ok, ${summary.warn} warnings, ${summary.error} errors`);
3312
- console.log("");
3313
- for (const check of checks) {
3314
- const marker = check.severity === "ok" ? "✓" : check.severity === "warn" ? "!" : "✗";
3315
- console.log(`${marker} ${check.key}: ${check.message}`);
3316
- if (check.details) {
3317
- console.log(` ${check.details}`);
3318
- }
3319
- }
3320
- if (options.fix) {
3321
- console.log("");
3322
- if (fixActions.length > 0) {
3323
- console.log(`Auto-fix actions (${options.dryRun ? "dry-run" : "applied"}):`);
3324
- for (const action of fixActions) {
3325
- console.log(` - ${action.message}`);
3326
- }
3327
- }
3328
- else {
3329
- console.log("Auto-fix actions: none");
3330
- }
1877
+ if (account.accountId || candidate.accountId) {
1878
+ return account.accountId === candidate.accountId;
3331
1879
  }
3332
- return summary.error > 0 ? 1 : 0;
3333
- }
3334
- async function clearAccountsAndReset() {
3335
- await clearAccounts();
1880
+ return (account.refreshToken === candidate.refreshToken
1881
+ && sanitizeEmail(account.email) === sanitizeEmail(candidate.email));
3336
1882
  }
3337
1883
  async function handleManageAction(storage, menuResult) {
3338
1884
  if (typeof menuResult.switchAccountIndex === "number") {
3339
1885
  const index = menuResult.switchAccountIndex;
3340
- await runSwitch([String(index + 1)]);
1886
+ await runSwitchCommand([String(index + 1)], {
1887
+ setStoragePath,
1888
+ loadAccounts,
1889
+ persistAndSyncSelectedAccount,
1890
+ });
3341
1891
  return;
3342
1892
  }
3343
1893
  if (typeof menuResult.deleteAccountIndex === "number") {
3344
1894
  const idx = menuResult.deleteAccountIndex;
3345
- if (idx >= 0 && idx < storage.accounts.length) {
3346
- storage.accounts.splice(idx, 1);
3347
- storage.activeIndex = 0;
3348
- storage.activeIndexByFamily = {};
3349
- for (const family of MODEL_FAMILIES) {
3350
- storage.activeIndexByFamily[family] = 0;
3351
- }
3352
- await saveAccounts(storage);
1895
+ const selectedAccount = storage.accounts[idx];
1896
+ let deleted = false;
1897
+ if (selectedAccount) {
1898
+ await withAccountStorageTransaction(async (loadedStorage, persist) => {
1899
+ const nextStorage = loadedStorage
1900
+ ? structuredClone(loadedStorage)
1901
+ : structuredClone(storage);
1902
+ const nextIndex = resolveManageActionAccountIndex(nextStorage, idx, selectedAccount);
1903
+ if (nextIndex === null) {
1904
+ return;
1905
+ }
1906
+ const nextAccount = nextStorage.accounts[nextIndex];
1907
+ if (!matchesManageActionAccount(selectedAccount, nextAccount)) {
1908
+ return;
1909
+ }
1910
+ nextStorage.accounts.splice(nextIndex, 1);
1911
+ resetManageActionSelection(nextStorage, nextIndex);
1912
+ await persist(nextStorage);
1913
+ replaceManageActionStorage(storage, nextStorage);
1914
+ deleted = true;
1915
+ });
1916
+ }
1917
+ if (deleted) {
3353
1918
  console.log(`Deleted account ${idx + 1}.`);
3354
1919
  }
3355
1920
  return;
3356
1921
  }
3357
1922
  if (typeof menuResult.toggleAccountIndex === "number") {
3358
1923
  const idx = menuResult.toggleAccountIndex;
3359
- const account = storage.accounts[idx];
3360
- if (account) {
3361
- account.enabled = account.enabled === false;
3362
- await saveAccounts(storage);
3363
- console.log(`${account.enabled === false ? "Disabled" : "Enabled"} account ${idx + 1}.`);
1924
+ const selectedAccount = storage.accounts[idx];
1925
+ let nextEnabledState = null;
1926
+ if (selectedAccount) {
1927
+ await withAccountStorageTransaction(async (loadedStorage, persist) => {
1928
+ const nextStorage = loadedStorage
1929
+ ? structuredClone(loadedStorage)
1930
+ : structuredClone(storage);
1931
+ const nextIndex = resolveManageActionAccountIndex(nextStorage, idx, selectedAccount);
1932
+ if (nextIndex === null) {
1933
+ return;
1934
+ }
1935
+ const nextAccount = nextStorage.accounts[nextIndex];
1936
+ if (!nextAccount || !matchesManageActionAccount(selectedAccount, nextAccount)) {
1937
+ return;
1938
+ }
1939
+ nextAccount.enabled = nextAccount.enabled === false;
1940
+ await persist(nextStorage);
1941
+ replaceManageActionStorage(storage, nextStorage);
1942
+ nextEnabledState = nextAccount.enabled !== false;
1943
+ });
1944
+ }
1945
+ if (nextEnabledState !== null) {
1946
+ console.log(`${nextEnabledState ? "Enabled" : "Disabled"} account ${idx + 1}.`);
3364
1947
  }
3365
1948
  return;
3366
1949
  }
@@ -3369,7 +1952,15 @@ async function handleManageAction(storage, menuResult) {
3369
1952
  const existing = storage.accounts[idx];
3370
1953
  if (!existing)
3371
1954
  return;
3372
- const tokenResult = await runOAuthFlow(true);
1955
+ const signInMode = await promptOAuthSignInMode(null);
1956
+ if (signInMode === "cancel") {
1957
+ console.log(stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
1958
+ return;
1959
+ }
1960
+ if (signInMode !== "browser" && signInMode !== "manual") {
1961
+ return;
1962
+ }
1963
+ const tokenResult = await runOAuthFlow(true, signInMode);
3373
1964
  if (tokenResult.type !== "success") {
3374
1965
  console.error(`Refresh failed: ${tokenResult.message ?? tokenResult.reason ?? "unknown error"}`);
3375
1966
  return;
@@ -3380,7 +1971,17 @@ async function handleManageAction(storage, menuResult) {
3380
1971
  console.log(`Refreshed account ${idx + 1}.`);
3381
1972
  }
3382
1973
  }
3383
- async function runAuthLogin() {
1974
+ async function runAuthLogin(args) {
1975
+ const parsedArgs = parseAuthLoginArgs(args);
1976
+ if (!parsedArgs.ok) {
1977
+ if (parsedArgs.reason === "error") {
1978
+ console.error(parsedArgs.message);
1979
+ printUsage();
1980
+ return 1;
1981
+ }
1982
+ return 0;
1983
+ }
1984
+ const loginOptions = parsedArgs.options;
3384
1985
  setStoragePath(null);
3385
1986
  let pendingMenuQuotaRefresh = null;
3386
1987
  let menuQuotaRefreshStatus;
@@ -3421,7 +2022,9 @@ async function runAuthLogin() {
3421
2022
  const flaggedStorage = await loadFlaggedAccounts();
3422
2023
  const menuResult = await promptLoginMode(toExistingAccountInfo(currentStorage, quotaCache, displaySettings), {
3423
2024
  flaggedCount: flaggedStorage.accounts.length,
3424
- statusMessage: showFetchStatus ? () => menuQuotaRefreshStatus : undefined,
2025
+ statusMessage: showFetchStatus
2026
+ ? () => menuQuotaRefreshStatus
2027
+ : undefined,
3425
2028
  });
3426
2029
  if (menuResult.mode === "cancel") {
3427
2030
  console.log("Cancelled.");
@@ -3447,7 +2050,7 @@ async function runAuthLogin() {
3447
2050
  }
3448
2051
  if (menuResult.mode === "fix") {
3449
2052
  await runActionPanel("Auto-Fix", "Checking and fixing common issues", async () => {
3450
- await runFix(["--live"]);
2053
+ await runRepairFix(["--live"], createRepairCommandDeps());
3451
2054
  }, displaySettings);
3452
2055
  continue;
3453
2056
  }
@@ -3457,7 +2060,7 @@ async function runAuthLogin() {
3457
2060
  }
3458
2061
  if (menuResult.mode === "verify-flagged") {
3459
2062
  await runActionPanel("Problem Account Check", "Checking problem accounts", async () => {
3460
- await runVerifyFlagged([]);
2063
+ await runRepairVerifyFlagged([], createRepairCommandDeps());
3461
2064
  }, displaySettings);
3462
2065
  continue;
3463
2066
  }
@@ -3485,12 +2088,115 @@ async function runAuthLogin() {
3485
2088
  }
3486
2089
  }
3487
2090
  const refreshedStorage = await loadAccounts();
3488
- const existingCount = refreshedStorage?.accounts.length ?? 0;
2091
+ let existingCount = refreshedStorage?.accounts.length ?? 0;
3489
2092
  let forceNewLogin = existingCount > 0;
2093
+ let onboardingBackupDiscoveryWarning = null;
2094
+ const loadNamedBackupsForOnboarding = async () => {
2095
+ if (existingCount > 0) {
2096
+ onboardingBackupDiscoveryWarning = null;
2097
+ return [];
2098
+ }
2099
+ try {
2100
+ onboardingBackupDiscoveryWarning = null;
2101
+ return await getNamedBackups();
2102
+ }
2103
+ catch (error) {
2104
+ const code = error.code;
2105
+ log.debug("getNamedBackups failed, skipping restore option", {
2106
+ code,
2107
+ error: error instanceof Error ? error.message : String(error),
2108
+ });
2109
+ if (code && code !== "ENOENT") {
2110
+ onboardingBackupDiscoveryWarning =
2111
+ "Named backup discovery failed. Continuing with browser or manual sign-in only.";
2112
+ console.warn(onboardingBackupDiscoveryWarning);
2113
+ }
2114
+ else {
2115
+ onboardingBackupDiscoveryWarning = null;
2116
+ }
2117
+ return [];
2118
+ }
2119
+ };
2120
+ let namedBackups = await loadNamedBackupsForOnboarding();
3490
2121
  while (true) {
3491
- const tokenResult = await runOAuthFlow(forceNewLogin);
2122
+ const latestNamedBackup = namedBackups[0] ?? null;
2123
+ const preferManualMode = loginOptions.manual || isBrowserLaunchSuppressed();
2124
+ const signInMode = preferManualMode
2125
+ ? "manual"
2126
+ : await promptOAuthSignInMode(latestNamedBackup, onboardingBackupDiscoveryWarning);
2127
+ if (signInMode === "cancel") {
2128
+ if (existingCount > 0) {
2129
+ console.log(stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
2130
+ continue loginFlow;
2131
+ }
2132
+ console.log("Cancelled.");
2133
+ return 0;
2134
+ }
2135
+ if (signInMode === "restore-backup") {
2136
+ const latestAvailableBackup = namedBackups[0] ?? null;
2137
+ if (!latestAvailableBackup) {
2138
+ namedBackups = await loadNamedBackupsForOnboarding();
2139
+ continue;
2140
+ }
2141
+ const restoreMode = await promptBackupRestoreMode(latestAvailableBackup);
2142
+ if (restoreMode === "back") {
2143
+ namedBackups = await loadNamedBackupsForOnboarding();
2144
+ continue;
2145
+ }
2146
+ const selectedBackup = restoreMode === "manual"
2147
+ ? await promptManualBackupSelection(namedBackups)
2148
+ : latestAvailableBackup;
2149
+ if (!selectedBackup) {
2150
+ namedBackups = await loadNamedBackupsForOnboarding();
2151
+ continue;
2152
+ }
2153
+ const confirmed = await confirm(UI_COPY.oauth.restoreBackupConfirm(selectedBackup.fileName, selectedBackup.accountCount));
2154
+ if (!confirmed) {
2155
+ namedBackups = await loadNamedBackupsForOnboarding();
2156
+ continue;
2157
+ }
2158
+ const displaySettings = await loadDashboardDisplaySettings();
2159
+ applyUiThemeFromDashboardSettings(displaySettings);
2160
+ try {
2161
+ await runActionPanel("Load Backup", `Loading ${selectedBackup.fileName}`, async () => {
2162
+ const restoredStorage = await restoreAccountsFromBackup(selectedBackup.path, { persist: false });
2163
+ const targetIndex = resolveActiveIndex(restoredStorage);
2164
+ const { synced } = await persistAndSyncSelectedAccount({
2165
+ storage: restoredStorage,
2166
+ targetIndex,
2167
+ parsed: targetIndex + 1,
2168
+ switchReason: "restore",
2169
+ preserveActiveIndexByFamily: true,
2170
+ });
2171
+ console.log(UI_COPY.oauth.restoreBackupLoaded(selectedBackup.fileName, restoredStorage.accounts.length));
2172
+ if (!synced) {
2173
+ console.warn(UI_COPY.oauth.restoreBackupSyncWarning);
2174
+ }
2175
+ }, displaySettings);
2176
+ }
2177
+ catch (error) {
2178
+ const message = error instanceof Error ? error.message : String(error);
2179
+ if (error instanceof StorageError) {
2180
+ console.error(formatStorageErrorHint(error, selectedBackup.path));
2181
+ }
2182
+ else {
2183
+ console.error(`Backup restore failed: ${message}`);
2184
+ }
2185
+ const storageAfterRestoreAttempt = await loadAccounts().catch(() => null);
2186
+ if ((storageAfterRestoreAttempt?.accounts.length ?? 0) > 0) {
2187
+ continue loginFlow;
2188
+ }
2189
+ namedBackups = await loadNamedBackupsForOnboarding();
2190
+ continue;
2191
+ }
2192
+ continue loginFlow;
2193
+ }
2194
+ if (signInMode !== "browser" && signInMode !== "manual") {
2195
+ continue;
2196
+ }
2197
+ const tokenResult = await runOAuthFlow(forceNewLogin, signInMode);
3492
2198
  if (tokenResult.type !== "success") {
3493
- if (isUserCancelledOAuth(tokenResult)) {
2199
+ if (isOAuthCancellation(tokenResult)) {
3494
2200
  if (existingCount > 0) {
3495
2201
  console.log(stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
3496
2202
  continue loginFlow;
@@ -3506,7 +2212,14 @@ async function runAuthLogin() {
3506
2212
  await syncSelectionToCodex(resolved);
3507
2213
  const latestStorage = await loadAccounts();
3508
2214
  const count = latestStorage?.accounts.length ?? 1;
2215
+ existingCount = count;
2216
+ namedBackups = [];
2217
+ onboardingBackupDiscoveryWarning = null;
3509
2218
  console.log(`Added account. Total: ${count}`);
2219
+ console.log("Next steps:");
2220
+ console.log(" codex auth status Check that the wrapper is active.");
2221
+ console.log(" codex auth check Confirm your saved accounts look healthy.");
2222
+ console.log(" codex auth list Review saved accounts before switching.");
3510
2223
  if (count >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
3511
2224
  console.log(`Reached maximum account limit (${ACCOUNT_LIMITS.MAX_ACCOUNTS}).`);
3512
2225
  break;
@@ -3516,57 +2229,31 @@ async function runAuthLogin() {
3516
2229
  break;
3517
2230
  forceNewLogin = true;
3518
2231
  }
3519
- continue loginFlow;
3520
- }
3521
- }
3522
- async function runSwitch(args) {
3523
- setStoragePath(null);
3524
- const indexArg = args[0];
3525
- if (!indexArg) {
3526
- console.error("Missing index. Usage: codex auth switch <index>");
3527
- return 1;
3528
- }
3529
- const parsed = Number.parseInt(indexArg, 10);
3530
- if (!Number.isFinite(parsed) || parsed < 1) {
3531
- console.error(`Invalid index: ${indexArg}`);
3532
- return 1;
3533
- }
3534
- const targetIndex = parsed - 1;
3535
- const storage = await loadAccounts();
3536
- if (!storage || storage.accounts.length === 0) {
3537
- console.error("No accounts configured.");
3538
- return 1;
3539
- }
3540
- if (targetIndex < 0 || targetIndex >= storage.accounts.length) {
3541
- console.error(`Index out of range. Valid range: 1-${storage.accounts.length}`);
3542
- return 1;
3543
- }
3544
- const account = storage.accounts[targetIndex];
3545
- if (!account) {
3546
- console.error(`Account ${parsed} not found.`);
3547
- return 1;
3548
- }
3549
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
3550
- storage,
3551
- targetIndex,
3552
- parsed,
3553
- switchReason: "rotation",
3554
- });
3555
- if (!synced) {
3556
- console.warn(`Switched account ${parsed} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`);
3557
2232
  }
3558
- console.log(`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
3559
- return 0;
3560
2233
  }
3561
- async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, switchReason, initialSyncIdToken, }) {
2234
+ async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, switchReason, initialSyncIdToken, preserveActiveIndexByFamily = false, }) {
3562
2235
  const account = storage.accounts[targetIndex];
3563
2236
  if (!account) {
3564
2237
  throw new Error(`Account ${parsed} not found.`);
3565
2238
  }
2239
+ const shouldPreserveActiveIndexByFamily = preserveActiveIndexByFamily &&
2240
+ !!storage.activeIndexByFamily &&
2241
+ targetIndex === storage.activeIndex;
3566
2242
  storage.activeIndex = targetIndex;
3567
2243
  storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
3568
- for (const family of MODEL_FAMILIES) {
3569
- storage.activeIndexByFamily[family] = targetIndex;
2244
+ if (shouldPreserveActiveIndexByFamily) {
2245
+ const maxIndex = Math.max(0, storage.accounts.length - 1);
2246
+ for (const family of MODEL_FAMILIES) {
2247
+ const raw = storage.activeIndexByFamily[family];
2248
+ const candidate = typeof raw === "number" && Number.isFinite(raw) ? raw : targetIndex;
2249
+ storage.activeIndexByFamily[family] = Math.max(0, Math.min(candidate, maxIndex));
2250
+ }
2251
+ }
2252
+ else {
2253
+ storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
2254
+ for (const family of MODEL_FAMILIES) {
2255
+ storage.activeIndexByFamily[family] = targetIndex;
2256
+ }
3570
2257
  }
3571
2258
  const wasDisabled = account.enabled === false;
3572
2259
  if (wasDisabled) {
@@ -3618,222 +2305,26 @@ async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, swi
3618
2305
  return { synced, wasDisabled };
3619
2306
  }
3620
2307
  async function runBest(args) {
3621
- if (args.includes("--help") || args.includes("-h")) {
3622
- printBestUsage();
3623
- return 0;
3624
- }
3625
- const parsedArgs = parseBestArgs(args);
3626
- if (!parsedArgs.ok) {
3627
- console.error(parsedArgs.message);
3628
- printBestUsage();
3629
- return 1;
3630
- }
3631
- const options = parsedArgs.options;
3632
- if (options.modelProvided && !options.live) {
3633
- console.error("--model requires --live for codex auth best");
3634
- printBestUsage();
3635
- return 1;
3636
- }
3637
- setStoragePath(null);
3638
- const storage = await loadAccounts();
3639
- if (!storage || storage.accounts.length === 0) {
3640
- if (options.json) {
3641
- console.log(JSON.stringify({ error: "No accounts configured." }, null, 2));
3642
- }
3643
- else {
3644
- console.log("No accounts configured.");
3645
- }
3646
- return 1;
3647
- }
3648
- const now = Date.now();
3649
- const refreshFailures = new Map();
3650
- const liveQuotaByIndex = new Map();
3651
- const probeIdTokenByIndex = new Map();
3652
- const probeRefreshedIndices = new Set();
3653
- const probeErrors = [];
3654
- let changed = false;
3655
- const printProbeNotes = () => {
3656
- if (probeErrors.length === 0)
3657
- return;
3658
- console.log(`Live check notes (${probeErrors.length}):`);
3659
- for (const error of probeErrors) {
3660
- console.log(` - ${error}`);
3661
- }
3662
- };
3663
- const persistProbeChangesIfNeeded = async () => {
3664
- if (!changed)
3665
- return;
3666
- await saveAccounts(storage);
3667
- changed = false;
3668
- };
3669
- for (let i = 0; i < storage.accounts.length; i += 1) {
3670
- const account = storage.accounts[i];
3671
- if (!account || !options.live)
3672
- continue;
3673
- if (account.enabled === false)
3674
- continue;
3675
- let probeAccessToken = account.accessToken;
3676
- let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
3677
- if (!hasUsableAccessToken(account, now)) {
3678
- const refreshResult = await queuedRefresh(account.refreshToken);
3679
- if (refreshResult.type !== "success") {
3680
- refreshFailures.set(i, {
3681
- ...refreshResult,
3682
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
3683
- });
3684
- continue;
3685
- }
3686
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
3687
- const refreshedAccountId = extractAccountId(refreshResult.access);
3688
- if (account.refreshToken !== refreshResult.refresh) {
3689
- account.refreshToken = refreshResult.refresh;
3690
- changed = true;
3691
- }
3692
- if (account.accessToken !== refreshResult.access) {
3693
- account.accessToken = refreshResult.access;
3694
- changed = true;
3695
- }
3696
- if (account.expiresAt !== refreshResult.expires) {
3697
- account.expiresAt = refreshResult.expires;
3698
- changed = true;
3699
- }
3700
- if (refreshedEmail && refreshedEmail !== account.email) {
3701
- account.email = refreshedEmail;
3702
- changed = true;
3703
- }
3704
- if (refreshedAccountId && refreshedAccountId !== account.accountId) {
3705
- account.accountId = refreshedAccountId;
3706
- account.accountIdSource = "token";
3707
- changed = true;
3708
- }
3709
- if (refreshResult.idToken) {
3710
- probeIdTokenByIndex.set(i, refreshResult.idToken);
3711
- }
3712
- probeRefreshedIndices.add(i);
3713
- probeAccessToken = account.accessToken;
3714
- probeAccountId = account.accountId ?? refreshedAccountId;
3715
- }
3716
- if (!probeAccessToken || !probeAccountId) {
3717
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
3718
- continue;
3719
- }
3720
- try {
3721
- const liveQuota = await fetchCodexQuotaSnapshot({
3722
- accountId: probeAccountId,
3723
- accessToken: probeAccessToken,
3724
- model: options.model,
3725
- });
3726
- liveQuotaByIndex.set(i, liveQuota);
3727
- }
3728
- catch (error) {
3729
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
3730
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
3731
- }
3732
- }
3733
- const forecastInputs = storage.accounts.map((account, index) => ({
3734
- index,
3735
- account,
3736
- isCurrent: index === resolveActiveIndex(storage, "codex"),
3737
- now,
3738
- refreshFailure: refreshFailures.get(index),
3739
- liveQuota: liveQuotaByIndex.get(index),
3740
- }));
3741
- const forecastResults = evaluateForecastAccounts(forecastInputs);
3742
- const recommendation = recommendForecastAccount(forecastResults);
3743
- if (recommendation.recommendedIndex === null) {
3744
- await persistProbeChangesIfNeeded();
3745
- if (options.json) {
3746
- console.log(JSON.stringify({
3747
- error: recommendation.reason,
3748
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
3749
- }, null, 2));
3750
- }
3751
- else {
3752
- console.log(`No best account available: ${recommendation.reason}`);
3753
- printProbeNotes();
3754
- }
3755
- return 1;
3756
- }
3757
- const bestIndex = recommendation.recommendedIndex;
3758
- const bestAccount = storage.accounts[bestIndex];
3759
- if (!bestAccount) {
3760
- await persistProbeChangesIfNeeded();
3761
- if (options.json) {
3762
- console.log(JSON.stringify({ error: "Best account not found." }, null, 2));
3763
- }
3764
- else {
3765
- console.log("Best account not found.");
3766
- }
3767
- return 1;
3768
- }
3769
- // Check if already on best account
3770
- const currentIndex = resolveActiveIndex(storage, "codex");
3771
- if (currentIndex === bestIndex) {
3772
- const shouldSyncCurrentBest = probeRefreshedIndices.has(bestIndex) || probeIdTokenByIndex.has(bestIndex);
3773
- let alreadyBestSynced;
3774
- if (changed) {
3775
- bestAccount.lastUsed = now;
3776
- await persistProbeChangesIfNeeded();
3777
- }
3778
- if (shouldSyncCurrentBest) {
3779
- alreadyBestSynced = await setCodexCliActiveSelection({
3780
- accountId: bestAccount.accountId,
3781
- email: bestAccount.email,
3782
- accessToken: bestAccount.accessToken,
3783
- refreshToken: bestAccount.refreshToken,
3784
- expiresAt: bestAccount.expiresAt,
3785
- ...(probeIdTokenByIndex.has(bestIndex)
3786
- ? { idToken: probeIdTokenByIndex.get(bestIndex) }
3787
- : {}),
3788
- });
3789
- if (!alreadyBestSynced && !options.json) {
3790
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
3791
- }
3792
- }
3793
- if (options.json) {
3794
- console.log(JSON.stringify({
3795
- message: `Already on best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
3796
- accountIndex: bestIndex + 1,
3797
- reason: recommendation.reason,
3798
- ...(alreadyBestSynced !== undefined ? { synced: alreadyBestSynced } : {}),
3799
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
3800
- }, null, 2));
3801
- }
3802
- else {
3803
- console.log(`Already on best account ${bestIndex + 1}: ${formatAccountLabel(bestAccount, bestIndex)}`);
3804
- console.log(`Reason: ${recommendation.reason}`);
3805
- printProbeNotes();
3806
- }
3807
- return 0;
3808
- }
3809
- const targetIndex = bestIndex;
3810
- const parsed = targetIndex + 1;
3811
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
3812
- storage,
3813
- targetIndex,
3814
- parsed,
3815
- switchReason: "best",
3816
- initialSyncIdToken: probeIdTokenByIndex.get(bestIndex),
2308
+ return runBestCommand(args, {
2309
+ setStoragePath,
2310
+ loadAccounts,
2311
+ saveAccounts,
2312
+ parseBestArgs,
2313
+ printBestUsage,
2314
+ resolveActiveIndex,
2315
+ hasUsableAccessToken,
2316
+ queuedRefresh,
2317
+ normalizeFailureDetail,
2318
+ extractAccountId,
2319
+ extractAccountEmail,
2320
+ sanitizeEmail,
2321
+ formatAccountLabel,
2322
+ fetchCodexQuotaSnapshot,
2323
+ evaluateForecastAccounts,
2324
+ recommendForecastAccount,
2325
+ persistAndSyncSelectedAccount,
2326
+ setCodexCliActiveSelection,
3817
2327
  });
3818
- if (options.json) {
3819
- console.log(JSON.stringify({
3820
- message: `Switched to best account: ${formatAccountLabel(bestAccount, targetIndex)}`,
3821
- accountIndex: parsed,
3822
- reason: recommendation.reason,
3823
- synced,
3824
- wasDisabled,
3825
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
3826
- }, null, 2));
3827
- }
3828
- else {
3829
- console.log(`Switched to best account ${parsed}: ${formatAccountLabel(bestAccount, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
3830
- console.log(`Reason: ${recommendation.reason}`);
3831
- printProbeNotes();
3832
- if (!synced) {
3833
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
3834
- }
3835
- }
3836
- return 0;
3837
2328
  }
3838
2329
  export async function autoSyncActiveAccountToCodex() {
3839
2330
  setStoragePath(null);
@@ -3849,48 +2340,81 @@ export async function autoSyncActiveAccountToCodex() {
3849
2340
  if (!account) {
3850
2341
  return false;
3851
2342
  }
2343
+ const accountMatch = {
2344
+ accountId: account.accountId,
2345
+ email: account.email,
2346
+ refreshToken: account.refreshToken,
2347
+ };
3852
2348
  const now = Date.now();
3853
2349
  let syncAccessToken = account.accessToken;
3854
2350
  let syncRefreshToken = account.refreshToken;
3855
2351
  let syncExpiresAt = account.expiresAt;
3856
2352
  let syncIdToken;
2353
+ let syncAccountId = account.accountId;
2354
+ let syncEmail = account.email;
3857
2355
  let changed = false;
2356
+ let nextStoredAccount = null;
3858
2357
  if (!hasUsableAccessToken(account, now)) {
3859
2358
  const refreshResult = await queuedRefresh(account.refreshToken);
3860
- if (refreshResult.type === "success") {
3861
- const tokenAccountId = extractAccountId(refreshResult.access);
3862
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
3863
- if (account.refreshToken !== refreshResult.refresh) {
3864
- account.refreshToken = refreshResult.refresh;
3865
- changed = true;
3866
- }
3867
- if (account.accessToken !== refreshResult.access) {
3868
- account.accessToken = refreshResult.access;
3869
- changed = true;
3870
- }
3871
- if (account.expiresAt !== refreshResult.expires) {
3872
- account.expiresAt = refreshResult.expires;
3873
- changed = true;
3874
- }
3875
- if (nextEmail && nextEmail !== account.email) {
3876
- account.email = nextEmail;
3877
- changed = true;
2359
+ if (refreshResult.type !== "success") {
2360
+ return false;
2361
+ }
2362
+ nextStoredAccount = structuredClone(account);
2363
+ const tokenAccountId = extractAccountId(refreshResult.access);
2364
+ const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
2365
+ if (nextStoredAccount.refreshToken !== refreshResult.refresh) {
2366
+ nextStoredAccount.refreshToken = refreshResult.refresh;
2367
+ changed = true;
2368
+ }
2369
+ if (nextStoredAccount.accessToken !== refreshResult.access) {
2370
+ nextStoredAccount.accessToken = refreshResult.access;
2371
+ changed = true;
2372
+ }
2373
+ if (nextStoredAccount.expiresAt !== refreshResult.expires) {
2374
+ nextStoredAccount.expiresAt = refreshResult.expires;
2375
+ changed = true;
2376
+ }
2377
+ if (nextEmail && nextEmail !== nextStoredAccount.email) {
2378
+ nextStoredAccount.email = nextEmail;
2379
+ changed = true;
2380
+ }
2381
+ if (applyTokenAccountIdentity(nextStoredAccount, tokenAccountId)) {
2382
+ changed = true;
2383
+ }
2384
+ syncAccessToken = refreshResult.access;
2385
+ syncRefreshToken = refreshResult.refresh;
2386
+ syncExpiresAt = refreshResult.expires;
2387
+ syncIdToken = refreshResult.idToken;
2388
+ syncAccountId = nextStoredAccount.accountId;
2389
+ syncEmail = nextStoredAccount.email;
2390
+ }
2391
+ if (changed && nextStoredAccount) {
2392
+ let persisted = false;
2393
+ await withAccountStorageTransaction(async (loadedStorage, persist) => {
2394
+ if (!loadedStorage) {
2395
+ return;
3878
2396
  }
3879
- if (applyTokenAccountIdentity(account, tokenAccountId)) {
3880
- changed = true;
2397
+ const nextStorage = structuredClone(loadedStorage);
2398
+ const targetIndex = findMatchingAccountIndex(nextStorage.accounts, accountMatch, {
2399
+ allowUniqueAccountIdFallbackWithoutEmail: true,
2400
+ })
2401
+ ?? findMatchingAccountIndex(nextStorage.accounts, nextStoredAccount, {
2402
+ allowUniqueAccountIdFallbackWithoutEmail: true,
2403
+ });
2404
+ if (targetIndex === undefined) {
2405
+ return;
3881
2406
  }
3882
- syncAccessToken = refreshResult.access;
3883
- syncRefreshToken = refreshResult.refresh;
3884
- syncExpiresAt = refreshResult.expires;
3885
- syncIdToken = refreshResult.idToken;
2407
+ nextStorage.accounts[targetIndex] = structuredClone(nextStoredAccount);
2408
+ await persist(nextStorage);
2409
+ persisted = true;
2410
+ });
2411
+ if (!persisted) {
2412
+ return false;
3886
2413
  }
3887
2414
  }
3888
- if (changed) {
3889
- await saveAccounts(storage);
3890
- }
3891
2415
  return setCodexCliActiveSelection({
3892
- accountId: account.accountId,
3893
- email: account.email,
2416
+ accountId: syncAccountId,
2417
+ email: syncEmail,
3894
2418
  accessToken: syncAccessToken,
3895
2419
  refreshToken: syncRefreshToken,
3896
2420
  expiresAt: syncExpiresAt,
@@ -3920,24 +2444,32 @@ export async function runCodexMultiAuthCli(rawArgs) {
3920
2444
  return 0;
3921
2445
  }
3922
2446
  if (command === "login") {
3923
- return runAuthLogin();
2447
+ return runAuthLogin(rest);
3924
2448
  }
3925
2449
  if (command === "list" || command === "status") {
3926
- await showAccountStatus();
3927
- return 0;
2450
+ return runStatusCommand({
2451
+ setStoragePath,
2452
+ getStoragePath,
2453
+ loadAccounts,
2454
+ resolveActiveIndex,
2455
+ formatRateLimitEntry,
2456
+ });
3928
2457
  }
3929
2458
  if (command === "switch") {
3930
- return runSwitch(rest);
2459
+ return runSwitchCommand(rest, {
2460
+ setStoragePath,
2461
+ loadAccounts,
2462
+ persistAndSyncSelectedAccount,
2463
+ });
3931
2464
  }
3932
2465
  if (command === "check") {
3933
- await runHealthCheck({ liveProbe: true });
3934
- return 0;
2466
+ return runCheckCommand({ runHealthCheck });
3935
2467
  }
3936
2468
  if (command === "features") {
3937
- return runFeaturesReport();
2469
+ return runFeaturesCommand({ implementedFeatures: IMPLEMENTED_FEATURES });
3938
2470
  }
3939
2471
  if (command === "verify-flagged") {
3940
- return runVerifyFlagged(rest);
2472
+ return runRepairVerifyFlagged(rest, createRepairCommandDeps());
3941
2473
  }
3942
2474
  if (command === "forecast") {
3943
2475
  return runForecast(rest);
@@ -3946,13 +2478,53 @@ export async function runCodexMultiAuthCli(rawArgs) {
3946
2478
  return runBest(rest);
3947
2479
  }
3948
2480
  if (command === "report") {
3949
- return runReport(rest);
2481
+ return runReportCommand(rest, {
2482
+ setStoragePath,
2483
+ getStoragePath,
2484
+ loadAccounts,
2485
+ resolveActiveIndex,
2486
+ queuedRefresh,
2487
+ fetchCodexQuotaSnapshot,
2488
+ formatRateLimitEntry,
2489
+ normalizeFailureDetail,
2490
+ });
3950
2491
  }
3951
2492
  if (command === "fix") {
3952
- return runFix(rest);
2493
+ return runRepairFix(rest, createRepairCommandDeps());
3953
2494
  }
3954
2495
  if (command === "doctor") {
3955
- return runDoctor(rest);
2496
+ return runRepairDoctor(rest, createRepairCommandDeps());
2497
+ }
2498
+ if (command === "config") {
2499
+ const [subcommand, ...configArgs] = rest;
2500
+ if (subcommand === "explain") {
2501
+ return runConfigExplainCommand(configArgs, {
2502
+ getReport: getPluginConfigExplainReport,
2503
+ });
2504
+ }
2505
+ if (subcommand === "template") {
2506
+ return runInitConfigCommand(configArgs);
2507
+ }
2508
+ console.error(`Unknown config command: ${subcommand ?? "(missing)"}`);
2509
+ return 1;
2510
+ }
2511
+ if (command === "init-config") {
2512
+ return runInitConfigCommand(rest);
2513
+ }
2514
+ if (command === "debug") {
2515
+ const [subcommand, ...debugArgs] = rest;
2516
+ if (subcommand === "bundle") {
2517
+ return runDebugBundleCommand(debugArgs, {
2518
+ getConfigReport: getPluginConfigExplainReport,
2519
+ getStoragePath,
2520
+ loadAccounts,
2521
+ loadFlaggedAccounts,
2522
+ loadCodexCliState,
2523
+ getLastAccountsSaveTimestamp,
2524
+ });
2525
+ }
2526
+ console.error(`Unknown debug command: ${subcommand ?? "(missing)"}`);
2527
+ return 1;
3956
2528
  }
3957
2529
  console.error(`Unknown command: ${command}`);
3958
2530
  printUsage();