@hienlh/ppm 0.23.11 → 0.23.12
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 +18 -0
- package/CLAUDE.md +13 -2
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +37 -1
- package/bun.lock +34 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js +1 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-CGe5ikvs.js.gz +0 -0
- package/dist/web/assets/{agent-session-sheet-CBMsnl_p.js → agent-session-sheet-vBhfHYrC.js} +2 -2
- package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.br +0 -0
- package/dist/web/assets/agent-session-sheet-vBhfHYrC.js.gz +0 -0
- package/dist/web/assets/{agent-session-window-content-D9C9FA7E.js → agent-session-window-content-BxIxDN5i.js} +1 -1
- package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.br +0 -0
- package/dist/web/assets/agent-session-window-content-BxIxDN5i.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DhUVgDEG.js → ai-resource-editor-Y8clBPRv.js} +1 -1
- package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-Y8clBPRv.js.gz +0 -0
- package/dist/web/assets/{ai-settings-section-DOckWvp4.js → ai-settings-section-BB8vscte.js} +1 -1
- package/dist/web/assets/{ai-settings-section-CETpSpYd.js → ai-settings-section-Ck8uN_Bw.js} +1 -1
- package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Ck8uN_Bw.js.gz +0 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js +2 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js.br +0 -0
- package/dist/web/assets/android-tab-BIOUVVoW.js.gz +0 -0
- package/dist/web/assets/{api-settings-JIgKfcCx.js → api-settings-D1axn9vm.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-BJUiLUb7.js → appearance-settings-section-Bv5_n9lH.js} +1 -1
- package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-Bv5_n9lH.js.gz +0 -0
- package/dist/web/assets/{apps-panel-1VElRgV3.js → apps-panel-DXHF0QEv.js} +1 -1
- package/dist/web/assets/apps-panel-DXHF0QEv.js.br +0 -0
- package/dist/web/assets/apps-panel-DXHF0QEv.js.gz +0 -0
- package/dist/web/assets/{audio-preview-DYkyHu5X.js → audio-preview-DpjOKEm4.js} +1 -1
- package/dist/web/assets/audio-preview-DpjOKEm4.js.br +0 -0
- package/dist/web/assets/audio-preview-DpjOKEm4.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js +1 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js.br +0 -0
- package/dist/web/assets/branch-review-tab-4dgMNadQ.js.gz +0 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js +15 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js.br +0 -0
- package/dist/web/assets/chat-tab-Bbe_77oJ.js.gz +0 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js +1 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js.br +0 -0
- package/dist/web/assets/chat-tab-D3i5O3dO.js.gz +0 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js +7 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js.br +0 -0
- package/dist/web/assets/code-editor-DXsIWoQ1.js.gz +0 -0
- package/dist/web/assets/{codex-reset-credit-button-Fr45FUxi.js → codex-reset-credit-button-DRhievFQ.js} +1 -1
- package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-DRhievFQ.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-B-wdjSXI.js → conflict-editor-D_RImo_V.js} +3 -3
- package/dist/web/assets/conflict-editor-D_RImo_V.js.br +0 -0
- package/dist/web/assets/conflict-editor-D_RImo_V.js.gz +0 -0
- package/dist/web/assets/{csv-preview-B6RodObB.js → csv-preview-BuFdfLo3.js} +1 -1
- package/dist/web/assets/csv-preview-BuFdfLo3.js.br +0 -0
- package/dist/web/assets/csv-preview-BuFdfLo3.js.gz +0 -0
- package/dist/web/assets/{database-viewer-CdPBUQPY.js → database-viewer-ChcbYn9E.js} +1 -1
- package/dist/web/assets/database-viewer-ChcbYn9E.js.br +0 -0
- package/dist/web/assets/database-viewer-ChcbYn9E.js.gz +0 -0
- package/dist/web/assets/{design-settings-section-BpCpaBwL.js → design-settings-section-B_xOf51h.js} +1 -1
- package/dist/web/assets/design-settings-section-B_xOf51h.js.br +0 -0
- package/dist/web/assets/design-settings-section-B_xOf51h.js.gz +0 -0
- package/dist/web/assets/{design-skill-suggestion-DkT2E2IV.js → design-skill-suggestion-BbEDg15Z.js} +1 -1
- package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.br +0 -0
- package/dist/web/assets/design-skill-suggestion-BbEDg15Z.js.gz +0 -0
- package/dist/web/assets/{design-tab-BZb9eemI.js → design-tab-BQSYJ4cS.js} +6 -6
- package/dist/web/assets/design-tab-BQSYJ4cS.js.br +0 -0
- package/dist/web/assets/design-tab-BQSYJ4cS.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-CF4tp6j-.js → designs-sidebar-panel-AiPaYfSU.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-AiPaYfSU.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-BqEqwSw6.js → diff-viewer-BZ6glGK-.js} +1 -1
- package/dist/web/assets/diff-viewer-BZ6glGK-.js.br +0 -0
- package/dist/web/assets/diff-viewer-BZ6glGK-.js.gz +0 -0
- package/dist/web/assets/{edit-diff-preview-DD4HayXY.js → edit-diff-preview-D-FNLOU2.js} +1 -1
- package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.br +0 -0
- package/dist/web/assets/edit-diff-preview-D-FNLOU2.js.gz +0 -0
- package/dist/web/assets/{editor-language-service-CkA5sEdV.js → editor-language-service-4sbsdqBV.js} +1 -1
- package/dist/web/assets/editor-language-service-4sbsdqBV.js.br +0 -0
- package/dist/web/assets/editor-language-service-4sbsdqBV.js.gz +0 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js +2 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js.br +0 -0
- package/dist/web/assets/explorer-body-DbgrG1fe.js.gz +0 -0
- package/dist/web/assets/{explorer-window-content-CRJ2eMZ4.js → explorer-window-content-BCjWvHpp.js} +1 -1
- package/dist/web/assets/explorer-window-content-BCjWvHpp.js.br +0 -0
- package/dist/web/assets/explorer-window-content-BCjWvHpp.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-DwQTdCII.js → extension-manager-section-DiMF4RnT.js} +1 -1
- package/dist/web/assets/extension-manager-section-DiMF4RnT.js.br +0 -0
- package/dist/web/assets/extension-manager-section-DiMF4RnT.js.gz +0 -0
- package/dist/web/assets/{extension-webview-CMq-VTLu.js → extension-webview-Coti6Vfw.js} +1 -1
- package/dist/web/assets/extension-webview-Coti6Vfw.js.br +0 -0
- package/dist/web/assets/extension-webview-Coti6Vfw.js.gz +0 -0
- package/dist/web/assets/file-store-Cm7-pHux.js +1 -0
- package/dist/web/assets/file-store-Cm7-pHux.js.br +0 -0
- package/dist/web/assets/file-store-Cm7-pHux.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-B2lcyh-6.js → files-settings-section-ZTVwOwx0.js} +1 -1
- package/dist/web/assets/files-settings-section-ZTVwOwx0.js.br +0 -0
- package/dist/web/assets/files-settings-section-ZTVwOwx0.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-Doepm2sH.js → general-settings-section-B7Hjx_fb.js} +1 -1
- package/dist/web/assets/general-settings-section-B7Hjx_fb.js.br +0 -0
- package/dist/web/assets/general-settings-section-B7Hjx_fb.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-CJakNMDc.js → git-log-panel-CbcJ_YEH.js} +1 -1
- package/dist/web/assets/git-log-panel-CbcJ_YEH.js.br +0 -0
- package/dist/web/assets/git-log-panel-CbcJ_YEH.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-C_SeOhSq.js → glide-data-grid-BfByGSf1.js} +3 -3
- package/dist/web/assets/glide-data-grid-BfByGSf1.js.br +0 -0
- package/dist/web/assets/glide-data-grid-BfByGSf1.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js +1 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DCo9OP3D.js.gz +0 -0
- package/dist/web/assets/{image-preview-C1Frlx22.js → image-preview-Cjlps7Pt.js} +1 -1
- package/dist/web/assets/index-CyDLBddf.js +52 -0
- package/dist/web/assets/index-CyDLBddf.js.br +0 -0
- package/dist/web/assets/index-CyDLBddf.js.gz +0 -0
- package/dist/web/assets/index-Dh5apC3I.css +1 -0
- package/dist/web/assets/index-Dh5apC3I.css.br +0 -0
- package/dist/web/assets/index-Dh5apC3I.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-BmkIGQFV.js → jira-watcher-section-Dz_-sUC2.js} +1 -1
- package/dist/web/assets/keybindings-store-BTq5Z7XP.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-BgZe3QDr.js → keyboard-shortcuts-section-Ba5NYwlG.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-Ba5NYwlG.js.gz +0 -0
- package/dist/web/assets/{language-servers-section-CCMzgOaE.js → language-servers-section-DJJ7SFXo.js} +2 -2
- package/dist/web/assets/language-servers-section-DJJ7SFXo.js.br +0 -0
- package/dist/web/assets/language-servers-section-DJJ7SFXo.js.gz +0 -0
- package/dist/web/assets/{lsp-client-B10suuaZ.js → lsp-client-Dr3vQ1bb.js} +1 -1
- package/dist/web/assets/lsp-client-Dr3vQ1bb.js.br +0 -0
- package/dist/web/assets/lsp-client-Dr3vQ1bb.js.gz +0 -0
- package/dist/web/assets/{markdown-renderer-AHHFS3jc.js → markdown-renderer-CmnqFfTU.js} +3 -3
- package/dist/web/assets/markdown-renderer-CmnqFfTU.js.br +0 -0
- package/dist/web/assets/markdown-renderer-CmnqFfTU.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-LJdVMbGl.js +1 -0
- package/dist/web/assets/{mobile-explorer-sheet-CqCsLWSr.js → mobile-explorer-sheet-BiIhZtJ2.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-BiIhZtJ2.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js +1 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js.br +0 -0
- package/dist/web/assets/new-design-dialog-DI_hxvFV.js.gz +0 -0
- package/dist/web/assets/notification-store-9lBSplOV.js +1 -0
- package/dist/web/assets/{notifications-settings-section-CXgzH_bK.js → notifications-settings-section-0s-2OKRv.js} +1 -1
- package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-0s-2OKRv.js.gz +0 -0
- package/dist/web/assets/{overview-panel-BYB8mgRE.js → overview-panel-3htxfZS2.js} +1 -1
- package/dist/web/assets/overview-panel-3htxfZS2.js.br +0 -0
- package/dist/web/assets/overview-panel-3htxfZS2.js.gz +0 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js +1 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js.br +0 -0
- package/dist/web/assets/panel-store-B6PoCwBA.js.gz +0 -0
- package/dist/web/assets/{performance-panel-Dpuq3g0W.js → performance-panel-V8gpnnf7.js} +2 -2
- package/dist/web/assets/performance-panel-V8gpnnf7.js.br +0 -0
- package/dist/web/assets/performance-panel-V8gpnnf7.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-DW0Gkmow.js → postgres-viewer-sWS-ogTC.js} +1 -1
- package/dist/web/assets/postgres-viewer-sWS-ogTC.js.br +0 -0
- package/dist/web/assets/postgres-viewer-sWS-ogTC.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-DFtlGoW6.js → ppmbot-settings-section-CT8vvlzk.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-CT8vvlzk.js.gz +0 -0
- package/dist/web/assets/{pptx-export-CNyG32Xr.js → pptx-export-Ugjv0hso.js} +4 -4
- package/dist/web/assets/pptx-export-Ugjv0hso.js.br +0 -0
- package/dist/web/assets/pptx-export-Ugjv0hso.js.gz +0 -0
- package/dist/web/assets/{problems-panel-V5zqpuXq.js → problems-panel-Gcy79pSS.js} +1 -1
- package/dist/web/assets/problems-panel-Gcy79pSS.js.br +0 -0
- package/dist/web/assets/problems-panel-Gcy79pSS.js.gz +0 -0
- package/dist/web/assets/{process-row-format-CiJJSf_V.js → process-row-format-u6y7lOee.js} +1 -1
- package/dist/web/assets/process-table-BUTIp9Oy.js +1 -0
- package/dist/web/assets/process-table-BUTIp9Oy.js.br +0 -0
- package/dist/web/assets/process-table-BUTIp9Oy.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-B7AVlTfY.js → proxy-settings-section-Buv_8y54.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Buv_8y54.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Buv_8y54.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-gHmRfWKr.js → query-audit-section-N2DQtgJx.js} +1 -1
- package/dist/web/assets/query-audit-section-N2DQtgJx.js.br +0 -0
- package/dist/web/assets/query-audit-section-N2DQtgJx.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js +1 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-CRvJqmaz.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BoYINLbJ.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdP0yYu.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BeQ_Jrp7.js.gz +0 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js +1 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.br +0 -0
- package/dist/web/assets/remote-desktop-settings-section-Bs4sHS3l.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-DDr__JxQ.js.gz +0 -0
- package/dist/web/assets/{run-in-terminal-DKBcEGVu.js → run-in-terminal-DCBNtYHQ.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-sYDAsBMO.js → schedules-settings-section-_cHX2qOa.js} +1 -1
- package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-_cHX2qOa.js.gz +0 -0
- package/dist/web/assets/{send-to-chat-CCdOrKc7.js → send-to-chat-dNqNV7Y4.js} +1 -1
- package/dist/web/assets/send-to-chat-dNqNV7Y4.js.br +0 -0
- package/dist/web/assets/send-to-chat-dNqNV7Y4.js.gz +0 -0
- package/dist/web/assets/{services-panel-CKa2aP1d.js → services-panel-Dm54cnW3.js} +1 -1
- package/dist/web/assets/services-panel-Dm54cnW3.js.br +0 -0
- package/dist/web/assets/services-panel-Dm54cnW3.js.gz +0 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js +1 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js.br +0 -0
- package/dist/web/assets/settings-body-DRRQNaDA.js.gz +0 -0
- package/dist/web/assets/settings-section-content-DRaHfvm8.js +1 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js +2 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js.br +0 -0
- package/dist/web/assets/settings-section-content-U3mIx6hF.js.gz +0 -0
- package/dist/web/assets/{settings-store-DfuiLq_9.js → settings-store-Cyuo8ceh.js} +2 -2
- package/dist/web/assets/settings-store-Cyuo8ceh.js.br +0 -0
- package/dist/web/assets/settings-store-Cyuo8ceh.js.gz +0 -0
- package/dist/web/assets/settings-tab-CB_BhHTK.js +1 -0
- package/dist/web/assets/settings-window-content-CgZIMNX8.js +1 -0
- package/dist/web/assets/{slash-command-picker-CLevA_Gf.js → slash-command-picker-CQlIqMW0.js} +1 -1
- package/dist/web/assets/slash-command-picker-CQlIqMW0.js.br +0 -0
- package/dist/web/assets/slash-command-picker-CQlIqMW0.js.gz +0 -0
- package/dist/web/assets/{sql-query-editor-Db8b0jEr.js → sql-query-editor--Z4bcqd8.js} +1 -1
- package/dist/web/assets/sql-query-editor--Z4bcqd8.js.br +0 -0
- package/dist/web/assets/sql-query-editor--Z4bcqd8.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-VzYLMqkD.js → sqlite-viewer-CSmX3qaj.js} +1 -1
- package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-CSmX3qaj.js.gz +0 -0
- package/dist/web/assets/{system-monitor-body-BR60W_mG.js → system-monitor-body-LannpU6H.js} +2 -2
- package/dist/web/assets/system-monitor-body-LannpU6H.js.br +0 -0
- package/dist/web/assets/system-monitor-body-LannpU6H.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-BfVucwEF.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BLw1QBXo.js → system-monitor-window-content-kLNW9Ln3.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-BL61SB5m.js → tab-host-window-content-II_FtOYM.js} +1 -1
- package/dist/web/assets/tab-host-window-content-II_FtOYM.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-II_FtOYM.js.gz +0 -0
- package/dist/web/assets/{tab-store-CbPh8CAX.js → tab-store-DCpc0g0Q.js} +1 -1
- package/dist/web/assets/{terminal-tab-B4y6at4h.js → terminal-tab-CTe2polt.js} +1 -1
- package/dist/web/assets/terminal-tab-CTe2polt.js.br +0 -0
- package/dist/web/assets/terminal-tab-CTe2polt.js.gz +0 -0
- package/dist/web/assets/{tool-cards-K0r_yxI0.js → tool-cards-rxLXbBSm.js} +5 -5
- package/dist/web/assets/tool-cards-rxLXbBSm.js.br +0 -0
- package/dist/web/assets/tool-cards-rxLXbBSm.js.gz +0 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js +1 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.br +0 -0
- package/dist/web/assets/use-h264-canvas-decoder-BfO9QrlH.js.gz +0 -0
- package/dist/web/assets/{use-inline-blame-BK23icsE.js → use-inline-blame-CxZ_QWzS.js} +3 -3
- package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.br +0 -0
- package/dist/web/assets/use-inline-blame-CxZ_QWzS.js.gz +0 -0
- package/dist/web/assets/{use-lsp-CRdCPCKW.js → use-lsp-gCNE5A9K.js} +1 -1
- package/dist/web/assets/use-lsp-gCNE5A9K.js.br +0 -0
- package/dist/web/assets/use-lsp-gCNE5A9K.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-CpXT2uh3.js → use-monaco-theme-DvNGcCvz.js} +1 -1
- package/dist/web/assets/{use-process-kill-BWYX03KF.js → use-process-kill-Cbiu1mbY.js} +1 -1
- package/dist/web/assets/use-process-kill-Cbiu1mbY.js.br +0 -0
- package/dist/web/assets/use-process-kill-Cbiu1mbY.js.gz +0 -0
- package/dist/web/assets/{use-websocket-CiWQKhHr.js → use-websocket-AC7EG1LK.js} +1 -1
- package/dist/web/assets/video-preview-C2Y61WUE.js +1 -0
- package/dist/web/assets/video-preview-C2Y61WUE.js.br +0 -0
- package/dist/web/assets/video-preview-C2Y61WUE.js.gz +0 -0
- package/dist/web/assets/{voice-settings-section-Ceihggqf.js → voice-settings-section-DCXZeLYI.js} +1 -1
- package/dist/web/assets/voice-settings-section-DCXZeLYI.js.br +0 -0
- package/dist/web/assets/voice-settings-section-DCXZeLYI.js.gz +0 -0
- package/dist/web/assets/{window-pip-registry-mDgwpAwP.js → window-pip-registry-9Y9LQQYS.js} +1 -1
- package/dist/web/index.html +50 -7
- 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/docs/architecture/android-emulator-phase0-report-260921.md +310 -0
- package/docs/architecture/android-emulator-phase2-report-260921.md +163 -0
- package/docs/architecture/android-emulator-phase3-report-260921.md +203 -0
- package/docs/architecture/android-emulator-phase4-report-260921.md +191 -0
- package/docs/architecture/android-emulator-plan-260920.md +376 -0
- package/docs/architecture/plugins-and-tracing.md +48 -7
- package/package.json +4 -2
- package/scripts/gen-android-proto.ts +37 -0
- package/scripts/release.sh +3 -1
- package/src/cli/commands/chat-cmd.ts +2 -2
- package/src/providers/claude-agent-sdk-query-options.ts +28 -0
- package/src/providers/claude-agent-sdk.ts +153 -71
- package/src/providers/claude-warm-spare.ts +176 -0
- package/src/providers/provider.interface.ts +1 -0
- package/src/server/index.ts +53 -0
- package/src/server/middleware/gzip-json.ts +13 -1
- package/src/server/routes/android.ts +616 -0
- package/src/server/routes/chat.ts +31 -0
- package/src/server/routes/files.ts +52 -5
- package/src/server/routes/remote-desktop.ts +95 -2
- package/src/server/routes/trace.ts +66 -0
- package/src/server/ws/android.ts +101 -0
- package/src/server/ws/chat.ts +12 -17
- package/src/server/ws/global.ts +13 -1
- package/src/server/ws/remote-desktop.ts +6 -1
- package/src/services/android/android-apk.ts +387 -0
- package/src/services/android/android-capabilities.ts +154 -0
- package/src/services/android/android-flag.ts +12 -0
- package/src/services/android/android-grpc.ts +124 -0
- package/src/services/android/android-input.ts +129 -0
- package/src/services/android/android-logcat.ts +169 -0
- package/src/services/android/android-nonce.ts +50 -0
- package/src/services/android/android-operations.ts +124 -0
- package/src/services/android/android-screenshot.ts +53 -0
- package/src/services/android/android-session.ts +510 -0
- package/src/services/android/android-video.ts +495 -0
- package/src/services/android/avd-list.ts +131 -0
- package/src/services/android/avd-manager.ts +397 -0
- package/src/services/android/device-registry.ts +223 -0
- package/src/services/android/emulator-discovery.ts +160 -0
- package/src/services/android/emulator-launcher.ts +290 -0
- package/src/services/android/proto/emulator-controller-descriptor.ts +10 -0
- package/src/services/android/proto/emulator_controller.proto +1791 -0
- package/src/services/android/sdk-discovery.ts +178 -0
- package/src/services/android/system-images.ts +124 -0
- package/src/services/chat.service.ts +81 -4
- package/src/services/config.service.ts +1 -1
- package/src/services/file-filter.service.ts +2 -61
- package/src/services/file-glob.ts +67 -0
- package/src/services/file-index/file-index-runner.ts +169 -0
- package/src/services/file-index/file-index-worker.ts +73 -0
- package/src/services/file-index/index-search.ts +152 -0
- package/src/services/file-index/index-walk.ts +109 -0
- package/src/services/file-list-index.service.ts +189 -70
- package/src/services/file-watcher/linux-inotify.ts +238 -0
- package/src/services/file-watcher/watch-tree.ts +37 -6
- package/src/services/file-watcher.service.ts +40 -8
- package/src/services/file.service.ts +17 -6
- package/src/services/group-chat/group-chat.service.ts +1 -1
- package/src/services/jira-debug-session.service.ts +1 -1
- package/src/services/ppmbot/ppmbot-delegation.ts +1 -1
- package/src/services/ppmbot/ppmbot-service.ts +1 -1
- package/src/services/project-content-search.service.ts +45 -11
- package/src/services/proxy-agent-turn.ts +7 -3
- package/src/services/remote-desktop/mediamtx-catalog.ts +89 -0
- package/src/services/remote-desktop/mediamtx-config.ts +91 -0
- package/src/services/remote-desktop/mediamtx-install.service.ts +191 -0
- package/src/services/remote-desktop/mediamtx-paths.ts +51 -0
- package/src/services/remote-desktop/mediamtx-process.ts +152 -0
- package/src/services/remote-desktop/remote-desktop-capture-wayland.ts +312 -0
- package/src/services/remote-desktop/remote-desktop-capture.ts +60 -4
- package/src/services/remote-desktop/remote-desktop-portal.ts +529 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +56 -20
- package/src/services/remote-desktop/remote-desktop-session.ts +77 -3
- package/src/services/remote-desktop/remote-desktop-whep-registry.ts +36 -0
- package/src/services/scheduler-runner.ts +2 -3
- package/src/services/session-trace/browser-trace-ingest.ts +131 -0
- package/src/services/session-trace/session-trace-cleanup.ts +65 -0
- package/src/services/session-trace/session-trace-db.ts +89 -0
- package/src/services/session-trace/session-trace-store.ts +132 -0
- package/src/services/session-trace/trace-coalescer.ts +86 -0
- package/src/services/session-trace/trace-recorder.ts +304 -0
- package/src/services/session-trace/trace-writer.ts +83 -0
- package/src/shared/android-avd.ts +53 -0
- package/src/shared/android-protocol.ts +184 -0
- package/src/shared/file-index-limits.ts +17 -0
- package/src/shared/session-trace.ts +61 -0
- package/src/types/chat.ts +17 -0
- package/src/types/config.ts +27 -0
- package/src/types/project.ts +9 -0
- package/src/web/app.tsx +5 -0
- package/src/web/components/android/android-apk-upload.ts +75 -0
- package/src/web/components/android/android-avd-create-dialog.tsx +305 -0
- package/src/web/components/android/android-avd-destroy-dialog.tsx +151 -0
- package/src/web/components/android/android-clipboard-panel.tsx +94 -0
- package/src/web/components/android/android-controls.tsx +128 -0
- package/src/web/components/android/android-coords.ts +99 -0
- package/src/web/components/android/android-device-list.tsx +329 -0
- package/src/web/components/android/android-install-panel.tsx +280 -0
- package/src/web/components/android/android-log-filter.ts +75 -0
- package/src/web/components/android/android-logcat-panel.tsx +214 -0
- package/src/web/components/android/android-screenshot-download.ts +56 -0
- package/src/web/components/android/android-tab.tsx +66 -0
- package/src/web/components/android/android-tools-drawer.tsx +76 -0
- package/src/web/components/android/android-viewer.tsx +200 -0
- package/src/web/components/android/android-ws-url.ts +21 -0
- package/src/web/components/android/open-android.ts +21 -0
- package/src/web/components/android/use-android-available.ts +29 -0
- package/src/web/components/android/use-android-keyboard.ts +111 -0
- package/src/web/components/android/use-android-session.ts +277 -0
- package/src/web/components/android/use-android-touch.ts +123 -0
- package/src/web/components/chat/chat-tab.tsx +28 -2
- package/src/web/components/chat/message-input.tsx +3 -5
- package/src/web/components/editor/compare-picker.tsx +12 -2
- package/src/web/components/explorer/file-tree.tsx +6 -7
- package/src/web/components/explorer/use-tree-row-dnd.ts +0 -2
- package/src/web/components/floating-window/window-content-registry.ts +8 -8
- package/src/web/components/layout/command-palette.tsx +31 -9
- package/src/web/components/layout/mobile-drawer.tsx +8 -1
- package/src/web/components/layout/nav-section-rail.tsx +8 -1
- package/src/web/components/layout/reparenting-tab.tsx +2 -2
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/layout/tab-pool.tsx +31 -20
- package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +23 -1
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +64 -6
- package/src/web/components/remote-desktop/use-webrtc-canvas-video.ts +255 -0
- package/src/web/components/root-error-boundary.tsx +5 -0
- package/src/web/components/settings/remote-desktop-settings-section.tsx +176 -0
- package/src/web/components/settings/settings-categories.ts +3 -0
- package/src/web/components/settings/settings-section-content.tsx +25 -18
- package/src/web/hooks/use-chat-prewarm.ts +53 -0
- package/src/web/hooks/use-chat.ts +18 -4
- package/src/web/hooks/use-file-index-invalidation.ts +7 -5
- package/src/web/hooks/use-global-events.ts +12 -1
- package/src/web/hooks/use-remote-file-search.ts +39 -0
- package/src/web/hooks/use-ui-preload.ts +38 -0
- package/src/web/index.html +43 -0
- package/src/web/lib/chunk-recovery.ts +5 -1
- package/src/web/lib/device-id.ts +39 -0
- package/src/web/lib/feature-badges.ts +2 -0
- package/src/web/lib/lazy-with-preload.tsx +43 -0
- package/src/web/lib/preload-ui.ts +60 -0
- package/src/web/lib/score-file-search.ts +27 -14
- package/src/web/lib/tab-type-icons.ts +2 -1
- package/src/web/lib/trace-boot.ts +8 -0
- package/src/web/lib/trace-client.ts +516 -0
- package/src/web/main.tsx +12 -0
- package/src/web/stores/file-store.ts +124 -18
- package/src/web/stores/panel-utils.ts +3 -0
- package/src/web/stores/tab-store.ts +2 -1
- package/src/web/stores/window-panel-persistence.ts +3 -0
- package/vite.config.ts +3 -0
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js +0 -1
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-DEIm-W-D.js.gz +0 -0
- package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.br +0 -0
- package/dist/web/assets/agent-session-sheet-CBMsnl_p.js.gz +0 -0
- package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.br +0 -0
- package/dist/web/assets/agent-session-window-content-D9C9FA7E.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DhUVgDEG.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-CETpSpYd.js.br +0 -0
- package/dist/web/assets/ai-settings-section-CETpSpYd.js.gz +0 -0
- package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-BJUiLUb7.js.gz +0 -0
- package/dist/web/assets/apps-panel-1VElRgV3.js.br +0 -0
- package/dist/web/assets/apps-panel-1VElRgV3.js.gz +0 -0
- package/dist/web/assets/audio-preview-DYkyHu5X.js.br +0 -0
- package/dist/web/assets/audio-preview-DYkyHu5X.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js +0 -1
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js.br +0 -0
- package/dist/web/assets/branch-review-tab-DhTMSpSU.js.gz +0 -0
- package/dist/web/assets/chat-tab-Cg3BKugo.js +0 -15
- package/dist/web/assets/chat-tab-Cg3BKugo.js.br +0 -0
- package/dist/web/assets/chat-tab-Cg3BKugo.js.gz +0 -0
- package/dist/web/assets/chat-tab-tbUDK__Y.js +0 -1
- package/dist/web/assets/chat-tab-tbUDK__Y.js.br +0 -0
- package/dist/web/assets/chat-tab-tbUDK__Y.js.gz +0 -0
- package/dist/web/assets/code-editor-6ZiQehbr.js +0 -7
- package/dist/web/assets/code-editor-6ZiQehbr.js.br +0 -0
- package/dist/web/assets/code-editor-6ZiQehbr.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-Fr45FUxi.js.gz +0 -0
- package/dist/web/assets/conflict-editor-B-wdjSXI.js.br +0 -0
- package/dist/web/assets/conflict-editor-B-wdjSXI.js.gz +0 -0
- package/dist/web/assets/csv-preview-B6RodObB.js.br +0 -0
- package/dist/web/assets/csv-preview-B6RodObB.js.gz +0 -0
- package/dist/web/assets/database-viewer-CdPBUQPY.js.br +0 -0
- package/dist/web/assets/database-viewer-CdPBUQPY.js.gz +0 -0
- package/dist/web/assets/design-settings-section-BpCpaBwL.js.br +0 -0
- package/dist/web/assets/design-settings-section-BpCpaBwL.js.gz +0 -0
- package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.br +0 -0
- package/dist/web/assets/design-skill-suggestion-DkT2E2IV.js.gz +0 -0
- package/dist/web/assets/design-tab-BZb9eemI.js.br +0 -0
- package/dist/web/assets/design-tab-BZb9eemI.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-CF4tp6j-.js.gz +0 -0
- package/dist/web/assets/diff-viewer-BqEqwSw6.js.br +0 -0
- package/dist/web/assets/diff-viewer-BqEqwSw6.js.gz +0 -0
- package/dist/web/assets/edit-diff-preview-DD4HayXY.js.br +0 -0
- package/dist/web/assets/edit-diff-preview-DD4HayXY.js.gz +0 -0
- package/dist/web/assets/editor-language-service-CkA5sEdV.js.br +0 -0
- package/dist/web/assets/editor-language-service-CkA5sEdV.js.gz +0 -0
- package/dist/web/assets/explorer-body-Ds7oKuz7.js +0 -2
- package/dist/web/assets/explorer-body-Ds7oKuz7.js.br +0 -0
- package/dist/web/assets/explorer-body-Ds7oKuz7.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.br +0 -0
- package/dist/web/assets/explorer-window-content-CRJ2eMZ4.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-DwQTdCII.js.br +0 -0
- package/dist/web/assets/extension-manager-section-DwQTdCII.js.gz +0 -0
- package/dist/web/assets/extension-webview-CMq-VTLu.js.br +0 -0
- package/dist/web/assets/extension-webview-CMq-VTLu.js.gz +0 -0
- package/dist/web/assets/file-store-DY1_0nzY.js +0 -1
- package/dist/web/assets/file-store-DY1_0nzY.js.br +0 -0
- package/dist/web/assets/file-store-DY1_0nzY.js.gz +0 -0
- package/dist/web/assets/files-settings-section-B2lcyh-6.js.br +0 -0
- package/dist/web/assets/files-settings-section-B2lcyh-6.js.gz +0 -0
- package/dist/web/assets/general-settings-section-Doepm2sH.js.br +0 -0
- package/dist/web/assets/general-settings-section-Doepm2sH.js.gz +0 -0
- package/dist/web/assets/git-log-panel-CJakNMDc.js.br +0 -0
- package/dist/web/assets/git-log-panel-CJakNMDc.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-C_SeOhSq.js.br +0 -0
- package/dist/web/assets/glide-data-grid-C_SeOhSq.js.gz +0 -0
- package/dist/web/assets/group-chat-tab--by1xCQW.js +0 -1
- package/dist/web/assets/group-chat-tab--by1xCQW.js.br +0 -0
- package/dist/web/assets/group-chat-tab--by1xCQW.js.gz +0 -0
- package/dist/web/assets/index-DhWjx5iR.js +0 -52
- package/dist/web/assets/index-DhWjx5iR.js.br +0 -0
- package/dist/web/assets/index-DhWjx5iR.js.gz +0 -0
- package/dist/web/assets/index-oIvYSKTT.css +0 -1
- package/dist/web/assets/index-oIvYSKTT.css.br +0 -0
- package/dist/web/assets/index-oIvYSKTT.css.gz +0 -0
- package/dist/web/assets/keybindings-store-30LYZU0l.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BgZe3QDr.js.gz +0 -0
- package/dist/web/assets/language-servers-section-CCMzgOaE.js.br +0 -0
- package/dist/web/assets/language-servers-section-CCMzgOaE.js.gz +0 -0
- package/dist/web/assets/lsp-client-B10suuaZ.js.br +0 -0
- package/dist/web/assets/lsp-client-B10suuaZ.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-AHHFS3jc.js.br +0 -0
- package/dist/web/assets/markdown-renderer-AHHFS3jc.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-DxnXtBoj.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CqCsLWSr.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js +0 -1
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js.br +0 -0
- package/dist/web/assets/new-design-dialog-B9tgnQXq.js.gz +0 -0
- package/dist/web/assets/notification-store-HUVzO_bp.js +0 -1
- package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-CXgzH_bK.js.gz +0 -0
- package/dist/web/assets/overview-panel-BYB8mgRE.js.br +0 -0
- package/dist/web/assets/overview-panel-BYB8mgRE.js.gz +0 -0
- package/dist/web/assets/panel-store-CWHPmHwj.js +0 -1
- package/dist/web/assets/panel-store-CWHPmHwj.js.br +0 -0
- package/dist/web/assets/panel-store-CWHPmHwj.js.gz +0 -0
- package/dist/web/assets/performance-panel-Dpuq3g0W.js.br +0 -0
- package/dist/web/assets/performance-panel-Dpuq3g0W.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-DW0Gkmow.js.br +0 -0
- package/dist/web/assets/postgres-viewer-DW0Gkmow.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-DFtlGoW6.js.gz +0 -0
- package/dist/web/assets/pptx-export-CNyG32Xr.js.br +0 -0
- package/dist/web/assets/pptx-export-CNyG32Xr.js.gz +0 -0
- package/dist/web/assets/problems-panel-V5zqpuXq.js.br +0 -0
- package/dist/web/assets/problems-panel-V5zqpuXq.js.gz +0 -0
- package/dist/web/assets/process-table-DAj46HIl.js +0 -1
- package/dist/web/assets/process-table-DAj46HIl.js.br +0 -0
- package/dist/web/assets/process-table-DAj46HIl.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-B7AVlTfY.js.gz +0 -0
- package/dist/web/assets/query-audit-section-gHmRfWKr.js.br +0 -0
- package/dist/web/assets/query-audit-section-gHmRfWKr.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js +0 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Cgoznh8L.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-X12QbPhO.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DsSHawSK.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CHiQEd9Q.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-BnznIby3.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-sYDAsBMO.js.gz +0 -0
- package/dist/web/assets/send-to-chat-CCdOrKc7.js.br +0 -0
- package/dist/web/assets/send-to-chat-CCdOrKc7.js.gz +0 -0
- package/dist/web/assets/services-panel-CKa2aP1d.js.br +0 -0
- package/dist/web/assets/services-panel-CKa2aP1d.js.gz +0 -0
- package/dist/web/assets/settings-body-CEIF8lkl.js +0 -2
- package/dist/web/assets/settings-body-CEIF8lkl.js.br +0 -0
- package/dist/web/assets/settings-body-CEIF8lkl.js.gz +0 -0
- package/dist/web/assets/settings-store-DfuiLq_9.js.br +0 -0
- package/dist/web/assets/settings-store-DfuiLq_9.js.gz +0 -0
- package/dist/web/assets/settings-tab-Du5AUPni.js +0 -1
- package/dist/web/assets/settings-window-content-BTflfLez.js +0 -1
- package/dist/web/assets/slash-command-picker-CLevA_Gf.js.br +0 -0
- package/dist/web/assets/slash-command-picker-CLevA_Gf.js.gz +0 -0
- package/dist/web/assets/sql-query-editor-Db8b0jEr.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Db8b0jEr.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-VzYLMqkD.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-BR60W_mG.js.br +0 -0
- package/dist/web/assets/system-monitor-body-BR60W_mG.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-BYpantE2.js +0 -1
- package/dist/web/assets/tab-host-window-content-BL61SB5m.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-BL61SB5m.js.gz +0 -0
- package/dist/web/assets/terminal-tab-B4y6at4h.js.br +0 -0
- package/dist/web/assets/terminal-tab-B4y6at4h.js.gz +0 -0
- package/dist/web/assets/tool-cards-K0r_yxI0.js.br +0 -0
- package/dist/web/assets/tool-cards-K0r_yxI0.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-BK23icsE.js.br +0 -0
- package/dist/web/assets/use-inline-blame-BK23icsE.js.gz +0 -0
- package/dist/web/assets/use-lsp-CRdCPCKW.js.br +0 -0
- package/dist/web/assets/use-lsp-CRdCPCKW.js.gz +0 -0
- package/dist/web/assets/use-process-kill-BWYX03KF.js.br +0 -0
- package/dist/web/assets/use-process-kill-BWYX03KF.js.gz +0 -0
- package/dist/web/assets/video-preview-DugVkMB7.js +0 -1
- package/dist/web/assets/video-preview-DugVkMB7.js.br +0 -0
- package/dist/web/assets/video-preview-DugVkMB7.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-Ceihggqf.js.br +0 -0
- package/dist/web/assets/voice-settings-section-Ceihggqf.js.gz +0 -0
- /package/dist/web/assets/{__vite-browser-external-B_XMEguz.js → __vite-browser-external-C5mXParC.js} +0 -0
- /package/dist/web/assets/{agent-session-sheet-store-DO682WSg.js → agent-session-sheet-store-_OeezCXB.js} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js → api-settings-Cc_CpfmC.js} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js.br → api-settings-Cc_CpfmC.js.br} +0 -0
- /package/dist/web/assets/{api-settings-CpO8SPk9.js.gz → api-settings-Cc_CpfmC.js.gz} +0 -0
- /package/dist/web/assets/{api-themes-CJipDH_l.js → api-themes-BtMBSjuM.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js → can-open-in-ppm-DeE2-kd5.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.br → can-open-in-ppm-DeE2-kd5.js.br} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-Bw_xiOXX.js.gz → can-open-in-ppm-DeE2-kd5.js.gz} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js → csv-parser-CoxPdyLw.js} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js.br → csv-parser-CoxPdyLw.js.br} +0 -0
- /package/dist/web/assets/{csv-parser-DcRWxcNV.js.gz → csv-parser-CoxPdyLw.js.gz} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js → design-export-client-BfdZB7xK.js} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js.br → design-export-client-BfdZB7xK.js.br} +0 -0
- /package/dist/web/assets/{design-export-client-CHffN3DM.js.gz → design-export-client-BfdZB7xK.js.gz} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js → design-responsive-dialog-BiYy3ibS.js} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.br → design-responsive-dialog-BiYy3ibS.js.br} +0 -0
- /package/dist/web/assets/{design-responsive-dialog-DSCpv6O1.js.gz → design-responsive-dialog-BiYy3ibS.js.gz} +0 -0
- /package/dist/web/assets/{design-system-status-label-B_0I_3a8.js → design-system-status-label-ekiLyjKn.js} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js → docx-preview-Dt6RQpfA.js} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js.br → docx-preview-Dt6RQpfA.js.br} +0 -0
- /package/dist/web/assets/{docx-preview-B3eJyY2y.js.gz → docx-preview-Dt6RQpfA.js.gz} +0 -0
- /package/dist/web/assets/{editor-font-BNwo7zo5.js → editor-font-CBszn6vt.js} +0 -0
- /package/dist/web/assets/{format-bytes-CRZfxO2J.js → format-bytes-FQmpZ25A.js} +0 -0
- /package/dist/web/assets/{image-gallery-DC3gRAqk.js → image-gallery-B16hO6Ab.js} +0 -0
- /package/dist/web/assets/{image-preview-geometry-DN3DZQp0.js → image-preview-geometry-D4Ihz4UV.js} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js → lib-6APiJkU3.js} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js.br → lib-6APiJkU3.js.br} +0 -0
- /package/dist/web/assets/{lib-B9k5tn9s.js.gz → lib-6APiJkU3.js.gz} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js → libesm-5wREk7JN.js} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js.br → libesm-5wREk7JN.js.br} +0 -0
- /package/dist/web/assets/{libesm-5f7T4AHA.js.gz → libesm-5wREk7JN.js.gz} +0 -0
- /package/dist/web/assets/{media-url-CCGOIxzS.js → media-url-CrT-qulZ.js} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js → metric-chart-canvas-D7f-LQ8x.js} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.br → metric-chart-canvas-D7f-LQ8x.js.br} +0 -0
- /package/dist/web/assets/{metric-chart-canvas-CjFbZuhk.js.gz → metric-chart-canvas-D7f-LQ8x.js.gz} +0 -0
- /package/dist/web/assets/{monaco-dotenv-language-Bwq3gQsi.js → monaco-dotenv-language-DbSss7YR.js} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js → onboarding-entry-CGFdGLLP.js} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.br → onboarding-entry-CGFdGLLP.js.br} +0 -0
- /package/dist/web/assets/{onboarding-entry-CuH1Kqwu.js.gz → onboarding-entry-CGFdGLLP.js.gz} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js → pdf-preview-B8A4T-ES.js} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.br → pdf-preview-B8A4T-ES.js.br} +0 -0
- /package/dist/web/assets/{pdf-preview-X8bdpfE9.js.gz → pdf-preview-B8A4T-ES.js.gz} +0 -0
- /package/dist/web/assets/{separator-DMu7VFQa.js → separator-dHj3Km--.js} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js → sortable-header-BOa8o7NJ.js} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js.br → sortable-header-BOa8o7NJ.js.br} +0 -0
- /package/dist/web/assets/{sortable-header-CipIP7aH.js.gz → sortable-header-BOa8o7NJ.js.gz} +0 -0
- /package/dist/web/assets/{use-blob-url-DjuRQWnR.js → use-blob-url-BC69n15x.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js → vendor-xterm-CfasyE0m.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.br → vendor-xterm-CfasyE0m.js.br} +0 -0
- /package/dist/web/assets/{vendor-xterm-Be4L1NHd.js.gz → vendor-xterm-CfasyE0m.js.gz} +0 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ships the browser's console and its errors to the session trace (`POST /api/trace`), so a bug
|
|
3
|
+
* seen on a phone — or in any tab nobody had devtools open on — can be read on the server later,
|
|
4
|
+
* next to the chat turn the tab was showing.
|
|
5
|
+
*
|
|
6
|
+
* Every console level is sent, batched: errors within a second, everything else within a few.
|
|
7
|
+
* Each error-level entry also carries the last 50 console lines before it, so it still reads
|
|
8
|
+
* on its own when a flood got the lines around it rate-limited away.
|
|
9
|
+
*
|
|
10
|
+
* Installed by `trace-boot.ts`, the first import of `main.tsx`, so it is in the boot shell and
|
|
11
|
+
* running before the rest of the app evaluates: the failure that matters most, a chunk that
|
|
12
|
+
* will not load, is exactly what a lazily loaded reporter could never report. The case where
|
|
13
|
+
* the entry never runs at all belongs to the inline watchdog in `index.html`.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here may make things worse than the failure it reports. It never throws, never
|
|
16
|
+
* produces an unhandled rejection of its own, and caps what it records per minute — a tab has
|
|
17
|
+
* been measured throwing ~87k rejections a second, and that must cost a counter, not a network.
|
|
18
|
+
*/
|
|
19
|
+
import {
|
|
20
|
+
TRACE_INGEST_MAX_BATCH,
|
|
21
|
+
type BrowserTraceEntry,
|
|
22
|
+
type BrowserTraceType,
|
|
23
|
+
} from "../../shared/session-trace";
|
|
24
|
+
import { getDeviceId, uuidV4 } from "./device-id";
|
|
25
|
+
|
|
26
|
+
export const TRACE_QUEUE_KEY = "ppm-trace-queue";
|
|
27
|
+
export const MAX_QUEUE = 300;
|
|
28
|
+
export const MAX_BREADCRUMBS = 50;
|
|
29
|
+
export const MAX_ENTRIES_PER_MINUTE = 300;
|
|
30
|
+
/** Under the server's 16 KB per entry, with room for multi-byte characters. */
|
|
31
|
+
export const MAX_PAYLOAD_CHARS = 12_000;
|
|
32
|
+
/** Browsers refuse a keepalive request once the in-flight keepalive bodies pass 64 KB. */
|
|
33
|
+
export const KEEPALIVE_BUDGET_CHARS = 60_000;
|
|
34
|
+
export const ERROR_FLUSH_MS = 1_000;
|
|
35
|
+
export const LOG_FLUSH_MS = 5_000;
|
|
36
|
+
const MIN_BACKOFF_MS = 5_000;
|
|
37
|
+
/** How often a refused tab looks for a sign-in: a storage read, never a request. */
|
|
38
|
+
export const AUTH_RECHECK_MS = 5_000;
|
|
39
|
+
const MAX_BACKOFF_MS = 5 * 60_000;
|
|
40
|
+
const PERSIST_DEBOUNCE_MS = 1_000;
|
|
41
|
+
const MAX_PERSIST_CHARS = 256 * 1024;
|
|
42
|
+
const MAX_ARG_CHARS = 2_000;
|
|
43
|
+
const MAX_BREADCRUMB_CHARS = 300;
|
|
44
|
+
/** Nodes one logged object may cost to describe; past it, the rest is elided. */
|
|
45
|
+
const MAX_DESCRIBE_NODES = 500;
|
|
46
|
+
|
|
47
|
+
const CONSOLE_LEVELS = ["log", "info", "debug", "warn", "error"] as const;
|
|
48
|
+
export type ConsoleLevel = (typeof CONSOLE_LEVELS)[number];
|
|
49
|
+
|
|
50
|
+
const ERROR_TYPES: ReadonlySet<BrowserTraceType> = new Set([
|
|
51
|
+
"console_error",
|
|
52
|
+
"browser_error",
|
|
53
|
+
"unhandled_rejection",
|
|
54
|
+
"render_error",
|
|
55
|
+
"chunk_error",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
export interface SendResult {
|
|
59
|
+
status: number;
|
|
60
|
+
retryAfterMs?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface TraceClientDeps {
|
|
64
|
+
send: (body: string, keepalive: boolean, token: string | null) => Promise<SendResult>;
|
|
65
|
+
/** The token a request would carry now. After a 401, only a change to it is worth a retry. */
|
|
66
|
+
authToken: () => string | null;
|
|
67
|
+
storage: Pick<Storage, "getItem" | "setItem" | "removeItem"> | null;
|
|
68
|
+
now: () => number;
|
|
69
|
+
setTimer: (fn: () => void, ms: number) => unknown;
|
|
70
|
+
clearTimer: (handle: unknown) => void;
|
|
71
|
+
deviceId: () => string;
|
|
72
|
+
refId: () => string | null;
|
|
73
|
+
/** Fields stamped on every entry: which page load, which route. */
|
|
74
|
+
context: () => Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface Breadcrumb {
|
|
78
|
+
ts: number;
|
|
79
|
+
level: string;
|
|
80
|
+
text: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function cap(text: string, max: number): string {
|
|
84
|
+
return text.length <= max ? text : `${text.slice(0, max)}… [${text.length - max} more]`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function errorText(error: Error): string {
|
|
88
|
+
const head = `${error.name || "Error"}: ${error.message}`;
|
|
89
|
+
const stack = typeof error.stack === "string" ? error.stack : "";
|
|
90
|
+
// V8 starts the stack with the head; Firefox and Safari do not.
|
|
91
|
+
return stack.startsWith(head) ? stack : stack ? `${head}\n${stack}` : head;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One console argument as text, at a bounded cost however large the object logged. */
|
|
95
|
+
export function describe(value: unknown): string {
|
|
96
|
+
if (typeof value === "string") return cap(value, MAX_ARG_CHARS);
|
|
97
|
+
if (value instanceof Error) return cap(errorText(value), MAX_ARG_CHARS * 2);
|
|
98
|
+
if (value === undefined) return "undefined";
|
|
99
|
+
if (typeof value === "function") return `[function ${value.name || "anonymous"}]`;
|
|
100
|
+
if (value === null || typeof value !== "object") return String(value);
|
|
101
|
+
if (typeof Node !== "undefined" && value instanceof Node) {
|
|
102
|
+
return `<${(value as Element).tagName?.toLowerCase() ?? value.nodeName}>`;
|
|
103
|
+
}
|
|
104
|
+
if (typeof Event !== "undefined" && value instanceof Event) return describeEvent(value);
|
|
105
|
+
const seen = new WeakSet<object>();
|
|
106
|
+
let nodes = 0;
|
|
107
|
+
try {
|
|
108
|
+
const json = JSON.stringify(value, (_key, v: unknown) => {
|
|
109
|
+
if (++nodes > MAX_DESCRIBE_NODES) return "[…]";
|
|
110
|
+
if (typeof v === "bigint") return `${v}n`;
|
|
111
|
+
if (v instanceof Error) return errorText(v);
|
|
112
|
+
if (typeof v === "object" && v !== null) {
|
|
113
|
+
if (seen.has(v)) return "[circular]";
|
|
114
|
+
seen.add(v);
|
|
115
|
+
}
|
|
116
|
+
return v;
|
|
117
|
+
});
|
|
118
|
+
return cap(json ?? String(value), MAX_ARG_CHARS);
|
|
119
|
+
} catch {
|
|
120
|
+
return Object.prototype.toString.call(value);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* An Event keeps its fields on the prototype, so JSON sees only `{"isTrusted":true}` — which is
|
|
126
|
+
* all a promise rejected with a script's or a socket's `error` event used to leave in the log.
|
|
127
|
+
*/
|
|
128
|
+
function describeEvent(event: Event): string {
|
|
129
|
+
const target = event.target as (EventTarget & { tagName?: string; src?: string; href?: string; url?: string }) | null;
|
|
130
|
+
let on = "";
|
|
131
|
+
if (target) {
|
|
132
|
+
const name = target.tagName ? `<${target.tagName.toLowerCase()}>` : (target.constructor?.name ?? "");
|
|
133
|
+
const where = target.src || target.href || target.url || "";
|
|
134
|
+
on = ` on ${name}${where ? ` ${where}` : ""}`;
|
|
135
|
+
}
|
|
136
|
+
return cap(`[${event.constructor?.name ?? "Event"} ${event.type}${on}]`, MAX_ARG_CHARS);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A thrown value — an Error or anything else — as the fields an error entry carries. */
|
|
140
|
+
export function errorPayload(value: unknown, extra: Record<string, unknown> = {}): Record<string, unknown> {
|
|
141
|
+
if (value instanceof Error) {
|
|
142
|
+
return { name: value.name, message: cap(value.message, MAX_ARG_CHARS), stack: cap(errorText(value), MAX_ARG_CHARS * 3), ...extra };
|
|
143
|
+
}
|
|
144
|
+
return { message: describe(value), ...extra };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Shrink a payload until it fits one entry: oldest breadcrumbs first, then long strings, then a head. */
|
|
148
|
+
function fit(payload: Record<string, unknown>): Record<string, unknown> {
|
|
149
|
+
let json = JSON.stringify(payload);
|
|
150
|
+
if (json.length <= MAX_PAYLOAD_CHARS) return payload;
|
|
151
|
+
const out = { ...payload };
|
|
152
|
+
if (Array.isArray(out.breadcrumbs)) {
|
|
153
|
+
const crumbs = [...(out.breadcrumbs as unknown[])];
|
|
154
|
+
while (crumbs.length > 0 && json.length > MAX_PAYLOAD_CHARS) {
|
|
155
|
+
crumbs.shift();
|
|
156
|
+
out.breadcrumbs = crumbs;
|
|
157
|
+
json = JSON.stringify(out);
|
|
158
|
+
}
|
|
159
|
+
if (json.length <= MAX_PAYLOAD_CHARS) return out;
|
|
160
|
+
}
|
|
161
|
+
for (const [k, v] of Object.entries(out)) {
|
|
162
|
+
if (typeof v === "string" && v.length > 1_000) out[k] = cap(v, 1_000);
|
|
163
|
+
if (Array.isArray(v) && k === "args") out[k] = v.map((a) => (typeof a === "string" ? cap(a, 1_000) : a));
|
|
164
|
+
}
|
|
165
|
+
json = JSON.stringify(out);
|
|
166
|
+
if (json.length <= MAX_PAYLOAD_CHARS) return out;
|
|
167
|
+
return { truncated: true, head: json.slice(0, MAX_PAYLOAD_CHARS - 200) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface TraceClient {
|
|
171
|
+
record(type: BrowserTraceType, payload: Record<string, unknown>): void;
|
|
172
|
+
console(level: ConsoleLevel, args: unknown[]): void;
|
|
173
|
+
flush(): Promise<void>;
|
|
174
|
+
/** The page is going away: send what fits in one keepalive request, persist the rest. */
|
|
175
|
+
flushOnHide(): void;
|
|
176
|
+
pending(): number;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function createTraceClient(deps: TraceClientDeps): TraceClient {
|
|
180
|
+
let queue: BrowserTraceEntry[] = restore();
|
|
181
|
+
/** Entries in a request that has not answered yet — never put in a second one. */
|
|
182
|
+
const sending = new Set<BrowserTraceEntry>();
|
|
183
|
+
const breadcrumbs: Breadcrumb[] = [];
|
|
184
|
+
let flushTimer: unknown = null;
|
|
185
|
+
let flushDueAt = Infinity;
|
|
186
|
+
let persistTimer: unknown = null;
|
|
187
|
+
let inFlight = false;
|
|
188
|
+
let backoffMs = 0;
|
|
189
|
+
let blockedUntil = 0;
|
|
190
|
+
/** The token a 401 answered; undefined while nothing has been refused. */
|
|
191
|
+
let rejectedToken: string | null | undefined;
|
|
192
|
+
let windowStart = deps.now();
|
|
193
|
+
let windowCount = 0;
|
|
194
|
+
let droppedInWindow = 0;
|
|
195
|
+
|
|
196
|
+
function restore(): BrowserTraceEntry[] {
|
|
197
|
+
try {
|
|
198
|
+
const raw = deps.storage?.getItem(TRACE_QUEUE_KEY);
|
|
199
|
+
const parsed = raw ? (JSON.parse(raw) as unknown) : null;
|
|
200
|
+
return Array.isArray(parsed) ? (parsed as BrowserTraceEntry[]).slice(-MAX_QUEUE) : [];
|
|
201
|
+
} catch {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function persistNow(): void {
|
|
207
|
+
if (persistTimer != null) { deps.clearTimer(persistTimer); persistTimer = null; }
|
|
208
|
+
try {
|
|
209
|
+
if (queue.length === 0) { deps.storage?.removeItem(TRACE_QUEUE_KEY); return; }
|
|
210
|
+
let kept = queue;
|
|
211
|
+
let json = JSON.stringify(kept);
|
|
212
|
+
while (json.length > MAX_PERSIST_CHARS && kept.length > 1) {
|
|
213
|
+
kept = kept.slice(Math.ceil(kept.length / 4));
|
|
214
|
+
json = JSON.stringify(kept);
|
|
215
|
+
}
|
|
216
|
+
deps.storage?.setItem(TRACE_QUEUE_KEY, json);
|
|
217
|
+
} catch {
|
|
218
|
+
// Quota or disabled storage: the in-memory queue is still sent.
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function schedulePersist(): void {
|
|
223
|
+
if (persistTimer != null) return;
|
|
224
|
+
persistTimer = deps.setTimer(() => { persistTimer = null; persistNow(); }, PERSIST_DEBOUNCE_MS);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function schedule(ms: number): void {
|
|
228
|
+
const due = deps.now() + ms;
|
|
229
|
+
if (flushTimer != null && flushDueAt <= due) return;
|
|
230
|
+
if (flushTimer != null) deps.clearTimer(flushTimer);
|
|
231
|
+
flushDueAt = due;
|
|
232
|
+
flushTimer = deps.setTimer(() => {
|
|
233
|
+
flushTimer = null;
|
|
234
|
+
flushDueAt = Infinity;
|
|
235
|
+
void flush();
|
|
236
|
+
}, ms);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function refId(): string | null {
|
|
240
|
+
try { return deps.refId(); } catch { return null; }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function token(): string | null {
|
|
244
|
+
try { return deps.authToken(); } catch { return null; }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Refused, and no sign-in since: a request would only be refused again. */
|
|
248
|
+
function awaitingSignIn(current: string | null): boolean {
|
|
249
|
+
return rejectedToken !== undefined && current === rejectedToken;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function push(type: BrowserTraceType, payload: Record<string, unknown>, now: number): void {
|
|
253
|
+
let context: Record<string, unknown> = {};
|
|
254
|
+
try { context = deps.context(); } catch { /* entry without context */ }
|
|
255
|
+
queue.push({ ts: now, type, refId: refId(), payload: fit({ ...context, ...payload }) });
|
|
256
|
+
if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
|
|
257
|
+
schedulePersist();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The per-minute budget. Returns false once spent, counting what it turned away. */
|
|
261
|
+
function admit(now: number): boolean {
|
|
262
|
+
if (now - windowStart >= 60_000) {
|
|
263
|
+
const dropped = droppedInWindow;
|
|
264
|
+
windowStart = now;
|
|
265
|
+
windowCount = 0;
|
|
266
|
+
droppedInWindow = 0;
|
|
267
|
+
if (dropped > 0) {
|
|
268
|
+
windowCount++;
|
|
269
|
+
push("console_dropped", { count: dropped }, now);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (windowCount >= MAX_ENTRIES_PER_MINUTE) {
|
|
273
|
+
droppedInWindow++;
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
windowCount++;
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function record(type: BrowserTraceType, payload: Record<string, unknown>): void {
|
|
281
|
+
try {
|
|
282
|
+
const now = deps.now();
|
|
283
|
+
if (!admit(now)) return;
|
|
284
|
+
const isError = ERROR_TYPES.has(type);
|
|
285
|
+
push(type, isError ? { ...payload, breadcrumbs: breadcrumbs.slice() } : payload, now);
|
|
286
|
+
schedule(isError ? ERROR_FLUSH_MS : LOG_FLUSH_MS);
|
|
287
|
+
} catch {
|
|
288
|
+
// Recording must never be the thing that breaks the page.
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function consoleLine(level: ConsoleLevel, args: unknown[]): void {
|
|
293
|
+
try {
|
|
294
|
+
const now = deps.now();
|
|
295
|
+
if (!admit(now)) return;
|
|
296
|
+
const texts = args.map(describe);
|
|
297
|
+
const type = `console_${level}` as BrowserTraceType;
|
|
298
|
+
const isError = ERROR_TYPES.has(type);
|
|
299
|
+
// The line itself is not its own breadcrumb: breadcrumbs are what came before.
|
|
300
|
+
push(type, isError ? { args: texts, breadcrumbs: breadcrumbs.slice() } : { args: texts }, now);
|
|
301
|
+
breadcrumbs.push({ ts: now, level, text: cap(texts.join(" "), MAX_BREADCRUMB_CHARS) });
|
|
302
|
+
if (breadcrumbs.length > MAX_BREADCRUMBS) breadcrumbs.shift();
|
|
303
|
+
schedule(isError ? ERROR_FLUSH_MS : LOG_FLUSH_MS);
|
|
304
|
+
} catch {
|
|
305
|
+
// See record().
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function takeBatch(budgetChars: number): BrowserTraceEntry[] {
|
|
310
|
+
const batch: BrowserTraceEntry[] = [];
|
|
311
|
+
let chars = 200;
|
|
312
|
+
for (const entry of queue) {
|
|
313
|
+
if (sending.has(entry)) continue;
|
|
314
|
+
if (batch.length >= TRACE_INGEST_MAX_BATCH) break;
|
|
315
|
+
const size = JSON.stringify(entry).length + 1;
|
|
316
|
+
if (batch.length > 0 && chars + size > budgetChars) break;
|
|
317
|
+
batch.push(entry);
|
|
318
|
+
chars += size;
|
|
319
|
+
}
|
|
320
|
+
return batch;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function body(batch: BrowserTraceEntry[]): string {
|
|
324
|
+
return JSON.stringify({ deviceId: deps.deviceId(), sentAt: deps.now(), entries: batch });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function remove(batch: BrowserTraceEntry[]): void {
|
|
328
|
+
const sent = new Set(batch);
|
|
329
|
+
queue = queue.filter((e) => !sent.has(e));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function backOff(retryAfter?: number): void {
|
|
333
|
+
backoffMs = Math.min(MAX_BACKOFF_MS, Math.max(MIN_BACKOFF_MS, backoffMs * 2));
|
|
334
|
+
blockedUntil = deps.now() + Math.max(backoffMs, retryAfter ?? 0);
|
|
335
|
+
schedule(blockedUntil - deps.now());
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Worth another try: offline, rate-limited, or a server in trouble. */
|
|
339
|
+
function retryable(status: number): boolean {
|
|
340
|
+
return status === 0 || status === 429 || status >= 500;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function flush(): Promise<void> {
|
|
344
|
+
try {
|
|
345
|
+
if (queue.length === 0) return;
|
|
346
|
+
const now = deps.now();
|
|
347
|
+
if (inFlight || now < blockedUntil) {
|
|
348
|
+
schedule(Math.max(blockedUntil - now, ERROR_FLUSH_MS));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const auth = token();
|
|
352
|
+
if (awaitingSignIn(auth)) {
|
|
353
|
+
schedule(AUTH_RECHECK_MS);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const batch = takeBatch(Infinity);
|
|
357
|
+
if (batch.length === 0) return;
|
|
358
|
+
inFlight = true;
|
|
359
|
+
for (const e of batch) sending.add(e);
|
|
360
|
+
let result: SendResult;
|
|
361
|
+
try {
|
|
362
|
+
result = await deps.send(body(batch), false, auth);
|
|
363
|
+
} catch {
|
|
364
|
+
result = { status: 0 };
|
|
365
|
+
} finally {
|
|
366
|
+
inFlight = false;
|
|
367
|
+
for (const e of batch) sending.delete(e);
|
|
368
|
+
}
|
|
369
|
+
if (result.status >= 200 && result.status < 300) {
|
|
370
|
+
remove(batch);
|
|
371
|
+
backoffMs = 0;
|
|
372
|
+
blockedUntil = 0;
|
|
373
|
+
rejectedToken = undefined;
|
|
374
|
+
if (queue.length > 0) schedule(0);
|
|
375
|
+
} else if (result.status === 401) {
|
|
376
|
+
// Logged out, or a token that stopped working: kept for the sign-in that replaces it,
|
|
377
|
+
// so a fresh device's login screen is not the one page that cannot be debugged. The
|
|
378
|
+
// rows still only ever reach the server authenticated.
|
|
379
|
+
rejectedToken = auth;
|
|
380
|
+
schedule(AUTH_RECHECK_MS);
|
|
381
|
+
} else if (retryable(result.status)) {
|
|
382
|
+
backOff(result.retryAfterMs);
|
|
383
|
+
} else {
|
|
384
|
+
// 400/403/413: this batch will never be accepted, and retrying it forever would pin
|
|
385
|
+
// the queue.
|
|
386
|
+
remove(batch);
|
|
387
|
+
}
|
|
388
|
+
schedulePersist();
|
|
389
|
+
} catch {
|
|
390
|
+
inFlight = false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function flushOnHide(): void {
|
|
395
|
+
try {
|
|
396
|
+
const auth = token();
|
|
397
|
+
// Refused already: storage carries them to the next load instead.
|
|
398
|
+
if (queue.length > 0 && !awaitingSignIn(auth)) {
|
|
399
|
+
const batch = takeBatch(KEEPALIVE_BUDGET_CHARS);
|
|
400
|
+
if (batch.length > 0) {
|
|
401
|
+
remove(batch);
|
|
402
|
+
// Removed first so a page that does unload does not resend them from storage on the
|
|
403
|
+
// next load; put back if this page lives on and the request turns out to have failed.
|
|
404
|
+
const putBack = (status: number, retryAfter?: number) => {
|
|
405
|
+
queue.unshift(...batch);
|
|
406
|
+
if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
|
|
407
|
+
schedulePersist();
|
|
408
|
+
if (status === 401) {
|
|
409
|
+
rejectedToken = auth;
|
|
410
|
+
schedule(AUTH_RECHECK_MS);
|
|
411
|
+
} else {
|
|
412
|
+
backOff(retryAfter);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
deps.send(body(batch), true, auth)
|
|
416
|
+
.then((r) => { if (r.status === 401 || retryable(r.status)) putBack(r.status, r.retryAfterMs); })
|
|
417
|
+
.catch(() => putBack(0));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
persistNow();
|
|
421
|
+
} catch {
|
|
422
|
+
// Unloading; nothing left to do.
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (queue.length > 0) schedule(2_000);
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
record,
|
|
430
|
+
console: consoleLine,
|
|
431
|
+
flush,
|
|
432
|
+
flushOnHide,
|
|
433
|
+
pending: () => queue.length,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Wrap each console method so the line also reaches the client. The original always runs first
|
|
439
|
+
* and with the same arguments; a line produced while recording one is not recorded again.
|
|
440
|
+
*/
|
|
441
|
+
export function patchConsole(target: Console, client: Pick<TraceClient, "console">): void {
|
|
442
|
+
let recording = false;
|
|
443
|
+
for (const level of CONSOLE_LEVELS) {
|
|
444
|
+
const original = target[level];
|
|
445
|
+
if (typeof original !== "function") continue;
|
|
446
|
+
target[level] = function (this: Console, ...args: unknown[]) {
|
|
447
|
+
original.apply(this, args);
|
|
448
|
+
if (recording) return;
|
|
449
|
+
recording = true;
|
|
450
|
+
try { client.console(level, args); } finally { recording = false; }
|
|
451
|
+
} as Console[typeof level];
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
let installed: TraceClient | null = null;
|
|
456
|
+
let refResolver: () => string | null = () => null;
|
|
457
|
+
|
|
458
|
+
/** Which chat session the tab is showing, so its rows join that session's trace. Set by the app. */
|
|
459
|
+
export function setTraceRefResolver(resolver: () => string | null): void {
|
|
460
|
+
refResolver = resolver;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Report a failure the app caught itself (the root boundary, chunk recovery). No-op until installed. */
|
|
464
|
+
export function reportTraceError(type: "render_error" | "chunk_error", error: unknown, extra: Record<string, unknown> = {}): void {
|
|
465
|
+
installed?.record(type, errorPayload(error, extra));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function retryAfterMs(res: Response): number | undefined {
|
|
469
|
+
const s = Number(res.headers.get("retry-after"));
|
|
470
|
+
return Number.isFinite(s) && s > 0 ? s * 1000 : undefined;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function installTraceClient(): void {
|
|
474
|
+
if (installed || typeof window === "undefined" || typeof document === "undefined") return;
|
|
475
|
+
const pageId = uuidV4();
|
|
476
|
+
let storage: TraceClientDeps["storage"] = null;
|
|
477
|
+
try { storage = window.localStorage; } catch { /* private mode */ }
|
|
478
|
+
|
|
479
|
+
const client = createTraceClient({
|
|
480
|
+
send: async (payload, keepalive, token) => {
|
|
481
|
+
const res = await fetch("/api/trace", {
|
|
482
|
+
method: "POST",
|
|
483
|
+
keepalive,
|
|
484
|
+
headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) },
|
|
485
|
+
body: payload,
|
|
486
|
+
});
|
|
487
|
+
return { status: res.status, retryAfterMs: retryAfterMs(res) };
|
|
488
|
+
},
|
|
489
|
+
authToken: () => storage?.getItem("ppm-auth-token") ?? null,
|
|
490
|
+
storage,
|
|
491
|
+
now: () => Date.now(),
|
|
492
|
+
setTimer: (fn, ms) => window.setTimeout(fn, ms),
|
|
493
|
+
clearTimer: (handle) => window.clearTimeout(handle as number),
|
|
494
|
+
deviceId: getDeviceId,
|
|
495
|
+
refId: () => refResolver(),
|
|
496
|
+
context: () => ({ page: pageId, path: location.pathname }),
|
|
497
|
+
});
|
|
498
|
+
installed = client;
|
|
499
|
+
|
|
500
|
+
patchConsole(console, client);
|
|
501
|
+
window.addEventListener("error", (event) => {
|
|
502
|
+
client.record("browser_error", errorPayload(event.error ?? event.message, {
|
|
503
|
+
...(event.filename ? { source: event.filename, line: event.lineno, col: event.colno } : {}),
|
|
504
|
+
ua: navigator.userAgent,
|
|
505
|
+
}));
|
|
506
|
+
});
|
|
507
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
508
|
+
client.record("unhandled_rejection", errorPayload(event.reason, { ua: navigator.userAgent }));
|
|
509
|
+
});
|
|
510
|
+
// Mobile browsers often skip pagehide; `hidden` is the last event they reliably deliver.
|
|
511
|
+
window.addEventListener("pagehide", () => client.flushOnHide());
|
|
512
|
+
document.addEventListener("visibilitychange", () => {
|
|
513
|
+
if (document.visibilityState === "hidden") client.flushOnHide();
|
|
514
|
+
});
|
|
515
|
+
window.addEventListener("online", () => { void client.flush(); });
|
|
516
|
+
}
|
package/src/web/main.tsx
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
// First, so browser logging is running before anything below evaluates — see trace-boot.ts.
|
|
2
|
+
import "./lib/trace-boot.ts";
|
|
1
3
|
import { StrictMode } from "react";
|
|
2
4
|
import { createRoot } from "react-dom/client";
|
|
3
5
|
import { App } from "./app.tsx";
|
|
6
|
+
import { setTraceRefResolver } from "./lib/trace-client.ts";
|
|
7
|
+
import { tabSessionId } from "./lib/tab-session-id.ts";
|
|
8
|
+
import { useTabStore } from "./stores/tab-store.ts";
|
|
4
9
|
import { RootErrorBoundary } from "./components/root-error-boundary.tsx";
|
|
5
10
|
import { installChunkErrorRecovery } from "./lib/chunk-recovery.ts";
|
|
6
11
|
// Self-hosted, because a font stack is only a wish list: -apple-system and
|
|
@@ -48,6 +53,13 @@ if (typeof Node !== "undefined") {
|
|
|
48
53
|
// mounting is caught too.
|
|
49
54
|
installChunkErrorRecovery();
|
|
50
55
|
|
|
56
|
+
// Browser log rows carry the chat session the focused tab shows, which is what joins
|
|
57
|
+
// them to that session's trace on the server.
|
|
58
|
+
setTraceRefResolver(() => {
|
|
59
|
+
const { tabs, activeTabId } = useTabStore.getState();
|
|
60
|
+
return tabSessionId(tabs.find((t) => t.id === activeTabId)) ?? null;
|
|
61
|
+
});
|
|
62
|
+
|
|
51
63
|
createRoot(document.getElementById("root")!).render(
|
|
52
64
|
<StrictMode>
|
|
53
65
|
<RootErrorBoundary>
|