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