codex-multi-auth 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +929 -1712
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts.d.ts +10 -0
- package/dist/lib/accounts.d.ts.map +1 -1
- package/dist/lib/accounts.js +312 -69
- package/dist/lib/accounts.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/circuit-breaker.d.ts +4 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -1
- package/dist/lib/circuit-breaker.js +33 -1
- package/dist/lib/circuit-breaker.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/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 +71 -0
- package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/forecast.js +371 -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 +25 -0
- package/dist/lib/codex-manager/commands/report.d.ts.map +1 -0
- package/dist/lib/codex-manager/commands/report.js +399 -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/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/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/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/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/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/settings-hub-entry.d.ts +30 -0
- package/dist/lib/codex-manager/settings-hub-entry.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-entry.js +11 -0
- package/dist/lib/codex-manager/settings-hub-entry.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub-menu.d.ts +30 -0
- package/dist/lib/codex-manager/settings-hub-menu.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-menu.js +37 -0
- package/dist/lib/codex-manager/settings-hub-menu.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub-prompt.d.ts +25 -0
- package/dist/lib/codex-manager/settings-hub-prompt.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-hub-prompt.js +23 -0
- package/dist/lib/codex-manager/settings-hub-prompt.js.map +1 -0
- package/dist/lib/codex-manager/settings-hub.d.ts +10 -29
- package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
- package/dist/lib/codex-manager/settings-hub.js +260 -2168
- package/dist/lib/codex-manager/settings-hub.js.map +1 -1
- package/dist/lib/codex-manager/settings-panels.d.ts +56 -0
- package/dist/lib/codex-manager/settings-panels.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-panels.js +73 -0
- package/dist/lib/codex-manager/settings-panels.js.map +1 -0
- package/dist/lib/codex-manager/settings-persist-utils.d.ts +9 -0
- package/dist/lib/codex-manager/settings-persist-utils.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-persist-utils.js +33 -0
- package/dist/lib/codex-manager/settings-persist-utils.js.map +1 -0
- package/dist/lib/codex-manager/settings-preview.d.ts +12 -0
- package/dist/lib/codex-manager/settings-preview.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-preview.js +118 -0
- package/dist/lib/codex-manager/settings-preview.js.map +1 -0
- package/dist/lib/codex-manager/settings-write-queue.d.ts +14 -0
- package/dist/lib/codex-manager/settings-write-queue.d.ts.map +1 -0
- package/dist/lib/codex-manager/settings-write-queue.js +88 -0
- package/dist/lib/codex-manager/settings-write-queue.js.map +1 -0
- package/dist/lib/codex-manager/statusline-order.d.ts +3 -0
- package/dist/lib/codex-manager/statusline-order.d.ts.map +1 -0
- package/dist/lib/codex-manager/statusline-order.js +17 -0
- package/dist/lib/codex-manager/statusline-order.js.map +1 -0
- package/dist/lib/codex-manager/statusline-settings-panel.d.ts +40 -0
- package/dist/lib/codex-manager/statusline-settings-panel.d.ts.map +1 -0
- package/dist/lib/codex-manager/statusline-settings-panel.js +167 -0
- package/dist/lib/codex-manager/statusline-settings-panel.js.map +1 -0
- package/dist/lib/codex-manager/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.d.ts.map +1 -1
- package/dist/lib/codex-manager.js +441 -2199
- package/dist/lib/codex-manager.js.map +1 -1
- package/dist/lib/config.d.ts +38 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +371 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +13 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/forecast.d.ts +17 -0
- package/dist/lib/forecast.d.ts.map +1 -1
- package/dist/lib/forecast.js +33 -6
- package/dist/lib/forecast.js.map +1 -1
- package/dist/lib/health.d.ts +1 -0
- package/dist/lib/health.d.ts.map +1 -1
- package/dist/lib/health.js +6 -2
- package/dist/lib/health.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/parallel-probe.d.ts +1 -1
- package/dist/lib/parallel-probe.d.ts.map +1 -1
- package/dist/lib/parallel-probe.js +5 -2
- package/dist/lib/parallel-probe.js.map +1 -1
- package/dist/lib/proactive-refresh.d.ts +1 -1
- package/dist/lib/proactive-refresh.d.ts.map +1 -1
- package/dist/lib/proactive-refresh.js +14 -5
- package/dist/lib/proactive-refresh.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/refresh-guardian.d.ts +3 -0
- package/dist/lib/refresh-guardian.d.ts.map +1 -1
- package/dist/lib/refresh-guardian.js +142 -52
- package/dist/lib/refresh-guardian.js.map +1 -1
- package/dist/lib/request/failover-config.d.ts +4 -0
- package/dist/lib/request/failover-config.d.ts.map +1 -0
- package/dist/lib/request/failover-config.js +15 -0
- package/dist/lib/request/failover-config.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +10 -4
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +77 -21
- 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/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/rotation.d.ts +14 -11
- package/dist/lib/rotation.d.ts.map +1 -1
- package/dist/lib/rotation.js +36 -30
- package/dist/lib/rotation.js.map +1 -1
- package/dist/lib/runtime/account-check-helpers.d.ts +8 -0
- package/dist/lib/runtime/account-check-helpers.d.ts.map +1 -0
- package/dist/lib/runtime/account-check-helpers.js +30 -0
- package/dist/lib/runtime/account-check-helpers.js.map +1 -0
- package/dist/lib/runtime/account-check-types.d.ts +18 -0
- package/dist/lib/runtime/account-check-types.d.ts.map +1 -0
- package/dist/lib/runtime/account-check-types.js +12 -0
- package/dist/lib/runtime/account-check-types.js.map +1 -0
- package/dist/lib/runtime/account-check.d.ts +48 -0
- package/dist/lib/runtime/account-check.d.ts.map +1 -0
- package/dist/lib/runtime/account-check.js +202 -0
- package/dist/lib/runtime/account-check.js.map +1 -0
- package/dist/lib/runtime/account-health-check.d.ts +8 -0
- package/dist/lib/runtime/account-health-check.d.ts.map +1 -0
- package/dist/lib/runtime/account-health-check.js +30 -0
- package/dist/lib/runtime/account-health-check.js.map +1 -0
- package/dist/lib/runtime/account-manager-cache-entry.d.ts +25 -0
- package/dist/lib/runtime/account-manager-cache-entry.d.ts.map +1 -0
- package/dist/lib/runtime/account-manager-cache-entry.js +17 -0
- package/dist/lib/runtime/account-manager-cache-entry.js.map +1 -0
- package/dist/lib/runtime/account-manager-cache.d.ts +25 -0
- package/dist/lib/runtime/account-manager-cache.d.ts.map +1 -0
- package/dist/lib/runtime/account-manager-cache.js +44 -0
- package/dist/lib/runtime/account-manager-cache.js.map +1 -0
- package/dist/lib/runtime/account-pool.d.ts +23 -0
- package/dist/lib/runtime/account-pool.d.ts.map +1 -0
- package/dist/lib/runtime/account-pool.js +138 -0
- package/dist/lib/runtime/account-pool.js.map +1 -0
- package/dist/lib/runtime/account-scope.d.ts +10 -0
- package/dist/lib/runtime/account-scope.d.ts.map +1 -0
- package/dist/lib/runtime/account-scope.js +13 -0
- package/dist/lib/runtime/account-scope.js.map +1 -0
- package/dist/lib/runtime/account-select-event.d.ts +19 -0
- package/dist/lib/runtime/account-select-event.d.ts.map +1 -0
- package/dist/lib/runtime/account-select-event.js +53 -0
- package/dist/lib/runtime/account-select-event.js.map +1 -0
- package/dist/lib/runtime/account-selection.d.ts +28 -0
- package/dist/lib/runtime/account-selection.d.ts.map +1 -0
- package/dist/lib/runtime/account-selection.js +46 -0
- package/dist/lib/runtime/account-selection.js.map +1 -0
- package/dist/lib/runtime/account-state.d.ts +13 -0
- package/dist/lib/runtime/account-state.d.ts.map +1 -0
- package/dist/lib/runtime/account-state.js +38 -0
- package/dist/lib/runtime/account-state.js.map +1 -0
- package/dist/lib/runtime/account-status.d.ts +13 -0
- package/dist/lib/runtime/account-status.d.ts.map +1 -0
- package/dist/lib/runtime/account-status.js +37 -0
- package/dist/lib/runtime/account-status.js.map +1 -0
- package/dist/lib/runtime/account-storage-scope-entry.d.ts +26 -0
- package/dist/lib/runtime/account-storage-scope-entry.d.ts.map +1 -0
- package/dist/lib/runtime/account-storage-scope-entry.js +15 -0
- package/dist/lib/runtime/account-storage-scope-entry.js.map +1 -0
- package/dist/lib/runtime/auth-facade.d.ts +51 -0
- package/dist/lib/runtime/auth-facade.d.ts.map +1 -0
- package/dist/lib/runtime/auth-facade.js +48 -0
- package/dist/lib/runtime/auth-facade.js.map +1 -0
- package/dist/lib/runtime/browser-oauth-flow.d.ts +32 -0
- package/dist/lib/runtime/browser-oauth-flow.d.ts.map +1 -0
- package/dist/lib/runtime/browser-oauth-flow.js +31 -0
- package/dist/lib/runtime/browser-oauth-flow.js.map +1 -0
- package/dist/lib/runtime/capability-boost.d.ts +25 -0
- package/dist/lib/runtime/capability-boost.d.ts.map +1 -0
- package/dist/lib/runtime/capability-boost.js +26 -0
- package/dist/lib/runtime/capability-boost.js.map +1 -0
- package/dist/lib/runtime/event-handler.d.ts +53 -0
- package/dist/lib/runtime/event-handler.d.ts.map +1 -0
- package/dist/lib/runtime/event-handler.js +46 -0
- package/dist/lib/runtime/event-handler.js.map +1 -0
- package/dist/lib/runtime/flagged-verify-types.d.ts +9 -0
- package/dist/lib/runtime/flagged-verify-types.d.ts.map +1 -0
- package/dist/lib/runtime/flagged-verify-types.js +7 -0
- package/dist/lib/runtime/flagged-verify-types.js.map +1 -0
- package/dist/lib/runtime/hydrate-emails.d.ts +13 -0
- package/dist/lib/runtime/hydrate-emails.d.ts.map +1 -0
- package/dist/lib/runtime/hydrate-emails.js +56 -0
- package/dist/lib/runtime/hydrate-emails.js.map +1 -0
- package/dist/lib/runtime/live-sync-entry.d.ts +36 -0
- package/dist/lib/runtime/live-sync-entry.d.ts.map +1 -0
- package/dist/lib/runtime/live-sync-entry.js +14 -0
- package/dist/lib/runtime/live-sync-entry.js.map +1 -0
- package/dist/lib/runtime/live-sync.d.ts +35 -0
- package/dist/lib/runtime/live-sync.d.ts.map +1 -0
- package/dist/lib/runtime/live-sync.js +65 -0
- package/dist/lib/runtime/live-sync.js.map +1 -0
- package/dist/lib/runtime/loader-setup.d.ts +9 -0
- package/dist/lib/runtime/loader-setup.d.ts.map +1 -0
- package/dist/lib/runtime/loader-setup.js +8 -0
- package/dist/lib/runtime/loader-setup.js.map +1 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts +29 -0
- package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -0
- package/dist/lib/runtime/login-menu-accounts.js +33 -0
- package/dist/lib/runtime/login-menu-accounts.js.map +1 -0
- package/dist/lib/runtime/manual-oauth-flow.d.ts +40 -0
- package/dist/lib/runtime/manual-oauth-flow.d.ts.map +1 -0
- package/dist/lib/runtime/manual-oauth-flow.js +71 -0
- package/dist/lib/runtime/manual-oauth-flow.js.map +1 -0
- package/dist/lib/runtime/metrics.d.ts +29 -0
- package/dist/lib/runtime/metrics.d.ts.map +1 -0
- package/dist/lib/runtime/metrics.js +97 -0
- package/dist/lib/runtime/metrics.js.map +1 -0
- package/dist/lib/runtime/oauth-browser-flow.d.ts +9 -0
- package/dist/lib/runtime/oauth-browser-flow.d.ts.map +1 -0
- package/dist/lib/runtime/oauth-browser-flow.js +34 -0
- package/dist/lib/runtime/oauth-browser-flow.js.map +1 -0
- package/dist/lib/runtime/preemptive-quota.d.ts +13 -0
- package/dist/lib/runtime/preemptive-quota.d.ts.map +1 -0
- package/dist/lib/runtime/preemptive-quota.js +9 -0
- package/dist/lib/runtime/preemptive-quota.js.map +1 -0
- package/dist/lib/runtime/quota-headers.d.ts +12 -0
- package/dist/lib/runtime/quota-headers.d.ts.map +1 -0
- package/dist/lib/runtime/quota-headers.js +131 -0
- package/dist/lib/runtime/quota-headers.js.map +1 -0
- package/dist/lib/runtime/quota-probe.d.ts +18 -0
- package/dist/lib/runtime/quota-probe.d.ts.map +1 -0
- package/dist/lib/runtime/quota-probe.js +82 -0
- package/dist/lib/runtime/quota-probe.js.map +1 -0
- package/dist/lib/runtime/quota-settings.d.ts +14 -0
- package/dist/lib/runtime/quota-settings.d.ts.map +1 -0
- package/dist/lib/runtime/quota-settings.js +12 -0
- package/dist/lib/runtime/quota-settings.js.map +1 -0
- package/dist/lib/runtime/refresh-guardian-entry.d.ts +32 -0
- package/dist/lib/runtime/refresh-guardian-entry.d.ts.map +1 -0
- package/dist/lib/runtime/refresh-guardian-entry.js +12 -0
- package/dist/lib/runtime/refresh-guardian-entry.js.map +1 -0
- package/dist/lib/runtime/refresh-guardian.d.ts +24 -0
- package/dist/lib/runtime/refresh-guardian.d.ts.map +1 -0
- package/dist/lib/runtime/refresh-guardian.js +32 -0
- package/dist/lib/runtime/refresh-guardian.js.map +1 -0
- package/dist/lib/runtime/request-init.d.ts +5 -0
- package/dist/lib/runtime/request-init.d.ts.map +1 -0
- package/dist/lib/runtime/request-init.js +50 -0
- package/dist/lib/runtime/request-init.js.map +1 -0
- package/dist/lib/runtime/runtime-services.d.ts +58 -0
- package/dist/lib/runtime/runtime-services.d.ts.map +1 -0
- package/dist/lib/runtime/runtime-services.js +87 -0
- package/dist/lib/runtime/runtime-services.js.map +1 -0
- package/dist/lib/runtime/session-affinity-entry.d.ts +30 -0
- package/dist/lib/runtime/session-affinity-entry.d.ts.map +1 -0
- package/dist/lib/runtime/session-affinity-entry.js +11 -0
- package/dist/lib/runtime/session-affinity-entry.js.map +1 -0
- package/dist/lib/runtime/session-affinity.d.ts +13 -0
- package/dist/lib/runtime/session-affinity.d.ts.map +1 -0
- package/dist/lib/runtime/session-affinity.js +17 -0
- package/dist/lib/runtime/session-affinity.js.map +1 -0
- package/dist/lib/runtime/session-recovery.d.ts +8 -0
- package/dist/lib/runtime/session-recovery.d.ts.map +1 -0
- package/dist/lib/runtime/session-recovery.js +7 -0
- package/dist/lib/runtime/session-recovery.js.map +1 -0
- package/dist/lib/runtime/status-marker.d.ts +3 -0
- package/dist/lib/runtime/status-marker.d.ts.map +1 -0
- package/dist/lib/runtime/status-marker.js +15 -0
- package/dist/lib/runtime/status-marker.js.map +1 -0
- package/dist/lib/runtime/storage-scope.d.ts +13 -0
- package/dist/lib/runtime/storage-scope.d.ts.map +1 -0
- package/dist/lib/runtime/storage-scope.js +14 -0
- package/dist/lib/runtime/storage-scope.js.map +1 -0
- package/dist/lib/runtime/toast.d.ts +16 -0
- package/dist/lib/runtime/toast.d.ts.map +1 -0
- package/dist/lib/runtime/toast.js +16 -0
- package/dist/lib/runtime/toast.js.map +1 -0
- package/dist/lib/runtime/ui-runtime-entry.d.ts +7 -0
- package/dist/lib/runtime/ui-runtime-entry.d.ts.map +1 -0
- package/dist/lib/runtime/ui-runtime-entry.js +4 -0
- package/dist/lib/runtime/ui-runtime-entry.js.map +1 -0
- package/dist/lib/runtime/ui-runtime.d.ts +9 -0
- package/dist/lib/runtime/ui-runtime.d.ts.map +1 -0
- package/dist/lib/runtime/ui-runtime.js +23 -0
- package/dist/lib/runtime/ui-runtime.js.map +1 -0
- package/dist/lib/runtime/verify-flagged.d.ts +44 -0
- package/dist/lib/runtime/verify-flagged.d.ts.map +1 -0
- package/dist/lib/runtime/verify-flagged.js +84 -0
- package/dist/lib/runtime/verify-flagged.js.map +1 -0
- package/dist/lib/schemas.d.ts +2 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +2 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/session-affinity.d.ts +10 -0
- package/dist/lib/session-affinity.d.ts.map +1 -1
- package/dist/lib/session-affinity.js +53 -6
- package/dist/lib/session-affinity.js.map +1 -1
- package/dist/lib/storage/account-clear-entry.d.ts +16 -0
- package/dist/lib/storage/account-clear-entry.d.ts.map +1 -0
- package/dist/lib/storage/account-clear-entry.js +12 -0
- package/dist/lib/storage/account-clear-entry.js.map +1 -0
- package/dist/lib/storage/account-clear.d.ts +8 -0
- package/dist/lib/storage/account-clear.d.ts.map +1 -0
- package/dist/lib/storage/account-clear.js +29 -0
- package/dist/lib/storage/account-clear.js.map +1 -0
- package/dist/lib/storage/account-match-utils.d.ts +9 -0
- package/dist/lib/storage/account-match-utils.d.ts.map +1 -0
- package/dist/lib/storage/account-match-utils.js +45 -0
- package/dist/lib/storage/account-match-utils.js.map +1 -0
- package/dist/lib/storage/account-persistence.d.ts +3 -0
- package/dist/lib/storage/account-persistence.d.ts.map +1 -0
- package/dist/lib/storage/account-persistence.js +12 -0
- package/dist/lib/storage/account-persistence.js.map +1 -0
- package/dist/lib/storage/account-port.d.ts +50 -0
- package/dist/lib/storage/account-port.d.ts.map +1 -0
- package/dist/lib/storage/account-port.js +43 -0
- package/dist/lib/storage/account-port.js.map +1 -0
- package/dist/lib/storage/account-save-entry.d.ts +7 -0
- package/dist/lib/storage/account-save-entry.d.ts.map +1 -0
- package/dist/lib/storage/account-save-entry.js +6 -0
- package/dist/lib/storage/account-save-entry.js.map +1 -0
- package/dist/lib/storage/account-save.d.ts +30 -0
- package/dist/lib/storage/account-save.d.ts.map +1 -0
- package/dist/lib/storage/account-save.js +49 -0
- package/dist/lib/storage/account-save.js.map +1 -0
- package/dist/lib/storage/account-snapshot.d.ts +25 -0
- package/dist/lib/storage/account-snapshot.d.ts.map +1 -0
- package/dist/lib/storage/account-snapshot.js +78 -0
- package/dist/lib/storage/account-snapshot.js.map +1 -0
- package/dist/lib/storage/backup-metadata-builder.d.ts +32 -0
- package/dist/lib/storage/backup-metadata-builder.d.ts.map +1 -0
- package/dist/lib/storage/backup-metadata-builder.js +77 -0
- package/dist/lib/storage/backup-metadata-builder.js.map +1 -0
- package/dist/lib/storage/backup-metadata.d.ts +29 -0
- package/dist/lib/storage/backup-metadata.d.ts.map +1 -0
- package/dist/lib/storage/backup-metadata.js +16 -0
- package/dist/lib/storage/backup-metadata.js.map +1 -0
- package/dist/lib/storage/backup-paths.d.ts +10 -0
- package/dist/lib/storage/backup-paths.d.ts.map +1 -0
- package/dist/lib/storage/backup-paths.js +27 -0
- package/dist/lib/storage/backup-paths.js.map +1 -0
- package/dist/lib/storage/backup-restore.d.ts +11 -0
- package/dist/lib/storage/backup-restore.d.ts.map +1 -0
- package/dist/lib/storage/backup-restore.js +52 -0
- package/dist/lib/storage/backup-restore.js.map +1 -0
- package/dist/lib/storage/cache-artifacts.d.ts +2 -0
- package/dist/lib/storage/cache-artifacts.d.ts.map +1 -0
- package/dist/lib/storage/cache-artifacts.js +4 -0
- package/dist/lib/storage/cache-artifacts.js.map +1 -0
- package/dist/lib/storage/error-hints.d.ts +10 -0
- package/dist/lib/storage/error-hints.d.ts.map +1 -0
- package/dist/lib/storage/error-hints.js +36 -0
- package/dist/lib/storage/error-hints.js.map +1 -0
- package/dist/lib/storage/file-paths.d.ts +6 -0
- package/dist/lib/storage/file-paths.d.ts.map +1 -0
- package/dist/lib/storage/file-paths.js +17 -0
- package/dist/lib/storage/file-paths.js.map +1 -0
- package/dist/lib/storage/fixture-guards.d.ts +4 -0
- package/dist/lib/storage/fixture-guards.d.ts.map +1 -0
- package/dist/lib/storage/fixture-guards.js +27 -0
- package/dist/lib/storage/fixture-guards.js.map +1 -0
- package/dist/lib/storage/flagged-entry.d.ts +15 -0
- package/dist/lib/storage/flagged-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-entry.js +12 -0
- package/dist/lib/storage/flagged-entry.js.map +1 -0
- package/dist/lib/storage/flagged-load-entry.d.ts +20 -0
- package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-load-entry.js +13 -0
- package/dist/lib/storage/flagged-load-entry.js.map +1 -0
- package/dist/lib/storage/flagged-save-entry.d.ts +7 -0
- package/dist/lib/storage/flagged-save-entry.d.ts.map +1 -0
- package/dist/lib/storage/flagged-save-entry.js +6 -0
- package/dist/lib/storage/flagged-save-entry.js.map +1 -0
- package/dist/lib/storage/flagged-storage-file.d.ts +7 -0
- package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage-file.js +25 -0
- package/dist/lib/storage/flagged-storage-file.js.map +1 -0
- package/dist/lib/storage/flagged-storage-io.d.ts +28 -0
- package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage-io.js +135 -0
- package/dist/lib/storage/flagged-storage-io.js.map +1 -0
- package/dist/lib/storage/flagged-storage.d.ts +6 -0
- package/dist/lib/storage/flagged-storage.d.ts.map +1 -0
- package/dist/lib/storage/flagged-storage.js +90 -0
- package/dist/lib/storage/flagged-storage.js.map +1 -0
- package/dist/lib/storage/gitignore.d.ts +7 -0
- package/dist/lib/storage/gitignore.d.ts.map +1 -0
- package/dist/lib/storage/gitignore.js +34 -0
- package/dist/lib/storage/gitignore.js.map +1 -0
- package/dist/lib/storage/hash.d.ts +2 -0
- package/dist/lib/storage/hash.d.ts.map +1 -0
- package/dist/lib/storage/hash.js +5 -0
- package/dist/lib/storage/hash.js.map +1 -0
- package/dist/lib/storage/identity.d.ts +21 -0
- package/dist/lib/storage/identity.d.ts.map +1 -0
- package/dist/lib/storage/identity.js +59 -0
- package/dist/lib/storage/identity.js.map +1 -0
- package/dist/lib/storage/import-export.d.ts +24 -0
- package/dist/lib/storage/import-export.d.ts.map +1 -0
- package/dist/lib/storage/import-export.js +108 -0
- package/dist/lib/storage/import-export.js.map +1 -0
- package/dist/lib/storage/metadata-section.d.ts +16 -0
- package/dist/lib/storage/metadata-section.d.ts.map +1 -0
- package/dist/lib/storage/metadata-section.js +16 -0
- package/dist/lib/storage/metadata-section.js.map +1 -0
- package/dist/lib/storage/named-backup-entry.d.ts +15 -0
- package/dist/lib/storage/named-backup-entry.d.ts.map +1 -0
- package/dist/lib/storage/named-backup-entry.js +7 -0
- package/dist/lib/storage/named-backup-entry.js.map +1 -0
- package/dist/lib/storage/named-backups-entry.d.ts +19 -0
- package/dist/lib/storage/named-backups-entry.d.ts.map +1 -0
- package/dist/lib/storage/named-backups-entry.js +7 -0
- package/dist/lib/storage/named-backups-entry.js.map +1 -0
- package/dist/lib/storage/named-backups.d.ts +15 -0
- package/dist/lib/storage/named-backups.d.ts.map +1 -0
- package/dist/lib/storage/named-backups.js +68 -0
- package/dist/lib/storage/named-backups.js.map +1 -0
- package/dist/lib/storage/path-state.d.ts +9 -0
- package/dist/lib/storage/path-state.d.ts.map +1 -0
- package/dist/lib/storage/path-state.js +20 -0
- package/dist/lib/storage/path-state.js.map +1 -0
- package/dist/lib/storage/project-migration.d.ts +10 -0
- package/dist/lib/storage/project-migration.d.ts.map +1 -0
- package/dist/lib/storage/project-migration.js +38 -0
- package/dist/lib/storage/project-migration.js.map +1 -0
- package/dist/lib/storage/record-utils.d.ts +3 -0
- package/dist/lib/storage/record-utils.d.ts.map +1 -0
- package/dist/lib/storage/record-utils.js +9 -0
- package/dist/lib/storage/record-utils.js.map +1 -0
- package/dist/lib/storage/restore-assessment.d.ts +7 -0
- package/dist/lib/storage/restore-assessment.d.ts.map +1 -0
- package/dist/lib/storage/restore-assessment.js +42 -0
- package/dist/lib/storage/restore-assessment.js.map +1 -0
- package/dist/lib/storage/restore-backup-entry.d.ts +24 -0
- package/dist/lib/storage/restore-backup-entry.d.ts.map +1 -0
- package/dist/lib/storage/restore-backup-entry.js +10 -0
- package/dist/lib/storage/restore-backup-entry.js.map +1 -0
- package/dist/lib/storage/restore-metadata.d.ts +10 -0
- package/dist/lib/storage/restore-metadata.d.ts.map +1 -0
- package/dist/lib/storage/restore-metadata.js +18 -0
- package/dist/lib/storage/restore-metadata.js.map +1 -0
- package/dist/lib/storage/restore.d.ts +14 -0
- package/dist/lib/storage/restore.d.ts.map +1 -0
- package/dist/lib/storage/restore.js +53 -0
- package/dist/lib/storage/restore.js.map +1 -0
- package/dist/lib/storage/snapshot-inspectors.d.ts +22 -0
- package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -0
- package/dist/lib/storage/snapshot-inspectors.js +95 -0
- package/dist/lib/storage/snapshot-inspectors.js.map +1 -0
- package/dist/lib/storage/storage-parser.d.ts +15 -0
- package/dist/lib/storage/storage-parser.d.ts.map +1 -0
- package/dist/lib/storage/storage-parser.js +16 -0
- package/dist/lib/storage/storage-parser.js.map +1 -0
- package/dist/lib/storage/transactions.d.ts +23 -0
- package/dist/lib/storage/transactions.d.ts.map +1 -0
- package/dist/lib/storage/transactions.js +64 -0
- package/dist/lib/storage/transactions.js.map +1 -0
- package/dist/lib/storage.d.ts +8 -47
- package/dist/lib/storage.d.ts.map +1 -1
- package/dist/lib/storage.js +482 -1044
- package/dist/lib/storage.js.map +1 -1
- package/dist/lib/types.d.ts +80 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +41 -4
- package/scripts/check-pack-budget-lib.js +114 -0
- package/scripts/check-pack-budget.mjs +10 -0
- package/scripts/codex-multi-auth.js +34 -10
- package/scripts/install-codex-auth-utils.js +54 -14
- package/scripts/install-codex-auth.js +62 -33
- package/scripts/update-vendor-provenance.mjs +96 -0
- package/scripts/verify-vendor-provenance.mjs +53 -0
package/dist/lib/storage.js
CHANGED
|
@@ -1,233 +1,81 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
1
|
import { existsSync, promises as fs } from "node:fs";
|
|
4
|
-
import { basename, dirname,
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
5
3
|
import { ACCOUNT_LIMITS } from "./constants.js";
|
|
4
|
+
import { StorageError } from "./errors.js";
|
|
6
5
|
import { createLogger } from "./logger.js";
|
|
7
6
|
import { exportNamedBackupFile, getNamedBackupRoot, resolveNamedBackupPath, } from "./named-backup-export.js";
|
|
8
7
|
import { MODEL_FAMILIES } from "./prompts/codex.js";
|
|
9
|
-
import {
|
|
8
|
+
import { clearAccountStorageArtifacts } from "./storage/account-clear.js";
|
|
9
|
+
import { clearAccountsEntry } from "./storage/account-clear-entry.js";
|
|
10
|
+
import { collectDistinctIdentityValues, findNewestMatchingIndex as findNewestMatchingIndexByRef, selectNewestAccount, } from "./storage/account-match-utils.js";
|
|
11
|
+
import { cloneAccountStorageForPersistence } from "./storage/account-persistence.js";
|
|
12
|
+
import { describeAccountSnapshot as describeAccountSnapshotWithDeps, statSnapshot as statSnapshotWithDeps, } from "./storage/account-snapshot.js";
|
|
13
|
+
import { exportAccountsSnapshot, importAccountsSnapshot, } from "./storage/account-port.js";
|
|
14
|
+
import { saveAccountsToDisk } from "./storage/account-save.js";
|
|
15
|
+
import { saveAccountsEntry } from "./storage/account-save-entry.js";
|
|
16
|
+
import { buildBackupMetadata } from "./storage/backup-metadata-builder.js";
|
|
17
|
+
import { buildMetadataSection, } from "./storage/backup-metadata.js";
|
|
18
|
+
import { isCacheLikeBackupArtifactName } from "./storage/cache-artifacts.js";
|
|
19
|
+
import { ACCOUNTS_BACKUP_SUFFIX, ACCOUNTS_WAL_SUFFIX, getAccountsBackupPath, getAccountsBackupRecoveryCandidates, getAccountsWalPath, getIntentionalResetMarkerPath, RESET_MARKER_SUFFIX, } from "./storage/backup-paths.js";
|
|
20
|
+
import { restoreAccountsFromBackupPath } from "./storage/backup-restore.js";
|
|
21
|
+
import { looksLikeSyntheticFixtureStorage } from "./storage/fixture-guards.js";
|
|
22
|
+
import { loadFlaggedAccountsFromFile } from "./storage/flagged-storage-file.js";
|
|
23
|
+
import { clearFlaggedAccountsEntry } from "./storage/flagged-entry.js";
|
|
24
|
+
import { loadFlaggedAccountsEntry } from "./storage/flagged-load-entry.js";
|
|
25
|
+
import { saveFlaggedAccountsEntry } from "./storage/flagged-save-entry.js";
|
|
26
|
+
import { normalizeFlaggedStorage } from "./storage/flagged-storage.js";
|
|
27
|
+
import { clearFlaggedAccountsOnDisk, loadFlaggedAccountsState, saveFlaggedAccountsUnlockedToDisk, } from "./storage/flagged-storage-io.js";
|
|
28
|
+
import { computeSha256 } from "./storage/hash.js";
|
|
29
|
+
import { ensureCodexGitignoreEntry } from "./storage/gitignore.js";
|
|
30
|
+
import { exportAccountsToFile, mergeImportedAccounts, readImportFile, } from "./storage/import-export.js";
|
|
31
|
+
import { formatStorageErrorHint } from "./storage/error-hints.js";
|
|
32
|
+
export { StorageError } from "./errors.js";
|
|
33
|
+
export { formatStorageErrorHint, toStorageError } from "./storage/error-hints.js";
|
|
34
|
+
export { getAccountIdentityKey, normalizeEmailKey, } from "./storage/identity.js";
|
|
35
|
+
import { toAccountIdentityRef, } from "./storage/identity.js";
|
|
36
|
+
import { getFlaggedAccountsPath as buildFlaggedAccountsPath, getLegacyFlaggedAccountsPath as buildLegacyFlaggedAccountsPath, } from "./storage/file-paths.js";
|
|
10
37
|
import { migrateV1ToV3, } from "./storage/migrations.js";
|
|
38
|
+
import { exportNamedBackupEntry } from "./storage/named-backup-entry.js";
|
|
39
|
+
import { collectNamedBackups, } from "./storage/named-backups.js";
|
|
40
|
+
import { getNamedBackupsEntry } from "./storage/named-backups-entry.js";
|
|
41
|
+
import { getStoragePathState, setStoragePathState, } from "./storage/path-state.js";
|
|
11
42
|
import { findProjectRoot, getConfigDir, getProjectConfigDir, getProjectGlobalConfigDir, resolvePath, resolveProjectStorageIdentityRoot, } from "./storage/paths.js";
|
|
43
|
+
import { loadNormalizedStorageFromPath, mergeStorageForMigration, } from "./storage/project-migration.js";
|
|
44
|
+
import { clampIndex, isRecord } from "./storage/record-utils.js";
|
|
45
|
+
import { buildRestoreAssessment } from "./storage/restore-assessment.js";
|
|
46
|
+
import { restoreAccountsFromBackupEntry } from "./storage/restore-backup-entry.js";
|
|
47
|
+
import { createEmptyStorageWithRestoreMetadata as createEmptyStorageWithMetadata, withRestoreMetadata, } from "./storage/restore-metadata.js";
|
|
48
|
+
import { loadAccountsFromPath, parseAndNormalizeStorage, } from "./storage/storage-parser.js";
|
|
49
|
+
import { getTransactionSnapshotState, runInTransactionSnapshotContext, withAccountStorageTransaction as runWithAccountStorageTransaction, withStorageLock, } from "./storage/transactions.js";
|
|
12
50
|
const log = createLogger("storage");
|
|
13
51
|
const ACCOUNTS_FILE_NAME = "openai-codex-accounts.json";
|
|
14
52
|
const FLAGGED_ACCOUNTS_FILE_NAME = "openai-codex-flagged-accounts.json";
|
|
15
53
|
const LEGACY_FLAGGED_ACCOUNTS_FILE_NAME = "openai-codex-blocked-accounts.json";
|
|
16
|
-
const ACCOUNTS_BACKUP_SUFFIX = ".bak";
|
|
17
|
-
const ACCOUNTS_WAL_SUFFIX = ".wal";
|
|
18
54
|
const ACCOUNTS_BACKUP_HISTORY_DEPTH = 3;
|
|
19
55
|
const BACKUP_COPY_MAX_ATTEMPTS = 5;
|
|
20
56
|
const BACKUP_COPY_BASE_DELAY_MS = 10;
|
|
21
|
-
const RESET_MARKER_SUFFIX = ".reset-intent";
|
|
22
57
|
let storageBackupEnabled = true;
|
|
23
58
|
let lastAccountsSaveTimestamp = 0;
|
|
24
|
-
async function collectNamedBackups(storagePath) {
|
|
25
|
-
const backupRoot = getNamedBackupRoot(storagePath);
|
|
26
|
-
let entries;
|
|
27
|
-
try {
|
|
28
|
-
entries = await fs.readdir(backupRoot, {
|
|
29
|
-
withFileTypes: true,
|
|
30
|
-
encoding: "utf8",
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
const code = error.code;
|
|
35
|
-
if (code === "ENOENT")
|
|
36
|
-
return [];
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
const candidates = [];
|
|
40
|
-
for (const entry of entries) {
|
|
41
|
-
if (!entry.isFile())
|
|
42
|
-
continue;
|
|
43
|
-
if (!entry.name.toLowerCase().endsWith(".json"))
|
|
44
|
-
continue;
|
|
45
|
-
const candidatePath = join(backupRoot, entry.name);
|
|
46
|
-
try {
|
|
47
|
-
const statsBefore = await fs.stat(candidatePath);
|
|
48
|
-
const { normalized } = await loadAccountsFromPath(candidatePath);
|
|
49
|
-
if (!normalized || normalized.accounts.length === 0)
|
|
50
|
-
continue;
|
|
51
|
-
const statsAfter = await fs.stat(candidatePath).catch(() => null);
|
|
52
|
-
if (statsAfter && statsAfter.mtimeMs !== statsBefore.mtimeMs) {
|
|
53
|
-
log.debug("backup file changed between stat and load, mtime may be stale", {
|
|
54
|
-
candidatePath,
|
|
55
|
-
fileName: entry.name,
|
|
56
|
-
beforeMtimeMs: statsBefore.mtimeMs,
|
|
57
|
-
afterMtimeMs: statsAfter.mtimeMs,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
candidates.push({
|
|
61
|
-
path: candidatePath,
|
|
62
|
-
fileName: entry.name,
|
|
63
|
-
accountCount: normalized.accounts.length,
|
|
64
|
-
mtimeMs: statsBefore.mtimeMs,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
log.debug("Skipping named backup candidate after loadAccountsFromPath/fs.stat failure", {
|
|
69
|
-
candidatePath,
|
|
70
|
-
fileName: entry.name,
|
|
71
|
-
error: error instanceof Error
|
|
72
|
-
? {
|
|
73
|
-
message: error.message,
|
|
74
|
-
stack: error.stack,
|
|
75
|
-
}
|
|
76
|
-
: String(error),
|
|
77
|
-
});
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
candidates.sort((left, right) => {
|
|
82
|
-
const mtimeDelta = right.mtimeMs - left.mtimeMs;
|
|
83
|
-
if (mtimeDelta !== 0)
|
|
84
|
-
return mtimeDelta;
|
|
85
|
-
return left.fileName.localeCompare(right.fileName);
|
|
86
|
-
});
|
|
87
|
-
return candidates;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Custom error class for storage operations with platform-aware hints.
|
|
91
|
-
*/
|
|
92
|
-
export class StorageError extends Error {
|
|
93
|
-
code;
|
|
94
|
-
path;
|
|
95
|
-
hint;
|
|
96
|
-
constructor(message, code, path, hint, cause) {
|
|
97
|
-
super(message, { cause });
|
|
98
|
-
this.name = "StorageError";
|
|
99
|
-
this.code = code;
|
|
100
|
-
this.path = path;
|
|
101
|
-
this.hint = hint;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Generate platform-aware troubleshooting hint based on error code.
|
|
106
|
-
*/
|
|
107
|
-
export function formatStorageErrorHint(error, path) {
|
|
108
|
-
const err = error;
|
|
109
|
-
const code = err?.code || "UNKNOWN";
|
|
110
|
-
const isWindows = process.platform === "win32";
|
|
111
|
-
switch (code) {
|
|
112
|
-
case "EACCES":
|
|
113
|
-
case "EPERM":
|
|
114
|
-
return isWindows
|
|
115
|
-
? `Permission denied writing to ${path}. Check antivirus exclusions for this folder. Ensure you have write permissions.`
|
|
116
|
-
: `Permission denied writing to ${path}. Check folder permissions. Try: chmod 755 ~/.codex`;
|
|
117
|
-
case "EBUSY":
|
|
118
|
-
return `File is locked at ${path}. The file may be open in another program. Close any editors or processes accessing it.`;
|
|
119
|
-
case "ENOSPC":
|
|
120
|
-
return `Disk is full. Free up space and try again. Path: ${path}`;
|
|
121
|
-
case "EEMPTY":
|
|
122
|
-
return `File written but is empty. This may indicate a disk or filesystem issue. Path: ${path}`;
|
|
123
|
-
default:
|
|
124
|
-
return isWindows
|
|
125
|
-
? `Failed to write to ${path}. Check folder permissions and ensure path contains no special characters.`
|
|
126
|
-
: `Failed to write to ${path}. Check folder permissions and disk space.`;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
let storageMutex = Promise.resolve();
|
|
130
|
-
const transactionSnapshotContext = new AsyncLocalStorage();
|
|
131
|
-
function withStorageLock(fn) {
|
|
132
|
-
const previousMutex = storageMutex;
|
|
133
|
-
let releaseLock;
|
|
134
|
-
storageMutex = new Promise((resolve) => {
|
|
135
|
-
releaseLock = resolve;
|
|
136
|
-
});
|
|
137
|
-
return previousMutex.then(fn).finally(() => releaseLock());
|
|
138
|
-
}
|
|
139
|
-
function looksLikeSyntheticFixtureAccount(account) {
|
|
140
|
-
const email = typeof account.email === "string" ? account.email.trim().toLowerCase() : "";
|
|
141
|
-
const refreshToken = typeof account.refreshToken === "string"
|
|
142
|
-
? account.refreshToken.trim().toLowerCase()
|
|
143
|
-
: "";
|
|
144
|
-
const accountId = typeof account.accountId === "string"
|
|
145
|
-
? account.accountId.trim().toLowerCase()
|
|
146
|
-
: "";
|
|
147
|
-
if (!/^account\d+@example\.com$/.test(email)) {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
const hasSyntheticRefreshToken = refreshToken.startsWith("fake_refresh") ||
|
|
151
|
-
/^fake_refresh_token_\d+(_for_testing_only)?$/.test(refreshToken);
|
|
152
|
-
if (!hasSyntheticRefreshToken) {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
if (accountId.length === 0) {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
return /^acc(_|-)?\d+$/.test(accountId);
|
|
159
|
-
}
|
|
160
|
-
function looksLikeSyntheticFixtureStorage(storage) {
|
|
161
|
-
if (!storage || storage.accounts.length === 0)
|
|
162
|
-
return false;
|
|
163
|
-
return storage.accounts.every((account) => looksLikeSyntheticFixtureAccount(account));
|
|
164
|
-
}
|
|
165
59
|
async function ensureGitignore(storagePath) {
|
|
166
60
|
const state = getStoragePathState();
|
|
167
61
|
if (!state.currentStoragePath)
|
|
168
62
|
return;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
content = await fs.readFile(gitignorePath, "utf-8");
|
|
180
|
-
const lines = content.split("\n").map((l) => l.trim());
|
|
181
|
-
if (lines.includes(".codex") ||
|
|
182
|
-
lines.includes(".codex/") ||
|
|
183
|
-
lines.includes("/.codex") ||
|
|
184
|
-
lines.includes("/.codex/")) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
const newContent = content.endsWith("\n") || content === "" ? content : content + "\n";
|
|
189
|
-
await fs.writeFile(gitignorePath, newContent + ".codex/\n", "utf-8");
|
|
190
|
-
log.debug("Added .codex to .gitignore", { path: gitignorePath });
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
log.warn("Failed to update .gitignore", { error: String(error) });
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
let currentStorageState = {
|
|
197
|
-
currentStoragePath: null,
|
|
198
|
-
currentLegacyProjectStoragePath: null,
|
|
199
|
-
currentLegacyWorktreeStoragePath: null,
|
|
200
|
-
currentProjectRoot: null,
|
|
201
|
-
};
|
|
202
|
-
const storagePathStateContext = new AsyncLocalStorage();
|
|
203
|
-
function getStoragePathState() {
|
|
204
|
-
return storagePathStateContext.getStore() ?? currentStorageState;
|
|
205
|
-
}
|
|
206
|
-
function setStoragePathState(state) {
|
|
207
|
-
currentStorageState = state;
|
|
208
|
-
storagePathStateContext.enterWith(state);
|
|
63
|
+
await ensureCodexGitignoreEntry({
|
|
64
|
+
storagePath,
|
|
65
|
+
currentProjectRoot: state.currentProjectRoot,
|
|
66
|
+
logDebug: (message, details) => {
|
|
67
|
+
log.debug(message, details);
|
|
68
|
+
},
|
|
69
|
+
logWarn: (message, details) => {
|
|
70
|
+
log.warn(message, details);
|
|
71
|
+
},
|
|
72
|
+
});
|
|
209
73
|
}
|
|
210
74
|
export function setStorageBackupEnabled(enabled) {
|
|
211
75
|
storageBackupEnabled = enabled;
|
|
212
76
|
}
|
|
213
|
-
function getAccountsBackupPath(path) {
|
|
214
|
-
return `${path}${ACCOUNTS_BACKUP_SUFFIX}`;
|
|
215
|
-
}
|
|
216
|
-
function getAccountsBackupPathAtIndex(path, index) {
|
|
217
|
-
if (index <= 0) {
|
|
218
|
-
return getAccountsBackupPath(path);
|
|
219
|
-
}
|
|
220
|
-
return `${path}${ACCOUNTS_BACKUP_SUFFIX}.${index}`;
|
|
221
|
-
}
|
|
222
|
-
function getAccountsBackupRecoveryCandidates(path) {
|
|
223
|
-
const candidates = [];
|
|
224
|
-
for (let i = 0; i < ACCOUNTS_BACKUP_HISTORY_DEPTH; i += 1) {
|
|
225
|
-
candidates.push(getAccountsBackupPathAtIndex(path, i));
|
|
226
|
-
}
|
|
227
|
-
return candidates;
|
|
228
|
-
}
|
|
229
77
|
async function getAccountsBackupRecoveryCandidatesWithDiscovery(path) {
|
|
230
|
-
const knownCandidates = getAccountsBackupRecoveryCandidates(path);
|
|
78
|
+
const knownCandidates = getAccountsBackupRecoveryCandidates(path, ACCOUNTS_BACKUP_HISTORY_DEPTH);
|
|
231
79
|
const discoveredCandidates = new Set();
|
|
232
80
|
const candidatePrefix = `${basename(path)}.`;
|
|
233
81
|
const knownCandidateSet = new Set(knownCandidates);
|
|
@@ -267,9 +115,6 @@ async function getAccountsBackupRecoveryCandidatesWithDiscovery(path) {
|
|
|
267
115
|
const discoveredOrdered = Array.from(discoveredCandidates).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
|
|
268
116
|
return [...knownCandidates, ...discoveredOrdered];
|
|
269
117
|
}
|
|
270
|
-
function getAccountsWalPath(path) {
|
|
271
|
-
return `${path}${ACCOUNTS_WAL_SUFFIX}`;
|
|
272
|
-
}
|
|
273
118
|
async function copyFileWithRetry(sourcePath, destinationPath, options) {
|
|
274
119
|
const allowMissingSource = options?.allowMissingSource ?? false;
|
|
275
120
|
for (let attempt = 0; attempt < BACKUP_COPY_MAX_ATTEMPTS; attempt += 1) {
|
|
@@ -311,7 +156,7 @@ async function renameFileWithRetry(sourcePath, destinationPath) {
|
|
|
311
156
|
}
|
|
312
157
|
}
|
|
313
158
|
async function createRotatingAccountsBackup(path) {
|
|
314
|
-
const candidates = getAccountsBackupRecoveryCandidates(path);
|
|
159
|
+
const candidates = getAccountsBackupRecoveryCandidates(path, ACCOUNTS_BACKUP_HISTORY_DEPTH);
|
|
315
160
|
const rotationNonce = `${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
316
161
|
const stagedWrites = [];
|
|
317
162
|
const buildStagedPath = (targetPath, label) => `${targetPath}.rotate.${rotationNonce}.${label}.tmp`;
|
|
@@ -412,86 +257,22 @@ async function cleanupStaleRotatingBackupArtifacts(path) {
|
|
|
412
257
|
}
|
|
413
258
|
}
|
|
414
259
|
}
|
|
415
|
-
function computeSha256(value) {
|
|
416
|
-
return createHash("sha256").update(value).digest("hex");
|
|
417
|
-
}
|
|
418
|
-
function getIntentionalResetMarkerPath(path) {
|
|
419
|
-
return `${path}${RESET_MARKER_SUFFIX}`;
|
|
420
|
-
}
|
|
421
|
-
function createEmptyStorageWithMetadata(restoreEligible, restoreReason) {
|
|
422
|
-
return {
|
|
423
|
-
version: 3,
|
|
424
|
-
accounts: [],
|
|
425
|
-
activeIndex: 0,
|
|
426
|
-
activeIndexByFamily: {},
|
|
427
|
-
restoreEligible,
|
|
428
|
-
restoreReason,
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
function withRestoreMetadata(storage, restoreEligible, restoreReason) {
|
|
432
|
-
return {
|
|
433
|
-
...storage,
|
|
434
|
-
restoreEligible,
|
|
435
|
-
restoreReason,
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
function isCacheLikeBackupArtifactName(entryName) {
|
|
439
|
-
return entryName.toLowerCase().includes(".cache");
|
|
440
|
-
}
|
|
441
260
|
async function statSnapshot(path) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
catch (error) {
|
|
447
|
-
const code = error.code;
|
|
448
|
-
if (code !== "ENOENT") {
|
|
449
|
-
log.warn("Failed to stat backup candidate", {
|
|
450
|
-
path,
|
|
451
|
-
error: String(error),
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
return { exists: false };
|
|
455
|
-
}
|
|
261
|
+
return statSnapshotWithDeps(path, {
|
|
262
|
+
stat: fs.stat,
|
|
263
|
+
logWarn: (message, meta) => log.warn(message, meta),
|
|
264
|
+
});
|
|
456
265
|
}
|
|
457
266
|
async function describeAccountSnapshot(path, kind, index) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
index,
|
|
468
|
-
exists: true,
|
|
469
|
-
valid: !!normalized,
|
|
470
|
-
bytes: stats.bytes,
|
|
471
|
-
mtimeMs: stats.mtimeMs,
|
|
472
|
-
version: typeof storedVersion === "number" ? storedVersion : undefined,
|
|
473
|
-
accountCount: normalized?.accounts.length,
|
|
474
|
-
schemaErrors: schemaErrors.length > 0 ? schemaErrors : undefined,
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
catch (error) {
|
|
478
|
-
const code = error.code;
|
|
479
|
-
if (code !== "ENOENT") {
|
|
480
|
-
log.warn("Failed to inspect account snapshot", {
|
|
481
|
-
path,
|
|
482
|
-
error: String(error),
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
return {
|
|
486
|
-
kind,
|
|
487
|
-
path,
|
|
488
|
-
index,
|
|
489
|
-
exists: true,
|
|
490
|
-
valid: false,
|
|
491
|
-
bytes: stats.bytes,
|
|
492
|
-
mtimeMs: stats.mtimeMs,
|
|
493
|
-
};
|
|
494
|
-
}
|
|
267
|
+
return describeAccountSnapshotWithDeps(path, kind, {
|
|
268
|
+
index,
|
|
269
|
+
statSnapshot,
|
|
270
|
+
loadAccountsFromPath: (targetPath) => loadAccountsFromPath(targetPath, {
|
|
271
|
+
normalizeAccountStorage,
|
|
272
|
+
isRecord,
|
|
273
|
+
}),
|
|
274
|
+
logWarn: (message, meta) => log.warn(message, meta),
|
|
275
|
+
});
|
|
495
276
|
}
|
|
496
277
|
async function describeAccountsWalSnapshot(path) {
|
|
497
278
|
const stats = await statSnapshot(path);
|
|
@@ -525,7 +306,7 @@ async function describeAccountsWalSnapshot(path) {
|
|
|
525
306
|
mtimeMs: stats.mtimeMs,
|
|
526
307
|
};
|
|
527
308
|
}
|
|
528
|
-
const { normalized, storedVersion, schemaErrors } = parseAndNormalizeStorage(JSON.parse(entry.content));
|
|
309
|
+
const { normalized, storedVersion, schemaErrors } = parseAndNormalizeStorage(JSON.parse(entry.content), normalizeAccountStorage, isRecord);
|
|
529
310
|
return {
|
|
530
311
|
kind: "accounts-wal",
|
|
531
312
|
path,
|
|
@@ -550,9 +331,13 @@ async function describeAccountsWalSnapshot(path) {
|
|
|
550
331
|
}
|
|
551
332
|
}
|
|
552
333
|
async function loadFlaggedAccountsFromPath(path) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
334
|
+
return loadFlaggedAccountsFromFile(path, {
|
|
335
|
+
readFile: fs.readFile,
|
|
336
|
+
normalizeFlaggedStorage: (data) => normalizeFlaggedStorage(data, {
|
|
337
|
+
isRecord,
|
|
338
|
+
now: () => Date.now(),
|
|
339
|
+
}),
|
|
340
|
+
});
|
|
556
341
|
}
|
|
557
342
|
async function describeFlaggedSnapshot(path, kind, index) {
|
|
558
343
|
const stats = await statSnapshot(path);
|
|
@@ -592,21 +377,6 @@ async function describeFlaggedSnapshot(path, kind, index) {
|
|
|
592
377
|
};
|
|
593
378
|
}
|
|
594
379
|
}
|
|
595
|
-
function latestValidSnapshot(snapshots) {
|
|
596
|
-
return snapshots
|
|
597
|
-
.filter((snapshot) => snapshot.valid)
|
|
598
|
-
.sort((left, right) => (right.mtimeMs ?? 0) - (left.mtimeMs ?? 0))[0];
|
|
599
|
-
}
|
|
600
|
-
function buildMetadataSection(storagePath, snapshots) {
|
|
601
|
-
const latestValid = latestValidSnapshot(snapshots);
|
|
602
|
-
return {
|
|
603
|
-
storagePath,
|
|
604
|
-
latestValidPath: latestValid?.path,
|
|
605
|
-
snapshotCount: snapshots.length,
|
|
606
|
-
validSnapshotCount: snapshots.filter((snapshot) => snapshot.valid).length,
|
|
607
|
-
snapshots,
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
380
|
export function getLastAccountsSaveTimestamp() {
|
|
611
381
|
return lastAccountsSaveTimestamp;
|
|
612
382
|
}
|
|
@@ -668,76 +438,56 @@ export function buildNamedBackupPath(name) {
|
|
|
668
438
|
return resolveNamedBackupPath(name, getStoragePath());
|
|
669
439
|
}
|
|
670
440
|
export async function getNamedBackups() {
|
|
671
|
-
return
|
|
441
|
+
return getNamedBackupsEntry({
|
|
442
|
+
getStoragePath,
|
|
443
|
+
collectNamedBackups,
|
|
444
|
+
loadAccountsFromPath: (path) => loadAccountsFromPath(path, {
|
|
445
|
+
normalizeAccountStorage,
|
|
446
|
+
isRecord,
|
|
447
|
+
}),
|
|
448
|
+
logDebug: (message, details) => {
|
|
449
|
+
log.debug(message, details);
|
|
450
|
+
},
|
|
451
|
+
});
|
|
672
452
|
}
|
|
673
453
|
export async function restoreAccountsFromBackup(path, options) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
try {
|
|
688
|
-
resolvedBackupPath = await fs.realpath(path);
|
|
689
|
-
}
|
|
690
|
-
catch (error) {
|
|
691
|
-
const code = error.code;
|
|
692
|
-
if (code === "ENOENT") {
|
|
693
|
-
throw new Error(`Backup file no longer exists: ${path}`);
|
|
694
|
-
}
|
|
695
|
-
throw error;
|
|
696
|
-
}
|
|
697
|
-
const relativePath = relative(resolvedBackupRoot, resolvedBackupPath);
|
|
698
|
-
const isInsideBackupRoot = relativePath.length > 0 &&
|
|
699
|
-
!relativePath.startsWith("..") &&
|
|
700
|
-
!isAbsolute(relativePath);
|
|
701
|
-
if (!isInsideBackupRoot) {
|
|
702
|
-
throw new Error(`Backup path must stay inside ${resolvedBackupRoot}: ${path}`);
|
|
703
|
-
}
|
|
704
|
-
const { normalized } = await (async () => {
|
|
705
|
-
try {
|
|
706
|
-
return await loadAccountsFromPath(resolvedBackupPath);
|
|
707
|
-
}
|
|
708
|
-
catch (error) {
|
|
709
|
-
const code = error.code;
|
|
710
|
-
if (code === "ENOENT") {
|
|
711
|
-
throw new Error(`Backup file no longer exists: ${path}`);
|
|
712
|
-
}
|
|
713
|
-
throw error;
|
|
714
|
-
}
|
|
715
|
-
})();
|
|
716
|
-
if (!normalized || normalized.accounts.length === 0) {
|
|
717
|
-
throw new Error(`Backup does not contain any accounts: ${resolvedBackupPath}`);
|
|
718
|
-
}
|
|
719
|
-
if (options?.persist !== false) {
|
|
720
|
-
await saveAccounts(normalized);
|
|
721
|
-
}
|
|
722
|
-
return normalized;
|
|
454
|
+
return restoreAccountsFromBackupEntry({
|
|
455
|
+
path,
|
|
456
|
+
options,
|
|
457
|
+
restoreAccountsFromBackupPath,
|
|
458
|
+
getNamedBackupRoot,
|
|
459
|
+
getStoragePath,
|
|
460
|
+
realpath: fs.realpath,
|
|
461
|
+
loadAccountsFromPath: (path) => loadAccountsFromPath(path, {
|
|
462
|
+
normalizeAccountStorage,
|
|
463
|
+
isRecord,
|
|
464
|
+
}),
|
|
465
|
+
saveAccounts,
|
|
466
|
+
});
|
|
723
467
|
}
|
|
724
468
|
export async function exportNamedBackup(name, options) {
|
|
725
|
-
return
|
|
469
|
+
return exportNamedBackupEntry({
|
|
470
|
+
name,
|
|
471
|
+
options,
|
|
472
|
+
exportNamedBackupFile,
|
|
726
473
|
getStoragePath,
|
|
727
474
|
exportAccounts,
|
|
728
|
-
}
|
|
475
|
+
});
|
|
729
476
|
}
|
|
730
477
|
export function getFlaggedAccountsPath() {
|
|
731
|
-
return
|
|
478
|
+
return buildFlaggedAccountsPath(getStoragePath(), FLAGGED_ACCOUNTS_FILE_NAME);
|
|
732
479
|
}
|
|
733
480
|
function getLegacyFlaggedAccountsPath() {
|
|
734
|
-
return
|
|
481
|
+
return buildLegacyFlaggedAccountsPath(getStoragePath(), LEGACY_FLAGGED_ACCOUNTS_FILE_NAME);
|
|
735
482
|
}
|
|
736
|
-
async function migrateLegacyProjectStorageIfNeeded(
|
|
483
|
+
async function migrateLegacyProjectStorageIfNeeded(options) {
|
|
484
|
+
const persist = options?.persist ?? saveAccounts;
|
|
485
|
+
const commit = options?.commit ?? true;
|
|
737
486
|
const state = getStoragePathState();
|
|
738
487
|
if (!state.currentStoragePath) {
|
|
739
488
|
return null;
|
|
740
489
|
}
|
|
490
|
+
const currentStoragePath = state.currentStoragePath;
|
|
741
491
|
const candidatePaths = [
|
|
742
492
|
state.currentLegacyWorktreeStoragePath,
|
|
743
493
|
state.currentLegacyProjectStoragePath,
|
|
@@ -753,181 +503,117 @@ async function migrateLegacyProjectStorageIfNeeded(persist = saveAccounts) {
|
|
|
753
503
|
if (existingCandidatePaths.length === 0) {
|
|
754
504
|
return null;
|
|
755
505
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
506
|
+
const loadCurrentStorageForMigration = async () => loadNormalizedStorageFromPath(currentStoragePath, "current account storage", {
|
|
507
|
+
loadAccountsFromPath: (path) => loadAccountsFromPath(path, {
|
|
508
|
+
normalizeAccountStorage,
|
|
509
|
+
isRecord,
|
|
510
|
+
}),
|
|
511
|
+
logWarn: (message, details) => {
|
|
512
|
+
log.warn(message, details);
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
const readLiveCurrentStorageIfExportMode = async () => {
|
|
516
|
+
if (commit || !existsSync(currentStoragePath)) {
|
|
517
|
+
return { exists: false, storage: null };
|
|
762
518
|
}
|
|
763
|
-
const mergedStorage = mergeStorageForMigration(targetStorage, legacyStorage);
|
|
764
|
-
const fallbackStorage = targetStorage ?? legacyStorage;
|
|
765
519
|
try {
|
|
766
|
-
await
|
|
767
|
-
|
|
768
|
-
|
|
520
|
+
const { normalized, schemaErrors } = await loadAccountsFromPath(currentStoragePath, {
|
|
521
|
+
normalizeAccountStorage,
|
|
522
|
+
isRecord,
|
|
523
|
+
});
|
|
524
|
+
if (schemaErrors.length > 0) {
|
|
525
|
+
log.warn("current account storage schema validation warnings", {
|
|
526
|
+
path: currentStoragePath,
|
|
527
|
+
errors: schemaErrors.slice(0, 5),
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
exists: true,
|
|
532
|
+
storage: normalized,
|
|
533
|
+
};
|
|
769
534
|
}
|
|
770
535
|
catch (error) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
536
|
+
if (error.code === "ENOENT") {
|
|
537
|
+
return { exists: false, storage: null };
|
|
538
|
+
}
|
|
539
|
+
throw error;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
let targetStorage = await loadCurrentStorageForMigration();
|
|
543
|
+
let migrated = false;
|
|
544
|
+
for (const legacyPath of existingCandidatePaths) {
|
|
545
|
+
const liveCurrentStorageBeforeMerge = await readLiveCurrentStorageIfExportMode();
|
|
546
|
+
if (liveCurrentStorageBeforeMerge.exists) {
|
|
547
|
+
return liveCurrentStorageBeforeMerge.storage;
|
|
548
|
+
}
|
|
549
|
+
const legacyStorage = await loadNormalizedStorageFromPath(legacyPath, "legacy account storage", {
|
|
550
|
+
loadAccountsFromPath: (path) => loadAccountsFromPath(path, {
|
|
551
|
+
normalizeAccountStorage,
|
|
552
|
+
isRecord,
|
|
553
|
+
}),
|
|
554
|
+
logWarn: (message, details) => {
|
|
555
|
+
log.warn(message, details);
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
if (!legacyStorage) {
|
|
777
559
|
continue;
|
|
778
560
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
path: legacyPath,
|
|
783
|
-
});
|
|
561
|
+
const liveCurrentStorageAfterLegacyRead = await readLiveCurrentStorageIfExportMode();
|
|
562
|
+
if (liveCurrentStorageAfterLegacyRead.exists) {
|
|
563
|
+
return liveCurrentStorageAfterLegacyRead.storage;
|
|
784
564
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
565
|
+
const mergedStorage = mergeStorageForMigration(targetStorage, legacyStorage, normalizeAccountStorage);
|
|
566
|
+
const fallbackStorage = targetStorage ?? legacyStorage;
|
|
567
|
+
if (commit) {
|
|
568
|
+
try {
|
|
569
|
+
await persist(mergedStorage);
|
|
570
|
+
targetStorage = mergedStorage;
|
|
571
|
+
migrated = true;
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
targetStorage = fallbackStorage;
|
|
575
|
+
log.warn("Failed to persist migrated account storage", {
|
|
576
|
+
from: legacyPath,
|
|
577
|
+
to: currentStoragePath,
|
|
578
|
+
error: String(error),
|
|
579
|
+
});
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
try {
|
|
583
|
+
await fs.unlink(legacyPath);
|
|
584
|
+
log.info("Removed legacy account storage file after migration", {
|
|
789
585
|
path: legacyPath,
|
|
790
|
-
error: String(unlinkError),
|
|
791
586
|
});
|
|
792
587
|
}
|
|
588
|
+
catch (unlinkError) {
|
|
589
|
+
const code = unlinkError.code;
|
|
590
|
+
if (code !== "ENOENT") {
|
|
591
|
+
log.warn("Failed to remove legacy account storage file after migration", {
|
|
592
|
+
path: legacyPath,
|
|
593
|
+
error: String(unlinkError),
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
log.info("Migrated legacy project account storage", {
|
|
598
|
+
from: legacyPath,
|
|
599
|
+
to: currentStoragePath,
|
|
600
|
+
accounts: mergedStorage.accounts.length,
|
|
601
|
+
});
|
|
602
|
+
continue;
|
|
793
603
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
to: state.currentStoragePath,
|
|
797
|
-
accounts: mergedStorage.accounts.length,
|
|
798
|
-
});
|
|
604
|
+
targetStorage = mergedStorage;
|
|
605
|
+
migrated = true;
|
|
799
606
|
}
|
|
800
607
|
if (migrated) {
|
|
801
608
|
return targetStorage;
|
|
802
609
|
}
|
|
803
|
-
if (targetStorage && !existsSync(
|
|
610
|
+
if (targetStorage && !existsSync(currentStoragePath)) {
|
|
804
611
|
return targetStorage;
|
|
805
612
|
}
|
|
806
613
|
return null;
|
|
807
614
|
}
|
|
808
|
-
async function loadNormalizedStorageFromPath(path, label) {
|
|
809
|
-
try {
|
|
810
|
-
const { normalized, schemaErrors } = await loadAccountsFromPath(path);
|
|
811
|
-
if (schemaErrors.length > 0) {
|
|
812
|
-
log.warn(`${label} schema validation warnings`, {
|
|
813
|
-
path,
|
|
814
|
-
errors: schemaErrors.slice(0, 5),
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
return normalized;
|
|
818
|
-
}
|
|
819
|
-
catch (error) {
|
|
820
|
-
const code = error.code;
|
|
821
|
-
if (code !== "ENOENT") {
|
|
822
|
-
log.warn(`Failed to load ${label}`, {
|
|
823
|
-
path,
|
|
824
|
-
error: String(error),
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
return null;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
function mergeStorageForMigration(current, incoming) {
|
|
831
|
-
if (!current) {
|
|
832
|
-
return incoming;
|
|
833
|
-
}
|
|
834
|
-
const merged = normalizeAccountStorage({
|
|
835
|
-
version: 3,
|
|
836
|
-
activeIndex: current.activeIndex,
|
|
837
|
-
activeIndexByFamily: current.activeIndexByFamily,
|
|
838
|
-
accounts: [...current.accounts, ...incoming.accounts],
|
|
839
|
-
});
|
|
840
|
-
if (!merged) {
|
|
841
|
-
return current;
|
|
842
|
-
}
|
|
843
|
-
return merged;
|
|
844
|
-
}
|
|
845
|
-
function selectNewestAccount(current, candidate) {
|
|
846
|
-
if (!current)
|
|
847
|
-
return candidate;
|
|
848
|
-
const currentLastUsed = current.lastUsed || 0;
|
|
849
|
-
const candidateLastUsed = candidate.lastUsed || 0;
|
|
850
|
-
if (candidateLastUsed > currentLastUsed)
|
|
851
|
-
return candidate;
|
|
852
|
-
if (candidateLastUsed < currentLastUsed)
|
|
853
|
-
return current;
|
|
854
|
-
const currentAddedAt = current.addedAt || 0;
|
|
855
|
-
const candidateAddedAt = candidate.addedAt || 0;
|
|
856
|
-
return candidateAddedAt >= currentAddedAt ? candidate : current;
|
|
857
|
-
}
|
|
858
|
-
function normalizeAccountIdKey(accountId) {
|
|
859
|
-
if (!accountId)
|
|
860
|
-
return undefined;
|
|
861
|
-
const trimmed = accountId.trim();
|
|
862
|
-
return trimmed || undefined;
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Normalize email keys for case-insensitive account identity matching.
|
|
866
|
-
*/
|
|
867
|
-
export function normalizeEmailKey(email) {
|
|
868
|
-
if (!email)
|
|
869
|
-
return undefined;
|
|
870
|
-
const trimmed = email.trim();
|
|
871
|
-
if (!trimmed)
|
|
872
|
-
return undefined;
|
|
873
|
-
return trimmed.toLowerCase();
|
|
874
|
-
}
|
|
875
|
-
function normalizeRefreshTokenKey(refreshToken) {
|
|
876
|
-
if (!refreshToken)
|
|
877
|
-
return undefined;
|
|
878
|
-
const trimmed = refreshToken.trim();
|
|
879
|
-
return trimmed || undefined;
|
|
880
|
-
}
|
|
881
|
-
function toAccountIdentityRef(account) {
|
|
882
|
-
return {
|
|
883
|
-
accountId: normalizeAccountIdKey(account?.accountId),
|
|
884
|
-
emailKey: normalizeEmailKey(account?.email),
|
|
885
|
-
refreshToken: normalizeRefreshTokenKey(account?.refreshToken),
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
function collectDistinctIdentityValues(values) {
|
|
889
|
-
const distinct = new Set();
|
|
890
|
-
for (const value of values) {
|
|
891
|
-
if (value)
|
|
892
|
-
distinct.add(value);
|
|
893
|
-
}
|
|
894
|
-
return distinct;
|
|
895
|
-
}
|
|
896
|
-
export function getAccountIdentityKey(account) {
|
|
897
|
-
const ref = toAccountIdentityRef(account);
|
|
898
|
-
if (ref.accountId && ref.emailKey) {
|
|
899
|
-
return `account:${ref.accountId}::email:${ref.emailKey}`;
|
|
900
|
-
}
|
|
901
|
-
if (ref.accountId)
|
|
902
|
-
return `account:${ref.accountId}`;
|
|
903
|
-
if (ref.emailKey)
|
|
904
|
-
return `email:${ref.emailKey}`;
|
|
905
|
-
if (ref.refreshToken)
|
|
906
|
-
return `refresh:${ref.refreshToken}`;
|
|
907
|
-
return undefined;
|
|
908
|
-
}
|
|
909
615
|
function findNewestMatchingIndex(accounts, predicate) {
|
|
910
|
-
|
|
911
|
-
let match;
|
|
912
|
-
for (let i = 0; i < accounts.length; i += 1) {
|
|
913
|
-
const account = accounts[i];
|
|
914
|
-
if (!account)
|
|
915
|
-
continue;
|
|
916
|
-
const ref = toAccountIdentityRef(account);
|
|
917
|
-
if (!predicate(ref))
|
|
918
|
-
continue;
|
|
919
|
-
if (matchIndex === undefined) {
|
|
920
|
-
matchIndex = i;
|
|
921
|
-
match = account;
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
const newest = selectNewestAccount(match, account);
|
|
925
|
-
if (newest === account) {
|
|
926
|
-
matchIndex = i;
|
|
927
|
-
match = account;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
return matchIndex;
|
|
616
|
+
return findNewestMatchingIndexByRef(accounts, (account) => toAccountIdentityRef(account), predicate, selectNewestAccount);
|
|
931
617
|
}
|
|
932
618
|
function findCompositeAccountMatchIndex(accounts, candidateRef) {
|
|
933
619
|
if (!candidateRef.accountId || !candidateRef.emailKey)
|
|
@@ -1074,14 +760,6 @@ export function deduplicateAccounts(accounts) {
|
|
|
1074
760
|
export function deduplicateAccountsByEmail(accounts) {
|
|
1075
761
|
return deduplicateAccountsByIdentity(accounts);
|
|
1076
762
|
}
|
|
1077
|
-
function isRecord(value) {
|
|
1078
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1079
|
-
}
|
|
1080
|
-
function clampIndex(index, length) {
|
|
1081
|
-
if (length <= 0)
|
|
1082
|
-
return 0;
|
|
1083
|
-
return Math.max(0, Math.min(index, length - 1));
|
|
1084
|
-
}
|
|
1085
763
|
function extractActiveAccountRef(accounts, activeIndex) {
|
|
1086
764
|
const candidate = accounts[activeIndex];
|
|
1087
765
|
if (!isRecord(candidate))
|
|
@@ -1172,92 +850,27 @@ export async function loadAccounts() {
|
|
|
1172
850
|
}
|
|
1173
851
|
export async function getBackupMetadata() {
|
|
1174
852
|
const storagePath = getStoragePath();
|
|
1175
|
-
const walPath = getAccountsWalPath(storagePath);
|
|
1176
|
-
const accountCandidates = await getAccountsBackupRecoveryCandidatesWithDiscovery(storagePath);
|
|
1177
|
-
const accountSnapshots = [
|
|
1178
|
-
await describeAccountSnapshot(storagePath, "accounts-primary"),
|
|
1179
|
-
await describeAccountsWalSnapshot(walPath),
|
|
1180
|
-
];
|
|
1181
|
-
for (const [index, candidate] of accountCandidates.entries()) {
|
|
1182
|
-
const kind = candidate === `${storagePath}.bak`
|
|
1183
|
-
? "accounts-backup"
|
|
1184
|
-
: candidate.startsWith(`${storagePath}.bak.`)
|
|
1185
|
-
? "accounts-backup-history"
|
|
1186
|
-
: "accounts-discovered-backup";
|
|
1187
|
-
accountSnapshots.push(await describeAccountSnapshot(candidate, kind, index));
|
|
1188
|
-
}
|
|
1189
853
|
const flaggedPath = getFlaggedAccountsPath();
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
flaggedSnapshots.push(await describeFlaggedSnapshot(candidate, kind, index));
|
|
1201
|
-
}
|
|
1202
|
-
return {
|
|
1203
|
-
accounts: buildMetadataSection(storagePath, accountSnapshots),
|
|
1204
|
-
flaggedAccounts: buildMetadataSection(flaggedPath, flaggedSnapshots),
|
|
1205
|
-
};
|
|
854
|
+
return buildBackupMetadata({
|
|
855
|
+
storagePath,
|
|
856
|
+
flaggedPath,
|
|
857
|
+
walPath: getAccountsWalPath(storagePath),
|
|
858
|
+
getAccountsBackupRecoveryCandidatesWithDiscovery,
|
|
859
|
+
describeAccountSnapshot,
|
|
860
|
+
describeAccountsWalSnapshot,
|
|
861
|
+
describeFlaggedSnapshot,
|
|
862
|
+
buildMetadataSection,
|
|
863
|
+
});
|
|
1206
864
|
}
|
|
1207
865
|
export async function getRestoreAssessment() {
|
|
1208
866
|
const storagePath = getStoragePath();
|
|
1209
867
|
const resetMarkerPath = getIntentionalResetMarkerPath(storagePath);
|
|
1210
868
|
const backupMetadata = await getBackupMetadata();
|
|
1211
|
-
|
|
1212
|
-
return {
|
|
1213
|
-
storagePath,
|
|
1214
|
-
restoreEligible: false,
|
|
1215
|
-
restoreReason: "intentional-reset",
|
|
1216
|
-
backupMetadata,
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
const primarySnapshot = backupMetadata.accounts.snapshots.find((snapshot) => snapshot.kind === "accounts-primary");
|
|
1220
|
-
if (!primarySnapshot?.exists) {
|
|
1221
|
-
return {
|
|
1222
|
-
storagePath,
|
|
1223
|
-
restoreEligible: true,
|
|
1224
|
-
restoreReason: "missing-storage",
|
|
1225
|
-
latestSnapshot: backupMetadata.accounts.latestValidPath
|
|
1226
|
-
? backupMetadata.accounts.snapshots.find((snapshot) => snapshot.path === backupMetadata.accounts.latestValidPath)
|
|
1227
|
-
: undefined,
|
|
1228
|
-
backupMetadata,
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
|
-
if (primarySnapshot.valid && primarySnapshot.accountCount === 0) {
|
|
1232
|
-
return {
|
|
1233
|
-
storagePath,
|
|
1234
|
-
restoreEligible: true,
|
|
1235
|
-
restoreReason: "empty-storage",
|
|
1236
|
-
latestSnapshot: primarySnapshot,
|
|
1237
|
-
backupMetadata,
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
return {
|
|
869
|
+
return buildRestoreAssessment({
|
|
1241
870
|
storagePath,
|
|
1242
|
-
restoreEligible: false,
|
|
1243
|
-
latestSnapshot: backupMetadata.accounts.latestValidPath
|
|
1244
|
-
? backupMetadata.accounts.snapshots.find((snapshot) => snapshot.path === backupMetadata.accounts.latestValidPath)
|
|
1245
|
-
: undefined,
|
|
1246
871
|
backupMetadata,
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1249
|
-
function parseAndNormalizeStorage(data) {
|
|
1250
|
-
const schemaErrors = getValidationErrors(AnyAccountStorageSchema, data);
|
|
1251
|
-
const normalized = normalizeAccountStorage(data);
|
|
1252
|
-
const storedVersion = isRecord(data)
|
|
1253
|
-
? data.version
|
|
1254
|
-
: undefined;
|
|
1255
|
-
return { normalized, storedVersion, schemaErrors };
|
|
1256
|
-
}
|
|
1257
|
-
async function loadAccountsFromPath(path) {
|
|
1258
|
-
const content = await fs.readFile(path, "utf-8");
|
|
1259
|
-
const data = JSON.parse(content);
|
|
1260
|
-
return parseAndNormalizeStorage(data);
|
|
872
|
+
hasResetMarker: existsSync(resetMarkerPath),
|
|
873
|
+
});
|
|
1261
874
|
}
|
|
1262
875
|
async function loadAccountsFromJournal(path) {
|
|
1263
876
|
const walPath = getAccountsWalPath(path);
|
|
@@ -1284,7 +897,7 @@ async function loadAccountsFromJournal(path) {
|
|
|
1284
897
|
return null;
|
|
1285
898
|
}
|
|
1286
899
|
const data = JSON.parse(entry.content);
|
|
1287
|
-
const { normalized } = parseAndNormalizeStorage(data);
|
|
900
|
+
const { normalized } = parseAndNormalizeStorage(data, normalizeAccountStorage, isRecord);
|
|
1288
901
|
if (!normalized)
|
|
1289
902
|
return null;
|
|
1290
903
|
log.warn("Recovered account storage from WAL journal", { path, walPath });
|
|
@@ -1306,10 +919,13 @@ async function loadAccountsInternal(persistMigration) {
|
|
|
1306
919
|
const resetMarkerPath = getIntentionalResetMarkerPath(path);
|
|
1307
920
|
await cleanupStaleRotatingBackupArtifacts(path);
|
|
1308
921
|
const migratedLegacyStorage = persistMigration
|
|
1309
|
-
? await migrateLegacyProjectStorageIfNeeded(persistMigration)
|
|
922
|
+
? await migrateLegacyProjectStorageIfNeeded({ persist: persistMigration })
|
|
1310
923
|
: null;
|
|
1311
924
|
try {
|
|
1312
|
-
const { normalized, storedVersion, schemaErrors } = await loadAccountsFromPath(path
|
|
925
|
+
const { normalized, storedVersion, schemaErrors } = await loadAccountsFromPath(path, {
|
|
926
|
+
normalizeAccountStorage,
|
|
927
|
+
isRecord,
|
|
928
|
+
});
|
|
1313
929
|
if (schemaErrors.length > 0) {
|
|
1314
930
|
log.warn("Account storage schema validation warnings", {
|
|
1315
931
|
errors: schemaErrors.slice(0, 5),
|
|
@@ -1344,7 +960,10 @@ async function loadAccountsInternal(persistMigration) {
|
|
|
1344
960
|
if (backupPath === path)
|
|
1345
961
|
continue;
|
|
1346
962
|
try {
|
|
1347
|
-
const backup = await loadAccountsFromPath(backupPath
|
|
963
|
+
const backup = await loadAccountsFromPath(backupPath, {
|
|
964
|
+
normalizeAccountStorage,
|
|
965
|
+
isRecord,
|
|
966
|
+
});
|
|
1348
967
|
if (!backup.normalized)
|
|
1349
968
|
continue;
|
|
1350
969
|
if (looksLikeSyntheticFixtureStorage(backup.normalized))
|
|
@@ -1413,7 +1032,10 @@ async function loadAccountsInternal(persistMigration) {
|
|
|
1413
1032
|
const backupCandidates = await getAccountsBackupRecoveryCandidatesWithDiscovery(path);
|
|
1414
1033
|
for (const backupPath of backupCandidates) {
|
|
1415
1034
|
try {
|
|
1416
|
-
const backup = await loadAccountsFromPath(backupPath
|
|
1035
|
+
const backup = await loadAccountsFromPath(backupPath, {
|
|
1036
|
+
normalizeAccountStorage,
|
|
1037
|
+
isRecord,
|
|
1038
|
+
});
|
|
1417
1039
|
if (backup.schemaErrors.length > 0) {
|
|
1418
1040
|
log.warn("Backup account storage schema validation warnings", {
|
|
1419
1041
|
path: backupPath,
|
|
@@ -1459,138 +1081,160 @@ async function loadAccountsInternal(persistMigration) {
|
|
|
1459
1081
|
return null;
|
|
1460
1082
|
}
|
|
1461
1083
|
}
|
|
1084
|
+
async function loadAccountsForExport() {
|
|
1085
|
+
// Export reuses this helper from both paths in `exportAccounts()`. Keep the
|
|
1086
|
+
// read side effect free so export never clears a reset marker or races with
|
|
1087
|
+
// concurrent writers while normalizing legacy storage for the snapshot.
|
|
1088
|
+
const path = getStoragePath();
|
|
1089
|
+
const resetMarkerPath = getIntentionalResetMarkerPath(path);
|
|
1090
|
+
if (existsSync(resetMarkerPath)) {
|
|
1091
|
+
return createEmptyStorageWithMetadata(false, "intentional-reset");
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
const { normalized, schemaErrors } = await loadAccountsFromPath(path, {
|
|
1095
|
+
normalizeAccountStorage,
|
|
1096
|
+
isRecord,
|
|
1097
|
+
});
|
|
1098
|
+
if (schemaErrors.length > 0) {
|
|
1099
|
+
log.warn("Account storage schema validation warnings", {
|
|
1100
|
+
errors: schemaErrors.slice(0, 5),
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
if (existsSync(resetMarkerPath)) {
|
|
1104
|
+
return createEmptyStorageWithMetadata(false, "intentional-reset");
|
|
1105
|
+
}
|
|
1106
|
+
return normalized;
|
|
1107
|
+
}
|
|
1108
|
+
catch (error) {
|
|
1109
|
+
const code = error.code;
|
|
1110
|
+
if (existsSync(resetMarkerPath)) {
|
|
1111
|
+
return createEmptyStorageWithMetadata(false, "intentional-reset");
|
|
1112
|
+
}
|
|
1113
|
+
if (code === "ENOENT") {
|
|
1114
|
+
const migratedLegacyStorage = await migrateLegacyProjectStorageIfNeeded({ commit: false });
|
|
1115
|
+
if (existsSync(resetMarkerPath)) {
|
|
1116
|
+
return createEmptyStorageWithMetadata(false, "intentional-reset");
|
|
1117
|
+
}
|
|
1118
|
+
return migratedLegacyStorage;
|
|
1119
|
+
}
|
|
1120
|
+
throw error;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1462
1123
|
async function saveAccountsUnlocked(storage) {
|
|
1463
1124
|
const path = getStoragePath();
|
|
1464
1125
|
const resetMarkerPath = getIntentionalResetMarkerPath(path);
|
|
1465
|
-
const uniqueSuffix = `${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
1466
|
-
const tempPath = `${path}.${uniqueSuffix}.tmp`;
|
|
1467
1126
|
const walPath = getAccountsWalPath(path);
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1127
|
+
return saveAccountsToDisk(storage, {
|
|
1128
|
+
path,
|
|
1129
|
+
resetMarkerPath,
|
|
1130
|
+
walPath,
|
|
1131
|
+
storageBackupEnabled: storageBackupEnabled && existsSync(path),
|
|
1132
|
+
ensureDirectory: async () => {
|
|
1133
|
+
await fs.mkdir(dirname(path), { recursive: true });
|
|
1134
|
+
},
|
|
1135
|
+
ensureGitignore: () => ensureGitignore(path),
|
|
1136
|
+
looksLikeSyntheticFixtureStorage,
|
|
1137
|
+
loadExistingStorage: () => loadNormalizedStorageFromPath(path, "existing account storage", {
|
|
1138
|
+
loadAccountsFromPath: (candidatePath) => loadAccountsFromPath(candidatePath, {
|
|
1139
|
+
normalizeAccountStorage,
|
|
1140
|
+
isRecord,
|
|
1141
|
+
}),
|
|
1142
|
+
logWarn: (message, details) => {
|
|
1143
|
+
log.warn(message, details);
|
|
1144
|
+
},
|
|
1145
|
+
}),
|
|
1146
|
+
createSyntheticFixtureError: () => new StorageError("Refusing to overwrite non-synthetic account storage with synthetic fixture payload", "EINVALID", path, "Detected synthetic fixture-like account payload. Use explicit account import/login commands instead."),
|
|
1147
|
+
createRotatingAccountsBackup,
|
|
1148
|
+
computeSha256,
|
|
1149
|
+
writeJournal: async (content, journalPath) => {
|
|
1150
|
+
const journalEntry = {
|
|
1151
|
+
version: 1,
|
|
1152
|
+
createdAt: Date.now(),
|
|
1153
|
+
path: journalPath,
|
|
1154
|
+
checksum: computeSha256(content),
|
|
1155
|
+
content,
|
|
1156
|
+
};
|
|
1157
|
+
await fs.writeFile(walPath, JSON.stringify(journalEntry), {
|
|
1158
|
+
encoding: "utf-8",
|
|
1159
|
+
mode: 0o600,
|
|
1160
|
+
});
|
|
1161
|
+
},
|
|
1162
|
+
writeTemp: (tempPath, content) => fs.writeFile(tempPath, content, { encoding: "utf-8", mode: 0o600 }),
|
|
1163
|
+
statTemp: (tempPath) => fs.stat(tempPath),
|
|
1164
|
+
renameTempToPath: async (tempPath) => {
|
|
1165
|
+
let lastError = null;
|
|
1166
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
1167
|
+
try {
|
|
1168
|
+
await fs.rename(tempPath, path);
|
|
1169
|
+
return;
|
|
1478
1170
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1171
|
+
catch (renameError) {
|
|
1172
|
+
const code = renameError.code;
|
|
1173
|
+
if (code === "EPERM" || code === "EBUSY") {
|
|
1174
|
+
lastError = renameError;
|
|
1175
|
+
await new Promise((r) => setTimeout(r, 10 * 2 ** attempt));
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
throw renameError;
|
|
1483
1179
|
}
|
|
1484
|
-
// Ignore existing-file probe failures and continue with normal save flow.
|
|
1485
1180
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1181
|
+
if (lastError)
|
|
1182
|
+
throw lastError;
|
|
1183
|
+
},
|
|
1184
|
+
cleanupResetMarker: async () => {
|
|
1488
1185
|
try {
|
|
1489
|
-
await
|
|
1186
|
+
await fs.unlink(resetMarkerPath);
|
|
1490
1187
|
}
|
|
1491
|
-
catch
|
|
1492
|
-
|
|
1493
|
-
path,
|
|
1494
|
-
backupPath: getAccountsBackupPath(path),
|
|
1495
|
-
error: String(backupError),
|
|
1496
|
-
});
|
|
1188
|
+
catch {
|
|
1189
|
+
// Best effort cleanup.
|
|
1497
1190
|
}
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
const journalEntry = {
|
|
1501
|
-
version: 1,
|
|
1502
|
-
createdAt: Date.now(),
|
|
1503
|
-
path,
|
|
1504
|
-
checksum: computeSha256(content),
|
|
1505
|
-
content,
|
|
1506
|
-
};
|
|
1507
|
-
await fs.writeFile(walPath, JSON.stringify(journalEntry), {
|
|
1508
|
-
encoding: "utf-8",
|
|
1509
|
-
mode: 0o600,
|
|
1510
|
-
});
|
|
1511
|
-
await fs.writeFile(tempPath, content, { encoding: "utf-8", mode: 0o600 });
|
|
1512
|
-
const stats = await fs.stat(tempPath);
|
|
1513
|
-
if (stats.size === 0) {
|
|
1514
|
-
const emptyError = Object.assign(new Error("File written but size is 0"), { code: "EEMPTY" });
|
|
1515
|
-
throw emptyError;
|
|
1516
|
-
}
|
|
1517
|
-
// Retry rename with exponential backoff for Windows EPERM/EBUSY
|
|
1518
|
-
let lastError = null;
|
|
1519
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
1191
|
+
},
|
|
1192
|
+
cleanupWal: async () => {
|
|
1520
1193
|
try {
|
|
1521
|
-
await fs.
|
|
1522
|
-
try {
|
|
1523
|
-
await fs.unlink(resetMarkerPath);
|
|
1524
|
-
}
|
|
1525
|
-
catch {
|
|
1526
|
-
// Best effort cleanup.
|
|
1527
|
-
}
|
|
1528
|
-
lastAccountsSaveTimestamp = Date.now();
|
|
1529
|
-
try {
|
|
1530
|
-
await fs.unlink(walPath);
|
|
1531
|
-
}
|
|
1532
|
-
catch {
|
|
1533
|
-
// Best effort cleanup.
|
|
1534
|
-
}
|
|
1535
|
-
return;
|
|
1194
|
+
await fs.unlink(walPath);
|
|
1536
1195
|
}
|
|
1537
|
-
catch
|
|
1538
|
-
|
|
1539
|
-
if (code === "EPERM" || code === "EBUSY") {
|
|
1540
|
-
lastError = renameError;
|
|
1541
|
-
await new Promise((r) => setTimeout(r, 10 * 2 ** attempt));
|
|
1542
|
-
continue;
|
|
1543
|
-
}
|
|
1544
|
-
throw renameError;
|
|
1196
|
+
catch {
|
|
1197
|
+
// Best effort cleanup.
|
|
1545
1198
|
}
|
|
1546
|
-
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1199
|
+
},
|
|
1200
|
+
cleanupTemp: async (tempPath) => {
|
|
1201
|
+
try {
|
|
1202
|
+
await fs.unlink(tempPath);
|
|
1203
|
+
}
|
|
1204
|
+
catch {
|
|
1205
|
+
// Ignore cleanup failure.
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
1208
|
+
onSaved: () => {
|
|
1209
|
+
lastAccountsSaveTimestamp = Date.now();
|
|
1210
|
+
},
|
|
1211
|
+
logWarn: (message, details) => {
|
|
1212
|
+
log.warn(message, details);
|
|
1213
|
+
},
|
|
1214
|
+
logError: (message, details) => {
|
|
1215
|
+
log.error(message, details);
|
|
1216
|
+
},
|
|
1217
|
+
createStorageError: (error) => {
|
|
1218
|
+
const err = error;
|
|
1219
|
+
const code = err?.code || "UNKNOWN";
|
|
1220
|
+
const hint = formatStorageErrorHint(error, path);
|
|
1221
|
+
return new StorageError(`Failed to save accounts: ${err?.message || "Unknown error"}`, code, path, hint, err instanceof Error ? err : undefined);
|
|
1222
|
+
},
|
|
1223
|
+
backupPath: getAccountsBackupPath(path),
|
|
1224
|
+
createTempPath: () => `${path}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`,
|
|
1225
|
+
});
|
|
1568
1226
|
}
|
|
1569
|
-
function
|
|
1227
|
+
function cloneFlaggedStorageForPersistence(storage) {
|
|
1570
1228
|
return {
|
|
1571
|
-
version:
|
|
1229
|
+
version: 1,
|
|
1572
1230
|
accounts: structuredClone(storage?.accounts ?? []),
|
|
1573
|
-
activeIndex: typeof storage?.activeIndex === "number" &&
|
|
1574
|
-
Number.isFinite(storage.activeIndex)
|
|
1575
|
-
? storage.activeIndex
|
|
1576
|
-
: 0,
|
|
1577
|
-
activeIndexByFamily: structuredClone(storage?.activeIndexByFamily ?? {}),
|
|
1578
1231
|
};
|
|
1579
1232
|
}
|
|
1580
1233
|
export async function withAccountStorageTransaction(handler) {
|
|
1581
|
-
return
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
storagePath,
|
|
1586
|
-
active: true,
|
|
1587
|
-
};
|
|
1588
|
-
const current = state.snapshot;
|
|
1589
|
-
const persist = async (storage) => {
|
|
1590
|
-
await saveAccountsUnlocked(storage);
|
|
1591
|
-
state.snapshot = storage;
|
|
1592
|
-
};
|
|
1593
|
-
return transactionSnapshotContext.run(state, () => handler(current, persist));
|
|
1234
|
+
return runWithAccountStorageTransaction(handler, {
|
|
1235
|
+
getStoragePath,
|
|
1236
|
+
loadCurrent: () => loadAccountsInternal(saveAccountsUnlocked),
|
|
1237
|
+
saveAccounts: saveAccountsUnlocked,
|
|
1594
1238
|
});
|
|
1595
1239
|
}
|
|
1596
1240
|
export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
@@ -1602,12 +1246,14 @@ export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
|
1602
1246
|
active: true,
|
|
1603
1247
|
};
|
|
1604
1248
|
const current = state.snapshot;
|
|
1249
|
+
const currentFlagged = await loadFlaggedAccounts();
|
|
1605
1250
|
const persist = async (accountStorage, flaggedStorage) => {
|
|
1606
1251
|
const previousAccounts = cloneAccountStorageForPersistence(state.snapshot);
|
|
1607
1252
|
const nextAccounts = cloneAccountStorageForPersistence(accountStorage);
|
|
1253
|
+
const nextFlagged = cloneFlaggedStorageForPersistence(flaggedStorage);
|
|
1608
1254
|
await saveAccountsUnlocked(nextAccounts);
|
|
1609
1255
|
try {
|
|
1610
|
-
await saveFlaggedAccountsUnlocked(
|
|
1256
|
+
await saveFlaggedAccountsUnlocked(nextFlagged);
|
|
1611
1257
|
state.snapshot = nextAccounts;
|
|
1612
1258
|
}
|
|
1613
1259
|
catch (error) {
|
|
@@ -1626,7 +1272,37 @@ export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
|
1626
1272
|
throw error;
|
|
1627
1273
|
}
|
|
1628
1274
|
};
|
|
1629
|
-
return
|
|
1275
|
+
return runInTransactionSnapshotContext(state, () => handler(current, persist, currentFlagged));
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
export async function withFlaggedStorageTransaction(handler) {
|
|
1279
|
+
return withStorageLock(async () => {
|
|
1280
|
+
const current = await loadFlaggedAccounts();
|
|
1281
|
+
let snapshot = cloneFlaggedStorageForPersistence(current);
|
|
1282
|
+
const persist = async (storage) => {
|
|
1283
|
+
const previousStorage = cloneFlaggedStorageForPersistence(snapshot);
|
|
1284
|
+
const nextStorage = cloneFlaggedStorageForPersistence(storage);
|
|
1285
|
+
try {
|
|
1286
|
+
await saveFlaggedAccountsUnlocked(nextStorage);
|
|
1287
|
+
snapshot = nextStorage;
|
|
1288
|
+
}
|
|
1289
|
+
catch (error) {
|
|
1290
|
+
try {
|
|
1291
|
+
await saveFlaggedAccountsUnlocked(previousStorage);
|
|
1292
|
+
snapshot = previousStorage;
|
|
1293
|
+
}
|
|
1294
|
+
catch (rollbackError) {
|
|
1295
|
+
const combinedError = new AggregateError([error, rollbackError], "Flagged save failed and flagged storage rollback also failed");
|
|
1296
|
+
log.error("Failed to rollback flagged storage after flagged save failure", {
|
|
1297
|
+
error: String(error),
|
|
1298
|
+
rollbackError: String(rollbackError),
|
|
1299
|
+
});
|
|
1300
|
+
throw combinedError;
|
|
1301
|
+
}
|
|
1302
|
+
throw error;
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
return handler(structuredClone(snapshot), persist);
|
|
1630
1306
|
});
|
|
1631
1307
|
}
|
|
1632
1308
|
/**
|
|
@@ -1637,8 +1313,10 @@ export async function withAccountAndFlaggedStorageTransaction(handler) {
|
|
|
1637
1313
|
* @throws StorageError with platform-aware hints on failure
|
|
1638
1314
|
*/
|
|
1639
1315
|
export async function saveAccounts(storage) {
|
|
1640
|
-
return
|
|
1641
|
-
|
|
1316
|
+
return saveAccountsEntry({
|
|
1317
|
+
storage,
|
|
1318
|
+
withStorageLock,
|
|
1319
|
+
saveUnlocked: saveAccountsUnlocked,
|
|
1642
1320
|
});
|
|
1643
1321
|
}
|
|
1644
1322
|
/**
|
|
@@ -1646,266 +1324,75 @@ export async function saveAccounts(storage) {
|
|
|
1646
1324
|
* Silently ignores if file doesn't exist.
|
|
1647
1325
|
*/
|
|
1648
1326
|
export async function clearAccounts() {
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
const code = error.code;
|
|
1661
|
-
if (code !== "ENOENT") {
|
|
1662
|
-
log.error("Failed to clear account storage artifact", {
|
|
1663
|
-
path: targetPath,
|
|
1664
|
-
error: String(error),
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
};
|
|
1669
|
-
try {
|
|
1670
|
-
await Promise.all([
|
|
1671
|
-
clearPath(path),
|
|
1672
|
-
clearPath(walPath),
|
|
1673
|
-
...backupPaths.map(clearPath),
|
|
1674
|
-
]);
|
|
1675
|
-
}
|
|
1676
|
-
catch {
|
|
1677
|
-
// Individual path cleanup is already best-effort with per-artifact logging.
|
|
1678
|
-
}
|
|
1327
|
+
const path = getStoragePath();
|
|
1328
|
+
return clearAccountsEntry({
|
|
1329
|
+
path,
|
|
1330
|
+
withStorageLock,
|
|
1331
|
+
resetMarkerPath: getIntentionalResetMarkerPath(path),
|
|
1332
|
+
walPath: getAccountsWalPath(path),
|
|
1333
|
+
getBackupPaths: () => getAccountsBackupRecoveryCandidatesWithDiscovery(path),
|
|
1334
|
+
clearAccountStorageArtifacts,
|
|
1335
|
+
logError: (message, details) => {
|
|
1336
|
+
log.error(message, details);
|
|
1337
|
+
},
|
|
1679
1338
|
});
|
|
1680
1339
|
}
|
|
1681
|
-
function normalizeFlaggedStorage(data) {
|
|
1682
|
-
if (!isRecord(data) || data.version !== 1 || !Array.isArray(data.accounts)) {
|
|
1683
|
-
return { version: 1, accounts: [] };
|
|
1684
|
-
}
|
|
1685
|
-
const byRefreshToken = new Map();
|
|
1686
|
-
for (const rawAccount of data.accounts) {
|
|
1687
|
-
if (!isRecord(rawAccount))
|
|
1688
|
-
continue;
|
|
1689
|
-
const refreshToken = typeof rawAccount.refreshToken === "string"
|
|
1690
|
-
? rawAccount.refreshToken.trim()
|
|
1691
|
-
: "";
|
|
1692
|
-
if (!refreshToken)
|
|
1693
|
-
continue;
|
|
1694
|
-
const flaggedAt = typeof rawAccount.flaggedAt === "number"
|
|
1695
|
-
? rawAccount.flaggedAt
|
|
1696
|
-
: Date.now();
|
|
1697
|
-
const isAccountIdSource = (value) => value === "token" ||
|
|
1698
|
-
value === "id_token" ||
|
|
1699
|
-
value === "org" ||
|
|
1700
|
-
value === "manual";
|
|
1701
|
-
const isSwitchReason = (value) => value === "rate-limit" ||
|
|
1702
|
-
value === "initial" ||
|
|
1703
|
-
value === "rotation" ||
|
|
1704
|
-
value === "best" ||
|
|
1705
|
-
value === "restore";
|
|
1706
|
-
const isCooldownReason = (value) => value === "auth-failure" ||
|
|
1707
|
-
value === "network-error" ||
|
|
1708
|
-
value === "rate-limit";
|
|
1709
|
-
let rateLimitResetTimes;
|
|
1710
|
-
if (isRecord(rawAccount.rateLimitResetTimes)) {
|
|
1711
|
-
const normalizedRateLimits = {};
|
|
1712
|
-
for (const [key, value] of Object.entries(rawAccount.rateLimitResetTimes)) {
|
|
1713
|
-
if (typeof value === "number") {
|
|
1714
|
-
normalizedRateLimits[key] = value;
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
if (Object.keys(normalizedRateLimits).length > 0) {
|
|
1718
|
-
rateLimitResetTimes = normalizedRateLimits;
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
const accountIdSource = isAccountIdSource(rawAccount.accountIdSource)
|
|
1722
|
-
? rawAccount.accountIdSource
|
|
1723
|
-
: undefined;
|
|
1724
|
-
const lastSwitchReason = isSwitchReason(rawAccount.lastSwitchReason)
|
|
1725
|
-
? rawAccount.lastSwitchReason
|
|
1726
|
-
: undefined;
|
|
1727
|
-
const cooldownReason = isCooldownReason(rawAccount.cooldownReason)
|
|
1728
|
-
? rawAccount.cooldownReason
|
|
1729
|
-
: undefined;
|
|
1730
|
-
const normalized = {
|
|
1731
|
-
refreshToken,
|
|
1732
|
-
addedAt: typeof rawAccount.addedAt === "number" ? rawAccount.addedAt : flaggedAt,
|
|
1733
|
-
lastUsed: typeof rawAccount.lastUsed === "number"
|
|
1734
|
-
? rawAccount.lastUsed
|
|
1735
|
-
: flaggedAt,
|
|
1736
|
-
accountId: typeof rawAccount.accountId === "string"
|
|
1737
|
-
? rawAccount.accountId
|
|
1738
|
-
: undefined,
|
|
1739
|
-
accountIdSource,
|
|
1740
|
-
accountLabel: typeof rawAccount.accountLabel === "string"
|
|
1741
|
-
? rawAccount.accountLabel
|
|
1742
|
-
: undefined,
|
|
1743
|
-
email: typeof rawAccount.email === "string" ? rawAccount.email : undefined,
|
|
1744
|
-
enabled: typeof rawAccount.enabled === "boolean"
|
|
1745
|
-
? rawAccount.enabled
|
|
1746
|
-
: undefined,
|
|
1747
|
-
lastSwitchReason,
|
|
1748
|
-
rateLimitResetTimes,
|
|
1749
|
-
coolingDownUntil: typeof rawAccount.coolingDownUntil === "number"
|
|
1750
|
-
? rawAccount.coolingDownUntil
|
|
1751
|
-
: undefined,
|
|
1752
|
-
cooldownReason,
|
|
1753
|
-
flaggedAt,
|
|
1754
|
-
flaggedReason: typeof rawAccount.flaggedReason === "string"
|
|
1755
|
-
? rawAccount.flaggedReason
|
|
1756
|
-
: undefined,
|
|
1757
|
-
lastError: typeof rawAccount.lastError === "string"
|
|
1758
|
-
? rawAccount.lastError
|
|
1759
|
-
: undefined,
|
|
1760
|
-
};
|
|
1761
|
-
byRefreshToken.set(refreshToken, normalized);
|
|
1762
|
-
}
|
|
1763
|
-
return {
|
|
1764
|
-
version: 1,
|
|
1765
|
-
accounts: Array.from(byRefreshToken.values()),
|
|
1766
|
-
};
|
|
1767
|
-
}
|
|
1768
1340
|
export async function loadFlaggedAccounts() {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
error: String(error),
|
|
1787
|
-
});
|
|
1788
|
-
return empty;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
const legacyPath = getLegacyFlaggedAccountsPath();
|
|
1792
|
-
if (!existsSync(legacyPath)) {
|
|
1793
|
-
return empty;
|
|
1794
|
-
}
|
|
1795
|
-
try {
|
|
1796
|
-
const legacyContent = await fs.readFile(legacyPath, "utf-8");
|
|
1797
|
-
const legacyData = JSON.parse(legacyContent);
|
|
1798
|
-
const migrated = normalizeFlaggedStorage(legacyData);
|
|
1799
|
-
if (migrated.accounts.length > 0) {
|
|
1800
|
-
await saveFlaggedAccounts(migrated);
|
|
1801
|
-
}
|
|
1802
|
-
try {
|
|
1803
|
-
await fs.unlink(legacyPath);
|
|
1804
|
-
}
|
|
1805
|
-
catch {
|
|
1806
|
-
// Best effort cleanup.
|
|
1807
|
-
}
|
|
1808
|
-
log.info("Migrated legacy flagged account storage", {
|
|
1809
|
-
from: legacyPath,
|
|
1810
|
-
to: path,
|
|
1811
|
-
accounts: migrated.accounts.length,
|
|
1812
|
-
});
|
|
1813
|
-
return migrated;
|
|
1814
|
-
}
|
|
1815
|
-
catch (error) {
|
|
1816
|
-
log.error("Failed to migrate legacy flagged account storage", {
|
|
1817
|
-
from: legacyPath,
|
|
1818
|
-
to: path,
|
|
1819
|
-
error: String(error),
|
|
1820
|
-
});
|
|
1821
|
-
return empty;
|
|
1822
|
-
}
|
|
1341
|
+
return loadFlaggedAccountsEntry({
|
|
1342
|
+
getFlaggedAccountsPath,
|
|
1343
|
+
getLegacyFlaggedAccountsPath,
|
|
1344
|
+
getIntentionalResetMarkerPath,
|
|
1345
|
+
normalizeFlaggedStorage: (data) => normalizeFlaggedStorage(data, {
|
|
1346
|
+
isRecord,
|
|
1347
|
+
now: () => Date.now(),
|
|
1348
|
+
}),
|
|
1349
|
+
saveFlaggedAccounts,
|
|
1350
|
+
loadFlaggedAccountsState,
|
|
1351
|
+
logError: (message, details) => {
|
|
1352
|
+
log.error(message, details);
|
|
1353
|
+
},
|
|
1354
|
+
logInfo: (message, details) => {
|
|
1355
|
+
log.info(message, details);
|
|
1356
|
+
},
|
|
1357
|
+
});
|
|
1823
1358
|
}
|
|
1824
1359
|
async function saveFlaggedAccountsUnlocked(storage) {
|
|
1825
1360
|
const path = getFlaggedAccountsPath();
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
const content = JSON.stringify(normalizeFlaggedStorage(storage), null, 2);
|
|
1845
|
-
await fs.writeFile(tempPath, content, { encoding: "utf-8", mode: 0o600 });
|
|
1846
|
-
await renameFileWithRetry(tempPath, path);
|
|
1847
|
-
try {
|
|
1848
|
-
await fs.unlink(markerPath);
|
|
1849
|
-
}
|
|
1850
|
-
catch {
|
|
1851
|
-
// Best effort cleanup.
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
catch (error) {
|
|
1855
|
-
try {
|
|
1856
|
-
await fs.unlink(tempPath);
|
|
1857
|
-
}
|
|
1858
|
-
catch {
|
|
1859
|
-
// Ignore cleanup failures.
|
|
1860
|
-
}
|
|
1861
|
-
log.error("Failed to save flagged account storage", {
|
|
1862
|
-
path,
|
|
1863
|
-
error: String(error),
|
|
1864
|
-
});
|
|
1865
|
-
throw error;
|
|
1866
|
-
}
|
|
1361
|
+
return saveFlaggedAccountsUnlockedToDisk(storage, {
|
|
1362
|
+
path,
|
|
1363
|
+
markerPath: getIntentionalResetMarkerPath(path),
|
|
1364
|
+
normalizeFlaggedStorage: (data) => normalizeFlaggedStorage(data, {
|
|
1365
|
+
isRecord,
|
|
1366
|
+
now: () => Date.now(),
|
|
1367
|
+
}),
|
|
1368
|
+
copyFileWithRetry,
|
|
1369
|
+
renameFileWithRetry,
|
|
1370
|
+
logWarn: (message, details) => {
|
|
1371
|
+
log.warn(message, details);
|
|
1372
|
+
},
|
|
1373
|
+
logError: (message, details) => {
|
|
1374
|
+
log.error(message, details);
|
|
1375
|
+
},
|
|
1376
|
+
});
|
|
1867
1377
|
}
|
|
1868
1378
|
export async function saveFlaggedAccounts(storage) {
|
|
1869
|
-
return
|
|
1870
|
-
|
|
1379
|
+
return saveFlaggedAccountsEntry({
|
|
1380
|
+
storage,
|
|
1381
|
+
withStorageLock,
|
|
1382
|
+
saveUnlocked: saveFlaggedAccountsUnlocked,
|
|
1871
1383
|
});
|
|
1872
1384
|
}
|
|
1873
1385
|
export async function clearFlaggedAccounts() {
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
log.error("Failed to write flagged reset marker", {
|
|
1885
|
-
path,
|
|
1886
|
-
markerPath,
|
|
1887
|
-
error: String(error),
|
|
1888
|
-
});
|
|
1889
|
-
throw error;
|
|
1890
|
-
}
|
|
1891
|
-
const backupPaths = await getAccountsBackupRecoveryCandidatesWithDiscovery(path);
|
|
1892
|
-
for (const candidate of [path, ...backupPaths, markerPath]) {
|
|
1893
|
-
try {
|
|
1894
|
-
await fs.unlink(candidate);
|
|
1895
|
-
}
|
|
1896
|
-
catch (error) {
|
|
1897
|
-
const code = error.code;
|
|
1898
|
-
if (code !== "ENOENT") {
|
|
1899
|
-
log.error("Failed to clear flagged account storage", {
|
|
1900
|
-
path: candidate,
|
|
1901
|
-
error: String(error),
|
|
1902
|
-
});
|
|
1903
|
-
if (candidate === path) {
|
|
1904
|
-
throw error;
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1386
|
+
const path = getFlaggedAccountsPath();
|
|
1387
|
+
return clearFlaggedAccountsEntry({
|
|
1388
|
+
path,
|
|
1389
|
+
withStorageLock,
|
|
1390
|
+
markerPath: getIntentionalResetMarkerPath(path),
|
|
1391
|
+
getBackupPaths: () => getAccountsBackupRecoveryCandidatesWithDiscovery(path),
|
|
1392
|
+
clearFlaggedAccountsOnDisk,
|
|
1393
|
+
logError: (message, details) => {
|
|
1394
|
+
log.error(message, details);
|
|
1395
|
+
},
|
|
1909
1396
|
});
|
|
1910
1397
|
}
|
|
1911
1398
|
/**
|
|
@@ -1917,34 +1404,18 @@ export async function clearFlaggedAccounts() {
|
|
|
1917
1404
|
export async function exportAccounts(filePath, force = true, beforeCommit) {
|
|
1918
1405
|
const resolvedPath = resolvePath(filePath);
|
|
1919
1406
|
const currentStoragePath = getStoragePath();
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
}
|
|
1933
|
-
await fs.mkdir(dirname(resolvedPath), { recursive: true });
|
|
1934
|
-
await beforeCommit?.(resolvedPath);
|
|
1935
|
-
if (!force && existsSync(resolvedPath)) {
|
|
1936
|
-
throw new Error(`File already exists: ${resolvedPath}`);
|
|
1937
|
-
}
|
|
1938
|
-
const content = JSON.stringify({
|
|
1939
|
-
version: storage.version,
|
|
1940
|
-
accounts: storage.accounts,
|
|
1941
|
-
activeIndex: storage.activeIndex,
|
|
1942
|
-
activeIndexByFamily: storage.activeIndexByFamily,
|
|
1943
|
-
}, null, 2);
|
|
1944
|
-
await fs.writeFile(resolvedPath, content, { encoding: "utf-8", mode: 0o600 });
|
|
1945
|
-
log.info("Exported accounts", {
|
|
1946
|
-
path: resolvedPath,
|
|
1947
|
-
count: storage.accounts.length,
|
|
1407
|
+
await exportAccountsSnapshot({
|
|
1408
|
+
resolvedPath,
|
|
1409
|
+
force,
|
|
1410
|
+
currentStoragePath,
|
|
1411
|
+
transactionState: getTransactionSnapshotState(),
|
|
1412
|
+
readCurrentStorageUnlocked: () => loadAccountsForExport(),
|
|
1413
|
+
readCurrentStorage: () => withStorageLock(() => loadAccountsForExport()),
|
|
1414
|
+
exportAccountsToFile,
|
|
1415
|
+
beforeCommit,
|
|
1416
|
+
logInfo: (message, details) => {
|
|
1417
|
+
log.info(message, details);
|
|
1418
|
+
},
|
|
1948
1419
|
});
|
|
1949
1420
|
}
|
|
1950
1421
|
/**
|
|
@@ -1955,50 +1426,17 @@ export async function exportAccounts(filePath, force = true, beforeCommit) {
|
|
|
1955
1426
|
*/
|
|
1956
1427
|
export async function importAccounts(filePath) {
|
|
1957
1428
|
const resolvedPath = resolvePath(filePath);
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
}
|
|
1970
|
-
const normalized = normalizeAccountStorage(imported);
|
|
1971
|
-
if (!normalized) {
|
|
1972
|
-
throw new Error("Invalid account storage format");
|
|
1973
|
-
}
|
|
1974
|
-
const { imported: importedCount, total, skipped: skippedCount, } = await withAccountStorageTransaction(async (existing, persist) => {
|
|
1975
|
-
const existingAccounts = existing?.accounts ?? [];
|
|
1976
|
-
const existingActiveIndex = existing?.activeIndex ?? 0;
|
|
1977
|
-
const merged = [...existingAccounts, ...normalized.accounts];
|
|
1978
|
-
if (merged.length > ACCOUNT_LIMITS.MAX_ACCOUNTS) {
|
|
1979
|
-
const deduped = deduplicateAccounts(merged);
|
|
1980
|
-
if (deduped.length > ACCOUNT_LIMITS.MAX_ACCOUNTS) {
|
|
1981
|
-
throw new Error(`Import would exceed maximum of ${ACCOUNT_LIMITS.MAX_ACCOUNTS} accounts (would have ${deduped.length})`);
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
const deduplicatedAccounts = deduplicateAccounts(merged);
|
|
1985
|
-
const newStorage = {
|
|
1986
|
-
version: 3,
|
|
1987
|
-
accounts: deduplicatedAccounts,
|
|
1988
|
-
activeIndex: existingActiveIndex,
|
|
1989
|
-
activeIndexByFamily: existing?.activeIndexByFamily,
|
|
1990
|
-
};
|
|
1991
|
-
await persist(newStorage);
|
|
1992
|
-
const imported = deduplicatedAccounts.length - existingAccounts.length;
|
|
1993
|
-
const skipped = normalized.accounts.length - imported;
|
|
1994
|
-
return { imported, total: deduplicatedAccounts.length, skipped };
|
|
1995
|
-
});
|
|
1996
|
-
log.info("Imported accounts", {
|
|
1997
|
-
path: resolvedPath,
|
|
1998
|
-
imported: importedCount,
|
|
1999
|
-
skipped: skippedCount,
|
|
2000
|
-
total,
|
|
1429
|
+
return importAccountsSnapshot({
|
|
1430
|
+
resolvedPath,
|
|
1431
|
+
readImportFile,
|
|
1432
|
+
normalizeAccountStorage,
|
|
1433
|
+
withAccountStorageTransaction,
|
|
1434
|
+
mergeImportedAccounts,
|
|
1435
|
+
maxAccounts: ACCOUNT_LIMITS.MAX_ACCOUNTS,
|
|
1436
|
+
deduplicateAccounts,
|
|
1437
|
+
logInfo: (message, details) => {
|
|
1438
|
+
log.info(message, details);
|
|
1439
|
+
},
|
|
2001
1440
|
});
|
|
2002
|
-
return { imported: importedCount, total, skipped: skippedCount };
|
|
2003
1441
|
}
|
|
2004
1442
|
//# sourceMappingURL=storage.js.map
|