@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
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import type { StripMode } from "../services/transcript-images.ts";
|
|
2
2
|
import {
|
|
3
3
|
query,
|
|
4
|
+
type Query,
|
|
4
5
|
listSessions as sdkListSessions,
|
|
5
6
|
getSessionInfo as sdkGetSessionInfo,
|
|
6
7
|
getSessionMessages,
|
|
7
8
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
8
|
-
import { buildModelQueryOptions, buildSystemPromptOption, designMcpServers, preToolUseDecision } from "./claude-agent-sdk-query-options.ts";
|
|
9
|
+
import { allowedToolsFor, buildModelQueryOptions, buildSystemPromptOption, designMcpServers, preToolUseDecision, READ_ONLY_TOOLS } from "./claude-agent-sdk-query-options.ts";
|
|
10
|
+
import { WarmSpares, spawnFingerprint } from "./claude-warm-spare.ts";
|
|
9
11
|
import { CLAUDE_DESIGN_CHECK_TOOL } from "../services/design/mcp/design-mcp-tool.ts";
|
|
10
12
|
import { designToolDecision } from "../services/design/design-tool-policy.ts";
|
|
11
13
|
import { CLAUDE_MODELS } from "../types/claude-models.ts";
|
|
12
14
|
import { isImageLimitRejection } from "./image-limit-detection.ts";
|
|
13
15
|
import type {
|
|
14
16
|
AIProvider,
|
|
17
|
+
PrewarmInput,
|
|
15
18
|
Session,
|
|
16
19
|
SessionConfig,
|
|
17
20
|
SessionInfo,
|
|
@@ -38,7 +41,7 @@ import { isCompiledBinary } from "../services/autostart-generator.ts";
|
|
|
38
41
|
import { resolveClaudeCliPath } from "../services/claude-cli-resolver.ts";
|
|
39
42
|
import { resolve, dirname } from "node:path";
|
|
40
43
|
import { existsSync, readdirSync, unlinkSync, readFileSync, statSync } from "node:fs";
|
|
41
|
-
import { homedir } from "node:os";
|
|
44
|
+
import { homedir, totalmem } from "node:os";
|
|
42
45
|
|
|
43
46
|
const CLAUDE_PROJECTS_DIR = resolve(homedir(), ".claude/projects");
|
|
44
47
|
|
|
@@ -238,6 +241,10 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
238
241
|
* and this is the only place that knows what made it do so.
|
|
239
242
|
*/
|
|
240
243
|
private teardownReasons = new Map<string, string>();
|
|
244
|
+
/** CLIs started for chats that do not exist yet — see `prewarm`. */
|
|
245
|
+
private readonly warmSpares = new WarmSpares<Query>();
|
|
246
|
+
/** Hosts with less RAM keep no warm CLI: each one holds ~500 MB while it waits. */
|
|
247
|
+
private minWarmSpareHostMemory = 8 * 1024 ** 3;
|
|
241
248
|
|
|
242
249
|
/** Auth-related env keys for diagnostic logging */
|
|
243
250
|
private readonly AUTH_ENV_KEYS = ["ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN"];
|
|
@@ -510,7 +517,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
510
517
|
}
|
|
511
518
|
|
|
512
519
|
async createSession(config: SessionConfig): Promise<Session> {
|
|
513
|
-
const id = crypto.randomUUID();
|
|
520
|
+
const id = (config.adoptWarmSpare && config.projectPath && this.warmSpares.claim(config.projectPath)) || crypto.randomUUID();
|
|
514
521
|
const meta: Session = {
|
|
515
522
|
id,
|
|
516
523
|
providerId: this.id,
|
|
@@ -811,6 +818,131 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
811
818
|
return process.env.CLAUDE_CODE_OAUTH_TOKEN ? SUBSCRIPTION_PROMPT_CACHE_TTL_MS : API_KEY_PROMPT_CACHE_TTL_MS;
|
|
812
819
|
}
|
|
813
820
|
|
|
821
|
+
/**
|
|
822
|
+
* What a turn spawns the CLI with. `sendMessage` and `prewarm` both build it here, so a
|
|
823
|
+
* warm spare is only ever taken over by a turn that would have started that same process
|
|
824
|
+
* (`spawnFingerprint` compares the two).
|
|
825
|
+
*/
|
|
826
|
+
private buildQueryOptions(p: {
|
|
827
|
+
sessionId?: string;
|
|
828
|
+
resume?: string;
|
|
829
|
+
forkSession?: boolean;
|
|
830
|
+
cwd: string;
|
|
831
|
+
systemPrompt: ReturnType<typeof buildSystemPromptOption>;
|
|
832
|
+
env: Record<string, string | undefined>;
|
|
833
|
+
allowedTools: string[];
|
|
834
|
+
mcpServers: Record<string, unknown>;
|
|
835
|
+
permissionMode: string;
|
|
836
|
+
opts?: Pick<import("./provider.interface.ts").SendMessageOpts, "model" | "oneMContext" | "effort" | "thinkingBudget" | "maxTurns">;
|
|
837
|
+
providerConfig: Partial<import("../types/config.ts").AIProviderConfig>;
|
|
838
|
+
stderr: (chunk: string) => void;
|
|
839
|
+
}): Record<string, any> {
|
|
840
|
+
// 1M context (GA): the CLI enables a 1M window when the model name carries a
|
|
841
|
+
// [1m] suffix. The suffix is stripped before the API call. Requires an entitled
|
|
842
|
+
// account (Max/Team/Enterprise) and a supported model; otherwise the API errors.
|
|
843
|
+
// Per-call overrides win over provider config (lightweight calls can opt out of 1M;
|
|
844
|
+
// the chat input picker sets per-session model/effort/thinking). Effort enum is
|
|
845
|
+
// guarded inside the helper — "extra" would crash the CLI subprocess.
|
|
846
|
+
const mqo = buildModelQueryOptions(
|
|
847
|
+
{
|
|
848
|
+
model: p.opts?.model,
|
|
849
|
+
oneMContext: p.opts?.oneMContext,
|
|
850
|
+
effort: p.opts?.effort,
|
|
851
|
+
thinkingBudget: p.opts?.thinkingBudget,
|
|
852
|
+
},
|
|
853
|
+
p.providerConfig,
|
|
854
|
+
);
|
|
855
|
+
|
|
856
|
+
// Compiled binaries have no node_modules → resolve a Claude CLI explicitly
|
|
857
|
+
// (system claude or the one shipped in cli/). Source installs → undefined
|
|
858
|
+
// (SDK self-resolves from node_modules, unchanged).
|
|
859
|
+
const cliExecutablePath = resolveCliExecutablePath(
|
|
860
|
+
(p.providerConfig as { cli_command?: string }).cli_command,
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
return {
|
|
864
|
+
// Run the CLI under node only for a .js entry (or the win32 source-mode
|
|
865
|
+
// default). A native claude(.exe) must be spawned directly.
|
|
866
|
+
...(needsNodeInterpreter(process.platform, cliExecutablePath) && { executable: "node" }),
|
|
867
|
+
...(cliExecutablePath && { pathToClaudeCodeExecutable: cliExecutablePath }),
|
|
868
|
+
sessionId: p.sessionId,
|
|
869
|
+
resume: p.resume,
|
|
870
|
+
...(p.forkSession && { forkSession: true }),
|
|
871
|
+
cwd: p.cwd,
|
|
872
|
+
systemPrompt: p.systemPrompt,
|
|
873
|
+
settingSources: ["user", "project"],
|
|
874
|
+
env: p.env,
|
|
875
|
+
settings: { permissions: { allow: [], deny: [] } },
|
|
876
|
+
allowedTools: p.allowedTools,
|
|
877
|
+
...(Object.keys(p.mcpServers).length > 0 && { mcpServers: p.mcpServers }),
|
|
878
|
+
permissionMode: p.permissionMode,
|
|
879
|
+
allowDangerouslySkipPermissions: p.permissionMode === "bypassPermissions",
|
|
880
|
+
...(mqo.model && { model: mqo.model }),
|
|
881
|
+
...(mqo.effort && { effort: mqo.effort }),
|
|
882
|
+
maxTurns: p.opts?.maxTurns ?? p.providerConfig.max_turns ?? 1000,
|
|
883
|
+
...(p.providerConfig.max_budget_usd && { maxBudgetUsd: p.providerConfig.max_budget_usd }),
|
|
884
|
+
...(mqo.thinking && { thinking: mqo.thinking }),
|
|
885
|
+
// Beta headers are honored only for API-key auth; OAuth/subscription sessions
|
|
886
|
+
// reject them ("Custom betas are only available for API key users") and crash the
|
|
887
|
+
// subprocess. Entitled OAuth accounts still get 1M context via the [1m] model suffix.
|
|
888
|
+
...(mqo.use1m && !!p.env.ANTHROPIC_API_KEY && { betas: ["context-1m-2025-08-07"] }),
|
|
889
|
+
includePartialMessages: true,
|
|
890
|
+
stderr: p.stderr,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Start the CLI the next chat created in `projectPath` will run on, so that chat's first
|
|
896
|
+
* message does not wait ~1.4 s for it to boot (`claude-warm-spare.ts` has the numbers).
|
|
897
|
+
* Given the settings the message will carry, the options are built exactly as
|
|
898
|
+
* `sendMessage` builds them; anything this cannot foresee only makes that turn start cold.
|
|
899
|
+
*/
|
|
900
|
+
async prewarm(input: PrewarmInput): Promise<void> {
|
|
901
|
+
const { projectPath, accountId, opts } = input;
|
|
902
|
+
if (!existsSync(projectPath) || totalmem() < this.minWarmSpareHostMemory) return;
|
|
903
|
+
const providerConfig = this.getProviderConfig();
|
|
904
|
+
const permissionMode = opts?.permissionMode || providerConfig.permission_mode || "bypassPermissions";
|
|
905
|
+
const isBypass = permissionMode === "bypassPermissions";
|
|
906
|
+
|
|
907
|
+
let account: AccountWithTokens | null = null;
|
|
908
|
+
if (accountSelector.isEnabled()) {
|
|
909
|
+
// The session will be bound to the account its tab claimed, and its turn runs on that
|
|
910
|
+
// one. Without a claim the turn's own pick cannot be foreseen, so nothing is started.
|
|
911
|
+
if (!accountId || !accountSelector.canServe(accountId)) return;
|
|
912
|
+
account = await accountService.ensureFreshToken(accountId);
|
|
913
|
+
if (!account) return;
|
|
914
|
+
// What `sendMessage` does too: a background refresh may have rotated the token.
|
|
915
|
+
account = accountService.getWithTokens(accountId) ?? account;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const options = this.buildQueryOptions({
|
|
919
|
+
cwd: projectPath,
|
|
920
|
+
systemPrompt: buildSystemPromptOption(providerConfig.system_prompt),
|
|
921
|
+
env: this.buildQueryEnv(projectPath, account),
|
|
922
|
+
allowedTools: allowedToolsFor({ isBypass, agentTeams: providerConfig.agent_teams }),
|
|
923
|
+
mcpServers: this.resolveMcpServers(projectPath),
|
|
924
|
+
permissionMode,
|
|
925
|
+
opts,
|
|
926
|
+
providerConfig,
|
|
927
|
+
stderr: () => {},
|
|
928
|
+
});
|
|
929
|
+
this.warmSpares.offer(projectPath, spawnFingerprint(options), (sessionId, callbacks) => {
|
|
930
|
+
const { generator, controller } = createMessageChannel();
|
|
931
|
+
const q = query({
|
|
932
|
+
prompt: generator,
|
|
933
|
+
options: {
|
|
934
|
+
...options,
|
|
935
|
+
sessionId,
|
|
936
|
+
stderr: callbacks.stderr,
|
|
937
|
+
...(!isBypass && { hooks: { PreToolUse: [{ matcher: ".*", hooks: [callbacks.preToolUse] }] } }),
|
|
938
|
+
canUseTool: callbacks.canUseTool,
|
|
939
|
+
} as any,
|
|
940
|
+
});
|
|
941
|
+
console.log(`[sdk] session=${sessionId} warm CLI started for ${projectPath}`);
|
|
942
|
+
return { query: q, controller };
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
814
946
|
async *sendMessage(
|
|
815
947
|
_sessionId: string,
|
|
816
948
|
message: string,
|
|
@@ -899,26 +1031,9 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
899
1031
|
// No project root means nothing can be proven inside it, so every file tool asks.
|
|
900
1032
|
const designRoot = designPolicy && meta.projectPath && existsSync(meta.projectPath) ? meta.projectPath : undefined;
|
|
901
1033
|
|
|
902
|
-
// Build allowedTools based on permission mode.
|
|
903
|
-
// SDK auto-approves everything in allowedTools (skips canUseTool callback).
|
|
904
|
-
// In non-bypass modes, only pre-approve read-only tools so write/execute tools
|
|
905
|
-
// go through the permission evaluation chain → canUseTool callback.
|
|
906
|
-
// The design policy pre-approves nothing: the read-only list would let Read and Grep
|
|
907
|
-
// reach any path on disk and every MCP tool run unasked, which is exactly what a
|
|
908
|
-
// design session's agent (fed page content it did not write) must not do.
|
|
909
|
-
const readOnlyTools = ["Read", "Glob", "Grep", "WebSearch", "WebFetch", "ToolSearch"];
|
|
910
|
-
const writeTools = ["Write", "Edit", "Bash", "Agent", "Skill", "TodoWrite", "AskUserQuestion"];
|
|
911
|
-
const teamTools = providerConfig.agent_teams
|
|
912
|
-
? ["TeamCreate", "TeamDelete", "SendMessage", "TaskCreate", "TaskUpdate", "TaskList", "TaskGet"]
|
|
913
|
-
: [];
|
|
914
|
-
const mcpTools = ["mcp__*"];
|
|
915
1034
|
// `design_check` only reads the canvas, so a design session never asks before it runs.
|
|
916
1035
|
const designCheckTool = opts?.designSession && opts.designMcp ? CLAUDE_DESIGN_CHECK_TOOL : null;
|
|
917
|
-
const allowedTools = designPolicy
|
|
918
|
-
? (designCheckTool ? [designCheckTool] : [])
|
|
919
|
-
: isBypass
|
|
920
|
-
? [...readOnlyTools, ...writeTools, ...teamTools, ...mcpTools]
|
|
921
|
-
: [...readOnlyTools, ...mcpTools];
|
|
1036
|
+
const allowedTools = allowedToolsFor({ isBypass, agentTeams: providerConfig.agent_teams, designPolicy, designCheckTool });
|
|
922
1037
|
|
|
923
1038
|
/**
|
|
924
1039
|
* Approval events to yield from the generator.
|
|
@@ -991,7 +1106,7 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
991
1106
|
if (designToolDecision(toolName, hookInput?.tool_input, designRoot) === "allow") {
|
|
992
1107
|
return preToolUseDecision("allow");
|
|
993
1108
|
}
|
|
994
|
-
} else if (
|
|
1109
|
+
} else if (READ_ONLY_TOOLS.includes(toolName)) {
|
|
995
1110
|
// Read-only tools: always allow
|
|
996
1111
|
return {};
|
|
997
1112
|
}
|
|
@@ -1148,61 +1263,21 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
1148
1263
|
console.log(`[sdk] session=${sessionId} mcpServers: ${Object.keys(mcpServers).join(", ")}`);
|
|
1149
1264
|
}
|
|
1150
1265
|
|
|
1151
|
-
|
|
1152
|
-
// [1m] suffix. The suffix is stripped before the API call. Requires an entitled
|
|
1153
|
-
// account (Max/Team/Enterprise) and a supported model; otherwise the API errors.
|
|
1154
|
-
// Per-call overrides win over provider config (lightweight calls can opt out of 1M;
|
|
1155
|
-
// the chat input picker sets per-session model/effort/thinking). Effort enum is
|
|
1156
|
-
// guarded inside the helper — "extra" would crash the CLI subprocess.
|
|
1157
|
-
const mqo = buildModelQueryOptions(
|
|
1158
|
-
{
|
|
1159
|
-
model: opts?.model,
|
|
1160
|
-
oneMContext: opts?.oneMContext,
|
|
1161
|
-
effort: opts?.effort,
|
|
1162
|
-
thinkingBudget: opts?.thinkingBudget,
|
|
1163
|
-
},
|
|
1164
|
-
providerConfig,
|
|
1165
|
-
);
|
|
1166
|
-
const resolvedModel = mqo.model;
|
|
1167
|
-
const use1m = mqo.use1m;
|
|
1168
|
-
|
|
1169
|
-
// Compiled binaries have no node_modules → resolve a Claude CLI explicitly
|
|
1170
|
-
// (system claude or the one shipped in cli/). Source installs → undefined
|
|
1171
|
-
// (SDK self-resolves from node_modules, unchanged).
|
|
1172
|
-
const cliExecutablePath = resolveCliExecutablePath(
|
|
1173
|
-
(providerConfig as { cli_command?: string }).cli_command,
|
|
1174
|
-
);
|
|
1175
|
-
|
|
1176
|
-
const queryOptions: Record<string, any> = {
|
|
1177
|
-
// Run the CLI under node only for a .js entry (or the win32 source-mode
|
|
1178
|
-
// default). A native claude(.exe) must be spawned directly.
|
|
1179
|
-
...(needsNodeInterpreter(process.platform, cliExecutablePath) && { executable: "node" }),
|
|
1180
|
-
...(cliExecutablePath && { pathToClaudeCodeExecutable: cliExecutablePath }),
|
|
1266
|
+
const queryOptions = this.buildQueryOptions({
|
|
1181
1267
|
// First message: create session with this ID. Subsequent: resume by same ID.
|
|
1182
1268
|
sessionId: isFirstMessage && !shouldFork ? sessionId : undefined,
|
|
1183
1269
|
resume: (isFirstMessage && !shouldFork) ? undefined : (shouldFork ? forkSourceId : sessionId),
|
|
1184
|
-
|
|
1270
|
+
forkSession: shouldFork,
|
|
1185
1271
|
cwd: effectiveCwd,
|
|
1186
1272
|
systemPrompt: systemPromptOpt,
|
|
1187
|
-
settingSources: ["user", "project"],
|
|
1188
1273
|
env: queryEnv,
|
|
1189
|
-
settings: { permissions: { allow: [], deny: [] } },
|
|
1190
1274
|
allowedTools,
|
|
1191
|
-
|
|
1275
|
+
mcpServers,
|
|
1192
1276
|
permissionMode,
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
...(mqo.effort && { effort: mqo.effort }),
|
|
1196
|
-
maxTurns: opts?.maxTurns ?? providerConfig.max_turns ?? 1000,
|
|
1197
|
-
...(providerConfig.max_budget_usd && { maxBudgetUsd: providerConfig.max_budget_usd }),
|
|
1198
|
-
...(mqo.thinking && { thinking: mqo.thinking }),
|
|
1199
|
-
// Beta headers are honored only for API-key auth; OAuth/subscription sessions
|
|
1200
|
-
// reject them ("Custom betas are only available for API key users") and crash the
|
|
1201
|
-
// subprocess. Entitled OAuth accounts still get 1M context via the [1m] model suffix.
|
|
1202
|
-
...(use1m && !!queryEnv.ANTHROPIC_API_KEY && { betas: ["context-1m-2025-08-07"] }),
|
|
1203
|
-
includePartialMessages: true,
|
|
1277
|
+
opts,
|
|
1278
|
+
providerConfig,
|
|
1204
1279
|
stderr: stderrCallback,
|
|
1205
|
-
};
|
|
1280
|
+
});
|
|
1206
1281
|
|
|
1207
1282
|
// Crash retry: if subprocess exits with non-zero code before producing events,
|
|
1208
1283
|
// clean up and retry once with a fresh query before surfacing the error.
|
|
@@ -1252,21 +1327,28 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
1252
1327
|
};
|
|
1253
1328
|
};
|
|
1254
1329
|
|
|
1255
|
-
|
|
1330
|
+
// A new session's first attempt takes over the CLI `prewarm` started for it, if that
|
|
1331
|
+
// is exactly the process it would have spawned; otherwise it spawns one as always.
|
|
1332
|
+
const spare = crashRetryCount === 0 && isFirstMessage && !shouldFork
|
|
1333
|
+
? this.warmSpares.adopt(sessionId, spawnFingerprint(queryOptions), { canUseTool, preToolUse: preToolUseHook, stderr: stderrCallback })
|
|
1334
|
+
: undefined;
|
|
1335
|
+
const channel = spare ? undefined : createMessageChannel();
|
|
1336
|
+
const initialCtrl = spare?.controller ?? channel!.controller;
|
|
1256
1337
|
// On crash retry, use buildRetryMsg to get the latest user message (not the stale firstMsg)
|
|
1257
1338
|
const initRetry = crashRetryCount > 0 ? buildRetryMsg() : null;
|
|
1258
1339
|
initialCtrl.push(initRetry?.msg ?? firstMsg);
|
|
1259
1340
|
const initContent = initRetry?.lastUserContent ?? modelInput;
|
|
1260
1341
|
const initImages = initRetry?.lastUserImages ?? opts?.images;
|
|
1261
1342
|
|
|
1262
|
-
const initialQuery = query({
|
|
1263
|
-
prompt:
|
|
1343
|
+
const initialQuery = spare?.query ?? query({
|
|
1344
|
+
prompt: channel!.generator,
|
|
1264
1345
|
options: {
|
|
1265
1346
|
...queryOptions,
|
|
1266
1347
|
...(permissionHooks && { hooks: permissionHooks }),
|
|
1267
1348
|
canUseTool,
|
|
1268
1349
|
} as any,
|
|
1269
1350
|
});
|
|
1351
|
+
if (spare) console.log(`[sdk] session=${sessionId} took over the warm CLI started ${spare.ageMs} ms ago`);
|
|
1270
1352
|
this.streamingSessions.set(sessionId, { meta, query: initialQuery, controller: initialCtrl, lastUserContent: initContent, lastUserImages: initImages });
|
|
1271
1353
|
this.activeQueries.set(sessionId, initialQuery);
|
|
1272
1354
|
let eventSource: AsyncIterable<any> = initialQuery;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Claude CLI started before the chat that will use it exists.
|
|
3
|
+
*
|
|
4
|
+
* A new chat's first message used to wait for the CLI to boot before anything reached the
|
|
5
|
+
* API. Measured against a local stub API, so the figure is the CLI's own: 1.33–1.53 s from
|
|
6
|
+
* the message to the request, against 37–43 ms for a CLI started a few seconds earlier and
|
|
7
|
+
* waiting on stdin. The price is ~500 MB of RAM (the CLI plus the MCP servers it starts)
|
|
8
|
+
* and ~2.5% of a core while it waits — hence spares exist only while a new-chat composer is
|
|
9
|
+
* open, expire when nobody touches it, and are capped.
|
|
10
|
+
*
|
|
11
|
+
* A spare is spawned under a session id chosen now. `claim` hands that id to the next chat
|
|
12
|
+
* created in the project, and that chat's first turn takes the process over through `adopt`
|
|
13
|
+
* — but only when every option it would spawn a CLI with is the one the spare was spawned
|
|
14
|
+
* with. Any difference (another model, a rotated token, an edited MCP list) closes the spare
|
|
15
|
+
* and the turn starts cold, so a stale spare can cost the speed-up but never the behaviour.
|
|
16
|
+
* A spare closed unused has written nothing (the CLI creates the transcript on the first
|
|
17
|
+
* message), which is what lets its id go on to a cold start.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
|
|
21
|
+
/** The per-turn callbacks a CLI is spawned with. A spare's forward to the turn that adopts it. */
|
|
22
|
+
export interface SpareHandlers {
|
|
23
|
+
canUseTool: (...args: any[]) => Promise<any>;
|
|
24
|
+
preToolUse: (...args: any[]) => Promise<any>;
|
|
25
|
+
stderr: (chunk: string) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SpareProcess<Q> {
|
|
29
|
+
query: Q;
|
|
30
|
+
controller: { push(msg: unknown): void; done(): void };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Spare<Q> extends SpareProcess<Q> {
|
|
34
|
+
key: string;
|
|
35
|
+
sessionId: string;
|
|
36
|
+
fingerprint: string;
|
|
37
|
+
startedAt: number;
|
|
38
|
+
claimed: boolean;
|
|
39
|
+
handlers?: SpareHandlers;
|
|
40
|
+
/** What the CLI wrote to stderr before a turn took it, handed to that turn's handler. */
|
|
41
|
+
stderr: string;
|
|
42
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WarmSpareLimits {
|
|
46
|
+
/** How long a spare nobody claims stays, from the last `offer` for its key. */
|
|
47
|
+
idleMs: number;
|
|
48
|
+
/** How long a claimed spare waits for its session's first message. */
|
|
49
|
+
claimedMs: number;
|
|
50
|
+
/** Spares alive at once, across every project. */
|
|
51
|
+
max: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const DEFAULT_LIMITS: WarmSpareLimits = { idleMs: 5 * 60_000, claimedMs: 60_000, max: 2 };
|
|
55
|
+
|
|
56
|
+
export class WarmSpares<Q extends { close(): void; initializationResult?(): Promise<unknown> }> {
|
|
57
|
+
private readonly spares = new Map<string, Spare<Q>>();
|
|
58
|
+
|
|
59
|
+
constructor(private readonly limits: WarmSpareLimits = DEFAULT_LIMITS) {}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Keep a spare ready for `key`. One already waiting there with the same fingerprint is kept
|
|
63
|
+
* and its expiry pushed back; one with any other is replaced. Returns the waiting spare's
|
|
64
|
+
* session id.
|
|
65
|
+
*/
|
|
66
|
+
offer(key: string, fingerprint: string, start: (sessionId: string, callbacks: SpareHandlers) => SpareProcess<Q>): string {
|
|
67
|
+
const waiting = this.waiting(key);
|
|
68
|
+
if (waiting?.fingerprint === fingerprint) {
|
|
69
|
+
this.expireIn(waiting, this.limits.idleMs);
|
|
70
|
+
return waiting.sessionId;
|
|
71
|
+
}
|
|
72
|
+
if (waiting) this.close(waiting);
|
|
73
|
+
// Oldest first, and a claimed spare last: its first message is at most seconds away.
|
|
74
|
+
for (const victim of [...this.spares.values()].sort((a, b) => Number(a.claimed) - Number(b.claimed))) {
|
|
75
|
+
if (this.spares.size < this.limits.max) break;
|
|
76
|
+
this.close(victim);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const spare: Spare<Q> = {
|
|
80
|
+
key, fingerprint, sessionId: crypto.randomUUID(), startedAt: Date.now(), claimed: false, stderr: "",
|
|
81
|
+
} as Spare<Q>;
|
|
82
|
+
const callbacks: SpareHandlers = {
|
|
83
|
+
// Nothing can ask before a turn has pushed a message; refusing is the safe answer anyway.
|
|
84
|
+
canUseTool: (...args) => spare.handlers
|
|
85
|
+
? spare.handlers.canUseTool(...args)
|
|
86
|
+
: Promise.resolve({ behavior: "deny", message: "No turn has taken this process over yet" }),
|
|
87
|
+
preToolUse: (...args) => spare.handlers ? spare.handlers.preToolUse(...args) : Promise.resolve({}),
|
|
88
|
+
stderr: (chunk) => {
|
|
89
|
+
if (spare.handlers) spare.handlers.stderr(chunk);
|
|
90
|
+
else spare.stderr = (spare.stderr + chunk).slice(-2048);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
Object.assign(spare, start(spare.sessionId, callbacks));
|
|
94
|
+
this.spares.set(spare.sessionId, spare);
|
|
95
|
+
this.expireIn(spare, this.limits.idleMs);
|
|
96
|
+
// A CLI that dies during startup (a broken MCP entry, a missing binary) says so here;
|
|
97
|
+
// dropping it keeps a turn from adopting a process that is already gone.
|
|
98
|
+
spare.query.initializationResult?.().catch(() => {
|
|
99
|
+
if (this.spares.get(spare.sessionId) === spare) this.close(spare);
|
|
100
|
+
});
|
|
101
|
+
return spare.sessionId;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Hand the spare waiting for `key` to the session being created there. */
|
|
105
|
+
claim(key: string): string | undefined {
|
|
106
|
+
const spare = this.waiting(key);
|
|
107
|
+
if (!spare) return undefined;
|
|
108
|
+
spare.claimed = true;
|
|
109
|
+
this.expireIn(spare, this.limits.claimedMs);
|
|
110
|
+
return spare.sessionId;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Take over the spare started for `sessionId`, if the turn would have spawned exactly it.
|
|
115
|
+
* One that differs is closed rather than kept: its session is starting cold, and no other
|
|
116
|
+
* session can have its id.
|
|
117
|
+
*/
|
|
118
|
+
adopt(sessionId: string, fingerprint: string, handlers: SpareHandlers): (SpareProcess<Q> & { ageMs: number }) | undefined {
|
|
119
|
+
const spare = this.spares.get(sessionId);
|
|
120
|
+
if (!spare) return undefined;
|
|
121
|
+
this.forget(spare);
|
|
122
|
+
if (spare.fingerprint !== fingerprint) {
|
|
123
|
+
console.log(`[sdk] session=${sessionId} warm CLI does not match this turn's options — starting cold`);
|
|
124
|
+
this.stop(spare);
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
spare.handlers = handlers;
|
|
128
|
+
if (spare.stderr) handlers.stderr(spare.stderr);
|
|
129
|
+
return { query: spare.query, controller: spare.controller, ageMs: Date.now() - spare.startedAt };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
closeAll(): void {
|
|
133
|
+
for (const spare of [...this.spares.values()]) this.close(spare);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private waiting(key: string): Spare<Q> | undefined {
|
|
137
|
+
for (const spare of this.spares.values()) if (spare.key === key && !spare.claimed) return spare;
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private expireIn(spare: Spare<Q>, ms: number): void {
|
|
142
|
+
clearTimeout(spare.timer);
|
|
143
|
+
spare.timer = setTimeout(() => this.close(spare), ms);
|
|
144
|
+
spare.timer.unref?.();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private close(spare: Spare<Q>): void {
|
|
148
|
+
this.forget(spare);
|
|
149
|
+
this.stop(spare);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private forget(spare: Spare<Q>): void {
|
|
153
|
+
clearTimeout(spare.timer);
|
|
154
|
+
this.spares.delete(spare.sessionId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private stop(spare: Spare<Q>): void {
|
|
158
|
+
spare.controller.done();
|
|
159
|
+
spare.query.close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A digest of every option a CLI is spawned with but its session id. Functions do not
|
|
165
|
+
* serialise and have no say; everything else — the environment and its token included —
|
|
166
|
+
* does, which is also why this is a digest and not the text.
|
|
167
|
+
*/
|
|
168
|
+
export function spawnFingerprint(options: Record<string, unknown>): string {
|
|
169
|
+
const { sessionId: _sessionId, ...rest } = options;
|
|
170
|
+
return createHash("sha256").update(JSON.stringify(rest, sortKeys)).digest("hex");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function sortKeys(_key: string, value: unknown): unknown {
|
|
174
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
175
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
176
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -33,8 +33,10 @@ import { globalWebSocket } from "./ws/global.ts";
|
|
|
33
33
|
import { groupChatWebSocket } from "./ws/group-chat.ts";
|
|
34
34
|
import { remoteDesktopWebSocket } from "./ws/remote-desktop.ts";
|
|
35
35
|
import { lspWebSocket } from "./ws/lsp.ts";
|
|
36
|
+
import { androidWebSocket } from "./ws/android.ts";
|
|
36
37
|
import { lspManager } from "../services/lsp/lsp-manager.ts";
|
|
37
38
|
import { isRemoteDesktopEnabled } from "../services/remote-desktop/remote-desktop-flag.ts";
|
|
39
|
+
import { isAndroidEmulatorEnabled } from "../services/android/android-flag.ts";
|
|
38
40
|
import { ok, err } from "../types/api.ts";
|
|
39
41
|
|
|
40
42
|
/** Tee console.log/error to ~/.ppm/ppm.log while preserving terminal output */
|
|
@@ -239,6 +241,12 @@ app.get("/api/system/event-loop", async (c) => {
|
|
|
239
241
|
import { remoteDesktopRoutes } from "./routes/remote-desktop.ts";
|
|
240
242
|
app.route("/api/remote-desktop", remoteDesktopRoutes);
|
|
241
243
|
|
|
244
|
+
// Android emulator — OFF by default, opt-in via ANDROID_EMULATOR_ENABLED=1, see android-flag.ts.
|
|
245
|
+
// Unlike remote desktop this spawns multi-gigabyte emulator processes, and most hosts have no
|
|
246
|
+
// Android SDK at all, so it stays dark until somebody asks for it.
|
|
247
|
+
import { androidRoutes } from "./routes/android.ts";
|
|
248
|
+
app.route("/api/android", androidRoutes);
|
|
249
|
+
|
|
242
250
|
// Finishes an OAuth loopback login started from another device
|
|
243
251
|
import { loopbackRoutes } from "./routes/oauth-loopback.ts";
|
|
244
252
|
app.route("/api/loopback", loopbackRoutes);
|
|
@@ -270,6 +278,10 @@ app.route("/api/jira", jiraRoutes);
|
|
|
270
278
|
import { schedulesRoutes } from "./routes/schedules.ts";
|
|
271
279
|
app.route("/api/schedules", schedulesRoutes);
|
|
272
280
|
|
|
281
|
+
// Session trace — browser logs in, a session's timeline out
|
|
282
|
+
import { traceRoutes } from "./routes/trace.ts";
|
|
283
|
+
app.route("/api/trace", traceRoutes);
|
|
284
|
+
|
|
273
285
|
// AI resources (skills / agents / commands) management
|
|
274
286
|
import { aiResourcesRoutes } from "./routes/ai-resources.ts";
|
|
275
287
|
app.route("/api/ai-resources", aiResourcesRoutes);
|
|
@@ -879,6 +891,30 @@ if (process.argv.includes("__serve__")) {
|
|
|
879
891
|
setInterval(runCleanup, 24 * 60 * 60 * 1000);
|
|
880
892
|
}
|
|
881
893
|
|
|
894
|
+
// Same again for the session trace: age, then size. Skipped until something has been traced.
|
|
895
|
+
{
|
|
896
|
+
const { existsSync } = await import("node:fs");
|
|
897
|
+
const { getTraceDbPath } = await import("../services/session-trace/session-trace-db.ts");
|
|
898
|
+
const { cleanupSessionTrace } = await import("../services/session-trace/session-trace-cleanup.ts");
|
|
899
|
+
|
|
900
|
+
const runTraceCleanup = () => {
|
|
901
|
+
if (!existsSync(getTraceDbPath())) return;
|
|
902
|
+
try {
|
|
903
|
+
const { retention_days, max_size_mb } = configService.get("session_trace");
|
|
904
|
+
const { deletedByAge, deletedBySize, freedBytes } = cleanupSessionTrace(retention_days, max_size_mb);
|
|
905
|
+
const removed = deletedByAge + deletedBySize;
|
|
906
|
+
if (removed > 0) {
|
|
907
|
+
console.log(`[session-trace] pruned ${removed} rows (${(freedBytes / 1024 / 1024).toFixed(1)} MB freed)`);
|
|
908
|
+
}
|
|
909
|
+
} catch (e) {
|
|
910
|
+
console.error(`[session-trace] cleanup failed: ${(e as Error).message}`);
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
runTraceCleanup();
|
|
915
|
+
setInterval(runTraceCleanup, 24 * 60 * 60 * 1000);
|
|
916
|
+
}
|
|
917
|
+
|
|
882
918
|
// On Windows the supervisor reaps the previous server's whole process tree
|
|
883
919
|
// before respawning, so the port is released cleanly. A lingering bind can
|
|
884
920
|
// still appear for a moment during an upgrade handoff, so wait for it to
|
|
@@ -959,6 +995,20 @@ if (process.argv.includes("__serve__")) {
|
|
|
959
995
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
960
996
|
}
|
|
961
997
|
|
|
998
|
+
if (url.pathname === "/ws/android") {
|
|
999
|
+
// Same shape and the same reasoning as the remote-desktop branch above: an explicit
|
|
1000
|
+
// branch so this can never fall through to the terminal (shell) handler, with the flag
|
|
1001
|
+
// and `auth.enabled` re-checked independently of `isWsUpgradeAuthorized`, which returns
|
|
1002
|
+
// true unconditionally when PPM auth is disabled.
|
|
1003
|
+
if (!isAndroidEmulatorEnabled()) return new Response("Not Found", { status: 404 });
|
|
1004
|
+
if (!configService.get("auth").enabled) {
|
|
1005
|
+
return new Response("Forbidden: android emulator control requires PPM authentication to be enabled", { status: 403 });
|
|
1006
|
+
}
|
|
1007
|
+
const upgraded = server.upgrade(req, { data: { type: "android" } });
|
|
1008
|
+
if (upgraded) return undefined;
|
|
1009
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
1010
|
+
}
|
|
1011
|
+
|
|
962
1012
|
if (url.pathname.startsWith("/ws/project/")) {
|
|
963
1013
|
const parts = url.pathname.split("/");
|
|
964
1014
|
const projectName = decodeURIComponent(parts[3] ?? "");
|
|
@@ -1014,6 +1064,7 @@ if (process.argv.includes("__serve__")) {
|
|
|
1014
1064
|
else if (t === "extensions") extensionWebSocket.open(ws);
|
|
1015
1065
|
else if (t === "global") globalWebSocket.open(ws);
|
|
1016
1066
|
else if (t === "remote-desktop") remoteDesktopWebSocket.open(ws);
|
|
1067
|
+
else if (t === "android") androidWebSocket.open(ws);
|
|
1017
1068
|
else if (t === "terminal") terminalWebSocket.open(ws);
|
|
1018
1069
|
else if (t === "lsp") lspWebSocket.open(ws);
|
|
1019
1070
|
else ws.close(1008, "unknown socket type");
|
|
@@ -1025,6 +1076,7 @@ if (process.argv.includes("__serve__")) {
|
|
|
1025
1076
|
else if (t === "extensions") extensionWebSocket.message(ws, msg);
|
|
1026
1077
|
else if (t === "global") globalWebSocket.message(ws, msg);
|
|
1027
1078
|
else if (t === "remote-desktop") remoteDesktopWebSocket.message(ws, msg);
|
|
1079
|
+
else if (t === "android") androidWebSocket.message(ws, msg);
|
|
1028
1080
|
else if (t === "terminal") terminalWebSocket.message(ws, msg);
|
|
1029
1081
|
else if (t === "lsp") lspWebSocket.message(ws, msg);
|
|
1030
1082
|
},
|
|
@@ -1035,6 +1087,7 @@ if (process.argv.includes("__serve__")) {
|
|
|
1035
1087
|
else if (t === "extensions") extensionWebSocket.close(ws);
|
|
1036
1088
|
else if (t === "global") globalWebSocket.close(ws);
|
|
1037
1089
|
else if (t === "remote-desktop") remoteDesktopWebSocket.close(ws);
|
|
1090
|
+
else if (t === "android") androidWebSocket.close(ws);
|
|
1038
1091
|
else if (t === "terminal") terminalWebSocket.close(ws);
|
|
1039
1092
|
else if (t === "lsp") lspWebSocket.close(ws);
|
|
1040
1093
|
},
|
|
@@ -5,9 +5,20 @@
|
|
|
5
5
|
* untouched. JSON compresses ~5-8x; a 542KB file index becomes ~80KB on the wire.
|
|
6
6
|
*/
|
|
7
7
|
import type { MiddlewareHandler } from "hono";
|
|
8
|
+
import { gzip } from "node:zlib";
|
|
9
|
+
import { promisify } from "node:util";
|
|
8
10
|
|
|
9
11
|
const MIN_SIZE_BYTES = 1024;
|
|
10
12
|
|
|
13
|
+
/**
|
|
14
|
+
* From this size on, compress on zlib's thread pool instead of the event loop. `Bun.gzipSync`
|
|
15
|
+
* stopped the server for 93 ms on each download of nxsys-workspace's 22 MB file index, while the
|
|
16
|
+
* pool left it waiting 3 ms at most. Below it the synchronous call blocks for well under a
|
|
17
|
+
* millisecond (0.3 ms for 200 KB) and answers sooner than the pool does.
|
|
18
|
+
*/
|
|
19
|
+
const ASYNC_FROM_BYTES = 256 * 1024;
|
|
20
|
+
const gzipAsync = promisify(gzip);
|
|
21
|
+
|
|
11
22
|
export const gzipJson: MiddlewareHandler = async (c, next) => {
|
|
12
23
|
await next();
|
|
13
24
|
|
|
@@ -27,7 +38,8 @@ export const gzipJson: MiddlewareHandler = async (c, next) => {
|
|
|
27
38
|
return;
|
|
28
39
|
}
|
|
29
40
|
|
|
30
|
-
const
|
|
41
|
+
const bytes = new Uint8Array(body);
|
|
42
|
+
const gzipped = body.byteLength < ASYNC_FROM_BYTES ? Bun.gzipSync(bytes) : await gzipAsync(bytes);
|
|
31
43
|
const headers = new Headers(res.headers);
|
|
32
44
|
headers.set("Content-Encoding", "gzip");
|
|
33
45
|
headers.set("Content-Length", String(gzipped.byteLength));
|