codex-multi-auth 1.2.0 → 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 (675) hide show
  1. package/README.md +49 -32
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +910 -1704
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/auth/index.d.ts +2 -0
  6. package/dist/lib/auth/index.d.ts.map +1 -0
  7. package/dist/lib/auth/index.js +2 -0
  8. package/dist/lib/auth/index.js.map +1 -0
  9. package/dist/lib/codex-cli/index.d.ts +5 -0
  10. package/dist/lib/codex-cli/index.d.ts.map +1 -0
  11. package/dist/lib/codex-cli/index.js +5 -0
  12. package/dist/lib/codex-cli/index.js.map +1 -0
  13. package/dist/lib/codex-manager/auth-commands.d.ts +72 -0
  14. package/dist/lib/codex-manager/auth-commands.d.ts.map +1 -0
  15. package/dist/lib/codex-manager/auth-commands.js +643 -0
  16. package/dist/lib/codex-manager/auth-commands.js.map +1 -0
  17. package/dist/lib/codex-manager/backend-category-entry.d.ts +103 -0
  18. package/dist/lib/codex-manager/backend-category-entry.d.ts.map +1 -0
  19. package/dist/lib/codex-manager/backend-category-entry.js +23 -0
  20. package/dist/lib/codex-manager/backend-category-entry.js.map +1 -0
  21. package/dist/lib/codex-manager/backend-category-helpers.d.ts +10 -0
  22. package/dist/lib/codex-manager/backend-category-helpers.d.ts.map +1 -0
  23. package/dist/lib/codex-manager/backend-category-helpers.js +29 -0
  24. package/dist/lib/codex-manager/backend-category-helpers.js.map +1 -0
  25. package/dist/lib/codex-manager/backend-category-prompt.d.ts +55 -0
  26. package/dist/lib/codex-manager/backend-category-prompt.d.ts.map +1 -0
  27. package/dist/lib/codex-manager/backend-category-prompt.js +178 -0
  28. package/dist/lib/codex-manager/backend-category-prompt.js.map +1 -0
  29. package/dist/lib/codex-manager/backend-settings-controller.d.ts +11 -0
  30. package/dist/lib/codex-manager/backend-settings-controller.d.ts.map +1 -0
  31. package/dist/lib/codex-manager/backend-settings-controller.js +14 -0
  32. package/dist/lib/codex-manager/backend-settings-controller.js.map +1 -0
  33. package/dist/lib/codex-manager/backend-settings-entry.d.ts +20 -0
  34. package/dist/lib/codex-manager/backend-settings-entry.d.ts.map +1 -0
  35. package/dist/lib/codex-manager/backend-settings-entry.js +12 -0
  36. package/dist/lib/codex-manager/backend-settings-entry.js.map +1 -0
  37. package/dist/lib/codex-manager/backend-settings-helpers.d.ts +16 -0
  38. package/dist/lib/codex-manager/backend-settings-helpers.d.ts.map +1 -0
  39. package/dist/lib/codex-manager/backend-settings-helpers.js +102 -0
  40. package/dist/lib/codex-manager/backend-settings-helpers.js.map +1 -0
  41. package/dist/lib/codex-manager/backend-settings-prompt.d.ts +49 -0
  42. package/dist/lib/codex-manager/backend-settings-prompt.d.ts.map +1 -0
  43. package/dist/lib/codex-manager/backend-settings-prompt.js +121 -0
  44. package/dist/lib/codex-manager/backend-settings-prompt.js.map +1 -0
  45. package/dist/lib/codex-manager/backend-settings-schema.d.ts +55 -0
  46. package/dist/lib/codex-manager/backend-settings-schema.d.ts.map +1 -0
  47. package/dist/lib/codex-manager/backend-settings-schema.js +264 -0
  48. package/dist/lib/codex-manager/backend-settings-schema.js.map +1 -0
  49. package/dist/lib/codex-manager/behavior-settings-panel.d.ts +33 -0
  50. package/dist/lib/codex-manager/behavior-settings-panel.d.ts.map +1 -0
  51. package/dist/lib/codex-manager/behavior-settings-panel.js +180 -0
  52. package/dist/lib/codex-manager/behavior-settings-panel.js.map +1 -0
  53. package/dist/lib/codex-manager/best.d.ts +26 -0
  54. package/dist/lib/codex-manager/best.d.ts.map +1 -0
  55. package/dist/lib/codex-manager/best.js +249 -0
  56. package/dist/lib/codex-manager/best.js.map +1 -0
  57. package/dist/lib/codex-manager/commands/best.d.ts +81 -0
  58. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -0
  59. package/dist/lib/codex-manager/commands/best.js +223 -0
  60. package/dist/lib/codex-manager/commands/best.js.map +1 -0
  61. package/dist/lib/codex-manager/commands/check.d.ts +7 -0
  62. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -0
  63. package/dist/lib/codex-manager/commands/check.js +5 -0
  64. package/dist/lib/codex-manager/commands/check.js.map +1 -0
  65. package/dist/lib/codex-manager/commands/config-explain.d.ts +7 -0
  66. package/dist/lib/codex-manager/commands/config-explain.d.ts.map +1 -0
  67. package/dist/lib/codex-manager/commands/config-explain.js +31 -0
  68. package/dist/lib/codex-manager/commands/config-explain.js.map +1 -0
  69. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +28 -0
  70. package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -0
  71. package/dist/lib/codex-manager/commands/debug-bundle.js +62 -0
  72. package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -0
  73. package/dist/lib/codex-manager/commands/doctor.d.ts +85 -0
  74. package/dist/lib/codex-manager/commands/doctor.d.ts.map +1 -0
  75. package/dist/lib/codex-manager/commands/doctor.js +424 -0
  76. package/dist/lib/codex-manager/commands/doctor.js.map +1 -0
  77. package/dist/lib/codex-manager/commands/fix.d.ts +92 -0
  78. package/dist/lib/codex-manager/commands/fix.d.ts.map +1 -0
  79. package/dist/lib/codex-manager/commands/fix.js +347 -0
  80. package/dist/lib/codex-manager/commands/fix.js.map +1 -0
  81. package/dist/lib/codex-manager/commands/forecast.d.ts +70 -0
  82. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -0
  83. package/dist/lib/codex-manager/commands/forecast.js +281 -0
  84. package/dist/lib/codex-manager/commands/forecast.js.map +1 -0
  85. package/dist/lib/codex-manager/commands/init-config.d.ts +15 -0
  86. package/dist/lib/codex-manager/commands/init-config.d.ts.map +1 -0
  87. package/dist/lib/codex-manager/commands/init-config.js +95 -0
  88. package/dist/lib/codex-manager/commands/init-config.js.map +1 -0
  89. package/dist/lib/codex-manager/commands/report.d.ts +24 -0
  90. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -0
  91. package/dist/lib/codex-manager/commands/report.js +314 -0
  92. package/dist/lib/codex-manager/commands/report.js.map +1 -0
  93. package/dist/lib/codex-manager/commands/status.d.ts +23 -0
  94. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -0
  95. package/dist/lib/codex-manager/commands/status.js +50 -0
  96. package/dist/lib/codex-manager/commands/status.js.map +1 -0
  97. package/dist/lib/codex-manager/commands/switch.d.ts +22 -0
  98. package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -0
  99. package/dist/lib/codex-manager/commands/switch.js +41 -0
  100. package/dist/lib/codex-manager/commands/switch.js.map +1 -0
  101. package/dist/lib/codex-manager/commands/verify-flagged.d.ts +69 -0
  102. package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +1 -0
  103. package/dist/lib/codex-manager/commands/verify-flagged.js +251 -0
  104. package/dist/lib/codex-manager/commands/verify-flagged.js.map +1 -0
  105. package/dist/lib/codex-manager/config-explain.d.ts +2 -0
  106. package/dist/lib/codex-manager/config-explain.d.ts.map +1 -0
  107. package/dist/lib/codex-manager/config-explain.js +62 -0
  108. package/dist/lib/codex-manager/config-explain.js.map +1 -0
  109. package/dist/lib/codex-manager/config-template.d.ts +2 -0
  110. package/dist/lib/codex-manager/config-template.d.ts.map +1 -0
  111. package/dist/lib/codex-manager/config-template.js +112 -0
  112. package/dist/lib/codex-manager/config-template.js.map +1 -0
  113. package/dist/lib/codex-manager/dashboard-display-panel.d.ts +40 -0
  114. package/dist/lib/codex-manager/dashboard-display-panel.d.ts.map +1 -0
  115. package/dist/lib/codex-manager/dashboard-display-panel.js +187 -0
  116. package/dist/lib/codex-manager/dashboard-display-panel.js.map +1 -0
  117. package/dist/lib/codex-manager/dashboard-formatters.d.ts +6 -0
  118. package/dist/lib/codex-manager/dashboard-formatters.d.ts.map +1 -0
  119. package/dist/lib/codex-manager/dashboard-formatters.js +19 -0
  120. package/dist/lib/codex-manager/dashboard-formatters.js.map +1 -0
  121. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts +12 -0
  122. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts.map +1 -0
  123. package/dist/lib/codex-manager/dashboard-settings-controller.js +17 -0
  124. package/dist/lib/codex-manager/dashboard-settings-controller.js.map +1 -0
  125. package/dist/lib/codex-manager/dashboard-settings-data.d.ts +10 -0
  126. package/dist/lib/codex-manager/dashboard-settings-data.d.ts.map +1 -0
  127. package/dist/lib/codex-manager/dashboard-settings-data.js +95 -0
  128. package/dist/lib/codex-manager/dashboard-settings-data.js.map +1 -0
  129. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts +22 -0
  130. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts.map +1 -0
  131. package/dist/lib/codex-manager/dashboard-settings-entry.js +13 -0
  132. package/dist/lib/codex-manager/dashboard-settings-entry.js.map +1 -0
  133. package/dist/lib/codex-manager/debug-bundle.d.ts +2 -0
  134. package/dist/lib/codex-manager/debug-bundle.d.ts.map +1 -0
  135. package/dist/lib/codex-manager/debug-bundle.js +128 -0
  136. package/dist/lib/codex-manager/debug-bundle.js.map +1 -0
  137. package/dist/lib/codex-manager/doctor.d.ts +20 -0
  138. package/dist/lib/codex-manager/doctor.d.ts.map +1 -0
  139. package/dist/lib/codex-manager/doctor.js +546 -0
  140. package/dist/lib/codex-manager/doctor.js.map +1 -0
  141. package/dist/lib/codex-manager/experimental-settings-entry.d.ts +7 -0
  142. package/dist/lib/codex-manager/experimental-settings-entry.d.ts.map +1 -0
  143. package/dist/lib/codex-manager/experimental-settings-entry.js +30 -0
  144. package/dist/lib/codex-manager/experimental-settings-entry.js.map +1 -0
  145. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts +66 -0
  146. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -0
  147. package/dist/lib/codex-manager/experimental-settings-prompt.js +221 -0
  148. package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -0
  149. package/dist/lib/codex-manager/experimental-settings-schema.d.ts +23 -0
  150. package/dist/lib/codex-manager/experimental-settings-schema.d.ts.map +1 -0
  151. package/dist/lib/codex-manager/experimental-settings-schema.js +34 -0
  152. package/dist/lib/codex-manager/experimental-settings-schema.js.map +1 -0
  153. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts +30 -0
  154. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts.map +1 -0
  155. package/dist/lib/codex-manager/experimental-sync-target-entry.js +16 -0
  156. package/dist/lib/codex-manager/experimental-sync-target-entry.js.map +1 -0
  157. package/dist/lib/codex-manager/experimental-sync-target.d.ts +23 -0
  158. package/dist/lib/codex-manager/experimental-sync-target.d.ts.map +1 -0
  159. package/dist/lib/codex-manager/experimental-sync-target.js +39 -0
  160. package/dist/lib/codex-manager/experimental-sync-target.js.map +1 -0
  161. package/dist/lib/codex-manager/fix.d.ts +26 -0
  162. package/dist/lib/codex-manager/fix.d.ts.map +1 -0
  163. package/dist/lib/codex-manager/fix.js +366 -0
  164. package/dist/lib/codex-manager/fix.js.map +1 -0
  165. package/dist/lib/codex-manager/forecast-report-commands.d.ts +54 -0
  166. package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -0
  167. package/dist/lib/codex-manager/forecast-report-commands.js +443 -0
  168. package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -0
  169. package/dist/lib/codex-manager/forecast.d.ts +26 -0
  170. package/dist/lib/codex-manager/forecast.d.ts.map +1 -0
  171. package/dist/lib/codex-manager/forecast.js +218 -0
  172. package/dist/lib/codex-manager/forecast.js.map +1 -0
  173. package/dist/lib/codex-manager/help.d.ts +36 -0
  174. package/dist/lib/codex-manager/help.d.ts.map +1 -0
  175. package/dist/lib/codex-manager/help.js +125 -0
  176. package/dist/lib/codex-manager/help.js.map +1 -0
  177. package/dist/lib/codex-manager/info.d.ts +11 -0
  178. package/dist/lib/codex-manager/info.d.ts.map +1 -0
  179. package/dist/lib/codex-manager/info.js +91 -0
  180. package/dist/lib/codex-manager/info.js.map +1 -0
  181. package/dist/lib/codex-manager/login.d.ts +40 -0
  182. package/dist/lib/codex-manager/login.d.ts.map +1 -0
  183. package/dist/lib/codex-manager/login.js +1041 -0
  184. package/dist/lib/codex-manager/login.js.map +1 -0
  185. package/dist/lib/codex-manager/repair-commands.d.ts +70 -0
  186. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -0
  187. package/dist/lib/codex-manager/repair-commands.js +1671 -0
  188. package/dist/lib/codex-manager/repair-commands.js.map +1 -0
  189. package/dist/lib/codex-manager/report.d.ts +39 -0
  190. package/dist/lib/codex-manager/report.d.ts.map +1 -0
  191. package/dist/lib/codex-manager/report.js +243 -0
  192. package/dist/lib/codex-manager/report.js.map +1 -0
  193. package/dist/lib/codex-manager/settings-hub-entry.d.ts +30 -0
  194. package/dist/lib/codex-manager/settings-hub-entry.d.ts.map +1 -0
  195. package/dist/lib/codex-manager/settings-hub-entry.js +11 -0
  196. package/dist/lib/codex-manager/settings-hub-entry.js.map +1 -0
  197. package/dist/lib/codex-manager/settings-hub-menu.d.ts +30 -0
  198. package/dist/lib/codex-manager/settings-hub-menu.d.ts.map +1 -0
  199. package/dist/lib/codex-manager/settings-hub-menu.js +37 -0
  200. package/dist/lib/codex-manager/settings-hub-menu.js.map +1 -0
  201. package/dist/lib/codex-manager/settings-hub-prompt.d.ts +25 -0
  202. package/dist/lib/codex-manager/settings-hub-prompt.d.ts.map +1 -0
  203. package/dist/lib/codex-manager/settings-hub-prompt.js +23 -0
  204. package/dist/lib/codex-manager/settings-hub-prompt.js.map +1 -0
  205. package/dist/lib/codex-manager/settings-hub.d.ts +10 -29
  206. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  207. package/dist/lib/codex-manager/settings-hub.js +260 -2168
  208. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  209. package/dist/lib/codex-manager/settings-panels.d.ts +56 -0
  210. package/dist/lib/codex-manager/settings-panels.d.ts.map +1 -0
  211. package/dist/lib/codex-manager/settings-panels.js +73 -0
  212. package/dist/lib/codex-manager/settings-panels.js.map +1 -0
  213. package/dist/lib/codex-manager/settings-persist-utils.d.ts +9 -0
  214. package/dist/lib/codex-manager/settings-persist-utils.d.ts.map +1 -0
  215. package/dist/lib/codex-manager/settings-persist-utils.js +33 -0
  216. package/dist/lib/codex-manager/settings-persist-utils.js.map +1 -0
  217. package/dist/lib/codex-manager/settings-preview.d.ts +12 -0
  218. package/dist/lib/codex-manager/settings-preview.d.ts.map +1 -0
  219. package/dist/lib/codex-manager/settings-preview.js +118 -0
  220. package/dist/lib/codex-manager/settings-preview.js.map +1 -0
  221. package/dist/lib/codex-manager/settings-write-queue.d.ts +14 -0
  222. package/dist/lib/codex-manager/settings-write-queue.d.ts.map +1 -0
  223. package/dist/lib/codex-manager/settings-write-queue.js +88 -0
  224. package/dist/lib/codex-manager/settings-write-queue.js.map +1 -0
  225. package/dist/lib/codex-manager/statusline-order.d.ts +3 -0
  226. package/dist/lib/codex-manager/statusline-order.d.ts.map +1 -0
  227. package/dist/lib/codex-manager/statusline-order.js +17 -0
  228. package/dist/lib/codex-manager/statusline-order.js.map +1 -0
  229. package/dist/lib/codex-manager/statusline-settings-panel.d.ts +40 -0
  230. package/dist/lib/codex-manager/statusline-settings-panel.d.ts.map +1 -0
  231. package/dist/lib/codex-manager/statusline-settings-panel.js +167 -0
  232. package/dist/lib/codex-manager/statusline-settings-panel.js.map +1 -0
  233. package/dist/lib/codex-manager/switch.d.ts +31 -0
  234. package/dist/lib/codex-manager/switch.d.ts.map +1 -0
  235. package/dist/lib/codex-manager/switch.js +103 -0
  236. package/dist/lib/codex-manager/switch.js.map +1 -0
  237. package/dist/lib/codex-manager/theme-settings-panel.d.ts +26 -0
  238. package/dist/lib/codex-manager/theme-settings-panel.d.ts.map +1 -0
  239. package/dist/lib/codex-manager/theme-settings-panel.js +131 -0
  240. package/dist/lib/codex-manager/theme-settings-panel.js.map +1 -0
  241. package/dist/lib/codex-manager/unified-settings-controller.d.ts +27 -0
  242. package/dist/lib/codex-manager/unified-settings-controller.d.ts.map +1 -0
  243. package/dist/lib/codex-manager/unified-settings-controller.js +50 -0
  244. package/dist/lib/codex-manager/unified-settings-controller.js.map +1 -0
  245. package/dist/lib/codex-manager/unified-settings-entry.d.ts +6 -0
  246. package/dist/lib/codex-manager/unified-settings-entry.d.ts.map +1 -0
  247. package/dist/lib/codex-manager/unified-settings-entry.js +23 -0
  248. package/dist/lib/codex-manager/unified-settings-entry.js.map +1 -0
  249. package/dist/lib/codex-manager/verify-flagged.d.ts +20 -0
  250. package/dist/lib/codex-manager/verify-flagged.d.ts.map +1 -0
  251. package/dist/lib/codex-manager/verify-flagged.js +316 -0
  252. package/dist/lib/codex-manager/verify-flagged.js.map +1 -0
  253. package/dist/lib/codex-manager.d.ts.map +1 -1
  254. package/dist/lib/codex-manager.js +439 -2199
  255. package/dist/lib/codex-manager.js.map +1 -1
  256. package/dist/lib/config.d.ts +38 -0
  257. package/dist/lib/config.d.ts.map +1 -1
  258. package/dist/lib/config.js +371 -9
  259. package/dist/lib/config.js.map +1 -1
  260. package/dist/lib/errors.d.ts +9 -0
  261. package/dist/lib/errors.d.ts.map +1 -1
  262. package/dist/lib/errors.js +13 -0
  263. package/dist/lib/errors.js.map +1 -1
  264. package/dist/lib/forecast.d.ts +17 -0
  265. package/dist/lib/forecast.d.ts.map +1 -1
  266. package/dist/lib/forecast.js +33 -6
  267. package/dist/lib/forecast.js.map +1 -1
  268. package/dist/lib/named-backup-export.js +2 -2
  269. package/dist/lib/named-backup-export.js.map +1 -1
  270. package/dist/lib/prompts/codex.d.ts +8 -3
  271. package/dist/lib/prompts/codex.d.ts.map +1 -1
  272. package/dist/lib/prompts/codex.js +16 -26
  273. package/dist/lib/prompts/codex.js.map +1 -1
  274. package/dist/lib/request/failover-config.d.ts +4 -0
  275. package/dist/lib/request/failover-config.d.ts.map +1 -0
  276. package/dist/lib/request/failover-config.js +15 -0
  277. package/dist/lib/request/failover-config.js.map +1 -0
  278. package/dist/lib/request/fetch-helpers.d.ts +10 -4
  279. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  280. package/dist/lib/request/fetch-helpers.js +13 -8
  281. package/dist/lib/request/fetch-helpers.js.map +1 -1
  282. package/dist/lib/request/helpers/model-map.d.ts +49 -12
  283. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  284. package/dist/lib/request/helpers/model-map.js +303 -108
  285. package/dist/lib/request/helpers/model-map.js.map +1 -1
  286. package/dist/lib/request/helpers/tool-utils.d.ts +2 -15
  287. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
  288. package/dist/lib/request/helpers/tool-utils.js +40 -27
  289. package/dist/lib/request/helpers/tool-utils.js.map +1 -1
  290. package/dist/lib/request/index.d.ts +5 -0
  291. package/dist/lib/request/index.d.ts.map +1 -0
  292. package/dist/lib/request/index.js +5 -0
  293. package/dist/lib/request/index.js.map +1 -0
  294. package/dist/lib/request/request-init.d.ts +3 -0
  295. package/dist/lib/request/request-init.d.ts.map +1 -0
  296. package/dist/lib/request/request-init.js +50 -0
  297. package/dist/lib/request/request-init.js.map +1 -0
  298. package/dist/lib/request/request-transformer.d.ts +19 -6
  299. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  300. package/dist/lib/request/request-transformer.js +210 -193
  301. package/dist/lib/request/request-transformer.js.map +1 -1
  302. package/dist/lib/request/response-compaction.d.ts +20 -0
  303. package/dist/lib/request/response-compaction.d.ts.map +1 -0
  304. package/dist/lib/request/response-compaction.js +124 -0
  305. package/dist/lib/request/response-compaction.js.map +1 -0
  306. package/dist/lib/request/response-handler.d.ts +2 -0
  307. package/dist/lib/request/response-handler.d.ts.map +1 -1
  308. package/dist/lib/request/response-handler.js +567 -11
  309. package/dist/lib/request/response-handler.js.map +1 -1
  310. package/dist/lib/request/response-metadata.d.ts +3 -0
  311. package/dist/lib/request/response-metadata.d.ts.map +1 -0
  312. package/dist/lib/request/response-metadata.js +61 -0
  313. package/dist/lib/request/response-metadata.js.map +1 -0
  314. package/dist/lib/request/trace-mode.d.ts +9 -0
  315. package/dist/lib/request/trace-mode.d.ts.map +1 -0
  316. package/dist/lib/request/trace-mode.js +14 -0
  317. package/dist/lib/request/trace-mode.js.map +1 -0
  318. package/dist/lib/request/wait-utils.d.ts +14 -0
  319. package/dist/lib/request/wait-utils.d.ts.map +1 -0
  320. package/dist/lib/request/wait-utils.js +44 -0
  321. package/dist/lib/request/wait-utils.js.map +1 -0
  322. package/dist/lib/runtime/account-check-helpers.d.ts +8 -0
  323. package/dist/lib/runtime/account-check-helpers.d.ts.map +1 -0
  324. package/dist/lib/runtime/account-check-helpers.js +30 -0
  325. package/dist/lib/runtime/account-check-helpers.js.map +1 -0
  326. package/dist/lib/runtime/account-check-types.d.ts +18 -0
  327. package/dist/lib/runtime/account-check-types.d.ts.map +1 -0
  328. package/dist/lib/runtime/account-check-types.js +12 -0
  329. package/dist/lib/runtime/account-check-types.js.map +1 -0
  330. package/dist/lib/runtime/account-check.d.ts +48 -0
  331. package/dist/lib/runtime/account-check.d.ts.map +1 -0
  332. package/dist/lib/runtime/account-check.js +202 -0
  333. package/dist/lib/runtime/account-check.js.map +1 -0
  334. package/dist/lib/runtime/account-health-check.d.ts +8 -0
  335. package/dist/lib/runtime/account-health-check.d.ts.map +1 -0
  336. package/dist/lib/runtime/account-health-check.js +30 -0
  337. package/dist/lib/runtime/account-health-check.js.map +1 -0
  338. package/dist/lib/runtime/account-manager-cache-entry.d.ts +25 -0
  339. package/dist/lib/runtime/account-manager-cache-entry.d.ts.map +1 -0
  340. package/dist/lib/runtime/account-manager-cache-entry.js +17 -0
  341. package/dist/lib/runtime/account-manager-cache-entry.js.map +1 -0
  342. package/dist/lib/runtime/account-manager-cache.d.ts +25 -0
  343. package/dist/lib/runtime/account-manager-cache.d.ts.map +1 -0
  344. package/dist/lib/runtime/account-manager-cache.js +44 -0
  345. package/dist/lib/runtime/account-manager-cache.js.map +1 -0
  346. package/dist/lib/runtime/account-pool.d.ts +23 -0
  347. package/dist/lib/runtime/account-pool.d.ts.map +1 -0
  348. package/dist/lib/runtime/account-pool.js +138 -0
  349. package/dist/lib/runtime/account-pool.js.map +1 -0
  350. package/dist/lib/runtime/account-scope.d.ts +10 -0
  351. package/dist/lib/runtime/account-scope.d.ts.map +1 -0
  352. package/dist/lib/runtime/account-scope.js +13 -0
  353. package/dist/lib/runtime/account-scope.js.map +1 -0
  354. package/dist/lib/runtime/account-select-event.d.ts +19 -0
  355. package/dist/lib/runtime/account-select-event.d.ts.map +1 -0
  356. package/dist/lib/runtime/account-select-event.js +53 -0
  357. package/dist/lib/runtime/account-select-event.js.map +1 -0
  358. package/dist/lib/runtime/account-selection.d.ts +28 -0
  359. package/dist/lib/runtime/account-selection.d.ts.map +1 -0
  360. package/dist/lib/runtime/account-selection.js +46 -0
  361. package/dist/lib/runtime/account-selection.js.map +1 -0
  362. package/dist/lib/runtime/account-state.d.ts +13 -0
  363. package/dist/lib/runtime/account-state.d.ts.map +1 -0
  364. package/dist/lib/runtime/account-state.js +38 -0
  365. package/dist/lib/runtime/account-state.js.map +1 -0
  366. package/dist/lib/runtime/account-status.d.ts +13 -0
  367. package/dist/lib/runtime/account-status.d.ts.map +1 -0
  368. package/dist/lib/runtime/account-status.js +37 -0
  369. package/dist/lib/runtime/account-status.js.map +1 -0
  370. package/dist/lib/runtime/account-storage-scope-entry.d.ts +26 -0
  371. package/dist/lib/runtime/account-storage-scope-entry.d.ts.map +1 -0
  372. package/dist/lib/runtime/account-storage-scope-entry.js +15 -0
  373. package/dist/lib/runtime/account-storage-scope-entry.js.map +1 -0
  374. package/dist/lib/runtime/auth-facade.d.ts +51 -0
  375. package/dist/lib/runtime/auth-facade.d.ts.map +1 -0
  376. package/dist/lib/runtime/auth-facade.js +48 -0
  377. package/dist/lib/runtime/auth-facade.js.map +1 -0
  378. package/dist/lib/runtime/browser-oauth-flow.d.ts +32 -0
  379. package/dist/lib/runtime/browser-oauth-flow.d.ts.map +1 -0
  380. package/dist/lib/runtime/browser-oauth-flow.js +31 -0
  381. package/dist/lib/runtime/browser-oauth-flow.js.map +1 -0
  382. package/dist/lib/runtime/capability-boost.d.ts +25 -0
  383. package/dist/lib/runtime/capability-boost.d.ts.map +1 -0
  384. package/dist/lib/runtime/capability-boost.js +26 -0
  385. package/dist/lib/runtime/capability-boost.js.map +1 -0
  386. package/dist/lib/runtime/event-handler.d.ts +53 -0
  387. package/dist/lib/runtime/event-handler.d.ts.map +1 -0
  388. package/dist/lib/runtime/event-handler.js +46 -0
  389. package/dist/lib/runtime/event-handler.js.map +1 -0
  390. package/dist/lib/runtime/flagged-verify-types.d.ts +9 -0
  391. package/dist/lib/runtime/flagged-verify-types.d.ts.map +1 -0
  392. package/dist/lib/runtime/flagged-verify-types.js +7 -0
  393. package/dist/lib/runtime/flagged-verify-types.js.map +1 -0
  394. package/dist/lib/runtime/hydrate-emails.d.ts +13 -0
  395. package/dist/lib/runtime/hydrate-emails.d.ts.map +1 -0
  396. package/dist/lib/runtime/hydrate-emails.js +56 -0
  397. package/dist/lib/runtime/hydrate-emails.js.map +1 -0
  398. package/dist/lib/runtime/live-sync-entry.d.ts +36 -0
  399. package/dist/lib/runtime/live-sync-entry.d.ts.map +1 -0
  400. package/dist/lib/runtime/live-sync-entry.js +14 -0
  401. package/dist/lib/runtime/live-sync-entry.js.map +1 -0
  402. package/dist/lib/runtime/live-sync.d.ts +35 -0
  403. package/dist/lib/runtime/live-sync.d.ts.map +1 -0
  404. package/dist/lib/runtime/live-sync.js +65 -0
  405. package/dist/lib/runtime/live-sync.js.map +1 -0
  406. package/dist/lib/runtime/loader-setup.d.ts +9 -0
  407. package/dist/lib/runtime/loader-setup.d.ts.map +1 -0
  408. package/dist/lib/runtime/loader-setup.js +8 -0
  409. package/dist/lib/runtime/loader-setup.js.map +1 -0
  410. package/dist/lib/runtime/login-menu-accounts.d.ts +29 -0
  411. package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -0
  412. package/dist/lib/runtime/login-menu-accounts.js +33 -0
  413. package/dist/lib/runtime/login-menu-accounts.js.map +1 -0
  414. package/dist/lib/runtime/manual-oauth-flow.d.ts +40 -0
  415. package/dist/lib/runtime/manual-oauth-flow.d.ts.map +1 -0
  416. package/dist/lib/runtime/manual-oauth-flow.js +71 -0
  417. package/dist/lib/runtime/manual-oauth-flow.js.map +1 -0
  418. package/dist/lib/runtime/metrics.d.ts +29 -0
  419. package/dist/lib/runtime/metrics.d.ts.map +1 -0
  420. package/dist/lib/runtime/metrics.js +97 -0
  421. package/dist/lib/runtime/metrics.js.map +1 -0
  422. package/dist/lib/runtime/oauth-browser-flow.d.ts +9 -0
  423. package/dist/lib/runtime/oauth-browser-flow.d.ts.map +1 -0
  424. package/dist/lib/runtime/oauth-browser-flow.js +34 -0
  425. package/dist/lib/runtime/oauth-browser-flow.js.map +1 -0
  426. package/dist/lib/runtime/preemptive-quota.d.ts +13 -0
  427. package/dist/lib/runtime/preemptive-quota.d.ts.map +1 -0
  428. package/dist/lib/runtime/preemptive-quota.js +9 -0
  429. package/dist/lib/runtime/preemptive-quota.js.map +1 -0
  430. package/dist/lib/runtime/quota-headers.d.ts +12 -0
  431. package/dist/lib/runtime/quota-headers.d.ts.map +1 -0
  432. package/dist/lib/runtime/quota-headers.js +131 -0
  433. package/dist/lib/runtime/quota-headers.js.map +1 -0
  434. package/dist/lib/runtime/quota-probe.d.ts +18 -0
  435. package/dist/lib/runtime/quota-probe.d.ts.map +1 -0
  436. package/dist/lib/runtime/quota-probe.js +82 -0
  437. package/dist/lib/runtime/quota-probe.js.map +1 -0
  438. package/dist/lib/runtime/quota-settings.d.ts +14 -0
  439. package/dist/lib/runtime/quota-settings.d.ts.map +1 -0
  440. package/dist/lib/runtime/quota-settings.js +12 -0
  441. package/dist/lib/runtime/quota-settings.js.map +1 -0
  442. package/dist/lib/runtime/refresh-guardian-entry.d.ts +32 -0
  443. package/dist/lib/runtime/refresh-guardian-entry.d.ts.map +1 -0
  444. package/dist/lib/runtime/refresh-guardian-entry.js +12 -0
  445. package/dist/lib/runtime/refresh-guardian-entry.js.map +1 -0
  446. package/dist/lib/runtime/refresh-guardian.d.ts +24 -0
  447. package/dist/lib/runtime/refresh-guardian.d.ts.map +1 -0
  448. package/dist/lib/runtime/refresh-guardian.js +32 -0
  449. package/dist/lib/runtime/refresh-guardian.js.map +1 -0
  450. package/dist/lib/runtime/request-init.d.ts +5 -0
  451. package/dist/lib/runtime/request-init.d.ts.map +1 -0
  452. package/dist/lib/runtime/request-init.js +50 -0
  453. package/dist/lib/runtime/request-init.js.map +1 -0
  454. package/dist/lib/runtime/runtime-services.d.ts +58 -0
  455. package/dist/lib/runtime/runtime-services.d.ts.map +1 -0
  456. package/dist/lib/runtime/runtime-services.js +87 -0
  457. package/dist/lib/runtime/runtime-services.js.map +1 -0
  458. package/dist/lib/runtime/session-affinity-entry.d.ts +30 -0
  459. package/dist/lib/runtime/session-affinity-entry.d.ts.map +1 -0
  460. package/dist/lib/runtime/session-affinity-entry.js +11 -0
  461. package/dist/lib/runtime/session-affinity-entry.js.map +1 -0
  462. package/dist/lib/runtime/session-affinity.d.ts +13 -0
  463. package/dist/lib/runtime/session-affinity.d.ts.map +1 -0
  464. package/dist/lib/runtime/session-affinity.js +17 -0
  465. package/dist/lib/runtime/session-affinity.js.map +1 -0
  466. package/dist/lib/runtime/session-recovery.d.ts +8 -0
  467. package/dist/lib/runtime/session-recovery.d.ts.map +1 -0
  468. package/dist/lib/runtime/session-recovery.js +7 -0
  469. package/dist/lib/runtime/session-recovery.js.map +1 -0
  470. package/dist/lib/runtime/status-marker.d.ts +3 -0
  471. package/dist/lib/runtime/status-marker.d.ts.map +1 -0
  472. package/dist/lib/runtime/status-marker.js +15 -0
  473. package/dist/lib/runtime/status-marker.js.map +1 -0
  474. package/dist/lib/runtime/storage-scope.d.ts +13 -0
  475. package/dist/lib/runtime/storage-scope.d.ts.map +1 -0
  476. package/dist/lib/runtime/storage-scope.js +14 -0
  477. package/dist/lib/runtime/storage-scope.js.map +1 -0
  478. package/dist/lib/runtime/toast.d.ts +16 -0
  479. package/dist/lib/runtime/toast.d.ts.map +1 -0
  480. package/dist/lib/runtime/toast.js +16 -0
  481. package/dist/lib/runtime/toast.js.map +1 -0
  482. package/dist/lib/runtime/ui-runtime-entry.d.ts +7 -0
  483. package/dist/lib/runtime/ui-runtime-entry.d.ts.map +1 -0
  484. package/dist/lib/runtime/ui-runtime-entry.js +4 -0
  485. package/dist/lib/runtime/ui-runtime-entry.js.map +1 -0
  486. package/dist/lib/runtime/ui-runtime.d.ts +9 -0
  487. package/dist/lib/runtime/ui-runtime.d.ts.map +1 -0
  488. package/dist/lib/runtime/ui-runtime.js +23 -0
  489. package/dist/lib/runtime/ui-runtime.js.map +1 -0
  490. package/dist/lib/runtime/verify-flagged.d.ts +44 -0
  491. package/dist/lib/runtime/verify-flagged.d.ts.map +1 -0
  492. package/dist/lib/runtime/verify-flagged.js +84 -0
  493. package/dist/lib/runtime/verify-flagged.js.map +1 -0
  494. package/dist/lib/schemas.d.ts +2 -0
  495. package/dist/lib/schemas.d.ts.map +1 -1
  496. package/dist/lib/schemas.js +2 -0
  497. package/dist/lib/schemas.js.map +1 -1
  498. package/dist/lib/session-affinity.d.ts +10 -0
  499. package/dist/lib/session-affinity.d.ts.map +1 -1
  500. package/dist/lib/session-affinity.js +53 -6
  501. package/dist/lib/session-affinity.js.map +1 -1
  502. package/dist/lib/storage/account-clear-entry.d.ts +16 -0
  503. package/dist/lib/storage/account-clear-entry.d.ts.map +1 -0
  504. package/dist/lib/storage/account-clear-entry.js +12 -0
  505. package/dist/lib/storage/account-clear-entry.js.map +1 -0
  506. package/dist/lib/storage/account-clear.d.ts +8 -0
  507. package/dist/lib/storage/account-clear.d.ts.map +1 -0
  508. package/dist/lib/storage/account-clear.js +29 -0
  509. package/dist/lib/storage/account-clear.js.map +1 -0
  510. package/dist/lib/storage/account-match-utils.d.ts +9 -0
  511. package/dist/lib/storage/account-match-utils.d.ts.map +1 -0
  512. package/dist/lib/storage/account-match-utils.js +45 -0
  513. package/dist/lib/storage/account-match-utils.js.map +1 -0
  514. package/dist/lib/storage/account-persistence.d.ts +3 -0
  515. package/dist/lib/storage/account-persistence.d.ts.map +1 -0
  516. package/dist/lib/storage/account-persistence.js +12 -0
  517. package/dist/lib/storage/account-persistence.js.map +1 -0
  518. package/dist/lib/storage/account-port.d.ts +50 -0
  519. package/dist/lib/storage/account-port.d.ts.map +1 -0
  520. package/dist/lib/storage/account-port.js +43 -0
  521. package/dist/lib/storage/account-port.js.map +1 -0
  522. package/dist/lib/storage/account-save-entry.d.ts +7 -0
  523. package/dist/lib/storage/account-save-entry.d.ts.map +1 -0
  524. package/dist/lib/storage/account-save-entry.js +6 -0
  525. package/dist/lib/storage/account-save-entry.js.map +1 -0
  526. package/dist/lib/storage/account-save.d.ts +30 -0
  527. package/dist/lib/storage/account-save.d.ts.map +1 -0
  528. package/dist/lib/storage/account-save.js +49 -0
  529. package/dist/lib/storage/account-save.js.map +1 -0
  530. package/dist/lib/storage/account-snapshot.d.ts +25 -0
  531. package/dist/lib/storage/account-snapshot.d.ts.map +1 -0
  532. package/dist/lib/storage/account-snapshot.js +78 -0
  533. package/dist/lib/storage/account-snapshot.js.map +1 -0
  534. package/dist/lib/storage/backup-metadata-builder.d.ts +32 -0
  535. package/dist/lib/storage/backup-metadata-builder.d.ts.map +1 -0
  536. package/dist/lib/storage/backup-metadata-builder.js +77 -0
  537. package/dist/lib/storage/backup-metadata-builder.js.map +1 -0
  538. package/dist/lib/storage/backup-metadata.d.ts +29 -0
  539. package/dist/lib/storage/backup-metadata.d.ts.map +1 -0
  540. package/dist/lib/storage/backup-metadata.js +16 -0
  541. package/dist/lib/storage/backup-metadata.js.map +1 -0
  542. package/dist/lib/storage/backup-paths.d.ts +10 -0
  543. package/dist/lib/storage/backup-paths.d.ts.map +1 -0
  544. package/dist/lib/storage/backup-paths.js +27 -0
  545. package/dist/lib/storage/backup-paths.js.map +1 -0
  546. package/dist/lib/storage/backup-restore.d.ts +11 -0
  547. package/dist/lib/storage/backup-restore.d.ts.map +1 -0
  548. package/dist/lib/storage/backup-restore.js +52 -0
  549. package/dist/lib/storage/backup-restore.js.map +1 -0
  550. package/dist/lib/storage/cache-artifacts.d.ts +2 -0
  551. package/dist/lib/storage/cache-artifacts.d.ts.map +1 -0
  552. package/dist/lib/storage/cache-artifacts.js +4 -0
  553. package/dist/lib/storage/cache-artifacts.js.map +1 -0
  554. package/dist/lib/storage/error-hints.d.ts +10 -0
  555. package/dist/lib/storage/error-hints.d.ts.map +1 -0
  556. package/dist/lib/storage/error-hints.js +36 -0
  557. package/dist/lib/storage/error-hints.js.map +1 -0
  558. package/dist/lib/storage/file-paths.d.ts +6 -0
  559. package/dist/lib/storage/file-paths.d.ts.map +1 -0
  560. package/dist/lib/storage/file-paths.js +17 -0
  561. package/dist/lib/storage/file-paths.js.map +1 -0
  562. package/dist/lib/storage/fixture-guards.d.ts +4 -0
  563. package/dist/lib/storage/fixture-guards.d.ts.map +1 -0
  564. package/dist/lib/storage/fixture-guards.js +27 -0
  565. package/dist/lib/storage/fixture-guards.js.map +1 -0
  566. package/dist/lib/storage/flagged-entry.d.ts +15 -0
  567. package/dist/lib/storage/flagged-entry.d.ts.map +1 -0
  568. package/dist/lib/storage/flagged-entry.js +12 -0
  569. package/dist/lib/storage/flagged-entry.js.map +1 -0
  570. package/dist/lib/storage/flagged-load-entry.d.ts +20 -0
  571. package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -0
  572. package/dist/lib/storage/flagged-load-entry.js +13 -0
  573. package/dist/lib/storage/flagged-load-entry.js.map +1 -0
  574. package/dist/lib/storage/flagged-save-entry.d.ts +7 -0
  575. package/dist/lib/storage/flagged-save-entry.d.ts.map +1 -0
  576. package/dist/lib/storage/flagged-save-entry.js +6 -0
  577. package/dist/lib/storage/flagged-save-entry.js.map +1 -0
  578. package/dist/lib/storage/flagged-storage-file.d.ts +7 -0
  579. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -0
  580. package/dist/lib/storage/flagged-storage-file.js +25 -0
  581. package/dist/lib/storage/flagged-storage-file.js.map +1 -0
  582. package/dist/lib/storage/flagged-storage-io.d.ts +28 -0
  583. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -0
  584. package/dist/lib/storage/flagged-storage-io.js +135 -0
  585. package/dist/lib/storage/flagged-storage-io.js.map +1 -0
  586. package/dist/lib/storage/flagged-storage.d.ts +6 -0
  587. package/dist/lib/storage/flagged-storage.d.ts.map +1 -0
  588. package/dist/lib/storage/flagged-storage.js +90 -0
  589. package/dist/lib/storage/flagged-storage.js.map +1 -0
  590. package/dist/lib/storage/gitignore.d.ts +7 -0
  591. package/dist/lib/storage/gitignore.d.ts.map +1 -0
  592. package/dist/lib/storage/gitignore.js +34 -0
  593. package/dist/lib/storage/gitignore.js.map +1 -0
  594. package/dist/lib/storage/hash.d.ts +2 -0
  595. package/dist/lib/storage/hash.d.ts.map +1 -0
  596. package/dist/lib/storage/hash.js +5 -0
  597. package/dist/lib/storage/hash.js.map +1 -0
  598. package/dist/lib/storage/identity.d.ts +15 -0
  599. package/dist/lib/storage/identity.d.ts.map +1 -0
  600. package/dist/lib/storage/identity.js +48 -0
  601. package/dist/lib/storage/identity.js.map +1 -0
  602. package/dist/lib/storage/import-export.d.ts +24 -0
  603. package/dist/lib/storage/import-export.d.ts.map +1 -0
  604. package/dist/lib/storage/import-export.js +108 -0
  605. package/dist/lib/storage/import-export.js.map +1 -0
  606. package/dist/lib/storage/metadata-section.d.ts +16 -0
  607. package/dist/lib/storage/metadata-section.d.ts.map +1 -0
  608. package/dist/lib/storage/metadata-section.js +16 -0
  609. package/dist/lib/storage/metadata-section.js.map +1 -0
  610. package/dist/lib/storage/named-backup-entry.d.ts +15 -0
  611. package/dist/lib/storage/named-backup-entry.d.ts.map +1 -0
  612. package/dist/lib/storage/named-backup-entry.js +7 -0
  613. package/dist/lib/storage/named-backup-entry.js.map +1 -0
  614. package/dist/lib/storage/named-backups-entry.d.ts +19 -0
  615. package/dist/lib/storage/named-backups-entry.d.ts.map +1 -0
  616. package/dist/lib/storage/named-backups-entry.js +7 -0
  617. package/dist/lib/storage/named-backups-entry.js.map +1 -0
  618. package/dist/lib/storage/named-backups.d.ts +15 -0
  619. package/dist/lib/storage/named-backups.d.ts.map +1 -0
  620. package/dist/lib/storage/named-backups.js +68 -0
  621. package/dist/lib/storage/named-backups.js.map +1 -0
  622. package/dist/lib/storage/path-state.d.ts +9 -0
  623. package/dist/lib/storage/path-state.d.ts.map +1 -0
  624. package/dist/lib/storage/path-state.js +20 -0
  625. package/dist/lib/storage/path-state.js.map +1 -0
  626. package/dist/lib/storage/project-migration.d.ts +10 -0
  627. package/dist/lib/storage/project-migration.d.ts.map +1 -0
  628. package/dist/lib/storage/project-migration.js +38 -0
  629. package/dist/lib/storage/project-migration.js.map +1 -0
  630. package/dist/lib/storage/record-utils.d.ts +3 -0
  631. package/dist/lib/storage/record-utils.d.ts.map +1 -0
  632. package/dist/lib/storage/record-utils.js +9 -0
  633. package/dist/lib/storage/record-utils.js.map +1 -0
  634. package/dist/lib/storage/restore-assessment.d.ts +7 -0
  635. package/dist/lib/storage/restore-assessment.d.ts.map +1 -0
  636. package/dist/lib/storage/restore-assessment.js +42 -0
  637. package/dist/lib/storage/restore-assessment.js.map +1 -0
  638. package/dist/lib/storage/restore-backup-entry.d.ts +24 -0
  639. package/dist/lib/storage/restore-backup-entry.d.ts.map +1 -0
  640. package/dist/lib/storage/restore-backup-entry.js +10 -0
  641. package/dist/lib/storage/restore-backup-entry.js.map +1 -0
  642. package/dist/lib/storage/restore-metadata.d.ts +10 -0
  643. package/dist/lib/storage/restore-metadata.d.ts.map +1 -0
  644. package/dist/lib/storage/restore-metadata.js +18 -0
  645. package/dist/lib/storage/restore-metadata.js.map +1 -0
  646. package/dist/lib/storage/restore.d.ts +14 -0
  647. package/dist/lib/storage/restore.d.ts.map +1 -0
  648. package/dist/lib/storage/restore.js +53 -0
  649. package/dist/lib/storage/restore.js.map +1 -0
  650. package/dist/lib/storage/snapshot-inspectors.d.ts +22 -0
  651. package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -0
  652. package/dist/lib/storage/snapshot-inspectors.js +95 -0
  653. package/dist/lib/storage/snapshot-inspectors.js.map +1 -0
  654. package/dist/lib/storage/storage-parser.d.ts +15 -0
  655. package/dist/lib/storage/storage-parser.d.ts.map +1 -0
  656. package/dist/lib/storage/storage-parser.js +16 -0
  657. package/dist/lib/storage/storage-parser.js.map +1 -0
  658. package/dist/lib/storage/transactions.d.ts +23 -0
  659. package/dist/lib/storage/transactions.d.ts.map +1 -0
  660. package/dist/lib/storage/transactions.js +64 -0
  661. package/dist/lib/storage/transactions.js.map +1 -0
  662. package/dist/lib/storage.d.ts +8 -47
  663. package/dist/lib/storage.d.ts.map +1 -1
  664. package/dist/lib/storage.js +482 -1044
  665. package/dist/lib/storage.js.map +1 -1
  666. package/dist/lib/types.d.ts +80 -1
  667. package/dist/lib/types.d.ts.map +1 -1
  668. package/package.json +41 -4
  669. package/scripts/check-pack-budget-lib.js +114 -0
  670. package/scripts/check-pack-budget.mjs +10 -0
  671. package/scripts/codex-multi-auth.js +34 -10
  672. package/scripts/install-codex-auth-utils.js +54 -14
  673. package/scripts/install-codex-auth.js +62 -33
  674. package/scripts/update-vendor-provenance.mjs +96 -0
  675. package/scripts/verify-vendor-provenance.mjs +53 -0
@@ -1,30 +1,41 @@
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, isBrowserLaunchSuppressed, 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";
13
26
  import { createLogger } from "./logger.js";
14
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";
15
30
  import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./quota-probe.js";
16
31
  import { queuedRefresh } from "./refresh-queue.js";
17
- import { loadQuotaCache, saveQuotaCache, } from "./quota-cache.js";
18
- import { clearAccounts, findMatchingAccountIndex, formatStorageErrorHint, getNamedBackups, getStoragePath, loadFlaggedAccounts, loadAccounts, StorageError, restoreAccountsFromBackup, saveFlaggedAccounts, saveAccounts, setStoragePath, withAccountAndFlaggedStorageTransaction, withAccountStorageTransaction, } from "./storage.js";
19
- import { getCodexCliAuthPath, getCodexCliConfigPath, loadCodexCliState, } from "./codex-cli/state.js";
20
- 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";
21
33
  import { ANSI } from "./ui/ansi.js";
22
- import { UI_COPY } from "./ui/copy.js";
23
34
  import { confirm } from "./ui/confirm.js";
35
+ import { UI_COPY } from "./ui/copy.js";
24
36
  import { paintUiText, quotaToneFromLeftPercent } from "./ui/format.js";
25
37
  import { getUiRuntimeOptions } from "./ui/runtime.js";
26
38
  import { select } from "./ui/select.js";
27
- import { applyUiThemeFromDashboardSettings, configureUnifiedSettings, resolveMenuLayoutMode } from "./codex-manager/settings-hub.js";
28
39
  const log = createLogger("codex-manager");
29
40
  function stylePromptText(text, tone) {
30
41
  if (!output.isTTY)
@@ -48,6 +59,28 @@ function stylePromptText(text, tone) {
48
59
  : ANSI.dim;
49
60
  return `${legacyCode}${text}${ANSI.reset}`;
50
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
+ }
51
84
  function collapseWhitespace(value) {
52
85
  return value.replace(/\s+/g, " ").trim();
53
86
  }
@@ -64,11 +97,10 @@ function extractErrorMessageFromPayload(payload) {
64
97
  const directMessage = typeof record.message === "string"
65
98
  ? collapseWhitespace(record.message)
66
99
  : "";
67
- const directCode = typeof record.code === "string"
68
- ? collapseWhitespace(record.code)
69
- : "";
100
+ const directCode = typeof record.code === "string" ? collapseWhitespace(record.code) : "";
70
101
  if (directMessage) {
71
- if (directCode && !directMessage.toLowerCase().includes(directCode.toLowerCase())) {
102
+ if (directCode &&
103
+ !directMessage.toLowerCase().includes(directCode.toLowerCase())) {
72
104
  return `${directMessage} [${directCode}]`;
73
105
  }
74
106
  return directMessage;
@@ -120,11 +152,36 @@ function formatResultSummary(segments) {
120
152
  const rendered = segments.map((segment) => stylePromptText(segment.text, segment.tone));
121
153
  return `${stylePromptText("Result:", "accent")} ${joinStyledSegments(rendered)}`;
122
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
+ }
123
177
  function styleQuotaSummary(summary) {
124
178
  const normalized = collapseWhitespace(summary);
125
179
  if (!normalized)
126
180
  return stylePromptText(summary, "muted");
127
- const segments = normalized.split("|").map((segment) => segment.trim()).filter(Boolean);
181
+ const segments = normalized
182
+ .split("|")
183
+ .map((segment) => segment.trim())
184
+ .filter(Boolean);
128
185
  if (segments.length === 0)
129
186
  return stylePromptText(normalized, "muted");
130
187
  const rendered = segments.map((segment) => {
@@ -207,55 +264,6 @@ function isAbortError(error) {
207
264
  const maybe = error;
208
265
  return maybe.name === "AbortError" || maybe.code === "ABORT_ERR";
209
266
  }
210
- function isUserCancelledOAuth(result) {
211
- if (result.type !== "failed")
212
- return false;
213
- const message = (result.message ?? "").toLowerCase();
214
- return message.includes("cancelled");
215
- }
216
- function printUsage() {
217
- console.log([
218
- "Codex Multi-Auth CLI",
219
- "",
220
- "Usage:",
221
- " codex auth login [--manual|--no-browser]",
222
- " codex auth list",
223
- " codex auth status",
224
- " codex auth switch <index>",
225
- " codex auth best [--live] [--json] [--model <model>]",
226
- " codex auth check",
227
- " codex auth features",
228
- " codex auth verify-flagged [--dry-run] [--json] [--no-restore]",
229
- " codex auth forecast [--live] [--json] [--model <model>]",
230
- " codex auth report [--live] [--json] [--model <model>] [--out <path>]",
231
- " codex auth fix [--dry-run] [--json] [--live] [--model <model>]",
232
- " codex auth doctor [--json] [--fix] [--dry-run]",
233
- "",
234
- "Notes:",
235
- " - Uses ~/.codex/multi-auth/openai-codex-accounts.json",
236
- " - Syncs active account into Codex CLI auth state",
237
- ].join("\n"));
238
- }
239
- function parseAuthLoginArgs(args) {
240
- const options = {
241
- manual: false,
242
- };
243
- for (const arg of args) {
244
- if (arg === "--manual" || arg === "--no-browser") {
245
- options.manual = true;
246
- continue;
247
- }
248
- if (arg === "--help" || arg === "-h") {
249
- printUsage();
250
- return { ok: false, message: "" };
251
- }
252
- return {
253
- ok: false,
254
- message: `Unknown login option: ${arg}`,
255
- };
256
- }
257
- return { ok: true, options };
258
- }
259
267
  const IMPLEMENTED_FEATURES = [
260
268
  { id: 1, name: "Multi-account OAuth login dashboard" },
261
269
  { id: 2, name: "Account add/update dedupe by token/id/email" },
@@ -299,14 +307,6 @@ const IMPLEMENTED_FEATURES = [
299
307
  { id: 40, name: "OAuth browser-first flow with manual callback fallback" },
300
308
  { id: 41, name: "Auto-switch to best account command" },
301
309
  ];
302
- function runFeaturesReport() {
303
- console.log(`Implemented features (${IMPLEMENTED_FEATURES.length})`);
304
- console.log("");
305
- for (const feature of IMPLEMENTED_FEATURES) {
306
- console.log(`${feature.id}. ${feature.name}`);
307
- }
308
- return 0;
309
- }
310
310
  function resolveActiveIndex(storage, family = "codex") {
311
311
  const total = storage.accounts.length;
312
312
  if (total === 0)
@@ -637,7 +637,8 @@ const ACCESS_TOKEN_FRESH_WINDOW_MS = 5 * 60 * 1000;
637
637
  function hasUsableAccessToken(account, now) {
638
638
  if (!account.accessToken)
639
639
  return false;
640
- if (typeof account.expiresAt !== "number" || !Number.isFinite(account.expiresAt))
640
+ if (typeof account.expiresAt !== "number" ||
641
+ !Number.isFinite(account.expiresAt))
641
642
  return false;
642
643
  return account.expiresAt - now > ACCESS_TOKEN_FRESH_WINDOW_MS;
643
644
  }
@@ -652,7 +653,8 @@ function hasLikelyInvalidRefreshToken(refreshToken) {
652
653
  function mapAccountStatus(account, index, activeIndex, now) {
653
654
  if (account.enabled === false)
654
655
  return "disabled";
655
- if (typeof account.coolingDownUntil === "number" && account.coolingDownUntil > now) {
656
+ if (typeof account.coolingDownUntil === "number" &&
657
+ account.coolingDownUntil > now) {
656
658
  return "cooldown";
657
659
  }
658
660
  const rateLimit = formatRateLimitEntry(account, now, "codex");
@@ -672,7 +674,9 @@ function parseLeftPercentFromQuotaSummary(summary, windowLabel) {
672
674
  return Math.max(0, Math.min(100, value));
673
675
  }
674
676
  function readQuotaLeftPercent(account, windowLabel) {
675
- const direct = windowLabel === "5h" ? account.quota5hLeftPercent : account.quota7dLeftPercent;
677
+ const direct = windowLabel === "5h"
678
+ ? account.quota5hLeftPercent
679
+ : account.quota7dLeftPercent;
676
680
  if (typeof direct === "number" && Number.isFinite(direct)) {
677
681
  return Math.max(0, Math.min(100, Math.round(direct)));
678
682
  }
@@ -705,7 +709,8 @@ function compareReadyFirstAccounts(left, right) {
705
709
  const right7d = readQuotaLeftPercent(right, "7d");
706
710
  if (left7d !== right7d)
707
711
  return right7d - left7d;
708
- const bucketDelta = accountStatusSortBucket(left.status) - accountStatusSortBucket(right.status);
712
+ const bucketDelta = accountStatusSortBucket(left.status) -
713
+ accountStatusSortBucket(right.status);
709
714
  if (bucketDelta !== 0)
710
715
  return bucketDelta;
711
716
  const leftLastUsed = left.lastUsed ?? 0;
@@ -717,14 +722,19 @@ function compareReadyFirstAccounts(left, right) {
717
722
  return leftSource - rightSource;
718
723
  }
719
724
  function applyAccountMenuOrdering(accounts, displaySettings) {
720
- const sortEnabled = displaySettings.menuSortEnabled ?? (DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortEnabled ?? true);
721
- 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";
722
731
  if (!sortEnabled || sortMode !== "ready-first") {
723
732
  return [...accounts];
724
733
  }
725
734
  const sorted = [...accounts].sort(compareReadyFirstAccounts);
726
735
  const pinCurrent = displaySettings.menuSortPinCurrent ??
727
- (DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortPinCurrent ?? false);
736
+ DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuSortPinCurrent ??
737
+ false;
728
738
  if (pinCurrent) {
729
739
  const currentIndex = sorted.findIndex((account) => account.isCurrentAccount);
730
740
  if (currentIndex > 0) {
@@ -775,7 +785,11 @@ function toExistingAccountInfo(storage, quotaCache, displaySettings) {
775
785
  showHintsForUnselectedRows: layoutMode === "expanded-rows",
776
786
  highlightCurrentRow: displaySettings.menuHighlightCurrentRow ?? true,
777
787
  focusStyle: displaySettings.menuFocusStyle ?? "row-invert",
778
- statuslineFields: displaySettings.menuStatuslineFields ?? ["last-used", "limits", "status"],
788
+ statuslineFields: displaySettings.menuStatuslineFields ?? [
789
+ "last-used",
790
+ "limits",
791
+ "status",
792
+ ],
779
793
  };
780
794
  });
781
795
  const orderedAccounts = applyAccountMenuOrdering(baseAccounts, displaySettings);
@@ -844,8 +858,8 @@ function applyTokenAccountIdentity(account, tokenAccountId) {
844
858
  if (!nextIdentity.accountId) {
845
859
  return false;
846
860
  }
847
- if (nextIdentity.accountId === account.accountId
848
- && nextIdentity.accountIdSource === account.accountIdSource) {
861
+ if (nextIdentity.accountId === account.accountId &&
862
+ nextIdentity.accountIdSource === account.accountIdSource) {
849
863
  return false;
850
864
  }
851
865
  account.accountId = nextIdentity.accountId;
@@ -945,7 +959,8 @@ function isReadlineClosedError(error) {
945
959
  const errorCode = typeof error === "object" && error !== null && "code" in error
946
960
  ? String(error.code)
947
961
  : "";
948
- return errorCode === "ERR_USE_AFTER_CLOSE" || /readline was closed/i.test(error.message);
962
+ return (errorCode === "ERR_USE_AFTER_CLOSE" ||
963
+ /readline was closed/i.test(error.message));
949
964
  }
950
965
  export function formatBackupSavedAt(mtimeMs) {
951
966
  return new Date(mtimeMs).toLocaleString(undefined, {
@@ -962,13 +977,21 @@ async function promptOAuthSignInMode(backupOption, backupDiscoveryWarning = null
962
977
  }
963
978
  const ui = getUiRuntimeOptions();
964
979
  const items = [
965
- { label: UI_COPY.oauth.signInHeading, value: "cancel", kind: "heading" },
980
+ {
981
+ label: UI_COPY.oauth.signInHeading,
982
+ value: "cancel",
983
+ kind: "heading",
984
+ },
966
985
  { label: UI_COPY.oauth.openBrowser, value: "browser", color: "green" },
967
986
  { label: UI_COPY.oauth.manualMode, value: "manual", color: "yellow" },
968
987
  ...(backupOption
969
988
  ? [
970
989
  { separator: true, label: "", value: "cancel" },
971
- { label: UI_COPY.oauth.restoreHeading, value: "cancel", kind: "heading" },
990
+ {
991
+ label: UI_COPY.oauth.restoreHeading,
992
+ value: "cancel",
993
+ kind: "heading",
994
+ },
972
995
  {
973
996
  label: UI_COPY.oauth.restoreSavedBackup,
974
997
  value: "restore-backup",
@@ -1373,7 +1396,9 @@ async function runOAuthFlow(forceNewLogin, signInMode) {
1373
1396
  : signInMode === "manual"
1374
1397
  ? UI_COPY.oauth.callbackBypassed
1375
1398
  : UI_COPY.oauth.callbackUnavailable, "warning"));
1376
- code = await promptManualCallback(state, { allowNonTty: signInMode === "manual" });
1399
+ code = await promptManualCallback(state, {
1400
+ allowNonTty: signInMode === "manual",
1401
+ });
1377
1402
  }
1378
1403
  }
1379
1404
  finally {
@@ -1400,7 +1425,8 @@ async function persistAccountPool(results, replaceAll) {
1400
1425
  const tokenAccountId = extractAccountId(result.access);
1401
1426
  const accountId = resolveRequestAccountId(result.accountIdOverride, result.accountIdSource, tokenAccountId);
1402
1427
  const accountIdSource = accountId
1403
- ? (result.accountIdSource ?? (result.accountIdOverride ? "manual" : "token"))
1428
+ ? (result.accountIdSource ??
1429
+ (result.accountIdOverride ? "manual" : "token"))
1404
1430
  : undefined;
1405
1431
  const accountLabel = result.accountLabel;
1406
1432
  const accountEmail = sanitizeEmail(extractAccountEmail(result.access, result.idToken));
@@ -1483,47 +1509,11 @@ async function syncSelectionToCodex(tokens) {
1483
1509
  idToken: tokens.idToken,
1484
1510
  });
1485
1511
  }
1486
- async function showAccountStatus() {
1487
- setStoragePath(null);
1488
- const storage = await loadAccounts();
1489
- const path = getStoragePath();
1490
- if (!storage || storage.accounts.length === 0) {
1491
- console.log("No accounts configured.");
1492
- console.log(`Storage: ${path}`);
1493
- return;
1494
- }
1495
- const now = Date.now();
1496
- const activeIndex = resolveActiveIndex(storage, "codex");
1497
- console.log(`Accounts (${storage.accounts.length})`);
1498
- console.log(`Storage: ${path}`);
1499
- console.log("");
1500
- for (let i = 0; i < storage.accounts.length; i += 1) {
1501
- const account = storage.accounts[i];
1502
- if (!account)
1503
- continue;
1504
- const label = formatAccountLabel(account, i);
1505
- const markers = [];
1506
- if (i === activeIndex)
1507
- markers.push("current");
1508
- if (account.enabled === false)
1509
- markers.push("disabled");
1510
- const rateLimit = formatRateLimitEntry(account, now, "codex");
1511
- if (rateLimit)
1512
- markers.push("rate-limited");
1513
- const cooldown = formatCooldown(account, now);
1514
- if (cooldown)
1515
- markers.push(`cooldown:${cooldown}`);
1516
- const markerLabel = markers.length > 0 ? ` [${markers.join(", ")}]` : "";
1517
- const lastUsed = typeof account.lastUsed === "number" && account.lastUsed > 0
1518
- ? `used ${formatWaitTime(now - account.lastUsed)} ago`
1519
- : "never used";
1520
- console.log(`${i + 1}. ${label}${markerLabel} ${lastUsed}`);
1521
- }
1522
- }
1523
1512
  async function runHealthCheck(options = {}) {
1524
1513
  const forceRefresh = options.forceRefresh === true;
1525
1514
  const liveProbe = options.liveProbe === true;
1526
1515
  const probeModel = options.model?.trim() || "gpt-5-codex";
1516
+ const modelInspection = inspectRequestedModel(probeModel);
1527
1517
  const display = options.display ?? DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
1528
1518
  const quotaCache = liveProbe ? await loadQuotaCache() : null;
1529
1519
  const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
@@ -1547,6 +1537,9 @@ async function runHealthCheck(options = {}) {
1547
1537
  console.log(stylePromptText(forceRefresh
1548
1538
  ? `Checking ${storage.accounts.length} account(s) with full refresh test...`
1549
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
+ }
1550
1543
  for (let i = 0; i < storage.accounts.length; i += 1) {
1551
1544
  const account = storage.accounts[i];
1552
1545
  if (!account)
@@ -1570,14 +1563,15 @@ async function runHealthCheck(options = {}) {
1570
1563
  : undefined;
1571
1564
  if (!probeAccountId || !currentAccessToken) {
1572
1565
  warnings += 1;
1573
- healthDetail = "signed in and working (live check skipped: missing account ID)";
1566
+ healthDetail =
1567
+ "signed in and working (live check skipped: missing account ID)";
1574
1568
  }
1575
1569
  else {
1576
1570
  try {
1577
1571
  const snapshot = await fetchCodexQuotaSnapshot({
1578
1572
  accountId: probeAccountId,
1579
1573
  accessToken: currentAccessToken,
1580
- model: probeModel,
1574
+ model: modelInspection.normalized,
1581
1575
  });
1582
1576
  if (workingQuotaCache) {
1583
1577
  quotaCacheChanged =
@@ -1647,14 +1641,15 @@ async function runHealthCheck(options = {}) {
1647
1641
  const probeAccountId = account.accountId ?? tokenAccountId;
1648
1642
  if (!probeAccountId) {
1649
1643
  warnings += 1;
1650
- healthyMessage = "working now (live check skipped: missing account ID)";
1644
+ healthyMessage =
1645
+ "working now (live check skipped: missing account ID)";
1651
1646
  }
1652
1647
  else {
1653
1648
  try {
1654
1649
  const snapshot = await fetchCodexQuotaSnapshot({
1655
1650
  accountId: probeAccountId,
1656
1651
  accessToken: result.access,
1657
- model: probeModel,
1652
+ model: modelInspection.normalized,
1658
1653
  });
1659
1654
  if (workingQuotaCache) {
1660
1655
  quotaCacheChanged =
@@ -1698,7 +1693,9 @@ async function runHealthCheck(options = {}) {
1698
1693
  if (changed) {
1699
1694
  await saveAccounts(storage);
1700
1695
  }
1701
- if (activeAccountRefreshed && activeIndex >= 0 && activeIndex < storage.accounts.length) {
1696
+ if (activeAccountRefreshed &&
1697
+ activeIndex >= 0 &&
1698
+ activeIndex < storage.accounts.length) {
1702
1699
  const activeAccount = storage.accounts[activeIndex];
1703
1700
  if (activeAccount) {
1704
1701
  await setCodexCliActiveSelection({
@@ -1713,21 +1710,16 @@ async function runHealthCheck(options = {}) {
1713
1710
  console.log("");
1714
1711
  console.log(formatResultSummary([
1715
1712
  { text: `${ok} working`, tone: "success" },
1716
- { text: `${failed} need re-login`, tone: failed > 0 ? "danger" : "muted" },
1717
- { 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
+ },
1718
1721
  ]));
1719
1722
  }
1720
- function printForecastUsage() {
1721
- console.log([
1722
- "Usage:",
1723
- " codex auth forecast [--live] [--json] [--model <model>]",
1724
- "",
1725
- "Options:",
1726
- " --live, -l Probe live quota headers via Codex backend",
1727
- " --json, -j Print machine-readable JSON output",
1728
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1729
- ].join("\n"));
1730
- }
1731
1723
  function printBestUsage() {
1732
1724
  console.log([
1733
1725
  "Usage:",
@@ -1743,79 +1735,6 @@ function printBestUsage() {
1743
1735
  " - Switches to the recommended account when it is not already active",
1744
1736
  ].join("\n"));
1745
1737
  }
1746
- function printFixUsage() {
1747
- console.log([
1748
- "Usage:",
1749
- " codex auth fix [--dry-run] [--json] [--live] [--model <model>]",
1750
- "",
1751
- "Options:",
1752
- " --dry-run, -n Preview changes without writing storage",
1753
- " --json, -j Print machine-readable JSON output",
1754
- " --live, -l Run live session probe before deciding health",
1755
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1756
- "",
1757
- "Behavior:",
1758
- " - Refreshes tokens for enabled accounts",
1759
- " - Disables hard-failed accounts (never deletes)",
1760
- " - Recommends a better current account when needed",
1761
- ].join("\n"));
1762
- }
1763
- function printVerifyFlaggedUsage() {
1764
- console.log([
1765
- "Usage:",
1766
- " codex auth verify-flagged [--dry-run] [--json] [--no-restore]",
1767
- "",
1768
- "Options:",
1769
- " --dry-run, -n Preview changes without writing storage",
1770
- " --json, -j Print machine-readable JSON output",
1771
- " --no-restore Check flagged accounts without restoring healthy ones",
1772
- "",
1773
- "Behavior:",
1774
- " - Refresh-checks accounts from flagged storage",
1775
- " - Restores healthy accounts back to active storage by default",
1776
- ].join("\n"));
1777
- }
1778
- function parseForecastArgs(args) {
1779
- const options = {
1780
- live: false,
1781
- json: false,
1782
- model: "gpt-5-codex",
1783
- };
1784
- for (let i = 0; i < args.length; i += 1) {
1785
- const arg = args[i];
1786
- if (!arg)
1787
- continue;
1788
- if (!arg)
1789
- continue;
1790
- if (arg === "--live" || arg === "-l") {
1791
- options.live = true;
1792
- continue;
1793
- }
1794
- if (arg === "--json" || arg === "-j") {
1795
- options.json = true;
1796
- continue;
1797
- }
1798
- if (arg === "--model" || arg === "-m") {
1799
- const value = args[i + 1];
1800
- if (!value) {
1801
- return { ok: false, message: "Missing value for --model" };
1802
- }
1803
- options.model = value;
1804
- i += 1;
1805
- continue;
1806
- }
1807
- if (arg.startsWith("--model=")) {
1808
- const value = arg.slice("--model=".length).trim();
1809
- if (!value) {
1810
- return { ok: false, message: "Missing value for --model" };
1811
- }
1812
- options.model = value;
1813
- continue;
1814
- }
1815
- return { ok: false, message: `Unknown option: ${arg}` };
1816
- }
1817
- return { ok: true, options };
1818
- }
1819
1738
  function parseBestArgs(args) {
1820
1739
  const options = {
1821
1740
  live: false,
@@ -1858,1703 +1777,173 @@ function parseBestArgs(args) {
1858
1777
  }
1859
1778
  return { ok: true, options };
1860
1779
  }
1861
- function parseFixArgs(args) {
1862
- const options = {
1863
- dryRun: false,
1864
- json: false,
1865
- live: false,
1866
- model: "gpt-5-codex",
1867
- };
1868
- for (let i = 0; i < args.length; i += 1) {
1869
- const argValue = args[i];
1870
- if (typeof argValue !== "string")
1871
- continue;
1872
- if (argValue === "--dry-run" || argValue === "-n") {
1873
- options.dryRun = true;
1874
- continue;
1875
- }
1876
- if (argValue === "--json" || argValue === "-j") {
1877
- options.json = true;
1878
- continue;
1879
- }
1880
- if (argValue === "--live" || argValue === "-l") {
1881
- options.live = true;
1882
- continue;
1883
- }
1884
- if (argValue === "--model" || argValue === "-m") {
1885
- const value = args[i + 1];
1886
- if (!value) {
1887
- return { ok: false, message: "Missing value for --model" };
1888
- }
1889
- options.model = value;
1890
- i += 1;
1891
- continue;
1892
- }
1893
- if (argValue.startsWith("--model=")) {
1894
- const value = argValue.slice("--model=".length).trim();
1895
- if (!value) {
1896
- return { ok: false, message: "Missing value for --model" };
1897
- }
1898
- options.model = value;
1899
- continue;
1900
- }
1901
- 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;
1902
1817
  }
1903
- 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);
1904
1828
  }
1905
- function parseVerifyFlaggedArgs(args) {
1906
- const options = {
1907
- dryRun: false,
1908
- json: false,
1909
- restore: true,
1910
- };
1911
- for (const arg of args) {
1912
- if (arg === "--dry-run" || arg === "-n") {
1913
- options.dryRun = true;
1914
- continue;
1915
- }
1916
- if (arg === "--json" || arg === "-j") {
1917
- options.json = true;
1918
- continue;
1919
- }
1920
- if (arg === "--no-restore") {
1921
- options.restore = false;
1922
- 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;
1923
1836
  }
1924
- 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);
1925
1845
  }
1926
- return { ok: true, options };
1927
1846
  }
1928
- function printDoctorUsage() {
1929
- console.log([
1930
- "Usage:",
1931
- " codex auth doctor [--json] [--fix] [--dry-run]",
1932
- "",
1933
- "Options:",
1934
- " --json, -j Print machine-readable JSON diagnostics",
1935
- " --fix Apply safe auto-fixes to storage",
1936
- " --dry-run, -n Preview --fix changes without writing storage",
1937
- "",
1938
- "Behavior:",
1939
- " - Validates account storage readability",
1940
- " - Checks active index consistency and account duplication",
1941
- " - Flags placeholder/demo accounts and disabled-all scenarios",
1942
- ].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
+ };
1943
1854
  }
1944
- function parseDoctorArgs(args) {
1945
- const options = { json: false, fix: false, dryRun: false };
1946
- for (const arg of args) {
1947
- if (arg === "--json" || arg === "-j") {
1948
- options.json = true;
1949
- continue;
1950
- }
1951
- if (arg === "--fix") {
1952
- options.fix = true;
1953
- continue;
1954
- }
1955
- if (arg === "--dry-run" || arg === "-n") {
1956
- options.dryRun = true;
1957
- 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;
1958
1866
  }
1959
- return { ok: false, message: `Unknown option: ${arg}` };
1960
- }
1961
- if (options.dryRun && !options.fix) {
1962
- return { ok: false, message: "--dry-run requires --fix" };
1867
+ return null;
1963
1868
  }
1964
- return { ok: true, options };
1869
+ return fallbackIndex >= 0 && fallbackIndex < storage.accounts.length
1870
+ ? fallbackIndex
1871
+ : null;
1965
1872
  }
1966
- function printReportUsage() {
1967
- console.log([
1968
- "Usage:",
1969
- " codex auth report [--live] [--json] [--model <model>] [--out <path>]",
1970
- "",
1971
- "Options:",
1972
- " --live, -l Probe live quota headers via Codex backend",
1973
- " --json, -j Print machine-readable JSON output",
1974
- " --model, -m Probe model for live mode (default: gpt-5-codex)",
1975
- " --out Write JSON report to a file path",
1976
- ].join("\n"));
1873
+ function matchesManageActionAccount(account, candidate) {
1874
+ if (!account || !candidate) {
1875
+ return false;
1876
+ }
1877
+ if (account.accountId || candidate.accountId) {
1878
+ return account.accountId === candidate.accountId;
1879
+ }
1880
+ return (account.refreshToken === candidate.refreshToken
1881
+ && sanitizeEmail(account.email) === sanitizeEmail(candidate.email));
1977
1882
  }
1978
- function parseReportArgs(args) {
1979
- const options = {
1980
- live: false,
1981
- json: false,
1982
- model: "gpt-5-codex",
1983
- };
1984
- for (let i = 0; i < args.length; i += 1) {
1985
- const arg = args[i];
1986
- if (!arg)
1987
- continue;
1988
- if (arg === "--live" || arg === "-l") {
1989
- options.live = true;
1990
- continue;
1883
+ async function handleManageAction(storage, menuResult) {
1884
+ if (typeof menuResult.switchAccountIndex === "number") {
1885
+ const index = menuResult.switchAccountIndex;
1886
+ await runSwitchCommand([String(index + 1)], {
1887
+ setStoragePath,
1888
+ loadAccounts,
1889
+ persistAndSyncSelectedAccount,
1890
+ });
1891
+ return;
1892
+ }
1893
+ if (typeof menuResult.deleteAccountIndex === "number") {
1894
+ const idx = menuResult.deleteAccountIndex;
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
+ });
1991
1916
  }
1992
- if (arg === "--json" || arg === "-j") {
1993
- options.json = true;
1994
- continue;
1995
- }
1996
- if (arg === "--model" || arg === "-m") {
1997
- const value = args[i + 1];
1998
- if (!value) {
1999
- return { ok: false, message: "Missing value for --model" };
2000
- }
2001
- options.model = value;
2002
- i += 1;
2003
- continue;
2004
- }
2005
- if (arg.startsWith("--model=")) {
2006
- const value = arg.slice("--model=".length).trim();
2007
- if (!value) {
2008
- return { ok: false, message: "Missing value for --model" };
2009
- }
2010
- options.model = value;
2011
- continue;
2012
- }
2013
- if (arg === "--out") {
2014
- const value = args[i + 1];
2015
- if (!value) {
2016
- return { ok: false, message: "Missing value for --out" };
2017
- }
2018
- options.outPath = value;
2019
- i += 1;
2020
- continue;
2021
- }
2022
- if (arg.startsWith("--out=")) {
2023
- const value = arg.slice("--out=".length).trim();
2024
- if (!value) {
2025
- return { ok: false, message: "Missing value for --out" };
2026
- }
2027
- options.outPath = value;
2028
- continue;
2029
- }
2030
- return { ok: false, message: `Unknown option: ${arg}` };
2031
- }
2032
- return { ok: true, options };
2033
- }
2034
- function serializeForecastResults(results, liveQuotaByIndex, refreshFailures) {
2035
- return results.map((result) => {
2036
- const liveQuota = liveQuotaByIndex.get(result.index);
2037
- return {
2038
- index: result.index,
2039
- label: result.label,
2040
- isCurrent: result.isCurrent,
2041
- availability: result.availability,
2042
- riskScore: result.riskScore,
2043
- riskLevel: result.riskLevel,
2044
- waitMs: result.waitMs,
2045
- reasons: result.reasons,
2046
- liveQuota: liveQuota
2047
- ? {
2048
- status: liveQuota.status,
2049
- planType: liveQuota.planType,
2050
- activeLimit: liveQuota.activeLimit,
2051
- model: liveQuota.model,
2052
- summary: formatQuotaSnapshotLine(liveQuota),
2053
- }
2054
- : undefined,
2055
- refreshFailure: refreshFailures.get(result.index),
2056
- };
2057
- });
2058
- }
2059
- async function runForecast(args) {
2060
- if (args.includes("--help") || args.includes("-h")) {
2061
- printForecastUsage();
2062
- return 0;
2063
- }
2064
- const parsedArgs = parseForecastArgs(args);
2065
- if (!parsedArgs.ok) {
2066
- console.error(parsedArgs.message);
2067
- printForecastUsage();
2068
- return 1;
2069
- }
2070
- const options = parsedArgs.options;
2071
- const display = DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
2072
- const quotaCache = options.live ? await loadQuotaCache() : null;
2073
- const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
2074
- let quotaCacheChanged = false;
2075
- setStoragePath(null);
2076
- const storage = await loadAccounts();
2077
- if (!storage || storage.accounts.length === 0) {
2078
- console.log("No accounts configured.");
2079
- return 0;
2080
- }
2081
- const quotaEmailFallbackState = options.live && quotaCache
2082
- ? buildQuotaEmailFallbackState(storage.accounts)
2083
- : null;
2084
- const now = Date.now();
2085
- const activeIndex = resolveActiveIndex(storage, "codex");
2086
- const refreshFailures = new Map();
2087
- const liveQuotaByIndex = new Map();
2088
- const probeErrors = [];
2089
- for (let i = 0; i < storage.accounts.length; i += 1) {
2090
- const account = storage.accounts[i];
2091
- if (!account || !options.live)
2092
- continue;
2093
- if (account.enabled === false)
2094
- continue;
2095
- let probeAccessToken = account.accessToken;
2096
- let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
2097
- if (!hasUsableAccessToken(account, now)) {
2098
- const refreshResult = await queuedRefresh(account.refreshToken);
2099
- if (refreshResult.type !== "success") {
2100
- refreshFailures.set(i, {
2101
- ...refreshResult,
2102
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
2103
- });
2104
- continue;
2105
- }
2106
- probeAccessToken = refreshResult.access;
2107
- probeAccountId = account.accountId ?? extractAccountId(refreshResult.access);
2108
- }
2109
- if (!probeAccessToken || !probeAccountId) {
2110
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
2111
- continue;
2112
- }
2113
- try {
2114
- const liveQuota = await fetchCodexQuotaSnapshot({
2115
- accountId: probeAccountId,
2116
- accessToken: probeAccessToken,
2117
- model: options.model,
2118
- });
2119
- liveQuotaByIndex.set(i, liveQuota);
2120
- if (workingQuotaCache) {
2121
- const account = storage.accounts[i];
2122
- if (account) {
2123
- quotaCacheChanged =
2124
- updateQuotaCacheForAccount(workingQuotaCache, account, liveQuota, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
2125
- }
2126
- }
2127
- }
2128
- catch (error) {
2129
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2130
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
2131
- }
2132
- }
2133
- const forecastInputs = storage.accounts.map((account, index) => ({
2134
- index,
2135
- account,
2136
- isCurrent: index === activeIndex,
2137
- now,
2138
- refreshFailure: refreshFailures.get(index),
2139
- liveQuota: liveQuotaByIndex.get(index),
2140
- }));
2141
- const forecastResults = evaluateForecastAccounts(forecastInputs);
2142
- const summary = summarizeForecast(forecastResults);
2143
- const recommendation = recommendForecastAccount(forecastResults);
2144
- if (options.json) {
2145
- if (workingQuotaCache && quotaCacheChanged) {
2146
- await saveQuotaCache(workingQuotaCache);
2147
- }
2148
- console.log(JSON.stringify({
2149
- command: "forecast",
2150
- model: options.model,
2151
- liveProbe: options.live,
2152
- summary,
2153
- recommendation,
2154
- probeErrors,
2155
- accounts: serializeForecastResults(forecastResults, liveQuotaByIndex, refreshFailures),
2156
- }, null, 2));
2157
- return 0;
2158
- }
2159
- console.log(stylePromptText(`Best-account preview (${storage.accounts.length} account(s), model ${options.model}, live check ${options.live ? "on" : "off"})`, "accent"));
2160
- console.log(formatResultSummary([
2161
- { text: `${summary.ready} ready now`, tone: "success" },
2162
- { text: `${summary.delayed} waiting`, tone: "warning" },
2163
- { text: `${summary.unavailable} unavailable`, tone: summary.unavailable > 0 ? "danger" : "muted" },
2164
- { text: `${summary.highRisk} high risk`, tone: summary.highRisk > 0 ? "danger" : "muted" },
2165
- ]));
2166
- console.log("");
2167
- for (const result of forecastResults) {
2168
- if (!display.showPerAccountRows) {
2169
- continue;
2170
- }
2171
- const currentTag = result.isCurrent ? " [current]" : "";
2172
- const waitLabel = result.waitMs > 0 ? stylePromptText(`wait ${formatWaitTime(result.waitMs)}`, "muted") : "";
2173
- const indexLabel = stylePromptText(`${result.index + 1}.`, "accent");
2174
- const accountLabel = stylePromptText(`${result.label}${currentTag}`, "accent");
2175
- const riskLabel = stylePromptText(`${result.riskLevel} risk (${result.riskScore})`, riskTone(result.riskLevel));
2176
- const availabilityLabel = stylePromptText(result.availability, availabilityTone(result.availability));
2177
- const rowParts = [availabilityLabel, riskLabel];
2178
- if (waitLabel)
2179
- rowParts.push(waitLabel);
2180
- console.log(`${indexLabel} ${accountLabel} ${stylePromptText("|", "muted")} ${joinStyledSegments(rowParts)}`);
2181
- if (display.showForecastReasons && result.reasons.length > 0) {
2182
- console.log(` ${stylePromptText(result.reasons.slice(0, 3).join("; "), "muted")}`);
2183
- }
2184
- const liveQuota = liveQuotaByIndex.get(result.index);
2185
- if (display.showQuotaDetails && liveQuota) {
2186
- console.log(` ${stylePromptText("quota:", "accent")} ${styleQuotaSummary(formatCompactQuotaSnapshot(liveQuota))}`);
2187
- }
2188
- }
2189
- if (!display.showPerAccountRows) {
2190
- console.log(stylePromptText("Per-account lines are hidden in dashboard settings.", "muted"));
2191
- }
2192
- if (display.showRecommendations) {
2193
- console.log("");
2194
- if (recommendation.recommendedIndex !== null) {
2195
- const index = recommendation.recommendedIndex;
2196
- const account = forecastResults.find((result) => result.index === index);
2197
- if (account) {
2198
- console.log(`${stylePromptText("Best next account:", "accent")} ${stylePromptText(`${index + 1} (${account.label})`, "success")}`);
2199
- console.log(`${stylePromptText("Why:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2200
- if (index !== activeIndex) {
2201
- console.log(`${stylePromptText("Switch now with:", "accent")} codex auth switch ${index + 1}`);
2202
- }
2203
- }
2204
- }
2205
- else {
2206
- console.log(`${stylePromptText("Note:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2207
- }
2208
- }
2209
- if (display.showLiveProbeNotes && probeErrors.length > 0) {
2210
- console.log("");
2211
- console.log(stylePromptText(`Live check notes (${probeErrors.length}):`, "warning"));
2212
- for (const error of probeErrors) {
2213
- console.log(` ${stylePromptText("-", "warning")} ${stylePromptText(error, "muted")}`);
2214
- }
2215
- }
2216
- if (workingQuotaCache && quotaCacheChanged) {
2217
- await saveQuotaCache(workingQuotaCache);
2218
- }
2219
- return 0;
2220
- }
2221
- async function runReport(args) {
2222
- if (args.includes("--help") || args.includes("-h")) {
2223
- printReportUsage();
2224
- return 0;
2225
- }
2226
- const parsedArgs = parseReportArgs(args);
2227
- if (!parsedArgs.ok) {
2228
- console.error(parsedArgs.message);
2229
- printReportUsage();
2230
- return 1;
2231
- }
2232
- const options = parsedArgs.options;
2233
- setStoragePath(null);
2234
- const storagePath = getStoragePath();
2235
- const storage = await loadAccounts();
2236
- const now = Date.now();
2237
- const accountCount = storage?.accounts.length ?? 0;
2238
- const activeIndex = storage ? resolveActiveIndex(storage, "codex") : 0;
2239
- const refreshFailures = new Map();
2240
- const liveQuotaByIndex = new Map();
2241
- const probeErrors = [];
2242
- if (storage && options.live) {
2243
- for (let i = 0; i < storage.accounts.length; i += 1) {
2244
- const account = storage.accounts[i];
2245
- if (!account || account.enabled === false)
2246
- continue;
2247
- const refreshResult = await queuedRefresh(account.refreshToken);
2248
- if (refreshResult.type !== "success") {
2249
- refreshFailures.set(i, {
2250
- ...refreshResult,
2251
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
2252
- });
2253
- continue;
2254
- }
2255
- const accountId = account.accountId ?? extractAccountId(refreshResult.access);
2256
- if (!accountId) {
2257
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
2258
- continue;
2259
- }
2260
- try {
2261
- const liveQuota = await fetchCodexQuotaSnapshot({
2262
- accountId,
2263
- accessToken: refreshResult.access,
2264
- model: options.model,
2265
- });
2266
- liveQuotaByIndex.set(i, liveQuota);
2267
- }
2268
- catch (error) {
2269
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2270
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
2271
- }
2272
- }
2273
- }
2274
- const forecastResults = storage
2275
- ? evaluateForecastAccounts(storage.accounts.map((account, index) => ({
2276
- index,
2277
- account,
2278
- isCurrent: index === activeIndex,
2279
- now,
2280
- refreshFailure: refreshFailures.get(index),
2281
- liveQuota: liveQuotaByIndex.get(index),
2282
- })))
2283
- : [];
2284
- const forecastSummary = summarizeForecast(forecastResults);
2285
- const recommendation = recommendForecastAccount(forecastResults);
2286
- const enabledCount = storage
2287
- ? storage.accounts.filter((account) => account.enabled !== false).length
2288
- : 0;
2289
- const disabledCount = Math.max(0, accountCount - enabledCount);
2290
- const coolingCount = storage
2291
- ? storage.accounts.filter((account) => typeof account.coolingDownUntil === "number" && account.coolingDownUntil > now).length
2292
- : 0;
2293
- const rateLimitedCount = storage
2294
- ? storage.accounts.filter((account) => !!formatRateLimitEntry(account, now, "codex")).length
2295
- : 0;
2296
- const report = {
2297
- command: "report",
2298
- generatedAt: new Date(now).toISOString(),
2299
- storagePath,
2300
- model: options.model,
2301
- liveProbe: options.live,
2302
- accounts: {
2303
- total: accountCount,
2304
- enabled: enabledCount,
2305
- disabled: disabledCount,
2306
- coolingDown: coolingCount,
2307
- rateLimited: rateLimitedCount,
2308
- },
2309
- activeIndex: accountCount > 0 ? activeIndex + 1 : null,
2310
- forecast: {
2311
- summary: forecastSummary,
2312
- recommendation,
2313
- probeErrors,
2314
- accounts: serializeForecastResults(forecastResults, liveQuotaByIndex, refreshFailures),
2315
- },
2316
- };
2317
- if (options.outPath) {
2318
- const outputPath = resolve(process.cwd(), options.outPath);
2319
- await fs.mkdir(dirname(outputPath), { recursive: true });
2320
- await fs.writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`, "utf-8");
2321
- }
2322
- if (options.json) {
2323
- console.log(JSON.stringify(report, null, 2));
2324
- return 0;
2325
- }
2326
- console.log(`Report generated at ${report.generatedAt}`);
2327
- console.log(`Storage: ${report.storagePath}`);
2328
- console.log(`Accounts: ${report.accounts.total} total (${report.accounts.enabled} enabled, ${report.accounts.disabled} disabled, ${report.accounts.coolingDown} cooling, ${report.accounts.rateLimited} rate-limited)`);
2329
- if (report.activeIndex !== null) {
2330
- console.log(`Active account: ${report.activeIndex}`);
2331
- }
2332
- console.log(`Forecast: ${report.forecast.summary.ready} ready, ${report.forecast.summary.delayed} delayed, ${report.forecast.summary.unavailable} unavailable`);
2333
- if (report.forecast.recommendation.recommendedIndex !== null) {
2334
- console.log(`Recommendation: account ${report.forecast.recommendation.recommendedIndex + 1} (${report.forecast.recommendation.reason})`);
2335
- }
2336
- else {
2337
- console.log(`Recommendation: ${report.forecast.recommendation.reason}`);
2338
- }
2339
- if (options.outPath) {
2340
- console.log(`Report written: ${resolve(process.cwd(), options.outPath)}`);
2341
- }
2342
- if (report.forecast.probeErrors.length > 0) {
2343
- console.log(`Probe notes: ${report.forecast.probeErrors.length}`);
2344
- }
2345
- return 0;
2346
- }
2347
- function summarizeFixReports(reports) {
2348
- let healthy = 0;
2349
- let disabled = 0;
2350
- let warnings = 0;
2351
- let skipped = 0;
2352
- for (const report of reports) {
2353
- if (report.outcome === "healthy")
2354
- healthy += 1;
2355
- else if (report.outcome === "disabled-hard-failure")
2356
- disabled += 1;
2357
- else if (report.outcome === "warning-soft-failure")
2358
- warnings += 1;
2359
- else
2360
- skipped += 1;
2361
- }
2362
- return { healthy, disabled, warnings, skipped };
2363
- }
2364
- function createEmptyAccountStorage() {
2365
- const activeIndexByFamily = {};
2366
- for (const family of MODEL_FAMILIES) {
2367
- activeIndexByFamily[family] = 0;
2368
- }
2369
- return {
2370
- version: 3,
2371
- accounts: [],
2372
- activeIndex: 0,
2373
- activeIndexByFamily,
2374
- };
2375
- }
2376
- function findExistingAccountIndexForFlagged(storage, flagged, nextRefreshToken, nextAccountId, nextEmail) {
2377
- const flaggedEmail = sanitizeEmail(flagged.email);
2378
- const candidateAccountId = nextAccountId ?? flagged.accountId;
2379
- const candidateEmail = sanitizeEmail(nextEmail) ?? flaggedEmail;
2380
- const nextMatchIndex = findMatchingAccountIndex(storage.accounts, {
2381
- accountId: candidateAccountId,
2382
- email: candidateEmail,
2383
- refreshToken: nextRefreshToken,
2384
- }, {
2385
- allowUniqueAccountIdFallbackWithoutEmail: true,
2386
- });
2387
- if (nextMatchIndex !== undefined) {
2388
- return nextMatchIndex;
2389
- }
2390
- const flaggedMatchIndex = findMatchingAccountIndex(storage.accounts, {
2391
- accountId: candidateAccountId,
2392
- email: candidateEmail,
2393
- refreshToken: flagged.refreshToken,
2394
- }, {
2395
- allowUniqueAccountIdFallbackWithoutEmail: true,
2396
- });
2397
- return flaggedMatchIndex ?? -1;
2398
- }
2399
- function upsertRecoveredFlaggedAccount(storage, flagged, refreshResult, now) {
2400
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken)) ?? flagged.email;
2401
- const tokenAccountId = extractAccountId(refreshResult.access);
2402
- const { accountId: nextAccountId, accountIdSource: nextAccountIdSource } = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2403
- const existingIndex = findExistingAccountIndexForFlagged(storage, flagged, refreshResult.refresh, nextAccountId, nextEmail);
2404
- if (existingIndex >= 0) {
2405
- const existing = storage.accounts[existingIndex];
2406
- if (!existing) {
2407
- return { restored: false, changed: false, message: "existing account entry is missing" };
2408
- }
2409
- let changed = false;
2410
- if (existing.refreshToken !== refreshResult.refresh) {
2411
- existing.refreshToken = refreshResult.refresh;
2412
- changed = true;
2413
- }
2414
- if (existing.accessToken !== refreshResult.access) {
2415
- existing.accessToken = refreshResult.access;
2416
- changed = true;
2417
- }
2418
- if (existing.expiresAt !== refreshResult.expires) {
2419
- existing.expiresAt = refreshResult.expires;
2420
- changed = true;
2421
- }
2422
- if (nextEmail && nextEmail !== existing.email) {
2423
- existing.email = nextEmail;
2424
- changed = true;
2425
- }
2426
- if (nextAccountId !== undefined &&
2427
- ((nextAccountId !== existing.accountId)
2428
- || (nextAccountIdSource !== existing.accountIdSource))) {
2429
- existing.accountId = nextAccountId;
2430
- existing.accountIdSource = nextAccountIdSource;
2431
- changed = true;
2432
- }
2433
- if (existing.enabled === false) {
2434
- existing.enabled = true;
2435
- changed = true;
2436
- }
2437
- if (existing.accountLabel !== flagged.accountLabel && flagged.accountLabel) {
2438
- existing.accountLabel = flagged.accountLabel;
2439
- changed = true;
2440
- }
2441
- existing.lastUsed = now;
2442
- return {
2443
- restored: true,
2444
- changed,
2445
- message: `restored into existing account ${existingIndex + 1}`,
2446
- };
2447
- }
2448
- if (storage.accounts.length >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
2449
- return {
2450
- restored: false,
2451
- changed: false,
2452
- message: `cannot restore (max ${ACCOUNT_LIMITS.MAX_ACCOUNTS} accounts reached)`,
2453
- };
2454
- }
2455
- storage.accounts.push({
2456
- refreshToken: refreshResult.refresh,
2457
- accessToken: refreshResult.access,
2458
- expiresAt: refreshResult.expires,
2459
- accountId: nextAccountId,
2460
- accountIdSource: nextAccountIdSource,
2461
- accountLabel: flagged.accountLabel,
2462
- email: nextEmail,
2463
- addedAt: flagged.addedAt ?? now,
2464
- lastUsed: now,
2465
- enabled: true,
2466
- });
2467
- return {
2468
- restored: true,
2469
- changed: true,
2470
- message: `restored as account ${storage.accounts.length}`,
2471
- };
2472
- }
2473
- async function runVerifyFlagged(args) {
2474
- if (args.includes("--help") || args.includes("-h")) {
2475
- printVerifyFlaggedUsage();
2476
- return 0;
2477
- }
2478
- const parsedArgs = parseVerifyFlaggedArgs(args);
2479
- if (!parsedArgs.ok) {
2480
- console.error(parsedArgs.message);
2481
- printVerifyFlaggedUsage();
2482
- return 1;
2483
- }
2484
- const options = parsedArgs.options;
2485
- setStoragePath(null);
2486
- const flaggedStorage = await loadFlaggedAccounts();
2487
- if (flaggedStorage.accounts.length === 0) {
2488
- if (options.json) {
2489
- console.log(JSON.stringify({
2490
- command: "verify-flagged",
2491
- total: 0,
2492
- restored: 0,
2493
- healthyFlagged: 0,
2494
- stillFlagged: 0,
2495
- changed: false,
2496
- dryRun: options.dryRun,
2497
- restore: options.restore,
2498
- reports: [],
2499
- }, null, 2));
2500
- return 0;
2501
- }
2502
- console.log("No flagged accounts to check.");
2503
- return 0;
2504
- }
2505
- let storageChanged = false;
2506
- let flaggedChanged = false;
2507
- const reports = [];
2508
- const nextFlaggedAccounts = [];
2509
- const now = Date.now();
2510
- const refreshChecks = [];
2511
- for (let i = 0; i < flaggedStorage.accounts.length; i += 1) {
2512
- const flagged = flaggedStorage.accounts[i];
2513
- if (!flagged)
2514
- continue;
2515
- const label = formatAccountLabel(flagged, i);
2516
- refreshChecks.push({
2517
- index: i,
2518
- flagged,
2519
- label,
2520
- result: await queuedRefresh(flagged.refreshToken),
2521
- });
2522
- }
2523
- const applyRefreshChecks = (storage) => {
2524
- for (const check of refreshChecks) {
2525
- const { index: i, flagged, label, result } = check;
2526
- if (result.type === "success") {
2527
- if (!options.restore) {
2528
- const tokenAccountId = extractAccountId(result.access);
2529
- const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2530
- const nextFlagged = {
2531
- ...flagged,
2532
- refreshToken: result.refresh,
2533
- accessToken: result.access,
2534
- expiresAt: result.expires,
2535
- accountId: nextIdentity.accountId,
2536
- accountIdSource: nextIdentity.accountIdSource,
2537
- email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
2538
- lastUsed: now,
2539
- lastError: undefined,
2540
- };
2541
- nextFlaggedAccounts.push(nextFlagged);
2542
- if (JSON.stringify(nextFlagged) !== JSON.stringify(flagged)) {
2543
- flaggedChanged = true;
2544
- }
2545
- reports.push({
2546
- index: i,
2547
- label,
2548
- outcome: "healthy-flagged",
2549
- message: "session is healthy (left in flagged list due to --no-restore)",
2550
- });
2551
- continue;
2552
- }
2553
- const upsertResult = upsertRecoveredFlaggedAccount(storage, flagged, result, now);
2554
- if (upsertResult.restored) {
2555
- storageChanged = storageChanged || upsertResult.changed;
2556
- flaggedChanged = true;
2557
- reports.push({
2558
- index: i,
2559
- label,
2560
- outcome: "restored",
2561
- message: upsertResult.message,
2562
- });
2563
- continue;
2564
- }
2565
- const tokenAccountId = extractAccountId(result.access);
2566
- const nextIdentity = resolveStoredAccountIdentity(flagged.accountId, flagged.accountIdSource, tokenAccountId);
2567
- const updatedFlagged = {
2568
- ...flagged,
2569
- refreshToken: result.refresh,
2570
- accessToken: result.access,
2571
- expiresAt: result.expires,
2572
- accountId: nextIdentity.accountId,
2573
- accountIdSource: nextIdentity.accountIdSource,
2574
- email: sanitizeEmail(extractAccountEmail(result.access, result.idToken)) ?? flagged.email,
2575
- lastUsed: now,
2576
- lastError: upsertResult.message,
2577
- };
2578
- nextFlaggedAccounts.push(updatedFlagged);
2579
- if (JSON.stringify(updatedFlagged) !== JSON.stringify(flagged)) {
2580
- flaggedChanged = true;
2581
- }
2582
- reports.push({
2583
- index: i,
2584
- label,
2585
- outcome: "restore-skipped",
2586
- message: upsertResult.message,
2587
- });
2588
- continue;
2589
- }
2590
- const detail = normalizeFailureDetail(result.message, result.reason);
2591
- const failedFlagged = {
2592
- ...flagged,
2593
- lastError: detail,
2594
- };
2595
- nextFlaggedAccounts.push(failedFlagged);
2596
- if ((flagged.lastError ?? "") !== detail) {
2597
- flaggedChanged = true;
2598
- }
2599
- reports.push({
2600
- index: i,
2601
- label,
2602
- outcome: "still-flagged",
2603
- message: detail,
2604
- });
2605
- }
2606
- };
2607
- if (options.restore) {
2608
- if (options.dryRun) {
2609
- applyRefreshChecks((await loadAccounts()) ?? createEmptyAccountStorage());
2610
- }
2611
- else {
2612
- await withAccountAndFlaggedStorageTransaction(async (loadedStorage, persist) => {
2613
- const nextStorage = loadedStorage
2614
- ? structuredClone(loadedStorage)
2615
- : createEmptyAccountStorage();
2616
- applyRefreshChecks(nextStorage);
2617
- if (!storageChanged) {
2618
- return;
2619
- }
2620
- normalizeDoctorIndexes(nextStorage);
2621
- await persist(nextStorage, {
2622
- version: 1,
2623
- accounts: nextFlaggedAccounts,
2624
- });
2625
- });
2626
- }
2627
- }
2628
- else {
2629
- applyRefreshChecks(createEmptyAccountStorage());
2630
- }
2631
- const remainingFlagged = nextFlaggedAccounts.length;
2632
- const restored = reports.filter((report) => report.outcome === "restored").length;
2633
- const healthyFlagged = reports.filter((report) => report.outcome === "healthy-flagged").length;
2634
- const stillFlagged = reports.filter((report) => report.outcome === "still-flagged").length;
2635
- const changed = storageChanged || flaggedChanged;
2636
- if (!options.dryRun && flaggedChanged && (!options.restore || !storageChanged)) {
2637
- await saveFlaggedAccounts({
2638
- version: 1,
2639
- accounts: nextFlaggedAccounts,
2640
- });
2641
- }
2642
- if (options.json) {
2643
- console.log(JSON.stringify({
2644
- command: "verify-flagged",
2645
- total: flaggedStorage.accounts.length,
2646
- restored,
2647
- healthyFlagged,
2648
- stillFlagged,
2649
- remainingFlagged,
2650
- changed,
2651
- dryRun: options.dryRun,
2652
- restore: options.restore,
2653
- reports,
2654
- }, null, 2));
2655
- return 0;
2656
- }
2657
- console.log(stylePromptText(`Checking ${flaggedStorage.accounts.length} flagged account(s)...`, "accent"));
2658
- for (const report of reports) {
2659
- const tone = report.outcome === "restored"
2660
- ? "success"
2661
- : report.outcome === "healthy-flagged"
2662
- ? "warning"
2663
- : report.outcome === "restore-skipped"
2664
- ? "warning"
2665
- : "danger";
2666
- const marker = report.outcome === "restored"
2667
- ? "✓"
2668
- : report.outcome === "healthy-flagged"
2669
- ? "!"
2670
- : report.outcome === "restore-skipped"
2671
- ? "!"
2672
- : "✗";
2673
- console.log(`${stylePromptText(marker, tone)} ${stylePromptText(`${report.index + 1}. ${report.label}`, "accent")} ${stylePromptText("|", "muted")} ${styleAccountDetailText(report.message, tone)}`);
2674
- }
2675
- console.log("");
2676
- console.log(formatResultSummary([
2677
- { text: `${restored} restored`, tone: restored > 0 ? "success" : "muted" },
2678
- { text: `${healthyFlagged} healthy (kept flagged)`, tone: healthyFlagged > 0 ? "warning" : "muted" },
2679
- { text: `${stillFlagged} still flagged`, tone: stillFlagged > 0 ? "danger" : "muted" },
2680
- ]));
2681
- if (options.dryRun) {
2682
- console.log(stylePromptText("Preview only: no changes were saved.", "warning"));
2683
- }
2684
- else if (!changed) {
2685
- console.log(stylePromptText("No storage changes were needed.", "muted"));
2686
- }
2687
- return 0;
2688
- }
2689
- async function runFix(args) {
2690
- if (args.includes("--help") || args.includes("-h")) {
2691
- printFixUsage();
2692
- return 0;
2693
- }
2694
- const parsedArgs = parseFixArgs(args);
2695
- if (!parsedArgs.ok) {
2696
- console.error(parsedArgs.message);
2697
- printFixUsage();
2698
- return 1;
2699
- }
2700
- const options = parsedArgs.options;
2701
- const display = DEFAULT_DASHBOARD_DISPLAY_SETTINGS;
2702
- const quotaCache = options.live ? await loadQuotaCache() : null;
2703
- const workingQuotaCache = quotaCache ? cloneQuotaCacheData(quotaCache) : null;
2704
- let quotaCacheChanged = false;
2705
- setStoragePath(null);
2706
- const storage = await loadAccounts();
2707
- if (!storage || storage.accounts.length === 0) {
2708
- console.log("No accounts configured.");
2709
- return 0;
2710
- }
2711
- let quotaEmailFallbackState = options.live && quotaCache
2712
- ? buildQuotaEmailFallbackState(storage.accounts)
2713
- : null;
2714
- const now = Date.now();
2715
- const activeIndex = resolveActiveIndex(storage, "codex");
2716
- let changed = false;
2717
- const reports = [];
2718
- const refreshFailures = new Map();
2719
- const hardDisabledIndexes = [];
2720
- for (let i = 0; i < storage.accounts.length; i += 1) {
2721
- const account = storage.accounts[i];
2722
- if (!account)
2723
- continue;
2724
- const label = formatAccountLabel(account, i);
2725
- if (account.enabled === false) {
2726
- reports.push({
2727
- index: i,
2728
- label,
2729
- outcome: "already-disabled",
2730
- message: "already disabled",
2731
- });
2732
- continue;
2733
- }
2734
- if (hasUsableAccessToken(account, now)) {
2735
- if (options.live) {
2736
- const currentAccessToken = account.accessToken;
2737
- const probeAccountId = currentAccessToken
2738
- ? (account.accountId ?? extractAccountId(currentAccessToken))
2739
- : undefined;
2740
- if (probeAccountId && currentAccessToken) {
2741
- try {
2742
- const snapshot = await fetchCodexQuotaSnapshot({
2743
- accountId: probeAccountId,
2744
- accessToken: currentAccessToken,
2745
- model: options.model,
2746
- });
2747
- if (workingQuotaCache) {
2748
- quotaCacheChanged =
2749
- updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
2750
- }
2751
- reports.push({
2752
- index: i,
2753
- label,
2754
- outcome: "healthy",
2755
- message: display.showQuotaDetails
2756
- ? `live session OK (${formatCompactQuotaSnapshot(snapshot)})`
2757
- : "live session OK",
2758
- });
2759
- continue;
2760
- }
2761
- catch (error) {
2762
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2763
- reports.push({
2764
- index: i,
2765
- label,
2766
- outcome: "warning-soft-failure",
2767
- message: `live probe failed (${message}), trying refresh fallback`,
2768
- });
2769
- }
2770
- }
2771
- }
2772
- const refreshWarning = hasLikelyInvalidRefreshToken(account.refreshToken)
2773
- ? " (refresh token looks stale; re-login recommended)"
2774
- : "";
2775
- reports.push({
2776
- index: i,
2777
- label,
2778
- outcome: "healthy",
2779
- message: `access token still valid${refreshWarning}`,
2780
- });
2781
- continue;
2782
- }
2783
- const refreshResult = await queuedRefresh(account.refreshToken);
2784
- if (refreshResult.type === "success") {
2785
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
2786
- const nextAccountId = extractAccountId(refreshResult.access);
2787
- const previousEmail = account.email;
2788
- let accountChanged = false;
2789
- let accountIdentityChanged = false;
2790
- if (account.refreshToken !== refreshResult.refresh) {
2791
- account.refreshToken = refreshResult.refresh;
2792
- accountChanged = true;
2793
- }
2794
- if (account.accessToken !== refreshResult.access) {
2795
- account.accessToken = refreshResult.access;
2796
- accountChanged = true;
2797
- }
2798
- if (account.expiresAt !== refreshResult.expires) {
2799
- account.expiresAt = refreshResult.expires;
2800
- accountChanged = true;
2801
- }
2802
- if (nextEmail && nextEmail !== account.email) {
2803
- account.email = nextEmail;
2804
- accountChanged = true;
2805
- accountIdentityChanged = true;
2806
- }
2807
- if (applyTokenAccountIdentity(account, nextAccountId)) {
2808
- accountChanged = true;
2809
- accountIdentityChanged = true;
2810
- }
2811
- if (accountChanged)
2812
- changed = true;
2813
- if (accountIdentityChanged && options.live && workingQuotaCache) {
2814
- quotaEmailFallbackState = buildQuotaEmailFallbackState(storage.accounts);
2815
- quotaCacheChanged =
2816
- pruneUnsafeQuotaEmailCacheEntry(workingQuotaCache, previousEmail, storage.accounts, quotaEmailFallbackState) || quotaCacheChanged;
2817
- }
2818
- if (options.live) {
2819
- const probeAccountId = account.accountId ?? nextAccountId;
2820
- if (probeAccountId) {
2821
- try {
2822
- const snapshot = await fetchCodexQuotaSnapshot({
2823
- accountId: probeAccountId,
2824
- accessToken: refreshResult.access,
2825
- model: options.model,
2826
- });
2827
- if (workingQuotaCache) {
2828
- quotaCacheChanged =
2829
- updateQuotaCacheForAccount(workingQuotaCache, account, snapshot, storage.accounts, quotaEmailFallbackState ?? undefined) || quotaCacheChanged;
2830
- }
2831
- reports.push({
2832
- index: i,
2833
- label,
2834
- outcome: "healthy",
2835
- message: display.showQuotaDetails
2836
- ? `refresh + live probe succeeded (${formatCompactQuotaSnapshot(snapshot)})`
2837
- : "refresh + live probe succeeded",
2838
- });
2839
- continue;
2840
- }
2841
- catch (error) {
2842
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
2843
- reports.push({
2844
- index: i,
2845
- label,
2846
- outcome: "warning-soft-failure",
2847
- message: `refresh succeeded but live probe failed: ${message}`,
2848
- });
2849
- continue;
2850
- }
2851
- }
2852
- }
2853
- reports.push({
2854
- index: i,
2855
- label,
2856
- outcome: "healthy",
2857
- message: "refresh succeeded",
2858
- });
2859
- continue;
2860
- }
2861
- const detail = normalizeFailureDetail(refreshResult.message, refreshResult.reason);
2862
- refreshFailures.set(i, {
2863
- ...refreshResult,
2864
- message: detail,
2865
- });
2866
- if (isHardRefreshFailure(refreshResult)) {
2867
- account.enabled = false;
2868
- changed = true;
2869
- hardDisabledIndexes.push(i);
2870
- reports.push({
2871
- index: i,
2872
- label,
2873
- outcome: "disabled-hard-failure",
2874
- message: detail,
2875
- });
2876
- }
2877
- else {
2878
- reports.push({
2879
- index: i,
2880
- label,
2881
- outcome: "warning-soft-failure",
2882
- message: detail,
2883
- });
2884
- }
2885
- }
2886
- if (hardDisabledIndexes.length > 0) {
2887
- const enabledCount = storage.accounts.filter((account) => account.enabled !== false).length;
2888
- if (enabledCount === 0) {
2889
- const fallbackIndex = hardDisabledIndexes.includes(activeIndex) ? activeIndex : hardDisabledIndexes[0];
2890
- const fallback = typeof fallbackIndex === "number"
2891
- ? storage.accounts[fallbackIndex]
2892
- : undefined;
2893
- if (fallback && fallback.enabled === false) {
2894
- fallback.enabled = true;
2895
- changed = true;
2896
- const existingReport = reports.find((report) => report.index === fallbackIndex &&
2897
- report.outcome === "disabled-hard-failure");
2898
- if (existingReport) {
2899
- existingReport.outcome = "warning-soft-failure";
2900
- existingReport.message = `${existingReport.message} (kept enabled to avoid lockout; re-login required)`;
2901
- }
2902
- }
2903
- }
2904
- }
2905
- const forecastResults = evaluateForecastAccounts(storage.accounts.map((account, index) => ({
2906
- index,
2907
- account,
2908
- isCurrent: index === activeIndex,
2909
- now,
2910
- refreshFailure: refreshFailures.get(index),
2911
- })));
2912
- const recommendation = recommendForecastAccount(forecastResults);
2913
- const reportSummary = summarizeFixReports(reports);
2914
- if (changed && !options.dryRun) {
2915
- await saveAccounts(storage);
2916
- }
2917
- if (options.json) {
2918
- if (workingQuotaCache && quotaCacheChanged) {
2919
- await saveQuotaCache(workingQuotaCache);
2920
- }
2921
- console.log(JSON.stringify({
2922
- command: "fix",
2923
- dryRun: options.dryRun,
2924
- liveProbe: options.live,
2925
- model: options.model,
2926
- changed,
2927
- summary: reportSummary,
2928
- recommendation,
2929
- recommendedSwitchCommand: recommendation.recommendedIndex !== null &&
2930
- recommendation.recommendedIndex !== activeIndex
2931
- ? `codex auth switch ${recommendation.recommendedIndex + 1}`
2932
- : null,
2933
- reports,
2934
- }, null, 2));
2935
- return 0;
2936
- }
2937
- console.log(stylePromptText(`Auto-fix scan (${options.dryRun ? "preview" : "apply"})`, "accent"));
2938
- console.log(formatResultSummary([
2939
- { text: `${reportSummary.healthy} working`, tone: "success" },
2940
- { text: `${reportSummary.disabled} disabled`, tone: reportSummary.disabled > 0 ? "danger" : "muted" },
2941
- {
2942
- text: `${reportSummary.warnings} warning${reportSummary.warnings === 1 ? "" : "s"}`,
2943
- tone: reportSummary.warnings > 0 ? "warning" : "muted",
2944
- },
2945
- { text: `${reportSummary.skipped} already disabled`, tone: "muted" },
2946
- ]));
2947
- if (display.showPerAccountRows) {
2948
- console.log("");
2949
- for (const report of reports) {
2950
- const prefix = report.outcome === "healthy"
2951
- ? "✓"
2952
- : report.outcome === "disabled-hard-failure"
2953
- ? "✗"
2954
- : report.outcome === "warning-soft-failure"
2955
- ? "!"
2956
- : "-";
2957
- const tone = report.outcome === "healthy"
2958
- ? "success"
2959
- : report.outcome === "disabled-hard-failure"
2960
- ? "danger"
2961
- : report.outcome === "warning-soft-failure"
2962
- ? "warning"
2963
- : "muted";
2964
- console.log(`${stylePromptText(prefix, tone)} ${stylePromptText(`${report.index + 1}. ${report.label}`, "accent")} ${stylePromptText("|", "muted")} ${styleAccountDetailText(report.message, tone === "success" ? "muted" : tone)}`);
2965
- }
2966
- }
2967
- else {
2968
- console.log("");
2969
- console.log(stylePromptText("Per-account lines are hidden in dashboard settings.", "muted"));
2970
- }
2971
- if (display.showRecommendations) {
2972
- console.log("");
2973
- if (recommendation.recommendedIndex !== null) {
2974
- const target = recommendation.recommendedIndex + 1;
2975
- console.log(`${stylePromptText("Best next account:", "accent")} ${stylePromptText(String(target), "success")}`);
2976
- console.log(`${stylePromptText("Why:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2977
- if (recommendation.recommendedIndex !== activeIndex) {
2978
- console.log(`${stylePromptText("Switch now with:", "accent")} codex auth switch ${target}`);
2979
- }
2980
- }
2981
- else {
2982
- console.log(`${stylePromptText("Note:", "accent")} ${stylePromptText(recommendation.reason, "muted")}`);
2983
- }
2984
- }
2985
- if (workingQuotaCache && quotaCacheChanged) {
2986
- await saveQuotaCache(workingQuotaCache);
2987
- }
2988
- if (changed && options.dryRun) {
2989
- console.log(`\n${stylePromptText("Preview only: no changes were saved.", "warning")}`);
2990
- }
2991
- else if (changed) {
2992
- console.log(`\n${stylePromptText("Saved updates.", "success")}`);
2993
- }
2994
- else {
2995
- console.log(`\n${stylePromptText("No changes were needed.", "muted")}`);
2996
- }
2997
- return 0;
2998
- }
2999
- function hasPlaceholderEmail(value) {
3000
- if (!value)
3001
- return false;
3002
- const email = value.trim().toLowerCase();
3003
- if (!email)
3004
- return false;
3005
- return (email.endsWith("@example.com") ||
3006
- email.includes("account1@example.com") ||
3007
- email.includes("account2@example.com") ||
3008
- email.includes("account3@example.com"));
3009
- }
3010
- function normalizeDoctorIndexes(storage) {
3011
- const total = storage.accounts.length;
3012
- const nextActive = total === 0 ? 0 : Math.max(0, Math.min(storage.activeIndex, total - 1));
3013
- let changed = false;
3014
- if (storage.activeIndex !== nextActive) {
3015
- storage.activeIndex = nextActive;
3016
- changed = true;
3017
- }
3018
- storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
3019
- for (const family of MODEL_FAMILIES) {
3020
- const raw = storage.activeIndexByFamily[family];
3021
- const fallback = storage.activeIndex;
3022
- const candidate = typeof raw === "number" && Number.isFinite(raw) ? raw : fallback;
3023
- const clamped = total === 0 ? 0 : Math.max(0, Math.min(candidate, total - 1));
3024
- if (storage.activeIndexByFamily[family] !== clamped) {
3025
- storage.activeIndexByFamily[family] = clamped;
3026
- changed = true;
3027
- }
3028
- }
3029
- return changed;
3030
- }
3031
- function getDoctorRefreshTokenKey(refreshToken) {
3032
- if (typeof refreshToken !== "string")
3033
- return undefined;
3034
- const trimmed = refreshToken.trim();
3035
- return trimmed || undefined;
3036
- }
3037
- function applyDoctorFixes(storage) {
3038
- let changed = false;
3039
- const actions = [];
3040
- if (normalizeDoctorIndexes(storage)) {
3041
- changed = true;
3042
- actions.push({
3043
- key: "active-index",
3044
- message: "Normalized active account indexes",
3045
- });
3046
- }
3047
- const seenRefreshTokens = new Map();
3048
- for (let i = 0; i < storage.accounts.length; i += 1) {
3049
- const account = storage.accounts[i];
3050
- if (!account)
3051
- continue;
3052
- const refreshToken = getDoctorRefreshTokenKey(account.refreshToken);
3053
- if (!refreshToken)
3054
- continue;
3055
- const existingTokenIndex = seenRefreshTokens.get(refreshToken);
3056
- if (typeof existingTokenIndex === "number") {
3057
- if (account.enabled !== false) {
3058
- account.enabled = false;
3059
- changed = true;
3060
- actions.push({
3061
- key: "duplicate-refresh-token",
3062
- message: `Disabled duplicate token entry on account ${i + 1} (kept account ${existingTokenIndex + 1})`,
3063
- });
3064
- }
3065
- }
3066
- else {
3067
- seenRefreshTokens.set(refreshToken, i);
3068
- }
3069
- const tokenEmail = sanitizeEmail(extractAccountEmail(account.accessToken));
3070
- if (tokenEmail &&
3071
- (!sanitizeEmail(account.email) || hasPlaceholderEmail(account.email))) {
3072
- account.email = tokenEmail;
3073
- changed = true;
3074
- actions.push({
3075
- key: "email-from-token",
3076
- message: `Updated account ${i + 1} email from token claims`,
3077
- });
3078
- }
3079
- const tokenAccountId = extractAccountId(account.accessToken);
3080
- if (!account.accountId && tokenAccountId) {
3081
- account.accountId = tokenAccountId;
3082
- account.accountIdSource = "token";
3083
- changed = true;
3084
- actions.push({
3085
- key: "account-id-from-token",
3086
- message: `Filled missing accountId for account ${i + 1}`,
3087
- });
3088
- }
3089
- }
3090
- const enabledCount = storage.accounts.filter((account) => account.enabled !== false).length;
3091
- if (storage.accounts.length > 0 && enabledCount === 0) {
3092
- const index = resolveActiveIndex(storage, "codex");
3093
- const candidate = storage.accounts[index] ?? storage.accounts[0];
3094
- if (candidate) {
3095
- candidate.enabled = true;
3096
- changed = true;
3097
- actions.push({
3098
- key: "enabled-accounts",
3099
- message: `Re-enabled account ${index + 1} to avoid an all-disabled pool`,
3100
- });
3101
- }
3102
- }
3103
- if (normalizeDoctorIndexes(storage)) {
3104
- changed = true;
3105
- }
3106
- return { changed, actions };
3107
- }
3108
- async function runDoctor(args) {
3109
- if (args.includes("--help") || args.includes("-h")) {
3110
- printDoctorUsage();
3111
- return 0;
3112
- }
3113
- const parsedArgs = parseDoctorArgs(args);
3114
- if (!parsedArgs.ok) {
3115
- console.error(parsedArgs.message);
3116
- printDoctorUsage();
3117
- return 1;
3118
- }
3119
- const options = parsedArgs.options;
3120
- setStoragePath(null);
3121
- const storagePath = getStoragePath();
3122
- const checks = [];
3123
- const addCheck = (check) => {
3124
- checks.push(check);
3125
- };
3126
- addCheck({
3127
- key: "storage-file",
3128
- severity: existsSync(storagePath) ? "ok" : "warn",
3129
- message: existsSync(storagePath)
3130
- ? "Account storage file found"
3131
- : "Account storage file does not exist yet (first login pending)",
3132
- details: storagePath,
3133
- });
3134
- if (existsSync(storagePath)) {
3135
- try {
3136
- const stat = await fs.stat(storagePath);
3137
- addCheck({
3138
- key: "storage-readable",
3139
- severity: stat.size > 0 ? "ok" : "warn",
3140
- message: stat.size > 0 ? "Storage file is readable" : "Storage file is empty",
3141
- details: `${stat.size} bytes`,
3142
- });
3143
- }
3144
- catch (error) {
3145
- addCheck({
3146
- key: "storage-readable",
3147
- severity: "error",
3148
- message: "Unable to read storage file metadata",
3149
- details: error instanceof Error ? error.message : String(error),
3150
- });
3151
- }
3152
- }
3153
- const codexAuthPath = getCodexCliAuthPath();
3154
- const codexConfigPath = getCodexCliConfigPath();
3155
- let codexAuthEmail;
3156
- let codexAuthAccountId;
3157
- addCheck({
3158
- key: "codex-auth-file",
3159
- severity: existsSync(codexAuthPath) ? "ok" : "warn",
3160
- message: existsSync(codexAuthPath)
3161
- ? "Codex auth file found"
3162
- : "Codex auth file does not exist",
3163
- details: codexAuthPath,
3164
- });
3165
- if (existsSync(codexAuthPath)) {
3166
- try {
3167
- const raw = await fs.readFile(codexAuthPath, "utf-8");
3168
- const parsed = JSON.parse(raw);
3169
- if (parsed && typeof parsed === "object") {
3170
- const payload = parsed;
3171
- const tokens = payload.tokens && typeof payload.tokens === "object"
3172
- ? payload.tokens
3173
- : null;
3174
- const accessToken = tokens && typeof tokens.access_token === "string"
3175
- ? tokens.access_token
3176
- : undefined;
3177
- const idToken = tokens && typeof tokens.id_token === "string"
3178
- ? tokens.id_token
3179
- : undefined;
3180
- const accountIdFromFile = tokens && typeof tokens.account_id === "string"
3181
- ? tokens.account_id
3182
- : undefined;
3183
- const emailFromFile = typeof payload.email === "string" ? payload.email : undefined;
3184
- codexAuthEmail = sanitizeEmail(emailFromFile ?? extractAccountEmail(accessToken, idToken));
3185
- codexAuthAccountId = accountIdFromFile ?? extractAccountId(accessToken);
3186
- }
3187
- addCheck({
3188
- key: "codex-auth-readable",
3189
- severity: "ok",
3190
- message: "Codex auth file is readable",
3191
- details: codexAuthEmail || codexAuthAccountId
3192
- ? `email=${codexAuthEmail ?? "unknown"}, accountId=${codexAuthAccountId ?? "unknown"}`
3193
- : undefined,
3194
- });
3195
- }
3196
- catch (error) {
3197
- addCheck({
3198
- key: "codex-auth-readable",
3199
- severity: "error",
3200
- message: "Unable to read Codex auth file",
3201
- details: error instanceof Error ? error.message : String(error),
3202
- });
3203
- }
3204
- }
3205
- addCheck({
3206
- key: "codex-config-file",
3207
- severity: existsSync(codexConfigPath) ? "ok" : "warn",
3208
- message: existsSync(codexConfigPath)
3209
- ? "Codex config file found"
3210
- : "Codex config file does not exist",
3211
- details: codexConfigPath,
3212
- });
3213
- let codexAuthStoreMode;
3214
- if (existsSync(codexConfigPath)) {
3215
- try {
3216
- const configRaw = await fs.readFile(codexConfigPath, "utf-8");
3217
- const match = configRaw.match(/^\s*cli_auth_credentials_store\s*=\s*"([^"]+)"\s*$/m);
3218
- if (match?.[1]) {
3219
- codexAuthStoreMode = match[1].trim();
3220
- }
3221
- }
3222
- catch (error) {
3223
- addCheck({
3224
- key: "codex-auth-store",
3225
- severity: "warn",
3226
- message: "Unable to read Codex auth-store config",
3227
- details: error instanceof Error ? error.message : String(error),
3228
- });
3229
- }
3230
- }
3231
- if (!checks.some((check) => check.key === "codex-auth-store")) {
3232
- addCheck({
3233
- key: "codex-auth-store",
3234
- severity: codexAuthStoreMode === "file" ? "ok" : "warn",
3235
- message: codexAuthStoreMode === "file"
3236
- ? "Codex auth storage is set to file"
3237
- : "Codex auth storage is not explicitly set to file",
3238
- details: codexAuthStoreMode ? `mode=${codexAuthStoreMode}` : "mode=unset",
3239
- });
3240
- }
3241
- const codexCliState = await loadCodexCliState({ forceRefresh: true });
3242
- addCheck({
3243
- key: "codex-cli-state",
3244
- severity: codexCliState ? "ok" : "warn",
3245
- message: codexCliState
3246
- ? "Codex CLI state loaded"
3247
- : "Codex CLI state unavailable",
3248
- details: codexCliState?.path,
3249
- });
3250
- const storage = await loadAccounts();
3251
- let fixChanged = false;
3252
- let fixActions = [];
3253
- if (options.fix && storage && storage.accounts.length > 0) {
3254
- const fixed = applyDoctorFixes(storage);
3255
- fixChanged = fixed.changed;
3256
- fixActions = fixed.actions;
3257
- if (fixChanged && !options.dryRun) {
3258
- await saveAccounts(storage);
3259
- }
3260
- addCheck({
3261
- key: "auto-fix",
3262
- severity: fixChanged ? "warn" : "ok",
3263
- message: fixChanged
3264
- ? options.dryRun
3265
- ? `Prepared ${fixActions.length} fix(es) (dry-run)`
3266
- : `Applied ${fixActions.length} fix(es)`
3267
- : "No safe auto-fixes needed",
3268
- });
3269
- }
3270
- if (!storage || storage.accounts.length === 0) {
3271
- addCheck({
3272
- key: "accounts",
3273
- severity: "warn",
3274
- message: "No accounts configured",
3275
- });
3276
- }
3277
- else {
3278
- addCheck({
3279
- key: "accounts",
3280
- severity: "ok",
3281
- message: `Loaded ${storage.accounts.length} account(s)`,
3282
- });
3283
- const activeIndex = resolveActiveIndex(storage, "codex");
3284
- const activeExists = activeIndex >= 0 && activeIndex < storage.accounts.length;
3285
- addCheck({
3286
- key: "active-index",
3287
- severity: activeExists ? "ok" : "error",
3288
- message: activeExists
3289
- ? `Active index is valid (${activeIndex + 1})`
3290
- : "Active index is out of range",
3291
- });
3292
- const disabledCount = storage.accounts.filter((a) => a.enabled === false).length;
3293
- addCheck({
3294
- key: "enabled-accounts",
3295
- severity: disabledCount >= storage.accounts.length ? "error" : "ok",
3296
- message: disabledCount >= storage.accounts.length
3297
- ? "All accounts are disabled"
3298
- : `${storage.accounts.length - disabledCount} enabled / ${disabledCount} disabled`,
3299
- });
3300
- const seenRefreshTokens = new Set();
3301
- let duplicateTokenCount = 0;
3302
- for (const account of storage.accounts) {
3303
- const token = getDoctorRefreshTokenKey(account.refreshToken);
3304
- if (!token)
3305
- continue;
3306
- if (seenRefreshTokens.has(token)) {
3307
- duplicateTokenCount += 1;
3308
- }
3309
- else {
3310
- seenRefreshTokens.add(token);
3311
- }
3312
- }
3313
- addCheck({
3314
- key: "duplicate-refresh-token",
3315
- severity: duplicateTokenCount > 0 ? "warn" : "ok",
3316
- message: duplicateTokenCount > 0
3317
- ? `Detected ${duplicateTokenCount} duplicate refresh token entr${duplicateTokenCount === 1 ? "y" : "ies"}`
3318
- : "No duplicate refresh tokens detected",
3319
- });
3320
- const seenEmails = new Set();
3321
- let duplicateEmailCount = 0;
3322
- let placeholderEmailCount = 0;
3323
- let likelyInvalidRefreshTokenCount = 0;
3324
- for (const account of storage.accounts) {
3325
- const email = sanitizeEmail(account.email);
3326
- if (!email)
3327
- continue;
3328
- if (seenEmails.has(email))
3329
- duplicateEmailCount += 1;
3330
- seenEmails.add(email);
3331
- if (hasPlaceholderEmail(email))
3332
- placeholderEmailCount += 1;
3333
- if (hasLikelyInvalidRefreshToken(account.refreshToken)) {
3334
- likelyInvalidRefreshTokenCount += 1;
3335
- }
3336
- }
3337
- addCheck({
3338
- key: "duplicate-email",
3339
- severity: duplicateEmailCount > 0 ? "warn" : "ok",
3340
- message: duplicateEmailCount > 0
3341
- ? `Detected ${duplicateEmailCount} duplicate email entr${duplicateEmailCount === 1 ? "y" : "ies"}`
3342
- : "No duplicate emails detected",
3343
- });
3344
- addCheck({
3345
- key: "placeholder-email",
3346
- severity: placeholderEmailCount > 0 ? "warn" : "ok",
3347
- message: placeholderEmailCount > 0
3348
- ? `${placeholderEmailCount} account(s) appear to be placeholder/demo entries`
3349
- : "No placeholder emails detected",
3350
- });
3351
- addCheck({
3352
- key: "refresh-token-shape",
3353
- severity: likelyInvalidRefreshTokenCount > 0 ? "warn" : "ok",
3354
- message: likelyInvalidRefreshTokenCount > 0
3355
- ? `${likelyInvalidRefreshTokenCount} account(s) have likely invalid refresh token format`
3356
- : "Refresh token format looks normal",
3357
- });
3358
- const now = Date.now();
3359
- const forecastResults = evaluateForecastAccounts(storage.accounts.map((account, index) => ({
3360
- index,
3361
- account,
3362
- isCurrent: index === activeIndex,
3363
- now,
3364
- })));
3365
- const recommendation = recommendForecastAccount(forecastResults);
3366
- if (recommendation.recommendedIndex !== null && recommendation.recommendedIndex !== activeIndex) {
3367
- addCheck({
3368
- key: "recommended-switch",
3369
- severity: "warn",
3370
- message: `A healthier account is available: switch to ${recommendation.recommendedIndex + 1}`,
3371
- details: recommendation.reason,
3372
- });
3373
- }
3374
- else {
3375
- addCheck({
3376
- key: "recommended-switch",
3377
- severity: "ok",
3378
- message: "Current account aligns with forecast recommendation",
3379
- });
3380
- }
3381
- if (activeExists) {
3382
- const activeAccount = storage.accounts[activeIndex];
3383
- const managerActiveEmail = sanitizeEmail(activeAccount?.email);
3384
- const managerActiveAccountId = activeAccount?.accountId;
3385
- const codexActiveEmail = sanitizeEmail(codexCliState?.activeEmail) ?? codexAuthEmail;
3386
- const codexActiveAccountId = codexCliState?.activeAccountId ?? codexAuthAccountId;
3387
- const isEmailMismatch = !!managerActiveEmail &&
3388
- !!codexActiveEmail &&
3389
- managerActiveEmail !== codexActiveEmail;
3390
- const isAccountIdMismatch = !!managerActiveAccountId &&
3391
- !!codexActiveAccountId &&
3392
- managerActiveAccountId !== codexActiveAccountId;
3393
- addCheck({
3394
- key: "active-selection-sync",
3395
- severity: isEmailMismatch || isAccountIdMismatch ? "warn" : "ok",
3396
- message: isEmailMismatch || isAccountIdMismatch
3397
- ? "Manager active account and Codex active account are not aligned"
3398
- : "Manager active account and Codex active account are aligned",
3399
- details: `manager=${managerActiveEmail ?? managerActiveAccountId ?? "unknown"} | codex=${codexActiveEmail ?? codexActiveAccountId ?? "unknown"}`,
3400
- });
3401
- if (options.fix && activeAccount) {
3402
- let syncAccessToken = activeAccount.accessToken;
3403
- let syncRefreshToken = activeAccount.refreshToken;
3404
- let syncExpiresAt = activeAccount.expiresAt;
3405
- let syncIdToken;
3406
- let storageChangedFromDoctorSync = false;
3407
- if (!hasUsableAccessToken(activeAccount, now)) {
3408
- if (options.dryRun) {
3409
- fixActions.push({
3410
- key: "doctor-refresh",
3411
- message: `Prepared active-account token refresh for account ${activeIndex + 1} (dry-run)`,
3412
- });
3413
- }
3414
- else {
3415
- const refreshResult = await queuedRefresh(activeAccount.refreshToken);
3416
- if (refreshResult.type === "success") {
3417
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
3418
- const refreshedAccountId = extractAccountId(refreshResult.access);
3419
- activeAccount.accessToken = refreshResult.access;
3420
- activeAccount.refreshToken = refreshResult.refresh;
3421
- activeAccount.expiresAt = refreshResult.expires;
3422
- if (refreshedEmail)
3423
- activeAccount.email = refreshedEmail;
3424
- applyTokenAccountIdentity(activeAccount, refreshedAccountId);
3425
- syncAccessToken = refreshResult.access;
3426
- syncRefreshToken = refreshResult.refresh;
3427
- syncExpiresAt = refreshResult.expires;
3428
- syncIdToken = refreshResult.idToken;
3429
- storageChangedFromDoctorSync = true;
3430
- fixActions.push({
3431
- key: "doctor-refresh",
3432
- message: `Refreshed active account tokens for account ${activeIndex + 1}`,
3433
- });
3434
- }
3435
- else {
3436
- addCheck({
3437
- key: "doctor-refresh",
3438
- severity: "warn",
3439
- message: "Unable to refresh active account before Codex sync",
3440
- details: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
3441
- });
3442
- }
3443
- }
3444
- }
3445
- if (storageChangedFromDoctorSync) {
3446
- fixChanged = true;
3447
- if (!options.dryRun) {
3448
- await saveAccounts(storage);
3449
- }
3450
- }
3451
- if (!options.dryRun) {
3452
- const synced = await setCodexCliActiveSelection({
3453
- accountId: activeAccount.accountId,
3454
- email: activeAccount.email,
3455
- accessToken: syncAccessToken,
3456
- refreshToken: syncRefreshToken,
3457
- expiresAt: syncExpiresAt,
3458
- ...(syncIdToken ? { idToken: syncIdToken } : {}),
3459
- });
3460
- if (synced) {
3461
- fixChanged = true;
3462
- fixActions.push({
3463
- key: "codex-active-sync",
3464
- message: "Synced manager active account into Codex auth state",
3465
- });
3466
- }
3467
- else {
3468
- addCheck({
3469
- key: "codex-active-sync",
3470
- severity: "warn",
3471
- message: "Failed to sync manager active account into Codex auth state",
3472
- });
3473
- }
3474
- }
3475
- else {
3476
- fixActions.push({
3477
- key: "codex-active-sync",
3478
- message: "Prepared Codex active-account sync (dry-run)",
3479
- });
3480
- }
3481
- }
3482
- }
3483
- }
3484
- const summary = checks.reduce((acc, check) => {
3485
- acc[check.severity] += 1;
3486
- return acc;
3487
- }, { ok: 0, warn: 0, error: 0 });
3488
- if (options.json) {
3489
- console.log(JSON.stringify({
3490
- command: "doctor",
3491
- storagePath,
3492
- summary,
3493
- checks,
3494
- fix: {
3495
- enabled: options.fix,
3496
- dryRun: options.dryRun,
3497
- changed: fixChanged,
3498
- actions: fixActions,
3499
- },
3500
- }, null, 2));
3501
- return summary.error > 0 ? 1 : 0;
3502
- }
3503
- console.log("Doctor diagnostics");
3504
- console.log(`Storage: ${storagePath}`);
3505
- console.log(`Summary: ${summary.ok} ok, ${summary.warn} warnings, ${summary.error} errors`);
3506
- console.log("");
3507
- for (const check of checks) {
3508
- const marker = check.severity === "ok" ? "✓" : check.severity === "warn" ? "!" : "✗";
3509
- console.log(`${marker} ${check.key}: ${check.message}`);
3510
- if (check.details) {
3511
- console.log(` ${check.details}`);
3512
- }
3513
- }
3514
- if (options.fix) {
3515
- console.log("");
3516
- if (fixActions.length > 0) {
3517
- console.log(`Auto-fix actions (${options.dryRun ? "dry-run" : "applied"}):`);
3518
- for (const action of fixActions) {
3519
- console.log(` - ${action.message}`);
3520
- }
3521
- }
3522
- else {
3523
- console.log("Auto-fix actions: none");
3524
- }
3525
- }
3526
- return summary.error > 0 ? 1 : 0;
3527
- }
3528
- async function clearAccountsAndReset() {
3529
- await clearAccounts();
3530
- }
3531
- async function handleManageAction(storage, menuResult) {
3532
- if (typeof menuResult.switchAccountIndex === "number") {
3533
- const index = menuResult.switchAccountIndex;
3534
- await runSwitch([String(index + 1)]);
3535
- return;
3536
- }
3537
- if (typeof menuResult.deleteAccountIndex === "number") {
3538
- const idx = menuResult.deleteAccountIndex;
3539
- if (idx >= 0 && idx < storage.accounts.length) {
3540
- storage.accounts.splice(idx, 1);
3541
- storage.activeIndex = 0;
3542
- storage.activeIndexByFamily = {};
3543
- for (const family of MODEL_FAMILIES) {
3544
- storage.activeIndexByFamily[family] = 0;
3545
- }
3546
- await saveAccounts(storage);
3547
- console.log(`Deleted account ${idx + 1}.`);
1917
+ if (deleted) {
1918
+ console.log(`Deleted account ${idx + 1}.`);
3548
1919
  }
3549
1920
  return;
3550
1921
  }
3551
1922
  if (typeof menuResult.toggleAccountIndex === "number") {
3552
1923
  const idx = menuResult.toggleAccountIndex;
3553
- const account = storage.accounts[idx];
3554
- if (account) {
3555
- account.enabled = account.enabled === false;
3556
- await saveAccounts(storage);
3557
- 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}.`);
3558
1947
  }
3559
1948
  return;
3560
1949
  }
@@ -3585,7 +1974,7 @@ async function handleManageAction(storage, menuResult) {
3585
1974
  async function runAuthLogin(args) {
3586
1975
  const parsedArgs = parseAuthLoginArgs(args);
3587
1976
  if (!parsedArgs.ok) {
3588
- if (parsedArgs.message) {
1977
+ if (parsedArgs.reason === "error") {
3589
1978
  console.error(parsedArgs.message);
3590
1979
  printUsage();
3591
1980
  return 1;
@@ -3633,7 +2022,9 @@ async function runAuthLogin(args) {
3633
2022
  const flaggedStorage = await loadFlaggedAccounts();
3634
2023
  const menuResult = await promptLoginMode(toExistingAccountInfo(currentStorage, quotaCache, displaySettings), {
3635
2024
  flaggedCount: flaggedStorage.accounts.length,
3636
- statusMessage: showFetchStatus ? () => menuQuotaRefreshStatus : undefined,
2025
+ statusMessage: showFetchStatus
2026
+ ? () => menuQuotaRefreshStatus
2027
+ : undefined,
3637
2028
  });
3638
2029
  if (menuResult.mode === "cancel") {
3639
2030
  console.log("Cancelled.");
@@ -3659,7 +2050,7 @@ async function runAuthLogin(args) {
3659
2050
  }
3660
2051
  if (menuResult.mode === "fix") {
3661
2052
  await runActionPanel("Auto-Fix", "Checking and fixing common issues", async () => {
3662
- await runFix(["--live"]);
2053
+ await runRepairFix(["--live"], createRepairCommandDeps());
3663
2054
  }, displaySettings);
3664
2055
  continue;
3665
2056
  }
@@ -3669,7 +2060,7 @@ async function runAuthLogin(args) {
3669
2060
  }
3670
2061
  if (menuResult.mode === "verify-flagged") {
3671
2062
  await runActionPanel("Problem Account Check", "Checking problem accounts", async () => {
3672
- await runVerifyFlagged([]);
2063
+ await runRepairVerifyFlagged([], createRepairCommandDeps());
3673
2064
  }, displaySettings);
3674
2065
  continue;
3675
2066
  }
@@ -3805,7 +2196,7 @@ async function runAuthLogin(args) {
3805
2196
  }
3806
2197
  const tokenResult = await runOAuthFlow(forceNewLogin, signInMode);
3807
2198
  if (tokenResult.type !== "success") {
3808
- if (isUserCancelledOAuth(tokenResult)) {
2199
+ if (isOAuthCancellation(tokenResult)) {
3809
2200
  if (existingCount > 0) {
3810
2201
  console.log(stylePromptText(UI_COPY.oauth.cancelledBackToMenu, "muted"));
3811
2202
  continue loginFlow;
@@ -3825,6 +2216,10 @@ async function runAuthLogin(args) {
3825
2216
  namedBackups = [];
3826
2217
  onboardingBackupDiscoveryWarning = null;
3827
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.");
3828
2223
  if (count >= ACCOUNT_LIMITS.MAX_ACCOUNTS) {
3829
2224
  console.log(`Reached maximum account limit (${ACCOUNT_LIMITS.MAX_ACCOUNTS}).`);
3830
2225
  break;
@@ -3834,47 +2229,7 @@ async function runAuthLogin(args) {
3834
2229
  break;
3835
2230
  forceNewLogin = true;
3836
2231
  }
3837
- continue loginFlow;
3838
- }
3839
- }
3840
- async function runSwitch(args) {
3841
- setStoragePath(null);
3842
- const indexArg = args[0];
3843
- if (!indexArg) {
3844
- console.error("Missing index. Usage: codex auth switch <index>");
3845
- return 1;
3846
- }
3847
- const parsed = Number.parseInt(indexArg, 10);
3848
- if (!Number.isFinite(parsed) || parsed < 1) {
3849
- console.error(`Invalid index: ${indexArg}`);
3850
- return 1;
3851
- }
3852
- const targetIndex = parsed - 1;
3853
- const storage = await loadAccounts();
3854
- if (!storage || storage.accounts.length === 0) {
3855
- console.error("No accounts configured.");
3856
- return 1;
3857
- }
3858
- if (targetIndex < 0 || targetIndex >= storage.accounts.length) {
3859
- console.error(`Index out of range. Valid range: 1-${storage.accounts.length}`);
3860
- return 1;
3861
- }
3862
- const account = storage.accounts[targetIndex];
3863
- if (!account) {
3864
- console.error(`Account ${parsed} not found.`);
3865
- return 1;
3866
- }
3867
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
3868
- storage,
3869
- targetIndex,
3870
- parsed,
3871
- switchReason: "rotation",
3872
- });
3873
- if (!synced) {
3874
- console.warn(`Switched account ${parsed} locally, but Codex auth sync did not complete. Multi-auth routing will still use this account.`);
3875
2232
  }
3876
- console.log(`Switched to account ${parsed}: ${formatAccountLabel(account, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
3877
- return 0;
3878
2233
  }
3879
2234
  async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, switchReason, initialSyncIdToken, preserveActiveIndexByFamily = false, }) {
3880
2235
  const account = storage.accounts[targetIndex];
@@ -3950,222 +2305,26 @@ async function persistAndSyncSelectedAccount({ storage, targetIndex, parsed, swi
3950
2305
  return { synced, wasDisabled };
3951
2306
  }
3952
2307
  async function runBest(args) {
3953
- if (args.includes("--help") || args.includes("-h")) {
3954
- printBestUsage();
3955
- return 0;
3956
- }
3957
- const parsedArgs = parseBestArgs(args);
3958
- if (!parsedArgs.ok) {
3959
- console.error(parsedArgs.message);
3960
- printBestUsage();
3961
- return 1;
3962
- }
3963
- const options = parsedArgs.options;
3964
- if (options.modelProvided && !options.live) {
3965
- console.error("--model requires --live for codex auth best");
3966
- printBestUsage();
3967
- return 1;
3968
- }
3969
- setStoragePath(null);
3970
- const storage = await loadAccounts();
3971
- if (!storage || storage.accounts.length === 0) {
3972
- if (options.json) {
3973
- console.log(JSON.stringify({ error: "No accounts configured." }, null, 2));
3974
- }
3975
- else {
3976
- console.log("No accounts configured.");
3977
- }
3978
- return 1;
3979
- }
3980
- const now = Date.now();
3981
- const refreshFailures = new Map();
3982
- const liveQuotaByIndex = new Map();
3983
- const probeIdTokenByIndex = new Map();
3984
- const probeRefreshedIndices = new Set();
3985
- const probeErrors = [];
3986
- let changed = false;
3987
- const printProbeNotes = () => {
3988
- if (probeErrors.length === 0)
3989
- return;
3990
- console.log(`Live check notes (${probeErrors.length}):`);
3991
- for (const error of probeErrors) {
3992
- console.log(` - ${error}`);
3993
- }
3994
- };
3995
- const persistProbeChangesIfNeeded = async () => {
3996
- if (!changed)
3997
- return;
3998
- await saveAccounts(storage);
3999
- changed = false;
4000
- };
4001
- for (let i = 0; i < storage.accounts.length; i += 1) {
4002
- const account = storage.accounts[i];
4003
- if (!account || !options.live)
4004
- continue;
4005
- if (account.enabled === false)
4006
- continue;
4007
- let probeAccessToken = account.accessToken;
4008
- let probeAccountId = account.accountId ?? extractAccountId(account.accessToken);
4009
- if (!hasUsableAccessToken(account, now)) {
4010
- const refreshResult = await queuedRefresh(account.refreshToken);
4011
- if (refreshResult.type !== "success") {
4012
- refreshFailures.set(i, {
4013
- ...refreshResult,
4014
- message: normalizeFailureDetail(refreshResult.message, refreshResult.reason),
4015
- });
4016
- continue;
4017
- }
4018
- const refreshedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
4019
- const refreshedAccountId = extractAccountId(refreshResult.access);
4020
- if (account.refreshToken !== refreshResult.refresh) {
4021
- account.refreshToken = refreshResult.refresh;
4022
- changed = true;
4023
- }
4024
- if (account.accessToken !== refreshResult.access) {
4025
- account.accessToken = refreshResult.access;
4026
- changed = true;
4027
- }
4028
- if (account.expiresAt !== refreshResult.expires) {
4029
- account.expiresAt = refreshResult.expires;
4030
- changed = true;
4031
- }
4032
- if (refreshedEmail && refreshedEmail !== account.email) {
4033
- account.email = refreshedEmail;
4034
- changed = true;
4035
- }
4036
- if (refreshedAccountId && refreshedAccountId !== account.accountId) {
4037
- account.accountId = refreshedAccountId;
4038
- account.accountIdSource = "token";
4039
- changed = true;
4040
- }
4041
- if (refreshResult.idToken) {
4042
- probeIdTokenByIndex.set(i, refreshResult.idToken);
4043
- }
4044
- probeRefreshedIndices.add(i);
4045
- probeAccessToken = account.accessToken;
4046
- probeAccountId = account.accountId ?? refreshedAccountId;
4047
- }
4048
- if (!probeAccessToken || !probeAccountId) {
4049
- probeErrors.push(`${formatAccountLabel(account, i)}: missing accountId for live probe`);
4050
- continue;
4051
- }
4052
- try {
4053
- const liveQuota = await fetchCodexQuotaSnapshot({
4054
- accountId: probeAccountId,
4055
- accessToken: probeAccessToken,
4056
- model: options.model,
4057
- });
4058
- liveQuotaByIndex.set(i, liveQuota);
4059
- }
4060
- catch (error) {
4061
- const message = normalizeFailureDetail(error instanceof Error ? error.message : String(error), undefined);
4062
- probeErrors.push(`${formatAccountLabel(account, i)}: ${message}`);
4063
- }
4064
- }
4065
- const forecastInputs = storage.accounts.map((account, index) => ({
4066
- index,
4067
- account,
4068
- isCurrent: index === resolveActiveIndex(storage, "codex"),
4069
- now,
4070
- refreshFailure: refreshFailures.get(index),
4071
- liveQuota: liveQuotaByIndex.get(index),
4072
- }));
4073
- const forecastResults = evaluateForecastAccounts(forecastInputs);
4074
- const recommendation = recommendForecastAccount(forecastResults);
4075
- if (recommendation.recommendedIndex === null) {
4076
- await persistProbeChangesIfNeeded();
4077
- if (options.json) {
4078
- console.log(JSON.stringify({
4079
- error: recommendation.reason,
4080
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
4081
- }, null, 2));
4082
- }
4083
- else {
4084
- console.log(`No best account available: ${recommendation.reason}`);
4085
- printProbeNotes();
4086
- }
4087
- return 1;
4088
- }
4089
- const bestIndex = recommendation.recommendedIndex;
4090
- const bestAccount = storage.accounts[bestIndex];
4091
- if (!bestAccount) {
4092
- await persistProbeChangesIfNeeded();
4093
- if (options.json) {
4094
- console.log(JSON.stringify({ error: "Best account not found." }, null, 2));
4095
- }
4096
- else {
4097
- console.log("Best account not found.");
4098
- }
4099
- return 1;
4100
- }
4101
- // Check if already on best account
4102
- const currentIndex = resolveActiveIndex(storage, "codex");
4103
- if (currentIndex === bestIndex) {
4104
- const shouldSyncCurrentBest = probeRefreshedIndices.has(bestIndex) || probeIdTokenByIndex.has(bestIndex);
4105
- let alreadyBestSynced;
4106
- if (changed) {
4107
- bestAccount.lastUsed = now;
4108
- await persistProbeChangesIfNeeded();
4109
- }
4110
- if (shouldSyncCurrentBest) {
4111
- alreadyBestSynced = await setCodexCliActiveSelection({
4112
- accountId: bestAccount.accountId,
4113
- email: bestAccount.email,
4114
- accessToken: bestAccount.accessToken,
4115
- refreshToken: bestAccount.refreshToken,
4116
- expiresAt: bestAccount.expiresAt,
4117
- ...(probeIdTokenByIndex.has(bestIndex)
4118
- ? { idToken: probeIdTokenByIndex.get(bestIndex) }
4119
- : {}),
4120
- });
4121
- if (!alreadyBestSynced && !options.json) {
4122
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
4123
- }
4124
- }
4125
- if (options.json) {
4126
- console.log(JSON.stringify({
4127
- message: `Already on best account: ${formatAccountLabel(bestAccount, bestIndex)}`,
4128
- accountIndex: bestIndex + 1,
4129
- reason: recommendation.reason,
4130
- ...(alreadyBestSynced !== undefined ? { synced: alreadyBestSynced } : {}),
4131
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
4132
- }, null, 2));
4133
- }
4134
- else {
4135
- console.log(`Already on best account ${bestIndex + 1}: ${formatAccountLabel(bestAccount, bestIndex)}`);
4136
- console.log(`Reason: ${recommendation.reason}`);
4137
- printProbeNotes();
4138
- }
4139
- return 0;
4140
- }
4141
- const targetIndex = bestIndex;
4142
- const parsed = targetIndex + 1;
4143
- const { synced, wasDisabled } = await persistAndSyncSelectedAccount({
4144
- storage,
4145
- targetIndex,
4146
- parsed,
4147
- switchReason: "best",
4148
- 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,
4149
2327
  });
4150
- if (options.json) {
4151
- console.log(JSON.stringify({
4152
- message: `Switched to best account: ${formatAccountLabel(bestAccount, targetIndex)}`,
4153
- accountIndex: parsed,
4154
- reason: recommendation.reason,
4155
- synced,
4156
- wasDisabled,
4157
- ...(probeErrors.length > 0 ? { probeErrors } : {}),
4158
- }, null, 2));
4159
- }
4160
- else {
4161
- console.log(`Switched to best account ${parsed}: ${formatAccountLabel(bestAccount, targetIndex)}${wasDisabled ? " (re-enabled)" : ""}`);
4162
- console.log(`Reason: ${recommendation.reason}`);
4163
- printProbeNotes();
4164
- if (!synced) {
4165
- console.warn("Codex auth sync did not complete. Multi-auth routing will still use this account.");
4166
- }
4167
- }
4168
- return 0;
4169
2328
  }
4170
2329
  export async function autoSyncActiveAccountToCodex() {
4171
2330
  setStoragePath(null);
@@ -4181,48 +2340,81 @@ export async function autoSyncActiveAccountToCodex() {
4181
2340
  if (!account) {
4182
2341
  return false;
4183
2342
  }
2343
+ const accountMatch = {
2344
+ accountId: account.accountId,
2345
+ email: account.email,
2346
+ refreshToken: account.refreshToken,
2347
+ };
4184
2348
  const now = Date.now();
4185
2349
  let syncAccessToken = account.accessToken;
4186
2350
  let syncRefreshToken = account.refreshToken;
4187
2351
  let syncExpiresAt = account.expiresAt;
4188
2352
  let syncIdToken;
2353
+ let syncAccountId = account.accountId;
2354
+ let syncEmail = account.email;
4189
2355
  let changed = false;
2356
+ let nextStoredAccount = null;
4190
2357
  if (!hasUsableAccessToken(account, now)) {
4191
2358
  const refreshResult = await queuedRefresh(account.refreshToken);
4192
- if (refreshResult.type === "success") {
4193
- const tokenAccountId = extractAccountId(refreshResult.access);
4194
- const nextEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
4195
- if (account.refreshToken !== refreshResult.refresh) {
4196
- account.refreshToken = refreshResult.refresh;
4197
- changed = true;
4198
- }
4199
- if (account.accessToken !== refreshResult.access) {
4200
- account.accessToken = refreshResult.access;
4201
- changed = true;
4202
- }
4203
- if (account.expiresAt !== refreshResult.expires) {
4204
- account.expiresAt = refreshResult.expires;
4205
- changed = true;
4206
- }
4207
- if (nextEmail && nextEmail !== account.email) {
4208
- account.email = nextEmail;
4209
- 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;
4210
2396
  }
4211
- if (applyTokenAccountIdentity(account, tokenAccountId)) {
4212
- 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;
4213
2406
  }
4214
- syncAccessToken = refreshResult.access;
4215
- syncRefreshToken = refreshResult.refresh;
4216
- syncExpiresAt = refreshResult.expires;
4217
- syncIdToken = refreshResult.idToken;
2407
+ nextStorage.accounts[targetIndex] = structuredClone(nextStoredAccount);
2408
+ await persist(nextStorage);
2409
+ persisted = true;
2410
+ });
2411
+ if (!persisted) {
2412
+ return false;
4218
2413
  }
4219
2414
  }
4220
- if (changed) {
4221
- await saveAccounts(storage);
4222
- }
4223
2415
  return setCodexCliActiveSelection({
4224
- accountId: account.accountId,
4225
- email: account.email,
2416
+ accountId: syncAccountId,
2417
+ email: syncEmail,
4226
2418
  accessToken: syncAccessToken,
4227
2419
  refreshToken: syncRefreshToken,
4228
2420
  expiresAt: syncExpiresAt,
@@ -4255,21 +2447,29 @@ export async function runCodexMultiAuthCli(rawArgs) {
4255
2447
  return runAuthLogin(rest);
4256
2448
  }
4257
2449
  if (command === "list" || command === "status") {
4258
- await showAccountStatus();
4259
- return 0;
2450
+ return runStatusCommand({
2451
+ setStoragePath,
2452
+ getStoragePath,
2453
+ loadAccounts,
2454
+ resolveActiveIndex,
2455
+ formatRateLimitEntry,
2456
+ });
4260
2457
  }
4261
2458
  if (command === "switch") {
4262
- return runSwitch(rest);
2459
+ return runSwitchCommand(rest, {
2460
+ setStoragePath,
2461
+ loadAccounts,
2462
+ persistAndSyncSelectedAccount,
2463
+ });
4263
2464
  }
4264
2465
  if (command === "check") {
4265
- await runHealthCheck({ liveProbe: true });
4266
- return 0;
2466
+ return runCheckCommand({ runHealthCheck });
4267
2467
  }
4268
2468
  if (command === "features") {
4269
- return runFeaturesReport();
2469
+ return runFeaturesCommand({ implementedFeatures: IMPLEMENTED_FEATURES });
4270
2470
  }
4271
2471
  if (command === "verify-flagged") {
4272
- return runVerifyFlagged(rest);
2472
+ return runRepairVerifyFlagged(rest, createRepairCommandDeps());
4273
2473
  }
4274
2474
  if (command === "forecast") {
4275
2475
  return runForecast(rest);
@@ -4278,13 +2478,53 @@ export async function runCodexMultiAuthCli(rawArgs) {
4278
2478
  return runBest(rest);
4279
2479
  }
4280
2480
  if (command === "report") {
4281
- return runReport(rest);
2481
+ return runReportCommand(rest, {
2482
+ setStoragePath,
2483
+ getStoragePath,
2484
+ loadAccounts,
2485
+ resolveActiveIndex,
2486
+ queuedRefresh,
2487
+ fetchCodexQuotaSnapshot,
2488
+ formatRateLimitEntry,
2489
+ normalizeFailureDetail,
2490
+ });
4282
2491
  }
4283
2492
  if (command === "fix") {
4284
- return runFix(rest);
2493
+ return runRepairFix(rest, createRepairCommandDeps());
4285
2494
  }
4286
2495
  if (command === "doctor") {
4287
- 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;
4288
2528
  }
4289
2529
  console.error(`Unknown command: ${command}`);
4290
2530
  printUsage();