@hienlh/ppm 0.19.2 → 0.19.3

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 (617) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +3 -2
  6. package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
  7. package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
  8. package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
  9. package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
  10. package/dist/web/assets/{angular-html-BlcrLjUx.js → angular-html-BuHNQIbQ.js} +1 -1
  11. package/dist/web/assets/{angular-ts-BUjtceAJ.js → angular-ts-DHRWoLc-.js} +1 -1
  12. package/dist/web/assets/api-client-BogqsFna.js +1 -0
  13. package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
  14. package/dist/web/assets/{api-themes-YsZgf6Xj.js → api-themes-Cknp3Q-P.js} +1 -1
  15. package/dist/web/assets/{apl-B3g0v1pk.js → apl-C1VSWVei.js} +1 -1
  16. package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
  17. package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
  18. package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
  19. package/dist/web/assets/{astro-FRnyn7cH.js → astro-RX-rChsZ.js} +1 -1
  20. package/dist/web/assets/{audio-preview-DWTRm4HC.js → audio-preview-CCd1jdje.js} +1 -1
  21. package/dist/web/assets/{blade-NLdOqUKw.js → blade-CZ6bxMbK.js} +1 -1
  22. package/dist/web/assets/{pencil-BlIIg6RH.js → brain-KDa9jFRq.js} +1 -1
  23. package/dist/web/assets/{bsl-CenHZfaU.js → bsl-BhgCXLWk.js} +1 -1
  24. package/dist/web/assets/{button-Cq8yixjf.js → button-BZtEJcUv.js} +1 -1
  25. package/dist/web/assets/{cairo-B_WR0aiQ.js → cairo-BrwygPkn.js} +1 -1
  26. package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
  27. package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
  28. package/dist/web/assets/check-DN75Ucvz.js +1 -0
  29. package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
  30. package/dist/web/assets/{cobol-DOynhWYj.js → cobol-C5TGW1Ir.js} +1 -1
  31. package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
  32. package/dist/web/assets/{coffee-Bn6dbw4P.js → coffee-CFgKlmgI.js} +1 -1
  33. package/dist/web/assets/{conflict-editor-Cgb-z12D.js → conflict-editor-BoRPwL3L.js} +1 -1
  34. package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
  35. package/dist/web/assets/{cpp-CoZR9t8r.js → cpp-BhG5q3-d.js} +1 -1
  36. package/dist/web/assets/{crystal-BSheONSg.js → crystal-DvuI7_tY.js} +1 -1
  37. package/dist/web/assets/{csv-preview-BZMHfFH3.js → csv-preview-BpoJqZQi.js} +1 -1
  38. package/dist/web/assets/{data-grid-overlay-editor-gwPoVSCU.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
  39. package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
  40. package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
  41. package/dist/web/assets/{docx-preview-DqVDOMmv.js → docx-preview-CWIDVNZn.js} +1 -1
  42. package/dist/web/assets/{edge-DTtoslQu.js → edge-BSIEsmlI.js} +1 -1
  43. package/dist/web/assets/{edit-diff-preview-CXSbSUBX.js → edit-diff-preview-DeHb85DN.js} +1 -1
  44. package/dist/web/assets/{elixir-CIBCZ3Zi.js → elixir-BF-lrGXM.js} +1 -1
  45. package/dist/web/assets/{elm-BLmb7K1H.js → elm-8mCsJOeN.js} +1 -1
  46. package/dist/web/assets/{erb-BBY8ApRd.js → erb-Dw5PUVKa.js} +1 -1
  47. package/dist/web/assets/{erlang-D5Zb5_P0.js → erlang-BvjBIA6O.js} +1 -1
  48. package/dist/web/assets/{esm-6F8v1jLL.js → esm-Bt9OwSnU.js} +1 -1
  49. package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
  50. package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
  51. package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
  52. package/dist/web/assets/{extension-webview-BSh6KGd7.js → extension-webview-Diz2OzVq.js} +1 -1
  53. package/dist/web/assets/{plus-BR0qewJI.js → eye-gWwaF4Lg.js} +1 -1
  54. package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
  55. package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
  56. package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
  57. package/dist/web/assets/{fortran-fixed-form-CBdwkEUJ.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
  58. package/dist/web/assets/{fsharp-AT4XdVYH.js → fsharp-CjUIHIHp.js} +1 -1
  59. package/dist/web/assets/{gdresource-D852fWBR.js → gdresource-x_NUQFrZ.js} +1 -1
  60. package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
  61. package/dist/web/assets/{git-commit-DWfWAyxy.js → git-commit-BOpxSbns.js} +1 -1
  62. package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
  63. package/dist/web/assets/{git-rebase-C6opk8gr.js → git-rebase-BW4xlK7k.js} +1 -1
  64. package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
  65. package/dist/web/assets/{glide-data-grid-CICFjJon.js → glide-data-grid-pKQuyYFj.js} +8 -8
  66. package/dist/web/assets/{glimmer-js-DLi5mWYO.js → glimmer-js-CT9jrmFj.js} +1 -1
  67. package/dist/web/assets/{glimmer-ts-D-scFX30.js → glimmer-ts-CjQT1lxK.js} +1 -1
  68. package/dist/web/assets/{glsl-dbQzF4iA.js → glsl-DsFJvtg5.js} +1 -1
  69. package/dist/web/assets/{graphql-D1m5Wen2.js → graphql-Dhlnmkn_.js} +1 -1
  70. package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
  71. package/dist/web/assets/{hack-CqYmpO7u.js → hack-BKHbsJ7u.js} +1 -1
  72. package/dist/web/assets/{haml-BXzKWEag.js → haml-C1Ih25Y2.js} +1 -1
  73. package/dist/web/assets/{handlebars-DXnrtHtq.js → handlebars-CaFitvmC.js} +1 -1
  74. package/dist/web/assets/{html-DhgQJ-xL.js → html-BwpKIE6V.js} +1 -1
  75. package/dist/web/assets/{html-derivative-xZRft-YE.js → html-derivative-uuTEquZU.js} +1 -1
  76. package/dist/web/assets/{http-B53mSj14.js → http-Cv4Pg5cR.js} +1 -1
  77. package/dist/web/assets/{hurl-CsomVy04.js → hurl-DubkI6TG.js} +1 -1
  78. package/dist/web/assets/{hxml-LffjWwKA.js → hxml-B9OVZFAB.js} +1 -1
  79. package/dist/web/assets/{image-preview-CNA6XMNS.js → image-preview-DRbZ0FA2.js} +1 -1
  80. package/dist/web/assets/index--KWVW-Ga.js +52 -0
  81. package/dist/web/assets/index-CvGx7wBQ.css +2 -0
  82. package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
  83. package/dist/web/assets/{jinja-CCHURl7V.js → jinja-BHb1jyl6.js} +1 -1
  84. package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
  85. package/dist/web/assets/{jison-7qBd8dnR.js → jison-CtpfXDzp.js} +1 -1
  86. package/dist/web/assets/{julia-BY9pnTcy.js → julia-TYjgpazU.js} +1 -1
  87. package/dist/web/assets/{just-D7wRSNRr.js → just-D27bzZuW.js} +1 -1
  88. package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
  89. package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
  90. package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
  91. package/dist/web/assets/{latex-RgsCae55.js → latex-D_MzopTn.js} +1 -1
  92. package/dist/web/assets/{liquid-CnDFzvOZ.js → liquid-BG_FG0Pe.js} +1 -1
  93. package/dist/web/assets/{lua-0lj5W8sm.js → lua-Bl29oC2L.js} +1 -1
  94. package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
  95. package/dist/web/assets/{markdown-renderer-dbLIvU2M.js → markdown-renderer-DDcIGQEd.js} +3 -3
  96. package/dist/web/assets/{marko-CQgqAhbV.js → marko-D-HI7dbm.js} +1 -1
  97. package/dist/web/assets/{mdc-BkIcTiIy.js → mdc-y444JQTQ.js} +1 -1
  98. package/dist/web/assets/{media-url-DDciwvLU.js → media-url-VzecHk9g.js} +1 -1
  99. package/dist/web/assets/{mobile-bottom-sheet-pjcHKorA.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
  100. package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
  101. package/dist/web/assets/{nextflow-DvIW68DS.js → nextflow-C5sKcldC.js} +1 -1
  102. package/dist/web/assets/{nginx-O-hft0gG.js → nginx-bXnEC3wm.js} +1 -1
  103. package/dist/web/assets/{nim-BxrR4HwQ.js → nim-pevhf4WD.js} +1 -1
  104. package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
  105. package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
  106. package/dist/web/assets/{number-overlay-editor-BoLRYuZT.js → number-overlay-editor-B7WINIW4.js} +1 -1
  107. package/dist/web/assets/{overview-panel-l3g1gT5e.js → overview-panel-mhN0qw6Q.js} +1 -1
  108. package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
  109. package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
  110. package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
  111. package/dist/web/assets/{perl-Da-uNqmL.js → perl-B1KUtMnW.js} +1 -1
  112. package/dist/web/assets/{php-JMu45mEx.js → php-BGmVZBAJ.js} +1 -1
  113. package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
  114. package/dist/web/assets/{postgres-viewer-DstZwUai.js → postgres-viewer-jaR0FYJ_.js} +3 -3
  115. package/dist/web/assets/power-CriQkeK5.js +1 -0
  116. package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
  117. package/dist/web/assets/{process-table-kmQ2-8qJ.js → process-table-D1r-Gupi.js} +1 -1
  118. package/dist/web/assets/{project-store-DTUCS65m.js → project-store-BuY7rxn_.js} +1 -1
  119. package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
  120. package/dist/web/assets/{pug-CLzyQhia.js → pug-BfN8CpEH.js} +1 -1
  121. package/dist/web/assets/{qml-BHXfGB1l.js → qml-D6fRdLZt.js} +1 -1
  122. package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
  123. package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
  124. package/dist/web/assets/{razor-BqetJLN3.js → razor-fg-9AScR.js} +1 -1
  125. package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
  126. package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
  127. package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
  128. package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
  129. package/dist/web/assets/{rst-iQdJZ0BP.js → rst-BZgx076b.js} +1 -1
  130. package/dist/web/assets/{ruby-CNwzZGdH.js → ruby-iUkWDcLo.js} +1 -1
  131. package/dist/web/assets/{run-in-terminal-CBXmVGWa.js → run-in-terminal-bKRwuO2z.js} +1 -1
  132. package/dist/web/assets/{sas-BpBIT2eR.js → sas-CLXO0lZ6.js} +1 -1
  133. package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
  134. package/dist/web/assets/{scss-D1MotuEp.js → scss-DQqteGaG.js} +1 -1
  135. package/dist/web/assets/send-Bb0jtcR6.js +1 -0
  136. package/dist/web/assets/{send-to-chat-DJAdlvJ8.js → send-to-chat-S8LCNX7V.js} +1 -1
  137. package/dist/web/assets/separator-DqGg3f9c.js +1 -0
  138. package/dist/web/assets/settings-body-omv04jbb.js +2 -0
  139. package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
  140. package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
  141. package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
  142. package/dist/web/assets/{shaderlab-BNqmcPQi.js → shaderlab-CeMTqagR.js} +1 -1
  143. package/dist/web/assets/{shellsession-BBLOVOO-.js → shellsession-BAkquZAV.js} +1 -1
  144. package/dist/web/assets/{soy-BpTGo9uy.js → soy-Bxm9Ez55.js} +1 -1
  145. package/dist/web/assets/{sparql-D7A3yKob.js → sparql-C1phwdLc.js} +1 -1
  146. package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
  147. package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
  148. package/dist/web/assets/{stata-B7N56ptS.js → stata-DGNMQdEb.js} +1 -1
  149. package/dist/web/assets/{surrealql-CTmnD-lv.js → surrealql-ByVrV320.js} +1 -1
  150. package/dist/web/assets/{svelte-BppwY9TR.js → svelte-CNWjIMhl.js} +1 -1
  151. package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
  152. package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
  153. package/dist/web/assets/{system-monitor-window-content-B9Y98UW6.js → system-monitor-window-content-BOjq53uU.js} +1 -1
  154. package/dist/web/assets/{tab-host-window-content-C1j8zr68.js → tab-host-window-content-CW_dlYlW.js} +1 -1
  155. package/dist/web/assets/{tab-store-DzYSsw5a.js → tab-store-PZsP5ZwG.js} +1 -1
  156. package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
  157. package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
  158. package/dist/web/assets/{templ-CvTMKKra.js → templ-Cf0gQL4I.js} +1 -1
  159. package/dist/web/assets/{terminal-tab-B9I_HlES.js → terminal-tab-HXZkicIc.js} +3 -3
  160. package/dist/web/assets/{tex--c4POEUE.js → tex-BTIqPw_R.js} +1 -1
  161. package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
  162. package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
  163. package/dist/web/assets/{ts-tags--kx7D-Hw.js → ts-tags-B8dN_Yf6.js} +1 -1
  164. package/dist/web/assets/{twig-UG9uMHGd.js → twig-BgemonbJ.js} +1 -1
  165. package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
  166. package/dist/web/assets/{use-blob-url-Bdxopwwc.js → use-blob-url-Gfi69PtP.js} +1 -1
  167. package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
  168. package/dist/web/assets/{use-open-team-member-D9EOB8-i.js → use-open-team-member-CruUTqzD.js} +1 -1
  169. package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
  170. package/dist/web/assets/{use-websocket-xOOwBdwP.js → use-websocket-DetaY8OI.js} +1 -1
  171. package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
  172. package/dist/web/assets/video-preview-C0uH2228.js +1 -0
  173. package/dist/web/assets/{vue-jjTomlbt.js → vue-D3D4lbXz.js} +1 -1
  174. package/dist/web/assets/{vue-html-8KEW6-I9.js → vue-html-DYEQFAiK.js} +1 -1
  175. package/dist/web/assets/{vue-vine-C5tohCkU.js → vue-vine-D0OPNscN.js} +1 -1
  176. package/dist/web/assets/{window-pip-registry-FdZdGF7h.js → window-pip-registry-BEN0QNSW.js} +1 -1
  177. package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
  178. package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
  179. package/dist/web/assets/{xml-ByFFosqL.js → xml-DHaWgpya.js} +1 -1
  180. package/dist/web/assets/{xsl-CZUwiLkj.js → xsl-DVRN8JsA.js} +1 -1
  181. package/dist/web/index.html +32 -55
  182. package/dist/web/sw.js +1 -1
  183. package/docs/codebase-summary.md +9 -5
  184. package/docs/lessons-learned.md +11 -0
  185. package/package.json +2 -2
  186. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  187. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  188. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  189. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  190. package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
  191. package/src/providers/codex-app-server/codex-history.ts +24 -0
  192. package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
  193. package/src/providers/codex-app-server/codex-protocol.ts +24 -0
  194. package/src/providers/codex-app-server/codex-provider.ts +159 -17
  195. package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
  196. package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
  197. package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
  198. package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
  199. package/src/server/routes/chat.ts +67 -9
  200. package/src/server/routes/codex-accounts.ts +9 -5
  201. package/src/server/ws/chat.ts +46 -1
  202. package/src/services/claude-usage-source.ts +40 -0
  203. package/src/services/claude-usage.service.ts +77 -146
  204. package/src/services/codex-account-login.ts +123 -55
  205. package/src/services/codex-account.service.ts +27 -4
  206. package/src/services/db.service.ts +161 -20
  207. package/src/services/edge-forwarder.ts +191 -191
  208. package/src/services/fs-credential-path-guard.ts +35 -8
  209. package/src/services/fs-path-guard.service.ts +1 -0
  210. package/src/services/postgres.service.ts +144 -72
  211. package/src/services/provider-usage/index.ts +63 -0
  212. package/src/services/provider-usage/register-usage-sources.ts +18 -0
  213. package/src/services/provider-usage/usage-memory-cache.ts +71 -0
  214. package/src/services/provider-usage/usage-registry.ts +157 -0
  215. package/src/services/provider-usage/usage-scheduler.ts +117 -0
  216. package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
  217. package/src/services/provider-usage/usage-source.ts +82 -0
  218. package/src/services/slash-discovery/builtin-commands.ts +13 -0
  219. package/src/services/slash-discovery/codex-skill-items.ts +57 -0
  220. package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
  221. package/src/services/slash-discovery/types.ts +19 -0
  222. package/src/services/windows-process-tree.ts +351 -351
  223. package/src/types/chat.ts +7 -1
  224. package/src/web/app.tsx +1 -7
  225. package/src/web/components/chat/chat-history-bar.tsx +10 -1
  226. package/src/web/components/chat/chat-tab.tsx +17 -1
  227. package/src/web/components/chat/message-input.tsx +11 -4
  228. package/src/web/components/chat/message-list.tsx +14 -0
  229. package/src/web/components/chat/slash-command-picker.tsx +22 -3
  230. package/src/web/components/chat/tool-cards.tsx +47 -4
  231. package/src/web/components/chat/usage-badge.tsx +43 -447
  232. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  233. package/src/web/components/floating-window/window-store-types.ts +1 -1
  234. package/src/web/components/layout/command-palette.tsx +2 -2
  235. package/src/web/components/layout/mobile-drawer.tsx +5 -4
  236. package/src/web/components/layout/mobile-nav.tsx +5 -1
  237. package/src/web/components/layout/nav-section-rail.tsx +2 -1
  238. package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
  239. package/src/web/components/layout/sidebar.tsx +0 -2
  240. package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
  241. package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
  242. package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
  243. package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
  244. package/src/web/components/settings/accounts/account-card.tsx +127 -0
  245. package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
  246. package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
  247. package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
  248. package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
  249. package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
  250. package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
  251. package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
  252. package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
  253. package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
  254. package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
  255. package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
  256. package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
  257. package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
  258. package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
  259. package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
  260. package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
  261. package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
  262. package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
  263. package/src/web/components/settings/ai-settings-section.tsx +14 -4
  264. package/src/web/components/settings/appearance-settings-section.tsx +125 -0
  265. package/src/web/components/settings/general-settings-section.tsx +87 -0
  266. package/src/web/components/settings/jira-watcher-section.tsx +34 -0
  267. package/src/web/components/settings/notifications-settings-section.tsx +37 -0
  268. package/src/web/components/settings/open-settings.ts +42 -0
  269. package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
  270. package/src/web/components/settings/settings-body.tsx +110 -0
  271. package/src/web/components/settings/settings-categories.ts +102 -0
  272. package/src/web/components/settings/settings-category-list.tsx +56 -0
  273. package/src/web/components/settings/settings-category-rail.tsx +62 -0
  274. package/src/web/components/settings/settings-section-content.tsx +43 -0
  275. package/src/web/components/settings/settings-tab.tsx +15 -329
  276. package/src/web/components/settings/settings-window-content.tsx +24 -0
  277. package/src/web/hooks/use-chat.ts +38 -1
  278. package/src/web/hooks/use-global-keybindings.ts +3 -4
  279. package/src/web/hooks/use-url-sync.ts +30 -0
  280. package/src/web/hooks/use-usage.ts +23 -11
  281. package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
  282. package/src/web/lib/slash-items-cache.ts +27 -7
  283. package/src/web/stores/panel-utils.ts +11 -0
  284. package/src/web/stores/settings-store.ts +4 -2
  285. package/src/web/stores/window-panel-actions.ts +1 -3
  286. package/src/web/stores/window-panel-persistence.ts +9 -4
  287. package/dist/web/assets/adaptive-context-menu-BbuRVTLn.js +0 -1
  288. package/dist/web/assets/ai-resource-editor-BCCOUzox.js +0 -1
  289. package/dist/web/assets/ai-settings-section-BEM1z7kp.js +0 -1
  290. package/dist/web/assets/api-client-D1CePgQ4.js +0 -1
  291. package/dist/web/assets/api-settings-CNtKkHqW.js +0 -1
  292. package/dist/web/assets/api-settings-Cubuqjsu.js +0 -1
  293. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
  294. package/dist/web/assets/chat-tab-CmXGsJGc.js +0 -15
  295. package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
  296. package/dist/web/assets/code-editor-DtHXzNZ1.js +0 -7
  297. package/dist/web/assets/context-menu-DfWZAxQy.js +0 -1
  298. package/dist/web/assets/database-viewer-GVaQVMdb.js +0 -1
  299. package/dist/web/assets/dialog-BSfuiTkJ.js +0 -1
  300. package/dist/web/assets/diff-viewer-BG5_fH1E.js +0 -4
  301. package/dist/web/assets/explorer-body-BPFnjxYj.js +0 -2
  302. package/dist/web/assets/explorer-window-content-Da1T9uM4.js +0 -1
  303. package/dist/web/assets/extension-store-Bhu--fPX.js +0 -1
  304. package/dist/web/assets/external-link-B6Hl0U4X.js +0 -1
  305. package/dist/web/assets/eye-off-CUdzgmX8.js +0 -1
  306. package/dist/web/assets/file-store-6HW2Lfl4.js +0 -1
  307. package/dist/web/assets/git-log-panel-DNC9fV0M.js +0 -1
  308. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
  309. package/dist/web/assets/group-chat-tab-DbUkcOaJ.js +0 -1
  310. package/dist/web/assets/index-DTqbMiVc.js +0 -52
  311. package/dist/web/assets/index-SFFv2-VM.css +0 -2
  312. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
  313. package/dist/web/assets/input-D0ol5jW0.js +0 -1
  314. package/dist/web/assets/keybindings-store-BGkB72gM.js +0 -1
  315. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +0 -1
  316. package/dist/web/assets/lock-CNxUGpTk.js +0 -1
  317. package/dist/web/assets/markdown-renderer-DEAVpo0C.js +0 -1
  318. package/dist/web/assets/mobile-explorer-sheet-CD_kl-jA.js +0 -1
  319. package/dist/web/assets/monitor-smartphone-DLZ6Q05s.js +0 -1
  320. package/dist/web/assets/notification-store-Dgbsw9p9.js +0 -1
  321. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
  322. package/dist/web/assets/panel-store-UOV8wrfR.js +0 -1
  323. package/dist/web/assets/pdf-preview-B2CJ_EKv.js +0 -1
  324. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
  325. package/dist/web/assets/play-DpEEBJTT.js +0 -1
  326. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
  327. package/dist/web/assets/refresh-cw-BQU5g2CY.js +0 -1
  328. package/dist/web/assets/remote-desktop-mobile-sheet-KKTRNjOL.js +0 -2
  329. package/dist/web/assets/remote-desktop-mobile-view-C1ST4-Fm.js +0 -1
  330. package/dist/web/assets/remote-desktop-readiness-gate-BH3BE5wA.js +0 -1
  331. package/dist/web/assets/remote-desktop-window-content-BaZJQt_N.js +0 -1
  332. package/dist/web/assets/rotate-ccw-CpjRo90J.js +0 -1
  333. package/dist/web/assets/scroll-area-lc0ZHORr.js +0 -1
  334. package/dist/web/assets/send-C_sLFt56.js +0 -1
  335. package/dist/web/assets/settings-store-zbPkG7o-.js +0 -2
  336. package/dist/web/assets/settings-tab-Cn-AqUP_.js +0 -1
  337. package/dist/web/assets/settings-tab-xyRpt2gq.js +0 -3
  338. package/dist/web/assets/shallow-CmPMAPte.js +0 -1
  339. package/dist/web/assets/sql-query-editor-B86NiZHI.js +0 -1
  340. package/dist/web/assets/sqlite-viewer-DB775F6C.js +0 -1
  341. package/dist/web/assets/switch-BmjKoIpR.js +0 -1
  342. package/dist/web/assets/system-monitor-body-BDxHQvy9.js +0 -2
  343. package/dist/web/assets/system-monitor-tab-BoTHLx6B.js +0 -1
  344. package/dist/web/assets/team-member-sheet-Cxz6h5Ne.js +0 -2
  345. package/dist/web/assets/team-member-window-content-CUSuRHCB.js +0 -1
  346. package/dist/web/assets/tool-cards-D3KSzd2K.js +0 -8
  347. package/dist/web/assets/trash-2-By4VMbC4.js +0 -1
  348. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
  349. package/dist/web/assets/triangle-alert-CUTURFbG.js +0 -1
  350. package/dist/web/assets/upload-BNlX8D07.js +0 -1
  351. package/dist/web/assets/use-monaco-theme-CVCye2kq.js +0 -1
  352. package/dist/web/assets/use-remote-desktop-display-choice-neUqyE3p.js +0 -1
  353. package/dist/web/assets/video-preview-DvpgBDhM.js +0 -1
  354. package/dist/web/assets/window-store-D0x_fxr0.js +0 -1
  355. package/src/web/components/chat/account-dialogs.tsx +0 -384
  356. package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
  357. package/src/web/components/settings/codex-accounts-section.tsx +0 -198
  358. package/src/web/components/settings/telegram-settings-section.tsx +0 -113
  359. /package/dist/web/assets/{abap-C5DFF0s3.js → abap-h3QyH8MW.js} +0 -0
  360. /package/dist/web/assets/{actionscript-3-B0qyRNbc.js → actionscript-3-mkxkq-FN.js} +0 -0
  361. /package/dist/web/assets/{ada-bAJzplug.js → ada-DhISK_EH.js} +0 -0
  362. /package/dist/web/assets/{andromeeda-DUCpISy6.js → andromeeda-DnlWWesX.js} +0 -0
  363. /package/dist/web/assets/{apache-BhAr84Z_.js → apache-P5AK3sfA.js} +0 -0
  364. /package/dist/web/assets/{apex-CRv8YnNq.js → apex-CJUPehg2.js} +0 -0
  365. /package/dist/web/assets/{api-client-BzWM7XPR.js → api-client-oY1tC3tM.js} +0 -0
  366. /package/dist/web/assets/{applescript-CD-Cw8bt.js → applescript-Dx6GGbay.js} +0 -0
  367. /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
  368. /package/dist/web/assets/{ara-BUzeQYoG.js → ara-95Kx3qKb.js} +0 -0
  369. /package/dist/web/assets/{asciidoc-CT2hTg-v.js → asciidoc-Dp0RbIej.js} +0 -0
  370. /package/dist/web/assets/{asm-CAu35JeY.js → asm-C82coUjU.js} +0 -0
  371. /package/dist/web/assets/{aurora-x-BeFJoIA1.js → aurora-x-BFc3zeop.js} +0 -0
  372. /package/dist/web/assets/{awk-Vau-DrGN.js → awk-ChjC0icr.js} +0 -0
  373. /package/dist/web/assets/{ayu-dark-CLe-OTsQ.js → ayu-dark-DYfUf_PB.js} +0 -0
  374. /package/dist/web/assets/{ayu-light-CJbQfu4M.js → ayu-light-Bh6O0plO.js} +0 -0
  375. /package/dist/web/assets/{ayu-mirage-GWaKwdrx.js → ayu-mirage-DlNhHTGq.js} +0 -0
  376. /package/dist/web/assets/{ballerina-DWu0USI5.js → ballerina-Ceoll3Sk.js} +0 -0
  377. /package/dist/web/assets/{bat-_07Huw7E.js → bat-C7QS6Lfq.js} +0 -0
  378. /package/dist/web/assets/{beancount-EcvogrcF.js → beancount-5NMEAZgs.js} +0 -0
  379. /package/dist/web/assets/{berry-BBB87SY7.js → berry-BBL83Vbk.js} +0 -0
  380. /package/dist/web/assets/{bibtex-DbueS87A.js → bibtex-L7rPkPSG.js} +0 -0
  381. /package/dist/web/assets/{bicep-tcmXnUo4.js → bicep-BOwHIINh.js} +0 -0
  382. /package/dist/web/assets/{bird2-B2qwilIR.js → bird2-CWLsMom2.js} +0 -0
  383. /package/dist/web/assets/{c-Z58oivSt.js → c-CD4Ku5Cr.js} +0 -0
  384. /package/dist/web/assets/{c3-U40cGT9p.js → c3-CpMl-w3G.js} +0 -0
  385. /package/dist/web/assets/{cadence-BFcD-LyM.js → cadence-CvgF6ehN.js} +0 -0
  386. /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
  387. /package/dist/web/assets/{catppuccin-frappe-RkC9AX6J.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
  388. /package/dist/web/assets/{catppuccin-latte-Bsi4l-nO.js → catppuccin-latte-DoiMCU_7.js} +0 -0
  389. /package/dist/web/assets/{catppuccin-macchiato-B8JIdTW-.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
  390. /package/dist/web/assets/{catppuccin-mocha-oH8wwrDj.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
  391. /package/dist/web/assets/{clarity-C3WNeRCR.js → clarity-DXYM8-SA.js} +0 -0
  392. /package/dist/web/assets/{clojure-CFoAz7u9.js → clojure-DzamsL69.js} +0 -0
  393. /package/dist/web/assets/{cmake-Bcsqp9yR.js → cmake-CR6RP_T_.js} +0 -0
  394. /package/dist/web/assets/{codeowners-DC2Z7heP.js → codeowners-D1s6-IyY.js} +0 -0
  395. /package/dist/web/assets/{codeql-BfG68Al5.js → codeql-DAqNWMkS.js} +0 -0
  396. /package/dist/web/assets/{common-lisp-Z5P-lKQe.js → common-lisp-8e-KtitB.js} +0 -0
  397. /package/dist/web/assets/{coq-BJslqrnv.js → coq-UEq0WUcO.js} +0 -0
  398. /package/dist/web/assets/{csharp-DhhZTOTs.js → csharp-wxJ0TuCm.js} +0 -0
  399. /package/dist/web/assets/{css-DiFIpMfE.js → css-Cerub-yB.js} +0 -0
  400. /package/dist/web/assets/{csv-DViLbHxI.js → csv-Bh9_WD_h.js} +0 -0
  401. /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
  402. /package/dist/web/assets/{cue-CoG3mg2I.js → cue-rzM1ztqU.js} +0 -0
  403. /package/dist/web/assets/{cypher-jTxC8NAB.js → cypher-DGaTQ8m0.js} +0 -0
  404. /package/dist/web/assets/{d-6hKIUAbU.js → d-DbkZGuhB.js} +0 -0
  405. /package/dist/web/assets/{dark-plus-D_-np2Sr.js → dark-plus-BO2bzZcA.js} +0 -0
  406. /package/dist/web/assets/{dart-CVpxLpVn.js → dart-BDhCoqVg.js} +0 -0
  407. /package/dist/web/assets/{data-grid-types-Dq_H1IPa.js → data-grid-types-CdAg-4W1.js} +0 -0
  408. /package/dist/web/assets/{dax-BPqJ8qIY.js → dax-Bh03stWh.js} +0 -0
  409. /package/dist/web/assets/{desktop-u_y5_Hr5.js → desktop-BOdr_fwq.js} +0 -0
  410. /package/dist/web/assets/{diff-Duj1XMrd.js → diff-Bb_gTsS2.js} +0 -0
  411. /package/dist/web/assets/{dist-CEZ4plgH.js → dist-BUMDu0av.js} +0 -0
  412. /package/dist/web/assets/{dist-aDSxTR_v.js → dist-DonmFWG8.js} +0 -0
  413. /package/dist/web/assets/{docker-8UGSCc7N.js → docker-BDsTF5ce.js} +0 -0
  414. /package/dist/web/assets/{dotenv-DVnj8kyR.js → dotenv-CbCWp9Ti.js} +0 -0
  415. /package/dist/web/assets/{dracula-D0cEmSg7.js → dracula-Cd1fxvoX.js} +0 -0
  416. /package/dist/web/assets/{dracula-soft-DfGS5Yfr.js → dracula-soft-B7eXkfXG.js} +0 -0
  417. /package/dist/web/assets/{dream-maker-qG8kMGTF.js → dream-maker-XlzuS21R.js} +0 -0
  418. /package/dist/web/assets/{emacs-lisp-iVqyMLyT.js → emacs-lisp-CXV1Ki_7.js} +0 -0
  419. /package/dist/web/assets/{everforest-dark-Cruygd5U.js → everforest-dark-DuqR60Dg.js} +0 -0
  420. /package/dist/web/assets/{everforest-light-BiJa8RHL.js → everforest-light-q4BPnenD.js} +0 -0
  421. /package/dist/web/assets/{fennel-H6XSEIoz.js → fennel-B-e0TX2Q.js} +0 -0
  422. /package/dist/web/assets/{fish-ho_7CxX6.js → fish-Bzb6-TtF.js} +0 -0
  423. /package/dist/web/assets/{fluent-1l5ssJTF.js → fluent-B-szH2-f.js} +0 -0
  424. /package/dist/web/assets/{folder-symlink-Dx1hOk8X.js → folder-symlink-fQRoG_rV.js} +0 -0
  425. /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
  426. /package/dist/web/assets/{fortran-free-form-Bo1x233A.js → fortran-free-form-OS6SGYC0.js} +0 -0
  427. /package/dist/web/assets/{gdscript-DffFMp_k.js → gdscript-FYq4OP2d.js} +0 -0
  428. /package/dist/web/assets/{gdshader-CvnpAiAF.js → gdshader-BpG6P8rw.js} +0 -0
  429. /package/dist/web/assets/{genie-2eOvAEzW.js → genie-BS9RLAna.js} +0 -0
  430. /package/dist/web/assets/{gherkin-DL-5OUlV.js → gherkin-B_9FdtGs.js} +0 -0
  431. /package/dist/web/assets/{github-dark-DrTZprgG.js → github-dark-cfaIlnBV.js} +0 -0
  432. /package/dist/web/assets/{github-dark-default-_mDH8IMP.js → github-dark-default-B2Wv65He.js} +0 -0
  433. /package/dist/web/assets/{github-dark-dimmed-C48hJuLn.js → github-dark-dimmed-CtM9X76s.js} +0 -0
  434. /package/dist/web/assets/{github-dark-high-contrast-BQ-zGGNe.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
  435. /package/dist/web/assets/{github-light-BYc90Yx8.js → github-light-DWEI10iG.js} +0 -0
  436. /package/dist/web/assets/{github-light-default-Daecj4J5.js → github-light-default-DcJkK1YN.js} +0 -0
  437. /package/dist/web/assets/{github-light-high-contrast-DoafCbsU.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
  438. /package/dist/web/assets/{gleam-hUWWjmnp.js → gleam-DVpiM6b2.js} +0 -0
  439. /package/dist/web/assets/{gn-7PJ3R70o.js → gn-B80DNc0i.js} +0 -0
  440. /package/dist/web/assets/{gnuplot-V-Pxck7b.js → gnuplot-D9i3MaT-.js} +0 -0
  441. /package/dist/web/assets/{go-ARF6DprD.js → go-CwCBf_Pt.js} +0 -0
  442. /package/dist/web/assets/{groovy-CKGBIPOz.js → groovy-D7mEfwmW.js} +0 -0
  443. /package/dist/web/assets/{gruvbox-dark-hard-Cm9wqdI3.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
  444. /package/dist/web/assets/{gruvbox-dark-medium-Cmsd6dG8.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
  445. /package/dist/web/assets/{gruvbox-dark-soft-BBGlGGCz.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
  446. /package/dist/web/assets/{gruvbox-light-hard-BCR7KmqG.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
  447. /package/dist/web/assets/{gruvbox-light-medium-CP32bga8.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
  448. /package/dist/web/assets/{gruvbox-light-soft-qjxk8ps6.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
  449. /package/dist/web/assets/{haskell-C5DzwGRa.js → haskell-CsXSYXRG.js} +0 -0
  450. /package/dist/web/assets/{haxe-C8f_jCVv.js → haxe-B1uyiI9t.js} +0 -0
  451. /package/dist/web/assets/{hcl-DetJkf6E.js → hcl-DlYKn5q9.js} +0 -0
  452. /package/dist/web/assets/{hjson-DeZhwIdo.js → hjson-C_0VMGJJ.js} +0 -0
  453. /package/dist/web/assets/{hlsl-SMIDLRyX.js → hlsl-zt2PEzbg.js} +0 -0
  454. /package/dist/web/assets/{horizon-BMAVDxMx.js → horizon-6wQfyOK0.js} +0 -0
  455. /package/dist/web/assets/{horizon-bright-3BaV_VGD.js → horizon-bright-CqI2qTim.js} +0 -0
  456. /package/dist/web/assets/{houston-BWBbEPvb.js → houston-BljpHDoG.js} +0 -0
  457. /package/dist/web/assets/{hy-DcsmE-Nm.js → hy-XzuL55dw.js} +0 -0
  458. /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
  459. /package/dist/web/assets/{imba-CnkgnYMf.js → imba-CqhhjBrP.js} +0 -0
  460. /package/dist/web/assets/{ini-BnvTdau7.js → ini-CiFDi6cY.js} +0 -0
  461. /package/dist/web/assets/{java-BLFACXat.js → java-OsQgTL5-.js} +0 -0
  462. /package/dist/web/assets/{javascript-BKcFWENk.js → javascript-qWAOScxl.js} +0 -0
  463. /package/dist/web/assets/{json-3AJtJADf.js → json-RiAKsaD1.js} +0 -0
  464. /package/dist/web/assets/{json5-DLyDVlwe.js → json5-BeCnS3H2.js} +0 -0
  465. /package/dist/web/assets/{jsonc-B5DgF1jW.js → jsonc-CbTW1Mka.js} +0 -0
  466. /package/dist/web/assets/{jsonl-CV_O71a2.js → jsonl-BLSi_669.js} +0 -0
  467. /package/dist/web/assets/{jsonnet-DZfPLgYz.js → jsonnet-C3xAggou.js} +0 -0
  468. /package/dist/web/assets/{jssm-Cr1zYOsY.js → jssm-CLdiagC9.js} +0 -0
  469. /package/dist/web/assets/{jsx-VN-JS2_q.js → jsx-lYE1lb50.js} +0 -0
  470. /package/dist/web/assets/{kanagawa-dragon-D9CcT3xg.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
  471. /package/dist/web/assets/{kanagawa-lotus-DLXsB8fr.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
  472. /package/dist/web/assets/{kanagawa-wave-weDs3Wjz.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
  473. /package/dist/web/assets/{kdl-0XF56jnT.js → kdl-BfDV0825.js} +0 -0
  474. /package/dist/web/assets/{keyboard-DBBaoQSu.js → keyboard-DiOYrs6C.js} +0 -0
  475. /package/dist/web/assets/{kotlin-BPkH9vzk.js → kotlin-DONxR4wD.js} +0 -0
  476. /package/dist/web/assets/{kusto-DMVpADb9.js → kusto-BDP63Fh5.js} +0 -0
  477. /package/dist/web/assets/{laserwave-Cl6T2cGP.js → laserwave-BeBrWApL.js} +0 -0
  478. /package/dist/web/assets/{lean-BvwzQGl1.js → lean-DOZc0N1A.js} +0 -0
  479. /package/dist/web/assets/{less-DI_03xkC.js → less-C3H9NlDv.js} +0 -0
  480. /package/dist/web/assets/{lib-v9x_qXgI.js → lib-xrSJ4EZX.js} +0 -0
  481. /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
  482. /package/dist/web/assets/{light-plus-hIiIsQN_.js → light-plus-C_kTL608.js} +0 -0
  483. /package/dist/web/assets/{llvm-B5hjjSWc.js → llvm-Dsp27U3P.js} +0 -0
  484. /package/dist/web/assets/{loader-circle-DouKgIXy.js → loader-circle-DPMrnLQ2.js} +0 -0
  485. /package/dist/web/assets/{log-D28raQiE.js → log-BywQaTCy.js} +0 -0
  486. /package/dist/web/assets/{logo-6Cdd4ASb.js → logo-BirJgciO.js} +0 -0
  487. /package/dist/web/assets/{luau-CK9nKpOX.js → luau-Btrz5kJB.js} +0 -0
  488. /package/dist/web/assets/{make-CVnxKCB6.js → make-Kl7nFQrS.js} +0 -0
  489. /package/dist/web/assets/{markdown-B9mMQzD5.js → markdown-qsC4P8S-.js} +0 -0
  490. /package/dist/web/assets/{material-theme-BZSs2o0j.js → material-theme-CC__ui3p.js} +0 -0
  491. /package/dist/web/assets/{material-theme-darker-BeAQBzQ_.js → material-theme-darker-D4FxG2FZ.js} +0 -0
  492. /package/dist/web/assets/{material-theme-lighter-BAe0IXcs.js → material-theme-lighter-zGqiWcxn.js} +0 -0
  493. /package/dist/web/assets/{material-theme-ocean-CiPIf0Np.js → material-theme-ocean-CZIdTTB1.js} +0 -0
  494. /package/dist/web/assets/{material-theme-palenight-DJBb4_XN.js → material-theme-palenight-CEIs0GcK.js} +0 -0
  495. /package/dist/web/assets/{matlab-CY0dbp_s.js → matlab-Cf0aUjN9.js} +0 -0
  496. /package/dist/web/assets/{mdx-DU9zbF7h.js → mdx-CIsXjH86.js} +0 -0
  497. /package/dist/web/assets/{mermaid-UmmjGwdh.js → mermaid-B06WO9Qc.js} +0 -0
  498. /package/dist/web/assets/{min-dark-C59GkzOm.js → min-dark-z8fyp0FT.js} +0 -0
  499. /package/dist/web/assets/{min-light-B9QglrdQ.js → min-light-Dr-qesbW.js} +0 -0
  500. /package/dist/web/assets/{mipsasm-Dc6Jsr_D.js → mipsasm-CWHzYLcE.js} +0 -0
  501. /package/dist/web/assets/{mojo-D5ij-nGW.js → mojo-D9zXrKmv.js} +0 -0
  502. /package/dist/web/assets/{monitor-CxPV_fQI.js → monitor-C835ucWH.js} +0 -0
  503. /package/dist/web/assets/{monokai-QAtE_yCP.js → monokai-4b-AKOpX.js} +0 -0
  504. /package/dist/web/assets/{moonbit-BMtIdiTL.js → moonbit-Dl4dhOZJ.js} +0 -0
  505. /package/dist/web/assets/{move-BB_jMHYZ.js → move-DTYKJmXy.js} +0 -0
  506. /package/dist/web/assets/{narrat-BvvoKGVn.js → narrat-DnvXOS-o.js} +0 -0
  507. /package/dist/web/assets/{nextflow-groovy-CqxtAsnF.js → nextflow-groovy-Di8QnEXg.js} +0 -0
  508. /package/dist/web/assets/{night-owl-CLoE9kjt.js → night-owl-Czh0yq-H.js} +0 -0
  509. /package/dist/web/assets/{night-owl-light-BwIFT5Xd.js → night-owl-light-D_OpZqOR.js} +0 -0
  510. /package/dist/web/assets/{nix-BKlnVT9r.js → nix-2MvcjpK3.js} +0 -0
  511. /package/dist/web/assets/{nord-B7R1KRAK.js → nord-CG_r4cyk.js} +0 -0
  512. /package/dist/web/assets/{nushell-CzUme-NJ.js → nushell-DINskMcU.js} +0 -0
  513. /package/dist/web/assets/{objective-c-D-UKKiOc.js → objective-c-Dv7BIicR.js} +0 -0
  514. /package/dist/web/assets/{objective-cpp-BucWn4Fx.js → objective-cpp-CpqsCxPb.js} +0 -0
  515. /package/dist/web/assets/{ocaml-EJCNXMGo.js → ocaml-DwTsYSmW.js} +0 -0
  516. /package/dist/web/assets/{odin-CEjnRU_3.js → odin-G-4O4k4t.js} +0 -0
  517. /package/dist/web/assets/{one-dark-pro-Bv469NX5.js → one-dark-pro-13Pv5SjI.js} +0 -0
  518. /package/dist/web/assets/{one-light-BlzO5RWI.js → one-light-Bi2e86mS.js} +0 -0
  519. /package/dist/web/assets/{openscad-Czl6alLw.js → openscad-D0WsOTij.js} +0 -0
  520. /package/dist/web/assets/{pascal-BI9fLvG4.js → pascal-DmDStuXr.js} +0 -0
  521. /package/dist/web/assets/{pkl-ChQLsyjU.js → pkl-Bl6Qjtuo.js} +0 -0
  522. /package/dist/web/assets/{plastic-DGL3mtdL.js → plastic-UeSVSjTI.js} +0 -0
  523. /package/dist/web/assets/{plsql-BJDjqcWp.js → plsql-DreFC689.js} +0 -0
  524. /package/dist/web/assets/{po-DK5O3OpF.js → po-ZIjccxsX.js} +0 -0
  525. /package/dist/web/assets/{poimandres-CnuqZT4T.js → poimandres-CvQHJPrI.js} +0 -0
  526. /package/dist/web/assets/{polar-DCsUxSsQ.js → polar-BrMB0Kgh.js} +0 -0
  527. /package/dist/web/assets/{portal-container-context-D-495m5C.js → portal-container-context-a399Hd9F.js} +0 -0
  528. /package/dist/web/assets/{postcss-8UjdFmis.js → postcss-pgUsil1T.js} +0 -0
  529. /package/dist/web/assets/{powerquery-CAp0CGQj.js → powerquery-CTsZkCg_.js} +0 -0
  530. /package/dist/web/assets/{powershell-CbBsKZJP.js → powershell-CcwVuBAp.js} +0 -0
  531. /package/dist/web/assets/{prisma-BnNpXCJN.js → prisma-spjnAI5W.js} +0 -0
  532. /package/dist/web/assets/{prolog-CC-l9E8Y.js → prolog-ChCkZpDO.js} +0 -0
  533. /package/dist/web/assets/{proto-CV_1k2rL.js → proto-wVGqK4gB.js} +0 -0
  534. /package/dist/web/assets/{puppet-2D8HU2bv.js → puppet-CED7iwPQ.js} +0 -0
  535. /package/dist/web/assets/{purescript-BEJAORgU.js → purescript-u9lGiQKV.js} +0 -0
  536. /package/dist/web/assets/{python-BBdbb7nn.js → python-DeqTx6ef.js} +0 -0
  537. /package/dist/web/assets/{qmldir-D5ZYI4Ie.js → qmldir-BH1uoPrX.js} +0 -0
  538. /package/dist/web/assets/{qss-BOJVGyXE.js → qss-CUVKA_13.js} +0 -0
  539. /package/dist/web/assets/{r-B5dlEtkj.js → r-BqdPzqYH.js} +0 -0
  540. /package/dist/web/assets/{racket-BUAEygJ6.js → racket-C2eTgPN3.js} +0 -0
  541. /package/dist/web/assets/{raku-BG0Ss0ID.js → raku-uAay1sWx.js} +0 -0
  542. /package/dist/web/assets/{react-B549sK_T.js → react-X4jUntEf.js} +0 -0
  543. /package/dist/web/assets/{red-C5yyQbtG.js → red-DTZP1ZfC.js} +0 -0
  544. /package/dist/web/assets/{reg-DHoBWtLf.js → reg-Cdk3YsxY.js} +0 -0
  545. /package/dist/web/assets/{regexp-DDtJrlKv.js → regexp-Bpa2aS7K.js} +0 -0
  546. /package/dist/web/assets/{rel-CR6WJDRD.js → rel-bNsb1-3L.js} +0 -0
  547. /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
  548. /package/dist/web/assets/{riscv-2V9tC-nx.js → riscv-DSaubByW.js} +0 -0
  549. /package/dist/web/assets/{ron-QLTmqGUw.js → ron-DbQzYM6i.js} +0 -0
  550. /package/dist/web/assets/{rose-pine-be0__X3A.js → rose-pine-C7RsprTu.js} +0 -0
  551. /package/dist/web/assets/{rose-pine-dawn-n_WQJwsW.js → rose-pine-dawn-VP976YzL.js} +0 -0
  552. /package/dist/web/assets/{rose-pine-moon-t2aLtzgB.js → rose-pine-moon-XHZNhOqd.js} +0 -0
  553. /package/dist/web/assets/{rosmsg-Ce1JD8vt.js → rosmsg-BVQlx0Lj.js} +0 -0
  554. /package/dist/web/assets/{rust-5fHH-ZoW.js → rust-VHaQ3rh3.js} +0 -0
  555. /package/dist/web/assets/{sass-DPmGFiOJ.js → sass-CO-kMkwD.js} +0 -0
  556. /package/dist/web/assets/{save-DYLiVzU8.js → save-CipGrG1k.js} +0 -0
  557. /package/dist/web/assets/{scala-BRMVFbZF.js → scala-cY0GXkiz.js} +0 -0
  558. /package/dist/web/assets/{scheme-BMSJWevF.js → scheme-D4LNZE0X.js} +0 -0
  559. /package/dist/web/assets/{sdbl-DTZ-pEHI.js → sdbl-Bh2Fd2cn.js} +0 -0
  560. /package/dist/web/assets/{shellscript-6-9yg5Cz.js → shellscript-KKclmCnt.js} +0 -0
  561. /package/dist/web/assets/{shield-check-CUI36aSc.js → shield-check-BsG3LV8i.js} +0 -0
  562. /package/dist/web/assets/{shield-off-GdkkVhb3.js → shield-off-7jKWpJBD.js} +0 -0
  563. /package/dist/web/assets/{slack-dark-DKJgiR7e.js → slack-dark-D6YQ-fqf.js} +0 -0
  564. /package/dist/web/assets/{slack-ochin-WjehVgyK.js → slack-ochin-CDf66CDj.js} +0 -0
  565. /package/dist/web/assets/{smalltalk-BChf4SN1.js → smalltalk-D2OH_QIl.js} +0 -0
  566. /package/dist/web/assets/{snazzy-light-C1WAwOx6.js → snazzy-light-CQeF8G1K.js} +0 -0
  567. /package/dist/web/assets/{solarized-dark-BxREZrGx.js → solarized-dark-LTPf1iLZ.js} +0 -0
  568. /package/dist/web/assets/{solarized-light-DG29B1pR.js → solarized-light-Ose1n88k.js} +0 -0
  569. /package/dist/web/assets/{solidity-bDbtGwdR.js → solidity-lFuOMHki.js} +0 -0
  570. /package/dist/web/assets/{splunk-Ceqx5njI.js → splunk-Bov-sB5S.js} +0 -0
  571. /package/dist/web/assets/{sql-OwMEKbz8.js → sql-BKyZ6wmk.js} +0 -0
  572. /package/dist/web/assets/{ssh-config-WAR3lu2k.js → ssh-config-CqJoMGMb.js} +0 -0
  573. /package/dist/web/assets/{stylus-Cjmoz3d8.js → stylus-Bf5pWRLI.js} +0 -0
  574. /package/dist/web/assets/{swift-S_ZSBG1q.js → swift-D5d1hn_u.js} +0 -0
  575. /package/dist/web/assets/{synthwave-84-DiUeWMSh.js → synthwave-84-B4c6YAgO.js} +0 -0
  576. /package/dist/web/assets/{system-verilog-RRjqPjUV.js → system-verilog-D7fXyDsI.js} +0 -0
  577. /package/dist/web/assets/{systemd-BD3SkXoU.js → systemd-oNagzlBK.js} +0 -0
  578. /package/dist/web/assets/{table-D5Q4ap4W.js → table-CRJsmfeg.js} +0 -0
  579. /package/dist/web/assets/{talonscript-DWefISOm.js → talonscript-C6Nqm9Dm.js} +0 -0
  580. /package/dist/web/assets/{tasl-DCjSS-09.js → tasl-CaWy-cBX.js} +0 -0
  581. /package/dist/web/assets/{tcl-DTYqT_66.js → tcl-C435P5Mq.js} +0 -0
  582. /package/dist/web/assets/{terraform-CkbP5QnR.js → terraform-CVDxqdPf.js} +0 -0
  583. /package/dist/web/assets/{text-wrap-DBvQD3kc.js → text-wrap-oiekm5SB.js} +0 -0
  584. /package/dist/web/assets/{tokyo-night-qVSqO9xy.js → tokyo-night-Uhs17tQ3.js} +0 -0
  585. /package/dist/web/assets/{toml-D9F5Osqs.js → toml-j6fPOJ6P.js} +0 -0
  586. /package/dist/web/assets/{tsv-C5tpHoxm.js → tsv-h78O6xrL.js} +0 -0
  587. /package/dist/web/assets/{tsx-CMWkGtGm.js → tsx-Bol-vCrQ.js} +0 -0
  588. /package/dist/web/assets/{turtle-Cn4z3xOi.js → turtle-Dw8eFf04.js} +0 -0
  589. /package/dist/web/assets/{typescript-DSyAEWsn.js → typescript-Dtz7DPci.js} +0 -0
  590. /package/dist/web/assets/{typespec-B93Qap7y.js → typespec-BIOQ7dMP.js} +0 -0
  591. /package/dist/web/assets/{typst-DtlOmFlX.js → typst-CoaGMjzu.js} +0 -0
  592. /package/dist/web/assets/{use-is-mobile-B9UZNwSm.js → use-is-mobile-CYW3FJFE.js} +0 -0
  593. /package/dist/web/assets/{use-visual-viewport-Sfb6CZ80.js → use-visual-viewport-rnvuooQZ.js} +0 -0
  594. /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
  595. /package/dist/web/assets/{v-Dfzf-szS.js → v-BoqFdpJ7.js} +0 -0
  596. /package/dist/web/assets/{vala-CyiQZQ9f.js → vala-COBpNnCC.js} +0 -0
  597. /package/dist/web/assets/{vb-C7DGSldd.js → vb-DxI86znh.js} +0 -0
  598. /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
  599. /package/dist/web/assets/{verilog-kF-hClY2.js → verilog-DcTAamuQ.js} +0 -0
  600. /package/dist/web/assets/{vesper-BmKcADuz.js → vesper-DO298JSv.js} +0 -0
  601. /package/dist/web/assets/{vhdl-CDnb16KJ.js → vhdl-Dq-bDVSN.js} +0 -0
  602. /package/dist/web/assets/{viml-B8cs6Ady.js → viml-zHTRWuQh.js} +0 -0
  603. /package/dist/web/assets/{vitesse-black-BR6o9Gto.js → vitesse-black-jlYMJCBf.js} +0 -0
  604. /package/dist/web/assets/{vitesse-dark-Dye14-KB.js → vitesse-dark-DS0sUlSZ.js} +0 -0
  605. /package/dist/web/assets/{vitesse-light-BLpswIxf.js → vitesse-light-HSeZ2YhG.js} +0 -0
  606. /package/dist/web/assets/{vyper-DU51Z97W.js → vyper-BQfKVO3M.js} +0 -0
  607. /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
  608. /package/dist/web/assets/{wasm-CVqlJAR2.js → wasm-B6mCuI8h.js} +0 -0
  609. /package/dist/web/assets/{wenyan-DhCa3hCn.js → wenyan-CfpS05sP.js} +0 -0
  610. /package/dist/web/assets/{wgsl-Bj1Ec4vv.js → wgsl-14GDsWRD.js} +0 -0
  611. /package/dist/web/assets/{wikitext-C90DHNa0.js → wikitext-Ccbq5EVX.js} +0 -0
  612. /package/dist/web/assets/{wit-DUgbOD7Z.js → wit-C19hb3R5.js} +0 -0
  613. /package/dist/web/assets/{wolfram-BgwUqD1I.js → wolfram-B-ZSwGwP.js} +0 -0
  614. /package/dist/web/assets/{x-DiOUR_mp.js → x-YDu4m2Iq.js} +0 -0
  615. /package/dist/web/assets/{yaml-DEQulj-8.js → yaml-mw_JUeio.js} +0 -0
  616. /package/dist/web/assets/{zenscript-C7Jp7TgT.js → zenscript-WbzDRjvF.js} +0 -0
  617. /package/dist/web/assets/{zig-9x4qXYLg.js → zig-F0vvImTJ.js} +0 -0
@@ -40,6 +40,28 @@ export function isChatUploadPath(resolved: string): boolean {
40
40
  return isInside(resolved, getUploadsDir());
41
41
  }
42
42
 
43
+ /**
44
+ * Images the codex image-generation tool produced. Codex writes them under its
45
+ * own CODEX_HOME, which PPM places inside the PPM dir (one home per account),
46
+ * so `isPpmDirPath` covers them and the chat could not render a picture the
47
+ * assistant had just made.
48
+ *
49
+ * The match is structural rather than a lookup of the account table: the path
50
+ * must be `<ppmDir>/codex-accounts/<something>/generated_images/<at least one
51
+ * more segment>`. That keeps the exception to the one subtree codex fills with
52
+ * generated pictures and leaves the rest of an account home — `auth.json`
53
+ * above all, but equally the session, log, and memory databases beside it —
54
+ * refused, since none of those sit under a `generated_images` segment. Callers
55
+ * pass an already-resolved path, so `..` cannot walk back out of the subtree.
56
+ */
57
+ export function isCodexGeneratedImagePath(resolved: string): boolean {
58
+ const root = resolve(getPpmDir(), "codex-accounts");
59
+ if (!isInside(resolved, root) || resolved === root) return false;
60
+ const rel = resolved.slice(root.length + 1).split(sep);
61
+ // [accountId, "generated_images", …at least one file segment]
62
+ return rel.length >= 3 && rel[1] === "generated_images";
63
+ }
64
+
43
65
  /**
44
66
  * True when the path is `~/.cloudflared` or anything inside it. Real
45
67
  * `homedir()` is a deliberate exception to the getPpmDir()-only rule (see
@@ -79,16 +101,21 @@ export function isCredentialPath(resolved: string): boolean {
79
101
  * tokens; the latter stores the Cloudflare login cert. Neither may be
80
102
  * downloadable through a generic file route.
81
103
  *
82
- * Chat uploads are the one exception, and only for the PPM-dir branch — chat
83
- * uploads always live under `getPpmDir()`, never under `~/.cloudflared`, so
84
- * the exception is a no-op for the cloudflared branch. Every caller applies
85
- * this to the requested path *and* to its real path, so a symlink parked in
86
- * the uploads directory still fails on the second call and cannot reach the
87
- * rest of the PPM dir through the exception, and a symlink pointing at
88
- * `~/.cloudflared/cert.pem` fails the same way.
104
+ * Chat uploads and codex-generated images are the exceptions, and only for the
105
+ * PPM-dir branch — both always live under `getPpmDir()`, never under
106
+ * `~/.cloudflared`, so the exceptions are a no-op for the cloudflared branch.
107
+ * Every caller applies this to the requested path *and* to its real path, so a
108
+ * symlink parked in the uploads directory still fails on the second call and
109
+ * cannot reach the rest of the PPM dir through the exception, and a symlink
110
+ * pointing at `~/.cloudflared/cert.pem` fails the same way.
111
+ *
112
+ * Both exceptions are read-only on purpose: they are absent from
113
+ * `assertNotPpmSubtree`, so nothing can be copied, moved, or written INTO
114
+ * those directories through a generic file route.
89
115
  */
90
116
  export function assertNotPpmDir(resolved: string): void {
91
- if (isCredentialPath(resolved) && !isChatUploadPath(resolved)) {
117
+ const allowed = isChatUploadPath(resolved) || isCodexGeneratedImagePath(resolved);
118
+ if (isCredentialPath(resolved) && !allowed) {
92
119
  throw Object.assign(new Error("Access denied"), { status: 403, code: "EDENIED" });
93
120
  }
94
121
  }
@@ -14,6 +14,7 @@ export { realPathOrSelf, realPathOrSelfSync };
14
14
  export {
15
15
  isPpmDirPath,
16
16
  isChatUploadPath,
17
+ isCodexGeneratedImagePath,
17
18
  isCloudflaredDirPath,
18
19
  isCredentialPath,
19
20
  assertNotPpmDir,
@@ -27,6 +27,39 @@ export interface PgQueryResult {
27
27
  /** Auto-close idle connections after 5 minutes */
28
28
  const IDLE_TIMEOUT_MS = 5 * 60 * 1000;
29
29
 
30
+ /** Seconds allowed for DNS + TCP + TLS + auth before postgres.js gives up.
31
+ * A healthy connect to a remote host over a VPN measures ~1s, so this only has to
32
+ * cover the *first* attempt on a cold network path — the retry below is what
33
+ * actually rescues that case, which is why this stays well under the library
34
+ * default of 30 (a longer value just makes a genuinely dead host take longer to
35
+ * report). */
36
+ const CONNECT_TIMEOUT_SEC = 15;
37
+
38
+ /** Extra attempts after a first attempt that failed before reaching the server. */
39
+ const CONNECT_RETRIES = 1;
40
+
41
+ /** Pause before re-attempting a connect-phase failure. */
42
+ const RETRY_DELAY_MS = 250;
43
+
44
+ /** Seconds to wait for a pool to drain before abandoning it, so evicting a wedged
45
+ * connection can never hang the caller. */
46
+ const END_TIMEOUT_SEC = 5;
47
+
48
+ /** Error codes that prove the statement never reached the server, so replaying it
49
+ * cannot double-apply a write. postgres.js holds a query until the startup
50
+ * handshake completes, so all of these are raised before any SQL is written to the
51
+ * socket. Ambiguous mid-query failures (CONNECTION_CLOSED, ECONNRESET) are
52
+ * deliberately excluded — retrying those could re-run an INSERT or UPDATE. */
53
+ const RETRYABLE_CONNECT_ERRORS = new Set([
54
+ "CONNECT_TIMEOUT",
55
+ "ECONNREFUSED",
56
+ "ETIMEDOUT",
57
+ "EHOSTUNREACH",
58
+ "ENETUNREACH",
59
+ "ENOTFOUND",
60
+ "EAI_AGAIN",
61
+ ]);
62
+
30
63
  interface CachedConn {
31
64
  sql: postgres.Sql;
32
65
  timer: ReturnType<typeof setTimeout>;
@@ -49,7 +82,12 @@ class PostgresService {
49
82
  const sslOpts = sslmode === "no-verify" || sslmode === "require"
50
83
  ? { rejectUnauthorized: false }
51
84
  : sslmode === "disable" ? false : undefined;
52
- const sql = postgres(connectionString, { max: 3, idle_timeout: 60, connect_timeout: 10, ssl: sslOpts as any });
85
+ const sql = postgres(connectionString, {
86
+ max: 3,
87
+ idle_timeout: 60,
88
+ connect_timeout: CONNECT_TIMEOUT_SEC,
89
+ ssl: sslOpts as any,
90
+ });
53
91
  const timer = setTimeout(() => this.disconnect(connectionString), IDLE_TIMEOUT_MS);
54
92
  this.cache.set(connectionString, { sql, timer });
55
93
  return sql;
@@ -60,15 +98,45 @@ class PostgresService {
60
98
  const cached = this.cache.get(connectionString);
61
99
  if (!cached) return;
62
100
  clearTimeout(cached.timer);
63
- try { await cached.sql.end(); } catch { /* already closed */ }
101
+ // Drop the cache entry first: end() on a pool whose socket never came up can
102
+ // stall for the full drain timeout, and callers must not see it again meanwhile.
64
103
  this.cache.delete(connectionString);
104
+ try { await cached.sql.end({ timeout: END_TIMEOUT_SEC }); } catch { /* already closed */ }
105
+ }
106
+
107
+ /** True when the error happened before the query was written to the socket. */
108
+ private isRetryableConnectError(e: unknown): boolean {
109
+ const code = (e as { code?: unknown } | null)?.code;
110
+ return typeof code === "string" && RETRYABLE_CONNECT_ERRORS.has(code);
111
+ }
112
+
113
+ /** Run an operation against the cached pool, retrying if the connection never
114
+ * got established. The first connect over a cold VPN path can burn ~20s on TCP
115
+ * SYN retransmits and then fail outright, while an immediate second attempt
116
+ * succeeds in about a second — without this, that first failure surfaces to the
117
+ * user as a hard error. Only connect-phase failures are replayed, so a retried
118
+ * write cannot be applied twice. */
119
+ private async withConnection<T>(
120
+ connectionString: string,
121
+ fn: (sql: postgres.Sql) => Promise<T>,
122
+ ): Promise<T> {
123
+ for (let attempt = 0; ; attempt++) {
124
+ const sql = this.connect(connectionString);
125
+ try {
126
+ return await fn(sql);
127
+ } catch (e) {
128
+ if (attempt >= CONNECT_RETRIES || !this.isRetryableConnectError(e)) throw e;
129
+ // Discard the pool that failed to connect so the retry starts clean.
130
+ await this.disconnect(connectionString);
131
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
132
+ }
133
+ }
65
134
  }
66
135
 
67
136
  /** Test connection */
68
137
  async testConnection(connectionString: string): Promise<{ ok: boolean; error?: string }> {
69
138
  try {
70
- const sql = this.connect(connectionString);
71
- await sql`SELECT 1`;
139
+ await this.withConnection(connectionString, (sql) => sql`SELECT 1`);
72
140
  return { ok: true };
73
141
  } catch (e) {
74
142
  return { ok: false, error: (e as Error).message };
@@ -77,15 +145,14 @@ class PostgresService {
77
145
 
78
146
  /** List all user tables with row counts */
79
147
  async getTables(connectionString: string): Promise<PgTableInfo[]> {
80
- const sql = this.connect(connectionString);
81
- const tables = await sql`
148
+ const tables = await this.withConnection(connectionString, (sql) => sql`
82
149
  SELECT t.schemaname as schema, t.tablename as name,
83
150
  COALESCE(s.n_live_tup, 0)::int as row_count
84
151
  FROM pg_tables t
85
152
  LEFT JOIN pg_stat_user_tables s ON t.schemaname = s.schemaname AND t.tablename = s.relname
86
153
  WHERE t.schemaname NOT IN ('pg_catalog', 'information_schema')
87
154
  ORDER BY t.schemaname, t.tablename
88
- `;
155
+ `);
89
156
  return tables.map((t) => ({
90
157
  name: t.name as string, schema: t.schema as string, rowCount: t.row_count as number,
91
158
  }));
@@ -93,33 +160,36 @@ class PostgresService {
93
160
 
94
161
  /** Get column schema for a table (with FK metadata) */
95
162
  async getTableSchema(connectionString: string, table: string, schema = "public"): Promise<PgColumnInfo[]> {
96
- const sql = this.connect(connectionString);
97
- const cols = await sql`
98
- SELECT c.column_name as name, c.data_type as type,
99
- c.is_nullable = 'YES' as nullable, c.column_default as default_value,
100
- COALESCE(tc.constraint_type = 'PRIMARY KEY', false) as pk
101
- FROM information_schema.columns c
102
- LEFT JOIN information_schema.key_column_usage kcu
103
- ON c.table_schema = kcu.table_schema AND c.table_name = kcu.table_name AND c.column_name = kcu.column_name
104
- LEFT JOIN information_schema.table_constraints tc
105
- ON kcu.constraint_name = tc.constraint_name AND tc.constraint_type = 'PRIMARY KEY'
106
- WHERE c.table_schema = ${schema} AND c.table_name = ${table}
107
- ORDER BY c.ordinal_position
108
- `;
109
-
110
- // Query FK references
111
- const fkRows = await sql`
112
- SELECT kcu.column_name as from_col,
113
- ccu.table_name as ref_table,
114
- ccu.column_name as ref_col
115
- FROM information_schema.table_constraints tc
116
- JOIN information_schema.key_column_usage kcu
117
- ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
118
- JOIN information_schema.constraint_column_usage ccu
119
- ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
120
- WHERE tc.constraint_type = 'FOREIGN KEY'
121
- AND tc.table_schema = ${schema} AND tc.table_name = ${table}
122
- `;
163
+ const { cols, fkRows } = await this.withConnection(connectionString, async (sql) => {
164
+ const cols = await sql`
165
+ SELECT c.column_name as name, c.data_type as type,
166
+ c.is_nullable = 'YES' as nullable, c.column_default as default_value,
167
+ COALESCE(tc.constraint_type = 'PRIMARY KEY', false) as pk
168
+ FROM information_schema.columns c
169
+ LEFT JOIN information_schema.key_column_usage kcu
170
+ ON c.table_schema = kcu.table_schema AND c.table_name = kcu.table_name AND c.column_name = kcu.column_name
171
+ LEFT JOIN information_schema.table_constraints tc
172
+ ON kcu.constraint_name = tc.constraint_name AND tc.constraint_type = 'PRIMARY KEY'
173
+ WHERE c.table_schema = ${schema} AND c.table_name = ${table}
174
+ ORDER BY c.ordinal_position
175
+ `;
176
+
177
+ // Query FK references
178
+ const fkRows = await sql`
179
+ SELECT kcu.column_name as from_col,
180
+ ccu.table_name as ref_table,
181
+ ccu.column_name as ref_col
182
+ FROM information_schema.table_constraints tc
183
+ JOIN information_schema.key_column_usage kcu
184
+ ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
185
+ JOIN information_schema.constraint_column_usage ccu
186
+ ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
187
+ WHERE tc.constraint_type = 'FOREIGN KEY'
188
+ AND tc.table_schema = ${schema} AND tc.table_name = ${table}
189
+ `;
190
+ return { cols, fkRows };
191
+ });
192
+
123
193
  const fkMap = new Map<string, { table: string; column: string }>();
124
194
  for (const fk of fkRows) {
125
195
  fkMap.set(fk.from_col as string, { table: fk.ref_table as string, column: fk.ref_col as string });
@@ -140,22 +210,24 @@ class PostgresService {
140
210
  connectionString: string, table: string, schema = "public",
141
211
  page = 1, limit = 100, orderBy?: string, orderDir: "ASC" | "DESC" = "ASC",
142
212
  ): Promise<{ columns: string[]; rows: Record<string, unknown>[]; total: number; page: number; limit: number }> {
143
- const sql = this.connect(connectionString);
144
- const fullTable = sql(`${schema}.${table}`);
145
-
146
- const [countRow] = await sql`SELECT COUNT(*)::int as cnt FROM ${fullTable}`;
147
- const total = (countRow?.cnt as number) ?? 0;
148
213
  const offset = (page - 1) * limit;
214
+ const { rows, total } = await this.withConnection(connectionString, async (sql) => {
215
+ const fullTable = sql(`${schema}.${table}`);
149
216
 
150
- let rows: postgres.RowList<postgres.Row[]>;
151
- if (orderBy) {
152
- const orderCol = sql(orderBy);
153
- rows = orderDir === "DESC"
154
- ? await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} DESC LIMIT ${limit} OFFSET ${offset}`
155
- : await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} ASC LIMIT ${limit} OFFSET ${offset}`;
156
- } else {
157
- rows = await sql`SELECT * FROM ${fullTable} LIMIT ${limit} OFFSET ${offset}`;
158
- }
217
+ const [countRow] = await sql`SELECT COUNT(*)::int as cnt FROM ${fullTable}`;
218
+ const total = (countRow?.cnt as number) ?? 0;
219
+
220
+ let rows: postgres.RowList<postgres.Row[]>;
221
+ if (orderBy) {
222
+ const orderCol = sql(orderBy);
223
+ rows = orderDir === "DESC"
224
+ ? await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} DESC LIMIT ${limit} OFFSET ${offset}`
225
+ : await sql`SELECT * FROM ${fullTable} ORDER BY ${orderCol} ASC LIMIT ${limit} OFFSET ${offset}`;
226
+ } else {
227
+ rows = await sql`SELECT * FROM ${fullTable} LIMIT ${limit} OFFSET ${offset}`;
228
+ }
229
+ return { rows, total };
230
+ });
159
231
 
160
232
  const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
161
233
  return { columns, rows: rows as unknown as Record<string, unknown>[], total, page, limit };
@@ -163,7 +235,6 @@ class PostgresService {
163
235
 
164
236
  /** Execute arbitrary SQL */
165
237
  async executeQuery(connectionString: string, sqlText: string): Promise<PgQueryResult> {
166
- const sql = this.connect(connectionString);
167
238
  const trimmed = sqlText.trim();
168
239
  const upper = trimmed.toUpperCase();
169
240
 
@@ -187,34 +258,37 @@ class PostgresService {
187
258
  const isSelect = upper.startsWith("SELECT") || upper.startsWith("WITH") ||
188
259
  upper.startsWith("EXPLAIN") || upper.startsWith("SHOW") || upper.startsWith("\\D");
189
260
 
190
- const start = performance.now();
191
- if (isSelect) {
192
- const rows = await sql.unsafe(sqlText);
193
- const executionTimeMs = Math.round(performance.now() - start);
194
- const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
195
- return { columns, rows: rows as unknown as Record<string, unknown>[], rowsAffected: 0, changeType: "select", executionTimeMs };
196
- }
261
+ return this.withConnection(connectionString, async (sql) => {
262
+ const start = performance.now();
263
+ if (isSelect) {
264
+ const rows = await sql.unsafe(sqlText);
265
+ const executionTimeMs = Math.round(performance.now() - start);
266
+ const columns = rows.length > 0 ? Object.keys(rows[0]!) : [];
267
+ return { columns, rows: rows as unknown as Record<string, unknown>[], rowsAffected: 0, changeType: "select" as const, executionTimeMs };
268
+ }
197
269
 
198
- const result = await sql.unsafe(sqlText);
199
- const executionTimeMs = Math.round(performance.now() - start);
200
- return { columns: [], rows: [], rowsAffected: result.count ?? 0, changeType: "modify", executionTimeMs };
270
+ const result = await sql.unsafe(sqlText);
271
+ const executionTimeMs = Math.round(performance.now() - start);
272
+ return { columns: [], rows: [], rowsAffected: result.count ?? 0, changeType: "modify" as const, executionTimeMs };
273
+ });
201
274
  }
202
275
 
203
276
  /** Execute multi-statement SQL script inside a transaction via sql.begin().
204
277
  * Strips user-supplied BEGIN/COMMIT/ROLLBACK since sql.begin() manages the transaction. */
205
278
  async executeScript(connectionString: string, scriptText: string): Promise<{ statementsRun: number; executionTimeMs: number }> {
206
- const sql = this.connect(connectionString);
207
279
  const txControl = /^(BEGIN|COMMIT|ROLLBACK|END)(;|\s|$)/i;
208
280
  const statements = splitSqlStatements(scriptText).filter((s) => !txControl.test(s));
209
281
  if (statements.length === 0) return { statementsRun: 0, executionTimeMs: 0 };
210
282
 
211
- const start = performance.now();
212
- await sql.begin(async (tx) => {
213
- for (const stmt of statements) {
214
- await tx.unsafe(stmt);
215
- }
283
+ return this.withConnection(connectionString, async (sql) => {
284
+ const start = performance.now();
285
+ await sql.begin(async (tx) => {
286
+ for (const stmt of statements) {
287
+ await tx.unsafe(stmt);
288
+ }
289
+ });
290
+ return { statementsRun: statements.length, executionTimeMs: Math.round(performance.now() - start) };
216
291
  });
217
- return { statementsRun: statements.length, executionTimeMs: Math.round(performance.now() - start) };
218
292
  }
219
293
 
220
294
  /** Update a single cell value */
@@ -222,11 +296,10 @@ class PostgresService {
222
296
  connectionString: string, table: string, schema = "public",
223
297
  pkColumn: string, pkValue: unknown, column: string, value: unknown,
224
298
  ): Promise<void> {
225
- const sql = this.connect(connectionString);
226
- await sql.unsafe(
299
+ await this.withConnection(connectionString, (sql) => sql.unsafe(
227
300
  `UPDATE "${schema}"."${table}" SET "${column}" = $1 WHERE "${pkColumn}" = $2`,
228
301
  [value as never, pkValue as never],
229
- );
302
+ ));
230
303
  }
231
304
 
232
305
  /** Delete a row by primary key */
@@ -234,11 +307,10 @@ class PostgresService {
234
307
  connectionString: string, table: string, schema = "public",
235
308
  pkColumn: string, pkValue: unknown,
236
309
  ): Promise<void> {
237
- const sql = this.connect(connectionString);
238
- await sql.unsafe(
310
+ await this.withConnection(connectionString, (sql) => sql.unsafe(
239
311
  `DELETE FROM "${schema}"."${table}" WHERE "${pkColumn}" = $1`,
240
312
  [pkValue as never],
241
- );
313
+ ));
242
314
  }
243
315
 
244
316
  /** Close all cached connections */
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared provider-usage layer.
3
+ *
4
+ * Adding a provider means writing a {@link ProviderUsageSource} — how to list
5
+ * its accounts and how to read one account's quota — and registering it. The
6
+ * background sweep, fetch timeout, in-memory cache, negative caching, snapshot
7
+ * persistence, and the synchronous read path all come with it.
8
+ *
9
+ * Registration lives in `register-usage-sources.ts` so this module stays free
10
+ * of imports from any particular provider.
11
+ */
12
+ export {
13
+ AMBIENT_ACCOUNT_KEY,
14
+ DEFAULT_POLL_INTERVAL_MS,
15
+ FETCH_TIMEOUT_MS,
16
+ USAGE_CACHE_TTL_MS,
17
+ USAGE_FAILURE_TTL_MS,
18
+ type ProviderUsageSource,
19
+ } from "./usage-source.ts";
20
+
21
+ export {
22
+ registerUsageSource,
23
+ getUsageSource,
24
+ listUsageSources,
25
+ getUsage,
26
+ getOrFetchUsage,
27
+ refreshUsage,
28
+ invalidateUsage,
29
+ _clearUsageSources,
30
+ } from "./usage-registry.ts";
31
+
32
+ export {
33
+ startProviderUsagePolling,
34
+ stopProviderUsagePolling,
35
+ sweepUsageSource,
36
+ _isUsagePollingActive,
37
+ } from "./usage-scheduler.ts";
38
+
39
+ export {
40
+ snapshotToUsage,
41
+ readStoredUsage,
42
+ writeStoredUsage,
43
+ } from "./usage-snapshot-store.ts";
44
+
45
+ export { _clearUsageCache, invalidateCachedUsage } from "./usage-memory-cache.ts";
46
+
47
+ import { _clearUsageCache } from "./usage-memory-cache.ts";
48
+ import { _clearInflightUsage } from "./usage-registry.ts";
49
+ import { _clearInflightSweeps, stopProviderUsagePolling } from "./usage-scheduler.ts";
50
+
51
+ /**
52
+ * Drop every piece of live state the layer holds: timers, in-flight fetches and
53
+ * sweeps, and the memory cache. Stored snapshots are left alone — they are the
54
+ * caller's data, not this layer's state.
55
+ *
56
+ * Used by tests, and by the production path that has to abandon a stuck sweep.
57
+ */
58
+ export function resetUsageRuntimeState(): void {
59
+ stopProviderUsagePolling();
60
+ _clearInflightSweeps();
61
+ _clearInflightUsage();
62
+ _clearUsageCache();
63
+ }
@@ -0,0 +1,18 @@
1
+ import { registerUsageSource } from "./usage-registry.ts";
2
+ import { claudeUsageSource } from "../claude-usage-source.ts";
3
+ import { codexUsageSource } from "../../providers/codex-app-server/codex-usage-source.ts";
4
+
5
+ /**
6
+ * Wires the concrete providers into the shared usage layer.
7
+ *
8
+ * Separate from `index.ts` so the layer itself imports no provider, and a
9
+ * provider importing the layer cannot create a cycle back through registration.
10
+ * Adding a provider is one line here plus its own source file.
11
+ *
12
+ * Idempotent: registration is keyed by provider id, so calling it again (a
13
+ * `bun --hot` reload, a test re-import) replaces rather than duplicates.
14
+ */
15
+ export function registerAllUsageSources(): void {
16
+ registerUsageSource(claudeUsageSource);
17
+ registerUsageSource(codexUsageSource);
18
+ }
@@ -0,0 +1,71 @@
1
+ import type { UsageInfo } from "../../types/chat.ts";
2
+ import { USAGE_CACHE_TTL_MS, USAGE_FAILURE_TTL_MS } from "./usage-source.ts";
3
+
4
+ /**
5
+ * In-memory layer in front of the snapshot store, keyed by provider + account.
6
+ *
7
+ * Two things live here that the store cannot express. One is freshness: the
8
+ * store knows the last value but not whether it is worth re-reading yet. The
9
+ * other is failure — a rejected fetch has nothing to persist, yet must still be
10
+ * remembered, or every request that misses re-attempts it. For codex that meant
11
+ * spawning an app-server per request, and a spawn that hangs is never reaped.
12
+ */
13
+
14
+ interface Entry {
15
+ /** Absent for a remembered failure. */
16
+ usage?: UsageInfo;
17
+ expiry: number;
18
+ failed: boolean;
19
+ }
20
+
21
+ const cache = new Map<string, Entry>();
22
+
23
+ function key(providerId: string, accountId: string): string {
24
+ return `${providerId}\0${accountId}`;
25
+ }
26
+
27
+ /** Cached value if still fresh; `undefined` when absent, stale, or a failure. */
28
+ export function getCachedUsageEntry(providerId: string, accountId: string): UsageInfo | undefined {
29
+ const hit = cache.get(key(providerId, accountId));
30
+ if (!hit || Date.now() >= hit.expiry || hit.failed) return undefined;
31
+ return hit.usage;
32
+ }
33
+
34
+ /**
35
+ * True while a recent failure is still remembered — the caller should neither
36
+ * re-fetch nor treat the absence as "never tried".
37
+ */
38
+ export function isRecentlyFailed(providerId: string, accountId: string): boolean {
39
+ const hit = cache.get(key(providerId, accountId));
40
+ return !!hit && hit.failed && Date.now() < hit.expiry;
41
+ }
42
+
43
+ export function setCachedUsage(providerId: string, accountId: string, usage: UsageInfo): void {
44
+ cache.set(key(providerId, accountId), {
45
+ usage,
46
+ expiry: Date.now() + USAGE_CACHE_TTL_MS,
47
+ failed: false,
48
+ });
49
+ }
50
+
51
+ export function setCachedFailure(providerId: string, accountId: string): void {
52
+ cache.set(key(providerId, accountId), {
53
+ expiry: Date.now() + USAGE_FAILURE_TTL_MS,
54
+ failed: true,
55
+ });
56
+ }
57
+
58
+ /** Drop one entry so the next read re-fetches — used by an explicit refresh. */
59
+ export function invalidateCachedUsage(providerId: string, accountId?: string): void {
60
+ if (accountId !== undefined) {
61
+ cache.delete(key(providerId, accountId));
62
+ return;
63
+ }
64
+ const prefix = `${providerId}\0`;
65
+ for (const k of cache.keys()) if (k.startsWith(prefix)) cache.delete(k);
66
+ }
67
+
68
+ /** Test seam: forget everything. */
69
+ export function _clearUsageCache(): void {
70
+ cache.clear();
71
+ }