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