@hienlh/ppm 0.20.8 → 0.20.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/accounts-settings-section-ByM99C7p.js +1 -0
- package/dist/web/assets/{ai-resource-editor-CMQfhxXp.js → ai-resource-editor-BsB9wa0z.js} +1 -1
- package/dist/web/assets/{ai-settings-section-M07DCjAI.js → ai-settings-section-BT-QVUBa.js} +1 -1
- package/dist/web/assets/ai-settings-section-BUPOINaw.js +1 -0
- package/dist/web/assets/{angular-html-BtJLNcMw.js → angular-html-CTx8qAkv.js} +1 -1
- package/dist/web/assets/{angular-ts-CQiu5F_W.js → angular-ts-BhBGUAnu.js} +1 -1
- package/dist/web/assets/api-client-C6VTn7cG.js +1 -0
- package/dist/web/assets/{api-settings-BQIwZoLm.js → api-settings-CqiVKFUX.js} +1 -1
- package/dist/web/assets/{api-themes-CmJZLEY1.js → api-themes-DtLXqXpC.js} +1 -1
- package/dist/web/assets/{apl-CKZvgs-w.js → apl-Dec3pSzF.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-BBlJtEm5.js → appearance-settings-section-DJEoECoJ.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-D55g5j-p.js +1 -0
- package/dist/web/assets/arrow-right-BqNSIscF.js +1 -0
- package/dist/web/assets/{astro-D0b8ssLD.js → astro-Cb_3-S86.js} +1 -1
- package/dist/web/assets/{audio-preview-DPMXOZ55.js → audio-preview-CF4tTFv_.js} +1 -1
- package/dist/web/assets/{blade-DOIpINLw.js → blade-1QMj5JxI.js} +1 -1
- package/dist/web/assets/{bsl-CJ7_JlQ8.js → bsl-D0bT3Wjp.js} +1 -1
- package/dist/web/assets/{button-zgoXa9r7.js → button-DhnNoqpV.js} +1 -1
- package/dist/web/assets/{cairo-5CpV2T1P.js → cairo-DTuGb2w7.js} +1 -1
- package/dist/web/assets/{chat-tab-Dn45anqr.js → chat-tab-fD2vWwj2.js} +8 -8
- package/dist/web/assets/circle-question-mark-DUaySkoB.js +1 -0
- package/dist/web/assets/{cobol-BreMVrtc.js → cobol-DSx_Nb7T.js} +1 -1
- package/dist/web/assets/{code-editor-CKASeDbu.js → code-editor-CyFLXCsD.js} +2 -2
- package/dist/web/assets/codex-daily-guard-Cg19EWxk.js +1 -0
- package/dist/web/assets/{coffee-D7DoKh5b.js → coffee-DBcQ7DKB.js} +1 -1
- package/dist/web/assets/{conflict-editor-C-Nv8YYj.js → conflict-editor-DSjo2FRy.js} +1 -1
- package/dist/web/assets/{context-menu-DM9CgxGh.js → context-menu-DchAkdnL.js} +1 -1
- package/dist/web/assets/{cpp-BnWeWAoE.js → cpp-Bj1azGr-.js} +1 -1
- package/dist/web/assets/{crystal-D2pJbFUn.js → crystal-CCgkDI0i.js} +1 -1
- package/dist/web/assets/{csv-preview-ou3KFzIH.js → csv-preview-BIE3Wpww.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-dnWnblYo.js → data-grid-overlay-editor-C2NAQ-88.js} +1 -1
- package/dist/web/assets/{database-viewer-Ckqg_-SS.js → database-viewer-CMvIORcM.js} +1 -1
- package/dist/web/assets/{diff-viewer-CMzFnBCD.js → diff-viewer-BQQ6fUr6.js} +1 -1
- package/dist/web/assets/{docx-preview-B7Aospmh.js → docx-preview-DcCh59Oe.js} +1 -1
- package/dist/web/assets/{edge-aqGMtbY3.js → edge-ClBfeO7z.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-BvgPvuhH.js → edit-diff-preview-CRjufuw_.js} +1 -1
- package/dist/web/assets/{elixir-CsYMBLc5.js → elixir-ir__-3kt.js} +1 -1
- package/dist/web/assets/{elm-BU8C2gFp.js → elm-CUv-xMuU.js} +1 -1
- package/dist/web/assets/{erb-Z_E49ab8.js → erb-DcRwHyUq.js} +1 -1
- package/dist/web/assets/{erlang-BRqOjXH9.js → erlang-DW-GAdmv.js} +1 -1
- package/dist/web/assets/{esm-BHyWbWKE.js → esm-DBm5LSCw.js} +1 -1
- package/dist/web/assets/{explorer-body-CVx4qaIF.js → explorer-body-DRo2JHP9.js} +2 -2
- package/dist/web/assets/explorer-window-content-7mnRpSqn.js +1 -0
- package/dist/web/assets/{extension-manager-section-CtcpVMWs.js → extension-manager-section-CEfr7-Kv.js} +1 -1
- package/dist/web/assets/{extension-webview-DZ_QRZSM.js → extension-webview-D7HqqdTv.js} +1 -1
- package/dist/web/assets/{files-settings-section-D7v-6nTa.js → files-settings-section-h6a-WpSO.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CWbxmDGR.js → fortran-fixed-form-DrQbMSkt.js} +1 -1
- package/dist/web/assets/{fsharp-DKMYbq6I.js → fsharp-DvDj84Ys.js} +1 -1
- package/dist/web/assets/{gdresource-B3yFau4V.js → gdresource-CPgY7R2s.js} +1 -1
- package/dist/web/assets/{general-settings-section-BW_h8pdA.js → general-settings-section-Bt3KNcyM.js} +1 -1
- package/dist/web/assets/{git-commit-BW0p3Ekk.js → git-commit-NJ9iGbTd.js} +1 -1
- package/dist/web/assets/{git-log-panel-BQZkJDu0.js → git-log-panel-H-Bg9p7m.js} +1 -1
- package/dist/web/assets/{git-rebase-DrvmQCS7.js → git-rebase-DBSy9OKE.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-At-P8hlA.js +1 -0
- package/dist/web/assets/{glide-data-grid-JKEslqGe.js → glide-data-grid-D3Xc_WGh.js} +4 -4
- package/dist/web/assets/{glimmer-js-BY1XApJJ.js → glimmer-js-EGWSSdwJ.js} +1 -1
- package/dist/web/assets/{glimmer-ts-DPZtxF8N.js → glimmer-ts-BpfXRYRV.js} +1 -1
- package/dist/web/assets/{glsl-DZ8G_Hvf.js → glsl-BtWQ4j8X.js} +1 -1
- package/dist/web/assets/{graphql-Ck06uCZy.js → graphql-DHR5AO2c.js} +1 -1
- package/dist/web/assets/{group-chat-tab-CTAhtVCr.js → group-chat-tab-BkLt7J_s.js} +1 -1
- package/dist/web/assets/{hack-pLogDwAs.js → hack-A3iOItmC.js} +1 -1
- package/dist/web/assets/{haml-CBqeCQ2A.js → haml-DXA8NXfZ.js} +1 -1
- package/dist/web/assets/{handlebars-B66JmVtp.js → handlebars-BdIFK4pP.js} +1 -1
- package/dist/web/assets/{html-Dk4wmfwC.js → html-BTA4eRkb.js} +1 -1
- package/dist/web/assets/{html-derivative-CJWqfN51.js → html-derivative-Cd_vDV4L.js} +1 -1
- package/dist/web/assets/{http-CexhotTh.js → http-CMY5fp9k.js} +1 -1
- package/dist/web/assets/{hurl-BScKFk3Z.js → hurl-DJZLRi6K.js} +1 -1
- package/dist/web/assets/{hxml-DAle7vtu.js → hxml-C2EdtM37.js} +1 -1
- package/dist/web/assets/{image-preview-V5SglTWk.js → image-preview-D_6-FhZP.js} +1 -1
- package/dist/web/assets/{index-DVNJ2W5R.js → index-BV_sFdHY.js} +6 -6
- package/dist/web/assets/index-Di9nVG3y.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-btSsuixb.js +1 -0
- package/dist/web/assets/{jinja-yHpyD80t.js → jinja-DWU92jHm.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-VyzVbAnw.js → jira-watcher-section-BhQzWJ3r.js} +1 -1
- package/dist/web/assets/{jison-BY6WLJVu.js → jison-Bt3f5-S6.js} +1 -1
- package/dist/web/assets/{julia-Dd_iYQDT.js → julia-BS-e0jQg.js} +1 -1
- package/dist/web/assets/{just-BTuUKpxF.js → just-OU89Ot3I.js} +1 -1
- package/dist/web/assets/keybindings-store-alreySno.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-DOJi75mV.js → keyboard-shortcuts-section-B_D0FamP.js} +1 -1
- package/dist/web/assets/{latex-Dt3OUlMZ.js → latex-Bt5mTs31.js} +1 -1
- package/dist/web/assets/{liquid-BxPJU2ix.js → liquid-C03IGCJ6.js} +1 -1
- package/dist/web/assets/{lua-DEGIvn7d.js → lua-Dl4zPu0P.js} +1 -1
- package/dist/web/assets/{markdown-renderer-DtQIHtLs.js → markdown-renderer-CG_nGh7k.js} +3 -3
- package/dist/web/assets/markdown-renderer-CgbqvSuc.js +1 -0
- package/dist/web/assets/{marko-BDxwBJur.js → marko-BEnkIoHY.js} +1 -1
- package/dist/web/assets/{mdc-B8aXPTg7.js → mdc-DGglct19.js} +1 -1
- package/dist/web/assets/{media-url-TYMlO0M5.js → media-url-LiPp2UsV.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-Ch1e_JBq.js → mobile-bottom-sheet-BizNfPAu.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-DAZTzi1k.js +1 -0
- package/dist/web/assets/{nextflow-DBVZnnT_.js → nextflow-DKFYidyC.js} +1 -1
- package/dist/web/assets/{nginx-Crpyd-Do.js → nginx-Bhq0tzlh.js} +1 -1
- package/dist/web/assets/{nim-BSqTBYDj.js → nim-CSuBzart.js} +1 -1
- package/dist/web/assets/notification-store-HCg5Shw9.js +1 -0
- package/dist/web/assets/{notifications-settings-section-CeRvkXOM.js → notifications-settings-section-BAs_p_ai.js} +1 -1
- package/dist/web/assets/{number-overlay-editor-DldrUNs4.js → number-overlay-editor-CYHlP43e.js} +1 -1
- package/dist/web/assets/{overview-panel-DA0QB1-5.js → overview-panel-xn2BYvn6.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-B20_sIFd.js +1 -0
- package/dist/web/assets/{panel-store-D42k25jy.js → panel-store-XhAruR6D.js} +1 -1
- package/dist/web/assets/{pdf-preview-Cq33D5ej.js → pdf-preview-BJjqU3AX.js} +1 -1
- package/dist/web/assets/{perl-BU0kbB-K.js → perl-uDVR7Ceh.js} +1 -1
- package/dist/web/assets/{php-D9n1ikHN.js → php-Bby8J3zk.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-nzfJdXCC.js +1 -0
- package/dist/web/assets/{postgres-viewer-C__Z0C0s.js → postgres-viewer-CJbHLcGE.js} +3 -3
- package/dist/web/assets/{ppmbot-settings-section-Cma4VU7p.js → ppmbot-settings-section-5U0a58oj.js} +1 -1
- package/dist/web/assets/{process-table-DH_HXtGc.js → process-table-CqGL0Bcj.js} +1 -1
- package/dist/web/assets/{project-store-C83MMnlt.js → project-store-Dy3A3LJT.js} +1 -1
- package/dist/web/assets/{proxy-settings-section-xrTsFlTF.js → proxy-settings-section-B1LDpGSc.js} +1 -1
- package/dist/web/assets/{pug-BOpaczDr.js → pug-EapfojTW.js} +1 -1
- package/dist/web/assets/{qml-B-cQXhrg.js → qml-DiR6J5_C.js} +1 -1
- package/dist/web/assets/{query-audit-section-Drk97gb8.js → query-audit-section-DfCvY94_.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-CS4sHKD5.js +1 -0
- package/dist/web/assets/{razor-DFB0pNCh.js → razor-B2QvlGNY.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-_LH4uCcA.js +2 -0
- package/dist/web/assets/{remote-desktop-mobile-view-Gk8UCVIx.js → remote-desktop-mobile-view-C5TUPDh_.js} +1 -1
- package/dist/web/assets/{remote-desktop-readiness-gate-6reSZvad.js → remote-desktop-readiness-gate-DuT4A893.js} +1 -1
- package/dist/web/assets/{remote-desktop-window-content-BW2KqONI.js → remote-desktop-window-content-B4V-1aJn.js} +1 -1
- package/dist/web/assets/{rst-DAXedsgm.js → rst-D2eJ0kH4.js} +1 -1
- package/dist/web/assets/{ruby-BCO4polC.js → ruby-DzpnBpz-.js} +1 -1
- package/dist/web/assets/{run-in-terminal-DoTOtgQS.js → run-in-terminal-DisO5HC-.js} +1 -1
- package/dist/web/assets/{sas-DfB5dcFC.js → sas-WDAKsX0U.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-DcKEz-S7.js → schedules-settings-section-C2SjwWFB.js} +1 -1
- package/dist/web/assets/{scss-BknIVgn7.js → scss-Ci6PdscL.js} +1 -1
- package/dist/web/assets/{send-to-chat-Kl-0dNwN.js → send-to-chat-DDFdKWNf.js} +1 -1
- package/dist/web/assets/{separator-DvCsdlws.js → separator-CyisF27O.js} +1 -1
- package/dist/web/assets/settings-body-CdFauCAU.js +2 -0
- package/dist/web/assets/{settings-store-_s4xFcFq.js → settings-store-BkNrLdjk.js} +2 -2
- package/dist/web/assets/settings-tab-DGUlmMbc.js +1 -0
- package/dist/web/assets/settings-window-content-CIH5sihe.js +1 -0
- package/dist/web/assets/{shaderlab-CPu31TpY.js → shaderlab-Cv3mWofL.js} +1 -1
- package/dist/web/assets/{shellsession-CFX4c_TM.js → shellsession-D1KVtQa1.js} +1 -1
- package/dist/web/assets/{soy-rz9hilgm.js → soy-BGLBwFZT.js} +1 -1
- package/dist/web/assets/{sparql-Bo8ZDeqE.js → sparql-TvnQmFDh.js} +1 -1
- package/dist/web/assets/{sql-query-editor-BoYxc5vd.js → sql-query-editor-DaQqRBNA.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-O5lF3MMj.js → sqlite-viewer-Bpz7QnBk.js} +1 -1
- package/dist/web/assets/{stata-B5J-QMyE.js → stata-Gi-BKyEv.js} +1 -1
- package/dist/web/assets/{streaming-store-QQvg1cS-.js → streaming-store-DAbnOfLu.js} +1 -1
- package/dist/web/assets/{surrealql-Caw1ADnG.js → surrealql-BVXs6tkz.js} +1 -1
- package/dist/web/assets/{svelte-D3oMVcxz.js → svelte-BlnFpLWV.js} +1 -1
- package/dist/web/assets/{system-monitor-body-C2mVJ7PV.js → system-monitor-body-BBJOWFCa.js} +2 -2
- package/dist/web/assets/system-monitor-tab-D7RIwhmg.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BaKoFDEz.js → system-monitor-window-content-CH0UmMP9.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-C7F6D8hJ.js → tab-host-window-content-C_yU5MH_.js} +1 -1
- package/dist/web/assets/{tab-store-B2ip0-Mv.js → tab-store-B7BksLr-.js} +1 -1
- package/dist/web/assets/team-member-sheet-CopMME1E.js +2 -0
- package/dist/web/assets/{team-member-window-content-BwOTsc9r.js → team-member-window-content-CUDQ6oX7.js} +1 -1
- package/dist/web/assets/{templ-BAdNyIOL.js → templ-YJYH5r1A.js} +1 -1
- package/dist/web/assets/{terminal-tab-DlZW3MCn.js → terminal-tab-BjI95uG5.js} +1 -1
- package/dist/web/assets/{tex-IQ8mELOR.js → tex-IVZLDIeh.js} +1 -1
- package/dist/web/assets/{tool-cards-yiwO861p.js → tool-cards-I71v1w5Z.js} +6 -6
- package/dist/web/assets/treemap-KZPCXAKY-BBrhTPZk.js +1 -0
- package/dist/web/assets/{ts-tags-D1DDO2jl.js → ts-tags-DBd321I-.js} +1 -1
- package/dist/web/assets/{twig-C8Fd5RvV.js → twig-DQENQjEn.js} +1 -1
- package/dist/web/assets/{use-blob-url-D358Rpyp.js → use-blob-url-CGChX4vi.js} +1 -1
- package/dist/web/assets/{use-inline-blame-D2pZjDoA.js → use-inline-blame-CRUwd6FF.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-DTPfPPvF.js → use-monaco-theme-qzMbRfCW.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DGEx9xB0.js → use-open-team-member-kmNnlzDD.js} +1 -1
- package/dist/web/assets/{use-remote-desktop-display-choice-BnVT4dhX.js → use-remote-desktop-display-choice-0zDkCmsK.js} +1 -1
- package/dist/web/assets/{use-websocket-DQg9BQcq.js → use-websocket-D8OGBwH8.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-CfY1-PkA.js → vendor-mermaid-BoRFiriK.js} +1 -1
- package/dist/web/assets/{video-preview-Dc4YbBqm.js → video-preview-Mlgl7hGr.js} +1 -1
- package/dist/web/assets/{vue-EvccXlkP.js → vue-Do2JXEwi.js} +1 -1
- package/dist/web/assets/{vue-html-Dg0xUJCh.js → vue-html-Bse0CwSC.js} +1 -1
- package/dist/web/assets/{vue-vine-BdrMg72k.js → vue-vine-C8JSXu5Z.js} +1 -1
- package/dist/web/assets/{window-pip-registry-BWxaVLj5.js → window-pip-registry-C2dcY0pE.js} +1 -1
- package/dist/web/assets/{window-store-CfxP5yor.js → window-store-2z4t5YAt.js} +1 -1
- package/dist/web/assets/ws-auth-DI1E6Fe7.js +1 -0
- package/dist/web/assets/{xml-MmDclzxK.js → xml-OX6YmzGK.js} +1 -1
- package/dist/web/assets/{xsl-DQEFs6Dd.js → xsl-RQUQgcgy.js} +1 -1
- package/dist/web/index.html +34 -34
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/providers/codex-app-server/codex-event-mapper.ts +28 -1
- package/src/providers/codex-app-server/codex-provider.ts +38 -5
- package/src/providers/codex-app-server/codex-usage-parser.ts +3 -1
- package/src/server/routes/codex-accounts.ts +9 -5
- package/src/services/codex-account-login.ts +33 -5
- package/src/services/codex-account-portability.ts +3 -2
- package/src/services/codex-account.service.ts +48 -7
- package/src/services/codex-login-callback.ts +18 -0
- package/src/services/db.service.ts +14 -0
- package/src/shared/codex-daily-guard.ts +27 -0
- package/src/shared/codex-plan-label.ts +17 -0
- package/src/web/components/chat/codex-usage-panel.tsx +22 -2
- package/src/web/components/settings/accounts/account-card.tsx +43 -2
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +43 -2
- package/src/web/components/settings/accounts/use-codex-accounts.ts +14 -2
- package/dist/web/assets/accounts-settings-section-CRXY4Bsc.js +0 -1
- package/dist/web/assets/ai-settings-section-446NZWhI.js +0 -1
- package/dist/web/assets/api-client-CYZ79uUS.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-DfR8k8Bc.js +0 -1
- package/dist/web/assets/circle-question-mark-qcWYO1P_.js +0 -1
- package/dist/web/assets/explorer-window-content-CqKVMkgW.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CPd78aaT.js +0 -1
- package/dist/web/assets/index-B9Wbjl0o.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BciNds6R.js +0 -1
- package/dist/web/assets/keybindings-store-B5dEz0n1.js +0 -1
- package/dist/web/assets/markdown-renderer-BGPo364Y.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-C1q_DhrI.js +0 -1
- package/dist/web/assets/notification-store-DvUtkAXs.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-ClD3l2Bs.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-PU6kD0rW.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-4LzjL0Zt.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-6JdUr0Bj.js +0 -2
- package/dist/web/assets/settings-body-CcKls51a.js +0 -2
- package/dist/web/assets/settings-tab-ChAk8nh2.js +0 -1
- package/dist/web/assets/settings-window-content-sA1K0cPW.js +0 -1
- package/dist/web/assets/system-monitor-tab-tXkg3jpV.js +0 -1
- package/dist/web/assets/team-member-sheet-BA70_jmM.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-DiumLNMg.js +0 -1
- package/dist/web/assets/use-accounts-data-B9t4BQaI.js +0 -1
- package/dist/web/assets/ws-auth-FRzMmfQx.js +0 -1
- /package/dist/web/assets/{abap-D-o-bvtI.js → abap-BIHt2d9F.js} +0 -0
- /package/dist/web/assets/{actionscript-3-D2tIzAm8.js → actionscript-3-rbCNRHFj.js} +0 -0
- /package/dist/web/assets/{ada-DxatBcxS.js → ada-CN94LQTJ.js} +0 -0
- /package/dist/web/assets/{andromeeda-D0LK9YJ3.js → andromeeda-BsmcoXuQ.js} +0 -0
- /package/dist/web/assets/{apache-B7uMPNiW.js → apache-pRQmqcVZ.js} +0 -0
- /package/dist/web/assets/{apex-B7GiXrAr.js → apex-CNEumC1X.js} +0 -0
- /package/dist/web/assets/{api-client-DwXlVfXP.js → api-client-DKvkxuDr.js} +0 -0
- /package/dist/web/assets/{applescript-D_SIFkKe.js → applescript-DjEVsS-3.js} +0 -0
- /package/dist/web/assets/{apply-theme-BVXAUYoN.js → apply-theme-Abd1TNqE.js} +0 -0
- /package/dist/web/assets/{ara-C3iECgdg.js → ara-DbhZRlTL.js} +0 -0
- /package/dist/web/assets/{asciidoc-gc8z_MMY.js → asciidoc-BsfbAXIK.js} +0 -0
- /package/dist/web/assets/{asm-27qeoPOI.js → asm-Vw5NMXjI.js} +0 -0
- /package/dist/web/assets/{aurora-x-C5J9fWe1.js → aurora-x-CoahU6sR.js} +0 -0
- /package/dist/web/assets/{awk-B7GBzF92.js → awk-ChKjCxHz.js} +0 -0
- /package/dist/web/assets/{ayu-dark-XlnQhuLL.js → ayu-dark-By9RKxTf.js} +0 -0
- /package/dist/web/assets/{ayu-light-DKLLilkU.js → ayu-light-CEsgeLON.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Dv-XOprJ.js → ayu-mirage-CYrtwSLW.js} +0 -0
- /package/dist/web/assets/{ballerina-DACRT-sT.js → ballerina-CCSVz22W.js} +0 -0
- /package/dist/web/assets/{bat-BlQCq2XE.js → bat-DU4--CMj.js} +0 -0
- /package/dist/web/assets/{beancount-DYDztRI0.js → beancount-DF_tlRlF.js} +0 -0
- /package/dist/web/assets/{berry-BE5YKqvx.js → berry-CZTnatvO.js} +0 -0
- /package/dist/web/assets/{bibtex-CAm7mgEm.js → bibtex-BSpXy8jk.js} +0 -0
- /package/dist/web/assets/{bicep-BKgAbfVa.js → bicep-yv1Ydq5Z.js} +0 -0
- /package/dist/web/assets/{bird2-yrBNbW1G.js → bird2-Db7Z7SMN.js} +0 -0
- /package/dist/web/assets/{c-txZFr4RM.js → c-C3ivhwA4.js} +0 -0
- /package/dist/web/assets/{c3-D_DrzBUd.js → c3-N4mKyst9.js} +0 -0
- /package/dist/web/assets/{cadence-CUFCEgG9.js → cadence-BYK8tFFk.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-_yqa69dV.js → can-open-in-ppm-DU5MH6XF.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Dn32MuhA.js → catppuccin-frappe-BK9Tv2XG.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-Dewo7yDf.js → catppuccin-latte-De4mVCp3.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-D8WOWZeo.js → catppuccin-macchiato-Bxt_D7cs.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-b0md48XM.js → catppuccin-mocha-Dz2o8gN7.js} +0 -0
- /package/dist/web/assets/{circle-OKk7s0DX.js → circle-BpGTLAas.js} +0 -0
- /package/dist/web/assets/{clarity-Ct3UqLBj.js → clarity-BJdh0Pw-.js} +0 -0
- /package/dist/web/assets/{clock-BmPiGrSF.js → clock-Dqq0H9yH.js} +0 -0
- /package/dist/web/assets/{clojure-CwP5aPEo.js → clojure-DG-FYGzn.js} +0 -0
- /package/dist/web/assets/{cmake-D5RF49e5.js → cmake-D1imEXan.js} +0 -0
- /package/dist/web/assets/{code-CRiIHSiv.js → code-CX6640Tv.js} +0 -0
- /package/dist/web/assets/{codeowners-Cl5897L_.js → codeowners-YpylTFB5.js} +0 -0
- /package/dist/web/assets/{codeql-BKh1RCg0.js → codeql-ajDEPgqi.js} +0 -0
- /package/dist/web/assets/{common-lisp-BhHuD6t0.js → common-lisp-C2sCrd-m.js} +0 -0
- /package/dist/web/assets/{copy-CXuIH4VC.js → copy-Bt_1tjoJ.js} +0 -0
- /package/dist/web/assets/{coq-Dco3qwD8.js → coq-BqybyDc9.js} +0 -0
- /package/dist/web/assets/{csharp-Dpjw9ZA9.js → csharp-nKCEiujy.js} +0 -0
- /package/dist/web/assets/{css-CPe4cabH.js → css-CJSp2CsD.js} +0 -0
- /package/dist/web/assets/{csv-BAcTN8Kt.js → csv-Nh9vQwQm.js} +0 -0
- /package/dist/web/assets/{csv-parser-DbxGKdMG.js → csv-parser-iow1WWML.js} +0 -0
- /package/dist/web/assets/{cue-Cr6y60TA.js → cue-Ch79RPsU.js} +0 -0
- /package/dist/web/assets/{cypher-DWtqXXFP.js → cypher-CgiOQ6hs.js} +0 -0
- /package/dist/web/assets/{d-BASiTZk4.js → d-CmBcySpH.js} +0 -0
- /package/dist/web/assets/{dark-plus-56kmnrcl.js → dark-plus-COLEX2ui.js} +0 -0
- /package/dist/web/assets/{dart-C5mZnjoE.js → dart-BTAArMcJ.js} +0 -0
- /package/dist/web/assets/{data-grid-types-BfGeMDy0.js → data-grid-types-tNj9zuRV.js} +0 -0
- /package/dist/web/assets/{database-Dz0W7OH7.js → database-9_p74TtY.js} +0 -0
- /package/dist/web/assets/{dax-B4I2e7d8.js → dax-BbZzRY9i.js} +0 -0
- /package/dist/web/assets/{desktop-DekGg0BT.js → desktop-OmQvU3-J.js} +0 -0
- /package/dist/web/assets/{diff-CWnJBd73.js → diff-BEPnb9Fb.js} +0 -0
- /package/dist/web/assets/{dist-BKOM_AoT.js → dist-BDpuZ376.js} +0 -0
- /package/dist/web/assets/{dist-CWMPvPem.js → dist-BiqPfYiP.js} +0 -0
- /package/dist/web/assets/{docker-DevchDxK.js → docker-Cg17HgTD.js} +0 -0
- /package/dist/web/assets/{dotenv-DCvtdaGJ.js → dotenv-DEMIaY7V.js} +0 -0
- /package/dist/web/assets/{download-D70Q4Ba6.js → download-CEPFebri.js} +0 -0
- /package/dist/web/assets/{dracula-8U6Vkk3l.js → dracula-BkCFq8SO.js} +0 -0
- /package/dist/web/assets/{dracula-soft-Bfeew5Wg.js → dracula-soft-Cjp6-ltF.js} +0 -0
- /package/dist/web/assets/{dream-maker-DDTJVuoy.js → dream-maker-Ce7Y_UKj.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CEHJqI4m.js → emacs-lisp-BjpyfTbN.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CHHLt7cq.js → everforest-dark-9r4okw1s.js} +0 -0
- /package/dist/web/assets/{everforest-light-TcTbK1sM.js → everforest-light-GCQTZ3Ju.js} +0 -0
- /package/dist/web/assets/{eye-dfZn7evX.js → eye-BQaWPI4P.js} +0 -0
- /package/dist/web/assets/{eye-off-CmtfyRUD.js → eye-off-BlB3QV90.js} +0 -0
- /package/dist/web/assets/{fennel-Bh4vsA1A.js → fennel-ITjY5Z6u.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-D5K2aaLi.js → file-exclamation-point-BDmSZbxs.js} +0 -0
- /package/dist/web/assets/{fish-D0T0fILG.js → fish-DP2uEzyg.js} +0 -0
- /package/dist/web/assets/{flask-conical-Dix4qNDj.js → flask-conical-Cj-J6cxs.js} +0 -0
- /package/dist/web/assets/{fluent-CgXDXdyy.js → fluent-irzaP1f6.js} +0 -0
- /package/dist/web/assets/{folder-symlink-BavsIzoj.js → folder-symlink-BSYalAHC.js} +0 -0
- /package/dist/web/assets/{format-bytes-Bc6SqF7M.js → format-bytes-B9Dbme9a.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-CUg3Xhz4.js → fortran-free-form-BAPoainr.js} +0 -0
- /package/dist/web/assets/{gdscript-Cx056p_k.js → gdscript-DJ098V1z.js} +0 -0
- /package/dist/web/assets/{gdshader-Dj1qP4I5.js → gdshader-DS2Jfy-K.js} +0 -0
- /package/dist/web/assets/{genie-SBt15wn7.js → genie-CqfOaFFx.js} +0 -0
- /package/dist/web/assets/{gherkin-bTJiHIrB.js → gherkin-CoZ2ucDH.js} +0 -0
- /package/dist/web/assets/{github-dark-BOPhyugs.js → github-dark-BNLgnSO9.js} +0 -0
- /package/dist/web/assets/{github-dark-default-Cktqe6NF.js → github-dark-default-CYlSggwi.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CeWfII_B.js → github-dark-dimmed-DumUysDk.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-CTx8LpdV.js → github-dark-high-contrast-TKrcDJ2I.js} +0 -0
- /package/dist/web/assets/{github-light-DVsO5Un1.js → github-light-DVENFHLJ.js} +0 -0
- /package/dist/web/assets/{github-light-default-BiyPxicr.js → github-light-default-DtN8AGcE.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-BPlnke0a.js → github-light-high-contrast-NSyF37x4.js} +0 -0
- /package/dist/web/assets/{gleam-CWSxvD0C.js → gleam-hp4NMSb5.js} +0 -0
- /package/dist/web/assets/{gn-D7NUVJuv.js → gn-Dptw2PEu.js} +0 -0
- /package/dist/web/assets/{gnuplot-c5uXmJKc.js → gnuplot-9juRzjwv.js} +0 -0
- /package/dist/web/assets/{go-CRsHi4Cw.js → go-D-QLCda0.js} +0 -0
- /package/dist/web/assets/{groovy-DpV0RA5r.js → groovy-DHKNy6Eh.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DD9fguUN.js → gruvbox-dark-hard-BFD6aQdB.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-BvcwpYJ-.js → gruvbox-dark-medium-D5llYBNp.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-48dCxX9M.js → gruvbox-dark-soft-DOztQmNR.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CjJ994Ip.js → gruvbox-light-hard-BFGD-8Pd.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-DQJQZ_rV.js → gruvbox-light-medium-DzoyXgT7.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-AqsC9mKw.js → gruvbox-light-soft-B1Ix2zLN.js} +0 -0
- /package/dist/web/assets/{hand-CIPALZsE.js → hand-BkDyLfcc.js} +0 -0
- /package/dist/web/assets/{haskell-B5V7yvmh.js → haskell-BnrJ0k7G.js} +0 -0
- /package/dist/web/assets/{haxe-LWTKfPKr.js → haxe-DwShHT_-.js} +0 -0
- /package/dist/web/assets/{hcl-QmNZdAtI.js → hcl-VOjEibVB.js} +0 -0
- /package/dist/web/assets/{hjson-CboJsAzK.js → hjson-BHEn7pNn.js} +0 -0
- /package/dist/web/assets/{hlsl-Bd-DO_WL.js → hlsl-CrvxlGii.js} +0 -0
- /package/dist/web/assets/{horizon-CU8Bi_ss.js → horizon-D3T-P0u6.js} +0 -0
- /package/dist/web/assets/{horizon-bright-iSQ3_3jP.js → horizon-bright-vifGsEdk.js} +0 -0
- /package/dist/web/assets/{houston-CTXv3Puz.js → houston-IrOgT7r4.js} +0 -0
- /package/dist/web/assets/{hy-Cd4hTidD.js → hy-5eSgTJ8G.js} +0 -0
- /package/dist/web/assets/{image-gallery-J8h8IY0c.js → image-gallery-COea0nSk.js} +0 -0
- /package/dist/web/assets/{imba-BipCHEY2.js → imba-Dp0I4DVl.js} +0 -0
- /package/dist/web/assets/{ini-Dd1KkMrB.js → ini-CHDKad9O.js} +0 -0
- /package/dist/web/assets/{java-rGr0OQD8.js → java-YLv7IDJ1.js} +0 -0
- /package/dist/web/assets/{javascript-DHhCH2TN.js → javascript-jr4kIdgl.js} +0 -0
- /package/dist/web/assets/{json-CMMLBCUf.js → json-EJWSOZfp.js} +0 -0
- /package/dist/web/assets/{json5-mPOwTOsB.js → json5-D6f_uNwJ.js} +0 -0
- /package/dist/web/assets/{jsonc-Bie0E6D3.js → jsonc-DrHCdmos.js} +0 -0
- /package/dist/web/assets/{jsonl-CpqHmtC1.js → jsonl-Cwc3cXEL.js} +0 -0
- /package/dist/web/assets/{jsonnet-D21Ms51e.js → jsonnet-BwZfaIFT.js} +0 -0
- /package/dist/web/assets/{jssm-BXnl1nur.js → jssm-BpEmg_MT.js} +0 -0
- /package/dist/web/assets/{jsx-Chnii_vB.js → jsx-DBGPwcr4.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DhxR0UjL.js → kanagawa-dragon-DRfVaGIX.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-fupRg5wp.js → kanagawa-lotus-CjOlny0Q.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DSWbRS9M.js → kanagawa-wave-DvdM88a4.js} +0 -0
- /package/dist/web/assets/{kdl-DirGHqUy.js → kdl-DSo3PvM0.js} +0 -0
- /package/dist/web/assets/{key-round-oIll4vo-.js → key-round-Cv4z7_KG.js} +0 -0
- /package/dist/web/assets/{keyboard-BLwfiRkT.js → keyboard-HG0BUtuN.js} +0 -0
- /package/dist/web/assets/{kotlin-DkhUVMDC.js → kotlin-BV8TwCGu.js} +0 -0
- /package/dist/web/assets/{kusto-DU7OpovF.js → kusto-DCo1Fdmm.js} +0 -0
- /package/dist/web/assets/{laserwave-iLKb1ZCT.js → laserwave-DPX9FvD9.js} +0 -0
- /package/dist/web/assets/{lean-Cuvh_QAP.js → lean-Cya8dqZu.js} +0 -0
- /package/dist/web/assets/{less-B5tQZOdI.js → less-CJUPgkIs.js} +0 -0
- /package/dist/web/assets/{lib-BAsciSLw.js → lib-DUBAP_Gd.js} +0 -0
- /package/dist/web/assets/{libesm-CdttTIAd.js → libesm-CcPYXCKp.js} +0 -0
- /package/dist/web/assets/{light-plus-DiHU2KL5.js → light-plus-BXXrlFJH.js} +0 -0
- /package/dist/web/assets/{llvm-sCBTKCKN.js → llvm-CUbQanZr.js} +0 -0
- /package/dist/web/assets/{loader-circle-DxI3O9_f.js → loader-circle-H81ywCuQ.js} +0 -0
- /package/dist/web/assets/{log-DtyR34Ie.js → log-B3fFlSMd.js} +0 -0
- /package/dist/web/assets/{logo-CP91aQyG.js → logo-ucAyw6zf.js} +0 -0
- /package/dist/web/assets/{luau-nfGo2ulP.js → luau-C4l6TSTB.js} +0 -0
- /package/dist/web/assets/{make-XbDn9GHe.js → make-CDtGIVc7.js} +0 -0
- /package/dist/web/assets/{markdown-BB1_3xLr.js → markdown-CH1zKu2p.js} +0 -0
- /package/dist/web/assets/{material-theme-CIx_sxAy.js → material-theme-CEuGVTmh.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-DsQeueDD.js → material-theme-darker-CUZ3MoIr.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-x_lMi74j.js → material-theme-lighter-DxAqJSid.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-Bkgh25P8.js → material-theme-ocean-BSvvHJp6.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-CoOjdbti.js → material-theme-palenight-QVOl50_m.js} +0 -0
- /package/dist/web/assets/{matlab-D6xZas_8.js → matlab-CZLQLwUp.js} +0 -0
- /package/dist/web/assets/{mdx-Cl5_YNoz.js → mdx-DO4NJsOo.js} +0 -0
- /package/dist/web/assets/{mermaid-8Eow4jz_.js → mermaid-CIwzg1uO.js} +0 -0
- /package/dist/web/assets/{min-dark-D4DhjgRE.js → min-dark-BdIgslXy.js} +0 -0
- /package/dist/web/assets/{min-light-DzAckd0t.js → min-light-DyU-T1r2.js} +0 -0
- /package/dist/web/assets/{mipsasm-zI_zu0tj.js → mipsasm-DRad-Klm.js} +0 -0
- /package/dist/web/assets/{mojo-DQnGGPHf.js → mojo-DXp5LDOc.js} +0 -0
- /package/dist/web/assets/{monitor-CG69bSMW.js → monitor-CKcYUoAV.js} +0 -0
- /package/dist/web/assets/{monokai-CrFoD2VD.js → monokai-BiKX3qby.js} +0 -0
- /package/dist/web/assets/{moonbit-DdphIGs6.js → moonbit-BOUoLF5r.js} +0 -0
- /package/dist/web/assets/{move-BUlkn0xW.js → move-Dlk64FWK.js} +0 -0
- /package/dist/web/assets/{narrat-CbOBdtCn.js → narrat-B-qd2Lh0.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CeVLUTuQ.js → nextflow-groovy-iAMYas0F.js} +0 -0
- /package/dist/web/assets/{night-owl-Bm4LhUR1.js → night-owl-3YNqjftR.js} +0 -0
- /package/dist/web/assets/{night-owl-light-DtcBiMEf.js → night-owl-light-GIncHb5w.js} +0 -0
- /package/dist/web/assets/{nix-DrdwQ70g.js → nix-xjlEb3uw.js} +0 -0
- /package/dist/web/assets/{nord-B0Head1v.js → nord-C8tvWx7P.js} +0 -0
- /package/dist/web/assets/{nushell-CmUdltKn.js → nushell-BqBb4pU6.js} +0 -0
- /package/dist/web/assets/{objective-c-BGDR33XN.js → objective-c-Bk6s0Cn5.js} +0 -0
- /package/dist/web/assets/{objective-cpp-Buc1Zhd0.js → objective-cpp-DGTVWQ3V.js} +0 -0
- /package/dist/web/assets/{ocaml-xdzl9rRD.js → ocaml-D6wPQ4kw.js} +0 -0
- /package/dist/web/assets/{odin-DiUFngnd.js → odin-C9mw2xRN.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-BURxmIp3.js → one-dark-pro-SMBlneFb.js} +0 -0
- /package/dist/web/assets/{one-light-BMRMInAj.js → one-light-C9JMU1Cx.js} +0 -0
- /package/dist/web/assets/{openscad-DAgLA5J3.js → openscad-C0I9CtuA.js} +0 -0
- /package/dist/web/assets/{pascal-CoDZxcRB.js → pascal-D0z4DJnY.js} +0 -0
- /package/dist/web/assets/{pkl-DzKM6BuM.js → pkl-CITYNGVz.js} +0 -0
- /package/dist/web/assets/{plastic-vF15LmNS.js → plastic-Bf0T6rmg.js} +0 -0
- /package/dist/web/assets/{plsql-C1v07teA.js → plsql-Chr907pu.js} +0 -0
- /package/dist/web/assets/{po-CLLC8BuA.js → po-07U75CYV.js} +0 -0
- /package/dist/web/assets/{poimandres-Uq0xysxM.js → poimandres-B0T4qwXY.js} +0 -0
- /package/dist/web/assets/{polar-2-luY1Pc.js → polar-BvNJMMV5.js} +0 -0
- /package/dist/web/assets/{portal-container-context-DeLx1cOI.js → portal-container-context-Bqn516X4.js} +0 -0
- /package/dist/web/assets/{postcss-lbFhe0rz.js → postcss-BSdf-zzo.js} +0 -0
- /package/dist/web/assets/{power-CvtnQHU4.js → power-D_wlLkC0.js} +0 -0
- /package/dist/web/assets/{powerquery-RL8O9Wie.js → powerquery-B2Ad_HK8.js} +0 -0
- /package/dist/web/assets/{powershell-upFWhb-b.js → powershell-DO5WY2v2.js} +0 -0
- /package/dist/web/assets/{prisma-DBtYaWFk.js → prisma-BfaRPYzs.js} +0 -0
- /package/dist/web/assets/{prolog-D8jSMykJ.js → prolog-Cz0Nue9e.js} +0 -0
- /package/dist/web/assets/{proto-BM4eadF7.js → proto-FVleUZJO.js} +0 -0
- /package/dist/web/assets/{puppet-BQDQ3Snu.js → puppet-BEX5o7hE.js} +0 -0
- /package/dist/web/assets/{purescript-Cj2aTX4s.js → purescript-BZX6Kw4j.js} +0 -0
- /package/dist/web/assets/{python-BYpqq6u4.js → python-CO89UIxE.js} +0 -0
- /package/dist/web/assets/{qmldir-4uiTcwAu.js → qmldir-DH526DWB.js} +0 -0
- /package/dist/web/assets/{qss-D5xB-Mqo.js → qss-wp8im4Iz.js} +0 -0
- /package/dist/web/assets/{r-P6Ujy8rf.js → r-F9n--bvq.js} +0 -0
- /package/dist/web/assets/{racket-COKgCFuM.js → racket-BQvbOE5D.js} +0 -0
- /package/dist/web/assets/{raku-z14Q9i7K.js → raku-B0iWSynj.js} +0 -0
- /package/dist/web/assets/{react-DqIU0LFM.js → react-Bcm1Oab0.js} +0 -0
- /package/dist/web/assets/{red-ObAvgvQ5.js → red-Cm00r9eI.js} +0 -0
- /package/dist/web/assets/{reg-Qv4f0JFA.js → reg-CBuhna22.js} +0 -0
- /package/dist/web/assets/{regexp-CEOVGIzy.js → regexp-C9j1_6oU.js} +0 -0
- /package/dist/web/assets/{rel-cWOyTUwd.js → rel-BYzlBhbq.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CMp5xxCW.js → resolve-theme-Dm8ehbOv.js} +0 -0
- /package/dist/web/assets/{riscv-BZDwx-lv.js → riscv-CQQP0Ro9.js} +0 -0
- /package/dist/web/assets/{ron-BpjosPS0.js → ron-j1ON9YzM.js} +0 -0
- /package/dist/web/assets/{rose-pine-DhxfJWcm.js → rose-pine-Cbri3jon.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-BbGt9Wy_.js → rose-pine-dawn-BMuvKrGo.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-dj4Zxgik.js → rose-pine-moon-4biZzaYD.js} +0 -0
- /package/dist/web/assets/{rosmsg-EciOaHo2.js → rosmsg-CXx22rtz.js} +0 -0
- /package/dist/web/assets/{rust-C_eH59O_.js → rust-D28cFIhL.js} +0 -0
- /package/dist/web/assets/{sass-Dos3R5lL.js → sass-DtNw4V28.js} +0 -0
- /package/dist/web/assets/{save-B1y4KIK9.js → save-BehCuMf4.js} +0 -0
- /package/dist/web/assets/{scala-8lVKsTvf.js → scala-hv69_YQC.js} +0 -0
- /package/dist/web/assets/{scheme-Bd3VdaZj.js → scheme-Dtp9B_ng.js} +0 -0
- /package/dist/web/assets/{sdbl-C4Yrjlgh.js → sdbl-DEwEngAE.js} +0 -0
- /package/dist/web/assets/{send-B9VUQlEK.js → send-CtXcGcsk.js} +0 -0
- /package/dist/web/assets/{shellscript-OFZbPKZH.js → shellscript-CCOF9tox.js} +0 -0
- /package/dist/web/assets/{shield-check-BJSX7cAS.js → shield-check-z9PX8HVZ.js} +0 -0
- /package/dist/web/assets/{shield-off-E1cQwXdc.js → shield-off-Du0U5dVI.js} +0 -0
- /package/dist/web/assets/{slack-dark-aL6vlxZs.js → slack-dark-DT0E0ABt.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BKt1E7sL.js → slack-ochin-DHHiQnxo.js} +0 -0
- /package/dist/web/assets/{smalltalk-D6qVBL1U.js → smalltalk-BF_I35xJ.js} +0 -0
- /package/dist/web/assets/{snazzy-light-BrWerT0t.js → snazzy-light-Cv7tIZo9.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CzxTJgVQ.js → solarized-dark-wZ5YPCys.js} +0 -0
- /package/dist/web/assets/{solarized-light-BecaDAnG.js → solarized-light-DGDOr1AS.js} +0 -0
- /package/dist/web/assets/{solidity-C9t7pZ84.js → solidity-BJknuPqJ.js} +0 -0
- /package/dist/web/assets/{sparkles-blTKn0vi.js → sparkles-CO9zx1f-.js} +0 -0
- /package/dist/web/assets/{splunk-CtxfFKtF.js → splunk-B1ClO_KU.js} +0 -0
- /package/dist/web/assets/{sql-7FE_Duz0.js → sql-CXZ66NtF.js} +0 -0
- /package/dist/web/assets/{ssh-config-3Fr37ACC.js → ssh-config-kgM67Sf6.js} +0 -0
- /package/dist/web/assets/{stylus-BP4mE1sO.js → stylus-RcVaInR-.js} +0 -0
- /package/dist/web/assets/{swift-0uqTFouD.js → swift-Cm0fh2lX.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DQaNM3DI.js → synthwave-84-CwLiazxr.js} +0 -0
- /package/dist/web/assets/{system-verilog-DUIVrZHZ.js → system-verilog-CzYaDVLH.js} +0 -0
- /package/dist/web/assets/{systemd-CtNo_WIh.js → systemd-DbSagfLw.js} +0 -0
- /package/dist/web/assets/{table-8HT8BG30.js → table-BK4h8E7u.js} +0 -0
- /package/dist/web/assets/{talonscript-B0HXaCVT.js → talonscript-BCFnWejL.js} +0 -0
- /package/dist/web/assets/{tasl-Cb7RAl11.js → tasl-BUdqtEe1.js} +0 -0
- /package/dist/web/assets/{tcl-B79aC6l5.js → tcl-CR_lTV9N.js} +0 -0
- /package/dist/web/assets/{terraform-75jJDjpW.js → terraform-Bqsha-xe.js} +0 -0
- /package/dist/web/assets/{text-wrap-DafY0-Pa.js → text-wrap-DLtVgSnU.js} +0 -0
- /package/dist/web/assets/{tokyo-night-Cz9Uh9qb.js → tokyo-night-Cnuo6JB6.js} +0 -0
- /package/dist/web/assets/{toml-CD6y9A-z.js → toml-CLFdhT22.js} +0 -0
- /package/dist/web/assets/{tsv-CXJMbBrb.js → tsv-Dw3tLrZN.js} +0 -0
- /package/dist/web/assets/{tsx-BuYN0I2t.js → tsx-B8k1sFqw.js} +0 -0
- /package/dist/web/assets/{turtle-DWH-Rjse.js → turtle-BMKMfTrj.js} +0 -0
- /package/dist/web/assets/{typescript-BPd-hsrR.js → typescript-D38LfGSl.js} +0 -0
- /package/dist/web/assets/{typespec-B0wvVykx.js → typespec-EbgGJHV_.js} +0 -0
- /package/dist/web/assets/{typst-DWG2c48-.js → typst-CQXV2SzX.js} +0 -0
- /package/dist/web/assets/{use-coarse-long-press-BsBpxWup.js → use-coarse-long-press-XrJ4Upuv.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-Bi42QhIi.js → use-is-mobile-Dypg-2ZO.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-CNdG4QHH.js → use-visual-viewport-Bj3QHRkF.js} +0 -0
- /package/dist/web/assets/{utils-IrnQujwR.js → utils-CwNUra6_.js} +0 -0
- /package/dist/web/assets/{v-Ch6E4Egd.js → v-Ci1jObJJ.js} +0 -0
- /package/dist/web/assets/{vala-CGVKwbPx.js → vala-C_NdfqrY.js} +0 -0
- /package/dist/web/assets/{vb-BIEbSxyT.js → vb-K_r_h_s8.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-YQlzIWv2.js → vendor-xterm-BtjgHRUe.js} +0 -0
- /package/dist/web/assets/{verilog-A-3pYPad.js → verilog-DQPYLsZI.js} +0 -0
- /package/dist/web/assets/{vesper-CK1naZAV.js → vesper-BrmzEV8s.js} +0 -0
- /package/dist/web/assets/{vhdl-CVawmgMg.js → vhdl-CjxQGaH2.js} +0 -0
- /package/dist/web/assets/{viml-CiWjaqBY.js → viml-CTKJNQH1.js} +0 -0
- /package/dist/web/assets/{vitesse-black-PYXW4-BO.js → vitesse-black-i-H_3p1i.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BU5s4AuG.js → vitesse-dark-DZC_Yy3_.js} +0 -0
- /package/dist/web/assets/{vitesse-light-DgkIT-N7.js → vitesse-light-rf4kpmk8.js} +0 -0
- /package/dist/web/assets/{vyper-D8cRDPp3.js → vyper-BLnOw4Nf.js} +0 -0
- /package/dist/web/assets/{wasm-ClFJgzmT.js → wasm-CWeHvGNG.js} +0 -0
- /package/dist/web/assets/{wasm-C02wlRbP.js → wasm-DbLMmiW0.js} +0 -0
- /package/dist/web/assets/{wenyan-B8plxIr8.js → wenyan-CLwdRuzd.js} +0 -0
- /package/dist/web/assets/{wgsl-CVujtm3V.js → wgsl-CBTDMVJR.js} +0 -0
- /package/dist/web/assets/{wikitext-CmcG9B69.js → wikitext-CQHrF84e.js} +0 -0
- /package/dist/web/assets/{wit--gl2Jium.js → wit-8xdbdipU.js} +0 -0
- /package/dist/web/assets/{wolfram-6T_1D-NS.js → wolfram-tFwx32a_.js} +0 -0
- /package/dist/web/assets/{x-Bdaog6Az.js → x-DlnNFF2t.js} +0 -0
- /package/dist/web/assets/{yaml-CIVV9uA1.js → yaml-BP8N91fU.js} +0 -0
- /package/dist/web/assets/{zenscript-CoT4yNdA.js → zenscript-B10Pj7JT.js} +0 -0
- /package/dist/web/assets/{zig-BWs2Qo3G.js → zig-BZKcTtrK.js} +0 -0
|
@@ -19,9 +19,11 @@ import {
|
|
|
19
19
|
peekCodexAccount,
|
|
20
20
|
selectCodexAccount,
|
|
21
21
|
getAllCodexUsages,
|
|
22
|
+
getCodexAccountUsage,
|
|
22
23
|
codexUsageLevel,
|
|
23
24
|
type CodexAccount,
|
|
24
25
|
} from "../../services/codex-account.service.ts";
|
|
26
|
+
import { dailyGuardMessage, dailyGuardState } from "../../shared/codex-daily-guard.ts";
|
|
25
27
|
import { isCodexAccountUsageLimited, markCodexAccountUsageLimited } from "../../services/codex-account-cooldown.ts";
|
|
26
28
|
import { isCodexUsageLimit, codexErrorMessage, parseCodexUsageLimitReset } from "./codex-usage-limit.ts";
|
|
27
29
|
import { killProcessTree } from "../../services/windows-process-tree.ts";
|
|
@@ -157,6 +159,8 @@ interface LiveSession {
|
|
|
157
159
|
* nothing downstream can notice. Follow-ups therefore wait in `pendingTurns`
|
|
158
160
|
* and are sent when the running turn reports `turn/completed`. */
|
|
159
161
|
turnInFlight?: boolean;
|
|
162
|
+
/** A daily-guard usage read is in progress before the next turn starts. */
|
|
163
|
+
checkingDailyGuard?: boolean;
|
|
160
164
|
/** Id of the running turn — `turn/interrupt` needs it, `threadId` alone is rejected. */
|
|
161
165
|
activeTurnId?: string | null;
|
|
162
166
|
/** Interrupt asked for before `turn/started` named the turn; fired on arrival. */
|
|
@@ -397,7 +401,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
397
401
|
return;
|
|
398
402
|
}
|
|
399
403
|
}
|
|
400
|
-
this.startTurn(live, message, opts);
|
|
404
|
+
void this.startTurn(live, message, opts);
|
|
401
405
|
for await (const ev of live.channel.iterator) {
|
|
402
406
|
yield ev;
|
|
403
407
|
}
|
|
@@ -407,7 +411,23 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
407
411
|
pushMessage(sessionId: string, content: string, opts?: SendMessageOpts): void {
|
|
408
412
|
const live = this.live.get(sessionId);
|
|
409
413
|
if (!live || live.client.isClosed) return;
|
|
410
|
-
this.startTurn(live, content, opts);
|
|
414
|
+
void this.startTurn(live, content, opts);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Return a refusal only when this session's managed account opted into pacing. */
|
|
418
|
+
private async dailyGuardError(live: LiveSession): Promise<string | null> {
|
|
419
|
+
const accountId = live.threadId ? getSessionCodexAccount(live.threadId) : null;
|
|
420
|
+
const account = accountId ? getCodexAccount(accountId) : null;
|
|
421
|
+
if (!account?.dailyGuardEnabled) return null;
|
|
422
|
+
try {
|
|
423
|
+
const usage = await getCodexAccountUsage(account.id);
|
|
424
|
+
const state = usage.session == null ? dailyGuardState(usage.weekly) : null;
|
|
425
|
+
return state?.blocked ? dailyGuardMessage(state) : null;
|
|
426
|
+
} catch {
|
|
427
|
+
// An unreadable quota cannot honestly be called spent. Keep the account usable and let
|
|
428
|
+
// Codex's real quota refusal remain the authority until the next successful usage read.
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
411
431
|
}
|
|
412
432
|
|
|
413
433
|
/**
|
|
@@ -418,7 +438,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
418
438
|
* second entry would both show it twice and shift every later `rollout-N` id, which
|
|
419
439
|
* fork anchors resolve against.
|
|
420
440
|
*/
|
|
421
|
-
private startTurn(live: LiveSession, message: string, opts?: SendMessageOpts, replay = false): void {
|
|
441
|
+
private async startTurn(live: LiveSession, message: string, opts?: SendMessageOpts, replay = false): Promise<void> {
|
|
422
442
|
if (!live.threadId) return;
|
|
423
443
|
// One turn at a time — anything sent now would be dropped without a trace.
|
|
424
444
|
// `now` additionally cuts the running turn short so this one answers next.
|
|
@@ -428,6 +448,19 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
428
448
|
if (priority === "now") this.interruptActiveTurn(live);
|
|
429
449
|
return;
|
|
430
450
|
}
|
|
451
|
+
if (live.checkingDailyGuard) {
|
|
452
|
+
this.enqueueTurn(live, { message, opts, priority: opts?.priority ?? "next" });
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
live.checkingDailyGuard = true;
|
|
456
|
+
const guardError = await this.dailyGuardError(live);
|
|
457
|
+
live.checkingDailyGuard = false;
|
|
458
|
+
if (guardError) {
|
|
459
|
+
live.channel.push({ type: "error", message: guardError });
|
|
460
|
+
live.channel.push({ type: "done", sessionId: live.threadId, resultSubtype: "error_during_execution" });
|
|
461
|
+
this.endTurn(live);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
431
464
|
live.turnInFlight = true;
|
|
432
465
|
// `/compact` → trigger codex's real compaction (thread/compact/start), not a text turn.
|
|
433
466
|
// Completion is surfaced on the `thread/compacted` notification (handleNotification).
|
|
@@ -516,7 +549,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
516
549
|
live.interruptRequested = false;
|
|
517
550
|
live.lastTurnInput = undefined;
|
|
518
551
|
const next = live.pendingTurns.shift();
|
|
519
|
-
if (next) this.startTurn(live, next.message, next.opts);
|
|
552
|
+
if (next) void this.startTurn(live, next.message, next.opts);
|
|
520
553
|
}
|
|
521
554
|
|
|
522
555
|
// ── Usage-limit rotation ──
|
|
@@ -593,7 +626,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
593
626
|
live.turnInFlight = false;
|
|
594
627
|
live.activeTurnId = null;
|
|
595
628
|
live.interruptRequested = false;
|
|
596
|
-
if (pending) this.startTurn(live, pending.message, pending.opts, true);
|
|
629
|
+
if (pending) void this.startTurn(live, pending.message, pending.opts, true);
|
|
597
630
|
else this.endTurn(live);
|
|
598
631
|
}
|
|
599
632
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { UsageInfo, LimitBucket } from "../provider.interface.ts";
|
|
2
2
|
import type { GetAccountRateLimitsResponse, RateLimitWindow } from "./codex-protocol.ts";
|
|
3
|
+
import { codexPlanLabel } from "../../shared/codex-plan-label.ts";
|
|
3
4
|
|
|
4
5
|
/** codex resetsAt is a unix timestamp; tolerate seconds or milliseconds. */
|
|
5
6
|
function toIso(resetsAt: number | null | undefined): string | undefined {
|
|
@@ -64,6 +65,7 @@ export function parseCodexUsage(res: GetAccountRateLimitsResponse | null | undef
|
|
|
64
65
|
out.session = toBucket(w);
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
|
-
|
|
68
|
+
const planLabel = codexPlanLabel(snap.planType);
|
|
69
|
+
if (planLabel) out.activeAccountLabel = planLabel;
|
|
68
70
|
return out;
|
|
69
71
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { ok, err } from "../../types/api.ts";
|
|
3
|
-
import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, selectCodexAccount, setCodexAccountStatus, codexUsageLevel, type CodexStrategy } from "../../services/codex-account.service.ts";
|
|
3
|
+
import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, selectCodexAccount, setCodexAccountStatus, setCodexDailyGuard, codexUsageLevel, type CodexStrategy } from "../../services/codex-account.service.ts";
|
|
4
4
|
import { addApiKeyAccount, startDeviceLogin, getDeviceLoginStatus, cancelDeviceLogin, startBrowserLogin, submitBrowserCallback, getBrowserLoginStatus, cancelBrowserLogin } from "../../services/codex-account-login.ts";
|
|
5
5
|
import { exportCodexEncrypted, importCodexEncrypted } from "../../services/codex-account-portability.ts";
|
|
6
6
|
|
|
@@ -43,11 +43,15 @@ codexAccountsRoutes.post("/pick", async (c) => {
|
|
|
43
43
|
* immediately instead of taking the better part of a minute.
|
|
44
44
|
*/
|
|
45
45
|
codexAccountsRoutes.patch("/:id", async (c) => {
|
|
46
|
-
const body = await c.req.json<{ status?: string }>().catch(() => ({} as { status?: string }));
|
|
47
|
-
if (body.status !==
|
|
48
|
-
return c.json(err("status must be active or disabled"), 400);
|
|
46
|
+
const body = await c.req.json<{ status?: string; dailyGuardEnabled?: unknown }>().catch(() => ({} as { status?: string; dailyGuardEnabled?: unknown }));
|
|
47
|
+
if (body.status !== undefined) {
|
|
48
|
+
if (body.status !== "active" && body.status !== "disabled") return c.json(err("status must be active or disabled"), 400);
|
|
49
|
+
const updated = setCodexAccountStatus(c.req.param("id"), body.status);
|
|
50
|
+
if (!updated) return c.json(err("Account not found"), 404);
|
|
51
|
+
return c.json(ok(updated));
|
|
49
52
|
}
|
|
50
|
-
|
|
53
|
+
if (typeof body.dailyGuardEnabled !== "boolean") return c.json(err("status or dailyGuardEnabled is required"), 400);
|
|
54
|
+
const updated = setCodexDailyGuard(c.req.param("id"), body.dailyGuardEnabled);
|
|
51
55
|
if (!updated) return c.json(err("Account not found"), 404);
|
|
52
56
|
return c.json(ok(updated));
|
|
53
57
|
});
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { randomUUID } from "node:crypto";
|
|
17
17
|
import { mkdirSync, rmSync } from "node:fs";
|
|
18
|
-
import { buildCodexCallback, checkCodexLoginPort, parseBrowserLogin, type BrowserLoginCallback } from "./codex-login-callback.ts";
|
|
18
|
+
import { buildCodexCallback, buildCodexSuccessCallback, checkCodexLoginPort, parseBrowserLogin, type BrowserLoginCallback } from "./codex-login-callback.ts";
|
|
19
19
|
import { CodexJsonRpcClient, CONTROL_REQUEST_TIMEOUT_MS } from "../providers/codex-app-server/codex-jsonrpc-client.ts";
|
|
20
20
|
import { codexAccountHome, createCodexAccount, type CodexAccount } from "./codex-account.service.ts";
|
|
21
21
|
|
|
@@ -218,18 +218,46 @@ export function submitBrowserCallback(id: string, callbackUrl: string, send: typ
|
|
|
218
218
|
browser.submitted = true;
|
|
219
219
|
const controller = new AbortController();
|
|
220
220
|
p.callbackAbort = controller;
|
|
221
|
-
|
|
221
|
+
let timedOut = false;
|
|
222
|
+
const timeout = unref(setTimeout(() => {
|
|
223
|
+
timedOut = true;
|
|
224
|
+
controller.abort();
|
|
225
|
+
// Bun can leave a loopback fetch pending after its AbortSignal fires. Do not leave the
|
|
226
|
+
// browser polling forever: the callback code cannot safely be reused, so make the
|
|
227
|
+
// failure visible and let the user start a fresh authorization.
|
|
228
|
+
if (isCurrentPendingLogin(id, p)) {
|
|
229
|
+
settle(id, { state: "error", error: "Codex did not finish the callback within 30 seconds. Start a new browser login and try again." });
|
|
230
|
+
}
|
|
231
|
+
}, 30_000));
|
|
222
232
|
void (async () => {
|
|
223
233
|
try {
|
|
224
|
-
|
|
225
|
-
//
|
|
234
|
+
// Connect to 127.0.0.1 (never a user-controlled localhost resolution), but preserve
|
|
235
|
+
// the OAuth redirect's localhost Host header. Codex's loopback listener associates
|
|
236
|
+
// the callback with the redirect URI and otherwise accepts the socket without
|
|
237
|
+
// completing the login.
|
|
238
|
+
let response = await send(target, {
|
|
239
|
+
redirect: "manual",
|
|
240
|
+
signal: controller.signal,
|
|
241
|
+
headers: { Host: "localhost:1455" },
|
|
242
|
+
});
|
|
243
|
+
if (response.status >= 300 && response.status < 400) {
|
|
244
|
+
const location = response.headers.get("location");
|
|
245
|
+
await response.body?.cancel();
|
|
246
|
+
if (!location) throw new Error("Codex returned a callback redirect without a destination.");
|
|
247
|
+
response = await send(buildCodexSuccessCallback(location, browser), {
|
|
248
|
+
redirect: "manual",
|
|
249
|
+
signal: controller.signal,
|
|
250
|
+
headers: { Host: "localhost:1455" },
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// Do not render or log either loopback OAuth response body.
|
|
226
254
|
await response.body?.cancel();
|
|
227
255
|
if (response.status >= 400 && isCurrentPendingLogin(id, p)) {
|
|
228
256
|
settle(id, { state: "error", error: "Codex rejected the callback. Start a new browser login." });
|
|
229
257
|
}
|
|
230
258
|
} catch {
|
|
231
259
|
// A closed callback socket may mean successful login. Polling owns the result.
|
|
232
|
-
if (isCurrentPendingLogin(id, p)) browser.submitted = false;
|
|
260
|
+
if (!timedOut && isCurrentPendingLogin(id, p)) browser.submitted = false;
|
|
233
261
|
} finally {
|
|
234
262
|
clearTimeout(timeout);
|
|
235
263
|
if (p.callbackAbort === controller) p.callbackAbort = undefined;
|
|
@@ -20,6 +20,7 @@ interface PortableCodexAccount {
|
|
|
20
20
|
label: string;
|
|
21
21
|
type: CodexAccountType;
|
|
22
22
|
planType?: string | null;
|
|
23
|
+
dailyGuardEnabled?: boolean;
|
|
23
24
|
authJson?: string | null; // contents of CODEX_HOME/auth.json
|
|
24
25
|
creds?: CodexCreds | null; // apiKey creds (chatgpt auth lives in authJson)
|
|
25
26
|
}
|
|
@@ -35,7 +36,7 @@ export function exportCodexEncrypted(password: string, accountIds?: string[]): s
|
|
|
35
36
|
const authPath = join(a.home, "auth.json");
|
|
36
37
|
if (existsSync(authPath)) authJson = readFileSync(authPath, "utf8");
|
|
37
38
|
} catch { /* ignore unreadable home */ }
|
|
38
|
-
return { id: a.id, label: a.label, type: a.type, planType: a.planType ?? null, authJson, creds: getCodexAccountCreds(a.id) };
|
|
39
|
+
return { id: a.id, label: a.label, type: a.type, planType: a.planType ?? null, dailyGuardEnabled: a.dailyGuardEnabled, authJson, creds: getCodexAccountCreds(a.id) };
|
|
39
40
|
});
|
|
40
41
|
return encryptWithPassword(JSON.stringify({ magic: BACKUP_MAGIC, accounts: portable }), password);
|
|
41
42
|
}
|
|
@@ -56,7 +57,7 @@ export function importCodexEncrypted(blob: string, password: string): { imported
|
|
|
56
57
|
// createCodexAccount inserts the row and mkdirs the CODEX_HOME (0700).
|
|
57
58
|
createCodexAccount({
|
|
58
59
|
id: p.id, label: p.label || "Imported", type: p.type,
|
|
59
|
-
planType: p.planType ?? null, creds: p.creds ?? undefined,
|
|
60
|
+
planType: p.planType ?? null, creds: p.creds ?? undefined, dailyGuardEnabled: p.dailyGuardEnabled !== false,
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
// Restore auth.json so codex re-auths from it on next spawn.
|
|
@@ -16,6 +16,7 @@ import { configService } from "./config.service.ts";
|
|
|
16
16
|
import { encrypt, decrypt } from "../lib/account-crypto.ts";
|
|
17
17
|
import { getOrFetchUsage } from "./provider-usage/usage-registry.ts";
|
|
18
18
|
import { isCodexAccountUsageLimited } from "./codex-account-cooldown.ts";
|
|
19
|
+
import { dailyGuardMessage, dailyGuardState } from "../shared/codex-daily-guard.ts";
|
|
19
20
|
import type { UsageInfo } from "../providers/provider.interface.ts";
|
|
20
21
|
|
|
21
22
|
export type CodexStrategy = "round-robin" | "fill-first" | "lowest-usage";
|
|
@@ -32,6 +33,7 @@ export interface CodexAccount {
|
|
|
32
33
|
home: string;
|
|
33
34
|
planType?: string | null;
|
|
34
35
|
status: CodexAccountStatus;
|
|
36
|
+
dailyGuardEnabled: boolean;
|
|
35
37
|
addedAt: string;
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -44,6 +46,7 @@ interface Row {
|
|
|
44
46
|
id: string; label: string | null; type: string; home: string;
|
|
45
47
|
plan_type: string | null; creds_enc: string | null; added_at: string;
|
|
46
48
|
status?: string | null;
|
|
49
|
+
daily_guard_enabled?: number | null;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
function rowToAccount(r: Row): CodexAccount {
|
|
@@ -56,6 +59,7 @@ function rowToAccount(r: Row): CodexAccount {
|
|
|
56
59
|
// A row written before the column existed reads as null; treat that as enabled, which
|
|
57
60
|
// is what it effectively was.
|
|
58
61
|
status: r.status === "disabled" ? "disabled" : "active",
|
|
62
|
+
dailyGuardEnabled: r.daily_guard_enabled !== 0,
|
|
59
63
|
addedAt: r.added_at,
|
|
60
64
|
};
|
|
61
65
|
}
|
|
@@ -87,7 +91,7 @@ export function getCodexAccountCreds(id: string): CodexCreds | null {
|
|
|
87
91
|
* verified label/planType and the creds to persist (encrypted). Idempotent dir create.
|
|
88
92
|
*/
|
|
89
93
|
export function createCodexAccount(input: {
|
|
90
|
-
label: string; type: CodexAccountType; planType?: string | null; creds?: CodexCreds; id?: string;
|
|
94
|
+
label: string; type: CodexAccountType; planType?: string | null; creds?: CodexCreds; id?: string; dailyGuardEnabled?: boolean;
|
|
91
95
|
}): CodexAccount {
|
|
92
96
|
const id = input.id ?? randomUUID();
|
|
93
97
|
const home = codexAccountHome(id);
|
|
@@ -98,8 +102,8 @@ export function createCodexAccount(input: {
|
|
|
98
102
|
const credsEnc = input.creds ? encrypt(JSON.stringify(input.creds)) : null;
|
|
99
103
|
try {
|
|
100
104
|
getDb().query(
|
|
101
|
-
"INSERT INTO codex_accounts (id, label, type, home, plan_type, creds_enc) VALUES (?, ?, ?, ?, ?, ?)",
|
|
102
|
-
).run(id, input.label, input.type, home, input.planType ?? null, credsEnc);
|
|
105
|
+
"INSERT INTO codex_accounts (id, label, type, home, plan_type, creds_enc, daily_guard_enabled) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
106
|
+
).run(id, input.label, input.type, home, input.planType ?? null, credsEnc, input.dailyGuardEnabled === false ? 0 : 1);
|
|
103
107
|
} catch (e) {
|
|
104
108
|
// Keep the invariant row⟺home: drop the dir if the row didn't land.
|
|
105
109
|
try { rmSync(home, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
@@ -136,6 +140,15 @@ export function setCodexAccountStatus(id: string, status: CodexAccountStatus): C
|
|
|
136
140
|
return getCodexAccount(id);
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
/** Enable or disable the seven-day quota pacing guard for one account. */
|
|
144
|
+
export function setCodexDailyGuard(id: string, enabled: boolean): CodexAccount | null {
|
|
145
|
+
if (!getCodexAccount(id)) return null;
|
|
146
|
+
getDb().query("UPDATE codex_accounts SET daily_guard_enabled = ? WHERE id = ?").run(enabled ? 1 : 0, id);
|
|
147
|
+
return getCodexAccount(id);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class CodexDailyGuardError extends Error {}
|
|
151
|
+
|
|
139
152
|
/** Remove the account row and its CODEX_HOME dir. */
|
|
140
153
|
export function removeCodexAccount(id: string): void {
|
|
141
154
|
const acct = getCodexAccount(id);
|
|
@@ -279,15 +292,43 @@ export async function resolveCodexAccountForSession(sessionId?: string): Promise
|
|
|
279
292
|
// would do nothing for exactly the conversations already using it. An account parked for
|
|
280
293
|
// an exhausted quota lets go for a different reason — it would refuse the turn, and
|
|
281
294
|
// spending one to rediscover that is worse than starting on an account that can answer.
|
|
282
|
-
if (sticky) {
|
|
283
|
-
const a = getCodexAccount(sticky);
|
|
284
|
-
if (a && a.status !== "disabled" && !isCodexAccountUsageLimited(a.id)) return a;
|
|
285
|
-
}
|
|
286
295
|
// Usage is only worth fetching when it can change the answer, and with at most one
|
|
287
296
|
// account able to serve it cannot. Parked accounts count as unable for the same reason
|
|
288
297
|
// they are skipped below, so a pool of two where one is out of quota resolves without a
|
|
289
298
|
// network read at all.
|
|
290
299
|
const usable = listCodexAccounts().filter((a) => a.status !== "disabled" && !isCodexAccountUsageLimited(a.id));
|
|
300
|
+
const guarded = usable.filter((a) => a.dailyGuardEnabled);
|
|
301
|
+
if (guarded.length > 0) {
|
|
302
|
+
const usages = await getAllCodexUsages();
|
|
303
|
+
const blocked = guarded.flatMap((a) => {
|
|
304
|
+
// Pacing is for plans that only expose the weekly bucket. A Plus account's separate
|
|
305
|
+
// five-hour window is already the useful short-term control and must not gain this row.
|
|
306
|
+
const usage = usages[a.id];
|
|
307
|
+
const state = usage?.session == null ? dailyGuardState(usage?.weekly) : null;
|
|
308
|
+
return state?.blocked ? [a.id] : [];
|
|
309
|
+
});
|
|
310
|
+
if (blocked.length > 0) {
|
|
311
|
+
const stickyAccount = sticky ? getCodexAccount(sticky) : null;
|
|
312
|
+
if (stickyAccount && !blocked.includes(stickyAccount.id)
|
|
313
|
+
&& stickyAccount.status !== "disabled" && !isCodexAccountUsageLimited(stickyAccount.id)) return stickyAccount;
|
|
314
|
+
if (blocked.length === usable.length && sessionId) {
|
|
315
|
+
const account = getCodexAccount(blocked[0]!);
|
|
316
|
+
const usage = account ? usages[account.id] : undefined;
|
|
317
|
+
const state = usage?.session == null ? dailyGuardState(usage?.weekly) : null;
|
|
318
|
+
throw new CodexDailyGuardError(state ? dailyGuardMessage(state) : "Daily guard reached for every available Codex account.");
|
|
319
|
+
}
|
|
320
|
+
return selectCodexAccount({ usageOf: (id) => codexUsageLevel(usages[id]), exclude: blocked });
|
|
321
|
+
}
|
|
322
|
+
if (sticky) {
|
|
323
|
+
const a = getCodexAccount(sticky);
|
|
324
|
+
if (a && a.status !== "disabled" && !isCodexAccountUsageLimited(a.id)) return a;
|
|
325
|
+
}
|
|
326
|
+
return selectCodexAccount({ usageOf: (id) => codexUsageLevel(usages[id]) });
|
|
327
|
+
}
|
|
328
|
+
if (sticky) {
|
|
329
|
+
const a = getCodexAccount(sticky);
|
|
330
|
+
if (a && a.status !== "disabled" && !isCodexAccountUsageLimited(a.id)) return a;
|
|
331
|
+
}
|
|
291
332
|
if (usable.length <= 1) return selectCodexAccount();
|
|
292
333
|
// Usage is read for every strategy, not just lowest-usage: it is also what lets the
|
|
293
334
|
// selector skip an account with no five-hour room left. Reading it only for lowest-usage
|
|
@@ -55,3 +55,21 @@ export function buildCodexCallback(callbackUrl: string, login: BrowserLoginCallb
|
|
|
55
55
|
expected.searchParams.set("state", state!);
|
|
56
56
|
return expected;
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Codex accepts the authorization callback, then redirects to its own loopback
|
|
61
|
+
* success endpoint with the exchanged id token. Follow exactly that endpoint
|
|
62
|
+
* while keeping the TCP connection on 127.0.0.1.
|
|
63
|
+
*/
|
|
64
|
+
export function buildCodexSuccessCallback(location: string, login: BrowserLoginCallback): URL {
|
|
65
|
+
let redirect: URL;
|
|
66
|
+
try { redirect = new URL(location); } catch { throw new Error("Codex returned an invalid callback redirect."); }
|
|
67
|
+
const expected = new URL(login.redirectUri);
|
|
68
|
+
if (redirect.protocol !== "http:" || redirect.hostname !== "localhost" || redirect.port !== expected.port
|
|
69
|
+
|| redirect.pathname !== "/success" || redirect.username || redirect.password || redirect.hash
|
|
70
|
+
|| redirect.searchParams.getAll("id_token").length !== 1) {
|
|
71
|
+
throw new Error("Codex returned an unsupported callback redirect.");
|
|
72
|
+
}
|
|
73
|
+
redirect.hostname = "127.0.0.1";
|
|
74
|
+
return redirect;
|
|
75
|
+
}
|
|
@@ -1121,6 +1121,20 @@ export function runMigrations(database: Database): void {
|
|
|
1121
1121
|
try { database.exec(`ALTER TABLE codex_accounts ADD COLUMN status TEXT NOT NULL DEFAULT 'active'`); } catch { /* exists */ }
|
|
1122
1122
|
database.exec(`PRAGMA user_version = 47;`);
|
|
1123
1123
|
}
|
|
1124
|
+
|
|
1125
|
+
if (current < 48) {
|
|
1126
|
+
// Optional per-account pacing for weekly-only Codex plans. Disabled by default so an
|
|
1127
|
+
// upgrade never changes how an existing account is selected or used.
|
|
1128
|
+
try { database.exec(`ALTER TABLE codex_accounts ADD COLUMN daily_guard_enabled INTEGER NOT NULL DEFAULT 0`); } catch { /* exists */ }
|
|
1129
|
+
database.exec(`PRAGMA user_version = 48;`);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if (current < 49) {
|
|
1133
|
+
// Daily guard was introduced as an opt-in during development. Ship it enabled so every
|
|
1134
|
+
// weekly-only account starts paced; Plus accounts simply do not surface or use it.
|
|
1135
|
+
database.exec(`UPDATE codex_accounts SET daily_guard_enabled = 1`);
|
|
1136
|
+
database.exec(`PRAGMA user_version = 49;`);
|
|
1137
|
+
}
|
|
1124
1138
|
}
|
|
1125
1139
|
|
|
1126
1140
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LimitBucket } from "../types/chat.ts";
|
|
2
|
+
|
|
3
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
4
|
+
const WEEK_DAYS = 7;
|
|
5
|
+
|
|
6
|
+
export interface DailyGuardState {
|
|
7
|
+
day: number;
|
|
8
|
+
cap: number;
|
|
9
|
+
used: number;
|
|
10
|
+
blocked: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Split a weekly quota into seven daily envelopes. */
|
|
14
|
+
export function dailyGuardState(bucket: LimitBucket | undefined, now = Date.now()): DailyGuardState | null {
|
|
15
|
+
if (!bucket || bucket.windowHours !== 168) return null;
|
|
16
|
+
const resetsAt = new Date(bucket.resetsAt).getTime();
|
|
17
|
+
if (!Number.isFinite(resetsAt) || resetsAt <= now) return null;
|
|
18
|
+
|
|
19
|
+
const startedAt = resetsAt - WEEK_DAYS * DAY_MS;
|
|
20
|
+
const day = Math.min(WEEK_DAYS, Math.max(1, Math.ceil((now - startedAt) / DAY_MS)));
|
|
21
|
+
const cap = day / WEEK_DAYS;
|
|
22
|
+
return { day, cap, used: bucket.utilization, blocked: bucket.utilization >= cap };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function dailyGuardMessage(state: DailyGuardState): string {
|
|
26
|
+
return `Daily guard reached: ${Math.round(state.used * 100)}% used; day ${state.day}/7 allows ${Math.round(state.cap * 100)}%. Turn off Daily guard on this account to continue.`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Convert Codex's internal subscription enum to the plan name PPM shows people. */
|
|
2
|
+
export function codexPlanLabel(planType: string | null | undefined): string | null {
|
|
3
|
+
if (!planType?.trim()) return null;
|
|
4
|
+
const key = planType.trim().toLowerCase();
|
|
5
|
+
const known: Record<string, string> = {
|
|
6
|
+
free: "ChatGPT Free",
|
|
7
|
+
go: "ChatGPT Go",
|
|
8
|
+
plus: "ChatGPT Plus",
|
|
9
|
+
pro: "ChatGPT Pro",
|
|
10
|
+
team: "ChatGPT Team",
|
|
11
|
+
business: "ChatGPT Business",
|
|
12
|
+
self_serve_business_prolite: "ChatGPT Business",
|
|
13
|
+
enterprise: "ChatGPT Enterprise",
|
|
14
|
+
edu: "ChatGPT Edu",
|
|
15
|
+
};
|
|
16
|
+
return known[key] ?? planType;
|
|
17
|
+
}
|
|
@@ -21,9 +21,11 @@ import { AccountBucketRow } from "@/components/settings/accounts/account-bucket-
|
|
|
21
21
|
import { formatResetTime } from "@/components/settings/accounts/account-usage-format";
|
|
22
22
|
import type { AccountUsageEntry } from "@/lib/api-settings";
|
|
23
23
|
import type { UsageInfo } from "../../../types/chat";
|
|
24
|
+
import { codexPlanLabel } from "../../../shared/codex-plan-label.ts";
|
|
25
|
+
import { dailyGuardState } from "../../../shared/codex-daily-guard.ts";
|
|
24
26
|
import { UsagePanelShell } from "./usage-panel-shell";
|
|
25
27
|
|
|
26
|
-
interface CodexAccount { id: string; label: string; type: string; planType?: string | null; status?: "active" | "disabled" }
|
|
28
|
+
interface CodexAccount { id: string; label: string; type: string; planType?: string | null; status?: "active" | "disabled"; dailyGuardEnabled?: boolean }
|
|
27
29
|
type Usage = Pick<UsageInfo, "fiveHour" | "sevenDay" | "session" | "weekly">;
|
|
28
30
|
|
|
29
31
|
/** Matches the whole-percent figure the bars show, so a refusal agrees with the card. */
|
|
@@ -137,6 +139,20 @@ export function CodexUsagePanel({ onClose, usage, onReload, onSelectAccount, sel
|
|
|
137
139
|
}
|
|
138
140
|
}, []);
|
|
139
141
|
|
|
142
|
+
const handleDailyGuardToggle = useCallback(async (id: string, enabled: boolean) => {
|
|
143
|
+
setTogglingId(id);
|
|
144
|
+
setPanelError(null);
|
|
145
|
+
try {
|
|
146
|
+
await api.patch(`/api/codex-accounts/${id}`, { dailyGuardEnabled: !enabled });
|
|
147
|
+
const d = await api.get<{ accounts: CodexAccount[] }>("/api/codex-accounts");
|
|
148
|
+
setAccounts(d.accounts);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
setPanelError((e as Error).message || "Could not change Daily guard");
|
|
151
|
+
} finally {
|
|
152
|
+
setTogglingId(null);
|
|
153
|
+
}
|
|
154
|
+
}, []);
|
|
155
|
+
|
|
140
156
|
/* No managed accounts → chats run on the ambient ~/.codex login, whose usage arrives on
|
|
141
157
|
the session prop rather than from the accounts endpoint. */
|
|
142
158
|
const ambient = loading && accounts.length === 0 ? (
|
|
@@ -171,6 +187,7 @@ export function CodexUsagePanel({ onClose, usage, onReload, onSelectAccount, sel
|
|
|
171
187
|
>
|
|
172
188
|
{(layout) => accounts.map((a) => {
|
|
173
189
|
const u = usages[a.id] ?? {};
|
|
190
|
+
const guard = u.session == null ? dailyGuardState(u.weekly) : null;
|
|
174
191
|
return (
|
|
175
192
|
<AccountCard
|
|
176
193
|
key={a.id}
|
|
@@ -179,12 +196,15 @@ export function CodexUsagePanel({ onClose, usage, onReload, onSelectAccount, sel
|
|
|
179
196
|
// those differ the moment a session is bound to an account.
|
|
180
197
|
isActive={a.id === (selectedAccountId ?? usage.activeAccountId)}
|
|
181
198
|
layout={layout}
|
|
182
|
-
planLabel={a.planType}
|
|
199
|
+
planLabel={codexPlanLabel(a.planType)}
|
|
183
200
|
onSelect={onSelectAccount ? handleSelect : undefined}
|
|
184
201
|
unselectableReason={unselectableReason(a, u)}
|
|
185
202
|
selecting={selectingId === a.id}
|
|
186
203
|
onToggle={handleToggle}
|
|
187
204
|
toggling={togglingId === a.id}
|
|
205
|
+
dailyGuard={guard ? { enabled: !!a.dailyGuardEnabled, state: guard } : undefined}
|
|
206
|
+
onDailyGuardToggle={guard ? () => void handleDailyGuardToggle(a.id, !!a.dailyGuardEnabled) : undefined}
|
|
207
|
+
dailyGuardToggling={togglingId === a.id}
|
|
188
208
|
/>
|
|
189
209
|
);
|
|
190
210
|
})}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* while every turn on them failed. The sign-in deadline replaced it.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { Download, Eye, KeyRound, RefreshCw, Trash2 } from "lucide-react";
|
|
21
|
+
import { CircleHelp, Download, Eye, KeyRound, RefreshCw, Trash2 } from "lucide-react";
|
|
22
22
|
import { Switch } from "@/components/ui/switch";
|
|
23
23
|
import type { AccountInfo, AccountUsageEntry, OAuthProfileData } from "../../../lib/api-settings";
|
|
24
24
|
import { AccountBucketRow } from "./account-bucket-row";
|
|
@@ -26,6 +26,7 @@ import { AccountHint } from "./account-hint";
|
|
|
26
26
|
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
|
+
import type { DailyGuardState } from "../../../../shared/codex-daily-guard.ts";
|
|
29
30
|
|
|
30
31
|
export interface AccountCardProps {
|
|
31
32
|
entry: AccountUsageEntry;
|
|
@@ -55,6 +56,10 @@ export interface AccountCardProps {
|
|
|
55
56
|
* a plan ("plus", "team") and that decides which quota windows the account even has, so
|
|
56
57
|
* it belongs on the card; Claude reports none and the badge simply does not appear. */
|
|
57
58
|
planLabel?: string | null;
|
|
59
|
+
/** Optional pacing control for Codex accounts that expose only a weekly quota. */
|
|
60
|
+
dailyGuard?: { enabled: boolean; state: DailyGuardState };
|
|
61
|
+
onDailyGuardToggle?: () => void;
|
|
62
|
+
dailyGuardToggling?: boolean;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
// Fixed widths so a row scrolls instead of squeezing. Two of them: a read-only card holds a
|
|
@@ -64,7 +69,7 @@ const STRIP_WIDTH = { readOnly: "min-w-[220px]", withActions: "min-w-[300px]" }
|
|
|
64
69
|
|
|
65
70
|
export function AccountCard({
|
|
66
71
|
entry, isActive, accountInfo, onToggle, toggling, onDelete, onExport, onViewProfile, flash,
|
|
67
|
-
onSelect, unselectableReason, selecting, planLabel,
|
|
72
|
+
onSelect, unselectableReason, selecting, planLabel, dailyGuard, onDailyGuardToggle, dailyGuardToggling,
|
|
68
73
|
layout = "list",
|
|
69
74
|
}: AccountCardProps) {
|
|
70
75
|
const { usage } = entry;
|
|
@@ -259,6 +264,42 @@ export function AccountCard({
|
|
|
259
264
|
</p>
|
|
260
265
|
)}
|
|
261
266
|
|
|
267
|
+
{dailyGuard && (
|
|
268
|
+
<div className={`flex min-h-12 items-center justify-between gap-3 rounded-md border px-2.5 py-1.5 ${
|
|
269
|
+
dailyGuard.enabled && dailyGuard.state.blocked ? "border-error/40 bg-error/5" : "border-border/50 bg-surface/40"
|
|
270
|
+
}`}>
|
|
271
|
+
<div className="min-w-0 space-y-0.5">
|
|
272
|
+
<div className={`flex items-center gap-1.5 text-xs font-medium ${dailyGuard.enabled && dailyGuard.state.blocked ? "text-error" : "text-text-secondary"}`}>
|
|
273
|
+
<span>Daily guard</span>
|
|
274
|
+
<Tooltip>
|
|
275
|
+
<TooltipTrigger asChild>
|
|
276
|
+
<button type="button" aria-label="About Daily guard" className="text-text-subtle hover:text-text-primary cursor-help">
|
|
277
|
+
<CircleHelp className="size-3.5" />
|
|
278
|
+
</button>
|
|
279
|
+
</TooltipTrigger>
|
|
280
|
+
<TooltipContent side="top" className="max-w-64 text-xs leading-relaxed">
|
|
281
|
+
Keeps a weekly-only Codex account on pace to last until its reset. Each day adds one seventh of the weekly quota. When usage reaches that day's cap, PPM pauses new turns until the next day.
|
|
282
|
+
</TooltipContent>
|
|
283
|
+
</Tooltip>
|
|
284
|
+
</div>
|
|
285
|
+
<p className={`text-[10px] tabular-nums ${dailyGuard.enabled && dailyGuard.state.blocked ? "text-error" : "text-text-subtle"}`}>
|
|
286
|
+
{dailyGuard.enabled && dailyGuard.state.blocked
|
|
287
|
+
? `${Math.round(dailyGuard.state.used * 100)}% used / ${Math.round(dailyGuard.state.cap * 100)}% daily cap. New turns paused.`
|
|
288
|
+
: `Day ${dailyGuard.state.day}/7, ${Math.round(dailyGuard.state.cap * 100)}% daily cap`}
|
|
289
|
+
</p>
|
|
290
|
+
</div>
|
|
291
|
+
{onDailyGuardToggle && (
|
|
292
|
+
<Switch
|
|
293
|
+
checked={dailyGuard.enabled}
|
|
294
|
+
onCheckedChange={onDailyGuardToggle}
|
|
295
|
+
disabled={dailyGuardToggling}
|
|
296
|
+
aria-label={dailyGuard.enabled ? "Disable Daily guard" : "Enable Daily guard"}
|
|
297
|
+
className="cursor-pointer shrink-0"
|
|
298
|
+
/>
|
|
299
|
+
)}
|
|
300
|
+
</div>
|
|
301
|
+
)}
|
|
302
|
+
|
|
262
303
|
<div className="flex items-center gap-2 text-[10px] text-text-subtle flex-wrap">
|
|
263
304
|
{usage.lastFetchedAt && (
|
|
264
305
|
<AccountHint
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { useState } from "react";
|
|
10
|
-
import { Download, KeyRound, Loader2, Plus, Settings, Trash2, Upload } from "lucide-react";
|
|
10
|
+
import { CircleHelp, Download, KeyRound, Loader2, Plus, Settings, Trash2, Upload } from "lucide-react";
|
|
11
11
|
import { Button } from "@/components/ui/button";
|
|
12
12
|
import { Switch } from "@/components/ui/switch";
|
|
13
13
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
@@ -17,6 +17,8 @@ import { CodexBackupDialog } from "./codex-backup-dialog";
|
|
|
17
17
|
import { CodexRotationDialog } from "./codex-rotation-dialog";
|
|
18
18
|
import { CodexUsageRows } from "./codex-usage-rows";
|
|
19
19
|
import { useCodexAccounts } from "./use-codex-accounts";
|
|
20
|
+
import { codexPlanLabel } from "../../../../shared/codex-plan-label.ts";
|
|
21
|
+
import { dailyGuardState } from "../../../../shared/codex-daily-guard.ts";
|
|
20
22
|
|
|
21
23
|
/** Codex multi-account management, separate from Claude accounts because codex auth is owned
|
|
22
24
|
* by the app-server per CODEX_HOME. Added by API key, browser login, or ChatGPT device code. */
|
|
@@ -86,7 +88,7 @@ export function CodexAccountsSection() {
|
|
|
86
88
|
<span className="text-[10px] uppercase tracking-wide text-text-subtle border border-border rounded px-1 shrink-0">
|
|
87
89
|
{a.type}
|
|
88
90
|
</span>
|
|
89
|
-
{a.planType && <span className="text-[10px] text-text-subtle shrink-0">{a.planType}</span>}
|
|
91
|
+
{codexPlanLabel(a.planType) && <span className="text-[10px] text-text-subtle shrink-0">{codexPlanLabel(a.planType)}</span>}
|
|
90
92
|
{/* Same control the Claude card uses, in the same place, so the two
|
|
91
93
|
sub-tabs read as one screen rather than two designs. */}
|
|
92
94
|
<Switch
|
|
@@ -111,6 +113,45 @@ export function CodexAccountsSection() {
|
|
|
111
113
|
</Tooltip>
|
|
112
114
|
</div>
|
|
113
115
|
<CodexUsageRows usage={u} />
|
|
116
|
+
{u.session == null && (() => {
|
|
117
|
+
const guard = dailyGuardState(u.weekly);
|
|
118
|
+
return guard ? (
|
|
119
|
+
<div className={`flex min-h-12 items-center justify-between gap-3 rounded-md border px-2.5 py-1.5 ${
|
|
120
|
+
a.dailyGuardEnabled && guard.blocked ? "border-error/40 bg-error/5" : "border-border/50 bg-surface/40"
|
|
121
|
+
}`}>
|
|
122
|
+
<div className="min-w-0 space-y-0.5">
|
|
123
|
+
<div className={`flex items-center gap-1.5 text-xs font-medium ${a.dailyGuardEnabled && guard.blocked ? "text-error" : "text-text-secondary"}`}>
|
|
124
|
+
<span>Daily guard</span>
|
|
125
|
+
<Tooltip>
|
|
126
|
+
<TooltipTrigger asChild>
|
|
127
|
+
<button type="button" aria-label="About Daily guard" className="text-text-subtle hover:text-text-primary cursor-help">
|
|
128
|
+
<CircleHelp className="size-3.5" />
|
|
129
|
+
</button>
|
|
130
|
+
</TooltipTrigger>
|
|
131
|
+
<TooltipContent side="top" className="max-w-64 text-xs leading-relaxed">
|
|
132
|
+
Keeps a weekly-only Codex account on pace to last until its reset. Each day adds one seventh of the weekly quota. When usage reaches that day's cap, PPM pauses new turns until the next day. Turn it off any time to use the remaining quota freely.
|
|
133
|
+
</TooltipContent>
|
|
134
|
+
</Tooltip>
|
|
135
|
+
</div>
|
|
136
|
+
<p className={`text-[10px] tabular-nums ${a.dailyGuardEnabled && guard.blocked ? "text-error" : "text-text-subtle"}`}>
|
|
137
|
+
{a.dailyGuardEnabled && guard.blocked
|
|
138
|
+
? `${Math.round(guard.used * 100)}% used / ${Math.round(guard.cap * 100)}% daily cap. New turns paused.`
|
|
139
|
+
: `Day ${guard.day}/7, ${Math.round(guard.cap * 100)}% daily cap`}
|
|
140
|
+
</p>
|
|
141
|
+
</div>
|
|
142
|
+
<span className="hidden" title={`Day ${guard.day}/7: hold weekly usage at or below ${Math.round(guard.cap * 100)}% to last until reset.`}>
|
|
143
|
+
Daily guard {a.dailyGuardEnabled ? `· Day ${guard.day}/7 cap ${Math.round(guard.cap * 100)}%` : ""}
|
|
144
|
+
</span>
|
|
145
|
+
<Switch
|
|
146
|
+
checked={!!a.dailyGuardEnabled}
|
|
147
|
+
onCheckedChange={() => void c.toggleDailyGuard(a.id, !!a.dailyGuardEnabled)}
|
|
148
|
+
disabled={c.toggling.has(a.id)}
|
|
149
|
+
aria-label={a.dailyGuardEnabled ? "Disable Daily guard" : "Enable Daily guard"}
|
|
150
|
+
className="cursor-pointer shrink-0"
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
) : null;
|
|
154
|
+
})()}
|
|
114
155
|
</AccountCardShell>
|
|
115
156
|
);
|
|
116
157
|
})}
|