@hienlh/ppm 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/CLAUDE.md +1 -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-Cs7BC5O1.js +1 -0
- package/dist/web/assets/accounts-settings-section-Cs7BC5O1.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-Cs7BC5O1.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DoCiQDKY.js → ai-resource-editor-TCREp5Ml.js} +1 -1
- package/dist/web/assets/ai-resource-editor-TCREp5Ml.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-TCREp5Ml.js.gz +0 -0
- package/dist/web/assets/{ai-settings-section-0x6GFJ56.js → ai-settings-section-B7Un4KiW.js} +1 -1
- package/dist/web/assets/ai-settings-section-B7Un4KiW.js.br +0 -0
- package/dist/web/assets/ai-settings-section-B7Un4KiW.js.gz +0 -0
- package/dist/web/assets/{ai-settings-section-Cp6jcnTn.js → ai-settings-section-BlKlcihJ.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-mRiGj3_g.js → appearance-settings-section-DACtiDor.js} +1 -1
- package/dist/web/assets/appearance-settings-section-DACtiDor.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-DACtiDor.js.gz +0 -0
- package/dist/web/assets/{audio-preview-CO7Ia2N5.js → audio-preview-DXtE1Lpz.js} +1 -1
- package/dist/web/assets/audio-preview-DXtE1Lpz.js.br +0 -0
- package/dist/web/assets/audio-preview-DXtE1Lpz.js.gz +0 -0
- package/dist/web/assets/{branch-review-tab-F3CALiEa.js → branch-review-tab-vHJuFXtB.js} +1 -1
- package/dist/web/assets/branch-review-tab-vHJuFXtB.js.br +0 -0
- package/dist/web/assets/branch-review-tab-vHJuFXtB.js.gz +0 -0
- package/dist/web/assets/chat-tab-Ba3NvUgt.js +1 -0
- package/dist/web/assets/chat-tab-Ba3NvUgt.js.br +0 -0
- package/dist/web/assets/chat-tab-Ba3NvUgt.js.gz +0 -0
- package/dist/web/assets/chat-tab-DeISufm0.js +15 -0
- package/dist/web/assets/chat-tab-DeISufm0.js.br +0 -0
- package/dist/web/assets/chat-tab-DeISufm0.js.gz +0 -0
- package/dist/web/assets/{code-editor-FsR6KpZa.js → code-editor-BCwiLVyc.js} +3 -3
- package/dist/web/assets/code-editor-BCwiLVyc.js.br +0 -0
- package/dist/web/assets/code-editor-BCwiLVyc.js.gz +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-DcIOYhAe.js +1 -0
- package/dist/web/assets/codex-sign-in-again-dialog-DcIOYhAe.js.br +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-DcIOYhAe.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-GGqIz6tt.js → conflict-editor-0YEDQ--c.js} +1 -1
- package/dist/web/assets/conflict-editor-0YEDQ--c.js.br +0 -0
- package/dist/web/assets/conflict-editor-0YEDQ--c.js.gz +0 -0
- package/dist/web/assets/{context-menu-CVO3HtIt.js → context-menu-CjYTq8DI.js} +1 -1
- package/dist/web/assets/context-menu-CjYTq8DI.js.br +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js.gz +0 -0
- package/dist/web/assets/{csv-preview-ycnxJ_6J.js → csv-preview-CYxznB1I.js} +1 -1
- package/dist/web/assets/csv-preview-CYxznB1I.js.br +0 -0
- package/dist/web/assets/csv-preview-CYxznB1I.js.gz +0 -0
- package/dist/web/assets/{database-viewer-y6URJv8N.js → database-viewer-eP3gCxGb.js} +1 -1
- package/dist/web/assets/database-viewer-eP3gCxGb.js.br +0 -0
- package/dist/web/assets/database-viewer-eP3gCxGb.js.gz +0 -0
- package/dist/web/assets/{design-responsive-dialog-D0QYfX7v.js → design-responsive-dialog-CcJETHO2.js} +1 -1
- package/dist/web/assets/design-responsive-dialog-CcJETHO2.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-CcJETHO2.js.gz +0 -0
- package/dist/web/assets/{design-tab-DgCTcwot.js → design-tab-B7aVMMfj.js} +1 -1
- package/dist/web/assets/design-tab-B7aVMMfj.js.br +0 -0
- package/dist/web/assets/design-tab-B7aVMMfj.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-Dtuph5jh.js → designs-sidebar-panel-8KI79o1u.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-8KI79o1u.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-8KI79o1u.js.gz +0 -0
- package/dist/web/assets/{dialog-CaShS4f5.js → dialog-tzBRkGeW.js} +1 -1
- package/dist/web/assets/dialog-tzBRkGeW.js.br +0 -0
- package/dist/web/assets/dialog-tzBRkGeW.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-ciBkExM7.js → diff-viewer-B_K7VQup.js} +1 -1
- package/dist/web/assets/diff-viewer-B_K7VQup.js.br +0 -0
- package/dist/web/assets/diff-viewer-B_K7VQup.js.gz +0 -0
- package/dist/web/assets/{docx-preview-C8K6QzOW.js → docx-preview-C-UKGMXR.js} +1 -1
- package/dist/web/assets/docx-preview-C-UKGMXR.js.br +0 -0
- package/dist/web/assets/docx-preview-C-UKGMXR.js.gz +0 -0
- package/dist/web/assets/{editor-language-service-CkoaAHjF.js → editor-language-service-DYBxZX66.js} +1 -1
- package/dist/web/assets/editor-language-service-DYBxZX66.js.br +0 -0
- package/dist/web/assets/editor-language-service-DYBxZX66.js.gz +0 -0
- package/dist/web/assets/{explorer-body-C5gXPGOl.js → explorer-body-C0syNltJ.js} +1 -1
- package/dist/web/assets/explorer-body-C0syNltJ.js.br +0 -0
- package/dist/web/assets/explorer-body-C0syNltJ.js.gz +0 -0
- package/dist/web/assets/{explorer-window-content-8EBl9Vkx.js → explorer-window-content-CfIomwTq.js} +1 -1
- package/dist/web/assets/explorer-window-content-CfIomwTq.js.br +0 -0
- package/dist/web/assets/explorer-window-content-CfIomwTq.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-DOwZYx8O.js → extension-manager-section-CL7pl3re.js} +1 -1
- package/dist/web/assets/extension-manager-section-CL7pl3re.js.br +0 -0
- package/dist/web/assets/extension-manager-section-CL7pl3re.js.gz +0 -0
- package/dist/web/assets/{extension-webview-CdEva8pD.js → extension-webview-FHTB0jh1.js} +1 -1
- package/dist/web/assets/extension-webview-FHTB0jh1.js.br +0 -0
- package/dist/web/assets/extension-webview-FHTB0jh1.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-DVemQIZ2.js → files-settings-section-C574RfCl.js} +1 -1
- package/dist/web/assets/files-settings-section-C574RfCl.js.br +0 -0
- package/dist/web/assets/files-settings-section-C574RfCl.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-DiY_5C93.js → general-settings-section--DSUTrEP.js} +1 -1
- package/dist/web/assets/general-settings-section--DSUTrEP.js.br +0 -0
- package/dist/web/assets/general-settings-section--DSUTrEP.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-Cq0WohOO.js → git-log-panel-BzwEbRwN.js} +1 -1
- package/dist/web/assets/git-log-panel-BzwEbRwN.js.br +0 -0
- package/dist/web/assets/git-log-panel-BzwEbRwN.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-DG9AnCmP.js → glide-data-grid-CUNWBFfN.js} +1 -1
- package/dist/web/assets/glide-data-grid-CUNWBFfN.js.br +0 -0
- package/dist/web/assets/glide-data-grid-CUNWBFfN.js.gz +0 -0
- package/dist/web/assets/{group-chat-tab-DxbDrFJl.js → group-chat-tab-Dp1p4Jdu.js} +1 -1
- package/dist/web/assets/group-chat-tab-Dp1p4Jdu.js.br +0 -0
- package/dist/web/assets/group-chat-tab-Dp1p4Jdu.js.gz +0 -0
- package/dist/web/assets/icons-CO_II6D-.js +1 -0
- package/dist/web/assets/icons-CO_II6D-.js.br +0 -0
- package/dist/web/assets/icons-CO_II6D-.js.gz +0 -0
- package/dist/web/assets/{image-preview-eAv91-an.js → image-preview-78Fguk1f.js} +1 -1
- package/dist/web/assets/{index-BVKT5UCu.js → index-CvUGUp7d.js} +6 -6
- package/dist/web/assets/index-CvUGUp7d.js.br +0 -0
- package/dist/web/assets/index-CvUGUp7d.js.gz +0 -0
- package/dist/web/assets/index-DMA-So69.css +1 -0
- package/dist/web/assets/index-DMA-So69.css.br +0 -0
- package/dist/web/assets/index-DMA-So69.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-Cr8gSVGY.js → jira-watcher-section-DoXl0GAq.js} +1 -1
- package/dist/web/assets/jira-watcher-section-DoXl0GAq.js.br +0 -0
- package/dist/web/assets/jira-watcher-section-DoXl0GAq.js.gz +0 -0
- package/dist/web/assets/{keybindings-store-Ch1mWz0q.js → keybindings-store-BqGm9AgI.js} +1 -1
- package/dist/web/assets/{keyboard-shortcuts-section-BvC6m_C2.js → keyboard-shortcuts-section-BogwnAdr.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-BogwnAdr.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BogwnAdr.js.gz +0 -0
- package/dist/web/assets/{markdown-renderer-56wMzGkr.js → markdown-renderer-C33dsNJ_.js} +1 -1
- package/dist/web/assets/{markdown-renderer-8JlfrfnD.js → markdown-renderer-CZiECUJp.js} +1 -1
- package/dist/web/assets/markdown-renderer-CZiECUJp.js.br +0 -0
- package/dist/web/assets/markdown-renderer-CZiECUJp.js.gz +0 -0
- package/dist/web/assets/{mobile-explorer-sheet-cJfocgE7.js → mobile-explorer-sheet-BOgo9tbK.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BOgo9tbK.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-BOgo9tbK.js.gz +0 -0
- package/dist/web/assets/{new-design-dialog-BO_D3nex.js → new-design-dialog-DcoejI1s.js} +1 -1
- package/dist/web/assets/new-design-dialog-DcoejI1s.js.br +0 -0
- package/dist/web/assets/new-design-dialog-DcoejI1s.js.gz +0 -0
- package/dist/web/assets/{notification-store-DNWazI7j.js → notification-store-D88Y9kF_.js} +1 -1
- package/dist/web/assets/{notifications-settings-section-DaMh3khs.js → notifications-settings-section-Bfxqz65i.js} +1 -1
- package/dist/web/assets/notifications-settings-section-Bfxqz65i.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-Bfxqz65i.js.gz +0 -0
- package/dist/web/assets/{onboarding-entry-CwkNJ69U.js → onboarding-entry-CcvySZ_l.js} +1 -1
- package/dist/web/assets/onboarding-entry-CcvySZ_l.js.br +0 -0
- package/dist/web/assets/onboarding-entry-CcvySZ_l.js.gz +0 -0
- package/dist/web/assets/{onboarding-quick-orientation-u7yM68d3.js → onboarding-quick-orientation-CT4PylG0.js} +1 -1
- package/dist/web/assets/onboarding-quick-orientation-CT4PylG0.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-CT4PylG0.js.gz +0 -0
- package/dist/web/assets/{pdf-preview-CaxTidh0.js → pdf-preview-BWim6mu1.js} +1 -1
- package/dist/web/assets/pdf-preview-BWim6mu1.js.br +0 -0
- package/dist/web/assets/pdf-preview-BWim6mu1.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-CJOOLdgO.js → postgres-viewer-HwlL1UTJ.js} +1 -1
- package/dist/web/assets/postgres-viewer-HwlL1UTJ.js.br +0 -0
- package/dist/web/assets/postgres-viewer-HwlL1UTJ.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-rNBdWG7F.js → ppmbot-settings-section-DF0kqdyO.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-DF0kqdyO.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-DF0kqdyO.js.gz +0 -0
- package/dist/web/assets/{problems-panel-BTmOGMcL.js → problems-panel-DRcq5ChN.js} +1 -1
- package/dist/web/assets/problems-panel-DRcq5ChN.js.br +0 -0
- package/dist/web/assets/problems-panel-DRcq5ChN.js.gz +0 -0
- package/dist/web/assets/{process-table-Bc9WEBsS.js → process-table-Ds0S_YK-.js} +1 -1
- package/dist/web/assets/process-table-Ds0S_YK-.js.br +0 -0
- package/dist/web/assets/process-table-Ds0S_YK-.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-Bo-FPk6Y.js → proxy-settings-section-Q-ttqVOa.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Q-ttqVOa.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Q-ttqVOa.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-CQfzKAf9.js → query-audit-section-Cn11xqgz.js} +1 -1
- package/dist/web/assets/query-audit-section-Cn11xqgz.js.br +0 -0
- package/dist/web/assets/query-audit-section-Cn11xqgz.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-clipboard-notice-DgeuoZ4n.js → remote-desktop-clipboard-notice-CvPPtrRq.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-CvPPtrRq.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CvPPtrRq.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-mobile-sheet-BpP7jI0p.js → remote-desktop-mobile-sheet-B-TT9Fdt.js} +2 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-B-TT9Fdt.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-B-TT9Fdt.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-mobile-view-Byn0lFCF.js → remote-desktop-mobile-view-hUnYxXDk.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-view-hUnYxXDk.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-hUnYxXDk.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-B2mXZ9vo.js → remote-desktop-readiness-gate-CUmaeiX9.js} +1 -1
- package/dist/web/assets/remote-desktop-readiness-gate-CUmaeiX9.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CUmaeiX9.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-window-content-CWt3VVjm.js → remote-desktop-window-content-DEPUOOGw.js} +1 -1
- package/dist/web/assets/remote-desktop-window-content-DEPUOOGw.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-DEPUOOGw.js.gz +0 -0
- package/dist/web/assets/{schedules-settings-section-Bx9Rm-NB.js → schedules-settings-section-Bv_wrzDp.js} +1 -1
- package/dist/web/assets/schedules-settings-section-Bv_wrzDp.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-Bv_wrzDp.js.gz +0 -0
- package/dist/web/assets/{settings-body-DU3LZkOy.js → settings-body-C5_chEpq.js} +2 -2
- package/dist/web/assets/settings-body-C5_chEpq.js.br +0 -0
- package/dist/web/assets/settings-body-C5_chEpq.js.gz +0 -0
- package/dist/web/assets/{settings-tab-Q5Ai_XB7.js → settings-tab-CogpEbq5.js} +1 -1
- package/dist/web/assets/{settings-window-content-DNg-jjm4.js → settings-window-content-BNRirQDZ.js} +1 -1
- package/dist/web/assets/{sql-query-editor-BMNsmnfi.js → sql-query-editor-Bg3lOrmt.js} +1 -1
- package/dist/web/assets/sql-query-editor-Bg3lOrmt.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Bg3lOrmt.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-DBdxQgCD.js → sqlite-viewer-DMKtDkI6.js} +1 -1
- package/dist/web/assets/sqlite-viewer-DMKtDkI6.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-DMKtDkI6.js.gz +0 -0
- package/dist/web/assets/{system-monitor-body-BEuqhoiF.js → system-monitor-body-Dst39LA7.js} +2 -2
- package/dist/web/assets/system-monitor-body-Dst39LA7.js.br +0 -0
- package/dist/web/assets/system-monitor-body-Dst39LA7.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-CEwME1_C.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-Dr6EgqpJ.js → system-monitor-window-content-DQRBjn9z.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-BhS39QWy.js → tab-host-window-content-Dlunzrfh.js} +1 -1
- package/dist/web/assets/tab-host-window-content-Dlunzrfh.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-Dlunzrfh.js.gz +0 -0
- package/dist/web/assets/{team-member-sheet-C5-PeHDb.js → team-member-sheet-CPZGU9j5.js} +2 -2
- package/dist/web/assets/team-member-sheet-CPZGU9j5.js.br +0 -0
- package/dist/web/assets/team-member-sheet-CPZGU9j5.js.gz +0 -0
- package/dist/web/assets/{team-member-window-content-CZjHuvdw.js → team-member-window-content-CwI9JD_H.js} +1 -1
- package/dist/web/assets/{team-member-window-content-CZjHuvdw.js.br → team-member-window-content-CwI9JD_H.js.br} +0 -0
- package/dist/web/assets/team-member-window-content-CwI9JD_H.js.gz +0 -0
- package/dist/web/assets/{terminal-tab-B0w7Jsbs.js → terminal-tab-BcQXleSa.js} +1 -1
- package/dist/web/assets/terminal-tab-BcQXleSa.js.br +0 -0
- package/dist/web/assets/terminal-tab-BcQXleSa.js.gz +0 -0
- package/dist/web/assets/{tool-cards-CmdjXi2c.js → tool-cards-DsQdMorI.js} +2 -2
- package/dist/web/assets/tool-cards-DsQdMorI.js.br +0 -0
- package/dist/web/assets/tool-cards-DsQdMorI.js.gz +0 -0
- package/dist/web/assets/{use-inline-blame-BSb807l9.js → use-inline-blame--fS-pzzX.js} +1 -1
- package/dist/web/assets/use-inline-blame--fS-pzzX.js.br +0 -0
- package/dist/web/assets/use-inline-blame--fS-pzzX.js.gz +0 -0
- package/dist/web/assets/{use-lsp-DhBjkGWr.js → use-lsp-DK26Eter.js} +1 -1
- package/dist/web/assets/use-lsp-DK26Eter.js.br +0 -0
- package/dist/web/assets/use-lsp-DK26Eter.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-CNEk66US.js → use-monaco-theme-BVatH9yZ.js} +1 -1
- package/dist/web/assets/{use-websocket-Bki-B4fj.js → use-websocket-C1mloX0R.js} +1 -1
- package/dist/web/assets/{video-preview-T1SMFp03.js → video-preview-C48Go7JZ.js} +1 -1
- package/dist/web/assets/video-preview-C48Go7JZ.js.br +0 -0
- package/dist/web/assets/video-preview-C48Go7JZ.js.gz +0 -0
- package/dist/web/index.html +6 -6
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/sw.js +1 -1
- package/dist/web/sw.js.br +0 -0
- package/dist/web/sw.js.gz +0 -0
- package/package.json +1 -1
- package/src/providers/codex-app-server/codex-auth-failure.ts +51 -0
- package/src/providers/codex-app-server/codex-event-mapper.ts +8 -0
- package/src/providers/codex-app-server/codex-provider.ts +97 -19
- package/src/providers/codex-app-server/codex-usage-source.ts +14 -1
- package/src/server/routes/codex-accounts.ts +18 -6
- package/src/services/codex-account-auth-state.ts +58 -0
- package/src/services/codex-account-login.ts +42 -9
- package/src/services/codex-account.service.ts +42 -7
- package/src/services/proxy-agent-anthropic-bridge.ts +2 -2
- package/src/services/proxy-agent-bridge.ts +2 -2
- package/src/services/proxy-agent-turn.ts +80 -20
- package/src/services/proxy-image-bridge.ts +3 -3
- package/src/services/proxy.service.ts +1 -1
- package/src/web/components/chat/codex-usage-panel.tsx +13 -1
- package/src/web/components/chat/usage-badge.tsx +14 -0
- package/src/web/components/settings/accounts/account-add-dialog.tsx +10 -3
- package/src/web/components/settings/accounts/account-card.tsx +15 -4
- package/src/web/components/settings/accounts/account-hint.tsx +19 -2
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +10 -2
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +18 -2
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +50 -33
- package/src/web/components/settings/accounts/codex-sign-in-again-dialog.tsx +47 -0
- package/src/web/components/settings/accounts/use-codex-accounts.ts +18 -4
- package/src/web/lib/fluent-icon.tsx +38 -2
- package/dist/web/assets/accounts-settings-section-CVJ7hQom.js +0 -1
- package/dist/web/assets/accounts-settings-section-CVJ7hQom.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-CVJ7hQom.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DoCiQDKY.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DoCiQDKY.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-0x6GFJ56.js.br +0 -0
- package/dist/web/assets/ai-settings-section-0x6GFJ56.js.gz +0 -0
- package/dist/web/assets/appearance-settings-section-mRiGj3_g.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-mRiGj3_g.js.gz +0 -0
- package/dist/web/assets/audio-preview-CO7Ia2N5.js.br +0 -0
- package/dist/web/assets/audio-preview-CO7Ia2N5.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-F3CALiEa.js.br +0 -0
- package/dist/web/assets/branch-review-tab-F3CALiEa.js.gz +0 -0
- package/dist/web/assets/chat-tab-D6BFnkCj.js +0 -1
- package/dist/web/assets/chat-tab-D6BFnkCj.js.br +0 -0
- package/dist/web/assets/chat-tab-D6BFnkCj.js.gz +0 -0
- package/dist/web/assets/chat-tab-IXHMUKMW.js +0 -15
- package/dist/web/assets/chat-tab-IXHMUKMW.js.br +0 -0
- package/dist/web/assets/chat-tab-IXHMUKMW.js.gz +0 -0
- package/dist/web/assets/code-editor-FsR6KpZa.js.br +0 -0
- package/dist/web/assets/code-editor-FsR6KpZa.js.gz +0 -0
- package/dist/web/assets/codex-daily-guard-Bn8jfcei.js +0 -1
- package/dist/web/assets/codex-daily-guard-Bn8jfcei.js.br +0 -0
- package/dist/web/assets/codex-daily-guard-Bn8jfcei.js.gz +0 -0
- package/dist/web/assets/conflict-editor-GGqIz6tt.js.br +0 -0
- package/dist/web/assets/conflict-editor-GGqIz6tt.js.gz +0 -0
- package/dist/web/assets/context-menu-CVO3HtIt.js.br +0 -0
- package/dist/web/assets/context-menu-CVO3HtIt.js.gz +0 -0
- package/dist/web/assets/csv-preview-ycnxJ_6J.js.br +0 -0
- package/dist/web/assets/csv-preview-ycnxJ_6J.js.gz +0 -0
- package/dist/web/assets/database-viewer-y6URJv8N.js.br +0 -0
- package/dist/web/assets/database-viewer-y6URJv8N.js.gz +0 -0
- package/dist/web/assets/design-responsive-dialog-D0QYfX7v.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-D0QYfX7v.js.gz +0 -0
- package/dist/web/assets/design-tab-DgCTcwot.js.br +0 -0
- package/dist/web/assets/design-tab-DgCTcwot.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-Dtuph5jh.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-Dtuph5jh.js.gz +0 -0
- package/dist/web/assets/dialog-CaShS4f5.js.br +0 -0
- package/dist/web/assets/dialog-CaShS4f5.js.gz +0 -0
- package/dist/web/assets/diff-viewer-ciBkExM7.js.br +0 -0
- package/dist/web/assets/diff-viewer-ciBkExM7.js.gz +0 -0
- package/dist/web/assets/docx-preview-C8K6QzOW.js.br +0 -0
- package/dist/web/assets/docx-preview-C8K6QzOW.js.gz +0 -0
- package/dist/web/assets/editor-language-service-CkoaAHjF.js.br +0 -0
- package/dist/web/assets/editor-language-service-CkoaAHjF.js.gz +0 -0
- package/dist/web/assets/explorer-body-C5gXPGOl.js.br +0 -0
- package/dist/web/assets/explorer-body-C5gXPGOl.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-8EBl9Vkx.js.br +0 -0
- package/dist/web/assets/explorer-window-content-8EBl9Vkx.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-DOwZYx8O.js.br +0 -0
- package/dist/web/assets/extension-manager-section-DOwZYx8O.js.gz +0 -0
- package/dist/web/assets/extension-webview-CdEva8pD.js.br +0 -0
- package/dist/web/assets/extension-webview-CdEva8pD.js.gz +0 -0
- package/dist/web/assets/files-settings-section-DVemQIZ2.js.br +0 -0
- package/dist/web/assets/files-settings-section-DVemQIZ2.js.gz +0 -0
- package/dist/web/assets/general-settings-section-DiY_5C93.js.br +0 -0
- package/dist/web/assets/general-settings-section-DiY_5C93.js.gz +0 -0
- package/dist/web/assets/git-log-panel-Cq0WohOO.js.br +0 -0
- package/dist/web/assets/git-log-panel-Cq0WohOO.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-DG9AnCmP.js.br +0 -0
- package/dist/web/assets/glide-data-grid-DG9AnCmP.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DxbDrFJl.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DxbDrFJl.js.gz +0 -0
- package/dist/web/assets/icons-C9ZwXgel.js +0 -1
- package/dist/web/assets/icons-C9ZwXgel.js.br +0 -0
- package/dist/web/assets/icons-C9ZwXgel.js.gz +0 -0
- package/dist/web/assets/index-BVKT5UCu.js.br +0 -0
- package/dist/web/assets/index-BVKT5UCu.js.gz +0 -0
- package/dist/web/assets/index-CzYrKDUF.css +0 -1
- package/dist/web/assets/index-CzYrKDUF.css.br +0 -0
- package/dist/web/assets/index-CzYrKDUF.css.gz +0 -0
- package/dist/web/assets/jira-watcher-section-Cr8gSVGY.js.br +0 -0
- package/dist/web/assets/jira-watcher-section-Cr8gSVGY.js.gz +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BvC6m_C2.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BvC6m_C2.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-8JlfrfnD.js.br +0 -0
- package/dist/web/assets/markdown-renderer-8JlfrfnD.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-cJfocgE7.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-cJfocgE7.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-BO_D3nex.js.br +0 -0
- package/dist/web/assets/new-design-dialog-BO_D3nex.js.gz +0 -0
- package/dist/web/assets/notifications-settings-section-DaMh3khs.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-DaMh3khs.js.gz +0 -0
- package/dist/web/assets/onboarding-entry-CwkNJ69U.js.br +0 -0
- package/dist/web/assets/onboarding-entry-CwkNJ69U.js.gz +0 -0
- package/dist/web/assets/onboarding-quick-orientation-u7yM68d3.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-u7yM68d3.js.gz +0 -0
- package/dist/web/assets/pdf-preview-CaxTidh0.js.br +0 -0
- package/dist/web/assets/pdf-preview-CaxTidh0.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-CJOOLdgO.js.br +0 -0
- package/dist/web/assets/postgres-viewer-CJOOLdgO.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-rNBdWG7F.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-rNBdWG7F.js.gz +0 -0
- package/dist/web/assets/problems-panel-BTmOGMcL.js.br +0 -0
- package/dist/web/assets/problems-panel-BTmOGMcL.js.gz +0 -0
- package/dist/web/assets/process-table-Bc9WEBsS.js.br +0 -0
- package/dist/web/assets/process-table-Bc9WEBsS.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-Bo-FPk6Y.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Bo-FPk6Y.js.gz +0 -0
- package/dist/web/assets/query-audit-section-CQfzKAf9.js.br +0 -0
- package/dist/web/assets/query-audit-section-CQfzKAf9.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DgeuoZ4n.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DgeuoZ4n.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BpP7jI0p.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BpP7jI0p.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-Byn0lFCF.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-Byn0lFCF.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-B2mXZ9vo.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-B2mXZ9vo.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-CWt3VVjm.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-CWt3VVjm.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-Bx9Rm-NB.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-Bx9Rm-NB.js.gz +0 -0
- package/dist/web/assets/settings-body-DU3LZkOy.js.br +0 -0
- package/dist/web/assets/settings-body-DU3LZkOy.js.gz +0 -0
- package/dist/web/assets/sql-query-editor-BMNsmnfi.js.br +0 -0
- package/dist/web/assets/sql-query-editor-BMNsmnfi.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-DBdxQgCD.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-DBdxQgCD.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-BEuqhoiF.js.br +0 -0
- package/dist/web/assets/system-monitor-body-BEuqhoiF.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-BAU1kzbY.js +0 -1
- package/dist/web/assets/tab-host-window-content-BhS39QWy.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-BhS39QWy.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-C5-PeHDb.js.br +0 -0
- package/dist/web/assets/team-member-sheet-C5-PeHDb.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-CZjHuvdw.js.gz +0 -0
- package/dist/web/assets/terminal-tab-B0w7Jsbs.js.br +0 -0
- package/dist/web/assets/terminal-tab-B0w7Jsbs.js.gz +0 -0
- package/dist/web/assets/tool-cards-CmdjXi2c.js.br +0 -0
- package/dist/web/assets/tool-cards-CmdjXi2c.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-BSb807l9.js.br +0 -0
- package/dist/web/assets/use-inline-blame-BSb807l9.js.gz +0 -0
- package/dist/web/assets/use-lsp-DhBjkGWr.js.br +0 -0
- package/dist/web/assets/use-lsp-DhBjkGWr.js.gz +0 -0
- package/dist/web/assets/video-preview-T1SMFp03.js.br +0 -0
- package/dist/web/assets/video-preview-T1SMFp03.js.gz +0 -0
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}var B=`-precache-`,V=async(e,t=B)=>{let n=(await self.caches.keys()).filter(n=>n.includes(t)&&n.includes(self.registration.scope)&&n!==e);return await Promise.all(n.map(e=>self.caches.delete(e))),n};function H(){self.addEventListener(`activate`,(e=>{let t=a.getPrecacheName();e.waitUntil(V(t).then(e=>{}))}))}function U(e){O().precache(e)}function W(e,t){U(e),z(t)}var G=class extends w{async _handle(e,n){let r=await n.cacheMatch(e),i;if(!r)try{r=await n.fetchAndCachePut(e)}catch(e){e instanceof Error&&(i=e)}if(!r)throw new t(`no-response`,{url:e.url,error:i});return r}};W([{"revision":"42b8694150f4feff73ca093c4abcfb3e","url":"index.html"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":null,"url":"assets/index-CzYrKDUF.css"},{"revision":null,"url":"assets/index-BVKT5UCu.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),H();var K=e=>(e.headers.get(`content-type`)??``).startsWith(`text/html`);F(({url:e})=>e.pathname.startsWith(`/assets/`),new G({cacheName:`ppm-assets`,plugins:[{cacheWillUpdate:async({response:e})=>K(e)?null:e,cachedResponseWillBeUsed:async({cachedResponse:e})=>e&&K(e)?null:e}]})),self.skipWaiting(),self.addEventListener(`activate`,()=>{self.clients.claim()});
|
|
1
|
+
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}var B=`-precache-`,V=async(e,t=B)=>{let n=(await self.caches.keys()).filter(n=>n.includes(t)&&n.includes(self.registration.scope)&&n!==e);return await Promise.all(n.map(e=>self.caches.delete(e))),n};function H(){self.addEventListener(`activate`,(e=>{let t=a.getPrecacheName();e.waitUntil(V(t).then(e=>{}))}))}function U(e){O().precache(e)}function W(e,t){U(e),z(t)}var G=class extends w{async _handle(e,n){let r=await n.cacheMatch(e),i;if(!r)try{r=await n.fetchAndCachePut(e)}catch(e){e instanceof Error&&(i=e)}if(!r)throw new t(`no-response`,{url:e.url,error:i});return r}};W([{"revision":"e44b0ffb84bec4e318fe35cd8c1f1ddc","url":"index.html"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":null,"url":"assets/index-DMA-So69.css"},{"revision":null,"url":"assets/index-CvUGUp7d.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),H();var K=e=>(e.headers.get(`content-type`)??``).startsWith(`text/html`);F(({url:e})=>e.pathname.startsWith(`/assets/`),new G({cacheName:`ppm-assets`,plugins:[{cacheWillUpdate:async({response:e})=>K(e)?null:e,cachedResponseWillBeUsed:async({cachedResponse:e})=>e&&K(e)?null:e}]})),self.skipWaiting(),self.addEventListener(`activate`,()=>{self.clients.claim()});
|
package/dist/web/sw.js.br
CHANGED
|
Binary file
|
package/dist/web/sw.js.gz
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognising a Codex account that is no longer signed in.
|
|
3
|
+
*
|
|
4
|
+
* A revoked or expired ChatGPT login does not stop the app-server from starting or
|
|
5
|
+
* from opening a thread — both succeed. It is the model request that fails, and codex
|
|
6
|
+
* then spends ~24s in its own reconnect loop ("Reconnecting... 1/5" twice over) before
|
|
7
|
+
* it gives up. Each of those notices carries the real cause in `additionalDetails`
|
|
8
|
+
* (`workspace routing discovery unauthorized (401)`), so the first one is already
|
|
9
|
+
* enough to know that waiting will not help.
|
|
10
|
+
*
|
|
11
|
+
* The same account's quota read fails with `401 Unauthorized ... "code": "token_revoked"`,
|
|
12
|
+
* which is the other place this is recognised.
|
|
13
|
+
*
|
|
14
|
+
* Narrow on purpose, like the usage-limit patterns: a false positive takes a working
|
|
15
|
+
* account out of rotation. A bare "401" is not enough — it has to come with the word
|
|
16
|
+
* the status means, or with codex's own sign-in wording.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const AUTH_FAILURE_PATTERNS = [
|
|
20
|
+
/\b401\b[^\n]{0,40}unauthori[sz]ed/i,
|
|
21
|
+
/unauthori[sz]ed[^\n]{0,40}\b401\b/i,
|
|
22
|
+
/\btoken_revoked\b/i,
|
|
23
|
+
/invalidated\s+oauth\s+token/i,
|
|
24
|
+
/refresh\s+token\s+(?:was|has\s+been)\s+(?:revoked|invalidated|already\s+used)/i,
|
|
25
|
+
/(?:log|sign)\s+(?:out\s+and\s+sign\s+)?in\s+again/i,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/** Whether this error text means the account has to be signed in again. */
|
|
29
|
+
export function isCodexAuthFailure(message: string): boolean {
|
|
30
|
+
return AUTH_FAILURE_PATTERNS.some((re) => re.test(message));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Everything a codex `error` notification says about its cause, as one string.
|
|
35
|
+
*
|
|
36
|
+
* The headline of a retry notice is just "Reconnecting... 2/5"; the reason sits in
|
|
37
|
+
* `additionalDetails`, so matching the message alone would never see it.
|
|
38
|
+
*/
|
|
39
|
+
export function codexErrorText(params: unknown): string {
|
|
40
|
+
const p = (params && typeof params === "object" ? params : {}) as Record<string, unknown>;
|
|
41
|
+
const err = (p.error && typeof p.error === "object" ? p.error : {}) as Record<string, unknown>;
|
|
42
|
+
return [err.message, err.additionalDetails, p.message]
|
|
43
|
+
.filter((s): s is string => typeof s === "string" && s.length > 0)
|
|
44
|
+
.join(" — ");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The message shown when a turn cannot run because its account is signed out. */
|
|
48
|
+
export function codexSignedOutMessage(accountLabel?: string | null): string {
|
|
49
|
+
const who = accountLabel ? `Codex account ${accountLabel}` : "The Codex account";
|
|
50
|
+
return `${who} is signed out (its login was revoked or expired). Press "Sign in again" on its card in Settings → Accounts → Codex.`;
|
|
51
|
+
}
|
|
@@ -270,6 +270,14 @@ export function mapCodexEvent(notif: Notif, sessionId: string): ChatEvent[] {
|
|
|
270
270
|
const message = typeof err.message === "string" ? err.message
|
|
271
271
|
: typeof p.message === "string" ? p.message
|
|
272
272
|
: "codex error";
|
|
273
|
+
// `willRetry: true` is codex narrating its own retry loop ("Reconnecting...
|
|
274
|
+
// 2/5"), not a failure: the turn is still running and ends with either an
|
|
275
|
+
// answer or a final `willRetry: false` error. Reporting each one as an error
|
|
276
|
+
// put a card per attempt in the chat and made the proxy give up on a turn
|
|
277
|
+
// at the first reconnect.
|
|
278
|
+
if (p.willRetry === true) {
|
|
279
|
+
return [{ type: "status_update", phase: "retrying", message: redactTruncate(message, 256) }];
|
|
280
|
+
}
|
|
273
281
|
return [{ type: "error", message: redactTruncate(message, 1024) }];
|
|
274
282
|
}
|
|
275
283
|
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
import { dailyGuardMessage, dailyGuardState } from "../../shared/codex-daily-guard.ts";
|
|
27
27
|
import { isCodexAccountUsageLimited, markCodexAccountUsageLimited } from "../../services/codex-account-cooldown.ts";
|
|
28
28
|
import { isCodexUsageLimit, codexErrorMessage, parseCodexUsageLimitReset } from "./codex-usage-limit.ts";
|
|
29
|
+
import { isCodexAuthFailure, codexErrorText, codexSignedOutMessage } from "./codex-auth-failure.ts";
|
|
30
|
+
import {
|
|
31
|
+
isCodexAccountAuthFailed, markCodexAccountAuthFailed, clearCodexAccountAuthFailure,
|
|
32
|
+
} from "../../services/codex-account-auth-state.ts";
|
|
29
33
|
import { killProcessTree } from "../../services/windows-process-tree.ts";
|
|
30
34
|
import { homedir } from "node:os";
|
|
31
35
|
import { join } from "node:path";
|
|
@@ -181,8 +185,16 @@ interface LiveSession {
|
|
|
181
185
|
/** The turn currently in flight, kept so a rotation can send it again on the new
|
|
182
186
|
* account. Cleared when the turn ends — there is then nothing to replay. */
|
|
183
187
|
lastTurnInput?: { message: string; opts?: SendMessageOpts };
|
|
188
|
+
/** The running turn has already been reported as failed (its account is signed out) and
|
|
189
|
+
* interrupted. Codex keeps narrating it — more retry notices, a final error, then
|
|
190
|
+
* `turn/completed` — none of which may reach the caller a second time. Cleared by that
|
|
191
|
+
* `turn/completed`, which is also when the queue may move on. */
|
|
192
|
+
discardingTurn?: boolean;
|
|
184
193
|
}
|
|
185
194
|
|
|
195
|
+
/** Why a turn is being moved to another account. */
|
|
196
|
+
type RotationKind = "usage" | "auth";
|
|
197
|
+
|
|
186
198
|
/** A follow-up held back because a turn was already running. */
|
|
187
199
|
interface QueuedTurn {
|
|
188
200
|
message: string;
|
|
@@ -516,7 +528,15 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
516
528
|
// A quota refusal can come back as the rejection of the request itself rather than
|
|
517
529
|
// as an `error` notification, and it is the same situation either way — move the
|
|
518
530
|
// turn to an account that still has room instead of showing the refusal.
|
|
519
|
-
if (isCodexUsageLimit(reason) && this.beginRotation(live, reason)) return;
|
|
531
|
+
if (isCodexUsageLimit(reason) && this.beginRotation(live, reason, "usage")) return;
|
|
532
|
+
if (isCodexAuthFailure(reason)) {
|
|
533
|
+
this.markSessionAccountSignedOut(live, reason);
|
|
534
|
+
if (this.beginRotation(live, reason, "auth")) return;
|
|
535
|
+
live.channel.push({ type: "error", message: codexSignedOutMessage(this.sessionAccountLabel(live)) });
|
|
536
|
+
live.channel.push({ type: "done", sessionId: live.threadId ?? "", resultSubtype: "error_during_execution" });
|
|
537
|
+
this.endTurn(live);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
520
540
|
if (!live.client.isClosed) live.channel.push({ type: "error", message: redactTruncate(reason, 256) });
|
|
521
541
|
// No turn is running, so nothing will report `turn/completed` — release the
|
|
522
542
|
// queue here or every later follow-up waits on a turn that never existed.
|
|
@@ -556,6 +576,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
556
576
|
live.turnInFlight = false;
|
|
557
577
|
live.activeTurnId = null;
|
|
558
578
|
live.interruptRequested = false;
|
|
579
|
+
live.discardingTurn = false;
|
|
559
580
|
live.lastTurnInput = undefined;
|
|
560
581
|
const next = live.pendingTurns.shift();
|
|
561
582
|
if (next) void this.startTurn(live, next.message, next.opts);
|
|
@@ -575,29 +596,64 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
575
596
|
* user untouched. That is the honest outcome: an error saying the quota is spent is worth
|
|
576
597
|
* more than a silent retry on the account that just said so.
|
|
577
598
|
*/
|
|
578
|
-
private beginRotation(live: LiveSession, reason: string): boolean {
|
|
599
|
+
private beginRotation(live: LiveSession, reason: string, kind: RotationKind): boolean {
|
|
579
600
|
const threadId = live.threadId;
|
|
580
601
|
if (!threadId || live.rotating) return false;
|
|
581
602
|
const currentId = getSessionCodexAccount(threadId);
|
|
582
603
|
const candidates = listCodexAccounts().filter(
|
|
583
|
-
(a) => a.status !== "disabled" && a.id !== currentId
|
|
604
|
+
(a) => a.status !== "disabled" && a.id !== currentId
|
|
605
|
+
&& !isCodexAccountUsageLimited(a.id) && !isCodexAccountAuthFailed(a.id),
|
|
584
606
|
);
|
|
585
607
|
if (candidates.length === 0) return false;
|
|
586
608
|
live.rotating = true;
|
|
587
|
-
void this.rotateAccount(live, threadId, currentId, reason);
|
|
609
|
+
void this.rotateAccount(live, threadId, currentId, reason, kind);
|
|
588
610
|
return true;
|
|
589
611
|
}
|
|
590
612
|
|
|
591
|
-
/**
|
|
613
|
+
/** Mark the account serving this session as signed out, if it is a managed one. */
|
|
614
|
+
private markSessionAccountSignedOut(live: LiveSession, reason: string): void {
|
|
615
|
+
const accountId = live.threadId ? getSessionCodexAccount(live.threadId) : null;
|
|
616
|
+
if (accountId) markCodexAccountAuthFailed(accountId, redactTruncate(reason, 256));
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private sessionAccountLabel(live: LiveSession): string | null {
|
|
620
|
+
const accountId = live.threadId ? getSessionCodexAccount(live.threadId) : null;
|
|
621
|
+
return accountId ? getCodexAccount(accountId)?.label ?? null : null;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* The running turn's account turned out to be signed out.
|
|
626
|
+
*
|
|
627
|
+
* Recognised on codex's FIRST retry notice rather than its final error: the notice already
|
|
628
|
+
* names the 401, and waiting out the rest of the loop is ~24s of a subprocess and a request
|
|
629
|
+
* held open for an answer that cannot come. Moves the turn to another account when one can
|
|
630
|
+
* take it; otherwise reports it and interrupts the turn, discarding what codex still says
|
|
631
|
+
* about it.
|
|
632
|
+
*/
|
|
633
|
+
private handleAuthFailure(live: LiveSession, reason: string): void {
|
|
634
|
+
this.markSessionAccountSignedOut(live, reason);
|
|
635
|
+
if (this.beginRotation(live, reason, "auth")) return;
|
|
636
|
+
live.discardingTurn = true;
|
|
637
|
+
this.interruptActiveTurn(live);
|
|
638
|
+
live.channel.push({ type: "error", message: codexSignedOutMessage(this.sessionAccountLabel(live)) });
|
|
639
|
+
live.channel.push({ type: "done", sessionId: live.threadId ?? "", resultSubtype: "error_during_execution" });
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Bench the refused account, move the session onto a fresh one, and send the turn again. */
|
|
592
643
|
private async rotateAccount(
|
|
593
644
|
live: LiveSession,
|
|
594
645
|
threadId: string,
|
|
595
646
|
currentId: string | null,
|
|
596
647
|
reason: string,
|
|
648
|
+
kind: RotationKind,
|
|
597
649
|
): Promise<void> {
|
|
598
650
|
const pending = live.lastTurnInput;
|
|
599
|
-
const reset = parseCodexUsageLimitReset(reason);
|
|
600
|
-
|
|
651
|
+
const reset = kind === "usage" ? parseCodexUsageLimitReset(reason) : null;
|
|
652
|
+
// A signed-out account was already marked by the caller; only a quota refusal parks here.
|
|
653
|
+
if (currentId && kind === "usage") markCodexAccountUsageLimited(currentId, reset?.atMs);
|
|
654
|
+
const giveUp = (): void => this.abandonRotation(live, kind === "auth"
|
|
655
|
+
? codexSignedOutMessage(currentId ? getCodexAccount(currentId)?.label : null)
|
|
656
|
+
: this.usageLimitMessage(reason, reset?.text));
|
|
601
657
|
|
|
602
658
|
let next: CodexAccount | null = null;
|
|
603
659
|
try {
|
|
@@ -612,12 +668,12 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
612
668
|
next = selectCodexAccount(currentId ? { exclude: [currentId] } : undefined);
|
|
613
669
|
}
|
|
614
670
|
|
|
615
|
-
if (!next) {
|
|
671
|
+
if (!next) { giveUp(); return; }
|
|
616
672
|
|
|
617
|
-
console.warn(`[codex] session=${threadId} usage limit — switching to ${next.id} (${next.label})`);
|
|
673
|
+
console.warn(`[codex] session=${threadId} ${kind === "auth" ? "account signed out" : "usage limit"} — switching to ${next.id} (${next.label})`);
|
|
618
674
|
live.channel.push({
|
|
619
675
|
type: "account_retry",
|
|
620
|
-
reason: "Usage limit reached — switching account",
|
|
676
|
+
reason: kind === "auth" ? "Account signed out — switching account" : "Usage limit reached — switching account",
|
|
621
677
|
accountId: next.id,
|
|
622
678
|
accountLabel: next.label,
|
|
623
679
|
});
|
|
@@ -626,7 +682,7 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
626
682
|
await this.respawnOn(live, threadId, next);
|
|
627
683
|
} catch (e) {
|
|
628
684
|
console.error(`[codex] session=${threadId} rotation to ${next.id} failed: ${redactTruncate((e as Error)?.message, 200)}`);
|
|
629
|
-
|
|
685
|
+
giveUp();
|
|
630
686
|
return;
|
|
631
687
|
}
|
|
632
688
|
|
|
@@ -639,14 +695,15 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
639
695
|
else this.endTurn(live);
|
|
640
696
|
}
|
|
641
697
|
|
|
698
|
+
private usageLimitMessage(reason: string, resetText?: string): string {
|
|
699
|
+
const suffix = resetText ? ` Try again ${resetText}.` : "";
|
|
700
|
+
return `${redactTruncate(reason, 512)}${suffix} Add another account in Settings → Accounts, or wait for the reset.`;
|
|
701
|
+
}
|
|
702
|
+
|
|
642
703
|
/** No account could take the turn — report the refusal and close the turn out. */
|
|
643
|
-
private abandonRotation(live: LiveSession,
|
|
704
|
+
private abandonRotation(live: LiveSession, message: string): void {
|
|
644
705
|
live.rotating = false;
|
|
645
|
-
|
|
646
|
-
live.channel.push({
|
|
647
|
-
type: "error",
|
|
648
|
-
message: `${redactTruncate(reason, 512)}${suffix} Add another account in Settings → Accounts, or wait for the reset.`,
|
|
649
|
-
});
|
|
706
|
+
live.channel.push({ type: "error", message });
|
|
650
707
|
live.channel.push({ type: "done", sessionId: live.threadId ?? "", resultSubtype: "error_during_execution" });
|
|
651
708
|
this.endTurn(live);
|
|
652
709
|
}
|
|
@@ -825,14 +882,35 @@ export class CodexAppServerProvider implements AIProvider {
|
|
|
825
882
|
private handleNotification(live: LiveSession, notif: JsonRpcNotification): void {
|
|
826
883
|
// A quota refusal is an ordinary `error` notification, so it has to be recognised
|
|
827
884
|
// before the mapper turns it into a plain error card in front of the user.
|
|
828
|
-
if (notif.method === "error" && !live.rotating) {
|
|
885
|
+
if (notif.method === "error" && !live.rotating && !live.discardingTurn) {
|
|
886
|
+
// Retry notices count too: the headline says only "Reconnecting... N/5", but their
|
|
887
|
+
// details name the 401, and waiting for the final error costs the whole retry loop.
|
|
888
|
+
const authText = codexErrorText(notif.params);
|
|
889
|
+
if (isCodexAuthFailure(authText)) { this.handleAuthFailure(live, authText); return; }
|
|
829
890
|
const reason = codexErrorMessage(notif.params);
|
|
830
|
-
if (isCodexUsageLimit(reason) && this.beginRotation(live, reason)) return;
|
|
891
|
+
if (isCodexUsageLimit(reason) && this.beginRotation(live, reason, "usage")) return;
|
|
831
892
|
}
|
|
832
893
|
// Mid-rotation the subprocess behind this session is being replaced. Anything it still
|
|
833
894
|
// emits describes the turn that was refused — including its `turn/completed`, which
|
|
834
895
|
// would close the turn the caller is about to be given a second time.
|
|
835
896
|
if (live.rotating) return;
|
|
897
|
+
// A turn already reported as failed: swallow the rest of it, and let the queue move on
|
|
898
|
+
// only once codex confirms it is over.
|
|
899
|
+
if (live.discardingTurn) {
|
|
900
|
+
if (notif.method === "turn/completed") {
|
|
901
|
+
live.discardingTurn = false;
|
|
902
|
+
live.currentAssistant = "";
|
|
903
|
+
live.currentEvents = [];
|
|
904
|
+
this.endTurn(live);
|
|
905
|
+
}
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (notif.method === "turn/completed"
|
|
909
|
+
&& (notif.params as { turn?: { status?: string } })?.turn?.status === "completed" && live.threadId) {
|
|
910
|
+
// An answered turn is proof the login works, whatever was observed before.
|
|
911
|
+
const accountId = getSessionCodexAccount(live.threadId);
|
|
912
|
+
if (accountId) clearCodexAccountAuthFailure(accountId);
|
|
913
|
+
}
|
|
836
914
|
if (notif.method === "item/agentMessage/delta") {
|
|
837
915
|
const d = (notif.params as { delta?: string })?.delta;
|
|
838
916
|
if (typeof d === "string") live.currentAssistant += d;
|
|
@@ -3,6 +3,8 @@ import type { ProviderUsageSource } from "../../services/provider-usage/usage-so
|
|
|
3
3
|
import { AMBIENT_ACCOUNT_KEY } from "../../services/provider-usage/usage-source.ts";
|
|
4
4
|
import { listCodexAccounts, getCodexAccount } from "../../services/codex-account.service.ts";
|
|
5
5
|
import { fetchCodexUsageLive } from "./codex-usage-fetch.ts";
|
|
6
|
+
import { isCodexAuthFailure } from "./codex-auth-failure.ts";
|
|
7
|
+
import { markCodexAccountAuthFailed, clearCodexAccountAuthFailure } from "../../services/codex-account-auth-state.ts";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Codex's plug into the shared usage layer.
|
|
@@ -29,7 +31,18 @@ export const codexUsageSource: ProviderUsageSource = {
|
|
|
29
31
|
// object would be persisted as a real reading for an account that is gone.
|
|
30
32
|
if (!account) throw new Error(`codex account ${accountId} no longer exists`);
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
let usage: UsageInfo;
|
|
35
|
+
try {
|
|
36
|
+
usage = await fetchCodexUsageLive(account.home);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// The sweep is the one thing that reads every account on a timer, so it is what keeps
|
|
39
|
+
// a signed-out account benched between turns — and what notices it first, before any
|
|
40
|
+
// turn is spent on it.
|
|
41
|
+
const message = (e as Error)?.message ?? String(e);
|
|
42
|
+
if (isCodexAuthFailure(message)) markCodexAccountAuthFailed(account.id, message.slice(0, 256));
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
clearCodexAccountAuthFailure(account.id);
|
|
33
46
|
// The rate-limit payload carries the plan name, which the parser puts in
|
|
34
47
|
// activeAccountLabel; the account's own label is the truthful one.
|
|
35
48
|
return { ...usage, activeAccountId: account.id, activeAccountLabel: account.label };
|
|
@@ -3,11 +3,20 @@ import { ok, err } from "../../types/api.ts";
|
|
|
3
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
|
+
import { isCodexAccountAuthFailed } from "../../services/codex-account-auth-state.ts";
|
|
6
7
|
|
|
7
8
|
/** Codex multi-account management. Mounted under /api/codex-accounts (auth-guarded). */
|
|
8
9
|
export const codexAccountsRoutes = new Hono();
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* `signedOut` is the server's live observation (a revoked/expired login seen on a turn or a
|
|
13
|
+
* quota read), not a stored field. The usage endpoint keeps serving the last good reading
|
|
14
|
+
* for such an account, so without it the card looked healthy while every turn failed.
|
|
15
|
+
*/
|
|
16
|
+
codexAccountsRoutes.get("/", (c) => c.json(ok({
|
|
17
|
+
accounts: listCodexAccounts().map((a) => ({ ...a, signedOut: isCodexAccountAuthFailed(a.id) })),
|
|
18
|
+
strategy: getCodexStrategy(),
|
|
19
|
+
})));
|
|
11
20
|
|
|
12
21
|
/** Per-account quota map { [accountId]: UsageInfo }. */
|
|
13
22
|
codexAccountsRoutes.get("/usage", async (c) => c.json(ok(await getAllCodexUsages())));
|
|
@@ -73,10 +82,12 @@ codexAccountsRoutes.post("/api-key", async (c) => {
|
|
|
73
82
|
catch (e) { return c.json(err((e as Error).message), 400); }
|
|
74
83
|
});
|
|
75
84
|
|
|
76
|
-
/** Begin ChatGPT device-code login → returns { id, userCode, verificationUrl }.
|
|
85
|
+
/** Begin ChatGPT device-code login → returns { id, userCode, verificationUrl }.
|
|
86
|
+
* With `accountId`, signs that existing account in again instead of adding one. */
|
|
77
87
|
codexAccountsRoutes.post("/device-login", async (c) => {
|
|
78
|
-
const body = await c.req.json<{ label?: string }>().catch(() => ({} as { label?: string }));
|
|
79
|
-
|
|
88
|
+
const body = await c.req.json<{ label?: string; accountId?: unknown }>().catch(() => ({} as { label?: string; accountId?: unknown }));
|
|
89
|
+
if (body.accountId !== undefined && typeof body.accountId !== "string") return c.json(err("accountId must be a string"), 400);
|
|
90
|
+
try { return c.json(ok(await startDeviceLogin(body.label, undefined, body.accountId))); }
|
|
80
91
|
catch (e) { return c.json(err((e as Error).message), 400); }
|
|
81
92
|
});
|
|
82
93
|
|
|
@@ -92,10 +103,11 @@ codexAccountsRoutes.delete("/device-login/:id", (c) => {
|
|
|
92
103
|
|
|
93
104
|
/** Browser OAuth uses the same durable polling as device-code login. */
|
|
94
105
|
codexAccountsRoutes.post("/browser-login", async (c) => {
|
|
95
|
-
const body = await c.req.json<{ label?: string }>().catch(() => ({} as { label?: string }));
|
|
106
|
+
const body = await c.req.json<{ label?: string; accountId?: unknown }>().catch(() => ({} as { label?: string; accountId?: unknown }));
|
|
96
107
|
if (body.label !== undefined && typeof body.label !== "string") return c.json(err("label must be a string"), 400);
|
|
108
|
+
if (body.accountId !== undefined && typeof body.accountId !== "string") return c.json(err("accountId must be a string"), 400);
|
|
97
109
|
c.header("Cache-Control", "no-store");
|
|
98
|
-
try { return c.json(ok(await startBrowserLogin(body.label))); }
|
|
110
|
+
try { return c.json(ok(await startBrowserLogin(body.label, undefined, body.accountId))); }
|
|
99
111
|
catch (e) { return c.json(err((e as Error).message), 400); }
|
|
100
112
|
});
|
|
101
113
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex accounts the server has seen refused for a revoked or expired login.
|
|
3
|
+
*
|
|
4
|
+
* Sibling of `codex-account-cooldown.ts`, and in memory for the same reason: this is the
|
|
5
|
+
* server's own observation, not the user's on/off switch, and writing it into the account
|
|
6
|
+
* row would show an account the user never touched as changed in Settings.
|
|
7
|
+
*
|
|
8
|
+
* Unlike a quota park this is a hard exclusion from selection. A spent quota still answers
|
|
9
|
+
* slowly; a signed-out account never answers at all, and every turn handed to it costs a
|
|
10
|
+
* subprocess and ~24s of codex retrying before it says so. That is what let a burst of
|
|
11
|
+
* proxy requests pin the server until the supervisor killed it.
|
|
12
|
+
*
|
|
13
|
+
* The mark lapses on its own so a misread error cannot bench an account forever, and the
|
|
14
|
+
* usage sweep (every 5 minutes) renews it for as long as the account really is signed out.
|
|
15
|
+
* A successful usage read, a completed turn or a fresh sign-in clears it at once.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const MARK_TTL_MS = 15 * 60 * 1000;
|
|
19
|
+
|
|
20
|
+
interface AuthFailure {
|
|
21
|
+
until: number;
|
|
22
|
+
reason: string;
|
|
23
|
+
since: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const failures = new Map<string, AuthFailure>();
|
|
27
|
+
|
|
28
|
+
/** Record that this account's login was refused. */
|
|
29
|
+
export function markCodexAccountAuthFailed(accountId: string, reason: string): void {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const prev = failures.get(accountId);
|
|
32
|
+
const fresh = !prev || prev.until <= now;
|
|
33
|
+
failures.set(accountId, { until: now + MARK_TTL_MS, reason, since: fresh ? now : prev.since });
|
|
34
|
+
if (fresh) console.warn(`[codex] account ${accountId} is signed out — skipping it until it signs in again`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Whether the account is currently known to be signed out. Lapsed marks drop as read. */
|
|
38
|
+
export function isCodexAccountAuthFailed(accountId: string): boolean {
|
|
39
|
+
const f = failures.get(accountId);
|
|
40
|
+
if (!f) return false;
|
|
41
|
+
if (f.until <= Date.now()) { failures.delete(accountId); return false; }
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** When the account was first seen signed out, or null if it is not. */
|
|
46
|
+
export function codexAccountAuthFailedSince(accountId: string): number | null {
|
|
47
|
+
return isCodexAccountAuthFailed(accountId) ? failures.get(accountId)!.since : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The account answered normally again — drop the mark. */
|
|
51
|
+
export function clearCodexAccountAuthFailure(accountId: string): void {
|
|
52
|
+
if (failures.delete(accountId)) console.log(`[codex] account ${accountId} is signed in again`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Test seam: forget every mark. */
|
|
56
|
+
export function _resetCodexAuthFailuresForTesting(): void {
|
|
57
|
+
failures.clear();
|
|
58
|
+
}
|
|
@@ -17,7 +17,9 @@ import { randomUUID } from "node:crypto";
|
|
|
17
17
|
import { mkdirSync, rmSync } from "node:fs";
|
|
18
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
|
-
import { codexAccountHome, createCodexAccount, type CodexAccount } from "./codex-account.service.ts";
|
|
20
|
+
import { codexAccountHome, createCodexAccount, getCodexAccount, updateCodexAccountMeta, type CodexAccount } from "./codex-account.service.ts";
|
|
21
|
+
import { clearCodexAccountAuthFailure } from "./codex-account-auth-state.ts";
|
|
22
|
+
import { refreshUsage } from "./provider-usage/usage-registry.ts";
|
|
21
23
|
|
|
22
24
|
const CLIENT_INFO = { name: "ppm", title: "PPM", version: "0.0.0" };
|
|
23
25
|
const CAPABILITIES = { experimentalApi: true, requestAttestation: false, optOutNotificationMethods: null };
|
|
@@ -78,6 +80,9 @@ interface ChatGptLogin {
|
|
|
78
80
|
browser?: BrowserLoginCallback;
|
|
79
81
|
callbackAbort?: AbortController;
|
|
80
82
|
client: LoginClient; home: string; label?: string;
|
|
83
|
+
/** Signing an existing account in again: the login runs in that account's own home and
|
|
84
|
+
* finalises by updating its row instead of creating one. */
|
|
85
|
+
reloginId?: string;
|
|
81
86
|
status: DeviceLoginStatus;
|
|
82
87
|
timer: ReturnType<typeof setTimeout>;
|
|
83
88
|
/** True from the completion notification until the account is written. The
|
|
@@ -116,7 +121,8 @@ function disposeLogin(id: string, rmHome: boolean): void {
|
|
|
116
121
|
clearTimeout(p.timer);
|
|
117
122
|
p.callbackAbort?.abort();
|
|
118
123
|
try { p.client.close(); } catch { /* ignore */ }
|
|
119
|
-
|
|
124
|
+
// A re-login borrows a live account's home; abandoning it must never delete that.
|
|
125
|
+
if (rmHome && !p.reloginId) { try { rmSync(p.home, { recursive: true, force: true }); } catch { /* ignore */ } }
|
|
120
126
|
}
|
|
121
127
|
|
|
122
128
|
/** Record the outcome and stop the app-server. First settle wins, so the close
|
|
@@ -131,7 +137,8 @@ function settle(id: string, status: Exclude<DeviceLoginStatus, { state: "pending
|
|
|
131
137
|
try { p.client.close(); } catch { /* ignore */ }
|
|
132
138
|
// A failure leaves no account owning the dir, so the half-built home goes now.
|
|
133
139
|
// A success must keep it: createCodexAccount adopted it as the account's home.
|
|
134
|
-
|
|
140
|
+
// A failed re-login leaves the account's home alone — it still belongs to the account.
|
|
141
|
+
if (status.state === "error" && !p.reloginId) { try { rmSync(p.home, { recursive: true, force: true }); } catch { /* ignore */ } }
|
|
135
142
|
p.timer = unref(setTimeout(() => { pendingLogins.delete(id); }, RESULT_GRACE_MS));
|
|
136
143
|
}
|
|
137
144
|
|
|
@@ -147,6 +154,21 @@ async function finalizeLogin(id: string, notif: CompletionNotification): Promise
|
|
|
147
154
|
const read = await p.client.request<AccountRead>("account/read", {}, CONTROL_REQUEST_TIMEOUT_MS);
|
|
148
155
|
if (!read?.account) throw new Error("login completed but account is empty");
|
|
149
156
|
if (p.status.state !== "pending") return;
|
|
157
|
+
if (p.reloginId) {
|
|
158
|
+
const existing = getCodexAccount(p.reloginId);
|
|
159
|
+
if (!existing) throw new Error("That account was removed while signing in. Add it again.");
|
|
160
|
+
updateCodexAccountMeta(existing.id, {
|
|
161
|
+
// The label is the user's name for this account ("Work"), not a copy of the email.
|
|
162
|
+
label: p.label || existing.label,
|
|
163
|
+
planType: read.account.planType ?? existing.planType ?? null,
|
|
164
|
+
});
|
|
165
|
+
// The fresh login is the proof: stop skipping the account and read its quota now,
|
|
166
|
+
// rather than showing the stale reading until the next five-minute sweep.
|
|
167
|
+
clearCodexAccountAuthFailure(existing.id);
|
|
168
|
+
void refreshUsage("codex", existing.id);
|
|
169
|
+
settle(id, { state: "done", account: getCodexAccount(existing.id)! });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
150
172
|
const account = createCodexAccount({
|
|
151
173
|
id, label: p.label || read.account.email || "ChatGPT", type: "chatgpt",
|
|
152
174
|
planType: read.account.planType ?? null,
|
|
@@ -163,12 +185,19 @@ async function startChatGptLogin(
|
|
|
163
185
|
method: "device" | "browser",
|
|
164
186
|
label: string | undefined,
|
|
165
187
|
makeClient: () => LoginClient,
|
|
188
|
+
reloginId?: string,
|
|
166
189
|
): Promise<ChatGptLoginStart> {
|
|
167
190
|
if (method === "browser" && hasPendingBrowserLogin()) {
|
|
168
191
|
throw new Error("A browser login is already in progress. Finish or cancel it first.");
|
|
169
192
|
}
|
|
193
|
+
// Re-login signs the SAME account in again, in its own CODEX_HOME: the id, the sessions
|
|
194
|
+
// bound to it, its transcripts and its settings all stay. Removing and re-adding was the
|
|
195
|
+
// only way before, and it cost every one of those.
|
|
196
|
+
const target = reloginId ? getCodexAccount(reloginId) : null;
|
|
197
|
+
if (reloginId && !target) throw new Error("Account not found");
|
|
198
|
+
if (target && target.type !== "chatgpt") throw new Error("Only a ChatGPT sign-in can be renewed; replace an API key by adding it again.");
|
|
170
199
|
const id = randomUUID();
|
|
171
|
-
const home = codexAccountHome(id);
|
|
200
|
+
const home = target ? target.home : codexAccountHome(id);
|
|
172
201
|
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
173
202
|
const client = makeClient();
|
|
174
203
|
client.onNotification((n) => {
|
|
@@ -178,7 +207,10 @@ async function startChatGptLogin(
|
|
|
178
207
|
});
|
|
179
208
|
client.onClose(() => settle(id, { state: "error", error: "login process exited" }));
|
|
180
209
|
const timer = unref(setTimeout(() => settle(id, { state: "error", error: "timed out" }), method === "browser" ? BROWSER_LOGIN_TTL : DEVICE_LOGIN_TTL));
|
|
181
|
-
const pending: ChatGptLogin = {
|
|
210
|
+
const pending: ChatGptLogin = {
|
|
211
|
+
method, client, home, label, status: { state: "pending" }, timer, finalizing: false,
|
|
212
|
+
...(target ? { reloginId: target.id } : {}),
|
|
213
|
+
};
|
|
182
214
|
pendingLogins.set(id, pending);
|
|
183
215
|
try {
|
|
184
216
|
if (method === "browser") await checkCodexLoginPort();
|
|
@@ -195,13 +227,14 @@ async function startChatGptLogin(
|
|
|
195
227
|
}
|
|
196
228
|
}
|
|
197
229
|
|
|
198
|
-
|
|
199
|
-
|
|
230
|
+
/** `reloginId` signs that existing ChatGPT account in again instead of adding a new one. */
|
|
231
|
+
export async function startDeviceLogin(label?: string, makeClient: () => LoginClient = () => new CodexJsonRpcClient(), reloginId?: string) {
|
|
232
|
+
const { id, userCode, verificationUrl } = await startChatGptLogin("device", label, makeClient, reloginId);
|
|
200
233
|
return { id, userCode, verificationUrl };
|
|
201
234
|
}
|
|
202
235
|
|
|
203
|
-
export async function startBrowserLogin(label?: string, makeClient: () => LoginClient = () => new CodexJsonRpcClient()) {
|
|
204
|
-
const { id, authUrl } = await startChatGptLogin("browser", label, makeClient);
|
|
236
|
+
export async function startBrowserLogin(label?: string, makeClient: () => LoginClient = () => new CodexJsonRpcClient(), reloginId?: string) {
|
|
237
|
+
const { id, authUrl } = await startChatGptLogin("browser", label, makeClient, reloginId);
|
|
205
238
|
return { id, authUrl };
|
|
206
239
|
}
|
|
207
240
|
|