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.
Files changed (651) hide show
  1. package/README.md +49 -32
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +929 -1712
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/accounts.d.ts +10 -0
  6. package/dist/lib/accounts.d.ts.map +1 -1
  7. package/dist/lib/accounts.js +312 -69
  8. package/dist/lib/accounts.js.map +1 -1
  9. package/dist/lib/auth/index.d.ts +2 -0
  10. package/dist/lib/auth/index.d.ts.map +1 -0
  11. package/dist/lib/auth/index.js +2 -0
  12. package/dist/lib/auth/index.js.map +1 -0
  13. package/dist/lib/circuit-breaker.d.ts +4 -0
  14. package/dist/lib/circuit-breaker.d.ts.map +1 -1
  15. package/dist/lib/circuit-breaker.js +33 -1
  16. package/dist/lib/circuit-breaker.js.map +1 -1
  17. package/dist/lib/codex-cli/index.d.ts +5 -0
  18. package/dist/lib/codex-cli/index.d.ts.map +1 -0
  19. package/dist/lib/codex-cli/index.js +5 -0
  20. package/dist/lib/codex-cli/index.js.map +1 -0
  21. package/dist/lib/codex-manager/auth-commands.d.ts +72 -0
  22. package/dist/lib/codex-manager/auth-commands.d.ts.map +1 -0
  23. package/dist/lib/codex-manager/auth-commands.js +643 -0
  24. package/dist/lib/codex-manager/auth-commands.js.map +1 -0
  25. package/dist/lib/codex-manager/backend-category-entry.d.ts +103 -0
  26. package/dist/lib/codex-manager/backend-category-entry.d.ts.map +1 -0
  27. package/dist/lib/codex-manager/backend-category-entry.js +23 -0
  28. package/dist/lib/codex-manager/backend-category-entry.js.map +1 -0
  29. package/dist/lib/codex-manager/backend-category-helpers.d.ts +10 -0
  30. package/dist/lib/codex-manager/backend-category-helpers.d.ts.map +1 -0
  31. package/dist/lib/codex-manager/backend-category-helpers.js +29 -0
  32. package/dist/lib/codex-manager/backend-category-helpers.js.map +1 -0
  33. package/dist/lib/codex-manager/backend-category-prompt.d.ts +55 -0
  34. package/dist/lib/codex-manager/backend-category-prompt.d.ts.map +1 -0
  35. package/dist/lib/codex-manager/backend-category-prompt.js +178 -0
  36. package/dist/lib/codex-manager/backend-category-prompt.js.map +1 -0
  37. package/dist/lib/codex-manager/backend-settings-controller.d.ts +11 -0
  38. package/dist/lib/codex-manager/backend-settings-controller.d.ts.map +1 -0
  39. package/dist/lib/codex-manager/backend-settings-controller.js +14 -0
  40. package/dist/lib/codex-manager/backend-settings-controller.js.map +1 -0
  41. package/dist/lib/codex-manager/backend-settings-entry.d.ts +20 -0
  42. package/dist/lib/codex-manager/backend-settings-entry.d.ts.map +1 -0
  43. package/dist/lib/codex-manager/backend-settings-entry.js +12 -0
  44. package/dist/lib/codex-manager/backend-settings-entry.js.map +1 -0
  45. package/dist/lib/codex-manager/backend-settings-helpers.d.ts +16 -0
  46. package/dist/lib/codex-manager/backend-settings-helpers.d.ts.map +1 -0
  47. package/dist/lib/codex-manager/backend-settings-helpers.js +102 -0
  48. package/dist/lib/codex-manager/backend-settings-helpers.js.map +1 -0
  49. package/dist/lib/codex-manager/backend-settings-prompt.d.ts +49 -0
  50. package/dist/lib/codex-manager/backend-settings-prompt.d.ts.map +1 -0
  51. package/dist/lib/codex-manager/backend-settings-prompt.js +121 -0
  52. package/dist/lib/codex-manager/backend-settings-prompt.js.map +1 -0
  53. package/dist/lib/codex-manager/backend-settings-schema.d.ts +55 -0
  54. package/dist/lib/codex-manager/backend-settings-schema.d.ts.map +1 -0
  55. package/dist/lib/codex-manager/backend-settings-schema.js +264 -0
  56. package/dist/lib/codex-manager/backend-settings-schema.js.map +1 -0
  57. package/dist/lib/codex-manager/behavior-settings-panel.d.ts +33 -0
  58. package/dist/lib/codex-manager/behavior-settings-panel.d.ts.map +1 -0
  59. package/dist/lib/codex-manager/behavior-settings-panel.js +180 -0
  60. package/dist/lib/codex-manager/behavior-settings-panel.js.map +1 -0
  61. package/dist/lib/codex-manager/commands/best.d.ts +81 -0
  62. package/dist/lib/codex-manager/commands/best.d.ts.map +1 -0
  63. package/dist/lib/codex-manager/commands/best.js +223 -0
  64. package/dist/lib/codex-manager/commands/best.js.map +1 -0
  65. package/dist/lib/codex-manager/commands/check.d.ts +7 -0
  66. package/dist/lib/codex-manager/commands/check.d.ts.map +1 -0
  67. package/dist/lib/codex-manager/commands/check.js +5 -0
  68. package/dist/lib/codex-manager/commands/check.js.map +1 -0
  69. package/dist/lib/codex-manager/commands/config-explain.d.ts +7 -0
  70. package/dist/lib/codex-manager/commands/config-explain.d.ts.map +1 -0
  71. package/dist/lib/codex-manager/commands/config-explain.js +31 -0
  72. package/dist/lib/codex-manager/commands/config-explain.js.map +1 -0
  73. package/dist/lib/codex-manager/commands/debug-bundle.d.ts +28 -0
  74. package/dist/lib/codex-manager/commands/debug-bundle.d.ts.map +1 -0
  75. package/dist/lib/codex-manager/commands/debug-bundle.js +62 -0
  76. package/dist/lib/codex-manager/commands/debug-bundle.js.map +1 -0
  77. package/dist/lib/codex-manager/commands/doctor.d.ts +85 -0
  78. package/dist/lib/codex-manager/commands/doctor.d.ts.map +1 -0
  79. package/dist/lib/codex-manager/commands/doctor.js +424 -0
  80. package/dist/lib/codex-manager/commands/doctor.js.map +1 -0
  81. package/dist/lib/codex-manager/commands/fix.d.ts +92 -0
  82. package/dist/lib/codex-manager/commands/fix.d.ts.map +1 -0
  83. package/dist/lib/codex-manager/commands/fix.js +347 -0
  84. package/dist/lib/codex-manager/commands/fix.js.map +1 -0
  85. package/dist/lib/codex-manager/commands/forecast.d.ts +71 -0
  86. package/dist/lib/codex-manager/commands/forecast.d.ts.map +1 -0
  87. package/dist/lib/codex-manager/commands/forecast.js +371 -0
  88. package/dist/lib/codex-manager/commands/forecast.js.map +1 -0
  89. package/dist/lib/codex-manager/commands/init-config.d.ts +15 -0
  90. package/dist/lib/codex-manager/commands/init-config.d.ts.map +1 -0
  91. package/dist/lib/codex-manager/commands/init-config.js +95 -0
  92. package/dist/lib/codex-manager/commands/init-config.js.map +1 -0
  93. package/dist/lib/codex-manager/commands/report.d.ts +25 -0
  94. package/dist/lib/codex-manager/commands/report.d.ts.map +1 -0
  95. package/dist/lib/codex-manager/commands/report.js +399 -0
  96. package/dist/lib/codex-manager/commands/report.js.map +1 -0
  97. package/dist/lib/codex-manager/commands/status.d.ts +23 -0
  98. package/dist/lib/codex-manager/commands/status.d.ts.map +1 -0
  99. package/dist/lib/codex-manager/commands/status.js +50 -0
  100. package/dist/lib/codex-manager/commands/status.js.map +1 -0
  101. package/dist/lib/codex-manager/commands/switch.d.ts +22 -0
  102. package/dist/lib/codex-manager/commands/switch.d.ts.map +1 -0
  103. package/dist/lib/codex-manager/commands/switch.js +41 -0
  104. package/dist/lib/codex-manager/commands/switch.js.map +1 -0
  105. package/dist/lib/codex-manager/commands/verify-flagged.d.ts +69 -0
  106. package/dist/lib/codex-manager/commands/verify-flagged.d.ts.map +1 -0
  107. package/dist/lib/codex-manager/commands/verify-flagged.js +251 -0
  108. package/dist/lib/codex-manager/commands/verify-flagged.js.map +1 -0
  109. package/dist/lib/codex-manager/dashboard-display-panel.d.ts +40 -0
  110. package/dist/lib/codex-manager/dashboard-display-panel.d.ts.map +1 -0
  111. package/dist/lib/codex-manager/dashboard-display-panel.js +187 -0
  112. package/dist/lib/codex-manager/dashboard-display-panel.js.map +1 -0
  113. package/dist/lib/codex-manager/dashboard-formatters.d.ts +6 -0
  114. package/dist/lib/codex-manager/dashboard-formatters.d.ts.map +1 -0
  115. package/dist/lib/codex-manager/dashboard-formatters.js +19 -0
  116. package/dist/lib/codex-manager/dashboard-formatters.js.map +1 -0
  117. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts +12 -0
  118. package/dist/lib/codex-manager/dashboard-settings-controller.d.ts.map +1 -0
  119. package/dist/lib/codex-manager/dashboard-settings-controller.js +17 -0
  120. package/dist/lib/codex-manager/dashboard-settings-controller.js.map +1 -0
  121. package/dist/lib/codex-manager/dashboard-settings-data.d.ts +10 -0
  122. package/dist/lib/codex-manager/dashboard-settings-data.d.ts.map +1 -0
  123. package/dist/lib/codex-manager/dashboard-settings-data.js +95 -0
  124. package/dist/lib/codex-manager/dashboard-settings-data.js.map +1 -0
  125. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts +22 -0
  126. package/dist/lib/codex-manager/dashboard-settings-entry.d.ts.map +1 -0
  127. package/dist/lib/codex-manager/dashboard-settings-entry.js +13 -0
  128. package/dist/lib/codex-manager/dashboard-settings-entry.js.map +1 -0
  129. package/dist/lib/codex-manager/experimental-settings-entry.d.ts +7 -0
  130. package/dist/lib/codex-manager/experimental-settings-entry.d.ts.map +1 -0
  131. package/dist/lib/codex-manager/experimental-settings-entry.js +30 -0
  132. package/dist/lib/codex-manager/experimental-settings-entry.js.map +1 -0
  133. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts +66 -0
  134. package/dist/lib/codex-manager/experimental-settings-prompt.d.ts.map +1 -0
  135. package/dist/lib/codex-manager/experimental-settings-prompt.js +221 -0
  136. package/dist/lib/codex-manager/experimental-settings-prompt.js.map +1 -0
  137. package/dist/lib/codex-manager/experimental-settings-schema.d.ts +23 -0
  138. package/dist/lib/codex-manager/experimental-settings-schema.d.ts.map +1 -0
  139. package/dist/lib/codex-manager/experimental-settings-schema.js +34 -0
  140. package/dist/lib/codex-manager/experimental-settings-schema.js.map +1 -0
  141. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts +30 -0
  142. package/dist/lib/codex-manager/experimental-sync-target-entry.d.ts.map +1 -0
  143. package/dist/lib/codex-manager/experimental-sync-target-entry.js +16 -0
  144. package/dist/lib/codex-manager/experimental-sync-target-entry.js.map +1 -0
  145. package/dist/lib/codex-manager/experimental-sync-target.d.ts +23 -0
  146. package/dist/lib/codex-manager/experimental-sync-target.d.ts.map +1 -0
  147. package/dist/lib/codex-manager/experimental-sync-target.js +39 -0
  148. package/dist/lib/codex-manager/experimental-sync-target.js.map +1 -0
  149. package/dist/lib/codex-manager/forecast-report-commands.d.ts +54 -0
  150. package/dist/lib/codex-manager/forecast-report-commands.d.ts.map +1 -0
  151. package/dist/lib/codex-manager/forecast-report-commands.js +443 -0
  152. package/dist/lib/codex-manager/forecast-report-commands.js.map +1 -0
  153. package/dist/lib/codex-manager/help.d.ts +36 -0
  154. package/dist/lib/codex-manager/help.d.ts.map +1 -0
  155. package/dist/lib/codex-manager/help.js +125 -0
  156. package/dist/lib/codex-manager/help.js.map +1 -0
  157. package/dist/lib/codex-manager/repair-commands.d.ts +70 -0
  158. package/dist/lib/codex-manager/repair-commands.d.ts.map +1 -0
  159. package/dist/lib/codex-manager/repair-commands.js +1671 -0
  160. package/dist/lib/codex-manager/repair-commands.js.map +1 -0
  161. package/dist/lib/codex-manager/settings-hub-entry.d.ts +30 -0
  162. package/dist/lib/codex-manager/settings-hub-entry.d.ts.map +1 -0
  163. package/dist/lib/codex-manager/settings-hub-entry.js +11 -0
  164. package/dist/lib/codex-manager/settings-hub-entry.js.map +1 -0
  165. package/dist/lib/codex-manager/settings-hub-menu.d.ts +30 -0
  166. package/dist/lib/codex-manager/settings-hub-menu.d.ts.map +1 -0
  167. package/dist/lib/codex-manager/settings-hub-menu.js +37 -0
  168. package/dist/lib/codex-manager/settings-hub-menu.js.map +1 -0
  169. package/dist/lib/codex-manager/settings-hub-prompt.d.ts +25 -0
  170. package/dist/lib/codex-manager/settings-hub-prompt.d.ts.map +1 -0
  171. package/dist/lib/codex-manager/settings-hub-prompt.js +23 -0
  172. package/dist/lib/codex-manager/settings-hub-prompt.js.map +1 -0
  173. package/dist/lib/codex-manager/settings-hub.d.ts +10 -29
  174. package/dist/lib/codex-manager/settings-hub.d.ts.map +1 -1
  175. package/dist/lib/codex-manager/settings-hub.js +260 -2168
  176. package/dist/lib/codex-manager/settings-hub.js.map +1 -1
  177. package/dist/lib/codex-manager/settings-panels.d.ts +56 -0
  178. package/dist/lib/codex-manager/settings-panels.d.ts.map +1 -0
  179. package/dist/lib/codex-manager/settings-panels.js +73 -0
  180. package/dist/lib/codex-manager/settings-panels.js.map +1 -0
  181. package/dist/lib/codex-manager/settings-persist-utils.d.ts +9 -0
  182. package/dist/lib/codex-manager/settings-persist-utils.d.ts.map +1 -0
  183. package/dist/lib/codex-manager/settings-persist-utils.js +33 -0
  184. package/dist/lib/codex-manager/settings-persist-utils.js.map +1 -0
  185. package/dist/lib/codex-manager/settings-preview.d.ts +12 -0
  186. package/dist/lib/codex-manager/settings-preview.d.ts.map +1 -0
  187. package/dist/lib/codex-manager/settings-preview.js +118 -0
  188. package/dist/lib/codex-manager/settings-preview.js.map +1 -0
  189. package/dist/lib/codex-manager/settings-write-queue.d.ts +14 -0
  190. package/dist/lib/codex-manager/settings-write-queue.d.ts.map +1 -0
  191. package/dist/lib/codex-manager/settings-write-queue.js +88 -0
  192. package/dist/lib/codex-manager/settings-write-queue.js.map +1 -0
  193. package/dist/lib/codex-manager/statusline-order.d.ts +3 -0
  194. package/dist/lib/codex-manager/statusline-order.d.ts.map +1 -0
  195. package/dist/lib/codex-manager/statusline-order.js +17 -0
  196. package/dist/lib/codex-manager/statusline-order.js.map +1 -0
  197. package/dist/lib/codex-manager/statusline-settings-panel.d.ts +40 -0
  198. package/dist/lib/codex-manager/statusline-settings-panel.d.ts.map +1 -0
  199. package/dist/lib/codex-manager/statusline-settings-panel.js +167 -0
  200. package/dist/lib/codex-manager/statusline-settings-panel.js.map +1 -0
  201. package/dist/lib/codex-manager/theme-settings-panel.d.ts +26 -0
  202. package/dist/lib/codex-manager/theme-settings-panel.d.ts.map +1 -0
  203. package/dist/lib/codex-manager/theme-settings-panel.js +131 -0
  204. package/dist/lib/codex-manager/theme-settings-panel.js.map +1 -0
  205. package/dist/lib/codex-manager/unified-settings-controller.d.ts +27 -0
  206. package/dist/lib/codex-manager/unified-settings-controller.d.ts.map +1 -0
  207. package/dist/lib/codex-manager/unified-settings-controller.js +50 -0
  208. package/dist/lib/codex-manager/unified-settings-controller.js.map +1 -0
  209. package/dist/lib/codex-manager/unified-settings-entry.d.ts +6 -0
  210. package/dist/lib/codex-manager/unified-settings-entry.d.ts.map +1 -0
  211. package/dist/lib/codex-manager/unified-settings-entry.js +23 -0
  212. package/dist/lib/codex-manager/unified-settings-entry.js.map +1 -0
  213. package/dist/lib/codex-manager.d.ts.map +1 -1
  214. package/dist/lib/codex-manager.js +441 -2199
  215. package/dist/lib/codex-manager.js.map +1 -1
  216. package/dist/lib/config.d.ts +38 -0
  217. package/dist/lib/config.d.ts.map +1 -1
  218. package/dist/lib/config.js +371 -9
  219. package/dist/lib/config.js.map +1 -1
  220. package/dist/lib/errors.d.ts +9 -0
  221. package/dist/lib/errors.d.ts.map +1 -1
  222. package/dist/lib/errors.js +13 -0
  223. package/dist/lib/errors.js.map +1 -1
  224. package/dist/lib/forecast.d.ts +17 -0
  225. package/dist/lib/forecast.d.ts.map +1 -1
  226. package/dist/lib/forecast.js +33 -6
  227. package/dist/lib/forecast.js.map +1 -1
  228. package/dist/lib/health.d.ts +1 -0
  229. package/dist/lib/health.d.ts.map +1 -1
  230. package/dist/lib/health.js +6 -2
  231. package/dist/lib/health.js.map +1 -1
  232. package/dist/lib/named-backup-export.js +2 -2
  233. package/dist/lib/named-backup-export.js.map +1 -1
  234. package/dist/lib/parallel-probe.d.ts +1 -1
  235. package/dist/lib/parallel-probe.d.ts.map +1 -1
  236. package/dist/lib/parallel-probe.js +5 -2
  237. package/dist/lib/parallel-probe.js.map +1 -1
  238. package/dist/lib/proactive-refresh.d.ts +1 -1
  239. package/dist/lib/proactive-refresh.d.ts.map +1 -1
  240. package/dist/lib/proactive-refresh.js +14 -5
  241. package/dist/lib/proactive-refresh.js.map +1 -1
  242. package/dist/lib/prompts/codex.d.ts +8 -3
  243. package/dist/lib/prompts/codex.d.ts.map +1 -1
  244. package/dist/lib/prompts/codex.js +16 -26
  245. package/dist/lib/prompts/codex.js.map +1 -1
  246. package/dist/lib/refresh-guardian.d.ts +3 -0
  247. package/dist/lib/refresh-guardian.d.ts.map +1 -1
  248. package/dist/lib/refresh-guardian.js +142 -52
  249. package/dist/lib/refresh-guardian.js.map +1 -1
  250. package/dist/lib/request/failover-config.d.ts +4 -0
  251. package/dist/lib/request/failover-config.d.ts.map +1 -0
  252. package/dist/lib/request/failover-config.js +15 -0
  253. package/dist/lib/request/failover-config.js.map +1 -0
  254. package/dist/lib/request/fetch-helpers.d.ts +10 -4
  255. package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
  256. package/dist/lib/request/fetch-helpers.js +77 -21
  257. package/dist/lib/request/fetch-helpers.js.map +1 -1
  258. package/dist/lib/request/helpers/model-map.d.ts +49 -12
  259. package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
  260. package/dist/lib/request/helpers/model-map.js +303 -108
  261. package/dist/lib/request/helpers/model-map.js.map +1 -1
  262. package/dist/lib/request/helpers/tool-utils.d.ts +2 -15
  263. package/dist/lib/request/helpers/tool-utils.d.ts.map +1 -1
  264. package/dist/lib/request/helpers/tool-utils.js +40 -27
  265. package/dist/lib/request/helpers/tool-utils.js.map +1 -1
  266. package/dist/lib/request/index.d.ts +5 -0
  267. package/dist/lib/request/index.d.ts.map +1 -0
  268. package/dist/lib/request/index.js +5 -0
  269. package/dist/lib/request/index.js.map +1 -0
  270. package/dist/lib/request/request-init.d.ts +3 -0
  271. package/dist/lib/request/request-init.d.ts.map +1 -0
  272. package/dist/lib/request/request-init.js +50 -0
  273. package/dist/lib/request/request-init.js.map +1 -0
  274. package/dist/lib/request/request-transformer.d.ts +19 -6
  275. package/dist/lib/request/request-transformer.d.ts.map +1 -1
  276. package/dist/lib/request/request-transformer.js +210 -193
  277. package/dist/lib/request/request-transformer.js.map +1 -1
  278. package/dist/lib/request/response-compaction.d.ts +20 -0
  279. package/dist/lib/request/response-compaction.d.ts.map +1 -0
  280. package/dist/lib/request/response-compaction.js +124 -0
  281. package/dist/lib/request/response-compaction.js.map +1 -0
  282. package/dist/lib/request/response-handler.d.ts +2 -0
  283. package/dist/lib/request/response-handler.d.ts.map +1 -1
  284. package/dist/lib/request/response-handler.js +567 -11
  285. package/dist/lib/request/response-handler.js.map +1 -1
  286. package/dist/lib/request/response-metadata.d.ts +3 -0
  287. package/dist/lib/request/response-metadata.d.ts.map +1 -0
  288. package/dist/lib/request/response-metadata.js +61 -0
  289. package/dist/lib/request/response-metadata.js.map +1 -0
  290. package/dist/lib/request/wait-utils.d.ts +14 -0
  291. package/dist/lib/request/wait-utils.d.ts.map +1 -0
  292. package/dist/lib/request/wait-utils.js +44 -0
  293. package/dist/lib/request/wait-utils.js.map +1 -0
  294. package/dist/lib/rotation.d.ts +14 -11
  295. package/dist/lib/rotation.d.ts.map +1 -1
  296. package/dist/lib/rotation.js +36 -30
  297. package/dist/lib/rotation.js.map +1 -1
  298. package/dist/lib/runtime/account-check-helpers.d.ts +8 -0
  299. package/dist/lib/runtime/account-check-helpers.d.ts.map +1 -0
  300. package/dist/lib/runtime/account-check-helpers.js +30 -0
  301. package/dist/lib/runtime/account-check-helpers.js.map +1 -0
  302. package/dist/lib/runtime/account-check-types.d.ts +18 -0
  303. package/dist/lib/runtime/account-check-types.d.ts.map +1 -0
  304. package/dist/lib/runtime/account-check-types.js +12 -0
  305. package/dist/lib/runtime/account-check-types.js.map +1 -0
  306. package/dist/lib/runtime/account-check.d.ts +48 -0
  307. package/dist/lib/runtime/account-check.d.ts.map +1 -0
  308. package/dist/lib/runtime/account-check.js +202 -0
  309. package/dist/lib/runtime/account-check.js.map +1 -0
  310. package/dist/lib/runtime/account-health-check.d.ts +8 -0
  311. package/dist/lib/runtime/account-health-check.d.ts.map +1 -0
  312. package/dist/lib/runtime/account-health-check.js +30 -0
  313. package/dist/lib/runtime/account-health-check.js.map +1 -0
  314. package/dist/lib/runtime/account-manager-cache-entry.d.ts +25 -0
  315. package/dist/lib/runtime/account-manager-cache-entry.d.ts.map +1 -0
  316. package/dist/lib/runtime/account-manager-cache-entry.js +17 -0
  317. package/dist/lib/runtime/account-manager-cache-entry.js.map +1 -0
  318. package/dist/lib/runtime/account-manager-cache.d.ts +25 -0
  319. package/dist/lib/runtime/account-manager-cache.d.ts.map +1 -0
  320. package/dist/lib/runtime/account-manager-cache.js +44 -0
  321. package/dist/lib/runtime/account-manager-cache.js.map +1 -0
  322. package/dist/lib/runtime/account-pool.d.ts +23 -0
  323. package/dist/lib/runtime/account-pool.d.ts.map +1 -0
  324. package/dist/lib/runtime/account-pool.js +138 -0
  325. package/dist/lib/runtime/account-pool.js.map +1 -0
  326. package/dist/lib/runtime/account-scope.d.ts +10 -0
  327. package/dist/lib/runtime/account-scope.d.ts.map +1 -0
  328. package/dist/lib/runtime/account-scope.js +13 -0
  329. package/dist/lib/runtime/account-scope.js.map +1 -0
  330. package/dist/lib/runtime/account-select-event.d.ts +19 -0
  331. package/dist/lib/runtime/account-select-event.d.ts.map +1 -0
  332. package/dist/lib/runtime/account-select-event.js +53 -0
  333. package/dist/lib/runtime/account-select-event.js.map +1 -0
  334. package/dist/lib/runtime/account-selection.d.ts +28 -0
  335. package/dist/lib/runtime/account-selection.d.ts.map +1 -0
  336. package/dist/lib/runtime/account-selection.js +46 -0
  337. package/dist/lib/runtime/account-selection.js.map +1 -0
  338. package/dist/lib/runtime/account-state.d.ts +13 -0
  339. package/dist/lib/runtime/account-state.d.ts.map +1 -0
  340. package/dist/lib/runtime/account-state.js +38 -0
  341. package/dist/lib/runtime/account-state.js.map +1 -0
  342. package/dist/lib/runtime/account-status.d.ts +13 -0
  343. package/dist/lib/runtime/account-status.d.ts.map +1 -0
  344. package/dist/lib/runtime/account-status.js +37 -0
  345. package/dist/lib/runtime/account-status.js.map +1 -0
  346. package/dist/lib/runtime/account-storage-scope-entry.d.ts +26 -0
  347. package/dist/lib/runtime/account-storage-scope-entry.d.ts.map +1 -0
  348. package/dist/lib/runtime/account-storage-scope-entry.js +15 -0
  349. package/dist/lib/runtime/account-storage-scope-entry.js.map +1 -0
  350. package/dist/lib/runtime/auth-facade.d.ts +51 -0
  351. package/dist/lib/runtime/auth-facade.d.ts.map +1 -0
  352. package/dist/lib/runtime/auth-facade.js +48 -0
  353. package/dist/lib/runtime/auth-facade.js.map +1 -0
  354. package/dist/lib/runtime/browser-oauth-flow.d.ts +32 -0
  355. package/dist/lib/runtime/browser-oauth-flow.d.ts.map +1 -0
  356. package/dist/lib/runtime/browser-oauth-flow.js +31 -0
  357. package/dist/lib/runtime/browser-oauth-flow.js.map +1 -0
  358. package/dist/lib/runtime/capability-boost.d.ts +25 -0
  359. package/dist/lib/runtime/capability-boost.d.ts.map +1 -0
  360. package/dist/lib/runtime/capability-boost.js +26 -0
  361. package/dist/lib/runtime/capability-boost.js.map +1 -0
  362. package/dist/lib/runtime/event-handler.d.ts +53 -0
  363. package/dist/lib/runtime/event-handler.d.ts.map +1 -0
  364. package/dist/lib/runtime/event-handler.js +46 -0
  365. package/dist/lib/runtime/event-handler.js.map +1 -0
  366. package/dist/lib/runtime/flagged-verify-types.d.ts +9 -0
  367. package/dist/lib/runtime/flagged-verify-types.d.ts.map +1 -0
  368. package/dist/lib/runtime/flagged-verify-types.js +7 -0
  369. package/dist/lib/runtime/flagged-verify-types.js.map +1 -0
  370. package/dist/lib/runtime/hydrate-emails.d.ts +13 -0
  371. package/dist/lib/runtime/hydrate-emails.d.ts.map +1 -0
  372. package/dist/lib/runtime/hydrate-emails.js +56 -0
  373. package/dist/lib/runtime/hydrate-emails.js.map +1 -0
  374. package/dist/lib/runtime/live-sync-entry.d.ts +36 -0
  375. package/dist/lib/runtime/live-sync-entry.d.ts.map +1 -0
  376. package/dist/lib/runtime/live-sync-entry.js +14 -0
  377. package/dist/lib/runtime/live-sync-entry.js.map +1 -0
  378. package/dist/lib/runtime/live-sync.d.ts +35 -0
  379. package/dist/lib/runtime/live-sync.d.ts.map +1 -0
  380. package/dist/lib/runtime/live-sync.js +65 -0
  381. package/dist/lib/runtime/live-sync.js.map +1 -0
  382. package/dist/lib/runtime/loader-setup.d.ts +9 -0
  383. package/dist/lib/runtime/loader-setup.d.ts.map +1 -0
  384. package/dist/lib/runtime/loader-setup.js +8 -0
  385. package/dist/lib/runtime/loader-setup.js.map +1 -0
  386. package/dist/lib/runtime/login-menu-accounts.d.ts +29 -0
  387. package/dist/lib/runtime/login-menu-accounts.d.ts.map +1 -0
  388. package/dist/lib/runtime/login-menu-accounts.js +33 -0
  389. package/dist/lib/runtime/login-menu-accounts.js.map +1 -0
  390. package/dist/lib/runtime/manual-oauth-flow.d.ts +40 -0
  391. package/dist/lib/runtime/manual-oauth-flow.d.ts.map +1 -0
  392. package/dist/lib/runtime/manual-oauth-flow.js +71 -0
  393. package/dist/lib/runtime/manual-oauth-flow.js.map +1 -0
  394. package/dist/lib/runtime/metrics.d.ts +29 -0
  395. package/dist/lib/runtime/metrics.d.ts.map +1 -0
  396. package/dist/lib/runtime/metrics.js +97 -0
  397. package/dist/lib/runtime/metrics.js.map +1 -0
  398. package/dist/lib/runtime/oauth-browser-flow.d.ts +9 -0
  399. package/dist/lib/runtime/oauth-browser-flow.d.ts.map +1 -0
  400. package/dist/lib/runtime/oauth-browser-flow.js +34 -0
  401. package/dist/lib/runtime/oauth-browser-flow.js.map +1 -0
  402. package/dist/lib/runtime/preemptive-quota.d.ts +13 -0
  403. package/dist/lib/runtime/preemptive-quota.d.ts.map +1 -0
  404. package/dist/lib/runtime/preemptive-quota.js +9 -0
  405. package/dist/lib/runtime/preemptive-quota.js.map +1 -0
  406. package/dist/lib/runtime/quota-headers.d.ts +12 -0
  407. package/dist/lib/runtime/quota-headers.d.ts.map +1 -0
  408. package/dist/lib/runtime/quota-headers.js +131 -0
  409. package/dist/lib/runtime/quota-headers.js.map +1 -0
  410. package/dist/lib/runtime/quota-probe.d.ts +18 -0
  411. package/dist/lib/runtime/quota-probe.d.ts.map +1 -0
  412. package/dist/lib/runtime/quota-probe.js +82 -0
  413. package/dist/lib/runtime/quota-probe.js.map +1 -0
  414. package/dist/lib/runtime/quota-settings.d.ts +14 -0
  415. package/dist/lib/runtime/quota-settings.d.ts.map +1 -0
  416. package/dist/lib/runtime/quota-settings.js +12 -0
  417. package/dist/lib/runtime/quota-settings.js.map +1 -0
  418. package/dist/lib/runtime/refresh-guardian-entry.d.ts +32 -0
  419. package/dist/lib/runtime/refresh-guardian-entry.d.ts.map +1 -0
  420. package/dist/lib/runtime/refresh-guardian-entry.js +12 -0
  421. package/dist/lib/runtime/refresh-guardian-entry.js.map +1 -0
  422. package/dist/lib/runtime/refresh-guardian.d.ts +24 -0
  423. package/dist/lib/runtime/refresh-guardian.d.ts.map +1 -0
  424. package/dist/lib/runtime/refresh-guardian.js +32 -0
  425. package/dist/lib/runtime/refresh-guardian.js.map +1 -0
  426. package/dist/lib/runtime/request-init.d.ts +5 -0
  427. package/dist/lib/runtime/request-init.d.ts.map +1 -0
  428. package/dist/lib/runtime/request-init.js +50 -0
  429. package/dist/lib/runtime/request-init.js.map +1 -0
  430. package/dist/lib/runtime/runtime-services.d.ts +58 -0
  431. package/dist/lib/runtime/runtime-services.d.ts.map +1 -0
  432. package/dist/lib/runtime/runtime-services.js +87 -0
  433. package/dist/lib/runtime/runtime-services.js.map +1 -0
  434. package/dist/lib/runtime/session-affinity-entry.d.ts +30 -0
  435. package/dist/lib/runtime/session-affinity-entry.d.ts.map +1 -0
  436. package/dist/lib/runtime/session-affinity-entry.js +11 -0
  437. package/dist/lib/runtime/session-affinity-entry.js.map +1 -0
  438. package/dist/lib/runtime/session-affinity.d.ts +13 -0
  439. package/dist/lib/runtime/session-affinity.d.ts.map +1 -0
  440. package/dist/lib/runtime/session-affinity.js +17 -0
  441. package/dist/lib/runtime/session-affinity.js.map +1 -0
  442. package/dist/lib/runtime/session-recovery.d.ts +8 -0
  443. package/dist/lib/runtime/session-recovery.d.ts.map +1 -0
  444. package/dist/lib/runtime/session-recovery.js +7 -0
  445. package/dist/lib/runtime/session-recovery.js.map +1 -0
  446. package/dist/lib/runtime/status-marker.d.ts +3 -0
  447. package/dist/lib/runtime/status-marker.d.ts.map +1 -0
  448. package/dist/lib/runtime/status-marker.js +15 -0
  449. package/dist/lib/runtime/status-marker.js.map +1 -0
  450. package/dist/lib/runtime/storage-scope.d.ts +13 -0
  451. package/dist/lib/runtime/storage-scope.d.ts.map +1 -0
  452. package/dist/lib/runtime/storage-scope.js +14 -0
  453. package/dist/lib/runtime/storage-scope.js.map +1 -0
  454. package/dist/lib/runtime/toast.d.ts +16 -0
  455. package/dist/lib/runtime/toast.d.ts.map +1 -0
  456. package/dist/lib/runtime/toast.js +16 -0
  457. package/dist/lib/runtime/toast.js.map +1 -0
  458. package/dist/lib/runtime/ui-runtime-entry.d.ts +7 -0
  459. package/dist/lib/runtime/ui-runtime-entry.d.ts.map +1 -0
  460. package/dist/lib/runtime/ui-runtime-entry.js +4 -0
  461. package/dist/lib/runtime/ui-runtime-entry.js.map +1 -0
  462. package/dist/lib/runtime/ui-runtime.d.ts +9 -0
  463. package/dist/lib/runtime/ui-runtime.d.ts.map +1 -0
  464. package/dist/lib/runtime/ui-runtime.js +23 -0
  465. package/dist/lib/runtime/ui-runtime.js.map +1 -0
  466. package/dist/lib/runtime/verify-flagged.d.ts +44 -0
  467. package/dist/lib/runtime/verify-flagged.d.ts.map +1 -0
  468. package/dist/lib/runtime/verify-flagged.js +84 -0
  469. package/dist/lib/runtime/verify-flagged.js.map +1 -0
  470. package/dist/lib/schemas.d.ts +2 -0
  471. package/dist/lib/schemas.d.ts.map +1 -1
  472. package/dist/lib/schemas.js +2 -0
  473. package/dist/lib/schemas.js.map +1 -1
  474. package/dist/lib/session-affinity.d.ts +10 -0
  475. package/dist/lib/session-affinity.d.ts.map +1 -1
  476. package/dist/lib/session-affinity.js +53 -6
  477. package/dist/lib/session-affinity.js.map +1 -1
  478. package/dist/lib/storage/account-clear-entry.d.ts +16 -0
  479. package/dist/lib/storage/account-clear-entry.d.ts.map +1 -0
  480. package/dist/lib/storage/account-clear-entry.js +12 -0
  481. package/dist/lib/storage/account-clear-entry.js.map +1 -0
  482. package/dist/lib/storage/account-clear.d.ts +8 -0
  483. package/dist/lib/storage/account-clear.d.ts.map +1 -0
  484. package/dist/lib/storage/account-clear.js +29 -0
  485. package/dist/lib/storage/account-clear.js.map +1 -0
  486. package/dist/lib/storage/account-match-utils.d.ts +9 -0
  487. package/dist/lib/storage/account-match-utils.d.ts.map +1 -0
  488. package/dist/lib/storage/account-match-utils.js +45 -0
  489. package/dist/lib/storage/account-match-utils.js.map +1 -0
  490. package/dist/lib/storage/account-persistence.d.ts +3 -0
  491. package/dist/lib/storage/account-persistence.d.ts.map +1 -0
  492. package/dist/lib/storage/account-persistence.js +12 -0
  493. package/dist/lib/storage/account-persistence.js.map +1 -0
  494. package/dist/lib/storage/account-port.d.ts +50 -0
  495. package/dist/lib/storage/account-port.d.ts.map +1 -0
  496. package/dist/lib/storage/account-port.js +43 -0
  497. package/dist/lib/storage/account-port.js.map +1 -0
  498. package/dist/lib/storage/account-save-entry.d.ts +7 -0
  499. package/dist/lib/storage/account-save-entry.d.ts.map +1 -0
  500. package/dist/lib/storage/account-save-entry.js +6 -0
  501. package/dist/lib/storage/account-save-entry.js.map +1 -0
  502. package/dist/lib/storage/account-save.d.ts +30 -0
  503. package/dist/lib/storage/account-save.d.ts.map +1 -0
  504. package/dist/lib/storage/account-save.js +49 -0
  505. package/dist/lib/storage/account-save.js.map +1 -0
  506. package/dist/lib/storage/account-snapshot.d.ts +25 -0
  507. package/dist/lib/storage/account-snapshot.d.ts.map +1 -0
  508. package/dist/lib/storage/account-snapshot.js +78 -0
  509. package/dist/lib/storage/account-snapshot.js.map +1 -0
  510. package/dist/lib/storage/backup-metadata-builder.d.ts +32 -0
  511. package/dist/lib/storage/backup-metadata-builder.d.ts.map +1 -0
  512. package/dist/lib/storage/backup-metadata-builder.js +77 -0
  513. package/dist/lib/storage/backup-metadata-builder.js.map +1 -0
  514. package/dist/lib/storage/backup-metadata.d.ts +29 -0
  515. package/dist/lib/storage/backup-metadata.d.ts.map +1 -0
  516. package/dist/lib/storage/backup-metadata.js +16 -0
  517. package/dist/lib/storage/backup-metadata.js.map +1 -0
  518. package/dist/lib/storage/backup-paths.d.ts +10 -0
  519. package/dist/lib/storage/backup-paths.d.ts.map +1 -0
  520. package/dist/lib/storage/backup-paths.js +27 -0
  521. package/dist/lib/storage/backup-paths.js.map +1 -0
  522. package/dist/lib/storage/backup-restore.d.ts +11 -0
  523. package/dist/lib/storage/backup-restore.d.ts.map +1 -0
  524. package/dist/lib/storage/backup-restore.js +52 -0
  525. package/dist/lib/storage/backup-restore.js.map +1 -0
  526. package/dist/lib/storage/cache-artifacts.d.ts +2 -0
  527. package/dist/lib/storage/cache-artifacts.d.ts.map +1 -0
  528. package/dist/lib/storage/cache-artifacts.js +4 -0
  529. package/dist/lib/storage/cache-artifacts.js.map +1 -0
  530. package/dist/lib/storage/error-hints.d.ts +10 -0
  531. package/dist/lib/storage/error-hints.d.ts.map +1 -0
  532. package/dist/lib/storage/error-hints.js +36 -0
  533. package/dist/lib/storage/error-hints.js.map +1 -0
  534. package/dist/lib/storage/file-paths.d.ts +6 -0
  535. package/dist/lib/storage/file-paths.d.ts.map +1 -0
  536. package/dist/lib/storage/file-paths.js +17 -0
  537. package/dist/lib/storage/file-paths.js.map +1 -0
  538. package/dist/lib/storage/fixture-guards.d.ts +4 -0
  539. package/dist/lib/storage/fixture-guards.d.ts.map +1 -0
  540. package/dist/lib/storage/fixture-guards.js +27 -0
  541. package/dist/lib/storage/fixture-guards.js.map +1 -0
  542. package/dist/lib/storage/flagged-entry.d.ts +15 -0
  543. package/dist/lib/storage/flagged-entry.d.ts.map +1 -0
  544. package/dist/lib/storage/flagged-entry.js +12 -0
  545. package/dist/lib/storage/flagged-entry.js.map +1 -0
  546. package/dist/lib/storage/flagged-load-entry.d.ts +20 -0
  547. package/dist/lib/storage/flagged-load-entry.d.ts.map +1 -0
  548. package/dist/lib/storage/flagged-load-entry.js +13 -0
  549. package/dist/lib/storage/flagged-load-entry.js.map +1 -0
  550. package/dist/lib/storage/flagged-save-entry.d.ts +7 -0
  551. package/dist/lib/storage/flagged-save-entry.d.ts.map +1 -0
  552. package/dist/lib/storage/flagged-save-entry.js +6 -0
  553. package/dist/lib/storage/flagged-save-entry.js.map +1 -0
  554. package/dist/lib/storage/flagged-storage-file.d.ts +7 -0
  555. package/dist/lib/storage/flagged-storage-file.d.ts.map +1 -0
  556. package/dist/lib/storage/flagged-storage-file.js +25 -0
  557. package/dist/lib/storage/flagged-storage-file.js.map +1 -0
  558. package/dist/lib/storage/flagged-storage-io.d.ts +28 -0
  559. package/dist/lib/storage/flagged-storage-io.d.ts.map +1 -0
  560. package/dist/lib/storage/flagged-storage-io.js +135 -0
  561. package/dist/lib/storage/flagged-storage-io.js.map +1 -0
  562. package/dist/lib/storage/flagged-storage.d.ts +6 -0
  563. package/dist/lib/storage/flagged-storage.d.ts.map +1 -0
  564. package/dist/lib/storage/flagged-storage.js +90 -0
  565. package/dist/lib/storage/flagged-storage.js.map +1 -0
  566. package/dist/lib/storage/gitignore.d.ts +7 -0
  567. package/dist/lib/storage/gitignore.d.ts.map +1 -0
  568. package/dist/lib/storage/gitignore.js +34 -0
  569. package/dist/lib/storage/gitignore.js.map +1 -0
  570. package/dist/lib/storage/hash.d.ts +2 -0
  571. package/dist/lib/storage/hash.d.ts.map +1 -0
  572. package/dist/lib/storage/hash.js +5 -0
  573. package/dist/lib/storage/hash.js.map +1 -0
  574. package/dist/lib/storage/identity.d.ts +21 -0
  575. package/dist/lib/storage/identity.d.ts.map +1 -0
  576. package/dist/lib/storage/identity.js +59 -0
  577. package/dist/lib/storage/identity.js.map +1 -0
  578. package/dist/lib/storage/import-export.d.ts +24 -0
  579. package/dist/lib/storage/import-export.d.ts.map +1 -0
  580. package/dist/lib/storage/import-export.js +108 -0
  581. package/dist/lib/storage/import-export.js.map +1 -0
  582. package/dist/lib/storage/metadata-section.d.ts +16 -0
  583. package/dist/lib/storage/metadata-section.d.ts.map +1 -0
  584. package/dist/lib/storage/metadata-section.js +16 -0
  585. package/dist/lib/storage/metadata-section.js.map +1 -0
  586. package/dist/lib/storage/named-backup-entry.d.ts +15 -0
  587. package/dist/lib/storage/named-backup-entry.d.ts.map +1 -0
  588. package/dist/lib/storage/named-backup-entry.js +7 -0
  589. package/dist/lib/storage/named-backup-entry.js.map +1 -0
  590. package/dist/lib/storage/named-backups-entry.d.ts +19 -0
  591. package/dist/lib/storage/named-backups-entry.d.ts.map +1 -0
  592. package/dist/lib/storage/named-backups-entry.js +7 -0
  593. package/dist/lib/storage/named-backups-entry.js.map +1 -0
  594. package/dist/lib/storage/named-backups.d.ts +15 -0
  595. package/dist/lib/storage/named-backups.d.ts.map +1 -0
  596. package/dist/lib/storage/named-backups.js +68 -0
  597. package/dist/lib/storage/named-backups.js.map +1 -0
  598. package/dist/lib/storage/path-state.d.ts +9 -0
  599. package/dist/lib/storage/path-state.d.ts.map +1 -0
  600. package/dist/lib/storage/path-state.js +20 -0
  601. package/dist/lib/storage/path-state.js.map +1 -0
  602. package/dist/lib/storage/project-migration.d.ts +10 -0
  603. package/dist/lib/storage/project-migration.d.ts.map +1 -0
  604. package/dist/lib/storage/project-migration.js +38 -0
  605. package/dist/lib/storage/project-migration.js.map +1 -0
  606. package/dist/lib/storage/record-utils.d.ts +3 -0
  607. package/dist/lib/storage/record-utils.d.ts.map +1 -0
  608. package/dist/lib/storage/record-utils.js +9 -0
  609. package/dist/lib/storage/record-utils.js.map +1 -0
  610. package/dist/lib/storage/restore-assessment.d.ts +7 -0
  611. package/dist/lib/storage/restore-assessment.d.ts.map +1 -0
  612. package/dist/lib/storage/restore-assessment.js +42 -0
  613. package/dist/lib/storage/restore-assessment.js.map +1 -0
  614. package/dist/lib/storage/restore-backup-entry.d.ts +24 -0
  615. package/dist/lib/storage/restore-backup-entry.d.ts.map +1 -0
  616. package/dist/lib/storage/restore-backup-entry.js +10 -0
  617. package/dist/lib/storage/restore-backup-entry.js.map +1 -0
  618. package/dist/lib/storage/restore-metadata.d.ts +10 -0
  619. package/dist/lib/storage/restore-metadata.d.ts.map +1 -0
  620. package/dist/lib/storage/restore-metadata.js +18 -0
  621. package/dist/lib/storage/restore-metadata.js.map +1 -0
  622. package/dist/lib/storage/restore.d.ts +14 -0
  623. package/dist/lib/storage/restore.d.ts.map +1 -0
  624. package/dist/lib/storage/restore.js +53 -0
  625. package/dist/lib/storage/restore.js.map +1 -0
  626. package/dist/lib/storage/snapshot-inspectors.d.ts +22 -0
  627. package/dist/lib/storage/snapshot-inspectors.d.ts.map +1 -0
  628. package/dist/lib/storage/snapshot-inspectors.js +95 -0
  629. package/dist/lib/storage/snapshot-inspectors.js.map +1 -0
  630. package/dist/lib/storage/storage-parser.d.ts +15 -0
  631. package/dist/lib/storage/storage-parser.d.ts.map +1 -0
  632. package/dist/lib/storage/storage-parser.js +16 -0
  633. package/dist/lib/storage/storage-parser.js.map +1 -0
  634. package/dist/lib/storage/transactions.d.ts +23 -0
  635. package/dist/lib/storage/transactions.d.ts.map +1 -0
  636. package/dist/lib/storage/transactions.js +64 -0
  637. package/dist/lib/storage/transactions.js.map +1 -0
  638. package/dist/lib/storage.d.ts +8 -47
  639. package/dist/lib/storage.d.ts.map +1 -1
  640. package/dist/lib/storage.js +482 -1044
  641. package/dist/lib/storage.js.map +1 -1
  642. package/dist/lib/types.d.ts +80 -1
  643. package/dist/lib/types.d.ts.map +1 -1
  644. package/package.json +41 -4
  645. package/scripts/check-pack-budget-lib.js +114 -0
  646. package/scripts/check-pack-budget.mjs +10 -0
  647. package/scripts/codex-multi-auth.js +34 -10
  648. package/scripts/install-codex-auth-utils.js +54 -14
  649. package/scripts/install-codex-auth.js +62 -33
  650. package/scripts/update-vendor-provenance.mjs +96 -0
  651. package/scripts/verify-vendor-provenance.mjs +53 -0
@@ -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, isAbsolute, join, relative } from "node:path";
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 { AnyAccountStorageSchema, getValidationErrors } from "./schemas.js";
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
- const configDir = dirname(storagePath);
170
- const inferredProjectRoot = dirname(configDir);
171
- const candidateRoots = [state.currentProjectRoot, inferredProjectRoot].filter((root) => typeof root === "string" && root.length > 0);
172
- const projectRoot = candidateRoots.find((root) => existsSync(join(root, ".git")));
173
- if (!projectRoot)
174
- return;
175
- const gitignorePath = join(projectRoot, ".gitignore");
176
- try {
177
- let content = "";
178
- if (existsSync(gitignorePath)) {
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
- try {
443
- const stats = await fs.stat(path);
444
- return { exists: true, bytes: stats.size, mtimeMs: stats.mtimeMs };
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
- const stats = await statSnapshot(path);
459
- if (!stats.exists) {
460
- return { kind, path, index, exists: false, valid: false };
461
- }
462
- try {
463
- const { normalized, schemaErrors, storedVersion } = await loadAccountsFromPath(path);
464
- return {
465
- kind,
466
- path,
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
- const content = await fs.readFile(path, "utf-8");
554
- const data = JSON.parse(content);
555
- return normalizeFlaggedStorage(data);
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 collectNamedBackups(getStoragePath());
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
- const backupRoot = getNamedBackupRoot(getStoragePath());
675
- let resolvedBackupRoot;
676
- try {
677
- resolvedBackupRoot = await fs.realpath(backupRoot);
678
- }
679
- catch (error) {
680
- const code = error.code;
681
- if (code === "ENOENT") {
682
- throw new Error(`Backup root does not exist: ${backupRoot}`);
683
- }
684
- throw error;
685
- }
686
- let resolvedBackupPath;
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 exportNamedBackupFile(name, {
469
+ return exportNamedBackupEntry({
470
+ name,
471
+ options,
472
+ exportNamedBackupFile,
726
473
  getStoragePath,
727
474
  exportAccounts,
728
- }, options);
475
+ });
729
476
  }
730
477
  export function getFlaggedAccountsPath() {
731
- return join(dirname(getStoragePath()), FLAGGED_ACCOUNTS_FILE_NAME);
478
+ return buildFlaggedAccountsPath(getStoragePath(), FLAGGED_ACCOUNTS_FILE_NAME);
732
479
  }
733
480
  function getLegacyFlaggedAccountsPath() {
734
- return join(dirname(getStoragePath()), LEGACY_FLAGGED_ACCOUNTS_FILE_NAME);
481
+ return buildLegacyFlaggedAccountsPath(getStoragePath(), LEGACY_FLAGGED_ACCOUNTS_FILE_NAME);
735
482
  }
736
- async function migrateLegacyProjectStorageIfNeeded(persist = saveAccounts) {
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
- let targetStorage = await loadNormalizedStorageFromPath(state.currentStoragePath, "current account storage");
757
- let migrated = false;
758
- for (const legacyPath of existingCandidatePaths) {
759
- const legacyStorage = await loadNormalizedStorageFromPath(legacyPath, "legacy account storage");
760
- if (!legacyStorage) {
761
- continue;
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 persist(mergedStorage);
767
- targetStorage = mergedStorage;
768
- migrated = true;
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
- targetStorage = fallbackStorage;
772
- log.warn("Failed to persist migrated account storage", {
773
- from: legacyPath,
774
- to: state.currentStoragePath,
775
- error: String(error),
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
- try {
780
- await fs.unlink(legacyPath);
781
- log.info("Removed legacy account storage file after migration", {
782
- path: legacyPath,
783
- });
561
+ const liveCurrentStorageAfterLegacyRead = await readLiveCurrentStorageIfExportMode();
562
+ if (liveCurrentStorageAfterLegacyRead.exists) {
563
+ return liveCurrentStorageAfterLegacyRead.storage;
784
564
  }
785
- catch (unlinkError) {
786
- const code = unlinkError.code;
787
- if (code !== "ENOENT") {
788
- log.warn("Failed to remove legacy account storage file after migration", {
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
- log.info("Migrated legacy project account storage", {
795
- from: legacyPath,
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(state.currentStoragePath)) {
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
- let matchIndex;
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
- const flaggedCandidates = await getAccountsBackupRecoveryCandidatesWithDiscovery(flaggedPath);
1191
- const flaggedSnapshots = [
1192
- await describeFlaggedSnapshot(flaggedPath, "flagged-primary"),
1193
- ];
1194
- for (const [index, candidate] of flaggedCandidates.entries()) {
1195
- const kind = candidate === `${flaggedPath}.bak`
1196
- ? "flagged-backup"
1197
- : candidate.startsWith(`${flaggedPath}.bak.`)
1198
- ? "flagged-backup-history"
1199
- : "flagged-discovered-backup";
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
- if (existsSync(resetMarkerPath)) {
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
- try {
1469
- await fs.mkdir(dirname(path), { recursive: true });
1470
- await ensureGitignore(path);
1471
- if (looksLikeSyntheticFixtureStorage(storage)) {
1472
- try {
1473
- const existing = await loadNormalizedStorageFromPath(path, "existing account storage");
1474
- if (existing &&
1475
- existing.accounts.length > 0 &&
1476
- !looksLikeSyntheticFixtureStorage(existing)) {
1477
- throw 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.");
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
- catch (error) {
1481
- if (error instanceof StorageError) {
1482
- throw error;
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
- if (storageBackupEnabled && existsSync(path)) {
1181
+ if (lastError)
1182
+ throw lastError;
1183
+ },
1184
+ cleanupResetMarker: async () => {
1488
1185
  try {
1489
- await createRotatingAccountsBackup(path);
1186
+ await fs.unlink(resetMarkerPath);
1490
1187
  }
1491
- catch (backupError) {
1492
- log.warn("Failed to create account storage backup", {
1493
- path,
1494
- backupPath: getAccountsBackupPath(path),
1495
- error: String(backupError),
1496
- });
1188
+ catch {
1189
+ // Best effort cleanup.
1497
1190
  }
1498
- }
1499
- const content = JSON.stringify(storage, null, 2);
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.rename(tempPath, path);
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 (renameError) {
1538
- const code = renameError.code;
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
- if (lastError)
1548
- throw lastError;
1549
- }
1550
- catch (error) {
1551
- try {
1552
- await fs.unlink(tempPath);
1553
- }
1554
- catch {
1555
- // Ignore cleanup failure.
1556
- }
1557
- const err = error;
1558
- const code = err?.code || "UNKNOWN";
1559
- const hint = formatStorageErrorHint(error, path);
1560
- log.error("Failed to save accounts", {
1561
- path,
1562
- code,
1563
- message: err?.message,
1564
- hint,
1565
- });
1566
- throw new StorageError(`Failed to save accounts: ${err?.message || "Unknown error"}`, code, path, hint, err instanceof Error ? err : undefined);
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 cloneAccountStorageForPersistence(storage) {
1227
+ function cloneFlaggedStorageForPersistence(storage) {
1570
1228
  return {
1571
- version: 3,
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 withStorageLock(async () => {
1582
- const storagePath = getStoragePath();
1583
- const state = {
1584
- snapshot: await loadAccountsInternal(saveAccountsUnlocked),
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(flaggedStorage);
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 transactionSnapshotContext.run(state, () => handler(current, persist));
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 withStorageLock(async () => {
1641
- await saveAccountsUnlocked(storage);
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
- return withStorageLock(async () => {
1650
- const path = getStoragePath();
1651
- const resetMarkerPath = getIntentionalResetMarkerPath(path);
1652
- const walPath = getAccountsWalPath(path);
1653
- const backupPaths = await getAccountsBackupRecoveryCandidatesWithDiscovery(path);
1654
- await fs.writeFile(resetMarkerPath, JSON.stringify({ version: 1, createdAt: Date.now() }), { encoding: "utf-8", mode: 0o600 });
1655
- const clearPath = async (targetPath) => {
1656
- try {
1657
- await fs.unlink(targetPath);
1658
- }
1659
- catch (error) {
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
- const path = getFlaggedAccountsPath();
1770
- const resetMarkerPath = getIntentionalResetMarkerPath(path);
1771
- const empty = { version: 1, accounts: [] };
1772
- try {
1773
- const content = await fs.readFile(path, "utf-8");
1774
- const data = JSON.parse(content);
1775
- const loaded = normalizeFlaggedStorage(data);
1776
- if (existsSync(resetMarkerPath)) {
1777
- return empty;
1778
- }
1779
- return loaded;
1780
- }
1781
- catch (error) {
1782
- const code = error.code;
1783
- if (code !== "ENOENT") {
1784
- log.error("Failed to load flagged account storage", {
1785
- path,
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
- const markerPath = getIntentionalResetMarkerPath(path);
1827
- const uniqueSuffix = `${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
1828
- const tempPath = `${path}.${uniqueSuffix}.tmp`;
1829
- try {
1830
- await fs.mkdir(dirname(path), { recursive: true });
1831
- if (existsSync(path)) {
1832
- try {
1833
- await copyFileWithRetry(path, `${path}.bak`, {
1834
- allowMissingSource: true,
1835
- });
1836
- }
1837
- catch (backupError) {
1838
- log.warn("Failed to create flagged backup snapshot", {
1839
- path,
1840
- error: String(backupError),
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 withStorageLock(async () => {
1870
- await saveFlaggedAccountsUnlocked(storage);
1379
+ return saveFlaggedAccountsEntry({
1380
+ storage,
1381
+ withStorageLock,
1382
+ saveUnlocked: saveFlaggedAccountsUnlocked,
1871
1383
  });
1872
1384
  }
1873
1385
  export async function clearFlaggedAccounts() {
1874
- return withStorageLock(async () => {
1875
- const path = getFlaggedAccountsPath();
1876
- const markerPath = getIntentionalResetMarkerPath(path);
1877
- try {
1878
- await fs.writeFile(markerPath, "reset", {
1879
- encoding: "utf-8",
1880
- mode: 0o600,
1881
- });
1882
- }
1883
- catch (error) {
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
- if (!force && existsSync(resolvedPath)) {
1921
- throw new Error(`File already exists: ${resolvedPath}`);
1922
- }
1923
- const transactionState = transactionSnapshotContext.getStore();
1924
- const storage = transactionState?.active &&
1925
- transactionState.storagePath === currentStoragePath
1926
- ? transactionState.snapshot
1927
- : transactionState?.active
1928
- ? await loadAccountsInternal(saveAccountsUnlocked)
1929
- : await withAccountStorageTransaction((current) => Promise.resolve(current));
1930
- if (!storage || storage.accounts.length === 0) {
1931
- throw new Error("No accounts to export");
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
- // Check file exists with friendly error
1959
- if (!existsSync(resolvedPath)) {
1960
- throw new Error(`Import file not found: ${resolvedPath}`);
1961
- }
1962
- const content = await fs.readFile(resolvedPath, "utf-8");
1963
- let imported;
1964
- try {
1965
- imported = JSON.parse(content);
1966
- }
1967
- catch {
1968
- throw new Error(`Invalid JSON in import file: ${resolvedPath}`);
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