codex-multi-auth 1.1.11 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1023 -1661
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts.d.ts +22 -2
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +84 -1
- package/dist/lib/accounts.js.map +1 -1
- package/dist/lib/auth/browser.d.ts +1 -0
- package/dist/lib/auth/browser.d.ts.map +1 -1
- package/dist/lib/auth/browser.js +13 -0
- package/dist/lib/auth/browser.js.map +1 -1
- package/dist/lib/auth/index.d.ts +2 -0
- package/dist/lib/auth/index.d.ts.map +1 -0
- package/dist/lib/auth/index.js +2 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/token-utils.d.ts +16 -0
- package/dist/lib/auth/token-utils.d.ts.map +1 -1
- package/dist/lib/auth/token-utils.js +13 -0
- package/dist/lib/auth/token-utils.js.map +1 -1
- package/dist/lib/codex-cli/index.d.ts +5 -0
- package/dist/lib/codex-cli/index.d.ts.map +1 -0
- package/dist/lib/codex-cli/index.js +5 -0
- package/dist/lib/codex-cli/index.js.map +1 -0
- package/dist/lib/codex-manager/auth-commands.d.ts +72 -0
- package/dist/lib/codex-manager/auth-commands.d.ts.map +1 -0
- package/dist/lib/codex-manager/auth-commands.js +643 -0
- package/dist/lib/codex-manager/auth-commands.js.map +1 -0
- package/dist/lib/codex-manager/backend-category-entry.d.ts +103 -0
- package/dist/lib/codex-manager/backend-category-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-category-entry.js +23 -0
- package/dist/lib/codex-manager/backend-category-entry.js.map +1 -0
- package/dist/lib/codex-manager/backend-category-helpers.d.ts +10 -0
- package/dist/lib/codex-manager/backend-category-helpers.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-category-helpers.js +29 -0
- package/dist/lib/codex-manager/backend-category-helpers.js.map +1 -0
- package/dist/lib/codex-manager/backend-category-prompt.d.ts +55 -0
- package/dist/lib/codex-manager/backend-category-prompt.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-category-prompt.js +178 -0
- package/dist/lib/codex-manager/backend-category-prompt.js.map +1 -0
- package/dist/lib/codex-manager/backend-settings-controller.d.ts +11 -0
- package/dist/lib/codex-manager/backend-settings-controller.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-settings-controller.js +14 -0
- package/dist/lib/codex-manager/backend-settings-controller.js.map +1 -0
- package/dist/lib/codex-manager/backend-settings-entry.d.ts +20 -0
- package/dist/lib/codex-manager/backend-settings-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-settings-entry.js +12 -0
- package/dist/lib/codex-manager/backend-settings-entry.js.map +1 -0
- package/dist/lib/codex-manager/backend-settings-helpers.d.ts +16 -0
- package/dist/lib/codex-manager/backend-settings-helpers.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-settings-helpers.js +102 -0
- package/dist/lib/codex-manager/backend-settings-helpers.js.map +1 -0
- package/dist/lib/codex-manager/backend-settings-prompt.d.ts +49 -0
- package/dist/lib/codex-manager/backend-settings-prompt.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-settings-prompt.js +121 -0
- package/dist/lib/codex-manager/backend-settings-prompt.js.map +1 -0
- package/dist/lib/codex-manager/backend-settings-schema.d.ts +55 -0
- package/dist/lib/codex-manager/backend-settings-schema.d.ts.map +1 -0
- package/dist/lib/codex-manager/backend-settings-schema.js +264 -0
- package/dist/lib/codex-manager/backend-settings-schema.js.map +1 -0
- package/dist/lib/codex-manager/behavior-settings-panel.d.ts +33 -0
- package/dist/lib/codex-manager/behavior-settings-panel.d.ts.map +1 -0
- package/dist/lib/codex-manager/behavior-settings-panel.js +180 -0
- package/dist/lib/codex-manager/behavior-settings-panel.js.map +1 -0
- package/dist/lib/codex-manager/best.d.ts +26 -0
- package/dist/lib/codex-manager/best.d.ts.map +1 -0
- package/dist/lib/codex-manager/best.js +249 -0
- package/dist/lib/codex-manager/best.js.map +1 -0
- package/dist/lib/codex-manager/commands/best.d.ts +81 -0
- package/dist/lib/codex-manager/commands/best.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/best.js +223 -0
- package/dist/lib/codex-manager/commands/best.js.map +1 -0
- package/dist/lib/codex-manager/commands/check.d.ts +7 -0
- package/dist/lib/codex-manager/commands/check.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/check.js +5 -0
- package/dist/lib/codex-manager/commands/check.js.map +1 -0
- package/dist/lib/codex-manager/commands/config-explain.d.ts +7 -0
- package/dist/lib/codex-manager/commands/config-explain.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/config-explain.js +31 -0
- package/dist/lib/codex-manager/commands/config-explain.js.map +1 -0
- package/dist/lib/codex-manager/commands/debug-bundle.d.ts +28 -0
- package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/debug-bundle.js +62 -0
- package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -0
- package/dist/lib/codex-manager/commands/doctor.d.ts +85 -0
- package/dist/lib/codex-manager/commands/doctor.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/doctor.js +424 -0
- package/dist/lib/codex-manager/commands/doctor.js.map +1 -0
- package/dist/lib/codex-manager/commands/fix.d.ts +92 -0
- package/dist/lib/codex-manager/commands/fix.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/fix.js +347 -0
- package/dist/lib/codex-manager/commands/fix.js.map +1 -0
- package/dist/lib/codex-manager/commands/forecast.d.ts +70 -0
- package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/forecast.js +281 -0
- package/dist/lib/codex-manager/commands/forecast.js.map +1 -0
- package/dist/lib/codex-manager/commands/init-config.d.ts +15 -0
- package/dist/lib/codex-manager/commands/init-config.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/init-config.js +95 -0
- package/dist/lib/codex-manager/commands/init-config.js.map +1 -0
- package/dist/lib/codex-manager/commands/report.d.ts +24 -0
- package/dist/lib/codex-manager/commands/report.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/report.js +314 -0
- package/dist/lib/codex-manager/commands/report.js.map +1 -0
- package/dist/lib/codex-manager/commands/status.d.ts +23 -0
- package/dist/lib/codex-manager/commands/status.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/status.js +50 -0
- package/dist/lib/codex-manager/commands/status.js.map +1 -0
- package/dist/lib/codex-manager/commands/switch.d.ts +22 -0
- package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/switch.js +41 -0
- package/dist/lib/codex-manager/commands/switch.js.map +1 -0
- package/dist/lib/codex-manager/commands/verify-flagged.d.ts +69 -0
- package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/verify-flagged.js +251 -0
- package/dist/lib/codex-manager/commands/verify-flagged.js.map +1 -0
- package/dist/lib/codex-manager/config-explain.d.ts +2 -0
- package/dist/lib/codex-manager/config-explain.d.ts.map +1 -0
- package/dist/lib/codex-manager/config-explain.js +62 -0
- package/dist/lib/codex-manager/config-explain.js.map +1 -0
- package/dist/lib/codex-manager/config-template.d.ts +2 -0
- package/dist/lib/codex-manager/config-template.d.ts.map +1 -0
- package/dist/lib/codex-manager/config-template.js +112 -0
- package/dist/lib/codex-manager/config-template.js.map +1 -0
- package/dist/lib/codex-manager/dashboard-display-panel.d.ts +40 -0
- package/dist/lib/codex-manager/dashboard-display-panel.d.ts.map +1 -0
- package/dist/lib/codex-manager/dashboard-display-panel.js +187 -0
- package/dist/lib/codex-manager/dashboard-display-panel.js.map +1 -0
- package/dist/lib/codex-manager/dashboard-formatters.d.ts +6 -0
- package/dist/lib/codex-manager/dashboard-formatters.d.ts.map +1 -0
- package/dist/lib/codex-manager/dashboard-formatters.js +19 -0
- package/dist/lib/codex-manager/dashboard-formatters.js.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-controller.d.ts +12 -0
- package/dist/lib/codex-manager/dashboard-settings-controller.d.ts.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-controller.js +17 -0
- package/dist/lib/codex-manager/dashboard-settings-controller.js.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-data.d.ts +10 -0
- package/dist/lib/codex-manager/dashboard-settings-data.d.ts.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-data.js +95 -0
- package/dist/lib/codex-manager/dashboard-settings-data.js.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-entry.d.ts +22 -0
- package/dist/lib/codex-manager/dashboard-settings-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/dashboard-settings-entry.js +13 -0
- package/dist/lib/codex-manager/dashboard-settings-entry.js.map +1 -0
- package/dist/lib/codex-manager/debug-bundle.d.ts +2 -0
- package/dist/lib/codex-manager/debug-bundle.d.ts.map +1 -0
- package/dist/lib/codex-manager/debug-bundle.js +128 -0
- package/dist/lib/codex-manager/debug-bundle.js.map +1 -0
- package/dist/lib/codex-manager/doctor.d.ts +20 -0
- package/dist/lib/codex-manager/doctor.d.ts.map +1 -0
- package/dist/lib/codex-manager/doctor.js +546 -0
- package/dist/lib/codex-manager/doctor.js.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-entry.d.ts +7 -0
- package/dist/lib/codex-manager/experimental-settings-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-entry.js +30 -0
- package/dist/lib/codex-manager/experimental-settings-entry.js.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-prompt.d.ts +66 -0
- package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-prompt.js +221 -0
- package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-schema.d.ts +23 -0
- package/dist/lib/codex-manager/experimental-settings-schema.d.ts.map +1 -0
- package/dist/lib/codex-manager/experimental-settings-schema.js +34 -0
- package/dist/lib/codex-manager/experimental-settings-schema.js.map +1 -0
- package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts +30 -0
- package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/experimental-sync-target-entry.js +16 -0
- package/dist/lib/codex-manager/experimental-sync-target-entry.js.map +1 -0
- package/dist/lib/codex-manager/experimental-sync-target.d.ts +23 -0
- package/dist/lib/codex-manager/experimental-sync-target.d.ts.map +1 -0
- package/dist/lib/codex-manager/experimental-sync-target.js +39 -0
- package/dist/lib/codex-manager/experimental-sync-target.js.map +1 -0
- package/dist/lib/codex-manager/fix.d.ts +26 -0
- package/dist/lib/codex-manager/fix.d.ts.map +1 -0
- package/dist/lib/codex-manager/fix.js +366 -0
- package/dist/lib/codex-manager/fix.js.map +1 -0
- package/dist/lib/codex-manager/forecast-report-commands.d.ts +54 -0
- package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -0
- package/dist/lib/codex-manager/forecast-report-commands.js +443 -0
- package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -0
- package/dist/lib/codex-manager/forecast.d.ts +26 -0
- package/dist/lib/codex-manager/forecast.d.ts.map +1 -0
- package/dist/lib/codex-manager/forecast.js +218 -0
- package/dist/lib/codex-manager/forecast.js.map +1 -0
- package/dist/lib/codex-manager/help.d.ts +36 -0
- package/dist/lib/codex-manager/help.d.ts.map +1 -0
- package/dist/lib/codex-manager/help.js +125 -0
- package/dist/lib/codex-manager/help.js.map +1 -0
- package/dist/lib/codex-manager/info.d.ts +11 -0
- package/dist/lib/codex-manager/info.d.ts.map +1 -0
- package/dist/lib/codex-manager/info.js +91 -0
- package/dist/lib/codex-manager/info.js.map +1 -0
- package/dist/lib/codex-manager/login.d.ts +40 -0
- package/dist/lib/codex-manager/login.d.ts.map +1 -0
- package/dist/lib/codex-manager/login.js +1041 -0
- package/dist/lib/codex-manager/login.js.map +1 -0
- package/dist/lib/codex-manager/repair-commands.d.ts +70 -0
- package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -0
- package/dist/lib/codex-manager/repair-commands.js +1671 -0
- package/dist/lib/codex-manager/repair-commands.js.map +1 -0
- package/dist/lib/codex-manager/report.d.ts +39 -0
- package/dist/lib/codex-manager/report.d.ts.map +1 -0
- package/dist/lib/codex-manager/report.js +243 -0
- package/dist/lib/codex-manager/report.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub-entry.d.ts +30 -0
- package/dist/lib/codex-manager/settings-hub-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-entry.js +11 -0
- package/dist/lib/codex-manager/settings-hub-entry.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub-menu.d.ts +30 -0
- package/dist/lib/codex-manager/settings-hub-menu.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-menu.js +37 -0
- package/dist/lib/codex-manager/settings-hub-menu.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub-prompt.d.ts +25 -0
- package/dist/lib/codex-manager/settings-hub-prompt.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-prompt.js +23 -0
- package/dist/lib/codex-manager/settings-hub-prompt.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub.d.ts +12 -10
- package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
- package/dist/lib/codex-manager/settings-hub.js +262 -2187
- package/dist/lib/codex-manager/settings-hub.js.map +1 -1
- package/dist/lib/codex-manager/settings-panels.d.ts +56 -0
- package/dist/lib/codex-manager/settings-panels.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-panels.js +73 -0
- package/dist/lib/codex-manager/settings-panels.js.map +1 -0
- package/dist/lib/codex-manager/settings-persist-utils.d.ts +9 -0
- package/dist/lib/codex-manager/settings-persist-utils.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-persist-utils.js +33 -0
- package/dist/lib/codex-manager/settings-persist-utils.js.map +1 -0
- package/dist/lib/codex-manager/settings-preview.d.ts +12 -0
- package/dist/lib/codex-manager/settings-preview.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-preview.js +118 -0
- package/dist/lib/codex-manager/settings-preview.js.map +1 -0
- package/dist/lib/codex-manager/settings-write-queue.d.ts +14 -0
- package/dist/lib/codex-manager/settings-write-queue.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-write-queue.js +88 -0
- package/dist/lib/codex-manager/settings-write-queue.js.map +1 -0
- package/dist/lib/codex-manager/statusline-order.d.ts +3 -0
- package/dist/lib/codex-manager/statusline-order.d.ts.map +1 -0
- package/dist/lib/codex-manager/statusline-order.js +17 -0
- package/dist/lib/codex-manager/statusline-order.js.map +1 -0
- package/dist/lib/codex-manager/statusline-settings-panel.d.ts +40 -0
- package/dist/lib/codex-manager/statusline-settings-panel.d.ts.map +1 -0
- package/dist/lib/codex-manager/statusline-settings-panel.js +167 -0
- package/dist/lib/codex-manager/statusline-settings-panel.js.map +1 -0
- package/dist/lib/codex-manager/switch.d.ts +31 -0
- package/dist/lib/codex-manager/switch.d.ts.map +1 -0
- package/dist/lib/codex-manager/switch.js +103 -0
- package/dist/lib/codex-manager/switch.js.map +1 -0
- package/dist/lib/codex-manager/theme-settings-panel.d.ts +26 -0
- package/dist/lib/codex-manager/theme-settings-panel.d.ts.map +1 -0
- package/dist/lib/codex-manager/theme-settings-panel.js +131 -0
- package/dist/lib/codex-manager/theme-settings-panel.js.map +1 -0
- package/dist/lib/codex-manager/unified-settings-controller.d.ts +27 -0
- package/dist/lib/codex-manager/unified-settings-controller.d.ts.map +1 -0
- package/dist/lib/codex-manager/unified-settings-controller.js +50 -0
- package/dist/lib/codex-manager/unified-settings-controller.js.map +1 -0
- package/dist/lib/codex-manager/unified-settings-entry.d.ts +6 -0
- package/dist/lib/codex-manager/unified-settings-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/unified-settings-entry.js +23 -0
- package/dist/lib/codex-manager/unified-settings-entry.js.map +1 -0
- package/dist/lib/codex-manager/verify-flagged.d.ts +20 -0
- package/dist/lib/codex-manager/verify-flagged.d.ts.map +1 -0
- package/dist/lib/codex-manager/verify-flagged.js +316 -0
- package/dist/lib/codex-manager/verify-flagged.js.map +1 -0
- package/dist/lib/codex-manager.d.ts +1 -0
- package/dist/lib/codex-manager.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +767 -2195
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/config.d.ts +38 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +371 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/forecast.d.ts +17 -0
- package/dist/lib/forecast.d.ts.map +1 -1
- package/dist/lib/forecast.js +33 -6
- package/dist/lib/forecast.js.map +1 -1
- package/dist/lib/named-backup-export.js +2 -2
- package/dist/lib/named-backup-export.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +8 -3
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +16 -26
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/request/failover-config.d.ts +4 -0
- package/dist/lib/request/failover-config.d.ts.map +1 -0
- package/dist/lib/request/failover-config.js +15 -0
- package/dist/lib/request/failover-config.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +28 -4
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +170 -8
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +49 -12
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +303 -108
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/helpers/tool-utils.d.ts +2 -15
- package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
- package/dist/lib/request/helpers/tool-utils.js +40 -27
- package/dist/lib/request/helpers/tool-utils.js.map +1 -1
- package/dist/lib/request/index.d.ts +5 -0
- package/dist/lib/request/index.d.ts.map +1 -0
- package/dist/lib/request/index.js +5 -0
- package/dist/lib/request/index.js.map +1 -0
- package/dist/lib/request/request-init.d.ts +3 -0
- package/dist/lib/request/request-init.d.ts.map +1 -0
- package/dist/lib/request/request-init.js +50 -0
- package/dist/lib/request/request-init.js.map +1 -0
- package/dist/lib/request/request-transformer.d.ts +19 -6
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +210 -193
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/request/response-compaction.d.ts +20 -0
- package/dist/lib/request/response-compaction.d.ts.map +1 -0
- package/dist/lib/request/response-compaction.js +124 -0
- package/dist/lib/request/response-compaction.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts +2 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -1
- package/dist/lib/request/response-handler.js +567 -11
- package/dist/lib/request/response-handler.js.map +1 -1
- package/dist/lib/request/response-metadata.d.ts +3 -0
- package/dist/lib/request/response-metadata.d.ts.map +1 -0
- package/dist/lib/request/response-metadata.js +61 -0
- package/dist/lib/request/response-metadata.js.map +1 -0
- package/dist/lib/request/trace-mode.d.ts +9 -0
- package/dist/lib/request/trace-mode.d.ts.map +1 -0
- package/dist/lib/request/trace-mode.js +14 -0
- package/dist/lib/request/trace-mode.js.map +1 -0
- package/dist/lib/request/wait-utils.d.ts +14 -0
- package/dist/lib/request/wait-utils.d.ts.map +1 -0
- package/dist/lib/request/wait-utils.js +44 -0
- package/dist/lib/request/wait-utils.js.map +1 -0
- package/dist/lib/runtime/account-check-helpers.d.ts +8 -0
- package/dist/lib/runtime/account-check-helpers.d.ts.map +1 -0
- package/dist/lib/runtime/account-check-helpers.js +30 -0
- package/dist/lib/runtime/account-check-helpers.js.map +1 -0
- package/dist/lib/runtime/account-check-types.d.ts +18 -0
- package/dist/lib/runtime/account-check-types.d.ts.map +1 -0
- package/dist/lib/runtime/account-check-types.js +12 -0
- package/dist/lib/runtime/account-check-types.js.map +1 -0
- package/dist/lib/runtime/account-check.d.ts +48 -0
- package/dist/lib/runtime/account-check.d.ts.map +1 -0
- package/dist/lib/runtime/account-check.js +202 -0
- package/dist/lib/runtime/account-check.js.map +1 -0
- package/dist/lib/runtime/account-health-check.d.ts +8 -0
- package/dist/lib/runtime/account-health-check.d.ts.map +1 -0
- package/dist/lib/runtime/account-health-check.js +30 -0
- package/dist/lib/runtime/account-health-check.js.map +1 -0
- package/dist/lib/runtime/account-manager-cache-entry.d.ts +25 -0
- package/dist/lib/runtime/account-manager-cache-entry.d.ts.map +1 -0
- package/dist/lib/runtime/account-manager-cache-entry.js +17 -0
- package/dist/lib/runtime/account-manager-cache-entry.js.map +1 -0
- package/dist/lib/runtime/account-manager-cache.d.ts +25 -0
- package/dist/lib/runtime/account-manager-cache.d.ts.map +1 -0
- package/dist/lib/runtime/account-manager-cache.js +44 -0
- package/dist/lib/runtime/account-manager-cache.js.map +1 -0
- package/dist/lib/runtime/account-pool.d.ts +23 -0
- package/dist/lib/runtime/account-pool.d.ts.map +1 -0
- package/dist/lib/runtime/account-pool.js +138 -0
- package/dist/lib/runtime/account-pool.js.map +1 -0
- package/dist/lib/runtime/account-scope.d.ts +10 -0
- package/dist/lib/runtime/account-scope.d.ts.map +1 -0
- package/dist/lib/runtime/account-scope.js +13 -0
- package/dist/lib/runtime/account-scope.js.map +1 -0
- package/dist/lib/runtime/account-select-event.d.ts +19 -0
- package/dist/lib/runtime/account-select-event.d.ts.map +1 -0
- package/dist/lib/runtime/account-select-event.js +53 -0
- package/dist/lib/runtime/account-select-event.js.map +1 -0
- package/dist/lib/runtime/account-selection.d.ts +28 -0
- package/dist/lib/runtime/account-selection.d.ts.map +1 -0
- package/dist/lib/runtime/account-selection.js +46 -0
- package/dist/lib/runtime/account-selection.js.map +1 -0
- package/dist/lib/runtime/account-state.d.ts +13 -0
- package/dist/lib/runtime/account-state.d.ts.map +1 -0
- package/dist/lib/runtime/account-state.js +38 -0
- package/dist/lib/runtime/account-state.js.map +1 -0
- package/dist/lib/runtime/account-status.d.ts +13 -0
- package/dist/lib/runtime/account-status.d.ts.map +1 -0
- package/dist/lib/runtime/account-status.js +37 -0
- package/dist/lib/runtime/account-status.js.map +1 -0
- package/dist/lib/runtime/account-storage-scope-entry.d.ts +26 -0
- package/dist/lib/runtime/account-storage-scope-entry.d.ts.map +1 -0
- package/dist/lib/runtime/account-storage-scope-entry.js +15 -0
- package/dist/lib/runtime/account-storage-scope-entry.js.map +1 -0
- package/dist/lib/runtime/auth-facade.d.ts +51 -0
- package/dist/lib/runtime/auth-facade.d.ts.map +1 -0
- package/dist/lib/runtime/auth-facade.js +48 -0
- package/dist/lib/runtime/auth-facade.js.map +1 -0
- package/dist/lib/runtime/browser-oauth-flow.d.ts +32 -0
- package/dist/lib/runtime/browser-oauth-flow.d.ts.map +1 -0
- package/dist/lib/runtime/browser-oauth-flow.js +31 -0
- package/dist/lib/runtime/browser-oauth-flow.js.map +1 -0
- package/dist/lib/runtime/capability-boost.d.ts +25 -0
- package/dist/lib/runtime/capability-boost.d.ts.map +1 -0
- package/dist/lib/runtime/capability-boost.js +26 -0
- package/dist/lib/runtime/capability-boost.js.map +1 -0
- package/dist/lib/runtime/event-handler.d.ts +53 -0
- package/dist/lib/runtime/event-handler.d.ts.map +1 -0
- package/dist/lib/runtime/event-handler.js +46 -0
- package/dist/lib/runtime/event-handler.js.map +1 -0
- package/dist/lib/runtime/flagged-verify-types.d.ts +9 -0
- package/dist/lib/runtime/flagged-verify-types.d.ts.map +1 -0
- package/dist/lib/runtime/flagged-verify-types.js +7 -0
- package/dist/lib/runtime/flagged-verify-types.js.map +1 -0
- package/dist/lib/runtime/hydrate-emails.d.ts +13 -0
- package/dist/lib/runtime/hydrate-emails.d.ts.map +1 -0
- package/dist/lib/runtime/hydrate-emails.js +56 -0
- package/dist/lib/runtime/hydrate-emails.js.map +1 -0
- package/dist/lib/runtime/live-sync-entry.d.ts +36 -0
- package/dist/lib/runtime/live-sync-entry.d.ts.map +1 -0
- package/dist/lib/runtime/live-sync-entry.js +14 -0
- package/dist/lib/runtime/live-sync-entry.js.map +1 -0
- package/dist/lib/runtime/live-sync.d.ts +35 -0
- package/dist/lib/runtime/live-sync.d.ts.map +1 -0
- package/dist/lib/runtime/live-sync.js +65 -0
- package/dist/lib/runtime/live-sync.js.map +1 -0
- package/dist/lib/runtime/loader-setup.d.ts +9 -0
- package/dist/lib/runtime/loader-setup.d.ts.map +1 -0
- package/dist/lib/runtime/loader-setup.js +8 -0
- package/dist/lib/runtime/loader-setup.js.map +1 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts +29 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -0
- package/dist/lib/runtime/login-menu-accounts.js +33 -0
- package/dist/lib/runtime/login-menu-accounts.js.map +1 -0
- package/dist/lib/runtime/manual-oauth-flow.d.ts +40 -0
- package/dist/lib/runtime/manual-oauth-flow.d.ts.map +1 -0
- package/dist/lib/runtime/manual-oauth-flow.js +71 -0
- package/dist/lib/runtime/manual-oauth-flow.js.map +1 -0
- package/dist/lib/runtime/metrics.d.ts +29 -0
- package/dist/lib/runtime/metrics.d.ts.map +1 -0
- package/dist/lib/runtime/metrics.js +97 -0
- package/dist/lib/runtime/metrics.js.map +1 -0
- package/dist/lib/runtime/oauth-browser-flow.d.ts +9 -0
- package/dist/lib/runtime/oauth-browser-flow.d.ts.map +1 -0
- package/dist/lib/runtime/oauth-browser-flow.js +34 -0
- package/dist/lib/runtime/oauth-browser-flow.js.map +1 -0
- package/dist/lib/runtime/preemptive-quota.d.ts +13 -0
- package/dist/lib/runtime/preemptive-quota.d.ts.map +1 -0
- package/dist/lib/runtime/preemptive-quota.js +9 -0
- package/dist/lib/runtime/preemptive-quota.js.map +1 -0
- package/dist/lib/runtime/quota-headers.d.ts +12 -0
- package/dist/lib/runtime/quota-headers.d.ts.map +1 -0
- package/dist/lib/runtime/quota-headers.js +131 -0
- package/dist/lib/runtime/quota-headers.js.map +1 -0
- package/dist/lib/runtime/quota-probe.d.ts +18 -0
- package/dist/lib/runtime/quota-probe.d.ts.map +1 -0
- package/dist/lib/runtime/quota-probe.js +82 -0
- package/dist/lib/runtime/quota-probe.js.map +1 -0
- package/dist/lib/runtime/quota-settings.d.ts +14 -0
- package/dist/lib/runtime/quota-settings.d.ts.map +1 -0
- package/dist/lib/runtime/quota-settings.js +12 -0
- package/dist/lib/runtime/quota-settings.js.map +1 -0
- package/dist/lib/runtime/refresh-guardian-entry.d.ts +32 -0
- package/dist/lib/runtime/refresh-guardian-entry.d.ts.map +1 -0
- package/dist/lib/runtime/refresh-guardian-entry.js +12 -0
- package/dist/lib/runtime/refresh-guardian-entry.js.map +1 -0
- package/dist/lib/runtime/refresh-guardian.d.ts +24 -0
- package/dist/lib/runtime/refresh-guardian.d.ts.map +1 -0
- package/dist/lib/runtime/refresh-guardian.js +32 -0
- package/dist/lib/runtime/refresh-guardian.js.map +1 -0
- package/dist/lib/runtime/request-init.d.ts +5 -0
- package/dist/lib/runtime/request-init.d.ts.map +1 -0
- package/dist/lib/runtime/request-init.js +50 -0
- package/dist/lib/runtime/request-init.js.map +1 -0
- package/dist/lib/runtime/runtime-services.d.ts +58 -0
- package/dist/lib/runtime/runtime-services.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-services.js +87 -0
- package/dist/lib/runtime/runtime-services.js.map +1 -0
- package/dist/lib/runtime/session-affinity-entry.d.ts +30 -0
- package/dist/lib/runtime/session-affinity-entry.d.ts.map +1 -0
- package/dist/lib/runtime/session-affinity-entry.js +11 -0
- package/dist/lib/runtime/session-affinity-entry.js.map +1 -0
- package/dist/lib/runtime/session-affinity.d.ts +13 -0
- package/dist/lib/runtime/session-affinity.d.ts.map +1 -0
- package/dist/lib/runtime/session-affinity.js +17 -0
- package/dist/lib/runtime/session-affinity.js.map +1 -0
- package/dist/lib/runtime/session-recovery.d.ts +8 -0
- package/dist/lib/runtime/session-recovery.d.ts.map +1 -0
- package/dist/lib/runtime/session-recovery.js +7 -0
- package/dist/lib/runtime/session-recovery.js.map +1 -0
- package/dist/lib/runtime/status-marker.d.ts +3 -0
- package/dist/lib/runtime/status-marker.d.ts.map +1 -0
- package/dist/lib/runtime/status-marker.js +15 -0
- package/dist/lib/runtime/status-marker.js.map +1 -0
- package/dist/lib/runtime/storage-scope.d.ts +13 -0
- package/dist/lib/runtime/storage-scope.d.ts.map +1 -0
- package/dist/lib/runtime/storage-scope.js +14 -0
- package/dist/lib/runtime/storage-scope.js.map +1 -0
- package/dist/lib/runtime/toast.d.ts +16 -0
- package/dist/lib/runtime/toast.d.ts.map +1 -0
- package/dist/lib/runtime/toast.js +16 -0
- package/dist/lib/runtime/toast.js.map +1 -0
- package/dist/lib/runtime/ui-runtime-entry.d.ts +7 -0
- package/dist/lib/runtime/ui-runtime-entry.d.ts.map +1 -0
- package/dist/lib/runtime/ui-runtime-entry.js +4 -0
- package/dist/lib/runtime/ui-runtime-entry.js.map +1 -0
- package/dist/lib/runtime/ui-runtime.d.ts +9 -0
- package/dist/lib/runtime/ui-runtime.d.ts.map +1 -0
- package/dist/lib/runtime/ui-runtime.js +23 -0
- package/dist/lib/runtime/ui-runtime.js.map +1 -0
- package/dist/lib/runtime/verify-flagged.d.ts +44 -0
- package/dist/lib/runtime/verify-flagged.d.ts.map +1 -0
- package/dist/lib/runtime/verify-flagged.js +84 -0
- package/dist/lib/runtime/verify-flagged.js.map +1 -0
- package/dist/lib/schemas.d.ts +9 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +9 -1
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/session-affinity.d.ts +10 -0
- package/dist/lib/session-affinity.d.ts.map +1 -1
- package/dist/lib/session-affinity.js +53 -6
- package/dist/lib/session-affinity.js.map +1 -1
- package/dist/lib/storage/account-clear-entry.d.ts +16 -0
- package/dist/lib/storage/account-clear-entry.d.ts.map +1 -0
- package/dist/lib/storage/account-clear-entry.js +12 -0
- package/dist/lib/storage/account-clear-entry.js.map +1 -0
- package/dist/lib/storage/account-clear.d.ts +8 -0
- package/dist/lib/storage/account-clear.d.ts.map +1 -0
- package/dist/lib/storage/account-clear.js +29 -0
- package/dist/lib/storage/account-clear.js.map +1 -0
- package/dist/lib/storage/account-match-utils.d.ts +9 -0
- package/dist/lib/storage/account-match-utils.d.ts.map +1 -0
- package/dist/lib/storage/account-match-utils.js +45 -0
- package/dist/lib/storage/account-match-utils.js.map +1 -0
- package/dist/lib/storage/account-persistence.d.ts +3 -0
- package/dist/lib/storage/account-persistence.d.ts.map +1 -0
- package/dist/lib/storage/account-persistence.js +12 -0
- package/dist/lib/storage/account-persistence.js.map +1 -0
- package/dist/lib/storage/account-port.d.ts +50 -0
- package/dist/lib/storage/account-port.d.ts.map +1 -0
- package/dist/lib/storage/account-port.js +43 -0
- package/dist/lib/storage/account-port.js.map +1 -0
- package/dist/lib/storage/account-save-entry.d.ts +7 -0
- package/dist/lib/storage/account-save-entry.d.ts.map +1 -0
- package/dist/lib/storage/account-save-entry.js +6 -0
- package/dist/lib/storage/account-save-entry.js.map +1 -0
- package/dist/lib/storage/account-save.d.ts +30 -0
- package/dist/lib/storage/account-save.d.ts.map +1 -0
- package/dist/lib/storage/account-save.js +49 -0
- package/dist/lib/storage/account-save.js.map +1 -0
- package/dist/lib/storage/account-snapshot.d.ts +25 -0
- package/dist/lib/storage/account-snapshot.d.ts.map +1 -0
- package/dist/lib/storage/account-snapshot.js +78 -0
- package/dist/lib/storage/account-snapshot.js.map +1 -0
- package/dist/lib/storage/backup-metadata-builder.d.ts +32 -0
- package/dist/lib/storage/backup-metadata-builder.d.ts.map +1 -0
- package/dist/lib/storage/backup-metadata-builder.js +77 -0
- package/dist/lib/storage/backup-metadata-builder.js.map +1 -0
- package/dist/lib/storage/backup-metadata.d.ts +29 -0
- package/dist/lib/storage/backup-metadata.d.ts.map +1 -0
- package/dist/lib/storage/backup-metadata.js +16 -0
- package/dist/lib/storage/backup-metadata.js.map +1 -0
- package/dist/lib/storage/backup-paths.d.ts +10 -0
- package/dist/lib/storage/backup-paths.d.ts.map +1 -0
- package/dist/lib/storage/backup-paths.js +27 -0
- package/dist/lib/storage/backup-paths.js.map +1 -0
- package/dist/lib/storage/backup-restore.d.ts +11 -0
- package/dist/lib/storage/backup-restore.d.ts.map +1 -0
- package/dist/lib/storage/backup-restore.js +52 -0
- package/dist/lib/storage/backup-restore.js.map +1 -0
- package/dist/lib/storage/cache-artifacts.d.ts +2 -0
- package/dist/lib/storage/cache-artifacts.d.ts.map +1 -0
- package/dist/lib/storage/cache-artifacts.js +4 -0
- package/dist/lib/storage/cache-artifacts.js.map +1 -0
- package/dist/lib/storage/error-hints.d.ts +10 -0
- package/dist/lib/storage/error-hints.d.ts.map +1 -0
- package/dist/lib/storage/error-hints.js +36 -0
- package/dist/lib/storage/error-hints.js.map +1 -0
- package/dist/lib/storage/file-paths.d.ts +6 -0
- package/dist/lib/storage/file-paths.d.ts.map +1 -0
- package/dist/lib/storage/file-paths.js +17 -0
- package/dist/lib/storage/file-paths.js.map +1 -0
- package/dist/lib/storage/fixture-guards.d.ts +4 -0
- package/dist/lib/storage/fixture-guards.d.ts.map +1 -0
- package/dist/lib/storage/fixture-guards.js +27 -0
- package/dist/lib/storage/fixture-guards.js.map +1 -0
- package/dist/lib/storage/flagged-entry.d.ts +15 -0
- package/dist/lib/storage/flagged-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-entry.js +12 -0
- package/dist/lib/storage/flagged-entry.js.map +1 -0
- package/dist/lib/storage/flagged-load-entry.d.ts +20 -0
- package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-load-entry.js +13 -0
- package/dist/lib/storage/flagged-load-entry.js.map +1 -0
- package/dist/lib/storage/flagged-save-entry.d.ts +7 -0
- package/dist/lib/storage/flagged-save-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-save-entry.js +6 -0
- package/dist/lib/storage/flagged-save-entry.js.map +1 -0
- package/dist/lib/storage/flagged-storage-file.d.ts +7 -0
- package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage-file.js +25 -0
- package/dist/lib/storage/flagged-storage-file.js.map +1 -0
- package/dist/lib/storage/flagged-storage-io.d.ts +28 -0
- package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage-io.js +135 -0
- package/dist/lib/storage/flagged-storage-io.js.map +1 -0
- package/dist/lib/storage/flagged-storage.d.ts +6 -0
- package/dist/lib/storage/flagged-storage.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage.js +90 -0
- package/dist/lib/storage/flagged-storage.js.map +1 -0
- package/dist/lib/storage/gitignore.d.ts +7 -0
- package/dist/lib/storage/gitignore.d.ts.map +1 -0
- package/dist/lib/storage/gitignore.js +34 -0
- package/dist/lib/storage/gitignore.js.map +1 -0
- package/dist/lib/storage/hash.d.ts +2 -0
- package/dist/lib/storage/hash.d.ts.map +1 -0
- package/dist/lib/storage/hash.js +5 -0
- package/dist/lib/storage/hash.js.map +1 -0
- package/dist/lib/storage/identity.d.ts +15 -0
- package/dist/lib/storage/identity.d.ts.map +1 -0
- package/dist/lib/storage/identity.js +48 -0
- package/dist/lib/storage/identity.js.map +1 -0
- package/dist/lib/storage/import-export.d.ts +24 -0
- package/dist/lib/storage/import-export.d.ts.map +1 -0
- package/dist/lib/storage/import-export.js +108 -0
- package/dist/lib/storage/import-export.js.map +1 -0
- package/dist/lib/storage/metadata-section.d.ts +16 -0
- package/dist/lib/storage/metadata-section.d.ts.map +1 -0
- package/dist/lib/storage/metadata-section.js +16 -0
- package/dist/lib/storage/metadata-section.js.map +1 -0
- package/dist/lib/storage/migrations.d.ts +5 -2
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/storage/named-backup-entry.d.ts +15 -0
- package/dist/lib/storage/named-backup-entry.d.ts.map +1 -0
- package/dist/lib/storage/named-backup-entry.js +7 -0
- package/dist/lib/storage/named-backup-entry.js.map +1 -0
- package/dist/lib/storage/named-backups-entry.d.ts +19 -0
- package/dist/lib/storage/named-backups-entry.d.ts.map +1 -0
- package/dist/lib/storage/named-backups-entry.js +7 -0
- package/dist/lib/storage/named-backups-entry.js.map +1 -0
- package/dist/lib/storage/named-backups.d.ts +15 -0
- package/dist/lib/storage/named-backups.d.ts.map +1 -0
- package/dist/lib/storage/named-backups.js +68 -0
- package/dist/lib/storage/named-backups.js.map +1 -0
- package/dist/lib/storage/path-state.d.ts +9 -0
- package/dist/lib/storage/path-state.d.ts.map +1 -0
- package/dist/lib/storage/path-state.js +20 -0
- package/dist/lib/storage/path-state.js.map +1 -0
- package/dist/lib/storage/project-migration.d.ts +10 -0
- package/dist/lib/storage/project-migration.d.ts.map +1 -0
- package/dist/lib/storage/project-migration.js +38 -0
- package/dist/lib/storage/project-migration.js.map +1 -0
- package/dist/lib/storage/record-utils.d.ts +3 -0
- package/dist/lib/storage/record-utils.d.ts.map +1 -0
- package/dist/lib/storage/record-utils.js +9 -0
- package/dist/lib/storage/record-utils.js.map +1 -0
- package/dist/lib/storage/restore-assessment.d.ts +7 -0
- package/dist/lib/storage/restore-assessment.d.ts.map +1 -0
- package/dist/lib/storage/restore-assessment.js +42 -0
- package/dist/lib/storage/restore-assessment.js.map +1 -0
- package/dist/lib/storage/restore-backup-entry.d.ts +24 -0
- package/dist/lib/storage/restore-backup-entry.d.ts.map +1 -0
- package/dist/lib/storage/restore-backup-entry.js +10 -0
- package/dist/lib/storage/restore-backup-entry.js.map +1 -0
- package/dist/lib/storage/restore-metadata.d.ts +10 -0
- package/dist/lib/storage/restore-metadata.d.ts.map +1 -0
- package/dist/lib/storage/restore-metadata.js +18 -0
- package/dist/lib/storage/restore-metadata.js.map +1 -0
- package/dist/lib/storage/restore.d.ts +14 -0
- package/dist/lib/storage/restore.d.ts.map +1 -0
- package/dist/lib/storage/restore.js +53 -0
- package/dist/lib/storage/restore.js.map +1 -0
- package/dist/lib/storage/snapshot-inspectors.d.ts +22 -0
- package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -0
- package/dist/lib/storage/snapshot-inspectors.js +95 -0
- package/dist/lib/storage/snapshot-inspectors.js.map +1 -0
- package/dist/lib/storage/storage-parser.d.ts +15 -0
- package/dist/lib/storage/storage-parser.d.ts.map +1 -0
- package/dist/lib/storage/storage-parser.js +16 -0
- package/dist/lib/storage/storage-parser.js.map +1 -0
- package/dist/lib/storage/transactions.d.ts +23 -0
- package/dist/lib/storage/transactions.d.ts.map +1 -0
- package/dist/lib/storage/transactions.js +64 -0
- package/dist/lib/storage/transactions.js.map +1 -0
- package/dist/lib/storage.d.ts +12 -41
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +527 -938
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/types.d.ts +80 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/ui/copy.d.ts +24 -4
- package/dist/lib/ui/copy.d.ts.map +1 -1
- package/dist/lib/ui/copy.js +24 -4
- package/dist/lib/ui/copy.js.map +1 -1
- package/package.json +44 -6
- package/scripts/check-pack-budget-lib.js +114 -0
- package/scripts/check-pack-budget.mjs +10 -0
- package/scripts/codex-multi-auth.js +34 -10
- package/scripts/install-codex-auth-utils.js +54 -14
- package/scripts/install-codex-auth.js +62 -33
- package/scripts/update-vendor-provenance.mjs +96 -0
- package/scripts/verify-vendor-provenance.mjs +53 -0
package/dist/index.js
CHANGED
|
@@ -23,39 +23,68 @@
|
|
|
23
23
|
|
|
24
24
|
*/
|
|
25
25
|
import { tool } from "@codex-ai/plugin/tool";
|
|
26
|
+
import { AccountManager, extractAccountEmail, extractAccountId, formatAccountLabel, formatCooldown, formatWaitTime, getAccountIdCandidates, isCodexCliSyncEnabled, lookupCodexCliTokensByEmail, parseRateLimitReason, resolveRequestAccountId, resolveRuntimeRequestIdentity, sanitizeEmail, selectBestAccountCandidate, shouldUpdateAccountIdFromToken, } from "./lib/accounts.js";
|
|
26
27
|
import { createAuthorizationFlow, exchangeAuthorizationCode, parseAuthorizationInput, redactOAuthUrlForLog, REDIRECT_URI, } from "./lib/auth/auth.js";
|
|
27
|
-
import {
|
|
28
|
-
import { openBrowserUrl } from "./lib/auth/browser.js";
|
|
28
|
+
import { isBrowserLaunchSuppressed, openBrowserUrl } from "./lib/auth/browser.js";
|
|
29
29
|
import { startLocalOAuthServer } from "./lib/auth/server.js";
|
|
30
|
-
import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
|
|
31
|
-
import { getCodexMode, getFastSession, getFastSessionStrategy, getFastSessionMaxInputItems, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getFallbackToGpt52OnUnsupportedGpt53, getUnsupportedCodexPolicy, getUnsupportedCodexFallbackChain, getTokenRefreshSkewMs, getSessionRecovery, getAutoResume, getToastDurationMs, getPerProjectAccounts, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getPidOffsetEnabled, getFetchTimeoutMs, getStreamStallTimeoutMs, getCodexTuiV2, getCodexTuiColorProfile, getCodexTuiGlyphMode, getLiveAccountSync, getLiveAccountSyncDebounceMs, getLiveAccountSyncPollMs, getSessionAffinity, getSessionAffinityTtlMs, getSessionAffinityMaxEntries, getProactiveRefreshGuardian, getProactiveRefreshIntervalMs, getProactiveRefreshBufferMs, getNetworkErrorCooldownMs, getServerErrorCooldownMs, getStorageBackupEnabled, getPreemptiveQuotaEnabled, getPreemptiveQuotaRemainingPercent5h, getPreemptiveQuotaRemainingPercent7d, getPreemptiveQuotaMaxDeferralMs, loadPluginConfig, } from "./lib/config.js";
|
|
32
|
-
import { AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, ACCOUNT_LIMITS, } from "./lib/constants.js";
|
|
33
|
-
import { initLogger, logRequest, logDebug, logInfo, logWarn, logError, setCorrelationId, clearCorrelationId, } from "./lib/logger.js";
|
|
34
30
|
import { checkAndNotify } from "./lib/auto-update-checker.js";
|
|
31
|
+
import { CapabilityPolicyStore } from "./lib/capability-policy.js";
|
|
32
|
+
import { promptAddAnotherAccount, promptLoginMode } from "./lib/cli.js";
|
|
33
|
+
import { getAutoResume, getCodexMode, getEmptyResponseMaxRetries, getEmptyResponseRetryDelayMs, getFallbackToGpt52OnUnsupportedGpt53, getFastSession, getFastSessionMaxInputItems, getFastSessionStrategy, getFetchTimeoutMs, getLiveAccountSync, getLiveAccountSyncDebounceMs, getLiveAccountSyncPollMs, getNetworkErrorCooldownMs, getPerProjectAccounts, getPidOffsetEnabled, getPreemptiveQuotaEnabled, getPreemptiveQuotaMaxDeferralMs, getPreemptiveQuotaRemainingPercent5h, getPreemptiveQuotaRemainingPercent7d, getProactiveRefreshBufferMs, getProactiveRefreshGuardian, getProactiveRefreshIntervalMs, getRateLimitToastDebounceMs, getRetryAllAccountsMaxRetries, getRetryAllAccountsMaxWaitMs, getRetryAllAccountsRateLimited, getBackgroundResponses, getResponseContinuation, getServerErrorCooldownMs, getSessionAffinity, getSessionAffinityMaxEntries, getSessionAffinityTtlMs, getSessionRecovery, getStorageBackupEnabled, getStreamStallTimeoutMs, getToastDurationMs, getTokenRefreshSkewMs, getUnsupportedCodexFallbackChain, getUnsupportedCodexPolicy, loadPluginConfig, } from "./lib/config.js";
|
|
34
|
+
import { ACCOUNT_LIMITS, AUTH_LABELS, CODEX_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, } from "./lib/constants.js";
|
|
35
35
|
import { handleContextOverflow } from "./lib/context-overflow.js";
|
|
36
|
-
import {
|
|
37
|
-
import { getStoragePath, loadAccounts, saveAccounts, withAccountStorageTransaction, clearAccounts, setStoragePath, exportAccounts, importAccounts, loadFlaggedAccounts, saveFlaggedAccounts, clearFlaggedAccounts, findMatchingAccountIndex, StorageError, formatStorageErrorHint, setStorageBackupEnabled, } from "./lib/storage.js";
|
|
38
|
-
import { createCodexHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, getUnsupportedCodexModelInfo, resolveUnsupportedCodexFallbackModel, refreshAndUpdateToken, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
39
|
-
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
40
|
-
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
41
|
-
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
42
|
-
import { addJitter } from "./lib/rotation.js";
|
|
43
|
-
import { SessionAffinityStore } from "./lib/session-affinity.js";
|
|
36
|
+
import { EntitlementCache, resolveEntitlementAccountKey, } from "./lib/entitlement-cache.js";
|
|
44
37
|
import { LiveAccountSync } from "./lib/live-account-sync.js";
|
|
38
|
+
import { clearCorrelationId, initLogger, logDebug, logError, logInfo, logRequest, logWarn, setCorrelationId, } from "./lib/logger.js";
|
|
39
|
+
import { PreemptiveQuotaScheduler, readQuotaSchedulerSnapshot, } from "./lib/preemptive-quota-scheduler.js";
|
|
40
|
+
import { getModelFamily, MODEL_FAMILIES, prewarmCodexInstructions, } from "./lib/prompts/codex.js";
|
|
41
|
+
import { prewarmHostCodexPrompt } from "./lib/prompts/host-codex-prompt.js";
|
|
42
|
+
import { fetchCodexQuotaSnapshot, formatQuotaSnapshotLine, } from "./lib/quota-probe.js";
|
|
43
|
+
import { detectErrorType, getRecoveryToastContent, isRecoverableError, } from "./lib/recovery.js";
|
|
45
44
|
import { RefreshGuardian } from "./lib/refresh-guardian.js";
|
|
45
|
+
import { queuedRefresh } from "./lib/refresh-queue.js";
|
|
46
|
+
import { parseEnvInt, parseFailoverMode, } from "./lib/request/failover-config.js";
|
|
46
47
|
import { evaluateFailurePolicy, } from "./lib/request/failure-policy.js";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
48
|
+
import { applyProxyCompatibleInit, createCodexHeaders, extractRequestUrl, getUnsupportedCodexModelInfo, handleErrorResponse, handleSuccessResponse, isWorkspaceDisabledError, refreshAndUpdateToken, resolveUnsupportedCodexFallbackModel, rewriteUrlForCodex, shouldRefreshToken, transformRequestForCodex, } from "./lib/request/fetch-helpers.js";
|
|
49
|
+
import { getRateLimitBackoff, RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS, resetRateLimitBackoff, } from "./lib/request/rate-limit-backoff.js";
|
|
50
|
+
import { normalizeRequestInit, parseRequestBodyFromInit, } from "./lib/request/request-init.js";
|
|
51
|
+
import { applyFastSessionDefaults } from "./lib/request/request-transformer.js";
|
|
52
|
+
import { applyResponseCompaction } from "./lib/request/response-compaction.js";
|
|
53
|
+
import { isEmptyResponse } from "./lib/request/response-handler.js";
|
|
54
|
+
import { parseRetryAfterHintMs, sanitizeResponseHeadersForLog, } from "./lib/request/response-metadata.js";
|
|
50
55
|
import { withStreamingFailover } from "./lib/request/stream-failover.js";
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
56
|
+
import { createAbortableSleep, sleepWithCountdown, } from "./lib/request/wait-utils.js";
|
|
57
|
+
import { addJitter } from "./lib/rotation.js";
|
|
58
|
+
import { clampActiveIndices, isFlaggableFailure, } from "./lib/runtime/account-check-helpers.js";
|
|
59
|
+
import { runRuntimeAccountCheck } from "./lib/runtime/account-check.js";
|
|
60
|
+
import { createAccountCheckWorkingState } from "./lib/runtime/account-check-types.js";
|
|
61
|
+
import { invalidateRuntimeAccountManagerCache, reloadRuntimeAccountManager, } from "./lib/runtime/account-manager-cache.js";
|
|
62
|
+
import { persistAccountPoolResults, } from "./lib/runtime/account-pool.js";
|
|
63
|
+
import { resolveAccountSelection } from "./lib/runtime/account-selection.js";
|
|
64
|
+
import { formatRateLimitEntry, getRateLimitResetTimeForFamily, resolveActiveIndex, } from "./lib/runtime/account-status.js";
|
|
65
|
+
import { createPersistAccounts, runRuntimeOAuthFlow, } from "./lib/runtime/auth-facade.js";
|
|
66
|
+
import { applyAccountStorageScopeEntry } from "./lib/runtime/account-storage-scope-entry.js";
|
|
67
|
+
import { runBrowserOAuthFlow } from "./lib/runtime/browser-oauth-flow.js";
|
|
68
|
+
import { handleRuntimeEvent } from "./lib/runtime/event-handler.js";
|
|
69
|
+
import { hydrateRuntimeEmails } from "./lib/runtime/hydrate-emails.js";
|
|
70
|
+
import { buildLoginMenuAccounts } from "./lib/runtime/login-menu-accounts.js";
|
|
71
|
+
import { ensureLiveAccountSyncEntry } from "./lib/runtime/live-sync-entry.js";
|
|
72
|
+
import { applyLoaderRuntimeSetup } from "./lib/runtime/loader-setup.js";
|
|
73
|
+
import { buildManualOAuthFlow } from "./lib/runtime/manual-oauth-flow.js";
|
|
74
|
+
import { applyPreemptiveQuotaSettingsFromConfig, } from "./lib/runtime/quota-settings.js";
|
|
75
|
+
import { ensureLiveAccountSyncState, ensureRefreshGuardianState, ensureSessionAffinityState, } from "./lib/runtime/runtime-services.js";
|
|
76
|
+
import { applyAccountStorageScopeFromConfig } from "./lib/runtime/storage-scope.js";
|
|
77
|
+
import { showRuntimeToast } from "./lib/runtime/toast.js";
|
|
78
|
+
import { createRuntimeSessionRecoveryHook } from "./lib/runtime/session-recovery.js";
|
|
79
|
+
import { applyUiRuntimeFromConfig, getStatusMarker, } from "./lib/runtime/ui-runtime.js";
|
|
80
|
+
import { verifyRuntimeFlaggedAccounts } from "./lib/runtime/verify-flagged.js";
|
|
81
|
+
import { SessionAffinityStore } from "./lib/session-affinity.js";
|
|
58
82
|
import { registerCleanup } from "./lib/shutdown.js";
|
|
83
|
+
import { clearAccounts, clearFlaggedAccounts, exportAccounts, findMatchingAccountIndex, formatStorageErrorHint, getStoragePath, importAccounts, loadAccounts, loadFlaggedAccounts, StorageError, saveAccounts, saveFlaggedAccounts, setStorageBackupEnabled, setStoragePath, withAccountStorageTransaction, } from "./lib/storage.js";
|
|
84
|
+
import { buildTableHeader, buildTableRow, } from "./lib/table-formatter.js";
|
|
85
|
+
import { createHashlineEditTool, createHashlineReadTool, } from "./lib/tools/hashline-tools.js";
|
|
86
|
+
import { formatUiBadge, formatUiHeader, formatUiItem, formatUiKeyValue, formatUiSection, paintUiText, } from "./lib/ui/format.js";
|
|
87
|
+
import { setUiRuntimeOptions, } from "./lib/ui/runtime.js";
|
|
59
88
|
/**
|
|
60
89
|
* OpenAI Codex OAuth authentication plugin for Codex CLI host runtime
|
|
61
90
|
*
|
|
@@ -85,6 +114,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
85
114
|
let liveAccountSyncPath = null;
|
|
86
115
|
let refreshGuardian = null;
|
|
87
116
|
let refreshGuardianConfigKey = null;
|
|
117
|
+
let refreshGuardianCleanupRegistered = false;
|
|
88
118
|
let sessionAffinityStore = new SessionAffinityStore();
|
|
89
119
|
let sessionAffinityConfigKey = null;
|
|
90
120
|
const entitlementCache = new EntitlementCache();
|
|
@@ -103,80 +133,6 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
103
133
|
balanced: 15_000,
|
|
104
134
|
conservative: 20_000,
|
|
105
135
|
};
|
|
106
|
-
const parseFailoverMode = (value) => {
|
|
107
|
-
const normalized = (value ?? "").trim().toLowerCase();
|
|
108
|
-
if (normalized === "aggressive")
|
|
109
|
-
return "aggressive";
|
|
110
|
-
if (normalized === "conservative")
|
|
111
|
-
return "conservative";
|
|
112
|
-
return "balanced";
|
|
113
|
-
};
|
|
114
|
-
const parseEnvInt = (value) => {
|
|
115
|
-
if (value === undefined)
|
|
116
|
-
return undefined;
|
|
117
|
-
const parsed = Number.parseInt(value, 10);
|
|
118
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
119
|
-
};
|
|
120
|
-
const MAX_RETRY_HINT_MS = 5 * 60 * 1000;
|
|
121
|
-
const clampRetryHintMs = (value) => {
|
|
122
|
-
if (!Number.isFinite(value))
|
|
123
|
-
return null;
|
|
124
|
-
const normalized = Math.floor(value);
|
|
125
|
-
if (normalized <= 0)
|
|
126
|
-
return null;
|
|
127
|
-
return Math.min(normalized, MAX_RETRY_HINT_MS);
|
|
128
|
-
};
|
|
129
|
-
const parseRetryAfterHintMs = (headers) => {
|
|
130
|
-
const retryAfterMsHeader = headers.get("retry-after-ms")?.trim();
|
|
131
|
-
if (retryAfterMsHeader && /^\d+$/.test(retryAfterMsHeader)) {
|
|
132
|
-
return clampRetryHintMs(Number.parseInt(retryAfterMsHeader, 10));
|
|
133
|
-
}
|
|
134
|
-
const retryAfterHeader = headers.get("retry-after")?.trim();
|
|
135
|
-
if (retryAfterHeader && /^\d+$/.test(retryAfterHeader)) {
|
|
136
|
-
return clampRetryHintMs(Number.parseInt(retryAfterHeader, 10) * 1000);
|
|
137
|
-
}
|
|
138
|
-
if (retryAfterHeader) {
|
|
139
|
-
const retryAtMs = Date.parse(retryAfterHeader);
|
|
140
|
-
if (Number.isFinite(retryAtMs)) {
|
|
141
|
-
return clampRetryHintMs(retryAtMs - Date.now());
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const resetAtHeader = headers.get("x-ratelimit-reset")?.trim();
|
|
145
|
-
if (resetAtHeader && /^\d+$/.test(resetAtHeader)) {
|
|
146
|
-
const resetRaw = Number.parseInt(resetAtHeader, 10);
|
|
147
|
-
const resetAtMs = resetRaw < 10_000_000_000 ? resetRaw * 1000 : resetRaw;
|
|
148
|
-
return clampRetryHintMs(resetAtMs - Date.now());
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
151
|
-
};
|
|
152
|
-
const sanitizeResponseHeadersForLog = (headers) => {
|
|
153
|
-
const allowed = new Set([
|
|
154
|
-
"content-type",
|
|
155
|
-
"x-request-id",
|
|
156
|
-
"x-openai-request-id",
|
|
157
|
-
"x-codex-plan-type",
|
|
158
|
-
"x-codex-active-limit",
|
|
159
|
-
"x-codex-primary-used-percent",
|
|
160
|
-
"x-codex-primary-window-minutes",
|
|
161
|
-
"x-codex-primary-reset-at",
|
|
162
|
-
"x-codex-primary-reset-after-seconds",
|
|
163
|
-
"x-codex-secondary-used-percent",
|
|
164
|
-
"x-codex-secondary-window-minutes",
|
|
165
|
-
"x-codex-secondary-reset-at",
|
|
166
|
-
"x-codex-secondary-reset-after-seconds",
|
|
167
|
-
"retry-after",
|
|
168
|
-
"x-ratelimit-reset",
|
|
169
|
-
"x-ratelimit-reset-requests",
|
|
170
|
-
]);
|
|
171
|
-
const sanitized = {};
|
|
172
|
-
for (const [rawName, rawValue] of headers.entries()) {
|
|
173
|
-
const name = rawName.toLowerCase();
|
|
174
|
-
if (!allowed.has(name))
|
|
175
|
-
continue;
|
|
176
|
-
sanitized[name] = rawValue;
|
|
177
|
-
}
|
|
178
|
-
return sanitized;
|
|
179
|
-
};
|
|
180
136
|
const runtimeMetrics = {
|
|
181
137
|
startedAt: Date.now(),
|
|
182
138
|
totalRequests: 0,
|
|
@@ -197,500 +153,145 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
197
153
|
lastRequestAt: null,
|
|
198
154
|
lastError: null,
|
|
199
155
|
};
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
accountIdSource: candidate.source,
|
|
223
|
-
accountLabel: candidate.label,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Auto-select the best workspace candidate without prompting.
|
|
228
|
-
// This honors org/default/id-token signals and avoids forcing personal token IDs.
|
|
229
|
-
const choice = selectBestAccountCandidate(candidates);
|
|
230
|
-
if (!choice)
|
|
231
|
-
return tokens;
|
|
232
|
-
return {
|
|
233
|
-
...tokens,
|
|
234
|
-
accountIdOverride: choice.accountId,
|
|
235
|
-
accountIdSource: choice.source ?? "token",
|
|
236
|
-
accountLabel: choice.label,
|
|
237
|
-
};
|
|
156
|
+
const resolveTokenSuccessAccount = (tokens) => resolveAccountSelection(tokens, {
|
|
157
|
+
envAccountId: process.env.CODEX_AUTH_ACCOUNT_ID,
|
|
158
|
+
logInfo,
|
|
159
|
+
getAccountIdCandidates,
|
|
160
|
+
selectBestAccountCandidate,
|
|
161
|
+
});
|
|
162
|
+
const persistAccountPool = createPersistAccounts({
|
|
163
|
+
persistAccountPoolResults,
|
|
164
|
+
withAccountStorageTransaction,
|
|
165
|
+
extractAccountId,
|
|
166
|
+
extractAccountEmail,
|
|
167
|
+
sanitizeEmail,
|
|
168
|
+
findMatchingAccountIndex,
|
|
169
|
+
modelFamilies: MODEL_FAMILIES,
|
|
170
|
+
});
|
|
171
|
+
const accountManagerCacheInvalidationDeps = {
|
|
172
|
+
setCachedAccountManager: (value) => {
|
|
173
|
+
cachedAccountManager = value;
|
|
174
|
+
},
|
|
175
|
+
setAccountManagerPromise: (value) => {
|
|
176
|
+
accountManagerPromise = value;
|
|
177
|
+
},
|
|
238
178
|
};
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const parsed = parseAuthorizationInput(input);
|
|
245
|
-
if (!parsed.code) {
|
|
246
|
-
return `No authorization code found. Paste the full callback URL (e.g., ${REDIRECT_URI}?code=...)`;
|
|
247
|
-
}
|
|
248
|
-
if (!parsed.state) {
|
|
249
|
-
return "Missing OAuth state. Paste the full callback URL including both code and state parameters.";
|
|
250
|
-
}
|
|
251
|
-
if (parsed.state !== expectedState) {
|
|
252
|
-
return "OAuth state mismatch. Restart login and paste the callback URL generated for this login attempt.";
|
|
253
|
-
}
|
|
254
|
-
return undefined;
|
|
179
|
+
const makeReloadAccountManagerDeps = (authFallback) => ({
|
|
180
|
+
currentReloadInFlight: accountReloadInFlight,
|
|
181
|
+
loadFromDisk: (fallback) => AccountManager.loadFromDisk(fallback),
|
|
182
|
+
setCachedAccountManager: (value) => {
|
|
183
|
+
cachedAccountManager = value;
|
|
255
184
|
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (!parsed.code || !parsed.state) {
|
|
259
|
-
return {
|
|
260
|
-
type: "failed",
|
|
261
|
-
reason: "invalid_response",
|
|
262
|
-
message: "Missing authorization code or OAuth state",
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
if (parsed.state !== expectedState) {
|
|
266
|
-
return {
|
|
267
|
-
type: "failed",
|
|
268
|
-
reason: "invalid_response",
|
|
269
|
-
message: "OAuth state mismatch. Restart login and try again.",
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
const tokens = await exchangeAuthorizationCode(parsed.code, pkce.verifier, REDIRECT_URI);
|
|
273
|
-
if (tokens?.type === "success") {
|
|
274
|
-
const resolved = resolveAccountSelection(tokens);
|
|
275
|
-
if (onSuccess) {
|
|
276
|
-
await onSuccess(resolved);
|
|
277
|
-
}
|
|
278
|
-
return resolved;
|
|
279
|
-
}
|
|
280
|
-
return tokens?.type === "failed"
|
|
281
|
-
? tokens
|
|
282
|
-
: { type: "failed" };
|
|
185
|
+
setAccountManagerPromise: (value) => {
|
|
186
|
+
accountManagerPromise = value;
|
|
283
187
|
},
|
|
188
|
+
setReloadInFlight: (value) => {
|
|
189
|
+
accountReloadInFlight = value;
|
|
190
|
+
},
|
|
191
|
+
authFallback,
|
|
192
|
+
});
|
|
193
|
+
const reloadAccountManagerFromDisk = async (authFallback) => reloadRuntimeAccountManager(makeReloadAccountManagerDeps(authFallback));
|
|
194
|
+
const applyAccountStorageScope = (pluginConfig) => applyAccountStorageScopeEntry({
|
|
195
|
+
pluginConfig,
|
|
196
|
+
getPerProjectAccounts,
|
|
197
|
+
getStorageBackupEnabled,
|
|
198
|
+
setStorageBackupEnabled,
|
|
199
|
+
isCodexCliSyncEnabled,
|
|
200
|
+
getWarningShown: () => perProjectStorageWarningShown,
|
|
201
|
+
setWarningShown: (shown) => {
|
|
202
|
+
perProjectStorageWarningShown = shown;
|
|
203
|
+
},
|
|
204
|
+
logWarn,
|
|
205
|
+
pluginName: PLUGIN_NAME,
|
|
206
|
+
setStoragePath,
|
|
207
|
+
cwd: () => process.cwd(),
|
|
208
|
+
applyAccountStorageScopeFromConfig,
|
|
284
209
|
});
|
|
285
|
-
const runOAuthFlow = async (forceNewLogin = false) => {
|
|
286
|
-
const { pkce, state, url } = await createAuthorizationFlow({ forceNewLogin });
|
|
287
|
-
logInfo(`OAuth URL: ${redactOAuthUrlForLog(url)}`);
|
|
288
|
-
let serverInfo = null;
|
|
289
|
-
try {
|
|
290
|
-
serverInfo = await startLocalOAuthServer({ state });
|
|
291
|
-
}
|
|
292
|
-
catch (err) {
|
|
293
|
-
logDebug(`[${PLUGIN_NAME}] Failed to start OAuth server: ${err?.message ?? String(err)}`);
|
|
294
|
-
serverInfo = null;
|
|
295
|
-
}
|
|
296
|
-
openBrowserUrl(url);
|
|
297
|
-
if (!serverInfo || !serverInfo.ready) {
|
|
298
|
-
serverInfo?.close();
|
|
299
|
-
const message = `\n[${PLUGIN_NAME}] OAuth callback server failed to start. ` +
|
|
300
|
-
`Please retry with "${AUTH_LABELS.OAUTH_MANUAL}".\n`;
|
|
301
|
-
logWarn(message);
|
|
302
|
-
return { type: "failed" };
|
|
303
|
-
}
|
|
304
|
-
const result = await serverInfo.waitForCode(state);
|
|
305
|
-
serverInfo.close();
|
|
306
|
-
if (!result) {
|
|
307
|
-
return { type: "failed", reason: "unknown", message: "OAuth callback timeout or cancelled" };
|
|
308
|
-
}
|
|
309
|
-
return await exchangeAuthorizationCode(result.code, pkce.verifier, REDIRECT_URI);
|
|
310
|
-
};
|
|
311
|
-
const persistAccountPool = async (results, replaceAll = false) => {
|
|
312
|
-
if (results.length === 0)
|
|
313
|
-
return;
|
|
314
|
-
await withAccountStorageTransaction(async (loadedStorage, persist) => {
|
|
315
|
-
const now = Date.now();
|
|
316
|
-
const stored = replaceAll ? null : loadedStorage;
|
|
317
|
-
const accounts = stored?.accounts ? [...stored.accounts] : [];
|
|
318
|
-
for (const result of results) {
|
|
319
|
-
const accountId = result.accountIdOverride ?? extractAccountId(result.access);
|
|
320
|
-
const accountIdSource = accountId
|
|
321
|
-
? result.accountIdSource ??
|
|
322
|
-
(result.accountIdOverride ? "manual" : "token")
|
|
323
|
-
: undefined;
|
|
324
|
-
const accountLabel = result.accountLabel;
|
|
325
|
-
const accountEmail = sanitizeEmail(extractAccountEmail(result.access, result.idToken));
|
|
326
|
-
const existingIndex = findMatchingAccountIndex(accounts, {
|
|
327
|
-
accountId,
|
|
328
|
-
email: accountEmail,
|
|
329
|
-
refreshToken: result.refresh,
|
|
330
|
-
}, {
|
|
331
|
-
allowUniqueAccountIdFallbackWithoutEmail: true,
|
|
332
|
-
});
|
|
333
|
-
if (existingIndex === undefined) {
|
|
334
|
-
accounts.push({
|
|
335
|
-
accountId,
|
|
336
|
-
accountIdSource,
|
|
337
|
-
accountLabel,
|
|
338
|
-
email: accountEmail,
|
|
339
|
-
refreshToken: result.refresh,
|
|
340
|
-
accessToken: result.access,
|
|
341
|
-
expiresAt: result.expires,
|
|
342
|
-
addedAt: now,
|
|
343
|
-
lastUsed: now,
|
|
344
|
-
});
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
const existing = accounts[existingIndex];
|
|
348
|
-
if (!existing)
|
|
349
|
-
continue;
|
|
350
|
-
const nextEmail = accountEmail ?? sanitizeEmail(existing.email);
|
|
351
|
-
const nextAccountId = accountId ?? existing.accountId;
|
|
352
|
-
const nextAccountIdSource = accountId ? accountIdSource ?? existing.accountIdSource : existing.accountIdSource;
|
|
353
|
-
const nextAccountLabel = accountLabel ?? existing.accountLabel;
|
|
354
|
-
accounts[existingIndex] = {
|
|
355
|
-
...existing,
|
|
356
|
-
accountId: nextAccountId,
|
|
357
|
-
accountIdSource: nextAccountIdSource,
|
|
358
|
-
accountLabel: nextAccountLabel,
|
|
359
|
-
email: nextEmail,
|
|
360
|
-
refreshToken: result.refresh,
|
|
361
|
-
accessToken: result.access,
|
|
362
|
-
expiresAt: result.expires,
|
|
363
|
-
lastUsed: now,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
if (accounts.length === 0)
|
|
367
|
-
return;
|
|
368
|
-
const activeIndex = replaceAll
|
|
369
|
-
? 0
|
|
370
|
-
: typeof stored?.activeIndex === "number" && Number.isFinite(stored.activeIndex)
|
|
371
|
-
? stored.activeIndex
|
|
372
|
-
: 0;
|
|
373
|
-
const clampedActiveIndex = Math.max(0, Math.min(activeIndex, accounts.length - 1));
|
|
374
|
-
const activeIndexByFamily = {};
|
|
375
|
-
for (const family of MODEL_FAMILIES) {
|
|
376
|
-
const storedFamilyIndex = stored?.activeIndexByFamily?.[family];
|
|
377
|
-
const rawFamilyIndex = replaceAll
|
|
378
|
-
? 0
|
|
379
|
-
: typeof storedFamilyIndex === "number" && Number.isFinite(storedFamilyIndex)
|
|
380
|
-
? storedFamilyIndex
|
|
381
|
-
: clampedActiveIndex;
|
|
382
|
-
activeIndexByFamily[family] = Math.max(0, Math.min(Math.floor(rawFamilyIndex), accounts.length - 1));
|
|
383
|
-
}
|
|
384
|
-
await persist({
|
|
385
|
-
version: 3,
|
|
386
|
-
accounts,
|
|
387
|
-
activeIndex: clampedActiveIndex,
|
|
388
|
-
activeIndexByFamily,
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
};
|
|
392
|
-
const showToast = async (message, variant = "success", options) => {
|
|
393
|
-
try {
|
|
394
|
-
await client.tui.showToast({
|
|
395
|
-
body: {
|
|
396
|
-
message,
|
|
397
|
-
variant,
|
|
398
|
-
...(options?.title && { title: options.title }),
|
|
399
|
-
...(options?.duration && { duration: options.duration }),
|
|
400
|
-
},
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
catch {
|
|
404
|
-
// Ignore when TUI is not available.
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
const resolveActiveIndex = (storage, family = "codex") => {
|
|
408
|
-
const total = storage.accounts.length;
|
|
409
|
-
if (total === 0)
|
|
410
|
-
return 0;
|
|
411
|
-
const rawCandidate = storage.activeIndexByFamily?.[family] ?? storage.activeIndex;
|
|
412
|
-
const raw = Number.isFinite(rawCandidate) ? rawCandidate : 0;
|
|
413
|
-
return Math.max(0, Math.min(raw, total - 1));
|
|
414
|
-
};
|
|
415
|
-
const hydrateEmails = async (storage) => {
|
|
416
|
-
if (!storage)
|
|
417
|
-
return storage;
|
|
418
|
-
const skipHydrate = process.env.VITEST_WORKER_ID !== undefined ||
|
|
419
|
-
process.env.NODE_ENV === "test" ||
|
|
420
|
-
process.env.CODEX_SKIP_EMAIL_HYDRATE === "1";
|
|
421
|
-
if (skipHydrate)
|
|
422
|
-
return storage;
|
|
423
|
-
const accountsCopy = storage.accounts.map((account) => account ? { ...account } : account);
|
|
424
|
-
const accountsToHydrate = accountsCopy.filter((account) => account && !account.email);
|
|
425
|
-
if (accountsToHydrate.length === 0)
|
|
426
|
-
return storage;
|
|
427
|
-
let changed = false;
|
|
428
|
-
await Promise.all(accountsToHydrate.map(async (account) => {
|
|
429
|
-
try {
|
|
430
|
-
const refreshed = await queuedRefresh(account.refreshToken);
|
|
431
|
-
if (refreshed.type !== "success")
|
|
432
|
-
return;
|
|
433
|
-
const id = extractAccountId(refreshed.access);
|
|
434
|
-
const email = sanitizeEmail(extractAccountEmail(refreshed.access, refreshed.idToken));
|
|
435
|
-
if (id &&
|
|
436
|
-
id !== account.accountId &&
|
|
437
|
-
shouldUpdateAccountIdFromToken(account.accountIdSource, account.accountId)) {
|
|
438
|
-
account.accountId = id;
|
|
439
|
-
account.accountIdSource = "token";
|
|
440
|
-
changed = true;
|
|
441
|
-
}
|
|
442
|
-
if (email && email !== account.email) {
|
|
443
|
-
account.email = email;
|
|
444
|
-
changed = true;
|
|
445
|
-
}
|
|
446
|
-
if (refreshed.access && refreshed.access !== account.accessToken) {
|
|
447
|
-
account.accessToken = refreshed.access;
|
|
448
|
-
changed = true;
|
|
449
|
-
}
|
|
450
|
-
if (typeof refreshed.expires === "number" && refreshed.expires !== account.expiresAt) {
|
|
451
|
-
account.expiresAt = refreshed.expires;
|
|
452
|
-
changed = true;
|
|
453
|
-
}
|
|
454
|
-
if (refreshed.refresh && refreshed.refresh !== account.refreshToken) {
|
|
455
|
-
account.refreshToken = refreshed.refresh;
|
|
456
|
-
changed = true;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
catch {
|
|
460
|
-
logWarn(`[${PLUGIN_NAME}] Failed to hydrate email for account`);
|
|
461
|
-
}
|
|
462
|
-
}));
|
|
463
|
-
if (changed) {
|
|
464
|
-
storage.accounts = accountsCopy;
|
|
465
|
-
await saveAccounts(storage);
|
|
466
|
-
}
|
|
467
|
-
return storage;
|
|
468
|
-
};
|
|
469
|
-
const getRateLimitResetTimeForFamily = (account, now, family) => {
|
|
470
|
-
const times = account.rateLimitResetTimes;
|
|
471
|
-
if (!times)
|
|
472
|
-
return null;
|
|
473
|
-
let minReset = null;
|
|
474
|
-
const prefix = `${family}:`;
|
|
475
|
-
for (const [key, value] of Object.entries(times)) {
|
|
476
|
-
if (typeof value !== "number")
|
|
477
|
-
continue;
|
|
478
|
-
if (value <= now)
|
|
479
|
-
continue;
|
|
480
|
-
if (key !== family && !key.startsWith(prefix))
|
|
481
|
-
continue;
|
|
482
|
-
if (minReset === null || value < minReset) {
|
|
483
|
-
minReset = value;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
return minReset;
|
|
487
|
-
};
|
|
488
|
-
const formatRateLimitEntry = (account, now, family = "codex") => {
|
|
489
|
-
const resetAt = getRateLimitResetTimeForFamily(account, now, family);
|
|
490
|
-
if (typeof resetAt !== "number")
|
|
491
|
-
return null;
|
|
492
|
-
const remaining = resetAt - now;
|
|
493
|
-
if (remaining <= 0)
|
|
494
|
-
return null;
|
|
495
|
-
return `resets in ${formatWaitTime(remaining)}`;
|
|
496
|
-
};
|
|
497
|
-
const applyUiRuntimeFromConfig = (pluginConfig) => {
|
|
498
|
-
return setUiRuntimeOptions({
|
|
499
|
-
v2Enabled: getCodexTuiV2(pluginConfig),
|
|
500
|
-
colorProfile: getCodexTuiColorProfile(pluginConfig),
|
|
501
|
-
glyphMode: getCodexTuiGlyphMode(pluginConfig),
|
|
502
|
-
});
|
|
503
|
-
};
|
|
504
|
-
const resolveUiRuntime = () => {
|
|
505
|
-
return applyUiRuntimeFromConfig(loadPluginConfig());
|
|
506
|
-
};
|
|
507
|
-
const getStatusMarker = (ui, status) => {
|
|
508
|
-
if (!ui.v2Enabled) {
|
|
509
|
-
if (status === "ok")
|
|
510
|
-
return "✓";
|
|
511
|
-
if (status === "warning")
|
|
512
|
-
return "!";
|
|
513
|
-
return "✗";
|
|
514
|
-
}
|
|
515
|
-
if (status === "ok")
|
|
516
|
-
return ui.theme.glyphs.check;
|
|
517
|
-
if (status === "warning")
|
|
518
|
-
return "!";
|
|
519
|
-
return ui.theme.glyphs.cross;
|
|
520
|
-
};
|
|
521
|
-
const invalidateAccountManagerCache = () => {
|
|
522
|
-
cachedAccountManager = null;
|
|
523
|
-
accountManagerPromise = null;
|
|
524
|
-
};
|
|
525
|
-
const reloadAccountManagerFromDisk = async (authFallback) => {
|
|
526
|
-
if (accountReloadInFlight) {
|
|
527
|
-
return accountReloadInFlight;
|
|
528
|
-
}
|
|
529
|
-
accountReloadInFlight = (async () => {
|
|
530
|
-
const reloaded = await AccountManager.loadFromDisk(authFallback);
|
|
531
|
-
cachedAccountManager = reloaded;
|
|
532
|
-
accountManagerPromise = Promise.resolve(reloaded);
|
|
533
|
-
return reloaded;
|
|
534
|
-
})();
|
|
535
|
-
try {
|
|
536
|
-
return await accountReloadInFlight;
|
|
537
|
-
}
|
|
538
|
-
finally {
|
|
539
|
-
accountReloadInFlight = null;
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
const applyAccountStorageScope = (pluginConfig) => {
|
|
543
|
-
const perProjectAccounts = getPerProjectAccounts(pluginConfig);
|
|
544
|
-
setStorageBackupEnabled(getStorageBackupEnabled(pluginConfig));
|
|
545
|
-
if (isCodexCliSyncEnabled()) {
|
|
546
|
-
if (perProjectAccounts && !perProjectStorageWarningShown) {
|
|
547
|
-
perProjectStorageWarningShown = true;
|
|
548
|
-
logWarn(`[${PLUGIN_NAME}] CODEX_AUTH_PER_PROJECT_ACCOUNTS is ignored while Codex CLI sync is enabled. Using global account storage.`);
|
|
549
|
-
}
|
|
550
|
-
setStoragePath(null);
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
setStoragePath(perProjectAccounts ? process.cwd() : null);
|
|
554
|
-
};
|
|
555
210
|
const ensureLiveAccountSync = async (pluginConfig, authFallback) => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (!liveAccountSync) {
|
|
566
|
-
liveAccountSync = new LiveAccountSync(async () => {
|
|
567
|
-
await reloadAccountManagerFromDisk(authFallback);
|
|
211
|
+
const next = await ensureLiveAccountSyncEntry({
|
|
212
|
+
pluginConfig,
|
|
213
|
+
authFallback,
|
|
214
|
+
currentSync: liveAccountSync,
|
|
215
|
+
currentPath: liveAccountSyncPath,
|
|
216
|
+
getLiveAccountSync,
|
|
217
|
+
getStoragePath,
|
|
218
|
+
createSync: (oauthFallback) => new LiveAccountSync(async () => {
|
|
219
|
+
await reloadRuntimeAccountManager(makeReloadAccountManagerDeps(oauthFallback));
|
|
568
220
|
}, {
|
|
569
221
|
debounceMs: getLiveAccountSyncDebounceMs(pluginConfig),
|
|
570
222
|
pollIntervalMs: getLiveAccountSyncPollMs(pluginConfig),
|
|
571
|
-
})
|
|
572
|
-
registerCleanup
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
try {
|
|
580
|
-
await liveAccountSync.syncToPath(targetPath);
|
|
581
|
-
liveAccountSyncPath = targetPath;
|
|
582
|
-
switched = true;
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
catch (error) {
|
|
586
|
-
const code = error?.code;
|
|
587
|
-
if (code !== "EBUSY" && code !== "EPERM") {
|
|
588
|
-
throw error;
|
|
589
|
-
}
|
|
590
|
-
await new Promise((resolve) => setTimeout(resolve, 25 * 2 ** attempt));
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
if (!switched) {
|
|
594
|
-
logWarn(`[${PLUGIN_NAME}] Live account sync path switch failed due to transient filesystem locks; keeping previous watcher.`);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
223
|
+
}),
|
|
224
|
+
registerCleanup,
|
|
225
|
+
logWarn,
|
|
226
|
+
pluginName: PLUGIN_NAME,
|
|
227
|
+
ensureLiveAccountSyncState,
|
|
228
|
+
});
|
|
229
|
+
liveAccountSync = next.liveAccountSync;
|
|
230
|
+
liveAccountSyncPath = next.liveAccountSyncPath;
|
|
597
231
|
};
|
|
598
232
|
const ensureRefreshGuardian = (pluginConfig) => {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (refreshGuardian) {
|
|
613
|
-
refreshGuardian.stop();
|
|
614
|
-
}
|
|
615
|
-
refreshGuardian = new RefreshGuardian(() => cachedAccountManager, { intervalMs, bufferMs });
|
|
616
|
-
refreshGuardianConfigKey = configKey;
|
|
617
|
-
refreshGuardian.start();
|
|
618
|
-
registerCleanup(() => {
|
|
619
|
-
refreshGuardian?.stop();
|
|
233
|
+
const next = ensureRefreshGuardianState({
|
|
234
|
+
enabled: getProactiveRefreshGuardian(pluginConfig),
|
|
235
|
+
intervalMs: getProactiveRefreshIntervalMs(pluginConfig),
|
|
236
|
+
bufferMs: getProactiveRefreshBufferMs(pluginConfig),
|
|
237
|
+
currentGuardian: refreshGuardian,
|
|
238
|
+
currentConfigKey: refreshGuardianConfigKey,
|
|
239
|
+
currentCleanupRegistered: refreshGuardianCleanupRegistered,
|
|
240
|
+
getCurrentGuardian: () => refreshGuardian,
|
|
241
|
+
createGuardian: ({ intervalMs, bufferMs }) => new RefreshGuardian(() => cachedAccountManager, {
|
|
242
|
+
intervalMs,
|
|
243
|
+
bufferMs,
|
|
244
|
+
}),
|
|
245
|
+
registerCleanup,
|
|
620
246
|
});
|
|
247
|
+
refreshGuardian = next.refreshGuardian;
|
|
248
|
+
refreshGuardianConfigKey = next.refreshGuardianConfigKey;
|
|
249
|
+
refreshGuardianCleanupRegistered =
|
|
250
|
+
next.refreshGuardianCleanupRegistered;
|
|
621
251
|
};
|
|
622
252
|
const ensureSessionAffinity = (pluginConfig) => {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
if (sessionAffinityStore && sessionAffinityConfigKey === configKey)
|
|
632
|
-
return;
|
|
633
|
-
sessionAffinityStore = new SessionAffinityStore({ ttlMs, maxEntries });
|
|
634
|
-
sessionAffinityConfigKey = configKey;
|
|
635
|
-
};
|
|
636
|
-
const applyPreemptiveQuotaSettings = (pluginConfig) => {
|
|
637
|
-
preemptiveQuotaScheduler.configure({
|
|
638
|
-
enabled: getPreemptiveQuotaEnabled(pluginConfig),
|
|
639
|
-
remainingPercentThresholdPrimary: getPreemptiveQuotaRemainingPercent5h(pluginConfig),
|
|
640
|
-
remainingPercentThresholdSecondary: getPreemptiveQuotaRemainingPercent7d(pluginConfig),
|
|
641
|
-
maxDeferralMs: getPreemptiveQuotaMaxDeferralMs(pluginConfig),
|
|
253
|
+
const next = ensureSessionAffinityState({
|
|
254
|
+
enabled: getSessionAffinity(pluginConfig) ||
|
|
255
|
+
getResponseContinuation(pluginConfig),
|
|
256
|
+
ttlMs: getSessionAffinityTtlMs(pluginConfig),
|
|
257
|
+
maxEntries: getSessionAffinityMaxEntries(pluginConfig),
|
|
258
|
+
currentStore: sessionAffinityStore,
|
|
259
|
+
currentConfigKey: sessionAffinityConfigKey,
|
|
260
|
+
createStore: ({ ttlMs, maxEntries }) => new SessionAffinityStore({ ttlMs, maxEntries }),
|
|
642
261
|
});
|
|
262
|
+
sessionAffinityStore = next.sessionAffinityStore;
|
|
263
|
+
sessionAffinityConfigKey = next.sessionAffinityConfigKey;
|
|
643
264
|
};
|
|
265
|
+
const applyPreemptiveQuotaSettings = (pluginConfig) => applyPreemptiveQuotaSettingsFromConfig(pluginConfig, {
|
|
266
|
+
configure: (options) => preemptiveQuotaScheduler.configure(options),
|
|
267
|
+
getPreemptiveQuotaEnabled,
|
|
268
|
+
getPreemptiveQuotaRemainingPercent5h,
|
|
269
|
+
getPreemptiveQuotaRemainingPercent7d,
|
|
270
|
+
getPreemptiveQuotaMaxDeferralMs,
|
|
271
|
+
});
|
|
644
272
|
// Event handler for session recovery and account selection
|
|
645
|
-
const eventHandler = async (input) => {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
if (account) {
|
|
666
|
-
account.lastUsed = now;
|
|
667
|
-
account.lastSwitchReason = "rotation";
|
|
668
|
-
}
|
|
669
|
-
storage.activeIndex = index;
|
|
670
|
-
storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
|
|
671
|
-
for (const family of MODEL_FAMILIES) {
|
|
672
|
-
storage.activeIndexByFamily[family] = index;
|
|
673
|
-
}
|
|
674
|
-
await saveAccounts(storage);
|
|
675
|
-
if (cachedAccountManager) {
|
|
676
|
-
await cachedAccountManager.syncCodexCliActiveSelectionForIndex(index);
|
|
677
|
-
}
|
|
678
|
-
lastCodexCliActiveSyncIndex = index;
|
|
679
|
-
// Reload manager from disk so we don't overwrite newer rotated
|
|
680
|
-
// refresh tokens with stale in-memory state.
|
|
681
|
-
if (cachedAccountManager) {
|
|
682
|
-
await reloadAccountManagerFromDisk();
|
|
683
|
-
}
|
|
684
|
-
await showToast(`Switched to account ${index + 1}`, "info");
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
catch (error) {
|
|
689
|
-
logDebug(`[${PLUGIN_NAME}] Event handler error: ${error instanceof Error ? error.message : String(error)}`);
|
|
690
|
-
}
|
|
691
|
-
};
|
|
273
|
+
const eventHandler = async (input) => handleRuntimeEvent({
|
|
274
|
+
input,
|
|
275
|
+
providerId: PROVIDER_ID,
|
|
276
|
+
modelFamilies: MODEL_FAMILIES,
|
|
277
|
+
loadAccounts,
|
|
278
|
+
saveAccounts,
|
|
279
|
+
hasCachedAccountManager: () => !!cachedAccountManager,
|
|
280
|
+
syncCodexCliActiveSelectionForIndex: async (index) => {
|
|
281
|
+
if (!cachedAccountManager)
|
|
282
|
+
return;
|
|
283
|
+
await cachedAccountManager.syncCodexCliActiveSelectionForIndex(index);
|
|
284
|
+
},
|
|
285
|
+
setLastCodexCliActiveSyncIndex: (index) => {
|
|
286
|
+
lastCodexCliActiveSyncIndex = index;
|
|
287
|
+
},
|
|
288
|
+
reloadAccountManagerFromDisk: () => reloadAccountManagerFromDisk(),
|
|
289
|
+
showToast: (message, variant) => showRuntimeToast(client, message, variant),
|
|
290
|
+
logDebug,
|
|
291
|
+
pluginName: PLUGIN_NAME,
|
|
292
|
+
});
|
|
692
293
|
// Initialize runtime UI settings once on plugin load; auth/tools refresh this dynamically.
|
|
693
|
-
|
|
294
|
+
applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
694
295
|
return {
|
|
695
296
|
event: eventHandler,
|
|
696
297
|
auth: {
|
|
@@ -699,11 +300,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
699
300
|
* Loader function that configures OAuth authentication and request handling
|
|
700
301
|
*
|
|
701
302
|
* This function:
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
303
|
+
* 1. Validates OAuth authentication
|
|
304
|
+
* 2. Loads multi-account pool from disk (fallback to current auth)
|
|
305
|
+
* 3. Loads user configuration from runtime model config
|
|
306
|
+
* 4. Fetches Codex system instructions from GitHub (cached)
|
|
307
|
+
* 5. Returns SDK configuration with custom fetch implementation
|
|
707
308
|
*
|
|
708
309
|
* @param getAuth - Function to retrieve current auth state
|
|
709
310
|
* @param provider - Provider configuration from runtime model config
|
|
@@ -712,11 +313,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
712
313
|
async loader(getAuth, provider) {
|
|
713
314
|
const auth = await getAuth();
|
|
714
315
|
const pluginConfig = loadPluginConfig();
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
316
|
+
applyLoaderRuntimeSetup({
|
|
317
|
+
pluginConfig,
|
|
318
|
+
applyUiRuntimeFromConfig: (config) => applyUiRuntimeFromConfig(config, setUiRuntimeOptions),
|
|
319
|
+
applyAccountStorageScope,
|
|
320
|
+
ensureSessionAffinity,
|
|
321
|
+
ensureRefreshGuardian,
|
|
322
|
+
applyPreemptiveQuotaSettings,
|
|
323
|
+
});
|
|
720
324
|
// Only handle OAuth auth type, skip API key auth
|
|
721
325
|
if (auth.type !== "oauth") {
|
|
722
326
|
return {};
|
|
@@ -740,15 +344,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
740
344
|
try {
|
|
741
345
|
await ensureLiveAccountSync(pluginConfig, auth);
|
|
742
346
|
if (!accountManagerPromise) {
|
|
743
|
-
await
|
|
347
|
+
await reloadRuntimeAccountManager(makeReloadAccountManagerDeps(auth));
|
|
744
348
|
}
|
|
745
349
|
const managerPromise = accountManagerPromise ??
|
|
746
|
-
|
|
350
|
+
reloadRuntimeAccountManager(makeReloadAccountManagerDeps(auth));
|
|
747
351
|
let accountManager = await managerPromise;
|
|
748
352
|
cachedAccountManager = accountManager;
|
|
749
353
|
const refreshToken = auth.type === "oauth" ? auth.refresh : "";
|
|
750
|
-
const needsPersist = refreshToken &&
|
|
751
|
-
!accountManager.hasRefreshToken(refreshToken);
|
|
354
|
+
const needsPersist = refreshToken && !accountManager.hasRefreshToken(refreshToken);
|
|
752
355
|
if (needsPersist) {
|
|
753
356
|
await accountManager.saveToDisk();
|
|
754
357
|
}
|
|
@@ -789,7 +392,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
789
392
|
STREAM_FAILOVER_SOFT_TIMEOUT_BY_MODE[failoverMode]);
|
|
790
393
|
const streamFailoverHardTimeoutMs = Math.max(streamFailoverSoftTimeoutMs, parseEnvInt(process.env.CODEX_AUTH_STREAM_STALL_HARD_TIMEOUT_MS) ??
|
|
791
394
|
streamStallTimeoutMs);
|
|
792
|
-
const maxSameAccountRetries = failoverMode === "conservative"
|
|
395
|
+
const maxSameAccountRetries = failoverMode === "conservative"
|
|
396
|
+
? 2
|
|
397
|
+
: failoverMode === "balanced"
|
|
398
|
+
? 1
|
|
399
|
+
: 0;
|
|
793
400
|
const sessionRecoveryEnabled = getSessionRecovery(pluginConfig);
|
|
794
401
|
const autoResumeEnabled = getAutoResume(pluginConfig);
|
|
795
402
|
const emptyResponseMaxRetries = getEmptyResponseMaxRetries(pluginConfig);
|
|
@@ -818,11 +425,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
818
425
|
prewarmHostCodexPrompt();
|
|
819
426
|
}
|
|
820
427
|
}
|
|
821
|
-
const recoveryHook =
|
|
822
|
-
|
|
823
|
-
|
|
428
|
+
const recoveryHook = createRuntimeSessionRecoveryHook({
|
|
429
|
+
enabled: sessionRecoveryEnabled,
|
|
430
|
+
client,
|
|
431
|
+
directory: process.cwd(),
|
|
432
|
+
autoResume: autoResumeEnabled,
|
|
433
|
+
});
|
|
824
434
|
checkAndNotify(async (message, variant) => {
|
|
825
|
-
await
|
|
435
|
+
await showRuntimeToast(client, message, variant);
|
|
826
436
|
}).catch((err) => {
|
|
827
437
|
logDebug(`Update check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
828
438
|
});
|
|
@@ -847,7 +457,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
847
457
|
*/
|
|
848
458
|
async fetch(input, init) {
|
|
849
459
|
try {
|
|
850
|
-
if (cachedAccountManager &&
|
|
460
|
+
if (cachedAccountManager &&
|
|
461
|
+
cachedAccountManager !== accountManager) {
|
|
851
462
|
accountManager = cachedAccountManager;
|
|
852
463
|
}
|
|
853
464
|
// Step 1: Extract and rewrite URL for Codex backend
|
|
@@ -857,118 +468,58 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
857
468
|
// Instructions are fetched per model family (codex-max, codex, gpt-5.1)
|
|
858
469
|
// Capture original stream value before transformation
|
|
859
470
|
// generateText() sends no stream field, streamText() sends stream=true
|
|
860
|
-
const normalizeRequestInit = async (requestInput, requestInit) => {
|
|
861
|
-
if (requestInit)
|
|
862
|
-
return requestInit;
|
|
863
|
-
if (!(requestInput instanceof Request))
|
|
864
|
-
return requestInit;
|
|
865
|
-
const method = requestInput.method || "GET";
|
|
866
|
-
const normalized = {
|
|
867
|
-
method,
|
|
868
|
-
headers: new Headers(requestInput.headers),
|
|
869
|
-
};
|
|
870
|
-
if (method !== "GET" && method !== "HEAD") {
|
|
871
|
-
try {
|
|
872
|
-
const bodyText = await requestInput.clone().text();
|
|
873
|
-
if (bodyText) {
|
|
874
|
-
normalized.body = bodyText;
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
catch {
|
|
878
|
-
// Body may be unreadable; proceed without it.
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
return normalized;
|
|
882
|
-
};
|
|
883
|
-
const parseRequestBodyFromInit = async (body) => {
|
|
884
|
-
if (!body)
|
|
885
|
-
return {};
|
|
886
|
-
try {
|
|
887
|
-
if (typeof body === "string") {
|
|
888
|
-
return JSON.parse(body);
|
|
889
|
-
}
|
|
890
|
-
if (body instanceof Uint8Array) {
|
|
891
|
-
return JSON.parse(new TextDecoder().decode(body));
|
|
892
|
-
}
|
|
893
|
-
if (body instanceof ArrayBuffer) {
|
|
894
|
-
return JSON.parse(new TextDecoder().decode(new Uint8Array(body)));
|
|
895
|
-
}
|
|
896
|
-
if (ArrayBuffer.isView(body)) {
|
|
897
|
-
const view = new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
898
|
-
return JSON.parse(new TextDecoder().decode(view));
|
|
899
|
-
}
|
|
900
|
-
if (typeof Blob !== "undefined" && body instanceof Blob) {
|
|
901
|
-
return JSON.parse(await body.text());
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
catch {
|
|
905
|
-
logWarn("Failed to parse request body, using empty object");
|
|
906
|
-
}
|
|
907
|
-
return {};
|
|
908
|
-
};
|
|
909
471
|
const baseInit = await normalizeRequestInit(input, init);
|
|
910
|
-
const originalBody = await parseRequestBodyFromInit(baseInit?.body);
|
|
472
|
+
const originalBody = await parseRequestBodyFromInit(baseInit?.body, logWarn);
|
|
911
473
|
const isStreaming = originalBody.stream === true;
|
|
912
|
-
const parsedBody = Object.keys(originalBody).length > 0
|
|
474
|
+
const parsedBody = Object.keys(originalBody).length > 0
|
|
475
|
+
? { ...originalBody }
|
|
476
|
+
: undefined;
|
|
913
477
|
const transformation = await transformRequestForCodex(baseInit, url, effectiveUserConfig, codexMode, parsedBody, {
|
|
914
478
|
fastSession: fastSessionEnabled,
|
|
915
479
|
fastSessionStrategy,
|
|
916
480
|
fastSessionMaxInputItems,
|
|
481
|
+
deferFastSessionInputTrimming: fastSessionEnabled,
|
|
482
|
+
allowBackgroundResponses: getBackgroundResponses(pluginConfig),
|
|
917
483
|
});
|
|
918
484
|
let requestInit = transformation?.updatedInit ?? baseInit;
|
|
919
485
|
let transformedBody = transformation?.body;
|
|
486
|
+
let pendingFastSessionInputTrim = transformation?.deferredFastSessionInputTrim;
|
|
920
487
|
const promptCacheKey = transformedBody?.prompt_cache_key;
|
|
921
488
|
let model = transformedBody?.model;
|
|
922
489
|
let modelFamily = model ? getModelFamily(model) : "gpt-5.1";
|
|
923
490
|
let quotaKey = model ? `${modelFamily}:${model}` : modelFamily;
|
|
491
|
+
const responseContinuationEnabled = getResponseContinuation(pluginConfig);
|
|
924
492
|
const threadIdCandidate = (process.env.CODEX_THREAD_ID ?? promptCacheKey ?? "")
|
|
925
493
|
.toString()
|
|
926
494
|
.trim() || undefined;
|
|
927
495
|
const sessionAffinityKey = threadIdCandidate ?? promptCacheKey ?? null;
|
|
928
|
-
const effectivePromptCacheKey = (sessionAffinityKey ?? promptCacheKey ?? "")
|
|
496
|
+
const effectivePromptCacheKey = (sessionAffinityKey ?? promptCacheKey ?? "")
|
|
497
|
+
.toString()
|
|
498
|
+
.trim() || undefined;
|
|
499
|
+
const shouldUseResponseContinuation = Boolean(transformedBody) &&
|
|
500
|
+
responseContinuationEnabled &&
|
|
501
|
+
!transformedBody?.previous_response_id;
|
|
502
|
+
if (shouldUseResponseContinuation && transformedBody) {
|
|
503
|
+
const lastResponseId = sessionAffinityStore?.getLastResponseId(sessionAffinityKey);
|
|
504
|
+
if (lastResponseId) {
|
|
505
|
+
transformedBody = {
|
|
506
|
+
...transformedBody,
|
|
507
|
+
previous_response_id: lastResponseId,
|
|
508
|
+
};
|
|
509
|
+
requestInit = {
|
|
510
|
+
...requestInit,
|
|
511
|
+
body: JSON.stringify(transformedBody),
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
929
515
|
const preferredSessionAccountIndex = sessionAffinityStore?.getPreferredAccountIndex(sessionAffinityKey);
|
|
930
516
|
sessionAffinityStore?.prune();
|
|
931
|
-
const requestCorrelationId = setCorrelationId(threadIdCandidate
|
|
517
|
+
const requestCorrelationId = setCorrelationId(threadIdCandidate
|
|
518
|
+
? `${threadIdCandidate}:${Date.now()}`
|
|
519
|
+
: undefined);
|
|
932
520
|
runtimeMetrics.lastRequestAt = Date.now();
|
|
933
521
|
const abortSignal = requestInit?.signal ?? init?.signal ?? null;
|
|
934
|
-
const sleep = (
|
|
935
|
-
if (abortSignal?.aborted) {
|
|
936
|
-
reject(new Error("Aborted"));
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
const timeout = setTimeout(() => {
|
|
940
|
-
cleanup();
|
|
941
|
-
resolve();
|
|
942
|
-
}, ms);
|
|
943
|
-
const onAbort = () => {
|
|
944
|
-
cleanup();
|
|
945
|
-
reject(new Error("Aborted"));
|
|
946
|
-
};
|
|
947
|
-
const cleanup = () => {
|
|
948
|
-
clearTimeout(timeout);
|
|
949
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
950
|
-
};
|
|
951
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
952
|
-
});
|
|
953
|
-
const sleepWithCountdown = async (totalMs, message, intervalMs = 5000) => {
|
|
954
|
-
const startTime = Date.now();
|
|
955
|
-
const endTime = startTime + totalMs;
|
|
956
|
-
while (Date.now() < endTime) {
|
|
957
|
-
if (abortSignal?.aborted) {
|
|
958
|
-
throw new Error("Aborted");
|
|
959
|
-
}
|
|
960
|
-
const remaining = Math.max(0, endTime - Date.now());
|
|
961
|
-
const waitLabel = formatWaitTime(remaining);
|
|
962
|
-
await showToast(`${message} (${waitLabel} remaining)`, "warning", { duration: Math.min(intervalMs + 1000, toastDurationMs) });
|
|
963
|
-
const sleepTime = Math.min(intervalMs, remaining);
|
|
964
|
-
if (sleepTime > 0) {
|
|
965
|
-
await sleep(sleepTime);
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
break;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
};
|
|
522
|
+
const sleep = createAbortableSleep(abortSignal);
|
|
972
523
|
let allRateLimitedRetries = 0;
|
|
973
524
|
let emptyResponseRetries = 0;
|
|
974
525
|
const attemptedUnsupportedFallbackModels = new Set();
|
|
@@ -983,11 +534,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
983
534
|
let usedPreferredSessionAccount = false;
|
|
984
535
|
const capabilityBoostByAccount = {};
|
|
985
536
|
const accountSnapshotSource = accountManager;
|
|
986
|
-
const accountSnapshotList = typeof accountSnapshotSource.getAccountsSnapshot ===
|
|
987
|
-
|
|
537
|
+
const accountSnapshotList = typeof accountSnapshotSource.getAccountsSnapshot ===
|
|
538
|
+
"function"
|
|
539
|
+
? (accountSnapshotSource.getAccountsSnapshot() ?? [])
|
|
988
540
|
: [];
|
|
989
541
|
if (accountSnapshotList.length === 0 &&
|
|
990
|
-
typeof accountSnapshotSource.getAccountByIndex ===
|
|
542
|
+
typeof accountSnapshotSource.getAccountByIndex ===
|
|
543
|
+
"function") {
|
|
991
544
|
for (let accountSnapshotIndex = 0; accountSnapshotIndex < accountCount; accountSnapshotIndex += 1) {
|
|
992
545
|
const candidate = accountSnapshotSource.getAccountByIndex(accountSnapshotIndex);
|
|
993
546
|
if (candidate) {
|
|
@@ -997,9 +550,10 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
997
550
|
}
|
|
998
551
|
for (const candidate of accountSnapshotList) {
|
|
999
552
|
const accountKey = resolveEntitlementAccountKey(candidate);
|
|
1000
|
-
capabilityBoostByAccount[candidate.index] =
|
|
553
|
+
capabilityBoostByAccount[candidate.index] =
|
|
554
|
+
capabilityPolicyStore.getBoost(accountKey, model ?? modelFamily);
|
|
1001
555
|
}
|
|
1002
|
-
while (attempted.size < Math.max(1, accountCount)) {
|
|
556
|
+
accountAttemptLoop: while (attempted.size < Math.max(1, accountCount)) {
|
|
1003
557
|
let account = null;
|
|
1004
558
|
if (!usedPreferredSessionAccount &&
|
|
1005
559
|
typeof preferredSessionAccountIndex === "number") {
|
|
@@ -1041,7 +595,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1041
595
|
runtimeMetrics.authRefreshFailures++;
|
|
1042
596
|
runtimeMetrics.failedRequests++;
|
|
1043
597
|
runtimeMetrics.accountRotations++;
|
|
1044
|
-
runtimeMetrics.lastError =
|
|
598
|
+
runtimeMetrics.lastError =
|
|
599
|
+
err?.message ?? String(err);
|
|
1045
600
|
const failures = accountManager.incrementAuthFailures(account);
|
|
1046
601
|
const accountLabel = formatAccountLabel(account, account.index);
|
|
1047
602
|
const authFailurePolicy = evaluateFailurePolicy({
|
|
@@ -1055,7 +610,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1055
610
|
accountManager.removeAccount(account);
|
|
1056
611
|
sessionAffinityStore?.reindexAfterRemoval(removedIndex);
|
|
1057
612
|
accountManager.saveToDiskDebounced();
|
|
1058
|
-
await
|
|
613
|
+
await showRuntimeToast(client, `Removed ${accountLabel} after ${failures} consecutive auth failures. Run 'codex login' to re-add.`, "error", { duration: toastDurationMs * 2 });
|
|
1059
614
|
continue;
|
|
1060
615
|
}
|
|
1061
616
|
if (typeof authFailurePolicy.cooldownMs === "number" &&
|
|
@@ -1065,26 +620,34 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1065
620
|
accountManager.saveToDiskDebounced();
|
|
1066
621
|
continue;
|
|
1067
622
|
}
|
|
1068
|
-
const
|
|
1069
|
-
const
|
|
1070
|
-
const
|
|
623
|
+
const currentWorkspace = accountManager.getCurrentWorkspace(account);
|
|
624
|
+
const storedAccountId = currentWorkspace?.id ?? account.accountId;
|
|
625
|
+
const storedAccountIdSource = currentWorkspace
|
|
626
|
+
? "manual"
|
|
627
|
+
: account.accountIdSource;
|
|
628
|
+
const storedEmail = account.email;
|
|
629
|
+
const hadAccountId = !!storedAccountId;
|
|
630
|
+
const runtimeIdentity = resolveRuntimeRequestIdentity({
|
|
631
|
+
storedAccountId,
|
|
632
|
+
source: storedAccountIdSource,
|
|
633
|
+
storedEmail,
|
|
634
|
+
accessToken: accountAuth.access,
|
|
635
|
+
idToken: accountAuth.idToken,
|
|
636
|
+
});
|
|
637
|
+
const tokenAccountId = runtimeIdentity.tokenAccountId;
|
|
638
|
+
const accountId = runtimeIdentity.accountId;
|
|
1071
639
|
if (!accountId) {
|
|
1072
640
|
accountManager.markAccountCoolingDown(account, ACCOUNT_LIMITS.AUTH_FAILURE_COOLDOWN_MS, "auth-failure");
|
|
1073
641
|
accountManager.saveToDiskDebounced();
|
|
1074
642
|
continue;
|
|
1075
643
|
}
|
|
1076
|
-
const resolvedEmail =
|
|
644
|
+
const resolvedEmail = runtimeIdentity.email;
|
|
1077
645
|
const entitlementAccountKey = resolveEntitlementAccountKey({
|
|
1078
|
-
accountId:
|
|
646
|
+
accountId: storedAccountId ?? accountId,
|
|
1079
647
|
email: resolvedEmail,
|
|
1080
648
|
refreshToken: account.refreshToken,
|
|
1081
649
|
index: account.index,
|
|
1082
650
|
});
|
|
1083
|
-
account.accountId = accountId;
|
|
1084
|
-
if (!hadAccountId && tokenAccountId && accountId === tokenAccountId) {
|
|
1085
|
-
account.accountIdSource = account.accountIdSource ?? "token";
|
|
1086
|
-
}
|
|
1087
|
-
account.email = resolvedEmail;
|
|
1088
651
|
const entitlementBlock = entitlementCache.isBlocked(entitlementAccountKey, model ?? modelFamily);
|
|
1089
652
|
if (entitlementBlock.blocked) {
|
|
1090
653
|
runtimeMetrics.accountRotations++;
|
|
@@ -1092,16 +655,51 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1092
655
|
logWarn(`Skipping account ${account.index + 1} due to cached entitlement block (${formatWaitTime(entitlementBlock.waitMs)} remaining).`);
|
|
1093
656
|
continue;
|
|
1094
657
|
}
|
|
658
|
+
account.accountId = accountId;
|
|
659
|
+
if (!hadAccountId &&
|
|
660
|
+
tokenAccountId &&
|
|
661
|
+
accountId === tokenAccountId) {
|
|
662
|
+
account.accountIdSource =
|
|
663
|
+
storedAccountIdSource ?? "token";
|
|
664
|
+
}
|
|
665
|
+
if (resolvedEmail) {
|
|
666
|
+
account.email = resolvedEmail;
|
|
667
|
+
}
|
|
1095
668
|
if (accountCount > 1 &&
|
|
1096
669
|
accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1097
670
|
const accountLabel = formatAccountLabel(account, account.index);
|
|
1098
|
-
await
|
|
671
|
+
await showRuntimeToast(client, `Using ${accountLabel} (${account.index + 1}/${accountCount})`, "info");
|
|
1099
672
|
accountManager.markToastShown(account.index);
|
|
1100
673
|
}
|
|
1101
674
|
const headers = createCodexHeaders(requestInit, accountId, accountAuth.access, {
|
|
1102
675
|
model,
|
|
1103
676
|
promptCacheKey: effectivePromptCacheKey,
|
|
1104
677
|
});
|
|
678
|
+
if (transformedBody && pendingFastSessionInputTrim) {
|
|
679
|
+
const activeFastSessionInputTrim = pendingFastSessionInputTrim;
|
|
680
|
+
pendingFastSessionInputTrim = undefined;
|
|
681
|
+
const compactionResult = await applyResponseCompaction({
|
|
682
|
+
body: transformedBody,
|
|
683
|
+
requestUrl: url,
|
|
684
|
+
headers,
|
|
685
|
+
trim: activeFastSessionInputTrim,
|
|
686
|
+
fetchImpl: async (requestUrl, requestInit) => {
|
|
687
|
+
const normalizedCompactionUrl = typeof requestUrl === "string"
|
|
688
|
+
? requestUrl
|
|
689
|
+
: String(requestUrl);
|
|
690
|
+
return fetch(normalizedCompactionUrl, applyProxyCompatibleInit(normalizedCompactionUrl, requestInit));
|
|
691
|
+
},
|
|
692
|
+
signal: abortSignal,
|
|
693
|
+
timeoutMs: Math.min(fetchTimeoutMs, 4_000),
|
|
694
|
+
});
|
|
695
|
+
if (compactionResult.mode !== "unchanged") {
|
|
696
|
+
transformedBody = compactionResult.body;
|
|
697
|
+
requestInit = {
|
|
698
|
+
...(requestInit ?? {}),
|
|
699
|
+
body: JSON.stringify(transformedBody),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
}
|
|
1105
703
|
const quotaScheduleKey = `${entitlementAccountKey}:${model ?? modelFamily}`;
|
|
1106
704
|
const capabilityModelKey = model ?? modelFamily;
|
|
1107
705
|
const quotaDeferral = preemptiveQuotaScheduler.getDeferral(quotaScheduleKey);
|
|
@@ -1118,13 +716,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1118
716
|
if (!tokenConsumed) {
|
|
1119
717
|
accountManager.recordRateLimit(account, modelFamily, model);
|
|
1120
718
|
runtimeMetrics.accountRotations++;
|
|
1121
|
-
runtimeMetrics.lastError =
|
|
1122
|
-
`Local token bucket depleted for account ${account.index + 1} (${modelFamily}${model ? `:${model}` : ""})`;
|
|
719
|
+
runtimeMetrics.lastError = `Local token bucket depleted for account ${account.index + 1} (${modelFamily}${model ? `:${model}` : ""})`;
|
|
1123
720
|
logWarn(`Skipping account ${account.index + 1}: local token bucket depleted for ${modelFamily}${model ? `:${model}` : ""}`);
|
|
1124
721
|
continue;
|
|
1125
722
|
}
|
|
1126
723
|
let sameAccountRetryCount = 0;
|
|
1127
724
|
let successAccountForResponse = account;
|
|
725
|
+
let successEntitlementAccountKey = entitlementAccountKey;
|
|
1128
726
|
while (true) {
|
|
1129
727
|
let response;
|
|
1130
728
|
const fetchStart = performance.now();
|
|
@@ -1138,22 +736,25 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1138
736
|
fetchController.abort(timeoutReason);
|
|
1139
737
|
}, requestTimeoutMs);
|
|
1140
738
|
const onUserAbort = abortSignal
|
|
1141
|
-
? () => fetchController.abort(abortSignal.reason ??
|
|
739
|
+
? () => fetchController.abort(abortSignal.reason ??
|
|
740
|
+
new Error("Aborted by user"))
|
|
1142
741
|
: null;
|
|
1143
742
|
if (abortSignal?.aborted) {
|
|
1144
743
|
clearTimeout(fetchTimeoutId);
|
|
1145
744
|
fetchController.abort(abortSignal.reason ?? new Error("Aborted by user"));
|
|
1146
745
|
}
|
|
1147
746
|
else if (abortSignal && onUserAbort) {
|
|
1148
|
-
abortSignal.addEventListener("abort", onUserAbort, {
|
|
747
|
+
abortSignal.addEventListener("abort", onUserAbort, {
|
|
748
|
+
once: true,
|
|
749
|
+
});
|
|
1149
750
|
}
|
|
1150
751
|
try {
|
|
1151
752
|
runtimeMetrics.totalRequests++;
|
|
1152
|
-
response = await fetch(url, {
|
|
753
|
+
response = await fetch(url, applyProxyCompatibleInit(url, {
|
|
1153
754
|
...requestInit,
|
|
1154
755
|
headers,
|
|
1155
756
|
signal: fetchController.signal,
|
|
1156
|
-
});
|
|
757
|
+
}));
|
|
1157
758
|
}
|
|
1158
759
|
catch (networkError) {
|
|
1159
760
|
const fetchAbortReason = fetchController.signal.reason;
|
|
@@ -1166,11 +767,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1166
767
|
runtimeMetrics.userAborts++;
|
|
1167
768
|
runtimeMetrics.lastError = "request aborted by user";
|
|
1168
769
|
sessionAffinityStore?.forgetSession(sessionAffinityKey);
|
|
1169
|
-
throw
|
|
770
|
+
throw fetchAbortReason instanceof Error
|
|
1170
771
|
? fetchAbortReason
|
|
1171
|
-
: new Error("Aborted by user")
|
|
772
|
+
: new Error("Aborted by user");
|
|
1172
773
|
}
|
|
1173
|
-
const errorMsg = networkError instanceof Error
|
|
774
|
+
const errorMsg = networkError instanceof Error
|
|
775
|
+
? networkError.message
|
|
776
|
+
: String(networkError);
|
|
1174
777
|
logWarn(`Network error for account ${account.index + 1}: ${errorMsg}`);
|
|
1175
778
|
runtimeMetrics.failedRequests++;
|
|
1176
779
|
runtimeMetrics.networkErrors++;
|
|
@@ -1223,18 +826,21 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1223
826
|
if (contextOverflowResult.handled) {
|
|
1224
827
|
return contextOverflowResult.response;
|
|
1225
828
|
}
|
|
1226
|
-
const { response: errorResponse, rateLimit, errorBody } = await handleErrorResponse(response, {
|
|
829
|
+
const { response: errorResponse, rateLimit, errorBody, } = await handleErrorResponse(response, {
|
|
1227
830
|
requestCorrelationId,
|
|
1228
831
|
threadId: threadIdCandidate,
|
|
1229
832
|
});
|
|
1230
833
|
const unsupportedModelInfo = getUnsupportedCodexModelInfo(errorBody);
|
|
1231
834
|
const hasRemainingAccounts = attempted.size < Math.max(1, accountCount);
|
|
1232
|
-
const blockedModel = unsupportedModelInfo.unsupportedModel ??
|
|
835
|
+
const blockedModel = unsupportedModelInfo.unsupportedModel ??
|
|
836
|
+
model ??
|
|
837
|
+
"requested model";
|
|
1233
838
|
const blockedModelNormalized = blockedModel.toLowerCase();
|
|
1234
839
|
const shouldForceSparkFallback = unsupportedModelInfo.isUnsupported &&
|
|
1235
840
|
(blockedModelNormalized === "gpt-5.3-codex-spark" ||
|
|
1236
841
|
blockedModelNormalized.includes("gpt-5.3-codex-spark"));
|
|
1237
|
-
const allowUnsupportedFallback = fallbackOnUnsupportedCodexModel ||
|
|
842
|
+
const allowUnsupportedFallback = fallbackOnUnsupportedCodexModel ||
|
|
843
|
+
shouldForceSparkFallback;
|
|
1238
844
|
// Entitlements can differ by account/workspace, so try remaining
|
|
1239
845
|
// accounts before degrading the model via fallback.
|
|
1240
846
|
// Spark entitlement is commonly unavailable on non-Pro/Business workspaces;
|
|
@@ -1279,12 +885,16 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1279
885
|
model = fallbackModel;
|
|
1280
886
|
modelFamily = getModelFamily(model);
|
|
1281
887
|
quotaKey = `${modelFamily}:${model}`;
|
|
1282
|
-
if (transformedBody &&
|
|
888
|
+
if (transformedBody &&
|
|
889
|
+
typeof transformedBody === "object") {
|
|
1283
890
|
transformedBody = { ...transformedBody, model };
|
|
1284
891
|
}
|
|
1285
892
|
else {
|
|
1286
|
-
let fallbackBody = {
|
|
1287
|
-
|
|
893
|
+
let fallbackBody = {
|
|
894
|
+
model,
|
|
895
|
+
};
|
|
896
|
+
if (requestInit?.body &&
|
|
897
|
+
typeof requestInit.body === "string") {
|
|
1288
898
|
try {
|
|
1289
899
|
const parsed = JSON.parse(requestInit.body);
|
|
1290
900
|
fallbackBody = { ...parsed, model };
|
|
@@ -1307,11 +917,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1307
917
|
fallbackApplied: true,
|
|
1308
918
|
fallbackReason: "unsupported-model-entitlement",
|
|
1309
919
|
});
|
|
1310
|
-
await
|
|
920
|
+
await showRuntimeToast(client, `Model ${previousModel} is not available for this account. Retrying with ${model}.`, "warning", { duration: toastDurationMs });
|
|
1311
921
|
restartAccountTraversalWithFallback = true;
|
|
1312
922
|
break;
|
|
1313
923
|
}
|
|
1314
|
-
if (unsupportedModelInfo.isUnsupported &&
|
|
924
|
+
if (unsupportedModelInfo.isUnsupported &&
|
|
925
|
+
!allowUnsupportedFallback) {
|
|
1315
926
|
entitlementCache.markBlocked(entitlementAccountKey, blockedModel, "unsupported-model");
|
|
1316
927
|
capabilityPolicyStore.recordUnsupported(entitlementAccountKey, blockedModel);
|
|
1317
928
|
runtimeMetrics.lastError = `Unsupported model (strict): ${blockedModel}`;
|
|
@@ -1322,7 +933,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1322
933
|
fallbackApplied: false,
|
|
1323
934
|
fallbackReason: "unsupported-model-entitlement",
|
|
1324
935
|
});
|
|
1325
|
-
await
|
|
936
|
+
await showRuntimeToast(client, `Model ${blockedModel} is not available for this account. Strict policy blocked automatic fallback.`, "warning", { duration: toastDurationMs });
|
|
1326
937
|
}
|
|
1327
938
|
if (unsupportedModelInfo.isUnsupported &&
|
|
1328
939
|
allowUnsupportedFallback &&
|
|
@@ -1331,14 +942,71 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1331
942
|
entitlementCache.markBlocked(entitlementAccountKey, blockedModel, "unsupported-model");
|
|
1332
943
|
capabilityPolicyStore.recordUnsupported(entitlementAccountKey, blockedModel);
|
|
1333
944
|
}
|
|
1334
|
-
|
|
945
|
+
const workspaceErrorCode = errorBody?.error?.code ?? "";
|
|
946
|
+
const workspaceErrorMessage = errorBody?.error?.message ?? "";
|
|
947
|
+
const isDisabledWorkspaceError = isWorkspaceDisabledError(errorResponse.status, workspaceErrorCode, workspaceErrorMessage);
|
|
948
|
+
// Handle workspace disabled/expired errors by rotating to the next workspace
|
|
949
|
+
// within the same account before falling back to another account.
|
|
950
|
+
if (isDisabledWorkspaceError) {
|
|
951
|
+
runtimeMetrics.failedRequests++;
|
|
952
|
+
runtimeMetrics.lastError = `Workspace disabled for account ${account.index + 1}`;
|
|
953
|
+
if (!account.workspaces ||
|
|
954
|
+
account.workspaces.length === 0) {
|
|
955
|
+
logWarn(`Workspace disabled/expired for account ${account.index + 1} without tracked workspaces. Leaving account enabled.`, { errorCode: workspaceErrorCode });
|
|
956
|
+
if (hasRemainingAccounts) {
|
|
957
|
+
continue accountAttemptLoop;
|
|
958
|
+
}
|
|
959
|
+
return errorResponse;
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
const currentWorkspace = accountManager.getCurrentWorkspace(account);
|
|
963
|
+
const workspaceName = currentWorkspace?.name ??
|
|
964
|
+
currentWorkspace?.id ??
|
|
965
|
+
"unknown";
|
|
966
|
+
logWarn(`Workspace disabled/expired for account ${account.index + 1} - workspace: ${workspaceName}. Rotating to next workspace.`, { errorCode: workspaceErrorCode });
|
|
967
|
+
const disabledWorkspace = currentWorkspace
|
|
968
|
+
? accountManager.disableCurrentWorkspace(account, currentWorkspace.id)
|
|
969
|
+
: false;
|
|
970
|
+
let nextWorkspace = disabledWorkspace
|
|
971
|
+
? accountManager.rotateToNextWorkspace(account)
|
|
972
|
+
: accountManager.getCurrentWorkspace(account);
|
|
973
|
+
if (!disabledWorkspace &&
|
|
974
|
+
(!nextWorkspace ||
|
|
975
|
+
nextWorkspace.enabled === false)) {
|
|
976
|
+
nextWorkspace =
|
|
977
|
+
accountManager.rotateToNextWorkspace(account);
|
|
978
|
+
}
|
|
979
|
+
if (nextWorkspace) {
|
|
980
|
+
accountManager.saveToDiskDebounced();
|
|
981
|
+
const newWorkspaceName = nextWorkspace.name ?? nextWorkspace.id;
|
|
982
|
+
await showRuntimeToast(client, `Workspace ${workspaceName} disabled. Switched to ${newWorkspaceName}.`, "warning", { duration: toastDurationMs });
|
|
983
|
+
logInfo(`Rotated to workspace ${newWorkspaceName} for account ${account.index + 1}`);
|
|
984
|
+
// Allow the same account to be selected again with fresh request state.
|
|
985
|
+
attempted.delete(account.index);
|
|
986
|
+
continue accountAttemptLoop;
|
|
987
|
+
}
|
|
988
|
+
logWarn(`All workspaces disabled for account ${account.index + 1}. Disabling account.`);
|
|
989
|
+
accountManager.setAccountEnabled(account.index, false);
|
|
990
|
+
accountManager.saveToDiskDebounced();
|
|
991
|
+
await showRuntimeToast(client, `All workspaces disabled for account ${account.index + 1}. Switching to another account.`, "warning", { duration: toastDurationMs });
|
|
992
|
+
// Forget session affinity and continue the outer loop so another
|
|
993
|
+
// enabled account can service the request.
|
|
994
|
+
sessionAffinityStore?.forgetSession(sessionAffinityKey);
|
|
995
|
+
continue accountAttemptLoop;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (errorResponse.status === 403 &&
|
|
999
|
+
!unsupportedModelInfo.isUnsupported &&
|
|
1000
|
+
!isDisabledWorkspaceError) {
|
|
1335
1001
|
entitlementCache.markBlocked(entitlementAccountKey, model ?? modelFamily, "plan-entitlement");
|
|
1336
1002
|
capabilityPolicyStore.recordFailure(entitlementAccountKey, capabilityModelKey);
|
|
1337
1003
|
}
|
|
1338
|
-
if (recoveryHook &&
|
|
1004
|
+
if (recoveryHook &&
|
|
1005
|
+
errorBody &&
|
|
1006
|
+
isRecoverableError(errorBody)) {
|
|
1339
1007
|
const errorType = detectErrorType(errorBody);
|
|
1340
1008
|
const toastContent = getRecoveryToastContent(errorType);
|
|
1341
|
-
await
|
|
1009
|
+
await showRuntimeToast(client, `${toastContent.title}: ${toastContent.message}`, "warning", { duration: toastDurationMs });
|
|
1342
1010
|
logDebug(`[${PLUGIN_NAME}] Recoverable error detected: ${errorType}`);
|
|
1343
1011
|
}
|
|
1344
1012
|
// Handle 5xx server errors by rotating to another account
|
|
@@ -1349,7 +1017,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1349
1017
|
runtimeMetrics.accountRotations++;
|
|
1350
1018
|
runtimeMetrics.lastError = `HTTP ${response.status}`;
|
|
1351
1019
|
const serverRetryAfterMs = parseRetryAfterHintMs(response.headers);
|
|
1352
|
-
const policy = evaluateFailurePolicy({
|
|
1020
|
+
const policy = evaluateFailurePolicy({
|
|
1021
|
+
kind: "server",
|
|
1022
|
+
failoverMode,
|
|
1023
|
+
serverRetryAfterMs: serverRetryAfterMs ?? undefined,
|
|
1024
|
+
}, { serverCooldownMs: serverErrorCooldownMs });
|
|
1353
1025
|
if (policy.refundToken) {
|
|
1354
1026
|
accountManager.refundToken(account, modelFamily, model);
|
|
1355
1027
|
}
|
|
@@ -1380,7 +1052,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1380
1052
|
const waitLabel = formatWaitTime(delayMs);
|
|
1381
1053
|
if (delayMs <= RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS) {
|
|
1382
1054
|
if (accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1383
|
-
await
|
|
1055
|
+
await showRuntimeToast(client, `Rate limited. Retrying in ${waitLabel} (attempt ${attempt})...`, "warning", { duration: toastDurationMs });
|
|
1384
1056
|
accountManager.markToastShown(account.index);
|
|
1385
1057
|
}
|
|
1386
1058
|
await sleep(addJitter(Math.max(MIN_BACKOFF_MS, delayMs), 0.2));
|
|
@@ -1395,7 +1067,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1395
1067
|
logWarn(`Rate limited. Rotating account ${account.index + 1} (${account.email ?? "unknown"}).`);
|
|
1396
1068
|
if (accountManager.getAccountCount() > 1 &&
|
|
1397
1069
|
accountManager.shouldShowAccountToast(account.index, rateLimitToastDebounceMs)) {
|
|
1398
|
-
await
|
|
1070
|
+
await showRuntimeToast(client, `Rate limited. Switching accounts (retry in ${waitLabel}).`, "warning", { duration: toastDurationMs });
|
|
1399
1071
|
accountManager.markToastShown(account.index);
|
|
1400
1072
|
}
|
|
1401
1073
|
break;
|
|
@@ -1452,14 +1124,50 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1452
1124
|
});
|
|
1453
1125
|
continue;
|
|
1454
1126
|
}
|
|
1455
|
-
const
|
|
1456
|
-
const
|
|
1127
|
+
const fallbackStoredAccountId = fallbackAccount.accountId;
|
|
1128
|
+
const fallbackStoredAccountIdSource = fallbackAccount.accountIdSource;
|
|
1129
|
+
const fallbackStoredEmail = fallbackAccount.email;
|
|
1130
|
+
const hadFallbackAccountId = !!fallbackStoredAccountId;
|
|
1131
|
+
const fallbackRuntimeIdentity = resolveRuntimeRequestIdentity({
|
|
1132
|
+
storedAccountId: fallbackStoredAccountId,
|
|
1133
|
+
source: fallbackStoredAccountIdSource,
|
|
1134
|
+
storedEmail: fallbackStoredEmail,
|
|
1135
|
+
accessToken: fallbackAuth.access,
|
|
1136
|
+
idToken: fallbackAuth.idToken,
|
|
1137
|
+
});
|
|
1138
|
+
const fallbackTokenAccountId = fallbackRuntimeIdentity.tokenAccountId;
|
|
1139
|
+
const fallbackAccountId = fallbackRuntimeIdentity.accountId;
|
|
1457
1140
|
if (!fallbackAccountId) {
|
|
1458
1141
|
continue;
|
|
1459
1142
|
}
|
|
1143
|
+
const fallbackResolvedEmail = fallbackRuntimeIdentity.email;
|
|
1144
|
+
const fallbackEntitlementAccountKey = resolveEntitlementAccountKey({
|
|
1145
|
+
accountId: fallbackStoredAccountId ??
|
|
1146
|
+
fallbackAccountId,
|
|
1147
|
+
email: fallbackResolvedEmail,
|
|
1148
|
+
refreshToken: fallbackAccount.refreshToken,
|
|
1149
|
+
index: fallbackAccount.index,
|
|
1150
|
+
});
|
|
1151
|
+
const fallbackEntitlementBlock = entitlementCache.isBlocked(fallbackEntitlementAccountKey, model ?? modelFamily);
|
|
1152
|
+
if (fallbackEntitlementBlock.blocked) {
|
|
1153
|
+
runtimeMetrics.accountRotations++;
|
|
1154
|
+
runtimeMetrics.lastError = `Entitlement cached block for account ${fallbackAccount.index + 1}`;
|
|
1155
|
+
logWarn(`Skipping account ${fallbackAccount.index + 1} due to cached entitlement block (${formatWaitTime(fallbackEntitlementBlock.waitMs)} remaining).`);
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1460
1158
|
if (!accountManager.consumeToken(fallbackAccount, modelFamily, model)) {
|
|
1461
1159
|
continue;
|
|
1462
1160
|
}
|
|
1161
|
+
fallbackAccount.accountId = fallbackAccountId;
|
|
1162
|
+
if (!hadFallbackAccountId &&
|
|
1163
|
+
fallbackTokenAccountId &&
|
|
1164
|
+
fallbackAccountId === fallbackTokenAccountId) {
|
|
1165
|
+
fallbackAccount.accountIdSource =
|
|
1166
|
+
fallbackStoredAccountIdSource ?? "token";
|
|
1167
|
+
}
|
|
1168
|
+
if (fallbackResolvedEmail) {
|
|
1169
|
+
fallbackAccount.email = fallbackResolvedEmail;
|
|
1170
|
+
}
|
|
1463
1171
|
const fallbackHeaders = createCodexHeaders(requestInit, fallbackAccountId, fallbackAuth.access, {
|
|
1464
1172
|
model,
|
|
1465
1173
|
promptCacheKey: effectivePromptCacheKey,
|
|
@@ -1467,7 +1175,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1467
1175
|
const fallbackController = new AbortController();
|
|
1468
1176
|
const fallbackTimeoutId = setTimeout(() => fallbackController.abort(new Error("Request timeout")), fetchTimeoutMs);
|
|
1469
1177
|
const onFallbackAbort = abortSignal
|
|
1470
|
-
? () => fallbackController.abort(abortSignal.reason ??
|
|
1178
|
+
? () => fallbackController.abort(abortSignal.reason ??
|
|
1179
|
+
new Error("Aborted by user"))
|
|
1471
1180
|
: null;
|
|
1472
1181
|
if (abortSignal && onFallbackAbort) {
|
|
1473
1182
|
abortSignal.addEventListener("abort", onFallbackAbort, {
|
|
@@ -1476,14 +1185,14 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1476
1185
|
}
|
|
1477
1186
|
try {
|
|
1478
1187
|
runtimeMetrics.totalRequests++;
|
|
1479
|
-
const fallbackResponse = await fetch(url, {
|
|
1188
|
+
const fallbackResponse = await fetch(url, applyProxyCompatibleInit(url, {
|
|
1480
1189
|
...requestInit,
|
|
1481
1190
|
headers: fallbackHeaders,
|
|
1482
1191
|
signal: fallbackController.signal,
|
|
1483
|
-
});
|
|
1192
|
+
}));
|
|
1484
1193
|
const fallbackSnapshot = readQuotaSchedulerSnapshot(fallbackResponse.headers, fallbackResponse.status);
|
|
1485
1194
|
if (fallbackSnapshot) {
|
|
1486
|
-
preemptiveQuotaScheduler.update(`${
|
|
1195
|
+
preemptiveQuotaScheduler.update(`${fallbackEntitlementAccountKey}:${model ?? modelFamily}`, fallbackSnapshot);
|
|
1487
1196
|
}
|
|
1488
1197
|
if (!fallbackResponse.ok) {
|
|
1489
1198
|
try {
|
|
@@ -1500,15 +1209,19 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1500
1209
|
else {
|
|
1501
1210
|
accountManager.recordFailure(fallbackAccount, modelFamily, model);
|
|
1502
1211
|
}
|
|
1503
|
-
capabilityPolicyStore.recordFailure(
|
|
1212
|
+
capabilityPolicyStore.recordFailure(fallbackEntitlementAccountKey, capabilityModelKey);
|
|
1504
1213
|
continue;
|
|
1505
1214
|
}
|
|
1506
1215
|
successAccountForResponse = fallbackAccount;
|
|
1216
|
+
successEntitlementAccountKey =
|
|
1217
|
+
fallbackEntitlementAccountKey;
|
|
1507
1218
|
runtimeMetrics.streamFailoverRecoveries += 1;
|
|
1508
1219
|
if (fallbackAccount.index !== account.index) {
|
|
1509
1220
|
runtimeMetrics.streamFailoverCrossAccountRecoveries += 1;
|
|
1510
1221
|
runtimeMetrics.accountRotations += 1;
|
|
1511
|
-
|
|
1222
|
+
if (!responseContinuationEnabled) {
|
|
1223
|
+
sessionAffinityStore?.remember(sessionAffinityKey, fallbackAccount.index);
|
|
1224
|
+
}
|
|
1512
1225
|
}
|
|
1513
1226
|
logInfo(`Recovered stream via failover attempt ${failoverAttempt} using account ${fallbackAccount.index + 1}.`, { emittedBytes });
|
|
1514
1227
|
return fallbackResponse;
|
|
@@ -1516,14 +1229,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1516
1229
|
catch (streamFailoverError) {
|
|
1517
1230
|
accountManager.refundToken(fallbackAccount, modelFamily, model);
|
|
1518
1231
|
accountManager.recordFailure(fallbackAccount, modelFamily, model);
|
|
1519
|
-
capabilityPolicyStore.recordFailure(
|
|
1232
|
+
capabilityPolicyStore.recordFailure(fallbackEntitlementAccountKey, capabilityModelKey);
|
|
1520
1233
|
logWarn(`Stream failover attempt ${failoverAttempt} failed for account ${fallbackAccount.index + 1}.`, {
|
|
1521
1234
|
emittedBytes,
|
|
1522
1235
|
error: streamFailoverError instanceof Error
|
|
1523
1236
|
? streamFailoverError.message
|
|
1524
1237
|
: String(streamFailoverError),
|
|
1525
1238
|
});
|
|
1526
|
-
continue;
|
|
1527
1239
|
}
|
|
1528
1240
|
finally {
|
|
1529
1241
|
clearTimeout(fallbackTimeoutId);
|
|
@@ -1540,20 +1252,30 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1540
1252
|
requestInstanceId: requestCorrelationId ?? undefined,
|
|
1541
1253
|
});
|
|
1542
1254
|
}
|
|
1255
|
+
let storedResponseIdForSuccess = false;
|
|
1543
1256
|
const successResponse = await handleSuccessResponse(responseForSuccess, isStreaming, {
|
|
1257
|
+
onResponseId: (responseId) => {
|
|
1258
|
+
if (!responseContinuationEnabled)
|
|
1259
|
+
return;
|
|
1260
|
+
sessionAffinityStore?.remember(sessionAffinityKey, successAccountForResponse.index);
|
|
1261
|
+
sessionAffinityStore?.updateLastResponseId(sessionAffinityKey, responseId);
|
|
1262
|
+
storedResponseIdForSuccess = true;
|
|
1263
|
+
},
|
|
1544
1264
|
streamStallTimeoutMs,
|
|
1545
1265
|
});
|
|
1546
1266
|
if (!isStreaming && emptyResponseMaxRetries > 0) {
|
|
1547
1267
|
const clonedResponse = successResponse.clone();
|
|
1548
1268
|
try {
|
|
1549
1269
|
const bodyText = await clonedResponse.text();
|
|
1550
|
-
const parsedBody = bodyText
|
|
1270
|
+
const parsedBody = bodyText
|
|
1271
|
+
? JSON.parse(bodyText)
|
|
1272
|
+
: null;
|
|
1551
1273
|
if (isEmptyResponse(parsedBody)) {
|
|
1552
1274
|
if (emptyResponseRetries < emptyResponseMaxRetries) {
|
|
1553
1275
|
emptyResponseRetries++;
|
|
1554
1276
|
runtimeMetrics.emptyResponseRetries++;
|
|
1555
1277
|
logWarn(`Empty response received (attempt ${emptyResponseRetries}/${emptyResponseMaxRetries}). Retrying...`);
|
|
1556
|
-
await
|
|
1278
|
+
await showRuntimeToast(client, `Empty response. Retrying (${emptyResponseRetries}/${emptyResponseMaxRetries})...`, "warning", { duration: toastDurationMs });
|
|
1557
1279
|
accountManager.refundToken(account, modelFamily, model);
|
|
1558
1280
|
accountManager.recordFailure(account, modelFamily, model);
|
|
1559
1281
|
capabilityPolicyStore.recordFailure(entitlementAccountKey, capabilityModelKey);
|
|
@@ -1565,7 +1287,8 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1565
1287
|
sameAccountRetryCount < maxSameAccountRetries) {
|
|
1566
1288
|
sameAccountRetryCount += 1;
|
|
1567
1289
|
runtimeMetrics.sameAccountRetries += 1;
|
|
1568
|
-
const retryDelayMs = Math.max(0, Math.floor(emptyPolicy.retryDelayMs ??
|
|
1290
|
+
const retryDelayMs = Math.max(0, Math.floor(emptyPolicy.retryDelayMs ??
|
|
1291
|
+
emptyResponseRetryDelayMs));
|
|
1569
1292
|
if (retryDelayMs > 0) {
|
|
1570
1293
|
await sleep(addJitter(retryDelayMs, 0.2));
|
|
1571
1294
|
}
|
|
@@ -1585,18 +1308,21 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1585
1308
|
if (successAccountForResponse.index !== account.index) {
|
|
1586
1309
|
accountManager.markSwitched(successAccountForResponse, "rotation", modelFamily);
|
|
1587
1310
|
}
|
|
1588
|
-
const successAccountKey =
|
|
1589
|
-
? entitlementAccountKey
|
|
1590
|
-
: resolveEntitlementAccountKey(successAccountForResponse);
|
|
1311
|
+
const successAccountKey = successEntitlementAccountKey;
|
|
1591
1312
|
accountManager.recordSuccess(successAccountForResponse, modelFamily, model);
|
|
1592
1313
|
capabilityPolicyStore.recordSuccess(successAccountKey, capabilityModelKey);
|
|
1593
1314
|
entitlementCache.clear(successAccountKey, capabilityModelKey);
|
|
1594
|
-
|
|
1315
|
+
if (!responseContinuationEnabled ||
|
|
1316
|
+
(!isStreaming && !storedResponseIdForSuccess)) {
|
|
1317
|
+
sessionAffinityStore?.remember(sessionAffinityKey, successAccountForResponse.index);
|
|
1318
|
+
}
|
|
1595
1319
|
runtimeMetrics.successfulRequests++;
|
|
1596
1320
|
runtimeMetrics.lastError = null;
|
|
1597
|
-
if (lastCodexCliActiveSyncIndex !==
|
|
1321
|
+
if (lastCodexCliActiveSyncIndex !==
|
|
1322
|
+
successAccountForResponse.index) {
|
|
1598
1323
|
void accountManager.syncCodexCliActiveSelectionForIndex(successAccountForResponse.index);
|
|
1599
|
-
lastCodexCliActiveSyncIndex =
|
|
1324
|
+
lastCodexCliActiveSyncIndex =
|
|
1325
|
+
successAccountForResponse.index;
|
|
1600
1326
|
}
|
|
1601
1327
|
return successResponse;
|
|
1602
1328
|
}
|
|
@@ -1620,7 +1346,15 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1620
1346
|
waitMs <= retryAllAccountsMaxWaitMs) &&
|
|
1621
1347
|
allRateLimitedRetries < retryAllAccountsMaxRetries) {
|
|
1622
1348
|
const countdownMessage = `All ${count} account(s) rate-limited. Waiting`;
|
|
1623
|
-
await sleepWithCountdown(
|
|
1349
|
+
await sleepWithCountdown({
|
|
1350
|
+
totalMs: addJitter(waitMs, 0.2),
|
|
1351
|
+
message: countdownMessage,
|
|
1352
|
+
sleep,
|
|
1353
|
+
showToast: (message, variant, options) => showRuntimeToast(client, message, variant, options),
|
|
1354
|
+
formatWaitTime,
|
|
1355
|
+
toastDurationMs,
|
|
1356
|
+
abortSignal,
|
|
1357
|
+
});
|
|
1624
1358
|
allRateLimitedRetries++;
|
|
1625
1359
|
continue;
|
|
1626
1360
|
}
|
|
@@ -1657,580 +1391,70 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
1657
1391
|
type: "oauth",
|
|
1658
1392
|
authorize: async (inputs) => {
|
|
1659
1393
|
const authPluginConfig = loadPluginConfig();
|
|
1660
|
-
applyUiRuntimeFromConfig(authPluginConfig);
|
|
1661
|
-
|
|
1394
|
+
applyUiRuntimeFromConfig(authPluginConfig, setUiRuntimeOptions);
|
|
1395
|
+
applyAccountStorageScopeFromConfig(authPluginConfig, {
|
|
1396
|
+
getPerProjectAccounts,
|
|
1397
|
+
getStorageBackupEnabled,
|
|
1398
|
+
setStorageBackupEnabled,
|
|
1399
|
+
isCodexCliSyncEnabled,
|
|
1400
|
+
getWarningShown: () => perProjectStorageWarningShown,
|
|
1401
|
+
setWarningShown: (shown) => {
|
|
1402
|
+
perProjectStorageWarningShown = shown;
|
|
1403
|
+
},
|
|
1404
|
+
logWarn,
|
|
1405
|
+
pluginName: PLUGIN_NAME,
|
|
1406
|
+
setStoragePath,
|
|
1407
|
+
cwd: () => process.cwd(),
|
|
1408
|
+
});
|
|
1662
1409
|
const accounts = [];
|
|
1663
|
-
const noBrowser = inputs?.
|
|
1410
|
+
const noBrowser = inputs?.manual === "true" ||
|
|
1411
|
+
inputs?.noBrowser === "true" ||
|
|
1664
1412
|
inputs?.["no-browser"] === "true";
|
|
1665
|
-
const useManualMode = noBrowser;
|
|
1413
|
+
const useManualMode = noBrowser || isBrowserLaunchSuppressed();
|
|
1666
1414
|
const explicitLoginMode = inputs?.loginMode === "fresh" || inputs?.loginMode === "add"
|
|
1667
1415
|
? inputs.loginMode
|
|
1668
1416
|
: null;
|
|
1669
1417
|
let startFresh = explicitLoginMode === "fresh";
|
|
1670
1418
|
let refreshAccountIndex;
|
|
1671
|
-
const clampActiveIndices = (storage) => {
|
|
1672
|
-
const count = storage.accounts.length;
|
|
1673
|
-
if (count === 0) {
|
|
1674
|
-
storage.activeIndex = 0;
|
|
1675
|
-
storage.activeIndexByFamily = {};
|
|
1676
|
-
return;
|
|
1677
|
-
}
|
|
1678
|
-
storage.activeIndex = Math.max(0, Math.min(storage.activeIndex, count - 1));
|
|
1679
|
-
storage.activeIndexByFamily = storage.activeIndexByFamily ?? {};
|
|
1680
|
-
for (const family of MODEL_FAMILIES) {
|
|
1681
|
-
const raw = storage.activeIndexByFamily[family];
|
|
1682
|
-
const candidate = typeof raw === "number" && Number.isFinite(raw) ? raw : storage.activeIndex;
|
|
1683
|
-
storage.activeIndexByFamily[family] = Math.max(0, Math.min(candidate, count - 1));
|
|
1684
|
-
}
|
|
1685
|
-
};
|
|
1686
|
-
const isFlaggableFailure = (failure) => {
|
|
1687
|
-
if (failure.reason === "missing_refresh")
|
|
1688
|
-
return true;
|
|
1689
|
-
if (failure.statusCode === 401)
|
|
1690
|
-
return true;
|
|
1691
|
-
if (failure.statusCode !== 400)
|
|
1692
|
-
return false;
|
|
1693
|
-
const message = (failure.message ?? "").toLowerCase();
|
|
1694
|
-
return (message.includes("invalid_grant") ||
|
|
1695
|
-
message.includes("invalid refresh") ||
|
|
1696
|
-
message.includes("token has been revoked"));
|
|
1697
|
-
};
|
|
1698
|
-
const parseFiniteNumberHeader = (headers, name) => {
|
|
1699
|
-
const raw = headers.get(name);
|
|
1700
|
-
if (!raw)
|
|
1701
|
-
return undefined;
|
|
1702
|
-
const parsed = Number(raw);
|
|
1703
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1704
|
-
};
|
|
1705
|
-
const parseFiniteIntHeader = (headers, name) => {
|
|
1706
|
-
const raw = headers.get(name);
|
|
1707
|
-
if (!raw)
|
|
1708
|
-
return undefined;
|
|
1709
|
-
const parsed = Number.parseInt(raw, 10);
|
|
1710
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
1711
|
-
};
|
|
1712
|
-
const parseResetAtMs = (headers, prefix) => {
|
|
1713
|
-
const resetAfterSeconds = parseFiniteIntHeader(headers, `${prefix}-reset-after-seconds`);
|
|
1714
|
-
if (typeof resetAfterSeconds === "number" &&
|
|
1715
|
-
Number.isFinite(resetAfterSeconds) &&
|
|
1716
|
-
resetAfterSeconds > 0) {
|
|
1717
|
-
return Date.now() + resetAfterSeconds * 1000;
|
|
1718
|
-
}
|
|
1719
|
-
const resetAtRaw = headers.get(`${prefix}-reset-at`);
|
|
1720
|
-
if (!resetAtRaw)
|
|
1721
|
-
return undefined;
|
|
1722
|
-
const trimmed = resetAtRaw.trim();
|
|
1723
|
-
if (/^\d+$/.test(trimmed)) {
|
|
1724
|
-
const parsedNumber = Number.parseInt(trimmed, 10);
|
|
1725
|
-
if (Number.isFinite(parsedNumber) && parsedNumber > 0) {
|
|
1726
|
-
// Upstream sometimes returns seconds since epoch.
|
|
1727
|
-
return parsedNumber < 10_000_000_000 ? parsedNumber * 1000 : parsedNumber;
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
const parsedDate = Date.parse(trimmed);
|
|
1731
|
-
return Number.isFinite(parsedDate) ? parsedDate : undefined;
|
|
1732
|
-
};
|
|
1733
|
-
const hasCodexQuotaHeaders = (headers) => {
|
|
1734
|
-
const keys = [
|
|
1735
|
-
"x-codex-primary-used-percent",
|
|
1736
|
-
"x-codex-primary-window-minutes",
|
|
1737
|
-
"x-codex-primary-reset-at",
|
|
1738
|
-
"x-codex-primary-reset-after-seconds",
|
|
1739
|
-
"x-codex-secondary-used-percent",
|
|
1740
|
-
"x-codex-secondary-window-minutes",
|
|
1741
|
-
"x-codex-secondary-reset-at",
|
|
1742
|
-
"x-codex-secondary-reset-after-seconds",
|
|
1743
|
-
];
|
|
1744
|
-
return keys.some((key) => headers.get(key) !== null);
|
|
1745
|
-
};
|
|
1746
|
-
const parseCodexQuotaSnapshot = (headers, status) => {
|
|
1747
|
-
if (!hasCodexQuotaHeaders(headers))
|
|
1748
|
-
return null;
|
|
1749
|
-
const primaryPrefix = "x-codex-primary";
|
|
1750
|
-
const secondaryPrefix = "x-codex-secondary";
|
|
1751
|
-
const primary = {
|
|
1752
|
-
usedPercent: parseFiniteNumberHeader(headers, `${primaryPrefix}-used-percent`),
|
|
1753
|
-
windowMinutes: parseFiniteIntHeader(headers, `${primaryPrefix}-window-minutes`),
|
|
1754
|
-
resetAtMs: parseResetAtMs(headers, primaryPrefix),
|
|
1755
|
-
};
|
|
1756
|
-
const secondary = {
|
|
1757
|
-
usedPercent: parseFiniteNumberHeader(headers, `${secondaryPrefix}-used-percent`),
|
|
1758
|
-
windowMinutes: parseFiniteIntHeader(headers, `${secondaryPrefix}-window-minutes`),
|
|
1759
|
-
resetAtMs: parseResetAtMs(headers, secondaryPrefix),
|
|
1760
|
-
};
|
|
1761
|
-
const planTypeRaw = headers.get("x-codex-plan-type");
|
|
1762
|
-
const planType = planTypeRaw && planTypeRaw.trim() ? planTypeRaw.trim() : undefined;
|
|
1763
|
-
const activeLimit = parseFiniteIntHeader(headers, "x-codex-active-limit");
|
|
1764
|
-
return { status, planType, activeLimit, primary, secondary };
|
|
1765
|
-
};
|
|
1766
|
-
const formatQuotaWindowLabel = (windowMinutes) => {
|
|
1767
|
-
if (!windowMinutes || !Number.isFinite(windowMinutes) || windowMinutes <= 0) {
|
|
1768
|
-
return "quota";
|
|
1769
|
-
}
|
|
1770
|
-
if (windowMinutes % 1440 === 0)
|
|
1771
|
-
return `${windowMinutes / 1440}d`;
|
|
1772
|
-
if (windowMinutes % 60 === 0)
|
|
1773
|
-
return `${windowMinutes / 60}h`;
|
|
1774
|
-
return `${windowMinutes}m`;
|
|
1775
|
-
};
|
|
1776
|
-
const formatResetAt = (resetAtMs) => {
|
|
1777
|
-
if (!resetAtMs || !Number.isFinite(resetAtMs) || resetAtMs <= 0)
|
|
1778
|
-
return undefined;
|
|
1779
|
-
const date = new Date(resetAtMs);
|
|
1780
|
-
if (!Number.isFinite(date.getTime()))
|
|
1781
|
-
return undefined;
|
|
1782
|
-
const now = new Date();
|
|
1783
|
-
const sameDay = now.getFullYear() === date.getFullYear() &&
|
|
1784
|
-
now.getMonth() === date.getMonth() &&
|
|
1785
|
-
now.getDate() === date.getDate();
|
|
1786
|
-
const time = date.toLocaleTimeString(undefined, {
|
|
1787
|
-
hour: "2-digit",
|
|
1788
|
-
minute: "2-digit",
|
|
1789
|
-
hour12: false,
|
|
1790
|
-
});
|
|
1791
|
-
if (sameDay)
|
|
1792
|
-
return time;
|
|
1793
|
-
const day = date.toLocaleDateString(undefined, { month: "short", day: "2-digit" });
|
|
1794
|
-
return `${time} on ${day}`;
|
|
1795
|
-
};
|
|
1796
|
-
const formatCodexQuotaLine = (snapshot) => {
|
|
1797
|
-
const summarizeWindow = (label, window) => {
|
|
1798
|
-
const used = window.usedPercent;
|
|
1799
|
-
const left = typeof used === "number" && Number.isFinite(used)
|
|
1800
|
-
? Math.max(0, Math.min(100, Math.round(100 - used)))
|
|
1801
|
-
: undefined;
|
|
1802
|
-
const reset = formatResetAt(window.resetAtMs);
|
|
1803
|
-
let summary = label;
|
|
1804
|
-
if (left !== undefined)
|
|
1805
|
-
summary = `${summary} ${left}% left`;
|
|
1806
|
-
if (reset)
|
|
1807
|
-
summary = `${summary} (resets ${reset})`;
|
|
1808
|
-
return summary;
|
|
1809
|
-
};
|
|
1810
|
-
const primaryLabel = formatQuotaWindowLabel(snapshot.primary.windowMinutes);
|
|
1811
|
-
const secondaryLabel = formatQuotaWindowLabel(snapshot.secondary.windowMinutes);
|
|
1812
|
-
const parts = [
|
|
1813
|
-
summarizeWindow(primaryLabel, snapshot.primary),
|
|
1814
|
-
summarizeWindow(secondaryLabel, snapshot.secondary),
|
|
1815
|
-
];
|
|
1816
|
-
if (snapshot.planType)
|
|
1817
|
-
parts.push(`plan:${snapshot.planType}`);
|
|
1818
|
-
if (typeof snapshot.activeLimit === "number" && Number.isFinite(snapshot.activeLimit)) {
|
|
1819
|
-
parts.push(`active:${snapshot.activeLimit}`);
|
|
1820
|
-
}
|
|
1821
|
-
if (snapshot.status === 429)
|
|
1822
|
-
parts.push("rate-limited");
|
|
1823
|
-
return parts.join(", ");
|
|
1824
|
-
};
|
|
1825
|
-
const fetchCodexQuotaSnapshot = async (params) => {
|
|
1826
|
-
const QUOTA_PROBE_MODELS = ["gpt-5-codex", "gpt-5.3-codex", "gpt-5.2-codex"];
|
|
1827
|
-
let lastError = null;
|
|
1828
|
-
for (const model of QUOTA_PROBE_MODELS) {
|
|
1829
|
-
try {
|
|
1830
|
-
const instructions = await getCodexInstructions(model);
|
|
1831
|
-
const probeBody = {
|
|
1832
|
-
model,
|
|
1833
|
-
stream: true,
|
|
1834
|
-
store: false,
|
|
1835
|
-
include: ["reasoning.encrypted_content"],
|
|
1836
|
-
instructions,
|
|
1837
|
-
input: [
|
|
1838
|
-
{
|
|
1839
|
-
type: "message",
|
|
1840
|
-
role: "user",
|
|
1841
|
-
content: [{ type: "input_text", text: "quota ping" }],
|
|
1842
|
-
},
|
|
1843
|
-
],
|
|
1844
|
-
reasoning: { effort: "none", summary: "auto" },
|
|
1845
|
-
text: { verbosity: "low" },
|
|
1846
|
-
};
|
|
1847
|
-
const headers = createCodexHeaders(undefined, params.accountId, params.accessToken, {
|
|
1848
|
-
model,
|
|
1849
|
-
});
|
|
1850
|
-
headers.set("content-type", "application/json; charset=utf-8");
|
|
1851
|
-
const controller = new AbortController();
|
|
1852
|
-
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
1853
|
-
let response;
|
|
1854
|
-
try {
|
|
1855
|
-
response = await fetch(`${CODEX_BASE_URL}/codex/responses`, {
|
|
1856
|
-
method: "POST",
|
|
1857
|
-
headers,
|
|
1858
|
-
body: JSON.stringify(probeBody),
|
|
1859
|
-
signal: controller.signal,
|
|
1860
|
-
});
|
|
1861
|
-
}
|
|
1862
|
-
finally {
|
|
1863
|
-
clearTimeout(timeout);
|
|
1864
|
-
}
|
|
1865
|
-
const snapshot = parseCodexQuotaSnapshot(response.headers, response.status);
|
|
1866
|
-
if (snapshot) {
|
|
1867
|
-
// We only need headers; cancel the SSE stream immediately.
|
|
1868
|
-
try {
|
|
1869
|
-
await response.body?.cancel();
|
|
1870
|
-
}
|
|
1871
|
-
catch {
|
|
1872
|
-
// Ignore cancellation failures.
|
|
1873
|
-
}
|
|
1874
|
-
return snapshot;
|
|
1875
|
-
}
|
|
1876
|
-
if (!response.ok) {
|
|
1877
|
-
const bodyText = await response.text().catch(() => "");
|
|
1878
|
-
let errorBody = undefined;
|
|
1879
|
-
try {
|
|
1880
|
-
errorBody = bodyText ? JSON.parse(bodyText) : undefined;
|
|
1881
|
-
}
|
|
1882
|
-
catch {
|
|
1883
|
-
errorBody = { error: { message: bodyText } };
|
|
1884
|
-
}
|
|
1885
|
-
const unsupportedInfo = getUnsupportedCodexModelInfo(errorBody);
|
|
1886
|
-
if (unsupportedInfo.isUnsupported) {
|
|
1887
|
-
lastError = new Error(unsupportedInfo.message ?? `Model '${model}' unsupported for this account`);
|
|
1888
|
-
continue;
|
|
1889
|
-
}
|
|
1890
|
-
const message = (typeof errorBody?.error?.message === "string"
|
|
1891
|
-
? errorBody.error?.message
|
|
1892
|
-
: bodyText) || `HTTP ${response.status}`;
|
|
1893
|
-
throw new Error(message);
|
|
1894
|
-
}
|
|
1895
|
-
lastError = new Error("Codex response did not include quota headers");
|
|
1896
|
-
}
|
|
1897
|
-
catch (error) {
|
|
1898
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
throw lastError ?? new Error("Failed to fetch quotas");
|
|
1902
|
-
};
|
|
1903
|
-
const runAccountCheck = async (deepProbe) => {
|
|
1904
|
-
const loadedStorage = await hydrateEmails(await loadAccounts());
|
|
1905
|
-
const workingStorage = loadedStorage
|
|
1906
|
-
? {
|
|
1907
|
-
...loadedStorage,
|
|
1908
|
-
accounts: loadedStorage.accounts.map((account) => ({ ...account })),
|
|
1909
|
-
activeIndexByFamily: loadedStorage.activeIndexByFamily
|
|
1910
|
-
? { ...loadedStorage.activeIndexByFamily }
|
|
1911
|
-
: {},
|
|
1912
|
-
}
|
|
1913
|
-
: { version: 3, accounts: [], activeIndex: 0, activeIndexByFamily: {} };
|
|
1914
|
-
if (workingStorage.accounts.length === 0) {
|
|
1915
|
-
console.log("\nNo accounts to check.\n");
|
|
1916
|
-
return;
|
|
1917
|
-
}
|
|
1918
|
-
const flaggedStorage = await loadFlaggedAccounts();
|
|
1919
|
-
let storageChanged = false;
|
|
1920
|
-
let flaggedChanged = false;
|
|
1921
|
-
const removeFromActive = new Set();
|
|
1922
|
-
const total = workingStorage.accounts.length;
|
|
1923
|
-
let ok = 0;
|
|
1924
|
-
let disabled = 0;
|
|
1925
|
-
let errors = 0;
|
|
1926
|
-
console.log(`\nChecking ${deepProbe ? "full account health" : "quotas"} for all accounts...\n`);
|
|
1927
|
-
for (let i = 0; i < total; i += 1) {
|
|
1928
|
-
const account = workingStorage.accounts[i];
|
|
1929
|
-
if (!account)
|
|
1930
|
-
continue;
|
|
1931
|
-
const label = account.email ?? account.accountLabel ?? `Account ${i + 1}`;
|
|
1932
|
-
if (account.enabled === false) {
|
|
1933
|
-
disabled += 1;
|
|
1934
|
-
console.log(`[${i + 1}/${total}] ${label}: DISABLED`);
|
|
1935
|
-
continue;
|
|
1936
|
-
}
|
|
1937
|
-
try {
|
|
1938
|
-
// If we already have a valid cached access token, don't force-refresh.
|
|
1939
|
-
// This avoids flagging accounts where the refresh token has been burned
|
|
1940
|
-
// but the access token is still valid (same behavior as Codex CLI).
|
|
1941
|
-
const nowMs = Date.now();
|
|
1942
|
-
let accessToken = null;
|
|
1943
|
-
let tokenAccountId = undefined;
|
|
1944
|
-
let authDetail = "OK";
|
|
1945
|
-
if (account.accessToken &&
|
|
1946
|
-
(typeof account.expiresAt !== "number" ||
|
|
1947
|
-
!Number.isFinite(account.expiresAt) ||
|
|
1948
|
-
account.expiresAt > nowMs)) {
|
|
1949
|
-
accessToken = account.accessToken;
|
|
1950
|
-
authDetail = "OK (cached access)";
|
|
1951
|
-
tokenAccountId = extractAccountId(account.accessToken);
|
|
1952
|
-
if (tokenAccountId &&
|
|
1953
|
-
shouldUpdateAccountIdFromToken(account.accountIdSource, account.accountId) &&
|
|
1954
|
-
tokenAccountId !== account.accountId) {
|
|
1955
|
-
account.accountId = tokenAccountId;
|
|
1956
|
-
account.accountIdSource = "token";
|
|
1957
|
-
storageChanged = true;
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
// If Codex CLI has a valid cached access token for this email, use it
|
|
1961
|
-
// instead of forcing a refresh.
|
|
1962
|
-
if (!accessToken) {
|
|
1963
|
-
const cached = await lookupCodexCliTokensByEmail(account.email);
|
|
1964
|
-
if (cached &&
|
|
1965
|
-
(typeof cached.expiresAt !== "number" ||
|
|
1966
|
-
!Number.isFinite(cached.expiresAt) ||
|
|
1967
|
-
cached.expiresAt > nowMs)) {
|
|
1968
|
-
accessToken = cached.accessToken;
|
|
1969
|
-
authDetail = "OK (Codex CLI cache)";
|
|
1970
|
-
if (cached.refreshToken && cached.refreshToken !== account.refreshToken) {
|
|
1971
|
-
account.refreshToken = cached.refreshToken;
|
|
1972
|
-
storageChanged = true;
|
|
1973
|
-
}
|
|
1974
|
-
if (cached.accessToken && cached.accessToken !== account.accessToken) {
|
|
1975
|
-
account.accessToken = cached.accessToken;
|
|
1976
|
-
storageChanged = true;
|
|
1977
|
-
}
|
|
1978
|
-
if (cached.expiresAt !== account.expiresAt) {
|
|
1979
|
-
account.expiresAt = cached.expiresAt;
|
|
1980
|
-
storageChanged = true;
|
|
1981
|
-
}
|
|
1982
|
-
const hydratedEmail = sanitizeEmail(extractAccountEmail(cached.accessToken));
|
|
1983
|
-
if (hydratedEmail && hydratedEmail !== account.email) {
|
|
1984
|
-
account.email = hydratedEmail;
|
|
1985
|
-
storageChanged = true;
|
|
1986
|
-
}
|
|
1987
|
-
tokenAccountId = extractAccountId(cached.accessToken);
|
|
1988
|
-
if (tokenAccountId &&
|
|
1989
|
-
shouldUpdateAccountIdFromToken(account.accountIdSource, account.accountId) &&
|
|
1990
|
-
tokenAccountId !== account.accountId) {
|
|
1991
|
-
account.accountId = tokenAccountId;
|
|
1992
|
-
account.accountIdSource = "token";
|
|
1993
|
-
storageChanged = true;
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
if (!accessToken) {
|
|
1998
|
-
const refreshResult = await queuedRefresh(account.refreshToken);
|
|
1999
|
-
if (refreshResult.type !== "success") {
|
|
2000
|
-
errors += 1;
|
|
2001
|
-
const message = refreshResult.message ?? refreshResult.reason ?? "refresh failed";
|
|
2002
|
-
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message})`);
|
|
2003
|
-
if (deepProbe && isFlaggableFailure(refreshResult)) {
|
|
2004
|
-
const existingIndex = flaggedStorage.accounts.findIndex((flagged) => flagged.refreshToken === account.refreshToken);
|
|
2005
|
-
const flaggedRecord = {
|
|
2006
|
-
...account,
|
|
2007
|
-
flaggedAt: Date.now(),
|
|
2008
|
-
flaggedReason: "token-invalid",
|
|
2009
|
-
lastError: message,
|
|
2010
|
-
};
|
|
2011
|
-
if (existingIndex >= 0) {
|
|
2012
|
-
flaggedStorage.accounts[existingIndex] = flaggedRecord;
|
|
2013
|
-
}
|
|
2014
|
-
else {
|
|
2015
|
-
flaggedStorage.accounts.push(flaggedRecord);
|
|
2016
|
-
}
|
|
2017
|
-
removeFromActive.add(account.refreshToken);
|
|
2018
|
-
flaggedChanged = true;
|
|
2019
|
-
}
|
|
2020
|
-
continue;
|
|
2021
|
-
}
|
|
2022
|
-
accessToken = refreshResult.access;
|
|
2023
|
-
authDetail = "OK";
|
|
2024
|
-
if (refreshResult.refresh !== account.refreshToken) {
|
|
2025
|
-
account.refreshToken = refreshResult.refresh;
|
|
2026
|
-
storageChanged = true;
|
|
2027
|
-
}
|
|
2028
|
-
if (refreshResult.access && refreshResult.access !== account.accessToken) {
|
|
2029
|
-
account.accessToken = refreshResult.access;
|
|
2030
|
-
storageChanged = true;
|
|
2031
|
-
}
|
|
2032
|
-
if (typeof refreshResult.expires === "number" &&
|
|
2033
|
-
refreshResult.expires !== account.expiresAt) {
|
|
2034
|
-
account.expiresAt = refreshResult.expires;
|
|
2035
|
-
storageChanged = true;
|
|
2036
|
-
}
|
|
2037
|
-
const hydratedEmail = sanitizeEmail(extractAccountEmail(refreshResult.access, refreshResult.idToken));
|
|
2038
|
-
if (hydratedEmail && hydratedEmail !== account.email) {
|
|
2039
|
-
account.email = hydratedEmail;
|
|
2040
|
-
storageChanged = true;
|
|
2041
|
-
}
|
|
2042
|
-
tokenAccountId = extractAccountId(refreshResult.access);
|
|
2043
|
-
if (tokenAccountId &&
|
|
2044
|
-
shouldUpdateAccountIdFromToken(account.accountIdSource, account.accountId) &&
|
|
2045
|
-
tokenAccountId !== account.accountId) {
|
|
2046
|
-
account.accountId = tokenAccountId;
|
|
2047
|
-
account.accountIdSource = "token";
|
|
2048
|
-
storageChanged = true;
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
if (!accessToken) {
|
|
2052
|
-
throw new Error("Missing access token after refresh");
|
|
2053
|
-
}
|
|
2054
|
-
if (deepProbe) {
|
|
2055
|
-
ok += 1;
|
|
2056
|
-
const detail = tokenAccountId
|
|
2057
|
-
? `${authDetail} (id:${tokenAccountId.slice(-6)})`
|
|
2058
|
-
: authDetail;
|
|
2059
|
-
console.log(`[${i + 1}/${total}] ${label}: ${detail}`);
|
|
2060
|
-
continue;
|
|
2061
|
-
}
|
|
2062
|
-
try {
|
|
2063
|
-
const requestAccountId = resolveRequestAccountId(account.accountId, account.accountIdSource, tokenAccountId) ??
|
|
2064
|
-
tokenAccountId ??
|
|
2065
|
-
account.accountId;
|
|
2066
|
-
if (!requestAccountId) {
|
|
2067
|
-
throw new Error("Missing accountId for quota probe");
|
|
2068
|
-
}
|
|
2069
|
-
const snapshot = await fetchCodexQuotaSnapshot({
|
|
2070
|
-
accountId: requestAccountId,
|
|
2071
|
-
accessToken,
|
|
2072
|
-
});
|
|
2073
|
-
ok += 1;
|
|
2074
|
-
console.log(`[${i + 1}/${total}] ${label}: ${formatCodexQuotaLine(snapshot)}`);
|
|
2075
|
-
}
|
|
2076
|
-
catch (error) {
|
|
2077
|
-
errors += 1;
|
|
2078
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2079
|
-
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message.slice(0, 160)})`);
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
catch (error) {
|
|
2083
|
-
errors += 1;
|
|
2084
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2085
|
-
console.log(`[${i + 1}/${total}] ${label}: ERROR (${message.slice(0, 120)})`);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
if (removeFromActive.size > 0) {
|
|
2089
|
-
workingStorage.accounts = workingStorage.accounts.filter((account) => !removeFromActive.has(account.refreshToken));
|
|
2090
|
-
clampActiveIndices(workingStorage);
|
|
2091
|
-
storageChanged = true;
|
|
2092
|
-
}
|
|
2093
|
-
if (storageChanged) {
|
|
2094
|
-
await saveAccounts(workingStorage);
|
|
2095
|
-
invalidateAccountManagerCache();
|
|
2096
|
-
}
|
|
2097
|
-
if (flaggedChanged) {
|
|
2098
|
-
await saveFlaggedAccounts(flaggedStorage);
|
|
2099
|
-
}
|
|
2100
|
-
console.log("");
|
|
2101
|
-
console.log(`Results: ${ok} ok, ${errors} error, ${disabled} disabled`);
|
|
2102
|
-
if (removeFromActive.size > 0) {
|
|
2103
|
-
console.log(`Moved ${removeFromActive.size} account(s) to flagged pool (invalid refresh token).`);
|
|
2104
|
-
}
|
|
2105
|
-
console.log("");
|
|
2106
|
-
};
|
|
2107
|
-
const verifyFlaggedAccounts = async () => {
|
|
2108
|
-
const flaggedStorage = await loadFlaggedAccounts();
|
|
2109
|
-
if (flaggedStorage.accounts.length === 0) {
|
|
2110
|
-
console.log("\nNo flagged accounts to verify.\n");
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
console.log("\nVerifying flagged accounts...\n");
|
|
2114
|
-
const remaining = [];
|
|
2115
|
-
const restored = [];
|
|
2116
|
-
for (let i = 0; i < flaggedStorage.accounts.length; i += 1) {
|
|
2117
|
-
const flagged = flaggedStorage.accounts[i];
|
|
2118
|
-
if (!flagged)
|
|
2119
|
-
continue;
|
|
2120
|
-
const label = flagged.email ?? flagged.accountLabel ?? `Flagged ${i + 1}`;
|
|
2121
|
-
try {
|
|
2122
|
-
const cached = await lookupCodexCliTokensByEmail(flagged.email);
|
|
2123
|
-
const now = Date.now();
|
|
2124
|
-
if (cached &&
|
|
2125
|
-
typeof cached.expiresAt === "number" &&
|
|
2126
|
-
Number.isFinite(cached.expiresAt) &&
|
|
2127
|
-
cached.expiresAt > now) {
|
|
2128
|
-
const refreshToken = typeof cached.refreshToken === "string" && cached.refreshToken.trim()
|
|
2129
|
-
? cached.refreshToken.trim()
|
|
2130
|
-
: flagged.refreshToken;
|
|
2131
|
-
const resolved = resolveAccountSelection({
|
|
2132
|
-
type: "success",
|
|
2133
|
-
access: cached.accessToken,
|
|
2134
|
-
refresh: refreshToken,
|
|
2135
|
-
expires: cached.expiresAt,
|
|
2136
|
-
multiAccount: true,
|
|
2137
|
-
});
|
|
2138
|
-
if (!resolved.accountIdOverride && flagged.accountId) {
|
|
2139
|
-
resolved.accountIdOverride = flagged.accountId;
|
|
2140
|
-
resolved.accountIdSource = flagged.accountIdSource ?? "manual";
|
|
2141
|
-
}
|
|
2142
|
-
if (!resolved.accountLabel && flagged.accountLabel) {
|
|
2143
|
-
resolved.accountLabel = flagged.accountLabel;
|
|
2144
|
-
}
|
|
2145
|
-
restored.push(resolved);
|
|
2146
|
-
console.log(`[${i + 1}/${flaggedStorage.accounts.length}] ${label}: RESTORED (Codex CLI cache)`);
|
|
2147
|
-
continue;
|
|
2148
|
-
}
|
|
2149
|
-
const refreshResult = await queuedRefresh(flagged.refreshToken);
|
|
2150
|
-
if (refreshResult.type !== "success") {
|
|
2151
|
-
console.log(`[${i + 1}/${flaggedStorage.accounts.length}] ${label}: STILL FLAGGED (${refreshResult.message ?? refreshResult.reason ?? "refresh failed"})`);
|
|
2152
|
-
remaining.push(flagged);
|
|
2153
|
-
continue;
|
|
2154
|
-
}
|
|
2155
|
-
const resolved = resolveAccountSelection(refreshResult);
|
|
2156
|
-
if (!resolved.accountIdOverride && flagged.accountId) {
|
|
2157
|
-
resolved.accountIdOverride = flagged.accountId;
|
|
2158
|
-
resolved.accountIdSource = flagged.accountIdSource ?? "manual";
|
|
2159
|
-
}
|
|
2160
|
-
if (!resolved.accountLabel && flagged.accountLabel) {
|
|
2161
|
-
resolved.accountLabel = flagged.accountLabel;
|
|
2162
|
-
}
|
|
2163
|
-
restored.push(resolved);
|
|
2164
|
-
console.log(`[${i + 1}/${flaggedStorage.accounts.length}] ${label}: RESTORED`);
|
|
2165
|
-
}
|
|
2166
|
-
catch (error) {
|
|
2167
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2168
|
-
console.log(`[${i + 1}/${flaggedStorage.accounts.length}] ${label}: ERROR (${message.slice(0, 120)})`);
|
|
2169
|
-
remaining.push({
|
|
2170
|
-
...flagged,
|
|
2171
|
-
lastError: message,
|
|
2172
|
-
});
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
if (restored.length > 0) {
|
|
2176
|
-
await persistAccountPool(restored, false);
|
|
2177
|
-
invalidateAccountManagerCache();
|
|
2178
|
-
}
|
|
2179
|
-
await saveFlaggedAccounts({
|
|
2180
|
-
version: 1,
|
|
2181
|
-
accounts: remaining,
|
|
2182
|
-
});
|
|
2183
|
-
console.log("");
|
|
2184
|
-
console.log(`Results: ${restored.length} restored, ${remaining.length} still flagged`);
|
|
2185
|
-
console.log("");
|
|
2186
|
-
};
|
|
2187
1419
|
if (!explicitLoginMode) {
|
|
2188
1420
|
while (true) {
|
|
2189
|
-
const loadedStorage = await
|
|
1421
|
+
const loadedStorage = await hydrateRuntimeEmails(await loadAccounts(), {
|
|
1422
|
+
queuedRefresh,
|
|
1423
|
+
extractAccountId,
|
|
1424
|
+
sanitizeEmail,
|
|
1425
|
+
extractAccountEmail,
|
|
1426
|
+
shouldUpdateAccountIdFromToken,
|
|
1427
|
+
saveAccounts,
|
|
1428
|
+
logWarn,
|
|
1429
|
+
pluginName: PLUGIN_NAME,
|
|
1430
|
+
});
|
|
2190
1431
|
const workingStorage = loadedStorage
|
|
2191
1432
|
? {
|
|
2192
1433
|
...loadedStorage,
|
|
2193
|
-
accounts: loadedStorage.accounts.map((account) => ({
|
|
1434
|
+
accounts: loadedStorage.accounts.map((account) => ({
|
|
1435
|
+
...account,
|
|
1436
|
+
})),
|
|
2194
1437
|
activeIndexByFamily: loadedStorage.activeIndexByFamily
|
|
2195
1438
|
? { ...loadedStorage.activeIndexByFamily }
|
|
2196
1439
|
: {},
|
|
2197
1440
|
}
|
|
2198
|
-
: {
|
|
1441
|
+
: {
|
|
1442
|
+
version: 3,
|
|
1443
|
+
accounts: [],
|
|
1444
|
+
activeIndex: 0,
|
|
1445
|
+
activeIndexByFamily: {},
|
|
1446
|
+
};
|
|
2199
1447
|
const flaggedStorage = await loadFlaggedAccounts();
|
|
2200
|
-
if (workingStorage.accounts.length === 0 &&
|
|
1448
|
+
if (workingStorage.accounts.length === 0 &&
|
|
1449
|
+
flaggedStorage.accounts.length === 0) {
|
|
2201
1450
|
break;
|
|
2202
1451
|
}
|
|
2203
1452
|
const now = Date.now();
|
|
2204
1453
|
const activeIndex = resolveActiveIndex(workingStorage, "codex");
|
|
2205
|
-
const existingAccounts = workingStorage.accounts
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
}
|
|
2210
|
-
else if (typeof account.coolingDownUntil === "number" &&
|
|
2211
|
-
account.coolingDownUntil > now) {
|
|
2212
|
-
status = "cooldown";
|
|
2213
|
-
}
|
|
2214
|
-
else if (formatRateLimitEntry(account, now)) {
|
|
2215
|
-
status = "rate-limited";
|
|
2216
|
-
}
|
|
2217
|
-
else if (index === activeIndex) {
|
|
2218
|
-
status = "active";
|
|
2219
|
-
}
|
|
2220
|
-
else {
|
|
2221
|
-
status = "ok";
|
|
2222
|
-
}
|
|
2223
|
-
return {
|
|
2224
|
-
accountId: account.accountId,
|
|
2225
|
-
accountLabel: account.accountLabel,
|
|
2226
|
-
email: account.email,
|
|
2227
|
-
index,
|
|
2228
|
-
addedAt: account.addedAt,
|
|
2229
|
-
lastUsed: account.lastUsed,
|
|
2230
|
-
status,
|
|
2231
|
-
isCurrentAccount: index === activeIndex,
|
|
2232
|
-
enabled: account.enabled !== false,
|
|
2233
|
-
};
|
|
1454
|
+
const existingAccounts = buildLoginMenuAccounts(workingStorage.accounts, {
|
|
1455
|
+
now,
|
|
1456
|
+
activeIndex,
|
|
1457
|
+
formatRateLimitEntry: (account, currentNow) => formatRateLimitEntry(account, currentNow, formatWaitTime),
|
|
2234
1458
|
});
|
|
2235
1459
|
const menuResult = await promptLoginMode(existingAccounts, {
|
|
2236
1460
|
flaggedCount: flaggedStorage.accounts.length,
|
|
@@ -2245,16 +1469,62 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2245
1469
|
}),
|
|
2246
1470
|
};
|
|
2247
1471
|
}
|
|
1472
|
+
const accountCheckDeps = {
|
|
1473
|
+
hydrateEmails: (storage) => hydrateRuntimeEmails(storage, {
|
|
1474
|
+
queuedRefresh,
|
|
1475
|
+
extractAccountId,
|
|
1476
|
+
sanitizeEmail,
|
|
1477
|
+
extractAccountEmail,
|
|
1478
|
+
shouldUpdateAccountIdFromToken,
|
|
1479
|
+
saveAccounts,
|
|
1480
|
+
logWarn,
|
|
1481
|
+
pluginName: PLUGIN_NAME,
|
|
1482
|
+
}),
|
|
1483
|
+
loadAccounts,
|
|
1484
|
+
createEmptyStorage: () => ({
|
|
1485
|
+
version: 3,
|
|
1486
|
+
accounts: [],
|
|
1487
|
+
activeIndex: 0,
|
|
1488
|
+
activeIndexByFamily: {},
|
|
1489
|
+
}),
|
|
1490
|
+
loadFlaggedAccounts,
|
|
1491
|
+
createAccountCheckWorkingState,
|
|
1492
|
+
lookupCodexCliTokensByEmail,
|
|
1493
|
+
extractAccountId,
|
|
1494
|
+
shouldUpdateAccountIdFromToken,
|
|
1495
|
+
sanitizeEmail,
|
|
1496
|
+
extractAccountEmail,
|
|
1497
|
+
queuedRefresh,
|
|
1498
|
+
isRuntimeFlaggableFailure: isFlaggableFailure,
|
|
1499
|
+
fetchCodexQuotaSnapshot,
|
|
1500
|
+
resolveRequestAccountId,
|
|
1501
|
+
formatCodexQuotaLine: formatQuotaSnapshotLine,
|
|
1502
|
+
clampRuntimeActiveIndices: clampActiveIndices,
|
|
1503
|
+
MODEL_FAMILIES,
|
|
1504
|
+
saveAccounts,
|
|
1505
|
+
invalidateAccountManagerCache: () => invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps),
|
|
1506
|
+
saveFlaggedAccounts,
|
|
1507
|
+
showLine: (message) => console.log(message),
|
|
1508
|
+
};
|
|
2248
1509
|
if (menuResult.mode === "check") {
|
|
2249
|
-
await
|
|
1510
|
+
await runRuntimeAccountCheck(false, accountCheckDeps);
|
|
2250
1511
|
continue;
|
|
2251
1512
|
}
|
|
2252
1513
|
if (menuResult.mode === "deep-check") {
|
|
2253
|
-
await
|
|
1514
|
+
await runRuntimeAccountCheck(true, accountCheckDeps);
|
|
2254
1515
|
continue;
|
|
2255
1516
|
}
|
|
2256
1517
|
if (menuResult.mode === "verify-flagged") {
|
|
2257
|
-
await
|
|
1518
|
+
await verifyRuntimeFlaggedAccounts({
|
|
1519
|
+
loadFlaggedAccounts,
|
|
1520
|
+
lookupCodexCliTokensByEmail,
|
|
1521
|
+
queuedRefresh,
|
|
1522
|
+
resolveTokenSuccessAccount,
|
|
1523
|
+
persistAccounts: persistAccountPool,
|
|
1524
|
+
invalidateAccountManagerCache: () => invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps),
|
|
1525
|
+
saveFlaggedAccounts,
|
|
1526
|
+
showLine: (message) => console.log(message),
|
|
1527
|
+
});
|
|
2258
1528
|
continue;
|
|
2259
1529
|
}
|
|
2260
1530
|
if (menuResult.mode === "manage") {
|
|
@@ -2262,13 +1532,13 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2262
1532
|
const target = workingStorage.accounts[menuResult.deleteAccountIndex];
|
|
2263
1533
|
if (target) {
|
|
2264
1534
|
workingStorage.accounts.splice(menuResult.deleteAccountIndex, 1);
|
|
2265
|
-
clampActiveIndices(workingStorage);
|
|
1535
|
+
clampActiveIndices(workingStorage, MODEL_FAMILIES);
|
|
2266
1536
|
await saveAccounts(workingStorage);
|
|
2267
1537
|
await saveFlaggedAccounts({
|
|
2268
1538
|
version: 1,
|
|
2269
1539
|
accounts: flaggedStorage.accounts.filter((flagged) => flagged.refreshToken !== target.refreshToken),
|
|
2270
1540
|
});
|
|
2271
|
-
|
|
1541
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
2272
1542
|
console.log(`\nDeleted ${target.email ?? `Account ${menuResult.deleteAccountIndex + 1}`}.\n`);
|
|
2273
1543
|
}
|
|
2274
1544
|
continue;
|
|
@@ -2278,7 +1548,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2278
1548
|
if (target) {
|
|
2279
1549
|
target.enabled = target.enabled === false ? true : false;
|
|
2280
1550
|
await saveAccounts(workingStorage);
|
|
2281
|
-
|
|
1551
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
2282
1552
|
console.log(`\n${target.email ?? `Account ${menuResult.toggleAccountIndex + 1}`} ${target.enabled === false ? "disabled" : "enabled"}.\n`);
|
|
2283
1553
|
}
|
|
2284
1554
|
continue;
|
|
@@ -2295,7 +1565,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2295
1565
|
if (menuResult.deleteAll) {
|
|
2296
1566
|
await clearAccounts();
|
|
2297
1567
|
await clearFlaggedAccounts();
|
|
2298
|
-
|
|
1568
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
2299
1569
|
console.log("\nCleared saved accounts from active storage. Recovery snapshots remain available. Starting fresh.\n");
|
|
2300
1570
|
}
|
|
2301
1571
|
break;
|
|
@@ -2307,7 +1577,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2307
1577
|
const latestStorage = await loadAccounts();
|
|
2308
1578
|
const existingCount = latestStorage?.accounts.length ?? 0;
|
|
2309
1579
|
const requestedCount = Number.parseInt(inputs?.accountCount ?? "1", 10);
|
|
2310
|
-
const normalizedRequested = Number.isFinite(requestedCount)
|
|
1580
|
+
const normalizedRequested = Number.isFinite(requestedCount)
|
|
1581
|
+
? requestedCount
|
|
1582
|
+
: 1;
|
|
2311
1583
|
const availableSlots = refreshAccountIndex !== undefined
|
|
2312
1584
|
? 1
|
|
2313
1585
|
: startFresh
|
|
@@ -2332,39 +1604,72 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2332
1604
|
}
|
|
2333
1605
|
if (useManualMode) {
|
|
2334
1606
|
const { pkce, state, url } = await createAuthorizationFlow();
|
|
2335
|
-
return buildManualOAuthFlow(
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
1607
|
+
return buildManualOAuthFlow({
|
|
1608
|
+
pkce,
|
|
1609
|
+
url,
|
|
1610
|
+
expectedState: state,
|
|
1611
|
+
redirectUri: REDIRECT_URI,
|
|
1612
|
+
parseAuthorizationInput,
|
|
1613
|
+
exchangeAuthorizationCode,
|
|
1614
|
+
resolveTokenSuccess: resolveTokenSuccessAccount,
|
|
1615
|
+
instructions: AUTH_LABELS.INSTRUCTIONS_MANUAL,
|
|
1616
|
+
onSuccess: async (tokens) => {
|
|
1617
|
+
try {
|
|
1618
|
+
await persistAccountPool([tokens], startFresh);
|
|
1619
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
1620
|
+
}
|
|
1621
|
+
catch (err) {
|
|
1622
|
+
const storagePath = getStoragePath();
|
|
1623
|
+
const errorCode = err?.code || "UNKNOWN";
|
|
1624
|
+
const hint = err instanceof StorageError
|
|
1625
|
+
? err.hint
|
|
1626
|
+
: formatStorageErrorHint(err, storagePath);
|
|
1627
|
+
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
1628
|
+
await showRuntimeToast(client, hint, "error", {
|
|
1629
|
+
title: "Account Persistence Failed",
|
|
1630
|
+
duration: 10000,
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
},
|
|
2352
1634
|
});
|
|
2353
1635
|
}
|
|
2354
|
-
const explicitCountProvided = typeof inputs?.accountCount === "string" &&
|
|
1636
|
+
const explicitCountProvided = typeof inputs?.accountCount === "string" &&
|
|
1637
|
+
inputs.accountCount.trim().length > 0;
|
|
2355
1638
|
while (accounts.length < targetCount) {
|
|
2356
1639
|
logInfo(`=== OpenAI OAuth (Account ${accounts.length + 1}) ===`);
|
|
2357
1640
|
const forceNewLogin = accounts.length > 0 || refreshAccountIndex !== undefined;
|
|
2358
|
-
const result = await
|
|
1641
|
+
const result = await runRuntimeOAuthFlow(forceNewLogin, {
|
|
1642
|
+
runBrowserOAuthFlow: (input) => runBrowserOAuthFlow({
|
|
1643
|
+
...input,
|
|
1644
|
+
createAuthorizationFlow,
|
|
1645
|
+
redactOAuthUrlForLog,
|
|
1646
|
+
startLocalOAuthServer,
|
|
1647
|
+
openBrowserUrl,
|
|
1648
|
+
pluginName: PLUGIN_NAME,
|
|
1649
|
+
authManualLabel: AUTH_LABELS.OAUTH_MANUAL,
|
|
1650
|
+
exchangeAuthorizationCode,
|
|
1651
|
+
redirectUri: REDIRECT_URI,
|
|
1652
|
+
}),
|
|
1653
|
+
manualModeLabel: AUTH_LABELS.OAUTH_MANUAL,
|
|
1654
|
+
logInfo,
|
|
1655
|
+
logDebug,
|
|
1656
|
+
logWarn,
|
|
1657
|
+
pluginName: PLUGIN_NAME,
|
|
1658
|
+
});
|
|
2359
1659
|
let resolved = null;
|
|
2360
1660
|
if (result.type === "success") {
|
|
2361
|
-
resolved =
|
|
1661
|
+
resolved = resolveTokenSuccessAccount(result);
|
|
2362
1662
|
const email = extractAccountEmail(resolved.access, resolved.idToken);
|
|
2363
|
-
const accountId = resolved.accountIdOverride ??
|
|
2364
|
-
|
|
1663
|
+
const accountId = resolved.accountIdOverride ??
|
|
1664
|
+
extractAccountId(resolved.access);
|
|
1665
|
+
const label = resolved.accountLabel ??
|
|
1666
|
+
email ??
|
|
1667
|
+
accountId ??
|
|
1668
|
+
"Unknown account";
|
|
2365
1669
|
logInfo(`Authenticated as: ${label}`);
|
|
2366
1670
|
const isDuplicate = findMatchingAccountIndex(accounts.map((account) => ({
|
|
2367
|
-
accountId: account.accountIdOverride ??
|
|
1671
|
+
accountId: account.accountIdOverride ??
|
|
1672
|
+
extractAccountId(account.access),
|
|
2368
1673
|
email: sanitizeEmail(extractAccountEmail(account.access, account.idToken)),
|
|
2369
1674
|
refreshToken: account.refresh,
|
|
2370
1675
|
})), {
|
|
@@ -2394,11 +1699,11 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2394
1699
|
continue;
|
|
2395
1700
|
}
|
|
2396
1701
|
accounts.push(resolved);
|
|
2397
|
-
await
|
|
1702
|
+
await showRuntimeToast(client, `Account ${accounts.length} authenticated`, "success");
|
|
2398
1703
|
try {
|
|
2399
1704
|
const isFirstAccount = accounts.length === 1;
|
|
2400
1705
|
await persistAccountPool([resolved], isFirstAccount && startFresh);
|
|
2401
|
-
|
|
1706
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
2402
1707
|
}
|
|
2403
1708
|
catch (err) {
|
|
2404
1709
|
const storagePath = getStoragePath();
|
|
@@ -2407,7 +1712,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2407
1712
|
? err.hint
|
|
2408
1713
|
: formatStorageErrorHint(err, storagePath);
|
|
2409
1714
|
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
2410
|
-
await
|
|
1715
|
+
await showRuntimeToast(client, hint, "error", {
|
|
2411
1716
|
title: "Account Persistence Failed",
|
|
2412
1717
|
duration: 10000,
|
|
2413
1718
|
});
|
|
@@ -2463,20 +1768,48 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2463
1768
|
// Initialize storage path for manual OAuth flow
|
|
2464
1769
|
// Must happen BEFORE persistAccountPool to ensure correct storage location
|
|
2465
1770
|
const manualPluginConfig = loadPluginConfig();
|
|
2466
|
-
applyUiRuntimeFromConfig(manualPluginConfig);
|
|
2467
|
-
|
|
1771
|
+
applyUiRuntimeFromConfig(manualPluginConfig, setUiRuntimeOptions);
|
|
1772
|
+
applyAccountStorageScopeFromConfig(manualPluginConfig, {
|
|
1773
|
+
getPerProjectAccounts,
|
|
1774
|
+
getStorageBackupEnabled,
|
|
1775
|
+
setStorageBackupEnabled,
|
|
1776
|
+
isCodexCliSyncEnabled,
|
|
1777
|
+
getWarningShown: () => perProjectStorageWarningShown,
|
|
1778
|
+
setWarningShown: (shown) => {
|
|
1779
|
+
perProjectStorageWarningShown = shown;
|
|
1780
|
+
},
|
|
1781
|
+
logWarn,
|
|
1782
|
+
pluginName: PLUGIN_NAME,
|
|
1783
|
+
setStoragePath,
|
|
1784
|
+
cwd: () => process.cwd(),
|
|
1785
|
+
});
|
|
2468
1786
|
const { pkce, state, url } = await createAuthorizationFlow();
|
|
2469
|
-
return buildManualOAuthFlow(
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
1787
|
+
return buildManualOAuthFlow({
|
|
1788
|
+
pkce,
|
|
1789
|
+
url,
|
|
1790
|
+
expectedState: state,
|
|
1791
|
+
redirectUri: REDIRECT_URI,
|
|
1792
|
+
parseAuthorizationInput,
|
|
1793
|
+
exchangeAuthorizationCode,
|
|
1794
|
+
resolveTokenSuccess: resolveTokenSuccessAccount,
|
|
1795
|
+
instructions: AUTH_LABELS.INSTRUCTIONS_MANUAL,
|
|
1796
|
+
onSuccess: async (tokens) => {
|
|
1797
|
+
try {
|
|
1798
|
+
await persistAccountPool([tokens], false);
|
|
1799
|
+
}
|
|
1800
|
+
catch (err) {
|
|
1801
|
+
const storagePath = getStoragePath();
|
|
1802
|
+
const errorCode = err?.code || "UNKNOWN";
|
|
1803
|
+
const hint = err instanceof StorageError
|
|
1804
|
+
? err.hint
|
|
1805
|
+
: formatStorageErrorHint(err, storagePath);
|
|
1806
|
+
logError(`[${PLUGIN_NAME}] Failed to persist account: [${errorCode}] ${err?.message ?? String(err)}`);
|
|
1807
|
+
await showRuntimeToast(client, hint, "error", {
|
|
1808
|
+
title: "Account Persistence Failed",
|
|
1809
|
+
duration: 10000,
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
2480
1813
|
});
|
|
2481
1814
|
},
|
|
2482
1815
|
},
|
|
@@ -2492,7 +1825,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2492
1825
|
description: "List all Codex OAuth accounts and the current active index.",
|
|
2493
1826
|
args: {},
|
|
2494
1827
|
async execute() {
|
|
2495
|
-
const ui =
|
|
1828
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2496
1829
|
const storage = await loadAccounts();
|
|
2497
1830
|
const storePath = getStoragePath();
|
|
2498
1831
|
if (!storage || storage.accounts.length === 0) {
|
|
@@ -2531,7 +1864,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2531
1864
|
badges.push(formatUiBadge(ui, "current", "accent"));
|
|
2532
1865
|
if (account.enabled === false)
|
|
2533
1866
|
badges.push(formatUiBadge(ui, "disabled", "danger"));
|
|
2534
|
-
const rateLimit = formatRateLimitEntry(account, now);
|
|
1867
|
+
const rateLimit = formatRateLimitEntry(account, now, formatWaitTime);
|
|
2535
1868
|
if (rateLimit)
|
|
2536
1869
|
badges.push(formatUiBadge(ui, "rate-limited", "warning"));
|
|
2537
1870
|
if (typeof account.coolingDownUntil === "number" &&
|
|
@@ -2569,13 +1902,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2569
1902
|
storage.accounts.forEach((account, index) => {
|
|
2570
1903
|
const label = formatAccountLabel(account, index);
|
|
2571
1904
|
const statuses = [];
|
|
2572
|
-
const rateLimit = formatRateLimitEntry(account, now);
|
|
1905
|
+
const rateLimit = formatRateLimitEntry(account, now, formatWaitTime);
|
|
2573
1906
|
if (index === activeIndex)
|
|
2574
1907
|
statuses.push("active");
|
|
2575
1908
|
if (rateLimit)
|
|
2576
1909
|
statuses.push("rate-limited");
|
|
2577
|
-
if (typeof account.coolingDownUntil ===
|
|
2578
|
-
"number" &&
|
|
1910
|
+
if (typeof account.coolingDownUntil === "number" &&
|
|
2579
1911
|
account.coolingDownUntil > now) {
|
|
2580
1912
|
statuses.push("cooldown");
|
|
2581
1913
|
}
|
|
@@ -2596,10 +1928,12 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2596
1928
|
"codex-switch": tool({
|
|
2597
1929
|
description: "Switch active Codex account by index (1-based).",
|
|
2598
1930
|
args: {
|
|
2599
|
-
index: tool.schema
|
|
1931
|
+
index: tool.schema
|
|
1932
|
+
.number()
|
|
1933
|
+
.describe("Account number to switch to (1-based, e.g., 1 for first account)"),
|
|
2600
1934
|
},
|
|
2601
1935
|
async execute({ index }) {
|
|
2602
|
-
const ui =
|
|
1936
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2603
1937
|
const storage = await loadAccounts();
|
|
2604
1938
|
if (!storage || storage.accounts.length === 0) {
|
|
2605
1939
|
if (ui.v2Enabled) {
|
|
@@ -2641,7 +1975,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2641
1975
|
await saveAccounts(storage);
|
|
2642
1976
|
}
|
|
2643
1977
|
catch (saveError) {
|
|
2644
|
-
logWarn("Failed to save account switch", {
|
|
1978
|
+
logWarn("Failed to save account switch", {
|
|
1979
|
+
error: String(saveError),
|
|
1980
|
+
});
|
|
2645
1981
|
if (ui.v2Enabled) {
|
|
2646
1982
|
return [
|
|
2647
1983
|
...formatUiHeader(ui, "Switch account"),
|
|
@@ -2653,7 +1989,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2653
1989
|
return `Switched to ${formatAccountLabel(account, targetIndex)} but failed to persist. Changes may be lost on restart.`;
|
|
2654
1990
|
}
|
|
2655
1991
|
if (cachedAccountManager) {
|
|
2656
|
-
await
|
|
1992
|
+
await reloadRuntimeAccountManager(makeReloadAccountManagerDeps());
|
|
2657
1993
|
}
|
|
2658
1994
|
const label = formatAccountLabel(account, targetIndex);
|
|
2659
1995
|
if (ui.v2Enabled) {
|
|
@@ -2670,7 +2006,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2670
2006
|
description: "Show detailed status of Codex accounts and rate limits.",
|
|
2671
2007
|
args: {},
|
|
2672
2008
|
async execute() {
|
|
2673
|
-
const ui =
|
|
2009
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2674
2010
|
const storage = await loadAccounts();
|
|
2675
2011
|
if (!storage || storage.accounts.length === 0) {
|
|
2676
2012
|
if (ui.v2Enabled) {
|
|
@@ -2699,7 +2035,7 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2699
2035
|
badges.push(formatUiBadge(ui, "active", "accent"));
|
|
2700
2036
|
if (account.enabled === false)
|
|
2701
2037
|
badges.push(formatUiBadge(ui, "disabled", "danger"));
|
|
2702
|
-
const rateLimit = formatRateLimitEntry(account, now) ?? "none";
|
|
2038
|
+
const rateLimit = formatRateLimitEntry(account, now, formatWaitTime) ?? "none";
|
|
2703
2039
|
const cooldown = formatCooldown(account, now) ?? "none";
|
|
2704
2040
|
if (rateLimit !== "none")
|
|
2705
2041
|
badges.push(formatUiBadge(ui, "rate-limited", "warning"));
|
|
@@ -2715,7 +2051,9 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2715
2051
|
lines.push(...formatUiSection(ui, "Active index by model family"));
|
|
2716
2052
|
for (const family of MODEL_FAMILIES) {
|
|
2717
2053
|
const idx = storage.activeIndexByFamily?.[family];
|
|
2718
|
-
const familyIndexLabel = typeof idx === "number" && Number.isFinite(idx)
|
|
2054
|
+
const familyIndexLabel = typeof idx === "number" && Number.isFinite(idx)
|
|
2055
|
+
? String(idx + 1)
|
|
2056
|
+
: "-";
|
|
2719
2057
|
lines.push(formatUiItem(ui, `${family}: ${familyIndexLabel}`));
|
|
2720
2058
|
}
|
|
2721
2059
|
lines.push("");
|
|
@@ -2749,18 +2087,27 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2749
2087
|
storage.accounts.forEach((account, index) => {
|
|
2750
2088
|
const label = formatAccountLabel(account, index);
|
|
2751
2089
|
const active = index === activeIndex ? "Yes" : "No";
|
|
2752
|
-
const rateLimit = formatRateLimitEntry(account, now) ?? "None";
|
|
2090
|
+
const rateLimit = formatRateLimitEntry(account, now, formatWaitTime) ?? "None";
|
|
2753
2091
|
const cooldown = formatCooldown(account, now) ?? "No";
|
|
2754
2092
|
const lastUsed = typeof account.lastUsed === "number" && account.lastUsed > 0
|
|
2755
2093
|
? `${formatWaitTime(now - account.lastUsed)} ago`
|
|
2756
2094
|
: "-";
|
|
2757
|
-
lines.push(buildTableRow([
|
|
2095
|
+
lines.push(buildTableRow([
|
|
2096
|
+
String(index + 1),
|
|
2097
|
+
label,
|
|
2098
|
+
active,
|
|
2099
|
+
rateLimit,
|
|
2100
|
+
cooldown,
|
|
2101
|
+
lastUsed,
|
|
2102
|
+
], statusTableOptions));
|
|
2758
2103
|
});
|
|
2759
2104
|
lines.push("");
|
|
2760
2105
|
lines.push("Active index by model family:");
|
|
2761
2106
|
for (const family of MODEL_FAMILIES) {
|
|
2762
2107
|
const idx = storage.activeIndexByFamily?.[family];
|
|
2763
|
-
const familyIndexLabel = typeof idx === "number" && Number.isFinite(idx)
|
|
2108
|
+
const familyIndexLabel = typeof idx === "number" && Number.isFinite(idx)
|
|
2109
|
+
? String(idx + 1)
|
|
2110
|
+
: "-";
|
|
2764
2111
|
lines.push(` ${family}: ${familyIndexLabel}`);
|
|
2765
2112
|
}
|
|
2766
2113
|
lines.push("");
|
|
@@ -2777,95 +2124,97 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2777
2124
|
return lines.join("\n");
|
|
2778
2125
|
},
|
|
2779
2126
|
}),
|
|
2780
|
-
...(exposeAdvancedCodexTools
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
if (runtimeMetrics.lastError) {
|
|
2826
|
-
lines.push(`Last error: ${runtimeMetrics.lastError}`);
|
|
2827
|
-
}
|
|
2828
|
-
if (ui.v2Enabled) {
|
|
2829
|
-
const styled = [
|
|
2830
|
-
...formatUiHeader(ui, "Codex plugin metrics"),
|
|
2831
|
-
formatUiKeyValue(ui, "Uptime", formatWaitTime(uptimeMs)),
|
|
2832
|
-
formatUiKeyValue(ui, "Total upstream requests", String(total)),
|
|
2833
|
-
formatUiKeyValue(ui, "Successful responses", String(successful), "success"),
|
|
2834
|
-
formatUiKeyValue(ui, "Failed responses", String(runtimeMetrics.failedRequests), "danger"),
|
|
2835
|
-
formatUiKeyValue(ui, "Success rate", `${successRate}%`, "accent"),
|
|
2836
|
-
formatUiKeyValue(ui, "Average successful latency", `${avgLatencyMs}ms`),
|
|
2837
|
-
formatUiKeyValue(ui, "Rate-limited responses", String(runtimeMetrics.rateLimitedResponses), "warning"),
|
|
2838
|
-
formatUiKeyValue(ui, "Server errors (5xx)", String(runtimeMetrics.serverErrors), "danger"),
|
|
2839
|
-
formatUiKeyValue(ui, "Network errors", String(runtimeMetrics.networkErrors), "danger"),
|
|
2840
|
-
formatUiKeyValue(ui, "User aborts", String(runtimeMetrics.userAborts), "muted"),
|
|
2841
|
-
formatUiKeyValue(ui, "Auth refresh failures", String(runtimeMetrics.authRefreshFailures), "warning"),
|
|
2842
|
-
formatUiKeyValue(ui, "Account rotations", String(runtimeMetrics.accountRotations), "accent"),
|
|
2843
|
-
formatUiKeyValue(ui, "Same-account retries", String(runtimeMetrics.sameAccountRetries), "warning"),
|
|
2844
|
-
formatUiKeyValue(ui, "Stream failover attempts", String(runtimeMetrics.streamFailoverAttempts), "muted"),
|
|
2845
|
-
formatUiKeyValue(ui, "Stream failover recoveries", String(runtimeMetrics.streamFailoverRecoveries), "success"),
|
|
2846
|
-
formatUiKeyValue(ui, "Stream failover cross-account recoveries", String(runtimeMetrics.streamFailoverCrossAccountRecoveries), "accent"),
|
|
2847
|
-
formatUiKeyValue(ui, "Empty-response retries", String(runtimeMetrics.emptyResponseRetries), "warning"),
|
|
2848
|
-
formatUiKeyValue(ui, "Session affinity entries", String(sessionAffinityEntries), "muted"),
|
|
2849
|
-
formatUiKeyValue(ui, "Live sync", `${liveSyncSnapshot?.running ? "on" : "off"} (${liveSyncSnapshot?.reloadCount ?? 0} reloads)`, liveSyncSnapshot?.running ? "success" : "muted"),
|
|
2850
|
-
formatUiKeyValue(ui, "Refresh guardian", guardianStats
|
|
2851
|
-
? `on (${guardianStats.refreshed} refreshed, ${guardianStats.failed} failed)`
|
|
2852
|
-
: "off", guardianStats ? "success" : "muted"),
|
|
2853
|
-
formatUiKeyValue(ui, "Last upstream request", lastRequest, "muted"),
|
|
2127
|
+
...(exposeAdvancedCodexTools
|
|
2128
|
+
? {
|
|
2129
|
+
"codex-metrics": tool({
|
|
2130
|
+
description: "Show runtime request metrics for this plugin process.",
|
|
2131
|
+
args: {},
|
|
2132
|
+
execute() {
|
|
2133
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2134
|
+
const now = Date.now();
|
|
2135
|
+
const uptimeMs = Math.max(0, now - runtimeMetrics.startedAt);
|
|
2136
|
+
const total = runtimeMetrics.totalRequests;
|
|
2137
|
+
const successful = runtimeMetrics.successfulRequests;
|
|
2138
|
+
const successRate = total > 0 ? ((successful / total) * 100).toFixed(1) : "0.0";
|
|
2139
|
+
const avgLatencyMs = successful > 0
|
|
2140
|
+
? Math.round(runtimeMetrics.cumulativeLatencyMs / successful)
|
|
2141
|
+
: 0;
|
|
2142
|
+
const liveSyncSnapshot = liveAccountSync?.getSnapshot();
|
|
2143
|
+
const guardianStats = refreshGuardian?.getStats();
|
|
2144
|
+
const sessionAffinityEntries = sessionAffinityStore?.size() ?? 0;
|
|
2145
|
+
const lastRequest = runtimeMetrics.lastRequestAt !== null
|
|
2146
|
+
? `${formatWaitTime(now - runtimeMetrics.lastRequestAt)} ago`
|
|
2147
|
+
: "never";
|
|
2148
|
+
const lines = [
|
|
2149
|
+
"Codex Plugin Metrics:",
|
|
2150
|
+
"",
|
|
2151
|
+
`Uptime: ${formatWaitTime(uptimeMs)}`,
|
|
2152
|
+
`Total upstream requests: ${total}`,
|
|
2153
|
+
`Successful responses: ${successful}`,
|
|
2154
|
+
`Failed responses: ${runtimeMetrics.failedRequests}`,
|
|
2155
|
+
`Success rate: ${successRate}%`,
|
|
2156
|
+
`Average successful latency: ${avgLatencyMs}ms`,
|
|
2157
|
+
`Rate-limited responses: ${runtimeMetrics.rateLimitedResponses}`,
|
|
2158
|
+
`Server errors (5xx): ${runtimeMetrics.serverErrors}`,
|
|
2159
|
+
`Network errors: ${runtimeMetrics.networkErrors}`,
|
|
2160
|
+
`User aborts: ${runtimeMetrics.userAborts}`,
|
|
2161
|
+
`Auth refresh failures: ${runtimeMetrics.authRefreshFailures}`,
|
|
2162
|
+
`Account rotations: ${runtimeMetrics.accountRotations}`,
|
|
2163
|
+
`Same-account retries: ${runtimeMetrics.sameAccountRetries}`,
|
|
2164
|
+
`Stream failover attempts: ${runtimeMetrics.streamFailoverAttempts}`,
|
|
2165
|
+
`Stream failover recoveries: ${runtimeMetrics.streamFailoverRecoveries}`,
|
|
2166
|
+
`Stream failover cross-account recoveries: ${runtimeMetrics.streamFailoverCrossAccountRecoveries}`,
|
|
2167
|
+
`Empty-response retries: ${runtimeMetrics.emptyResponseRetries}`,
|
|
2168
|
+
`Session affinity entries: ${sessionAffinityEntries}`,
|
|
2169
|
+
`Live sync: ${liveSyncSnapshot?.running ? "on" : "off"} (${liveSyncSnapshot?.reloadCount ?? 0} reloads)`,
|
|
2170
|
+
`Refresh guardian: ${guardianStats ? "on" : "off"} (${guardianStats?.refreshed ?? 0} refreshed, ${guardianStats?.failed ?? 0} failed)`,
|
|
2171
|
+
`Last upstream request: ${lastRequest}`,
|
|
2854
2172
|
];
|
|
2855
2173
|
if (runtimeMetrics.lastError) {
|
|
2856
|
-
|
|
2174
|
+
lines.push(`Last error: ${runtimeMetrics.lastError}`);
|
|
2857
2175
|
}
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2176
|
+
if (ui.v2Enabled) {
|
|
2177
|
+
const styled = [
|
|
2178
|
+
...formatUiHeader(ui, "Codex plugin metrics"),
|
|
2179
|
+
formatUiKeyValue(ui, "Uptime", formatWaitTime(uptimeMs)),
|
|
2180
|
+
formatUiKeyValue(ui, "Total upstream requests", String(total)),
|
|
2181
|
+
formatUiKeyValue(ui, "Successful responses", String(successful), "success"),
|
|
2182
|
+
formatUiKeyValue(ui, "Failed responses", String(runtimeMetrics.failedRequests), "danger"),
|
|
2183
|
+
formatUiKeyValue(ui, "Success rate", `${successRate}%`, "accent"),
|
|
2184
|
+
formatUiKeyValue(ui, "Average successful latency", `${avgLatencyMs}ms`),
|
|
2185
|
+
formatUiKeyValue(ui, "Rate-limited responses", String(runtimeMetrics.rateLimitedResponses), "warning"),
|
|
2186
|
+
formatUiKeyValue(ui, "Server errors (5xx)", String(runtimeMetrics.serverErrors), "danger"),
|
|
2187
|
+
formatUiKeyValue(ui, "Network errors", String(runtimeMetrics.networkErrors), "danger"),
|
|
2188
|
+
formatUiKeyValue(ui, "User aborts", String(runtimeMetrics.userAborts), "muted"),
|
|
2189
|
+
formatUiKeyValue(ui, "Auth refresh failures", String(runtimeMetrics.authRefreshFailures), "warning"),
|
|
2190
|
+
formatUiKeyValue(ui, "Account rotations", String(runtimeMetrics.accountRotations), "accent"),
|
|
2191
|
+
formatUiKeyValue(ui, "Same-account retries", String(runtimeMetrics.sameAccountRetries), "warning"),
|
|
2192
|
+
formatUiKeyValue(ui, "Stream failover attempts", String(runtimeMetrics.streamFailoverAttempts), "muted"),
|
|
2193
|
+
formatUiKeyValue(ui, "Stream failover recoveries", String(runtimeMetrics.streamFailoverRecoveries), "success"),
|
|
2194
|
+
formatUiKeyValue(ui, "Stream failover cross-account recoveries", String(runtimeMetrics.streamFailoverCrossAccountRecoveries), "accent"),
|
|
2195
|
+
formatUiKeyValue(ui, "Empty-response retries", String(runtimeMetrics.emptyResponseRetries), "warning"),
|
|
2196
|
+
formatUiKeyValue(ui, "Session affinity entries", String(sessionAffinityEntries), "muted"),
|
|
2197
|
+
formatUiKeyValue(ui, "Live sync", `${liveSyncSnapshot?.running ? "on" : "off"} (${liveSyncSnapshot?.reloadCount ?? 0} reloads)`, liveSyncSnapshot?.running ? "success" : "muted"),
|
|
2198
|
+
formatUiKeyValue(ui, "Refresh guardian", guardianStats
|
|
2199
|
+
? `on (${guardianStats.refreshed} refreshed, ${guardianStats.failed} failed)`
|
|
2200
|
+
: "off", guardianStats ? "success" : "muted"),
|
|
2201
|
+
formatUiKeyValue(ui, "Last upstream request", lastRequest, "muted"),
|
|
2202
|
+
];
|
|
2203
|
+
if (runtimeMetrics.lastError) {
|
|
2204
|
+
styled.push(formatUiKeyValue(ui, "Last error", runtimeMetrics.lastError, "danger"));
|
|
2205
|
+
}
|
|
2206
|
+
return Promise.resolve(styled.join("\n"));
|
|
2207
|
+
}
|
|
2208
|
+
return Promise.resolve(lines.join("\n"));
|
|
2209
|
+
},
|
|
2210
|
+
}),
|
|
2211
|
+
}
|
|
2212
|
+
: {}),
|
|
2864
2213
|
"codex-health": tool({
|
|
2865
2214
|
description: "Check health of all Codex accounts by validating refresh tokens.",
|
|
2866
2215
|
args: {},
|
|
2867
2216
|
async execute() {
|
|
2868
|
-
const ui =
|
|
2217
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2869
2218
|
const storage = await loadAccounts();
|
|
2870
2219
|
if (!storage || storage.accounts.length === 0) {
|
|
2871
2220
|
if (ui.v2Enabled) {
|
|
@@ -2917,255 +2266,268 @@ export const OpenAIOAuthPlugin = async ({ client }) => {
|
|
|
2917
2266
|
return results.join("\n");
|
|
2918
2267
|
},
|
|
2919
2268
|
}),
|
|
2920
|
-
...(exposeAdvancedCodexTools
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2269
|
+
...(exposeAdvancedCodexTools
|
|
2270
|
+
? {
|
|
2271
|
+
"codex-remove": tool({
|
|
2272
|
+
description: "Remove a Codex account by index (1-based). Use codex-list to list accounts first.",
|
|
2273
|
+
args: {
|
|
2274
|
+
index: tool.schema
|
|
2275
|
+
.number()
|
|
2276
|
+
.describe("Account number to remove (1-based, e.g., 1 for first account)"),
|
|
2277
|
+
},
|
|
2278
|
+
async execute({ index }) {
|
|
2279
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2280
|
+
const storage = await loadAccounts();
|
|
2281
|
+
if (!storage || storage.accounts.length === 0) {
|
|
2282
|
+
if (ui.v2Enabled) {
|
|
2283
|
+
return [
|
|
2284
|
+
...formatUiHeader(ui, "Remove account"),
|
|
2285
|
+
"",
|
|
2286
|
+
formatUiItem(ui, "No accounts configured.", "warning"),
|
|
2287
|
+
].join("\n");
|
|
2288
|
+
}
|
|
2289
|
+
return "No Codex accounts configured. Nothing to remove.";
|
|
2936
2290
|
}
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2291
|
+
const targetIndex = Math.floor((index ?? 0) - 1);
|
|
2292
|
+
if (!Number.isFinite(targetIndex) ||
|
|
2293
|
+
targetIndex < 0 ||
|
|
2294
|
+
targetIndex >= storage.accounts.length) {
|
|
2295
|
+
if (ui.v2Enabled) {
|
|
2296
|
+
return [
|
|
2297
|
+
...formatUiHeader(ui, "Remove account"),
|
|
2298
|
+
"",
|
|
2299
|
+
formatUiItem(ui, `Invalid account number: ${index}`, "danger"),
|
|
2300
|
+
formatUiKeyValue(ui, "Valid range", `1-${storage.accounts.length}`, "muted"),
|
|
2301
|
+
formatUiItem(ui, "Use codex-list to list all accounts.", "accent"),
|
|
2302
|
+
].join("\n");
|
|
2303
|
+
}
|
|
2304
|
+
return `Invalid account number: ${index}\n\nValid range: 1-${storage.accounts.length}\n\nUse codex-list to list all accounts.`;
|
|
2951
2305
|
}
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
if (!account) {
|
|
2956
|
-
return `Account ${index} not found.`;
|
|
2957
|
-
}
|
|
2958
|
-
const label = formatAccountLabel(account, targetIndex);
|
|
2959
|
-
storage.accounts.splice(targetIndex, 1);
|
|
2960
|
-
if (storage.accounts.length === 0) {
|
|
2961
|
-
storage.activeIndex = 0;
|
|
2962
|
-
storage.activeIndexByFamily = {};
|
|
2963
|
-
}
|
|
2964
|
-
else {
|
|
2965
|
-
if (storage.activeIndex >= storage.accounts.length) {
|
|
2966
|
-
storage.activeIndex = 0;
|
|
2306
|
+
const account = storage.accounts[targetIndex];
|
|
2307
|
+
if (!account) {
|
|
2308
|
+
return `Account ${index} not found.`;
|
|
2967
2309
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2310
|
+
const label = formatAccountLabel(account, targetIndex);
|
|
2311
|
+
storage.accounts.splice(targetIndex, 1);
|
|
2312
|
+
if (storage.accounts.length === 0) {
|
|
2313
|
+
storage.activeIndex = 0;
|
|
2314
|
+
storage.activeIndexByFamily = {};
|
|
2970
2315
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2316
|
+
else {
|
|
2317
|
+
if (storage.activeIndex >= storage.accounts.length) {
|
|
2318
|
+
storage.activeIndex = 0;
|
|
2319
|
+
}
|
|
2320
|
+
else if (storage.activeIndex > targetIndex) {
|
|
2321
|
+
storage.activeIndex -= 1;
|
|
2322
|
+
}
|
|
2323
|
+
if (storage.activeIndexByFamily) {
|
|
2324
|
+
for (const family of MODEL_FAMILIES) {
|
|
2325
|
+
const idx = storage.activeIndexByFamily[family];
|
|
2326
|
+
if (typeof idx === "number") {
|
|
2327
|
+
if (idx >= storage.accounts.length) {
|
|
2328
|
+
storage.activeIndexByFamily[family] = 0;
|
|
2329
|
+
}
|
|
2330
|
+
else if (idx > targetIndex) {
|
|
2331
|
+
storage.activeIndexByFamily[family] = idx - 1;
|
|
2332
|
+
}
|
|
2980
2333
|
}
|
|
2981
2334
|
}
|
|
2982
2335
|
}
|
|
2983
2336
|
}
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2337
|
+
try {
|
|
2338
|
+
await saveAccounts(storage);
|
|
2339
|
+
}
|
|
2340
|
+
catch (saveError) {
|
|
2341
|
+
logWarn("Failed to save account removal", {
|
|
2342
|
+
error: String(saveError),
|
|
2343
|
+
});
|
|
2344
|
+
if (ui.v2Enabled) {
|
|
2345
|
+
return [
|
|
2346
|
+
...formatUiHeader(ui, "Remove account"),
|
|
2347
|
+
"",
|
|
2348
|
+
formatUiItem(ui, `Removed ${formatAccountLabel(account, targetIndex)} from memory`, "warning"),
|
|
2349
|
+
formatUiItem(ui, "Failed to persist. Change may be lost on restart.", "danger"),
|
|
2350
|
+
].join("\n");
|
|
2351
|
+
}
|
|
2352
|
+
return `Removed ${formatAccountLabel(account, targetIndex)} from memory but failed to persist. Changes may be lost on restart.`;
|
|
2353
|
+
}
|
|
2354
|
+
if (cachedAccountManager) {
|
|
2355
|
+
await reloadRuntimeAccountManager(makeReloadAccountManagerDeps());
|
|
2356
|
+
}
|
|
2357
|
+
const remaining = storage.accounts.length;
|
|
2990
2358
|
if (ui.v2Enabled) {
|
|
2991
2359
|
return [
|
|
2992
2360
|
...formatUiHeader(ui, "Remove account"),
|
|
2993
2361
|
"",
|
|
2994
|
-
formatUiItem(ui,
|
|
2995
|
-
|
|
2362
|
+
formatUiItem(ui, `${getStatusMarker(ui, "ok")} Removed: ${label}`, "success"),
|
|
2363
|
+
remaining > 0
|
|
2364
|
+
? formatUiKeyValue(ui, "Remaining accounts", String(remaining))
|
|
2365
|
+
: formatUiItem(ui, "No accounts remaining. Run: codex login", "warning"),
|
|
2996
2366
|
].join("\n");
|
|
2997
2367
|
}
|
|
2998
|
-
return `Removed ${formatAccountLabel(account, targetIndex)} from memory but failed to persist. Changes may be lost on restart.`;
|
|
2999
|
-
}
|
|
3000
|
-
if (cachedAccountManager) {
|
|
3001
|
-
await reloadAccountManagerFromDisk();
|
|
3002
|
-
}
|
|
3003
|
-
const remaining = storage.accounts.length;
|
|
3004
|
-
if (ui.v2Enabled) {
|
|
3005
2368
|
return [
|
|
3006
|
-
|
|
2369
|
+
`Removed: ${label}`,
|
|
3007
2370
|
"",
|
|
3008
|
-
formatUiItem(ui, `${getStatusMarker(ui, "ok")} Removed: ${label}`, "success"),
|
|
3009
2371
|
remaining > 0
|
|
3010
|
-
?
|
|
3011
|
-
:
|
|
2372
|
+
? `Remaining accounts: ${remaining}`
|
|
2373
|
+
: "No accounts remaining. Run: codex login",
|
|
3012
2374
|
].join("\n");
|
|
3013
|
-
}
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
return [
|
|
3032
|
-
...formatUiHeader(ui, "Refresh accounts"),
|
|
3033
|
-
"",
|
|
3034
|
-
formatUiItem(ui, "No accounts configured.", "warning"),
|
|
3035
|
-
formatUiItem(ui, "Run: codex login", "accent"),
|
|
3036
|
-
].join("\n");
|
|
2375
|
+
},
|
|
2376
|
+
}),
|
|
2377
|
+
"codex-refresh": tool({
|
|
2378
|
+
description: "Manually refresh OAuth tokens for all accounts to verify they're still valid.",
|
|
2379
|
+
args: {},
|
|
2380
|
+
async execute() {
|
|
2381
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2382
|
+
const storage = await loadAccounts();
|
|
2383
|
+
if (!storage || storage.accounts.length === 0) {
|
|
2384
|
+
if (ui.v2Enabled) {
|
|
2385
|
+
return [
|
|
2386
|
+
...formatUiHeader(ui, "Refresh accounts"),
|
|
2387
|
+
"",
|
|
2388
|
+
formatUiItem(ui, "No accounts configured.", "warning"),
|
|
2389
|
+
formatUiItem(ui, "Run: codex login", "accent"),
|
|
2390
|
+
].join("\n");
|
|
2391
|
+
}
|
|
2392
|
+
return "No Codex accounts configured. Run: codex login";
|
|
3037
2393
|
}
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
2394
|
+
const results = ui.v2Enabled
|
|
2395
|
+
? []
|
|
2396
|
+
: [`Refreshing ${storage.accounts.length} account(s):`, ""];
|
|
2397
|
+
let refreshedCount = 0;
|
|
2398
|
+
let failedCount = 0;
|
|
2399
|
+
for (let i = 0; i < storage.accounts.length; i++) {
|
|
2400
|
+
const account = storage.accounts[i];
|
|
2401
|
+
if (!account)
|
|
2402
|
+
continue;
|
|
2403
|
+
const label = formatAccountLabel(account, i);
|
|
2404
|
+
try {
|
|
2405
|
+
const refreshResult = await queuedRefresh(account.refreshToken);
|
|
2406
|
+
if (refreshResult.type === "success") {
|
|
2407
|
+
account.refreshToken = refreshResult.refresh;
|
|
2408
|
+
account.accessToken = refreshResult.access;
|
|
2409
|
+
account.expiresAt = refreshResult.expires;
|
|
2410
|
+
results.push(` ${getStatusMarker(ui, "ok")} ${label}: Refreshed`);
|
|
2411
|
+
refreshedCount++;
|
|
2412
|
+
}
|
|
2413
|
+
else {
|
|
2414
|
+
results.push(` ${getStatusMarker(ui, "error")} ${label}: Failed - ${refreshResult.message ?? refreshResult.reason}`);
|
|
2415
|
+
failedCount++;
|
|
2416
|
+
}
|
|
3058
2417
|
}
|
|
3059
|
-
|
|
3060
|
-
|
|
2418
|
+
catch (error) {
|
|
2419
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2420
|
+
results.push(` ${getStatusMarker(ui, "error")} ${label}: Error - ${errorMsg.slice(0, 120)}`);
|
|
3061
2421
|
failedCount++;
|
|
3062
2422
|
}
|
|
3063
2423
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
failedCount++;
|
|
3068
|
-
}
|
|
3069
|
-
}
|
|
3070
|
-
await saveAccounts(storage);
|
|
3071
|
-
if (cachedAccountManager) {
|
|
3072
|
-
await reloadAccountManagerFromDisk();
|
|
3073
|
-
}
|
|
3074
|
-
results.push("");
|
|
3075
|
-
results.push(`Summary: ${refreshedCount} refreshed, ${failedCount} failed`);
|
|
3076
|
-
if (ui.v2Enabled) {
|
|
3077
|
-
return [
|
|
3078
|
-
...formatUiHeader(ui, "Refresh accounts"),
|
|
3079
|
-
"",
|
|
3080
|
-
...results.map((line) => paintUiText(ui, line, "normal")),
|
|
3081
|
-
].join("\n");
|
|
3082
|
-
}
|
|
3083
|
-
return results.join("\n");
|
|
3084
|
-
},
|
|
3085
|
-
}),
|
|
3086
|
-
"codex-export": tool({
|
|
3087
|
-
description: "Export accounts to a JSON file for backup or migration to another machine.",
|
|
3088
|
-
args: {
|
|
3089
|
-
path: tool.schema.string().describe("File path to export to (e.g., ~/codex-backup.json)"),
|
|
3090
|
-
force: tool.schema.boolean().optional().describe("Overwrite existing file (default: true)"),
|
|
3091
|
-
},
|
|
3092
|
-
async execute({ path: filePath, force }) {
|
|
3093
|
-
const ui = resolveUiRuntime();
|
|
3094
|
-
try {
|
|
3095
|
-
await exportAccounts(filePath, force ?? true);
|
|
3096
|
-
const storage = await loadAccounts();
|
|
3097
|
-
const count = storage?.accounts.length ?? 0;
|
|
3098
|
-
if (ui.v2Enabled) {
|
|
3099
|
-
return [
|
|
3100
|
-
...formatUiHeader(ui, "Export accounts"),
|
|
3101
|
-
"",
|
|
3102
|
-
formatUiItem(ui, `${getStatusMarker(ui, "ok")} Exported ${count} account(s)`, "success"),
|
|
3103
|
-
formatUiKeyValue(ui, "Path", filePath, "muted"),
|
|
3104
|
-
].join("\n");
|
|
2424
|
+
await saveAccounts(storage);
|
|
2425
|
+
if (cachedAccountManager) {
|
|
2426
|
+
await reloadRuntimeAccountManager(makeReloadAccountManagerDeps());
|
|
3105
2427
|
}
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
catch (error) {
|
|
3109
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
2428
|
+
results.push("");
|
|
2429
|
+
results.push(`Summary: ${refreshedCount} refreshed, ${failedCount} failed`);
|
|
3110
2430
|
if (ui.v2Enabled) {
|
|
3111
2431
|
return [
|
|
3112
|
-
...formatUiHeader(ui, "
|
|
2432
|
+
...formatUiHeader(ui, "Refresh accounts"),
|
|
3113
2433
|
"",
|
|
3114
|
-
|
|
3115
|
-
formatUiKeyValue(ui, "Error", msg, "danger"),
|
|
2434
|
+
...results.map((line) => paintUiText(ui, line, "normal")),
|
|
3116
2435
|
].join("\n");
|
|
3117
2436
|
}
|
|
3118
|
-
return
|
|
3119
|
-
}
|
|
3120
|
-
},
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
2437
|
+
return results.join("\n");
|
|
2438
|
+
},
|
|
2439
|
+
}),
|
|
2440
|
+
"codex-export": tool({
|
|
2441
|
+
description: "Export accounts to a JSON file for backup or migration to another machine.",
|
|
2442
|
+
args: {
|
|
2443
|
+
path: tool.schema
|
|
2444
|
+
.string()
|
|
2445
|
+
.describe("File path to export to (e.g., ~/codex-backup.json)"),
|
|
2446
|
+
force: tool.schema
|
|
2447
|
+
.boolean()
|
|
2448
|
+
.optional()
|
|
2449
|
+
.describe("Overwrite existing file (default: true)"),
|
|
2450
|
+
},
|
|
2451
|
+
async execute({ path: filePath, force }) {
|
|
2452
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2453
|
+
try {
|
|
2454
|
+
await exportAccounts(filePath, force ?? true);
|
|
2455
|
+
const storage = await loadAccounts();
|
|
2456
|
+
const count = storage?.accounts.length ?? 0;
|
|
2457
|
+
if (ui.v2Enabled) {
|
|
2458
|
+
return [
|
|
2459
|
+
...formatUiHeader(ui, "Export accounts"),
|
|
2460
|
+
"",
|
|
2461
|
+
formatUiItem(ui, `${getStatusMarker(ui, "ok")} Exported ${count} account(s)`, "success"),
|
|
2462
|
+
formatUiKeyValue(ui, "Path", filePath, "muted"),
|
|
2463
|
+
].join("\n");
|
|
2464
|
+
}
|
|
2465
|
+
return `Exported ${count} account(s) to: ${filePath}`;
|
|
3135
2466
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
2467
|
+
catch (error) {
|
|
2468
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2469
|
+
if (ui.v2Enabled) {
|
|
2470
|
+
return [
|
|
2471
|
+
...formatUiHeader(ui, "Export accounts"),
|
|
2472
|
+
"",
|
|
2473
|
+
formatUiItem(ui, `${getStatusMarker(ui, "error")} Export failed`, "danger"),
|
|
2474
|
+
formatUiKeyValue(ui, "Error", msg, "danger"),
|
|
2475
|
+
].join("\n");
|
|
2476
|
+
}
|
|
2477
|
+
return `Export failed: ${msg}`;
|
|
3138
2478
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
2479
|
+
},
|
|
2480
|
+
}),
|
|
2481
|
+
"codex-import": tool({
|
|
2482
|
+
description: "Import accounts from a JSON file, merging with existing accounts.",
|
|
2483
|
+
args: {
|
|
2484
|
+
path: tool.schema
|
|
2485
|
+
.string()
|
|
2486
|
+
.describe("File path to import from (e.g., ~/codex-backup.json)"),
|
|
2487
|
+
},
|
|
2488
|
+
async execute({ path: filePath }) {
|
|
2489
|
+
const ui = applyUiRuntimeFromConfig(loadPluginConfig(), setUiRuntimeOptions);
|
|
2490
|
+
try {
|
|
2491
|
+
const result = await importAccounts(filePath);
|
|
2492
|
+
invalidateRuntimeAccountManagerCache(accountManagerCacheInvalidationDeps);
|
|
2493
|
+
const lines = [`Import complete.`, ``];
|
|
2494
|
+
if (result.imported > 0) {
|
|
2495
|
+
lines.push(`New accounts: ${result.imported}`);
|
|
2496
|
+
}
|
|
2497
|
+
if (result.skipped > 0) {
|
|
2498
|
+
lines.push(`Duplicates skipped: ${result.skipped}`);
|
|
2499
|
+
}
|
|
2500
|
+
lines.push(`Total accounts: ${result.total}`);
|
|
2501
|
+
if (ui.v2Enabled) {
|
|
2502
|
+
const styled = [
|
|
2503
|
+
...formatUiHeader(ui, "Import accounts"),
|
|
2504
|
+
"",
|
|
2505
|
+
formatUiItem(ui, `${getStatusMarker(ui, "ok")} Import complete`, "success"),
|
|
2506
|
+
formatUiKeyValue(ui, "Path", filePath, "muted"),
|
|
2507
|
+
formatUiKeyValue(ui, "New accounts", String(result.imported), result.imported > 0 ? "success" : "muted"),
|
|
2508
|
+
formatUiKeyValue(ui, "Duplicates skipped", String(result.skipped), result.skipped > 0 ? "warning" : "muted"),
|
|
2509
|
+
formatUiKeyValue(ui, "Total accounts", String(result.total), "accent"),
|
|
2510
|
+
];
|
|
2511
|
+
return styled.join("\n");
|
|
2512
|
+
}
|
|
2513
|
+
return lines.join("\n");
|
|
3151
2514
|
}
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
2515
|
+
catch (error) {
|
|
2516
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2517
|
+
if (ui.v2Enabled) {
|
|
2518
|
+
return [
|
|
2519
|
+
...formatUiHeader(ui, "Import accounts"),
|
|
2520
|
+
"",
|
|
2521
|
+
formatUiItem(ui, `${getStatusMarker(ui, "error")} Import failed`, "danger"),
|
|
2522
|
+
formatUiKeyValue(ui, "Error", msg, "danger"),
|
|
2523
|
+
].join("\n");
|
|
2524
|
+
}
|
|
2525
|
+
return `Import failed: ${msg}`;
|
|
3163
2526
|
}
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
}),
|
|
3168
|
-
} : {}),
|
|
2527
|
+
},
|
|
2528
|
+
}),
|
|
2529
|
+
}
|
|
2530
|
+
: {}),
|
|
3169
2531
|
},
|
|
3170
2532
|
};
|
|
3171
2533
|
};
|