@hienlh/ppm 0.23.4 → 0.23.6

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 (302) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +2 -1
  5. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js +1 -0
  6. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.br +0 -0
  7. package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.gz +0 -0
  8. package/dist/web/assets/{ai-resource-editor-DKcsVDle.js → ai-resource-editor-Dn_wB-o5.js} +1 -1
  9. package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.br +0 -0
  10. package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.gz +0 -0
  11. package/dist/web/assets/{ai-settings-section-D7zJ-hCO.js → ai-settings-section-CEbW8jRy.js} +1 -1
  12. package/dist/web/assets/ai-settings-section-CEbW8jRy.js.br +0 -0
  13. package/dist/web/assets/ai-settings-section-CEbW8jRy.js.gz +0 -0
  14. package/dist/web/assets/{ai-settings-section-OPqC2bYG.js → ai-settings-section-D-mwEYFZ.js} +1 -1
  15. package/dist/web/assets/{appearance-settings-section-DL466x83.js → appearance-settings-section-tw6TpFb6.js} +1 -1
  16. package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.br +0 -0
  17. package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.gz +0 -0
  18. package/dist/web/assets/{branch-review-tab-Dag86n_R.js → branch-review-tab-MBuMWp_i.js} +1 -1
  19. package/dist/web/assets/branch-review-tab-MBuMWp_i.js.br +0 -0
  20. package/dist/web/assets/branch-review-tab-MBuMWp_i.js.gz +0 -0
  21. package/dist/web/assets/{chat-tab-QFDhzzBZ.js → chat-tab-DVvnqhw6.js} +1 -1
  22. package/dist/web/assets/chat-tab-DVvnqhw6.js.br +0 -0
  23. package/dist/web/assets/chat-tab-DVvnqhw6.js.gz +0 -0
  24. package/dist/web/assets/chat-tab-DaEHva22.js +15 -0
  25. package/dist/web/assets/chat-tab-DaEHva22.js.br +0 -0
  26. package/dist/web/assets/chat-tab-DaEHva22.js.gz +0 -0
  27. package/dist/web/assets/{code-editor-K-IN8VwJ.js → code-editor-Bu9lhTVp.js} +3 -3
  28. package/dist/web/assets/code-editor-Bu9lhTVp.js.br +0 -0
  29. package/dist/web/assets/code-editor-Bu9lhTVp.js.gz +0 -0
  30. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js +1 -0
  31. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.br +0 -0
  32. package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.gz +0 -0
  33. package/dist/web/assets/{conflict-editor-qMGJH0Bx.js → conflict-editor-BA3y-qSH.js} +1 -1
  34. package/dist/web/assets/conflict-editor-BA3y-qSH.js.br +0 -0
  35. package/dist/web/assets/conflict-editor-BA3y-qSH.js.gz +0 -0
  36. package/dist/web/assets/{csv-preview-D2I742dl.js → csv-preview-BioxkrQF.js} +1 -1
  37. package/dist/web/assets/csv-preview-BioxkrQF.js.br +0 -0
  38. package/dist/web/assets/csv-preview-BioxkrQF.js.gz +0 -0
  39. package/dist/web/assets/{database-viewer-Bz4LjgIa.js → database-viewer-BSoJ0E6p.js} +1 -1
  40. package/dist/web/assets/database-viewer-BSoJ0E6p.js.br +0 -0
  41. package/dist/web/assets/database-viewer-BSoJ0E6p.js.gz +0 -0
  42. package/dist/web/assets/{design-tab-C987MtMR.js → design-tab-iU2CviE3.js} +1 -1
  43. package/dist/web/assets/design-tab-iU2CviE3.js.br +0 -0
  44. package/dist/web/assets/design-tab-iU2CviE3.js.gz +0 -0
  45. package/dist/web/assets/{designs-sidebar-panel-CapP1yXq.js → designs-sidebar-panel-DHh5QsH9.js} +1 -1
  46. package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.br +0 -0
  47. package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.gz +0 -0
  48. package/dist/web/assets/{diff-viewer-CdXi7cZN.js → diff-viewer-lctWoSmG.js} +1 -1
  49. package/dist/web/assets/diff-viewer-lctWoSmG.js.br +0 -0
  50. package/dist/web/assets/diff-viewer-lctWoSmG.js.gz +0 -0
  51. package/dist/web/assets/{editor-language-service-Zle_PZV8.js → editor-language-service-fPG56hQO.js} +1 -1
  52. package/dist/web/assets/editor-language-service-fPG56hQO.js.br +0 -0
  53. package/dist/web/assets/editor-language-service-fPG56hQO.js.gz +0 -0
  54. package/dist/web/assets/{explorer-body-DHMQR1Rk.js → explorer-body-BgF7FsxI.js} +1 -1
  55. package/dist/web/assets/explorer-body-BgF7FsxI.js.br +0 -0
  56. package/dist/web/assets/explorer-body-BgF7FsxI.js.gz +0 -0
  57. package/dist/web/assets/{explorer-window-content-Df40nQBk.js → explorer-window-content-C21w_7lf.js} +1 -1
  58. package/dist/web/assets/explorer-window-content-C21w_7lf.js.br +0 -0
  59. package/dist/web/assets/explorer-window-content-C21w_7lf.js.gz +0 -0
  60. package/dist/web/assets/{extension-manager-section-B2nZzJ2b.js → extension-manager-section-rbkXsZVR.js} +1 -1
  61. package/dist/web/assets/extension-manager-section-rbkXsZVR.js.br +0 -0
  62. package/dist/web/assets/extension-manager-section-rbkXsZVR.js.gz +0 -0
  63. package/dist/web/assets/{extension-webview-BGoDxkdF.js → extension-webview-uPO0WgCq.js} +1 -1
  64. package/dist/web/assets/extension-webview-uPO0WgCq.js.br +0 -0
  65. package/dist/web/assets/extension-webview-uPO0WgCq.js.gz +0 -0
  66. package/dist/web/assets/{files-settings-section-DC3OzzOl.js → files-settings-section-CT7mDb0L.js} +1 -1
  67. package/dist/web/assets/files-settings-section-CT7mDb0L.js.br +0 -0
  68. package/dist/web/assets/files-settings-section-CT7mDb0L.js.gz +0 -0
  69. package/dist/web/assets/{general-settings-section-i41W43ER.js → general-settings-section-DANPfMRN.js} +1 -1
  70. package/dist/web/assets/general-settings-section-DANPfMRN.js.br +0 -0
  71. package/dist/web/assets/general-settings-section-DANPfMRN.js.gz +0 -0
  72. package/dist/web/assets/{git-log-panel-DHzPIkJg.js → git-log-panel-BJPkLiEw.js} +1 -1
  73. package/dist/web/assets/git-log-panel-BJPkLiEw.js.br +0 -0
  74. package/dist/web/assets/git-log-panel-BJPkLiEw.js.gz +0 -0
  75. package/dist/web/assets/{glide-data-grid-Dj4vrDvj.js → glide-data-grid-R4PcAntf.js} +1 -1
  76. package/dist/web/assets/glide-data-grid-R4PcAntf.js.br +0 -0
  77. package/dist/web/assets/glide-data-grid-R4PcAntf.js.gz +0 -0
  78. package/dist/web/assets/{group-chat-tab-DfwZb-fI.js → group-chat-tab-BXMdo8w9.js} +1 -1
  79. package/dist/web/assets/group-chat-tab-BXMdo8w9.js.br +0 -0
  80. package/dist/web/assets/group-chat-tab-BXMdo8w9.js.gz +0 -0
  81. package/dist/web/assets/{index-jG9mLs-J.js → index-C8nGqkKN.js} +5 -5
  82. package/dist/web/assets/index-C8nGqkKN.js.br +0 -0
  83. package/dist/web/assets/index-C8nGqkKN.js.gz +0 -0
  84. package/dist/web/assets/{index-DMA-So69.css → index-DvClbTQx.css} +1 -1
  85. package/dist/web/assets/index-DvClbTQx.css.br +0 -0
  86. package/dist/web/assets/{index-DMA-So69.css.gz → index-DvClbTQx.css.gz} +0 -0
  87. package/dist/web/assets/{jira-watcher-section-BJ9edGEP.js → jira-watcher-section-DHRoyWcG.js} +1 -1
  88. package/dist/web/assets/{keybindings-store-BlapkC6O.js → keybindings-store-BGKlQQAv.js} +1 -1
  89. package/dist/web/assets/{keyboard-shortcuts-section-CxdGSNGr.js → keyboard-shortcuts-section-BKej4mUf.js} +1 -1
  90. package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.br +0 -0
  91. package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.gz +0 -0
  92. package/dist/web/assets/{markdown-renderer-ok96IgnS.js → markdown-renderer-BHtYqkYZ.js} +1 -1
  93. package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.br +0 -0
  94. package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.gz +0 -0
  95. package/dist/web/assets/{markdown-renderer-DJ9EVU1B.js → markdown-renderer-vDNuKjel.js} +1 -1
  96. package/dist/web/assets/{mobile-explorer-sheet-Cc03LUKy.js → mobile-explorer-sheet-DtvxS7-r.js} +1 -1
  97. package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.br +0 -0
  98. package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.gz +0 -0
  99. package/dist/web/assets/{new-design-dialog-Drkjb_LI.js → new-design-dialog-DS_XrpTG.js} +1 -1
  100. package/dist/web/assets/new-design-dialog-DS_XrpTG.js.br +0 -0
  101. package/dist/web/assets/new-design-dialog-DS_XrpTG.js.gz +0 -0
  102. package/dist/web/assets/{notification-store-DCyxho0O.js → notification-store-Dnnj20Ky.js} +1 -1
  103. package/dist/web/assets/{ppmbot-settings-section-C8PFlvaq.js → ppmbot-settings-section-D8AES1BA.js} +1 -1
  104. package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.br +0 -0
  105. package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.gz +0 -0
  106. package/dist/web/assets/{problems-panel-CFYO0Ro3.js → problems-panel-DTBG4beI.js} +1 -1
  107. package/dist/web/assets/problems-panel-DTBG4beI.js.br +0 -0
  108. package/dist/web/assets/problems-panel-DTBG4beI.js.gz +0 -0
  109. package/dist/web/assets/{process-table-BVeRguFg.js → process-table-D_gR2XA6.js} +1 -1
  110. package/dist/web/assets/process-table-D_gR2XA6.js.br +0 -0
  111. package/dist/web/assets/process-table-D_gR2XA6.js.gz +0 -0
  112. package/dist/web/assets/{proxy-settings-section-BcHIEANE.js → proxy-settings-section-CqtmbeRj.js} +1 -1
  113. package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.br +0 -0
  114. package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.gz +0 -0
  115. package/dist/web/assets/{remote-desktop-clipboard-notice-BGsp0edA.js → remote-desktop-clipboard-notice-Djt6cpNL.js} +1 -1
  116. package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.br +0 -0
  117. package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.gz +0 -0
  118. package/dist/web/assets/{remote-desktop-mobile-sheet-DxkmF8qb.js → remote-desktop-mobile-sheet-BQxhJUkm.js} +2 -2
  119. package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.br +0 -0
  120. package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.gz +0 -0
  121. package/dist/web/assets/{remote-desktop-mobile-view-DMD0M9Jh.js → remote-desktop-mobile-view-DGnT8RY7.js} +1 -1
  122. package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.br +0 -0
  123. package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.gz +0 -0
  124. package/dist/web/assets/{remote-desktop-readiness-gate-CsZCCRJI.js → remote-desktop-readiness-gate-DNJF8TcF.js} +1 -1
  125. package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.br +0 -0
  126. package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.gz +0 -0
  127. package/dist/web/assets/{remote-desktop-window-content-DrHw_vwY.js → remote-desktop-window-content-Ds0DKmM0.js} +1 -1
  128. package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.br +0 -0
  129. package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.gz +0 -0
  130. package/dist/web/assets/{schedules-settings-section-Ci0O2Ng6.js → schedules-settings-section-BKoPovy2.js} +1 -1
  131. package/dist/web/assets/schedules-settings-section-BKoPovy2.js.br +0 -0
  132. package/dist/web/assets/schedules-settings-section-BKoPovy2.js.gz +0 -0
  133. package/dist/web/assets/{settings-body-B6JGOIYb.js → settings-body-k2qPgRFe.js} +2 -2
  134. package/dist/web/assets/settings-body-k2qPgRFe.js.br +0 -0
  135. package/dist/web/assets/settings-body-k2qPgRFe.js.gz +0 -0
  136. package/dist/web/assets/{settings-tab-CIQM4fNE.js → settings-tab-Bb23VdBu.js} +1 -1
  137. package/dist/web/assets/{settings-window-content-BGNNz-E5.js → settings-window-content-ClarWusz.js} +1 -1
  138. package/dist/web/assets/{sql-query-editor-4iCGE4h2.js → sql-query-editor-BjIMi9Wk.js} +1 -1
  139. package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.br +0 -0
  140. package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.gz +0 -0
  141. package/dist/web/assets/{sqlite-viewer-CK6DulYk.js → sqlite-viewer-BNcA6Irq.js} +1 -1
  142. package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.br +0 -0
  143. package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.gz +0 -0
  144. package/dist/web/assets/{system-monitor-body-J9JtvAWV.js → system-monitor-body-D0T3ggt2.js} +2 -2
  145. package/dist/web/assets/system-monitor-body-D0T3ggt2.js.br +0 -0
  146. package/dist/web/assets/system-monitor-body-D0T3ggt2.js.gz +0 -0
  147. package/dist/web/assets/{system-monitor-tab-DjBeRE81.js → system-monitor-tab-I1Igmoey.js} +1 -1
  148. package/dist/web/assets/{system-monitor-window-content-BT7-ugFx.js → system-monitor-window-content-C5SeUv1H.js} +1 -1
  149. package/dist/web/assets/{tab-host-window-content-CWBaWKjA.js → tab-host-window-content-CIRGNCvi.js} +1 -1
  150. package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.br +0 -0
  151. package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.gz +0 -0
  152. package/dist/web/assets/{team-member-sheet-Br6mrxrw.js → team-member-sheet-CwXpLi3X.js} +2 -2
  153. package/dist/web/assets/team-member-sheet-CwXpLi3X.js.br +0 -0
  154. package/dist/web/assets/team-member-sheet-CwXpLi3X.js.gz +0 -0
  155. package/dist/web/assets/{team-member-window-content-CwCy7ZWf.js → team-member-window-content-4swiiDz7.js} +1 -1
  156. package/dist/web/assets/team-member-window-content-4swiiDz7.js.br +0 -0
  157. package/dist/web/assets/team-member-window-content-4swiiDz7.js.gz +0 -0
  158. package/dist/web/assets/{terminal-tab-BW14RpCb.js → terminal-tab-BGcdtMMT.js} +1 -1
  159. package/dist/web/assets/terminal-tab-BGcdtMMT.js.br +0 -0
  160. package/dist/web/assets/terminal-tab-BGcdtMMT.js.gz +0 -0
  161. package/dist/web/assets/{tool-cards-CgOBEUnb.js → tool-cards-D0-krtA2.js} +2 -2
  162. package/dist/web/assets/tool-cards-D0-krtA2.js.br +0 -0
  163. package/dist/web/assets/tool-cards-D0-krtA2.js.gz +0 -0
  164. package/dist/web/assets/{use-inline-blame-Bzjfru4n.js → use-inline-blame-pKaXyGU-.js} +1 -1
  165. package/dist/web/assets/use-inline-blame-pKaXyGU-.js.br +0 -0
  166. package/dist/web/assets/use-inline-blame-pKaXyGU-.js.gz +0 -0
  167. package/dist/web/assets/{use-lsp-hGdjL2hV.js → use-lsp-71azTXQz.js} +1 -1
  168. package/dist/web/assets/use-lsp-71azTXQz.js.br +0 -0
  169. package/dist/web/assets/use-lsp-71azTXQz.js.gz +0 -0
  170. package/dist/web/assets/{use-monaco-theme-rH6epJYA.js → use-monaco-theme-C88KphGx.js} +1 -1
  171. package/dist/web/assets/{use-websocket-QwURkjpV.js → use-websocket-Chk9guIU.js} +1 -1
  172. package/dist/web/assets/{video-preview-P9T-ZVdN.js → video-preview-DxEX-uGo.js} +1 -1
  173. package/dist/web/assets/video-preview-DxEX-uGo.js.br +0 -0
  174. package/dist/web/assets/video-preview-DxEX-uGo.js.gz +0 -0
  175. package/dist/web/index.html +2 -2
  176. package/dist/web/index.html.br +0 -0
  177. package/dist/web/index.html.gz +0 -0
  178. package/dist/web/sw.js +1 -1
  179. package/dist/web/sw.js.br +0 -0
  180. package/dist/web/sw.js.gz +0 -0
  181. package/package.json +1 -1
  182. package/src/providers/codex-app-server/codex-history.ts +43 -5
  183. package/src/providers/codex-app-server/codex-provider.ts +11 -5
  184. package/src/providers/codex-app-server/codex-reset-credit.ts +49 -0
  185. package/src/server/routes/codex-accounts.ts +14 -0
  186. package/src/server/ws/chat.ts +22 -3
  187. package/src/services/codex-reset-credit.service.ts +70 -0
  188. package/src/shared/usage-extra.ts +19 -0
  189. package/src/types/chat.ts +2 -0
  190. package/src/web/components/chat/codex-usage-panel.tsx +4 -0
  191. package/src/web/components/settings/accounts/account-card.tsx +5 -1
  192. package/src/web/components/settings/accounts/account-usage-extras.tsx +3 -2
  193. package/src/web/components/settings/accounts/codex-accounts-section.tsx +3 -1
  194. package/src/web/components/settings/accounts/codex-reset-credit-button.tsx +120 -0
  195. package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js +0 -1
  196. package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.br +0 -0
  197. package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.gz +0 -0
  198. package/dist/web/assets/ai-resource-editor-DKcsVDle.js.br +0 -0
  199. package/dist/web/assets/ai-resource-editor-DKcsVDle.js.gz +0 -0
  200. package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.br +0 -0
  201. package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.gz +0 -0
  202. package/dist/web/assets/appearance-settings-section-DL466x83.js.br +0 -0
  203. package/dist/web/assets/appearance-settings-section-DL466x83.js.gz +0 -0
  204. package/dist/web/assets/branch-review-tab-Dag86n_R.js.br +0 -0
  205. package/dist/web/assets/branch-review-tab-Dag86n_R.js.gz +0 -0
  206. package/dist/web/assets/chat-tab-CcYZLXls.js +0 -15
  207. package/dist/web/assets/chat-tab-CcYZLXls.js.br +0 -0
  208. package/dist/web/assets/chat-tab-CcYZLXls.js.gz +0 -0
  209. package/dist/web/assets/chat-tab-QFDhzzBZ.js.br +0 -0
  210. package/dist/web/assets/chat-tab-QFDhzzBZ.js.gz +0 -0
  211. package/dist/web/assets/code-editor-K-IN8VwJ.js.br +0 -0
  212. package/dist/web/assets/code-editor-K-IN8VwJ.js.gz +0 -0
  213. package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js +0 -1
  214. package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.br +0 -0
  215. package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.gz +0 -0
  216. package/dist/web/assets/conflict-editor-qMGJH0Bx.js.br +0 -0
  217. package/dist/web/assets/conflict-editor-qMGJH0Bx.js.gz +0 -0
  218. package/dist/web/assets/csv-preview-D2I742dl.js.br +0 -0
  219. package/dist/web/assets/csv-preview-D2I742dl.js.gz +0 -0
  220. package/dist/web/assets/database-viewer-Bz4LjgIa.js.br +0 -0
  221. package/dist/web/assets/database-viewer-Bz4LjgIa.js.gz +0 -0
  222. package/dist/web/assets/design-tab-C987MtMR.js.br +0 -0
  223. package/dist/web/assets/design-tab-C987MtMR.js.gz +0 -0
  224. package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.br +0 -0
  225. package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.gz +0 -0
  226. package/dist/web/assets/diff-viewer-CdXi7cZN.js.br +0 -0
  227. package/dist/web/assets/diff-viewer-CdXi7cZN.js.gz +0 -0
  228. package/dist/web/assets/editor-language-service-Zle_PZV8.js.br +0 -0
  229. package/dist/web/assets/editor-language-service-Zle_PZV8.js.gz +0 -0
  230. package/dist/web/assets/explorer-body-DHMQR1Rk.js.br +0 -0
  231. package/dist/web/assets/explorer-body-DHMQR1Rk.js.gz +0 -0
  232. package/dist/web/assets/explorer-window-content-Df40nQBk.js.br +0 -0
  233. package/dist/web/assets/explorer-window-content-Df40nQBk.js.gz +0 -0
  234. package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.br +0 -0
  235. package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.gz +0 -0
  236. package/dist/web/assets/extension-webview-BGoDxkdF.js.br +0 -0
  237. package/dist/web/assets/extension-webview-BGoDxkdF.js.gz +0 -0
  238. package/dist/web/assets/files-settings-section-DC3OzzOl.js.br +0 -0
  239. package/dist/web/assets/files-settings-section-DC3OzzOl.js.gz +0 -0
  240. package/dist/web/assets/general-settings-section-i41W43ER.js.br +0 -0
  241. package/dist/web/assets/general-settings-section-i41W43ER.js.gz +0 -0
  242. package/dist/web/assets/git-log-panel-DHzPIkJg.js.br +0 -0
  243. package/dist/web/assets/git-log-panel-DHzPIkJg.js.gz +0 -0
  244. package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.br +0 -0
  245. package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.gz +0 -0
  246. package/dist/web/assets/group-chat-tab-DfwZb-fI.js.br +0 -0
  247. package/dist/web/assets/group-chat-tab-DfwZb-fI.js.gz +0 -0
  248. package/dist/web/assets/index-DMA-So69.css.br +0 -0
  249. package/dist/web/assets/index-jG9mLs-J.js.br +0 -0
  250. package/dist/web/assets/index-jG9mLs-J.js.gz +0 -0
  251. package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.br +0 -0
  252. package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.gz +0 -0
  253. package/dist/web/assets/markdown-renderer-ok96IgnS.js.br +0 -0
  254. package/dist/web/assets/markdown-renderer-ok96IgnS.js.gz +0 -0
  255. package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.br +0 -0
  256. package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.gz +0 -0
  257. package/dist/web/assets/new-design-dialog-Drkjb_LI.js.br +0 -0
  258. package/dist/web/assets/new-design-dialog-Drkjb_LI.js.gz +0 -0
  259. package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.br +0 -0
  260. package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.gz +0 -0
  261. package/dist/web/assets/problems-panel-CFYO0Ro3.js.br +0 -0
  262. package/dist/web/assets/problems-panel-CFYO0Ro3.js.gz +0 -0
  263. package/dist/web/assets/process-table-BVeRguFg.js.br +0 -0
  264. package/dist/web/assets/process-table-BVeRguFg.js.gz +0 -0
  265. package/dist/web/assets/proxy-settings-section-BcHIEANE.js.br +0 -0
  266. package/dist/web/assets/proxy-settings-section-BcHIEANE.js.gz +0 -0
  267. package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.br +0 -0
  268. package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.gz +0 -0
  269. package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.br +0 -0
  270. package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.gz +0 -0
  271. package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.br +0 -0
  272. package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.gz +0 -0
  273. package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.br +0 -0
  274. package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.gz +0 -0
  275. package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.br +0 -0
  276. package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.gz +0 -0
  277. package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.br +0 -0
  278. package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.gz +0 -0
  279. package/dist/web/assets/settings-body-B6JGOIYb.js.br +0 -0
  280. package/dist/web/assets/settings-body-B6JGOIYb.js.gz +0 -0
  281. package/dist/web/assets/sql-query-editor-4iCGE4h2.js.br +0 -3
  282. package/dist/web/assets/sql-query-editor-4iCGE4h2.js.gz +0 -0
  283. package/dist/web/assets/sqlite-viewer-CK6DulYk.js.br +0 -0
  284. package/dist/web/assets/sqlite-viewer-CK6DulYk.js.gz +0 -0
  285. package/dist/web/assets/system-monitor-body-J9JtvAWV.js.br +0 -0
  286. package/dist/web/assets/system-monitor-body-J9JtvAWV.js.gz +0 -0
  287. package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.br +0 -0
  288. package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.gz +0 -0
  289. package/dist/web/assets/team-member-sheet-Br6mrxrw.js.br +0 -0
  290. package/dist/web/assets/team-member-sheet-Br6mrxrw.js.gz +0 -0
  291. package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.br +0 -0
  292. package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.gz +0 -0
  293. package/dist/web/assets/terminal-tab-BW14RpCb.js.br +0 -0
  294. package/dist/web/assets/terminal-tab-BW14RpCb.js.gz +0 -0
  295. package/dist/web/assets/tool-cards-CgOBEUnb.js.br +0 -0
  296. package/dist/web/assets/tool-cards-CgOBEUnb.js.gz +0 -0
  297. package/dist/web/assets/use-inline-blame-Bzjfru4n.js.br +0 -0
  298. package/dist/web/assets/use-inline-blame-Bzjfru4n.js.gz +0 -0
  299. package/dist/web/assets/use-lsp-hGdjL2hV.js.br +0 -0
  300. package/dist/web/assets/use-lsp-hGdjL2hV.js.gz +0 -0
  301. package/dist/web/assets/video-preview-P9T-ZVdN.js.br +0 -0
  302. package/dist/web/assets/video-preview-P9T-ZVdN.js.gz +0 -0
@@ -131,6 +131,12 @@ interface SessionEntry {
131
131
  idleSince?: number;
132
132
  /** When the last turn completed — the moment this session's prompt cache was last written */
133
133
  lastTurnEndedAt?: number;
134
+ /**
135
+ * When the message that opened the current turn reached the server — what "time to first
136
+ * event" is measured from. The consumer loop outlives turns, so its own clock would count
137
+ * the idle gap before a follow-up as waiting on the provider.
138
+ */
139
+ turnRequestedAt?: { at: number; cold: boolean };
134
140
  /** Pending release of the subprocess once its prompt cache lapses */
135
141
  cacheReleaseTimer?: ReturnType<typeof setTimeout>;
136
142
  }
@@ -716,9 +722,13 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
716
722
  const isMetadataEvent = evType === "account_info" || evType === "account_retry" || evType === "streaming_status" || evType === "status_update";
717
723
  if (!firstEventReceived && !isMetadataEvent) {
718
724
  firstEventReceived = true;
719
- const waitMs = Date.now() - startTime;
720
- console.log(`[chat] session=${sessionId} first SDK event after ${waitMs}ms: type=${evType}`);
721
- logSessionEvent(sessionId, "PERF", `First SDK event after ${waitMs}ms (type=${evType})`);
725
+ const requested = entry.turnRequestedAt;
726
+ entry.turnRequestedAt = undefined;
727
+ // A turn nobody sent over this socket (scheduler, remote trigger) has no receipt time.
728
+ const waitMs = Date.now() - (requested?.at ?? startTime);
729
+ const path = requested ? (requested.cold ? "cold" : "warm") : "unsent";
730
+ console.log(`[chat] session=${sessionId} first SDK event after ${waitMs}ms: type=${evType} path=${path}`);
731
+ logSessionEvent(sessionId, "PERF", `First SDK event after ${waitMs}ms (type=${evType}, ${path})`);
722
732
  if (heartbeat) clearInterval(heartbeat);
723
733
  const newPhase = evType === "thinking" ? "thinking" : "streaming";
724
734
  setPhase(sessionId, newPhase);
@@ -1197,6 +1207,8 @@ export const chatWebSocket = {
1197
1207
  }
1198
1208
 
1199
1209
  if (parsed.type === "message") {
1210
+ // Taken before any awaited work below (slash rewrites, resume), which is part of the wait.
1211
+ const messageReceivedAt = Date.now();
1200
1212
  // Images count as content: a message may carry only a picture, with nothing typed.
1201
1213
  const hasInlineImages = Array.isArray((parsed as { images?: unknown }).images)
1202
1214
  && ((parsed as { images: unknown[] }).images.length > 0);
@@ -1320,6 +1332,10 @@ export const chatWebSocket = {
1320
1332
 
1321
1333
  // Store user message for reconnect replay (turn_events includes only assistant events)
1322
1334
  entry.currentUserMessage = parsed.content;
1335
+ // Only a message that opens a turn is timed; one typed mid-turn joins the running turn.
1336
+ if (!entry.isStreamingActive || entry.phase === "idle") {
1337
+ entry.turnRequestedAt = { at: messageReceivedAt, cold: !entry.isStreamingActive };
1338
+ }
1323
1339
 
1324
1340
  if (!entry.isStreamingActive) {
1325
1341
  // First message or post-crash recovery: start persistent consumer
@@ -1467,6 +1483,9 @@ export const chatWebSocket = {
1467
1483
  broadcastBackgroundRegistry(sessionId);
1468
1484
  const provider = providerRegistry.get(providerId);
1469
1485
  const instruction = `Call the KillShell tool with task_id "${shellId}" to stop that background command, then reply with just "Stopped.".`;
1486
+ if (!entry.isStreamingActive || entry.phase === "idle") {
1487
+ entry.turnRequestedAt = { at: Date.now(), cold: !entry.isStreamingActive };
1488
+ }
1470
1489
  if (!entry.isStreamingActive) {
1471
1490
  if (provider && "resumeSession" in provider) await (provider as any).resumeSession(sessionId);
1472
1491
  if (entry.projectPath && provider && "ensureProjectPath" in provider) {
@@ -0,0 +1,70 @@
1
+ /**
2
+ * "Use reset" for a Codex account: spend one free rate-limit reset — but only once a limit has
3
+ * actually been reached.
4
+ *
5
+ * The gate lives on the server, not just on the button. A reset is one-use, it moves the
6
+ * weekly reset date (OpenAI: the next weekly reset is counted from when you continue, and the
7
+ * original one is not also granted), and spending it early throws away whatever was left of
8
+ * the window. So the decision is made against a *live* quota read taken right here, never
9
+ * against whatever the card was last shown.
10
+ */
11
+ import { randomUUID } from "node:crypto";
12
+ import { getCodexAccount } from "./codex-account.service.ts";
13
+ import { clearCodexAccountUsageLimit, isCodexAccountUsageLimited } from "./codex-account-cooldown.ts";
14
+ import { refreshUsage } from "./provider-usage/usage-registry.ts";
15
+ import { fetchCodexUsageLive } from "../providers/codex-app-server/codex-usage-fetch.ts";
16
+ import { consumeCodexResetCredit, type ResetCreditOutcome } from "../providers/codex-app-server/codex-reset-credit.ts";
17
+ import { usageLimitReached } from "../shared/usage-extra.ts";
18
+ import type { UsageInfo } from "../types/chat.ts";
19
+
20
+ /** A refusal the caller should show as-is, with the HTTP status that fits it. */
21
+ export class ResetCreditRefusedError extends Error {
22
+ constructor(message: string, readonly status: 404 | 409 | 502) { super(message); }
23
+ }
24
+
25
+ /** Accounts with a reset in flight — a double click must not become two attempts. */
26
+ const inFlight = new Set<string>();
27
+
28
+ export interface ResetCreditResult {
29
+ outcome: ResetCreditOutcome;
30
+ /** The account's quota after the attempt, freshly read. */
31
+ usage: UsageInfo;
32
+ }
33
+
34
+ /** The two ways out to Codex, replaceable so a test can stand in for the app-server. */
35
+ export interface ResetCreditCodexPorts {
36
+ readUsage: (codexHome: string) => Promise<UsageInfo>;
37
+ consume: (codexHome: string, idempotencyKey: string, creditId?: string) => Promise<ResetCreditOutcome>;
38
+ }
39
+
40
+ const LIVE_PORTS: ResetCreditCodexPorts = { readUsage: fetchCodexUsageLive, consume: consumeCodexResetCredit };
41
+
42
+ export async function spendCodexResetCredit(accountId: string, ports: ResetCreditCodexPorts = LIVE_PORTS): Promise<ResetCreditResult> {
43
+ const account = getCodexAccount(accountId);
44
+ if (!account) throw new ResetCreditRefusedError("Account not found", 404);
45
+ if (inFlight.has(accountId)) throw new ResetCreditRefusedError("A reset for this account is already in progress.", 409);
46
+ inFlight.add(accountId);
47
+ try {
48
+ let live: UsageInfo;
49
+ try {
50
+ live = await ports.readUsage(account.home);
51
+ } catch (e) {
52
+ // Refuse rather than guess: the only safe answer to "is the limit reached?" is a fresh one.
53
+ throw new ResetCreditRefusedError(`Could not read ${account.label}'s current usage, so no reset was used: ${(e as Error).message}`, 502);
54
+ }
55
+ // Codex refusing a turn for quota is also a reached limit, even if the figures lag it.
56
+ if (!usageLimitReached(live) && !isCodexAccountUsageLimited(accountId)) {
57
+ throw new ResetCreditRefusedError(`${account.label} has not reached a limit yet, so a reset would waste the usage left and move the weekly reset date. It is kept for when you hit the limit.`, 409);
58
+ }
59
+ if ((live.resetCredits?.available ?? 0) === 0) {
60
+ throw new ResetCreditRefusedError(`${account.label} has no free reset left.`, 409);
61
+ }
62
+ // The soonest-expiring credit first: the others outlive it.
63
+ const outcome = await ports.consume(account.home, randomUUID(), live.resetCredits?.nextCreditId);
64
+ if (outcome === "reset") clearCodexAccountUsageLimit(accountId);
65
+ console.log(`[codex] account ${accountId} reset credit → ${outcome}`);
66
+ return { outcome, usage: await refreshUsage("codex", accountId) };
67
+ } finally {
68
+ inFlight.delete(accountId);
69
+ }
70
+ }
@@ -84,9 +84,28 @@ export function parseCodexResetCredits(raw: unknown): ResetCredits | undefined {
84
84
  available: available.length,
85
85
  ...(typeof next?.expiresAt === "number" ? { nextExpiresAt: new Date(next.expiresAt * 1000).toISOString() } : {}),
86
86
  ...(typeof next?.title === "string" ? { title: next.title } : {}),
87
+ ...(typeof next?.id === "string" ? { nextCreditId: next.id } : {}),
87
88
  };
88
89
  }
89
90
 
91
+ /**
92
+ * Whether a limit has actually been reached — at 100% of a window, as the card rounds it.
93
+ *
94
+ * The one rule both the "Use reset" button and the server's refusal follow, so the button can
95
+ * never offer what the server would turn down. Reached, not approaching: spending a reset at
96
+ * 90% throws away the 10% left *and* moves the weekly reset date (OpenAI: the next weekly reset
97
+ * is counted from when you continue, and the original one is not also granted).
98
+ */
99
+ export function usageLimitReached(u: { session?: { utilization: number }; weekly?: { utilization: number } }): boolean {
100
+ const atCap = (util?: number) => Math.round((util ?? 0) * 100) >= 100;
101
+ return atCap(u.session?.utilization) || atCap(u.weekly?.utilization);
102
+ }
103
+
104
+ /** A reset may be offered: a limit is reached and a credit is left to spend on it. */
105
+ export function canUseResetCredit(u: { session?: { utilization: number }; weekly?: { utilization: number }; resetCredits?: ResetCredits }): boolean {
106
+ return usageLimitReached(u) && (u.resetCredits?.available ?? 0) > 0;
107
+ }
108
+
90
109
  /** The extra fields of a `UsageInfo`, as the JSON column holds them — null when there are none. */
91
110
  export function serializeUsageExtra(u: { weeklyScoped?: ScopedLimitBucket[]; resetCredits?: ResetCredits }): string | null {
92
111
  const extra: UsageExtra = {};
package/src/types/chat.ts CHANGED
@@ -230,6 +230,8 @@ export interface ResetCredits {
230
230
  nextExpiresAt?: string;
231
231
  /** What the soonest one resets, as Codex words it ("Full reset (Weekly + 5 hr)"). */
232
232
  title?: string;
233
+ /** Codex's opaque id of that soonest-expiring credit — the one "Use reset" spends. */
234
+ nextCreditId?: string;
233
235
  }
234
236
 
235
237
  /** Result subtype from SDK ResultMessage */
@@ -27,6 +27,7 @@ import { UsagePanelShell } from "./usage-panel-shell";
27
27
 
28
28
  import { CODEX_SIGNED_OUT_HINT, type CodexAccount } from "@/components/settings/accounts/use-codex-accounts";
29
29
  import { CodexSignInAgainDialog } from "@/components/settings/accounts/codex-sign-in-again-dialog";
30
+ import { CodexResetCreditButton } from "@/components/settings/accounts/codex-reset-credit-button";
30
31
  type Usage = Pick<UsageInfo, "fiveHour" | "sevenDay" | "session" | "weekly" | "resetCredits">;
31
32
 
32
33
  /** Matches the whole-percent figure the bars show, so a refusal agrees with the card. */
@@ -212,6 +213,9 @@ export function CodexUsagePanel({ onClose, usage, onReload, onSelectAccount, sel
212
213
  dailyGuardToggling={togglingId === a.id}
213
214
  reauthHint={a.signedOut ? CODEX_SIGNED_OUT_HINT : undefined}
214
215
  resetCredits={u.resetCredits}
216
+ resetCreditAction={
217
+ <CodexResetCreditButton account={a} usage={u} onDone={(m) => { setPanelError(m); void reload(); }} />
218
+ }
215
219
  // Only a ChatGPT sign-in can be renewed in place; a dead API key is replaced.
216
220
  onReauth={a.type === "chatgpt" ? () => setSignInAgain(a) : undefined}
217
221
  />
@@ -27,6 +27,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
27
27
  import { AccountCardShell } from "./accounts-pane-header";
28
28
  import { formatExpiry, formatLastUpdated, tokenStatus } from "./account-usage-format";
29
29
  import type { DailyGuardState } from "../../../../shared/codex-daily-guard.ts";
30
+ import type { ReactNode } from "react";
30
31
  import type { ResetCredits } from "../../../../types/chat";
31
32
  import { ResetCreditsChip, ScopedBucketRows } from "./account-usage-extras";
32
33
 
@@ -73,6 +74,8 @@ export interface AccountCardProps {
73
74
  onReauth?: (id: string) => void;
74
75
  /** Codex's free rate-limit resets for this account, shown in the footer. */
75
76
  resetCredits?: ResetCredits;
77
+ /** Control beside the reset count — Codex's "Use reset", which renders only at a limit. */
78
+ resetCreditAction?: ReactNode;
76
79
  }
77
80
 
78
81
  // Fixed widths so a row scrolls instead of squeezing. Two of them: a read-only card holds a
@@ -83,7 +86,7 @@ const STRIP_WIDTH = { readOnly: "min-w-[220px]", withActions: "min-w-[300px]" }
83
86
  export function AccountCard({
84
87
  entry, isActive, accountInfo, onToggle, toggling, onDelete, onExport, onViewProfile, flash,
85
88
  onSelect, unselectableReason, selecting, planLabel, dailyGuard, onDailyGuardToggle, dailyGuardToggling,
86
- reauthHint, onReauth, resetCredits, layout = "list",
89
+ reauthHint, onReauth, resetCredits, resetCreditAction, layout = "list",
87
90
  }: AccountCardProps) {
88
91
  const { usage } = entry;
89
92
  const hasBuckets = usage.session || usage.weekly || usage.weeklyOpus || usage.weeklySonnet || usage.weeklyScoped?.length;
@@ -318,6 +321,7 @@ export function AccountCard({
318
321
 
319
322
  <div className="flex items-center gap-2 text-[10px] text-text-subtle flex-wrap">
320
323
  <ResetCreditsChip credits={resetCredits} />
324
+ {resetCreditAction}
321
325
  {usage.lastFetchedAt && (
322
326
  <AccountHint
323
327
  className="inline-flex items-center gap-1"
@@ -5,7 +5,8 @@
5
5
  * - `ScopedBucketRows` — a model's own weekly limit (Claude's "Fable"), one bar per model,
6
6
  * labelled with the name the provider gives it.
7
7
  * - `ResetCreditsChip` — how many free rate-limit resets Codex has granted the account and
8
- * when the next one lapses. PPM only reports these; they are spent from Codex itself.
8
+ * when the next one lapses. Spending one is `CodexResetCreditButton`, shown beside it only
9
+ * once a limit is reached.
9
10
  */
10
11
 
11
12
  import { RotateCcw } from "@/lib/icons";
@@ -32,7 +33,7 @@ export function ResetCreditsChip({ credits }: { credits?: ResetCredits }) {
32
33
  : `Codex has granted this account ${n} free rate-limit reset${n === 1 ? "" : "s"}`
33
34
  + `${credits.title ? ` (${credits.title})` : ""}.`
34
35
  + `${credits.nextExpiresAt ? ` The next one expires on ${new Date(credits.nextExpiresAt).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" })}.` : ""}`
35
- + " Use one from the Codex app when a limit is reached — PPM only shows the count.";
36
+ + " A \"Use reset\" button appears here once a limit is reached; a reset moves the weekly reset date, so spending one earlier wastes it.";
36
37
  return (
37
38
  <AccountHint className={`inline-flex items-center gap-1 ${n > 0 ? "text-primary" : ""}`} hint={hint}>
38
39
  <RotateCcw className="size-3 shrink-0" aria-hidden />
@@ -28,6 +28,7 @@ import { CodexUsageRows } from "./codex-usage-rows";
28
28
  import { CODEX_SIGNED_OUT_HINT, useCodexAccounts, type CodexAccount } from "./use-codex-accounts";
29
29
  import { CodexSignInAgainDialog } from "./codex-sign-in-again-dialog";
30
30
  import { ResetCreditsChip } from "./account-usage-extras";
31
+ import { CodexResetCreditButton } from "./codex-reset-credit-button";
31
32
  import { AccountHint } from "./account-hint";
32
33
  import { codexPlanLabel } from "../../../../shared/codex-plan-label.ts";
33
34
  import { dailyGuardState } from "../../../../shared/codex-daily-guard.ts";
@@ -137,8 +138,9 @@ export function CodexAccountsSection() {
137
138
  </div>
138
139
  <CodexUsageRows usage={u} />
139
140
  {u.resetCredits && (
140
- <div className="text-[10px] text-text-subtle">
141
+ <div className="flex items-center gap-2 flex-wrap text-[10px] text-text-subtle">
141
142
  <ResetCreditsChip credits={u.resetCredits} />
143
+ <CodexResetCreditButton account={a} usage={u} onDone={(m) => { c.setMsg(m); void c.load(); }} />
142
144
  </div>
143
145
  )}
144
146
  {u.session == null && (() => {
@@ -0,0 +1,120 @@
1
+ /**
2
+ * "Use reset": spend one of Codex's free rate-limit resets on an account — offered only once a
3
+ * limit is actually reached.
4
+ *
5
+ * Rendered nothing at all otherwise, on purpose: a reset is one-use, it moves the weekly reset
6
+ * date (the next weekly reset is counted from when you continue; the original one is not also
7
+ * granted), and spent early it throws away whatever was left of the window. The server applies
8
+ * the same rule (`usageLimitReached`) against a live read, so a stale card cannot slip past it.
9
+ *
10
+ * The confirmation says exactly that before anything is spent. Bottom sheet below `md`,
11
+ * centred dialog above, like every other confirmation in the accounts pane.
12
+ */
13
+
14
+ import { useState } from "react";
15
+ import { Loader2, RotateCcw } from "@/lib/icons";
16
+ import { api } from "@/lib/api-client";
17
+ import { Button } from "@/components/ui/button";
18
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
19
+ import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
20
+ import { useIsMobile } from "@/hooks/use-is-mobile";
21
+ import { canUseResetCredit } from "../../../../shared/usage-extra.ts";
22
+ import type { LimitBucket, ResetCredits } from "../../../../types/chat";
23
+
24
+ type Outcome = "reset" | "nothingToReset" | "noCredit" | "alreadyRedeemed";
25
+
26
+ const OUTCOME_MESSAGE: Record<Outcome, (label: string) => string> = {
27
+ reset: (l) => `${l}: limits reset to 0%. The weekly reset date now counts from your next use.`,
28
+ nothingToReset: (l) => `${l}: nothing needed resetting, so the reset was kept.`,
29
+ noCredit: (l) => `${l} has no free reset left.`,
30
+ alreadyRedeemed: (l) => `${l}: that reset had already been used.`,
31
+ };
32
+
33
+ function when(iso?: string): string | null {
34
+ return iso ? new Date(iso).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }) : null;
35
+ }
36
+
37
+ export function CodexResetCreditButton({ account, usage, onDone }: {
38
+ account: { id: string; label: string };
39
+ usage: { session?: LimitBucket; weekly?: LimitBucket; resetCredits?: ResetCredits };
40
+ /** Called after an attempt, with a message saying what happened (or why it was refused). */
41
+ onDone: (message: string) => void;
42
+ }) {
43
+ const isMobile = useIsMobile();
44
+ const [confirming, setConfirming] = useState(false);
45
+ const [busy, setBusy] = useState(false);
46
+
47
+ if (!canUseResetCredit(usage)) return null;
48
+ const credits = usage.resetCredits!;
49
+
50
+ async function spend() {
51
+ setBusy(true);
52
+ try {
53
+ const r = await api.post<{ outcome: Outcome }>(`/api/codex-accounts/${account.id}/reset-credit`);
54
+ onDone(OUTCOME_MESSAGE[r.outcome]?.(account.label) ?? `${account.label}: ${r.outcome}`);
55
+ } catch (e) {
56
+ onDone((e as Error).message || "Could not use the reset");
57
+ } finally {
58
+ setBusy(false);
59
+ setConfirming(false);
60
+ }
61
+ }
62
+
63
+ const weeklyNow = when(usage.weekly?.resetsAt);
64
+ const expires = when(credits.nextExpiresAt);
65
+ const body = (
66
+ <div className="space-y-3" data-testid="codex-reset-credit-confirm">
67
+ <p className="text-sm">
68
+ Use 1 of <span className="font-medium">{account.label}</span>'s {credits.available} free reset{credits.available === 1 ? "" : "s"}?
69
+ </p>
70
+ <ul className="text-xs text-text-secondary space-y-1.5 list-disc pl-4">
71
+ <li>{credits.title ?? "Full reset"}: the 5-hour and weekly usage go back to 0% now.</li>
72
+ <li>
73
+ The weekly reset date moves: the next one is about 7 days after you continue using Codex
74
+ {weeklyNow ? `, not ${weeklyNow} as now` : ""}. You do not also get that one.
75
+ </li>
76
+ {expires && <li>This spends the reset that expires first ({expires}).</li>}
77
+ </ul>
78
+ <div className="flex flex-col-reverse md:flex-row gap-2 md:justify-end pt-1">
79
+ <Button variant="outline" onClick={() => setConfirming(false)} disabled={busy} className="min-h-11">
80
+ Keep it
81
+ </Button>
82
+ <Button onClick={() => void spend()} disabled={busy} className="min-h-11 gap-1.5">
83
+ {busy ? <Loader2 className="size-4 animate-spin" /> : <RotateCcw className="size-4" />}
84
+ Use reset
85
+ </Button>
86
+ </div>
87
+ </div>
88
+ );
89
+
90
+ return (
91
+ <>
92
+ <Button
93
+ size="sm"
94
+ variant="outline"
95
+ className="min-h-11 md:min-h-0 md:h-7 gap-1 px-2 text-[11px] cursor-pointer border-primary/40 text-primary"
96
+ onClick={() => setConfirming(true)}
97
+ disabled={busy}
98
+ >
99
+ <RotateCcw className="size-3.5" /> Use reset
100
+ </Button>
101
+ {confirming && (isMobile ? (
102
+ <BottomSheet open onClose={() => { if (!busy) setConfirming(false); }}>
103
+ <div className="px-4 pb-4">
104
+ <h2 className="text-base font-semibold mb-3">Use a free reset</h2>
105
+ {body}
106
+ </div>
107
+ </BottomSheet>
108
+ ) : (
109
+ <Dialog open onOpenChange={(v) => { if (!v && !busy) setConfirming(false); }}>
110
+ <DialogContent className="sm:max-w-sm">
111
+ <DialogHeader>
112
+ <DialogTitle className="text-sm">Use a free reset</DialogTitle>
113
+ </DialogHeader>
114
+ {body}
115
+ </DialogContent>
116
+ </Dialog>
117
+ ))}
118
+ </>
119
+ );
120
+ }
@@ -1 +0,0 @@
1
- import{s as e}from"./rolldown-runtime-C5c2KzVm.js";import{C as t,S as n}from"./vendor-markdown-QKPNiWEd.js";import"./vendor-ui-BHh12_0v.js";import{Jr as r,Lr as i,Mr as a,St as o,V as s,Zn as c,Zt as l,fn as u,fr as d,nt as f,st as p,un as m}from"./icons-CO_II6D-.js";import{r as h}from"./api-client-c8YOpTlT.js";import{n as g}from"./utils-btZ8C8-R.js";import"./portal-container-context-NFSuymMu.js";import"./chat-preparation-cache-BSIQGiWx.js";import{n as _}from"./use-is-mobile-BClDZJP5.js";import{n as v,t as y}from"./button-Ci9EUgqd.js";import{a as b,i as x,n as S,o as C,r as w,t as T}from"./dialog-B8clVkyi.js";import{t as E}from"./mobile-bottom-sheet-CuNi2FRv.js";import"./use-visual-viewport-BGpcMnIx.js";import{t as D}from"./input-CG8PS8mS.js";import{$n as O,Qn as k,Rn as A,Sn as j,Zn as M,bt as N,ct as P,dt as F,ft as I,lt as L,st as ee,ut as te}from"./index-jG9mLs-J.js";import{_ as ne,a as re,h as ie,m as ae,o as oe,p as se,r as ce,v as le,x as R,y as ue}from"./api-settings-BmvNoCbn.js";import{a as de,b as z,c as fe,f as B,g as pe,h as V,i as me,l as he,m as H,n as ge,o as U,p as _e,s as ve,t as ye,u as be,v as W,y as xe}from"./codex-sign-in-again-dialog-C8JvblLG.js";var G=e(t(),1),K=n(),q=[`Mon`,`Tue`,`Wed`,`Thu`,`Fri`,`Sat`,`Sun`],J=Array.from({length:24},(e,t)=>t);function Se(e,t){let n=Array.from({length:7},()=>Array.from({length:24},()=>({sum:0,count:0,avg:0})));for(let r of e){let e=t===`5h`?r.five_hour_util:r.weekly_util;if(e==null)continue;let i=new Date(r.recorded_at+(r.recorded_at.endsWith(`Z`)?``:`Z`)),a=(i.getDay()+6)%7,o=i.getHours();n[a][o].sum+=e,n[a][o].count+=1}for(let e of n)for(let t of e)t.avg=t.count>0?t.sum/t.count:0;return n}function Ce(e){return e.map(e=>{let t=e.reduce((e,t)=>e+t.sum,0),n=e.reduce((e,t)=>e+t.count,0);return n>0?t/n:0})}function we(e){return J.map(t=>{let n=0,r=0;for(let i of e)n+=i[t].sum,r+=i[t].count;return r>0?n/r:0})}function Y(e){return e===0?`bg-surface-elevated`:e<.3?`bg-success/30`:e<.5?`bg-success/60`:e<.7?`bg-warning/50`:e<.9?`bg-warning/80`:`bg-error/80`}function Te(e){return e<.3?`bg-success`:e<.7?`bg-warning`:`bg-error`}function Ee({accountId:e}){let[t,n]=(0,G.useState)(null),[r,i]=(0,G.useState)(!0),[a,o]=(0,G.useState)(`5h`);(0,G.useEffect)(()=>{i(!0),se(e).then(n).catch(()=>n([])).finally(()=>i(!1))},[e]);let s=(0,G.useMemo)(()=>t?Se(t,a):null,[t,a]),c=(0,G.useMemo)(()=>s?Ce(s):[],[s]),l=(0,G.useMemo)(()=>s?we(s):[],[s]),u=Math.max(...c,.01);if(Math.max(...l,.01),r)return(0,K.jsx)(`div`,{className:`flex items-center justify-center py-3`,children:(0,K.jsx)(m,{className:`size-3 animate-spin text-text-subtle`})});if(!t||t.length===0)return(0,K.jsx)(`div`,{className:`text-[10px] text-text-subtle py-2 text-center`,children:`No usage history yet`});let d=t.length,f=new Set(t.map(e=>new Date(e.recorded_at+(e.recorded_at.endsWith(`Z`)?``:`Z`)).toDateString())).size;return(0,K.jsxs)(`div`,{className:`mt-2 space-y-2`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`span`,{className:`text-[10px] font-medium text-text-subtle`,children:`Usage Pattern (7d)`}),(0,K.jsxs)(`div`,{className:`flex gap-0.5 text-[9px]`,children:[(0,K.jsx)(`button`,{onClick:()=>o(`5h`),className:`px-1.5 py-0.5 rounded cursor-pointer transition-colors ${a===`5h`?`bg-primary/15 text-primary`:`text-text-subtle hover:text-text-secondary`}`,title:`5-hour rolling window limit — resets every 5 hours`,children:`5h`}),(0,K.jsx)(`button`,{onClick:()=>o(`weekly`),className:`px-1.5 py-0.5 rounded cursor-pointer transition-colors ${a===`weekly`?`bg-primary/15 text-primary`:`text-text-subtle hover:text-text-secondary`}`,title:`Weekly limit — resets every 7 days`,children:`Wk`})]})]}),(0,K.jsxs)(`p`,{className:`text-[9px] text-text-subtle leading-tight`,children:[`Avg `,a===`5h`?`5-hour`:`weekly`,` limit usage over `,f,`d (`,d,` samples). Higher % = closer to rate limit. Hover cells for details.`]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{className:`text-[9px] text-text-subtle`,children:`Avg usage by day of week`}),(0,K.jsx)(`div`,{className:`flex flex-col gap-[2px] mt-0.5`,children:q.map((e,t)=>{let n=c[t]??0;return(0,K.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,K.jsx)(`span`,{className:`text-[8px] text-text-subtle w-5 shrink-0 text-right tabular-nums`,children:e}),(0,K.jsx)(`div`,{className:`flex-1 h-2.5 bg-surface-elevated rounded-sm overflow-hidden`,children:(0,K.jsx)(`div`,{className:`h-full rounded-sm transition-all ${Te(n)}`,style:{width:`${Math.round(n/u*100)}%`}})}),(0,K.jsxs)(`span`,{className:`text-[8px] text-text-subtle w-6 shrink-0 text-right tabular-nums`,children:[Math.round(n*100),`%`]})]},e)})})]}),(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{className:`text-[9px] text-text-subtle`,children:`Avg usage by hour (0h-23h)`}),(0,K.jsx)(`div`,{className:`flex gap-[1px] mt-0.5`,children:J.map(e=>{let t=l[e]??0;return(0,K.jsxs)(`div`,{className:`flex-1 flex flex-col items-center gap-[1px]`,children:[(0,K.jsx)(`div`,{className:`w-full aspect-square rounded-[2px] ${Y(t)}`,title:`${e}:00 — avg ${Math.round(t*100)}% usage`}),e%6==0&&(0,K.jsx)(`span`,{className:`text-[7px] text-text-subtle tabular-nums`,children:e})]},e)})})]}),s&&(0,K.jsxs)(`div`,{children:[(0,K.jsx)(`span`,{className:`text-[9px] text-text-subtle`,children:`Day x Hour heatmap`}),(0,K.jsxs)(`div`,{className:`flex flex-col gap-[1px] mt-0.5`,children:[q.map((e,t)=>(0,K.jsxs)(`div`,{className:`flex items-center gap-[1px]`,children:[(0,K.jsx)(`span`,{className:`text-[7px] text-text-subtle w-4 shrink-0 text-right`,children:e.charAt(0)}),J.map(n=>{let r=s[t][n];return(0,K.jsx)(`div`,{className:`flex-1 aspect-square rounded-[1px] ${Y(r.avg)}`,title:`${e} ${n}:00 — ${r.count>0?`avg ${Math.round(r.avg*100)}% (${r.count} samples)`:`no data`}`},n)})]},e)),(0,K.jsxs)(`div`,{className:`flex items-center gap-[1px]`,children:[(0,K.jsx)(`span`,{className:`w-4 shrink-0`}),J.map(e=>(0,K.jsx)(`div`,{className:`flex-1 text-center`,children:e%6==0&&(0,K.jsx)(`span`,{className:`text-[7px] text-text-subtle tabular-nums`,children:e})},e))]})]})]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-1.5 text-[8px] text-text-subtle`,children:[(0,K.jsx)(`span`,{children:`Low`}),(0,K.jsxs)(`div`,{className:`flex gap-[2px]`,children:[(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-success/30`}),(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-success/60`}),(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-warning/50`}),(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-warning/80`}),(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-error/80`})]}),(0,K.jsx)(`span`,{children:`High`}),(0,K.jsx)(`span`,{className:`ml-1`,children:`|`}),(0,K.jsx)(`div`,{className:`size-2 rounded-[1px] bg-surface-elevated border border-border/30`}),(0,K.jsx)(`span`,{children:`No data`})]})]})}function De({profile:e,accountId:t,onClose:n}){let i=[[`Name`,e.account?.display_name],[`Email`,e.account?.email],[`Org`,e.organization?.name],[`Type`,e.organization?.organization_type],[`Tier`,e.organization?.rate_limit_tier],[`Status`,e.organization?.subscription_status]];return(0,K.jsxs)(`section`,{className:`space-y-2 border-t border-border pt-3`,"data-testid":`account-profile-panel`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium`,children:`Profile`}),(0,K.jsx)(`button`,{className:`p-2 text-muted-foreground hover:text-foreground cursor-pointer`,onClick:n,"aria-label":`Close profile`,children:(0,K.jsx)(r,{className:`size-4`})})]}),(0,K.jsx)(`div`,{className:`grid grid-cols-[80px_1fr] gap-x-3 gap-y-1 text-xs`,children:i.map(([e,t])=>t?(0,K.jsxs)(`span`,{className:`contents`,children:[(0,K.jsx)(`span`,{className:`text-muted-foreground`,children:e}),(0,K.jsx)(`span`,{className:`break-all`,children:t})]},e):null)}),(0,K.jsx)(Ee,{accountId:t})]})}function Oe({display:e,onConfirm:t,onCancel:n}){let r=_(),i=(0,K.jsxs)(`div`,{className:`space-y-4`,"data-testid":`account-delete-confirm`,children:[(0,K.jsxs)(`p`,{className:`text-sm`,children:[`Remove `,(0,K.jsx)(`span`,{className:`font-medium`,children:e}),`?`]}),(0,K.jsx)(`p`,{className:`text-xs text-text-subtle`,children:`The stored token is deleted from this machine. Any export you already made keeps working.`}),(0,K.jsxs)(`div`,{className:`flex flex-col-reverse md:flex-row gap-2 md:justify-end pt-2`,children:[(0,K.jsx)(y,{variant:`outline`,onClick:n,className:`min-h-11`,children:`Cancel`}),(0,K.jsx)(y,{variant:`destructive`,autoFocus:!1,onClick:t,className:`min-h-11`,children:`Remove account`})]})]});return r?(0,K.jsx)(E,{open:!0,onClose:n,children:(0,K.jsxs)(`div`,{className:`px-4 pb-4`,children:[(0,K.jsx)(`h2`,{className:`text-base font-semibold mb-3`,children:`Remove account`}),i]})}):(0,K.jsx)(T,{open:!0,onOpenChange:e=>{e||n()},children:(0,K.jsxs)(S,{className:`sm:max-w-sm`,children:[(0,K.jsx)(b,{children:(0,K.jsx)(C,{className:`text-sm`,children:`Remove account`})}),i]})})}function ke(e){let t=new Blob([e],{type:`application/json`}),n=document.createElement(`a`);n.href=URL.createObjectURL(t),n.download=`ppm-accounts-backup.json`,n.click(),URL.revokeObjectURL(n.href)}function Ae({open:e,onOpenChange:t,accounts:n,preselectId:r,onMessage:i}){let a=n.filter(e=>e.hasRefreshToken),[o,s]=(0,G.useState)(new Set),[c,l]=(0,G.useState)(``),[d,g]=(0,G.useState)(!1),[_,v]=(0,G.useState)(!1),[E,O]=(0,G.useState)(!1),[k,A]=(0,G.useState)(!1);e&&!k&&(s(r?new Set([r]):new Set(a.map(e=>e.id))),A(!0)),!e&&k&&A(!1);function M(){t(!1),l(``),g(!1),v(!1)}async function P(e){if(o.size===0)return;O(!0);let t=c.trim()||`ppm-hienlh`;try{let n={"Content-Type":`application/json`},r=h();r&&(n.Authorization=`Bearer ${r}`);let a=await fetch(`/api/accounts/export`,{method:`POST`,headers:n,body:JSON.stringify({password:t,accountIds:[...o],includeRefreshToken:d,refreshBeforeExport:_})});if(!a.ok){let e=await a.json();throw Error(e.error??`Export failed: ${a.status}`)}let s=await a.text();e&&await j(s)?i?.(`Backup copied to clipboard!`):(ke(s),i?.(`Backup downloaded.`)),M()}catch{}O(!1)}let F=o.size>0&&!E;return(0,K.jsx)(T,{open:e,onOpenChange:e=>{e||M()},children:(0,K.jsxs)(S,{className:`sm:max-w-md`,children:[(0,K.jsxs)(b,{children:[(0,K.jsxs)(C,{className:`text-sm flex items-center gap-1.5`,children:[(0,K.jsx)(u,{className:`size-3.5`}),` Export Accounts`]}),(0,K.jsx)(w,{className:`text-xs`,children:`Select accounts and set a password to protect the backup.`})]}),(0,K.jsxs)(`div`,{className:`space-y-3`,children:[(0,K.jsxs)(`div`,{className:`space-y-1`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between mb-1`,children:[(0,K.jsx)(`p`,{className:`text-[11px] font-medium text-muted-foreground`,children:`Accounts to export`}),(0,K.jsx)(`button`,{className:`text-[10px] text-primary hover:underline cursor-pointer`,onClick:()=>s(o.size===a.length?new Set:new Set(a.map(e=>e.id))),children:o.size===a.length?`Deselect all`:`Select all`})]}),a.length===0?(0,K.jsx)(`p`,{className:`text-[10px] text-muted-foreground p-2 border rounded`,children:`No exportable accounts.`}):(0,K.jsx)(`div`,{className:`max-h-36 overflow-y-auto space-y-1 border rounded p-2`,children:a.map(e=>(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`input`,{type:`checkbox`,id:`exp-${e.id}`,checked:o.has(e.id),onChange:t=>{let n=new Set(o);t.target.checked?n.add(e.id):n.delete(e.id),s(n)},className:`size-3.5 accent-primary cursor-pointer`}),(0,K.jsx)(`label`,{htmlFor:`exp-${e.id}`,className:`text-xs cursor-pointer truncate`,children:e.label??e.email??e.id.slice(0,8)})]},e.id))})]}),(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsxs)(N,{className:`text-xs`,children:[`Password `,(0,K.jsx)(`span`,{className:`text-muted-foreground font-normal`,children:`(optional)`})]}),(0,K.jsx)(D,{type:`password`,placeholder:`Leave empty for default`,value:c,onChange:e=>l(e.target.value),className:`text-xs h-8`,autoComplete:`new-password`})]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`input`,{type:`checkbox`,id:`exp-full`,checked:d,onChange:e=>g(e.target.checked),className:`size-3.5 accent-primary cursor-pointer`}),(0,K.jsx)(`label`,{htmlFor:`exp-full`,className:`text-[11px] cursor-pointer`,children:`Include refresh tokens (full transfer)`})]}),(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`input`,{type:`checkbox`,id:`exp-refresh`,checked:_,onChange:e=>v(e.target.checked),className:`size-3.5 accent-primary cursor-pointer`}),(0,K.jsx)(`label`,{htmlFor:`exp-refresh`,className:`text-[11px] cursor-pointer`,children:`Refresh tokens before export`})]}),d?(0,K.jsxs)(`div`,{className:`rounded-md border border-error/30 bg-error/5 p-2.5`,children:[(0,K.jsx)(`p`,{className:`text-[10px] font-medium text-error`,children:`Full transfer — source accounts will expire`}),(0,K.jsx)(`p`,{className:`text-[10px] text-muted-foreground`,children:`Refresh tokens included. Source machine expires in ~1h after target refreshes.`})]}):_?(0,K.jsx)(`div`,{className:`rounded-md border border-warning/30 bg-warning/5 p-2.5`,children:(0,K.jsx)(`p`,{className:`text-[10px] font-medium text-warning`,children:`Refresh before export — invalidates previous shares`})}):(0,K.jsx)(`div`,{className:`rounded-md border border-success/30 bg-success/5 p-2.5`,children:(0,K.jsx)(`p`,{className:`text-[10px] font-medium text-success`,children:`Share current token (safe)`})}),(0,K.jsx)(`p`,{className:`text-[10px] text-muted-foreground`,children:`Encrypted with AES-256-GCM + scrypt.`})]}),(0,K.jsxs)(x,{className:`gap-1.5 flex-col sm:flex-row`,children:[(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`text-xs h-7 cursor-pointer`,onClick:M,children:`Cancel`}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`text-xs h-7 cursor-pointer`,disabled:!F,onClick:()=>P(!0),children:[(0,K.jsx)(f,{className:`size-3 mr-1`}),` Copy`]}),(0,K.jsx)(y,{size:`sm`,className:`text-xs h-7 cursor-pointer`,disabled:!F,onClick:()=>P(!1),children:E?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(m,{className:`size-3 animate-spin mr-1`}),` Exporting...`]}):(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(p,{className:`size-3 mr-1`}),` Download`]})})]})]})})}function je({open:e,onOpenChange:t,onSuccess:n}){let[r,i]=(0,G.useState)(``),[a,o]=(0,G.useState)(``),[s,c]=(0,G.useState)(!1),[l,d]=(0,G.useState)(null);function f(){t(!1),i(``),o(``),d(null)}async function p(){if(r.trim()){c(!0),d(null);try{let e=await ae({data:r.trim(),password:a.trim()||`ppm-hienlh`});f(),n(`Imported ${e.imported} account(s)`)}catch(e){d(e.message||`Import failed`)}c(!1)}}return(0,K.jsx)(T,{open:e,onOpenChange:e=>{e||f()},children:(0,K.jsxs)(S,{className:`sm:max-w-md`,children:[(0,K.jsxs)(b,{children:[(0,K.jsxs)(C,{className:`text-sm flex items-center gap-1.5`,children:[(0,K.jsx)(u,{className:`size-3.5`}),` Import Accounts`]}),(0,K.jsx)(w,{className:`text-xs`,children:`Paste backup data and enter the export password. Imported accounts are temporary (~1h).`})]}),(0,K.jsxs)(`div`,{className:`space-y-3`,children:[(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsx)(N,{className:`text-xs`,children:`Backup data`}),(0,K.jsx)(`textarea`,{value:r,onChange:e=>i(e.target.value),placeholder:`Paste backup JSON here...`,rows:4,className:`w-full text-xs p-2 rounded border border-border bg-background font-mono resize-none focus:outline-none focus:ring-1 focus:ring-primary`})]}),(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsxs)(N,{className:`text-xs`,children:[`Password `,(0,K.jsx)(`span`,{className:`text-muted-foreground font-normal`,children:`(optional)`})]}),(0,K.jsx)(D,{type:`password`,placeholder:`Leave empty for default`,value:a,onChange:e=>o(e.target.value),className:`text-xs h-8`,autoComplete:`current-password`})]})]}),l&&(0,K.jsx)(`div`,{className:`text-[11px] p-2 rounded bg-error/10 text-error`,children:l}),(0,K.jsxs)(x,{children:[(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`text-xs h-7 cursor-pointer`,onClick:f,children:`Cancel`}),(0,K.jsx)(y,{size:`sm`,className:`text-xs h-7 cursor-pointer`,disabled:!r.trim()||s,onClick:p,children:s?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(m,{className:`size-3 animate-spin mr-1`}),` Importing...`]}):`Import`})]})]})})}var X=typeof window<`u`?window.matchMedia(`(min-width: 768px)`):null;function Me(e){return X?.addEventListener(`change`,e),()=>X?.removeEventListener(`change`,e)}function Ne(){return X?.matches??!0}function Z(){let[e,t]=(0,G.useState)(null),[n,r]=(0,G.useState)(!0);return(0,G.useEffect)(()=>{r(!0),oe().then(t).finally(()=>r(!1))},[]),n?(0,K.jsx)(`p`,{className:`text-xs text-text-subtle py-4 text-center`,children:`Loading...`}):e?(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsx)(`label`,{className:`text-xs font-medium text-text-primary`,children:`Rotation Strategy`}),(0,K.jsxs)(ee,{value:e.strategy,onValueChange:async e=>{t(await R({strategy:e}))},children:[(0,K.jsx)(te,{className:`w-full h-9 text-xs`,children:(0,K.jsx)(F,{})}),(0,K.jsxs)(P,{children:[(0,K.jsx)(L,{value:`round-robin`,children:`Round-robin`}),(0,K.jsx)(L,{value:`fill-first`,children:`Fill-first`}),(0,K.jsx)(L,{value:`lowest-usage`,children:`Lowest usage`})]})]}),(0,K.jsxs)(`p`,{className:`text-[10px] text-text-subtle`,children:[e.strategy===`round-robin`&&`Cycles through accounts evenly`,e.strategy===`fill-first`&&`Uses one account until its limit, then moves on`,e.strategy===`lowest-usage`&&`Picks the account with the lowest current usage`]})]}),(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsx)(`label`,{className:`text-xs font-medium text-text-primary`,children:`Max Retry`}),(0,K.jsx)(`input`,{type:`number`,min:0,value:e.maxRetry,className:`w-full h-9 text-xs border rounded-md px-3 bg-background`,onChange:async e=>{let n=parseInt(e.target.value,10);!isNaN(n)&&n>=0&&t(await R({maxRetry:n}))}}),(0,K.jsx)(`p`,{className:`text-[10px] text-text-subtle`,children:`How many accounts to try on failure. 0 = try all available accounts.`})]}),(0,K.jsxs)(`div`,{className:`space-y-1.5 border-t border-border pt-3`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`label`,{className:`text-xs font-medium text-text-primary`,children:`Cooldown parking`}),(0,K.jsx)(I,{checked:e.cooldownEnabled,onCheckedChange:async e=>{t(await R({cooldownEnabled:e}))}})]}),(0,K.jsx)(`p`,{className:`text-[10px] text-text-subtle`,children:`Park a failing account (rate limit / usage limit / auth error) until it recovers. Off: accounts stay selectable — rotation still skips accounts already ≥95% on the 5-hour limit.`})]}),(0,K.jsxs)(`div`,{className:`flex items-center justify-between text-xs border-t border-border pt-3`,children:[(0,K.jsx)(`span`,{className:`text-text-subtle`,children:`Active accounts`}),(0,K.jsx)(`span`,{className:`font-medium text-text-primary`,children:e.activeCount})]})]}):(0,K.jsx)(`p`,{className:`text-xs text-text-subtle py-4 text-center`,children:`Failed to load settings`})}function Pe({open:e,onOpenChange:t}){let n=(0,G.useSyncExternalStore)(Me,Ne);return e?n?(0,K.jsx)(T,{open:e,onOpenChange:t,children:(0,K.jsxs)(S,{className:`sm:max-w-sm`,children:[(0,K.jsx)(b,{children:(0,K.jsxs)(C,{className:`text-sm flex items-center gap-2`,children:[(0,K.jsx)(d,{className:`size-4`}),` Rotation & Retry`]})}),(0,K.jsx)(Z,{})]})}):(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(`div`,{className:`fixed inset-0 z-50 transition-opacity duration-200 opacity-100`,onClick:()=>t(!1),style:{backgroundColor:`rgba(0,0,0,0.5)`}}),(0,K.jsxs)(`div`,{className:g(`fixed bottom-0 left-0 right-0 z-50 bg-background rounded-t-2xl border-t border-border shadow-2xl`,`transition-transform duration-300 ease-out max-h-[85vh] overflow-y-auto`,`translate-y-0`),children:[(0,K.jsx)(`div`,{className:`flex justify-center pt-3 pb-1`,children:(0,K.jsx)(`div`,{className:`w-10 h-1 rounded-full bg-border`})}),(0,K.jsxs)(`div`,{className:`flex items-center justify-between px-4 py-2 border-b border-border`,children:[(0,K.jsxs)(`span`,{className:`text-sm font-semibold flex items-center gap-2`,children:[(0,K.jsx)(d,{className:`size-4`}),` Rotation & Retry`]}),(0,K.jsx)(`button`,{onClick:()=>t(!1),className:`flex items-center justify-center size-7 rounded-md hover:bg-surface-elevated transition-colors`,children:(0,K.jsx)(r,{className:`size-4`})})]}),(0,K.jsx)(`div`,{className:`px-4 py-4 pb-8`,children:(0,K.jsx)(Z,{})})]})]}):null}var Fe=v(`inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2`,{variants:{variant:{default:`border-transparent bg-primary text-primary-foreground shadow`,secondary:`border-transparent bg-secondary text-secondary-foreground`,destructive:`border-transparent bg-destructive text-destructive-foreground shadow`,outline:`text-foreground`}},defaultVariants:{variant:`default`}});function Q({className:e,variant:t,...n}){return(0,K.jsx)(`div`,{className:g(Fe({variant:t}),e),...n})}function Ie(e){let t=Math.floor(Date.now()/1e3);return e>t?`${Math.floor((e-t)/60)}m`:`exp`}function Le({accounts:e}){let[t,n]=(0,G.useState)([]),[r,i]=(0,G.useState)(!1),[a,o]=(0,G.useState)(!1),[s,c]=(0,G.useState)(new Map),l=e.filter(e=>e.hasRefreshToken);async function u(){if(l.length!==0){i(!0);try{let e=await le(l.map(e=>e.id),a);n(t=>[...t,{round:t.length+1,time:new Date().toLocaleTimeString(),includeRefresh:a,items:e}])}catch{}i(!1)}}async function d(e,t){c(t=>new Map(t).set(e,{loading:!0}));try{let n=await ue(t);c(t=>new Map(t).set(e,{loading:!1,...n}))}catch(t){c(n=>new Map(n).set(e,{loading:!1,status:`error`,error:t.message}))}}function f(e,t){return[{key:`r${e.round}-pre-${t.id}`,label:`Pre-export`,token:t.preExportTokenFull,preview:t.preExportToken,expires:t.preExportExpires},{key:`r${e.round}-exp-${t.id}`,label:`Exported`,token:t.exportedTokenFull,preview:t.exportedToken,expires:t.exportedExpires},{key:`r${e.round}-post-${t.id}`,label:`Post-export`,token:t.postExportTokenFull,preview:t.postExportToken,expires:t.postExportExpires}]}function p(){for(let e of t)for(let t of e.items)for(let n of f(e,t))n.token&&d(n.key,n.token)}return(0,K.jsxs)(`section`,{className:`border-t pt-4 space-y-2`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,K.jsx)(`p`,{className:`text-xs font-medium`,children:`Simulate export`}),t.length>0&&(0,K.jsxs)(`span`,{className:`text-[10px] text-muted-foreground`,children:[t.length,` round(s)`]})]}),(0,K.jsx)(`p`,{className:`text-[11px] text-muted-foreground`,children:`Every run appends a round. Earlier rounds are kept so tokens can be compared across runs.`}),l.length===0?(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No OAuth account with a refresh token to export.`}):(0,K.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,K.jsx)(I,{id:`sim-include-refresh`,checked:a,onCheckedChange:o,className:`cursor-pointer`}),(0,K.jsx)(N,{htmlFor:`sim-include-refresh`,className:`text-[11px] cursor-pointer`,children:`Include refresh tokens`})]}),(0,K.jsx)(y,{size:`sm`,className:`h-7 text-xs cursor-pointer`,disabled:r,onClick:()=>void u(),children:r?(0,K.jsxs)(K.Fragment,{children:[(0,K.jsx)(m,{className:`size-3 animate-spin mr-1`}),` Exporting...`]}):`Run export #${t.length+1}`})]}),t.length>0&&(0,K.jsxs)(`div`,{className:`space-y-3`,children:[(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`w-full h-7 text-[11px] cursor-pointer`,onClick:p,children:[`Test all tokens (`,t.reduce((e,t)=>e+t.items.length*3,0),`)`]}),t.map(e=>(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsxs)(`p`,{className:`text-xs font-medium text-primary`,children:[`Round #`,e.round]}),(0,K.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:e.time}),(0,K.jsx)(Q,{variant:e.includeRefresh?`destructive`:`secondary`,className:`text-[9px] px-1 py-0`,children:e.includeRefresh?`with refresh`:`access only`})]}),e.items.map(t=>(0,K.jsxs)(`div`,{className:`p-2 rounded-lg border bg-card space-y-1.5`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`span`,{className:`text-[11px] font-medium`,children:t.label??t.email??t.id.slice(0,8)}),t.tokenChanged&&(0,K.jsx)(Q,{variant:`secondary`,className:`text-[9px] px-1 py-0`,children:`DB token changed`})]}),f(e,t).map(e=>{let t=s.get(e.key);return(0,K.jsxs)(`div`,{className:`flex items-center gap-1.5 text-[11px]`,children:[(0,K.jsx)(`span`,{className:`w-20 shrink-0 text-muted-foreground text-[10px]`,children:e.label}),(0,K.jsx)(`code`,{className:`flex-1 truncate font-mono text-[10px] bg-muted px-1 rounded`,children:e.preview??`N/A`}),e.expires&&(0,K.jsx)(`span`,{className:`text-[10px] text-muted-foreground shrink-0`,children:Ie(e.expires)}),e.token?(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`h-6 text-[10px] px-1.5 cursor-pointer shrink-0`,disabled:t?.loading,onClick:()=>void d(e.key,e.token),children:t?.loading?(0,K.jsx)(m,{className:`size-3 animate-spin`}):`Test`}):(0,K.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:`-`}),t&&!t.loading&&(0,K.jsx)(`span`,{className:`text-[10px] font-medium shrink-0 ${t.status?.startsWith(`valid`)?`text-success`:`text-error`}`,children:t.status})]},e.key)})]},`r${e.round}-${t.id}`))]},e.round))]})]})}function Re(e){let t=Math.floor(Date.now()/1e3),n=Math.floor(Math.abs(e-t)/60);return e>t?`${n}m left`:`expired ${n}m ago`}function ze({open:e,onOpenChange:t,accounts:n}){let[r,i]=(0,G.useState)(new Map),[a,s]=(0,G.useState)(!1);async function c(e){i(t=>new Map(t).set(e,{loading:!0}));try{let t=await ne(e,a);i(n=>new Map(n).set(e,{loading:!1,result:t}))}catch(t){i(n=>new Map(n).set(e,{loading:!1,error:t.message}))}}function l(){for(let e of n.filter(e=>e.expiresAt!==null))c(e.id)}return(0,K.jsx)(T,{open:e,onOpenChange:t,children:(0,K.jsxs)(S,{className:`sm:max-w-xl max-h-[85vh] flex flex-col`,children:[(0,K.jsxs)(b,{children:[(0,K.jsxs)(C,{className:`text-sm flex items-center gap-1.5`,children:[(0,K.jsx)(o,{className:`size-4`}),` Token Test`]}),(0,K.jsx)(w,{className:`text-xs`,children:`Probe stored tokens for validity. Nothing is modified.`})]}),(0,K.jsxs)(`div`,{className:`space-y-4 overflow-y-auto flex-1 pr-1`,children:[(0,K.jsxs)(`section`,{className:`space-y-2`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,K.jsx)(`p`,{className:`text-xs font-medium`,children:`Current tokens`}),(0,K.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,K.jsx)(I,{id:`token-test-refresh`,checked:a,onCheckedChange:s,className:`cursor-pointer`}),(0,K.jsx)(N,{htmlFor:`token-test-refresh`,className:`text-[11px] cursor-pointer`,children:`Also test refresh`})]}),(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`h-7 text-xs cursor-pointer`,onClick:l,children:`Test all`})]})]}),n.length===0&&(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`No accounts to test.`}),(0,K.jsx)(`div`,{className:`space-y-1.5`,children:n.map(e=>{let t=r.get(e.id),n=t?.result?.accessToken.status;return(0,K.jsxs)(`div`,{className:`p-2 rounded border bg-card space-y-1`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,K.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,K.jsx)(`span`,{className:`text-xs font-medium truncate block`,children:e.label??e.email??e.id.slice(0,8)}),e.expiresAt&&(0,K.jsx)(`span`,{className:`text-[10px] text-muted-foreground`,children:Re(e.expiresAt)})]}),(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`h-7 text-xs cursor-pointer shrink-0`,disabled:t?.loading,onClick:()=>void c(e.id),children:t?.loading?(0,K.jsx)(m,{className:`size-3 animate-spin`}):`Test`})]}),t&&!t.loading&&(0,K.jsxs)(`div`,{className:`text-[11px] pl-2 border-l-2 border-muted`,children:[t.error&&(0,K.jsx)(`p`,{className:`text-error`,children:t.error}),n&&(0,K.jsxs)(`span`,{className:n.startsWith(`valid`)?`text-success`:`text-error`,children:[n,t.result?.accessToken.code?` (${t.result.accessToken.code})`:``]})]})]},e.id)})})]}),(0,K.jsx)(Le,{accounts:n})]}),(0,K.jsx)(x,{children:(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`text-xs cursor-pointer`,onClick:()=>t(!1),children:`Close`})})]})})}function Be(){let{usages:e,accounts:t,activeAccountId:n,initialLoading:r,refreshing:a,flashIds:s,reload:u}=fe(),[f,h]=(0,G.useState)(null),[g,_]=(0,G.useState)(null),[v,b]=(0,G.useState)(null),[x,S]=(0,G.useState)(null),[C,w]=(0,G.useState)(null),[T,E]=(0,G.useState)(null),[D,O]=(0,G.useState)(void 0);function k(e){u(),e&&h(e)}async function A(e,t){_(e);try{await ie(e,{status:t===`disabled`?`active`:`disabled`})}catch(e){h(e.message)}_(null),u()}async function j(){if(v){try{await ce(v.id),h(`Account "${v.display}" removed.`),u()}catch(e){h(`Failed to remove: ${e.message}`)}b(null)}}let M=new Map(t.map(e=>[e.id,e])),N=e[0]?.usage.lastFetchedAt;return(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsx)(H,{description:`Connect multiple Claude accounts. PPM rotates between them automatically to avoid rate limits.`,onRefresh:()=>void u(),refreshing:a,disabled:r,actions:(0,K.jsxs)(K.Fragment,{children:[(0,K.jsxs)(y,{size:`sm`,className:`cursor-pointer gap-1.5`,onClick:()=>{O(void 0),E(`add`)},children:[(0,K.jsx)(c,{className:`size-4`}),` Add account`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>{w(null),E(`export`)},children:[(0,K.jsx)(p,{className:`size-4`}),` Export`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>E(`import`),children:[(0,K.jsx)(i,{className:`size-4`}),` Import`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>E(`rotation`),children:[(0,K.jsx)(d,{className:`size-4`}),` Rotation`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>E(`token-test`),children:[(0,K.jsx)(o,{className:`size-4`}),` Token test`]})]})}),(0,K.jsx)(V,{message:f,onDismiss:()=>h(null)}),(0,K.jsxs)(`section`,{className:`space-y-2`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium`,children:`Accounts`}),N&&(0,K.jsxs)(`span`,{className:`text-[10px] text-muted-foreground`,children:[`usage `,xe(new Date(N).getTime())]})]}),r?(0,K.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-xs text-muted-foreground`,children:[(0,K.jsx)(m,{className:`size-4 animate-spin`}),` Loading accounts...`]}):e.length===0?(0,K.jsxs)(`div`,{className:`flex flex-col items-center gap-2 py-8 text-center`,children:[(0,K.jsx)(l,{className:`size-5 text-muted-foreground`}),(0,K.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No accounts connected yet.`}),(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Add one to start using PPM.`})]}):(0,K.jsx)(B,{children:e.map(e=>(0,K.jsx)(he,{layout:`strip`,entry:e,isActive:e.accountId===n,accountInfo:M.get(e.accountId),onToggle:A,toggling:g===e.accountId,onDelete:(e,t)=>b({id:e,display:t}),onExport:e=>{w(e),E(`export`)},onViewProfile:(e,t)=>S({profile:e,accountId:t}),onReauth:()=>{O(e.accountLabel??e.accountId.slice(0,8)),E(`add`)},flash:s.has(e.accountId)},e.accountId))})]}),x&&(0,K.jsx)(De,{profile:x.profile,accountId:x.accountId,onClose:()=>S(null)}),v&&(0,K.jsx)(Oe,{display:v.display,onConfirm:()=>void j(),onCancel:()=>b(null)}),(0,K.jsx)(ve,{open:T===`add`,onOpenChange:e=>{E(e?`add`:null),e||O(void 0)},onSuccess:k,signInAgainFor:D}),(0,K.jsx)(Ae,{open:T===`export`,onOpenChange:e=>{E(e?`export`:null),e||w(null)},accounts:t,preselectId:C,onMessage:h}),(0,K.jsx)(je,{open:T===`import`,onOpenChange:e=>E(e?`import`:null),onSuccess:k}),(0,K.jsx)(Pe,{open:T===`rotation`,onOpenChange:e=>E(e?`rotation`:null)}),(0,K.jsx)(ze,{open:T===`token-test`,onOpenChange:e=>E(e?`token-test`:null),accounts:t})]})}function Ve({mode:e,onOpenChange:t,password:n,onPasswordChange:r,busy:a,onExport:o,onImport:s,error:c}){let l=(0,G.useRef)(null),d=e===`export`;return(0,K.jsx)(T,{open:e!==null,onOpenChange:t,children:(0,K.jsxs)(S,{className:`sm:max-w-md`,children:[(0,K.jsxs)(b,{children:[(0,K.jsxs)(C,{className:`text-sm flex items-center gap-1.5`,children:[(0,K.jsx)(u,{className:`size-4`}),` `,d?`Export Codex Accounts`:`Import Codex Accounts`]}),(0,K.jsx)(w,{className:`text-xs leading-relaxed`,children:d?`Writes a password-encrypted file containing each account's login.`:`Reads a backup file. The password must be the one it was exported with.`})]}),(0,K.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,K.jsx)(N,{htmlFor:`codex-backup-password`,className:`text-xs`,children:`Password`}),(0,K.jsx)(D,{id:`codex-backup-password`,type:`password`,placeholder:d?`Choose a password`:`Password used for the export`,value:n,onChange:e=>r(e.target.value),className:`text-xs`,autoComplete:d?`new-password`:`current-password`}),(0,K.jsx)(`p`,{className:`text-[10px] text-muted-foreground`,children:`A backup holds full logins — keep the file and its password together.`})]}),c&&(0,K.jsx)(`div`,{className:`text-[11px] p-2 rounded bg-error/10 text-error`,children:c}),(0,K.jsx)(`input`,{ref:l,type:`file`,accept:`application/json,.json`,className:`hidden`,onChange:e=>{let t=e.target.files?.[0];e.target.value=``,t&&s(t)}}),(0,K.jsxs)(x,{children:[(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`text-xs cursor-pointer`,onClick:()=>t(!1),children:`Cancel`}),(0,K.jsxs)(y,{size:`sm`,className:`text-xs cursor-pointer gap-1.5`,disabled:!n.trim()||a,onClick:()=>d?o():l.current?.click(),children:[a?(0,K.jsx)(m,{className:`size-3.5 animate-spin`}):d?(0,K.jsx)(p,{className:`size-3.5`}):(0,K.jsx)(i,{className:`size-3.5`}),d?`Download backup`:`Choose file`]})]})]})})}var He=[{value:`round-robin`,label:`Round-robin`,hint:`Take the next account each time.`},{value:`fill-first`,label:`Fill-first`,hint:`Stay on one account until it runs out.`},{value:`lowest-usage`,label:`Lowest usage`,hint:`Pick whichever has the most left.`}];function Ue({open:e,onOpenChange:t,strategy:n,onChange:r}){return(0,K.jsx)(T,{open:e,onOpenChange:t,children:(0,K.jsxs)(S,{className:`sm:max-w-md`,children:[(0,K.jsxs)(b,{children:[(0,K.jsx)(C,{className:`text-sm`,children:`Codex Rotation`}),(0,K.jsx)(w,{className:`text-xs leading-relaxed`,children:`How a new chat chooses among your Codex accounts.`})]}),(0,K.jsx)(`div`,{className:`space-y-2`,children:He.map(e=>(0,K.jsxs)(`button`,{type:`button`,onClick:()=>r(e.value),"aria-current":n===e.value?`true`:void 0,className:g(`w-full text-left rounded-md border px-3 py-2.5 min-h-11 cursor-pointer transition-colors`,n===e.value?`border-primary bg-primary/10`:`border-border hover:bg-accent/50 active:bg-accent`),children:[(0,K.jsx)(`p`,{className:`text-sm font-medium`,children:e.label}),(0,K.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:e.hint})]},e.value))}),(0,K.jsx)(x,{children:(0,K.jsx)(y,{size:`sm`,variant:`outline`,className:`text-xs cursor-pointer`,onClick:()=>t(!1),children:`Done`})})]})})}function $(e){return e==null?null:Math.round(e*100)}function We({usage:e}){let t=e.session==null&&e.weekly==null;return(0,K.jsxs)(`div`,{className:`space-y-2`,children:[(t||e.session!=null)&&(0,K.jsx)(W,{label:`5-Hour Session`,pct:$(e.fiveHour),reset:z(e.session),resetsAt:e.session?.resetsAt}),(t||e.weekly!=null)&&(0,K.jsx)(W,{label:`Weekly`,pct:$(e.sevenDay),reset:z(e.weekly),resetsAt:e.weekly?.resetsAt})]})}function Ge(){let[e,t]=(0,G.useState)(null),n=me(()=>t(null)),[r,o]=(0,G.useState)(null);return(0,K.jsxs)(`div`,{className:`space-y-4`,children:[(0,K.jsx)(H,{description:(0,K.jsxs)(K.Fragment,{children:[`Each Codex account keeps its own login (`,(0,K.jsx)(`code`,{children:`CODEX_HOME`}),`). One is picked per chat by the rotation setting.`]}),onRefresh:()=>void n.load(),refreshing:n.loading,actions:(0,K.jsxs)(K.Fragment,{children:[(0,K.jsxs)(y,{size:`sm`,className:`cursor-pointer gap-1.5`,onClick:()=>t(`add`),children:[(0,K.jsx)(c,{className:`size-4`}),` Add account`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>t(`export`),children:[(0,K.jsx)(p,{className:`size-4`}),` Export`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>t(`import`),children:[(0,K.jsx)(i,{className:`size-4`}),` Import`]}),(0,K.jsxs)(y,{size:`sm`,variant:`outline`,className:`cursor-pointer gap-1.5`,onClick:()=>t(`rotation`),children:[(0,K.jsx)(d,{className:`size-4`}),` Rotation`]})]})}),(0,K.jsx)(V,{message:n.msg??n.err,onDismiss:()=>{n.setMsg(null),n.setErr(null)}}),(0,K.jsxs)(`section`,{className:`space-y-2`,children:[(0,K.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,K.jsx)(`h3`,{className:`text-sm font-medium`,children:`Codex Accounts`}),(0,K.jsx)(`span`,{className:`text-[10px] text-muted-foreground capitalize`,children:n.strategy.replace(`-`,` `)})]}),n.loading&&n.accounts.length===0?(0,K.jsxs)(`div`,{className:`flex items-center gap-2 py-6 text-xs text-muted-foreground`,children:[(0,K.jsx)(m,{className:`size-4 animate-spin`}),` Loading accounts...`]}):n.accounts.length===0?(0,K.jsxs)(`div`,{className:`flex flex-col items-center gap-2 py-8 text-center`,children:[(0,K.jsx)(l,{className:`size-5 text-muted-foreground`}),(0,K.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`No Codex accounts yet.`}),(0,K.jsxs)(`p`,{className:`text-xs text-muted-foreground`,children:[`Chats use your default `,(0,K.jsx)(`code`,{children:`~/.codex`}),` login until you add one.`]})]}):(0,K.jsx)(B,{children:n.accounts.map(e=>{let t=n.usages[e.id]??{};return(0,K.jsxs)(_e,{dense:!0,className:`min-w-[300px] shrink-0 snap-start${e.signedOut?` opacity-60`:``}`,"data-testid":`account-card`,"data-account-id":e.id,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,K.jsx)(`span`,{className:`text-sm font-medium truncate flex-1 min-w-0`,children:e.label}),e.signedOut&&(0,K.jsx)(pe,{className:`text-[10px] text-error shrink-0 font-medium`,hint:`Codex rejected this account's login (revoked or expired), so no turn can run on it and it is skipped. The usage shown is the last reading. Click to sign in again — the account keeps its settings and chats.`,onClick:e.type===`chatgpt`?()=>o(e):void 0,children:`Sign in again`}),(0,K.jsx)(`span`,{className:`text-[10px] uppercase tracking-wide text-text-subtle border border-border rounded px-1 shrink-0`,children:e.type}),U(e.planType)&&(0,K.jsx)(`span`,{className:`text-[10px] text-text-subtle shrink-0`,children:U(e.planType)}),(0,K.jsx)(I,{checked:e.status!==`disabled`,onCheckedChange:()=>void n.toggle(e.id,e.status??`active`),disabled:n.toggling.has(e.id),"aria-label":e.status===`disabled`?`Enable account`:`Disable account`,className:`cursor-pointer shrink-0`}),(0,K.jsxs)(M,{children:[(0,K.jsx)(O,{asChild:!0,children:(0,K.jsx)(`button`,{type:`button`,onClick:()=>void n.remove(e.id),"aria-label":`Remove account`,className:`p-2 rounded cursor-pointer text-text-subtle hover:text-error hover:bg-surface-elevated transition-colors shrink-0`,children:(0,K.jsx)(a,{className:`size-4`})})}),(0,K.jsx)(k,{side:`top`,children:`Remove account`})]})]}),(0,K.jsx)(We,{usage:t}),t.resetCredits&&(0,K.jsx)(`div`,{className:`text-[10px] text-text-subtle`,children:(0,K.jsx)(be,{credits:t.resetCredits})}),t.session==null&&(()=>{let r=de(t.weekly);return r?(0,K.jsxs)(`div`,{className:`flex min-h-12 items-center justify-between gap-3 rounded-md border px-2.5 py-1.5 ${e.dailyGuardEnabled&&r.blocked?`border-error/40 bg-error/5`:`border-border/50 bg-surface/40`}`,children:[(0,K.jsxs)(`div`,{className:`min-w-0 space-y-0.5`,children:[(0,K.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs font-medium ${e.dailyGuardEnabled&&r.blocked?`text-error`:`text-text-secondary`}`,children:[(0,K.jsx)(`span`,{children:`Daily guard`}),(0,K.jsxs)(M,{children:[(0,K.jsx)(O,{asChild:!0,children:(0,K.jsx)(`button`,{type:`button`,"aria-label":`About Daily guard`,className:`text-text-subtle hover:text-text-primary cursor-help`,children:(0,K.jsx)(s,{className:`size-3.5`})})}),(0,K.jsx)(k,{side:`top`,className:`max-w-64 text-xs leading-relaxed`,children:`Spreads the weekly quota across five weekdays, adding 20% per weekday. Weekend slots keep the previous cap; unused allowance carries forward. Daily slots start at the reset time and use UTC weekdays. Turn it off any time to use the remaining quota freely.`})]})]}),(0,K.jsx)(`p`,{className:`text-[10px] tabular-nums ${e.dailyGuardEnabled&&r.blocked?`text-error`:`text-text-subtle`}`,children:e.dailyGuardEnabled&&r.blocked?`${Math.round(r.used*100)}% used / ${Math.round(r.cap*100)}% daily cap. New turns paused.`:`Weekday ${r.day}/5, ${Math.round(r.cap*100)}% daily cap`})]}),(0,K.jsxs)(`span`,{className:`hidden`,title:`Weekday ${r.day}/5: hold weekly usage at or below ${Math.round(r.cap*100)}% to last until reset.`,children:[`Daily guard `,e.dailyGuardEnabled?`· Weekday ${r.day}/5 cap ${Math.round(r.cap*100)}%`:``]}),(0,K.jsx)(I,{checked:!!e.dailyGuardEnabled,onCheckedChange:()=>void n.toggleDailyGuard(e.id,!!e.dailyGuardEnabled),disabled:n.toggling.has(e.id),"aria-label":e.dailyGuardEnabled?`Disable Daily guard`:`Enable Daily guard`,className:`cursor-pointer shrink-0`})]}):null})()]},e.id)})})]}),(0,K.jsx)(ge,{open:e===`add`,onOpenChange:e=>{e||n.cancelLogin(),t(e?`add`:null)},label:n.label,onLabelChange:n.setLabel,apiKey:n.apiKey,onApiKeyChange:n.setApiKey,adding:n.adding,onAddApiKey:()=>void n.addApiKey(),deviceWaiting:n.deviceWaiting,onStartDevice:()=>void n.startDevice(),device:n.device,browser:n.browser,onStartBrowser:()=>void n.startBrowser(),loginStarting:n.loginStarting,callbackUrl:n.callbackUrl,onCallbackUrlChange:n.setCallbackUrl,submittingCallback:n.submittingCallback,onSubmitCallback:()=>void n.submitCallback(),error:n.err}),(0,K.jsx)(Ve,{mode:e===`export`?`export`:e===`import`?`import`:null,onOpenChange:e=>{e||t(null)},password:n.backupPassword,onPasswordChange:n.setBackupPassword,busy:n.exporting||n.importing,onExport:()=>void n.doExport(),onImport:e=>void n.doImport(e),error:n.err}),r&&(0,K.jsx)(ye,{account:r,onClose:()=>o(null),onDone:()=>void n.load()}),(0,K.jsx)(Ue,{open:e===`rotation`,onOpenChange:e=>t(e?`rotation`:null),strategy:n.strategy,onChange:e=>void n.changeStrategy(e)})]})}var Ke=[{id:`claude`,label:`Claude`},{id:`codex`,label:`Codex`}];function qe(){let[e,t]=(0,G.useState)(null),[n,r]=(0,G.useState)(`claude`);(0,G.useEffect)(()=>{let e=!1;return re().then(n=>{e||t(new Set(Object.keys(n.providers??{})))}).catch(()=>{e||t(new Set([`claude`]))}),()=>{e=!0}},[]);let i=Ke.filter(t=>t.id===`claude`||e?.has(t.id)),a=i.length>1;return(0,K.jsxs)(`div`,{className:`space-y-4`,"data-testid":`accounts-pane`,"data-provider":n,children:[a&&(0,K.jsx)(`div`,{className:`flex gap-1 border-b border-border/50`,children:i.map(e=>(0,K.jsxs)(`button`,{onClick:()=>r(e.id),"data-testid":`accounts-tab-${e.id}`,"aria-current":n===e.id?`page`:void 0,className:`flex items-center gap-1.5 px-3 py-2 text-xs rounded-t transition-colors cursor-pointer ${n===e.id?`text-primary border-b-2 border-primary font-medium`:`text-text-subtle hover:text-text-secondary`}`,children:[(0,K.jsx)(A,{providerId:e.id}),e.label]},e.id))}),e===null&&!a&&(0,K.jsxs)(`div`,{className:`flex items-center gap-2 text-xs text-muted-foreground`,children:[(0,K.jsx)(m,{className:`size-3 animate-spin`}),` Checking configured providers...`]}),n===`claude`?(0,K.jsx)(Be,{}):(0,K.jsx)(Ge,{})]})}export{qe as AccountsSettingsSection};