@hienlh/ppm 0.23.6 → 0.23.7
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 +84 -0
- package/CLAUDE.md +21 -1
- package/Dockerfile.test +8 -1
- 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 +29 -1
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js +1 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-Dn_wB-o5.js → ai-resource-editor-CY4n_Ccv.js} +1 -1
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js +1 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-DDs11XV4.js +1 -0
- package/dist/web/assets/api-client-CJPnCOG-.js +1 -0
- package/dist/web/assets/{api-client-c8YOpTlT.js → api-client-D-SMbdiH.js} +1 -1
- package/dist/web/assets/api-client-D-SMbdiH.js.br +0 -0
- package/dist/web/assets/api-client-D-SMbdiH.js.gz +0 -0
- package/dist/web/assets/api-settings-BDkkhFMq.js +1 -0
- package/dist/web/assets/{api-settings-BmvNoCbn.js → api-settings-Bv9mLCty.js} +1 -1
- package/dist/web/assets/api-settings-Bv9mLCty.js.br +0 -0
- package/dist/web/assets/api-settings-Bv9mLCty.js.gz +0 -0
- package/dist/web/assets/{api-themes-6fOQJgM9.js → api-themes-3wW-zLzU.js} +1 -1
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js +1 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.gz +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js +1 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.br +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.gz +0 -0
- package/dist/web/assets/{audio-preview-vaG2cB-k.js → audio-preview-BfES4Cx5.js} +1 -1
- package/dist/web/assets/audio-preview-BfES4Cx5.js.br +0 -0
- package/dist/web/assets/audio-preview-BfES4Cx5.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js +1 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.br +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.gz +0 -0
- package/dist/web/assets/{button-Ci9EUgqd.js → button-DeAyLH9U.js} +1 -1
- package/dist/web/assets/button-DeAyLH9U.js.br +0 -0
- package/dist/web/assets/button-DeAyLH9U.js.gz +0 -0
- package/dist/web/assets/{chat-preparation-cache-BSIQGiWx.js → chat-preparation-cache-CcCd2g7A.js} +1 -1
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.gz +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js +1 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.br +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.gz +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js +15 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.br +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.gz +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js +7 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.br +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js +1 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-BA3y-qSH.js → conflict-editor-BpKW_vbL.js} +1 -1
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.br +0 -0
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.gz +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js +1 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.br +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.gz +0 -0
- package/dist/web/assets/{csv-preview-BioxkrQF.js → csv-preview-COYdTABa.js} +1 -1
- package/dist/web/assets/csv-preview-COYdTABa.js.br +0 -0
- package/dist/web/assets/csv-preview-COYdTABa.js.gz +0 -0
- package/dist/web/assets/{data-grid-overlay-editor-CQRVKr7y.js → data-grid-overlay-editor-CeZoduke.js} +1 -1
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.gz +0 -0
- package/dist/web/assets/{database-viewer-BSoJ0E6p.js → database-viewer-BQcBqCKm.js} +1 -1
- package/dist/web/assets/database-viewer-BQcBqCKm.js.br +0 -0
- package/dist/web/assets/database-viewer-BQcBqCKm.js.gz +0 -0
- package/dist/web/assets/{design-export-client-ChMcMoqW.js → design-export-client-OrnGCSwu.js} +1 -1
- package/dist/web/assets/design-export-client-OrnGCSwu.js.br +0 -0
- package/dist/web/assets/design-export-client-OrnGCSwu.js.gz +0 -0
- package/dist/web/assets/{design-responsive-dialog-DNBhJJLf.js → design-responsive-dialog-vJjQrC4n.js} +1 -1
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.gz +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js +33 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.br +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-DHh5QsH9.js → designs-sidebar-panel-D2kXwB8B.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.gz +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js +1 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.br +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-lctWoSmG.js → diff-viewer-Bj4DT6Mj.js} +1 -1
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.br +0 -0
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.gz +0 -0
- package/dist/web/assets/{docx-preview-C9XdKEj1.js → docx-preview-C2QqNGRE.js} +1 -1
- package/dist/web/assets/docx-preview-C2QqNGRE.js.br +0 -0
- package/dist/web/assets/docx-preview-C2QqNGRE.js.gz +0 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js +1 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.br +3 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.gz +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js +2 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.br +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js +1 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.br +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-rbkXsZVR.js → extension-manager-section-Bu0qcB2i.js} +1 -1
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.br +0 -0
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.gz +0 -0
- package/dist/web/assets/{extension-webview-uPO0WgCq.js → extension-webview-G_WJ0N52.js} +1 -1
- package/dist/web/assets/extension-webview-G_WJ0N52.js.br +0 -0
- package/dist/web/assets/extension-webview-G_WJ0N52.js.gz +0 -0
- package/dist/web/assets/{file-icons-gx_FBOXY.js → file-icons-l7fNOmdS.js} +1 -1
- package/dist/web/assets/file-icons-l7fNOmdS.js.br +0 -0
- package/dist/web/assets/file-icons-l7fNOmdS.js.gz +0 -0
- package/dist/web/assets/{file-store-D9KE0kQC.js → file-store-B0e7sLag.js} +1 -1
- package/dist/web/assets/file-store-B0e7sLag.js.br +0 -0
- package/dist/web/assets/file-store-B0e7sLag.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-CT7mDb0L.js → files-settings-section-ChZ_CynX.js} +1 -1
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.br +0 -0
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.gz +0 -0
- package/dist/web/assets/format-bytes-__396eCN.js +1 -0
- package/dist/web/assets/{format-file-meta-mQwA6SuY.js → format-file-meta-o1XBuoKS.js} +1 -1
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.br +0 -0
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.gz +0 -0
- package/dist/web/assets/{fs-api-DL36slbd.js → fs-api-CeHKwx95.js} +1 -1
- package/dist/web/assets/fs-api-CeHKwx95.js.br +0 -0
- package/dist/web/assets/fs-api-CeHKwx95.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-DANPfMRN.js → general-settings-section-MSvDEbFa.js} +1 -1
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.br +0 -0
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-BJPkLiEw.js → git-log-panel-gw5_a6j3.js} +1 -1
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.br +0 -0
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.gz +0 -0
- package/dist/web/assets/{git-repo-store-DSypNj19.js → git-repo-store-BpPnah5l.js} +1 -1
- package/dist/web/assets/git-repo-store-BpPnah5l.js.br +0 -0
- package/dist/web/assets/git-repo-store-BpPnah5l.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-R4PcAntf.js → glide-data-grid-Db2Cl26B.js} +5 -5
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.br +0 -0
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js +1 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.br +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.gz +0 -0
- package/dist/web/assets/{image-preview-CGdZrmLo.js → image-preview-CjNhpfWw.js} +1 -1
- package/dist/web/assets/index-CtAvmybi.js +52 -0
- package/dist/web/assets/index-CtAvmybi.js.br +0 -0
- package/dist/web/assets/index-CtAvmybi.js.gz +0 -0
- package/dist/web/assets/index-DeCJasyy.css +1 -0
- package/dist/web/assets/index-DeCJasyy.css.br +0 -0
- package/dist/web/assets/index-DeCJasyy.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-DHRoyWcG.js → jira-watcher-section-CKSI1Alo.js} +1 -1
- package/dist/web/assets/keybindings-store-C4_yyU4u.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-BKej4mUf.js → keyboard-shortcuts-section-CEaVUc9a.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.gz +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js +2 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.br +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.gz +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js +1 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.br +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-CTtDap-l.js +1 -0
- package/dist/web/assets/{markdown-renderer-BHtYqkYZ.js → markdown-renderer-D05fAr57.js} +2 -2
- package/dist/web/assets/markdown-renderer-D05fAr57.js.br +0 -0
- package/dist/web/assets/markdown-renderer-D05fAr57.js.gz +0 -0
- package/dist/web/assets/{media-url-CzUJrREv.js → media-url-C6NumuN5.js} +1 -1
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js +1 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.br +0 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.gz +0 -0
- package/dist/web/assets/{mobile-bottom-sheet-CuNi2FRv.js → mobile-bottom-sheet-D4UyOatB.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.gz +0 -0
- package/dist/web/assets/{new-design-dialog-DS_XrpTG.js → new-design-dialog-BgmQopJM.js} +1 -1
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.br +0 -0
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.gz +0 -0
- package/dist/web/assets/notification-store-q5QBklKA.js +1 -0
- package/dist/web/assets/{notifications-settings-section-C05WYqcE.js → notifications-settings-section-BBJ0r9OS.js} +1 -1
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.gz +0 -0
- package/dist/web/assets/{onboarding-entry-CmUqv1kl.js → onboarding-entry-BYOYswsi.js} +1 -1
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.br +0 -0
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.gz +0 -0
- package/dist/web/assets/{onboarding-quick-orientation-DIhWv1CN.js → onboarding-quick-orientation-DPHYFMbA.js} +1 -1
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.gz +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js +1 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.br +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.gz +0 -0
- package/dist/web/assets/{pdf-preview-qdXmMkOR.js → pdf-preview-c-L_kDAs.js} +1 -1
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.br +0 -0
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.gz +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js +3 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.br +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-Xvlun_n5.js → postgres-viewer-CnwDCMNd.js} +1 -1
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.br +0 -0
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-D8AES1BA.js → ppmbot-settings-section-BDXoSIDp.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.gz +0 -0
- package/dist/web/assets/{pptx-export-CG6MRNok.js → pptx-export-D0RDY48h.js} +4 -4
- package/dist/web/assets/pptx-export-D0RDY48h.js.br +0 -0
- package/dist/web/assets/pptx-export-D0RDY48h.js.gz +0 -0
- package/dist/web/assets/{problems-panel-DTBG4beI.js → problems-panel-BgY8oyo9.js} +1 -1
- package/dist/web/assets/problems-panel-BgY8oyo9.js.br +0 -0
- package/dist/web/assets/problems-panel-BgY8oyo9.js.gz +0 -0
- package/dist/web/assets/process-row-format-CaaMcHzn.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.br +0 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.gz +0 -0
- package/dist/web/assets/{project-framework-store-XxFXcUpb.js → project-framework-store-UDEMu28U.js} +1 -1
- package/dist/web/assets/{project-store-C5gJXkvb.js → project-store-DGa31y5B.js} +1 -1
- package/dist/web/assets/project-store-DGa31y5B.js.br +0 -0
- package/dist/web/assets/project-store-DGa31y5B.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-CqtmbeRj.js → proxy-settings-section-Db6Kc6xG.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-UKG_smyt.js → query-audit-section-C0PAXhfT.js} +1 -1
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.br +0 -0
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-clipboard-notice-Djt6cpNL.js → remote-desktop-clipboard-notice-DOVtobj_.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-DNJF8TcF.js → remote-desktop-readiness-gate-BsfoJzdc.js} +1 -1
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.gz +0 -0
- package/dist/web/assets/{schedules-settings-section-BKoPovy2.js → schedules-settings-section-CvbjQM8z.js} +1 -1
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.gz +0 -0
- package/dist/web/assets/{scroll-area-XonvWYn_.js → scroll-area-sjOeAfoD.js} +1 -1
- package/dist/web/assets/scroll-area-sjOeAfoD.js.br +0 -0
- package/dist/web/assets/scroll-area-sjOeAfoD.js.gz +0 -0
- package/dist/web/assets/{separator-sQj04Ari.js → separator-9A3PCG1t.js} +1 -1
- package/dist/web/assets/services-panel-CimW47P8.js +1 -0
- package/dist/web/assets/services-panel-CimW47P8.js.br +0 -0
- package/dist/web/assets/services-panel-CimW47P8.js.gz +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js +2 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.br +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.gz +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js +2 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.br +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.gz +0 -0
- package/dist/web/assets/settings-tab-CGhaRXjs.js +1 -0
- package/dist/web/assets/settings-window-content--Uyg_Psr.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.br +0 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.gz +0 -0
- package/dist/web/assets/{sql-query-editor-BjIMi9Wk.js → sql-query-editor-Dn-cgDrW.js} +1 -1
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-BNcA6Irq.js → sqlite-viewer-B0NfaMjq.js} +1 -1
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js +2 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.br +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-Cb3EZUdp.js +1 -0
- package/dist/web/assets/system-monitor-window-content-51CA-2PH.js +1 -0
- package/dist/web/assets/{tab-host-window-content-CIRGNCvi.js → tab-host-window-content-CSrcmKNG.js} +1 -1
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.gz +0 -0
- package/dist/web/assets/{team-member-sheet-CwXpLi3X.js → team-member-sheet-D4X6nh-x.js} +2 -2
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.br +0 -0
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.gz +0 -0
- package/dist/web/assets/{team-member-window-content-4swiiDz7.js → team-member-window-content-BxkbGVIL.js} +1 -1
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.br +0 -0
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.gz +0 -0
- package/dist/web/assets/{terminal-tab-BGcdtMMT.js → terminal-tab-BYo34IqU.js} +3 -3
- package/dist/web/assets/terminal-tab-BYo34IqU.js.br +0 -0
- package/dist/web/assets/terminal-tab-BYo34IqU.js.gz +0 -0
- package/dist/web/assets/{tool-cards-D0-krtA2.js → tool-cards-DvXjOped.js} +4 -4
- package/dist/web/assets/tool-cards-DvXjOped.js.br +0 -0
- package/dist/web/assets/tool-cards-DvXjOped.js.gz +0 -0
- package/dist/web/assets/{use-blob-url-CmavyEP8.js → use-blob-url-CISBWKG5.js} +1 -1
- package/dist/web/assets/use-inline-blame-Br6mheS4.js +16 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.gz +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js +6 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.br +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-C88KphGx.js → use-monaco-theme-UGOzyIhv.js} +1 -1
- package/dist/web/assets/use-process-kill-CyiiUD1E.js +1 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.br +0 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.gz +0 -0
- package/dist/web/assets/{use-websocket-Chk9guIU.js → use-websocket-Dpl21cHG.js} +1 -1
- package/dist/web/assets/{vendor-ui-BHh12_0v.js → vendor-ui-P9Ym7hCv.js} +1 -1
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.br +0 -0
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.gz +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js +1 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.br +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js +1 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.br +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.gz +0 -0
- package/dist/web/assets/ws-auth-CqI8-aoA.js +1 -0
- package/dist/web/index.html +20 -20
- 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/deployment-guide.md +23 -0
- package/package.json +1 -1
- package/release-from-worktree.sh +224 -0
- package/src/cli/commands/config-cmd.ts +12 -0
- package/src/providers/claude-agent-sdk.ts +65 -2
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +16 -1
- package/src/providers/codex-app-server/codex-provider.ts +39 -7
- package/src/providers/provider-probe.ts +63 -0
- package/src/providers/registry.ts +109 -29
- package/src/server/index.ts +9 -0
- package/src/server/middleware/auth.ts +5 -1
- package/src/server/routes/cross-origin-guard.ts +20 -0
- package/src/server/routes/git.ts +40 -4
- package/src/server/routes/lsp.ts +97 -4
- package/src/server/routes/resources.ts +72 -9
- package/src/server/routes/settings.ts +19 -1
- package/src/server/routes/speech.ts +66 -0
- package/src/server/routes/system-services.ts +76 -0
- package/src/server/routes/tunnel.ts +33 -1
- package/src/server/ws/chat.ts +74 -3
- package/src/services/codex-account-login.ts +26 -2
- package/src/services/codex-reset-credit.service.ts +16 -3
- package/src/services/compaction-savings.ts +127 -0
- package/src/services/db.service.ts +123 -8
- package/src/services/fs-ops/fs-ops-trash.service.ts +22 -9
- package/src/services/git-refs/for-each-ref.ts +118 -0
- package/src/services/git.service.ts +25 -2
- package/src/services/host-info/spawn-runner.ts +48 -4
- package/src/services/jsonl-transcript-parser.ts +7 -42
- package/src/services/lsp/lsp-install.ts +289 -0
- package/src/services/lsp/lsp-manager.ts +160 -29
- package/src/services/lsp/server-registry.ts +120 -4
- package/src/services/named-tunnel/named-tunnel-config.ts +8 -2
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +4 -0
- package/src/services/read-lines.ts +54 -0
- package/src/services/speech-to-text/whisper-catalog.ts +142 -0
- package/src/services/speech-to-text/whisper-download.ts +113 -0
- package/src/services/speech-to-text/whisper-hallucinations.ts +51 -0
- package/src/services/speech-to-text/whisper-install.service.ts +247 -0
- package/src/services/speech-to-text/whisper-paths.ts +62 -0
- package/src/services/speech-to-text/whisper-transcribe.service.ts +117 -0
- package/src/services/supervisor.ts +94 -11
- package/src/services/system-metrics/cpu-details-linux.ts +252 -0
- package/src/services/system-metrics/cpu-memory-collector.ts +140 -18
- package/src/services/system-metrics/device-collector-linux.ts +31 -0
- package/src/services/system-metrics/device-collector-types.ts +40 -0
- package/src/services/system-metrics/disk-devices-linux.ts +156 -0
- package/src/services/system-metrics/disk-inventory-linux.ts +150 -0
- package/src/services/system-metrics/fans-linux.ts +51 -0
- package/src/services/system-metrics/gpu-api-versions.ts +120 -0
- package/src/services/system-metrics/gpu-devices-linux.ts +370 -0
- package/src/services/system-metrics/gpu-fdinfo-linux.ts +253 -0
- package/src/services/system-metrics/hardware-inventory.ts +50 -0
- package/src/services/system-metrics/linux-fs.ts +43 -0
- package/src/services/system-metrics/memory-darwin.ts +91 -0
- package/src/services/system-metrics/memory-linux.ts +156 -0
- package/src/services/system-metrics/net-devices-linux.ts +114 -0
- package/src/services/system-metrics/net-inventory-linux.ts +100 -0
- package/src/services/system-metrics/partitions-linux.ts +158 -0
- package/src/services/system-metrics/process-collector-linux.ts +58 -6
- package/src/services/system-metrics/process-collector-types.ts +8 -1
- package/src/services/system-metrics/process-details-darwin.ts +166 -0
- package/src/services/system-metrics/process-details-linux.ts +150 -0
- package/src/services/system-metrics/process-grouping.ts +2 -1
- package/src/services/system-metrics/process-rows-builder.ts +2 -0
- package/src/services/system-metrics/process-swap-linux.ts +37 -0
- package/src/services/system-metrics/signal-executor.ts +84 -0
- package/src/services/system-metrics/signal-request-handler.ts +92 -0
- package/src/services/system-metrics/system-metrics-platform.ts +30 -4
- package/src/services/system-metrics/system-metrics-tick.ts +73 -4
- package/src/services/system-metrics/system-metrics.service.ts +87 -2
- package/src/services/system-metrics/udev-db.ts +40 -0
- package/src/services/system-services/app-cgroup-linux.ts +115 -0
- package/src/services/system-services/app-icon-service.ts +31 -0
- package/src/services/system-services/app-icons-linux.ts +124 -0
- package/src/services/system-services/apps-linux.ts +174 -0
- package/src/services/system-services/desktop-entry.ts +103 -0
- package/src/services/system-services/service-guard.ts +124 -0
- package/src/services/system-services/systemd-collector.ts +203 -0
- package/src/services/system-services/systemd-parse.ts +162 -0
- package/src/shared/prompt-cache-idle.ts +197 -0
- package/src/shared/turn-usage.ts +85 -1
- package/src/types/chat.ts +27 -0
- package/src/types/config.ts +14 -0
- package/src/types/git.ts +41 -0
- package/src/types/system-hardware.ts +137 -0
- package/src/types/system-metrics.ts +231 -2
- package/src/types/system-services.ts +73 -0
- package/src/web/components/branch-review/branch-review-tab.tsx +20 -20
- package/src/web/components/branch-review/branch-review-tree-row.tsx +1 -13
- package/src/web/components/chat/chat-tab.tsx +3 -0
- package/src/web/components/chat/compaction-divider.tsx +48 -0
- package/src/web/components/chat/idle-cache-notice.tsx +67 -0
- package/src/web/components/chat/message-input.tsx +48 -13
- package/src/web/components/chat/message-list.tsx +15 -0
- package/src/web/components/chat/prompt-cache-chip.tsx +74 -0
- package/src/web/components/editor/code-editor.tsx +1 -0
- package/src/web/components/editor/editor-language-service.tsx +11 -3
- package/src/web/components/editor/lsp-status.tsx +115 -13
- package/src/web/components/git/branch-picker.tsx +419 -0
- package/src/web/components/git/branch-select.tsx +34 -0
- package/src/web/components/git/create-worktree-dialog.tsx +17 -16
- package/src/web/components/git/git-repo-picker.tsx +28 -40
- package/src/web/components/layout/status-bar.tsx +42 -19
- package/src/web/components/settings/accounts/account-card.tsx +5 -1
- package/src/web/components/settings/ai-settings-section.tsx +67 -3
- package/src/web/components/settings/appearance-settings-section.tsx +2 -28
- package/src/web/components/settings/language-servers-section.tsx +362 -0
- package/src/web/components/settings/settings-categories.ts +8 -3
- package/src/web/components/settings/settings-section-content.tsx +2 -0
- package/src/web/components/settings/voice-settings-section.tsx +283 -0
- package/src/web/components/system/apps/app-row.tsx +110 -0
- package/src/web/components/system/apps/app-rows.ts +140 -0
- package/src/web/components/system/apps/apps-panel.tsx +129 -0
- package/src/web/components/system/metric-chart-canvas.tsx +79 -3
- package/src/web/components/system/overview-cards/card-shell.tsx +54 -0
- package/src/web/components/system/overview-cards/cpu-card.tsx +11 -6
- package/src/web/components/system/overview-cards/disk-card.tsx +11 -6
- package/src/web/components/system/overview-cards/gpu-card.tsx +16 -6
- package/src/web/components/system/overview-cards/mem-card.tsx +11 -6
- package/src/web/components/system/overview-cards/net-card.tsx +11 -6
- package/src/web/components/system/overview-device-target.ts +69 -0
- package/src/web/components/system/overview-panel.tsx +15 -1
- package/src/web/components/system/performance/cpu-core-grid.tsx +74 -0
- package/src/web/components/system/performance/cpu-detail.tsx +220 -0
- package/src/web/components/system/performance/detail-parts.tsx +149 -0
- package/src/web/components/system/performance/device-list.ts +139 -0
- package/src/web/components/system/performance/disk-detail.tsx +64 -0
- package/src/web/components/system/performance/fans-detail.tsx +35 -0
- package/src/web/components/system/performance/gpu-detail.tsx +116 -0
- package/src/web/components/system/performance/memory-detail.tsx +109 -0
- package/src/web/components/system/performance/nic-detail.tsx +55 -0
- package/src/web/components/system/performance/partition-list.tsx +79 -0
- package/src/web/components/system/performance/performance-panel.tsx +206 -0
- package/src/web/components/system/performance/use-hardware-inventory.ts +48 -0
- package/src/web/components/system/process-columns-grid.ts +5 -3
- package/src/web/components/system/process-details-dialog.tsx +116 -0
- package/src/web/components/system/process-group-row.tsx +6 -1
- package/src/web/components/system/process-row-format.ts +6 -0
- package/src/web/components/system/process-row-menu.tsx +72 -0
- package/src/web/components/system/process-row.tsx +7 -1
- package/src/web/components/system/process-table-model.ts +1 -1
- package/src/web/components/system/process-table-sort.ts +8 -4
- package/src/web/components/system/process-table-toolbar.tsx +10 -2
- package/src/web/components/system/process-table-totals.ts +3 -0
- package/src/web/components/system/process-table.tsx +25 -2
- package/src/web/components/system/services/service-action-confirm.tsx +100 -0
- package/src/web/components/system/services/service-columns.ts +103 -0
- package/src/web/components/system/services/service-details-sheet.tsx +97 -0
- package/src/web/components/system/services/service-resources.ts +105 -0
- package/src/web/components/system/services/service-row.tsx +146 -0
- package/src/web/components/system/services/service-rows.ts +92 -0
- package/src/web/components/system/services/service-sort.ts +90 -0
- package/src/web/components/system/services/services-header.tsx +51 -0
- package/src/web/components/system/services/services-panel.tsx +207 -0
- package/src/web/components/system/services/use-services.ts +92 -0
- package/src/web/components/system/sortable-header.tsx +13 -8
- package/src/web/components/system/sysmon-preferences.tsx +85 -0
- package/src/web/components/system/system-monitor-body.tsx +101 -43
- package/src/web/components/system/system-monitor-window-content.tsx +2 -3
- package/src/web/components/system/use-process-signal.ts +87 -0
- package/src/web/components/tunnels/public-tunnel-switch.tsx +82 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/components/ui/dialog.tsx +14 -1
- package/src/web/components/ui/search-select.tsx +279 -0
- package/src/web/components/ui/start-ellipsis.tsx +18 -0
- package/src/web/hooks/use-chat.ts +28 -0
- package/src/web/hooks/use-global-keybindings.ts +7 -5
- package/src/web/hooks/use-lsp.ts +23 -2
- package/src/web/hooks/use-minute-clock.ts +20 -0
- package/src/web/hooks/use-whisper-voice-input.ts +113 -0
- package/src/web/lib/api-tunnels.ts +19 -0
- package/src/web/lib/branch-select-items.ts +29 -0
- package/src/web/lib/cpu-graph-mode.ts +90 -0
- package/src/web/lib/format-bytes.ts +14 -0
- package/src/web/lib/git-ref-picker.ts +120 -0
- package/src/web/lib/lsp/lsp-client.ts +32 -0
- package/src/web/lib/search-select-rows.ts +55 -0
- package/src/web/lib/temperature.ts +27 -0
- package/src/web/lib/voice-wav.ts +69 -0
- package/src/web/stores/git-status-store.ts +31 -17
- package/src/web/stores/keybindings-store.ts +14 -1
- package/src/web/stores/settings-store.ts +65 -0
- package/src/web/styles/globals.css +11 -0
- package/dist/web/assets/accounts-settings-section-DUVO32SQ.js +0 -1
- package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js +0 -1
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js.br +0 -0
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-D-mwEYFZ.js +0 -1
- package/dist/web/assets/api-client-BgedDTvo.js +0 -1
- package/dist/web/assets/api-client-c8YOpTlT.js.br +0 -0
- package/dist/web/assets/api-client-c8YOpTlT.js.gz +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.br +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.gz +0 -0
- package/dist/web/assets/api-settings-Bti1-Zn3.js +0 -1
- package/dist/web/assets/appearance-settings-section-tw6TpFb6.js +0 -1
- package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.gz +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.br +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-MBuMWp_i.js +0 -1
- package/dist/web/assets/branch-review-tab-MBuMWp_i.js.br +0 -0
- package/dist/web/assets/branch-review-tab-MBuMWp_i.js.gz +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.br +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.gz +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.gz +0 -0
- package/dist/web/assets/chat-tab-DVvnqhw6.js +0 -1
- package/dist/web/assets/chat-tab-DVvnqhw6.js.br +0 -0
- package/dist/web/assets/chat-tab-DVvnqhw6.js.gz +0 -0
- package/dist/web/assets/chat-tab-DaEHva22.js +0 -15
- package/dist/web/assets/chat-tab-DaEHva22.js.br +0 -0
- package/dist/web/assets/chat-tab-DaEHva22.js.gz +0 -0
- package/dist/web/assets/code-editor-Bu9lhTVp.js +0 -7
- package/dist/web/assets/code-editor-Bu9lhTVp.js.br +0 -0
- package/dist/web/assets/code-editor-Bu9lhTVp.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js +0 -1
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.gz +0 -0
- package/dist/web/assets/conflict-editor-BA3y-qSH.js.br +0 -0
- package/dist/web/assets/conflict-editor-BA3y-qSH.js.gz +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js +0 -1
- package/dist/web/assets/context-menu-CjYTq8DI.js.br +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js.gz +0 -0
- package/dist/web/assets/csv-preview-BioxkrQF.js.br +0 -0
- package/dist/web/assets/csv-preview-BioxkrQF.js.gz +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.gz +0 -0
- package/dist/web/assets/database-viewer-BSoJ0E6p.js.br +0 -0
- package/dist/web/assets/database-viewer-BSoJ0E6p.js.gz +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.br +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.gz +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.gz +0 -0
- package/dist/web/assets/design-tab-iU2CviE3.js +0 -33
- package/dist/web/assets/design-tab-iU2CviE3.js.br +0 -0
- package/dist/web/assets/design-tab-iU2CviE3.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.gz +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js +0 -1
- package/dist/web/assets/dialog-B8clVkyi.js.br +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js.gz +0 -0
- package/dist/web/assets/diff-viewer-lctWoSmG.js.br +0 -0
- package/dist/web/assets/diff-viewer-lctWoSmG.js.gz +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.br +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.gz +0 -0
- package/dist/web/assets/editor-language-service-fPG56hQO.js +0 -1
- package/dist/web/assets/editor-language-service-fPG56hQO.js.br +0 -0
- package/dist/web/assets/editor-language-service-fPG56hQO.js.gz +0 -0
- package/dist/web/assets/explorer-body-BgF7FsxI.js +0 -2
- package/dist/web/assets/explorer-body-BgF7FsxI.js.br +0 -0
- package/dist/web/assets/explorer-body-BgF7FsxI.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-C21w_7lf.js +0 -1
- package/dist/web/assets/explorer-window-content-C21w_7lf.js.br +0 -0
- package/dist/web/assets/explorer-window-content-C21w_7lf.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-rbkXsZVR.js.br +0 -0
- package/dist/web/assets/extension-manager-section-rbkXsZVR.js.gz +0 -0
- package/dist/web/assets/extension-webview-uPO0WgCq.js.br +0 -0
- package/dist/web/assets/extension-webview-uPO0WgCq.js.gz +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.br +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.gz +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.br +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.gz +0 -0
- package/dist/web/assets/files-settings-section-CT7mDb0L.js.br +0 -0
- package/dist/web/assets/files-settings-section-CT7mDb0L.js.gz +0 -0
- package/dist/web/assets/format-bytes-DmSHAKFe.js +0 -1
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.br +0 -0
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.gz +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.br +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.gz +0 -0
- package/dist/web/assets/general-settings-section-DANPfMRN.js.br +0 -0
- package/dist/web/assets/general-settings-section-DANPfMRN.js.gz +0 -0
- package/dist/web/assets/git-log-panel-BJPkLiEw.js.br +0 -0
- package/dist/web/assets/git-log-panel-BJPkLiEw.js.gz +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.br +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-R4PcAntf.js.br +0 -0
- package/dist/web/assets/glide-data-grid-R4PcAntf.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js +0 -1
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js.br +0 -0
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js.gz +0 -0
- package/dist/web/assets/index-C8nGqkKN.js +0 -52
- package/dist/web/assets/index-C8nGqkKN.js.br +0 -0
- package/dist/web/assets/index-C8nGqkKN.js.gz +0 -0
- package/dist/web/assets/index-DvClbTQx.css +0 -1
- package/dist/web/assets/index-DvClbTQx.css.br +0 -0
- package/dist/web/assets/index-DvClbTQx.css.gz +0 -0
- package/dist/web/assets/keybindings-store-BGKlQQAv.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.br +0 -0
- package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-vDNuKjel.js +0 -1
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-DS_XrpTG.js.br +0 -0
- package/dist/web/assets/new-design-dialog-DS_XrpTG.js.gz +0 -0
- package/dist/web/assets/notification-store-Dnnj20Ky.js +0 -1
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.gz +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.br +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.gz +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.gz +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js +0 -1
- package/dist/web/assets/overview-panel-BZAW_nag.js.br +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js.gz +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.br +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.br +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-D8AES1BA.js.gz +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.br +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.gz +0 -0
- package/dist/web/assets/problems-panel-DTBG4beI.js.br +0 -0
- package/dist/web/assets/problems-panel-DTBG4beI.js.gz +0 -0
- package/dist/web/assets/process-table-D_gR2XA6.js +0 -1
- package/dist/web/assets/process-table-D_gR2XA6.js.br +0 -0
- package/dist/web/assets/process-table-D_gR2XA6.js.gz +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.br +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-CqtmbeRj.js.gz +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.br +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-BKoPovy2.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-BKoPovy2.js.gz +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.br +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.gz +0 -0
- package/dist/web/assets/settings-body-k2qPgRFe.js +0 -2
- package/dist/web/assets/settings-body-k2qPgRFe.js.br +0 -0
- package/dist/web/assets/settings-body-k2qPgRFe.js.gz +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js +0 -2
- package/dist/web/assets/settings-store-Br5c1efC.js.br +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js.gz +0 -0
- package/dist/web/assets/settings-tab-Bb23VdBu.js +0 -1
- package/dist/web/assets/settings-window-content-ClarWusz.js +0 -1
- package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.br +0 -0
- package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js +0 -2
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js.br +0 -0
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-I1Igmoey.js +0 -1
- package/dist/web/assets/system-monitor-window-content-C5SeUv1H.js +0 -1
- package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-CwXpLi3X.js.br +0 -0
- package/dist/web/assets/team-member-sheet-CwXpLi3X.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-4swiiDz7.js.br +0 -0
- package/dist/web/assets/team-member-window-content-4swiiDz7.js.gz +0 -0
- package/dist/web/assets/terminal-tab-BGcdtMMT.js.br +0 -0
- package/dist/web/assets/terminal-tab-BGcdtMMT.js.gz +0 -0
- package/dist/web/assets/tool-cards-D0-krtA2.js.br +0 -0
- package/dist/web/assets/tool-cards-D0-krtA2.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js +0 -16
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js.br +0 -0
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js.gz +0 -0
- package/dist/web/assets/use-lsp-71azTXQz.js +0 -6
- package/dist/web/assets/use-lsp-71azTXQz.js.br +0 -0
- package/dist/web/assets/use-lsp-71azTXQz.js.gz +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.br +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.gz +0 -0
- package/dist/web/assets/video-preview-DxEX-uGo.js +0 -1
- package/dist/web/assets/video-preview-DxEX-uGo.js.br +0 -0
- package/dist/web/assets/video-preview-DxEX-uGo.js.gz +0 -0
- package/dist/web/assets/ws-auth-kXgsX2Js.js +0 -1
- /package/dist/web/assets/{__vite-browser-external-BpSQWxcq.js → __vite-browser-external-BILfYaAs.js} +0 -0
- /package/dist/web/assets/{run-in-terminal-HIdNgYim.js → run-in-terminal-2pSiup8e.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js → vendor-xterm-DF1JTfsy.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.br → vendor-xterm-DF1JTfsy.js.br} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.gz → vendor-xterm-DF1JTfsy.js.gz} +0 -0
|
@@ -9,6 +9,8 @@ import { homedir } from "node:os";
|
|
|
9
9
|
import type { ChatEvent, ChatMessage } from "../types/chat.ts";
|
|
10
10
|
import { stringifyToolResultContent } from "../shared/tool-result-content.ts";
|
|
11
11
|
import { stripSharedContext } from "../shared/provider-context.ts";
|
|
12
|
+
import { readLines } from "./read-lines.ts";
|
|
13
|
+
import { readCompactions, applyCompactions } from "./compaction-savings.ts";
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* How large a transcript this module will look at, for the callers that go
|
|
@@ -307,48 +309,6 @@ export function validateJsonlPath(inputPath: string, maxBytes = MAX_FILE_SIZE):
|
|
|
307
309
|
return real;
|
|
308
310
|
}
|
|
309
311
|
|
|
310
|
-
/**
|
|
311
|
-
* Yield a file's lines without holding the file in memory.
|
|
312
|
-
*
|
|
313
|
-
* `Bun.file().text()` plus `split("\n")` costs the whole transcript twice over —
|
|
314
|
-
* 277MB resident for a 77MB file, which is what forced a cap low enough to
|
|
315
|
-
* reject real sessions. Peak here is one chunk plus one line.
|
|
316
|
-
*/
|
|
317
|
-
async function* readLines(filePath: string, fromByte = 0): AsyncGenerator<string> {
|
|
318
|
-
const decoder = new TextDecoder();
|
|
319
|
-
// Explicit reader rather than `for await` over the stream: the DOM lib's
|
|
320
|
-
// ReadableStream is not typed as async-iterable, and the `finally` is what
|
|
321
|
-
// releases it when a caller breaks early on `beforeUuid`.
|
|
322
|
-
// One byte *before* the offset, so the first line the split produces is
|
|
323
|
-
// always the tail of the record the offset fell inside — the empty string
|
|
324
|
-
// when it fell exactly on a newline. That makes the drop below unconditional
|
|
325
|
-
// instead of a boundary case, and a boundary case here silently costs a whole
|
|
326
|
-
// record rather than a fragment.
|
|
327
|
-
const file = fromByte > 0 ? Bun.file(filePath).slice(fromByte - 1) : Bun.file(filePath);
|
|
328
|
-
const reader = file.stream().getReader();
|
|
329
|
-
// A fragment is not always unparseable — the tail of one record is sometimes
|
|
330
|
-
// a complete smaller document — so it has to go by position rather than be
|
|
331
|
-
// left for `JSON.parse` to reject, or it enters as an invented message.
|
|
332
|
-
let dropPartial = fromByte > 0;
|
|
333
|
-
let buffered = "";
|
|
334
|
-
try {
|
|
335
|
-
while (true) {
|
|
336
|
-
const { done, value } = await reader.read();
|
|
337
|
-
if (done) break;
|
|
338
|
-
buffered += decoder.decode(value, { stream: true });
|
|
339
|
-
const lines = buffered.split("\n");
|
|
340
|
-
buffered = lines.pop() ?? "";
|
|
341
|
-
for (const line of lines) {
|
|
342
|
-
if (dropPartial) { dropPartial = false; continue; }
|
|
343
|
-
yield line;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
} finally {
|
|
347
|
-
reader.releaseLock();
|
|
348
|
-
}
|
|
349
|
-
buffered += decoder.decode();
|
|
350
|
-
if (buffered && !dropPartial) yield buffered;
|
|
351
|
-
}
|
|
352
312
|
|
|
353
313
|
/**
|
|
354
314
|
* Read a JSONL transcript file, parse entries, apply merge/nest pipeline, return ChatMessage[].
|
|
@@ -418,6 +378,11 @@ export async function parseJsonlTranscript(
|
|
|
418
378
|
const { mergeSubagentChildren } = await import("./subagent-transcript-merger.ts");
|
|
419
379
|
mergeSubagentChildren(filePath.replace(/\.jsonl$/, ""), merged);
|
|
420
380
|
|
|
381
|
+
// An expanded segment opens with its own compact summary, so it needs the same
|
|
382
|
+
// divider the newest one gets — otherwise only the last compaction in a chat is
|
|
383
|
+
// labelled and the earlier ones read as ordinary messages.
|
|
384
|
+
applyCompactions(merged, await readCompactions(filePath).catch(() => new Map()));
|
|
385
|
+
|
|
421
386
|
return merged.filter(
|
|
422
387
|
(msg) => msg.content.trim().length > 0 || (msg.events && msg.events.length > 0),
|
|
423
388
|
);
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installing a language server, when someone presses Install.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here runs because a file was opened. The editor says which server is missing and
|
|
5
|
+
* offers the button; pressing it is the consent, and that is the whole difference between this
|
|
6
|
+
* and an editor that fetches a binary off the network on its own.
|
|
7
|
+
*
|
|
8
|
+
* Four decisions worth keeping:
|
|
9
|
+
*
|
|
10
|
+
* - **Into `<ppm dir>/lsp-servers`, never `-g` and never `~/go/bin`.** A global
|
|
11
|
+
* `bun add typescript@5` replaces whatever TypeScript the user had installed globally, and a
|
|
12
|
+
* global bin directory is only found when it happens to be on the *service's* PATH — which
|
|
13
|
+
* for a systemd-started PPM is four entries and nothing of the sort. PPM's own directory is
|
|
14
|
+
* neither, `PPM_HOME` already isolates it for tests, and deleting it is the whole uninstall.
|
|
15
|
+
* `rustup` is the one exception, below.
|
|
16
|
+
* - **The packages come from the registry, by server id.** Nothing the browser sends is ever
|
|
17
|
+
* part of a command, so the route cannot be talked into installing something else.
|
|
18
|
+
* - **PPM installs servers, never toolchains.** `go install` needs a Go and
|
|
19
|
+
* `rustup component add` needs a rustup; where the host has neither, there is no button at
|
|
20
|
+
* all rather than one that fails when pressed.
|
|
21
|
+
* - **The directory gets a `package.json` before `bun add` runs.** Measured: `bun add` in a
|
|
22
|
+
* directory with no manifest does not create one there — it walks *up* to the nearest parent
|
|
23
|
+
* that has one and installs into that, leaving the directory it was asked about empty.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
26
|
+
import path from "node:path";
|
|
27
|
+
import { resolveBunPath } from "../autostart-generator.ts";
|
|
28
|
+
import { getPpmDir } from "../ppm-dir.ts";
|
|
29
|
+
import {
|
|
30
|
+
installedBinaryPath,
|
|
31
|
+
installedServerEntry,
|
|
32
|
+
packageName,
|
|
33
|
+
type LanguageServerDefinition,
|
|
34
|
+
type LanguageServerInstall,
|
|
35
|
+
} from "./server-registry.ts";
|
|
36
|
+
|
|
37
|
+
/** Generous: a cold `go install` builds the server from source. 10.6 s here, minutes elsewhere. */
|
|
38
|
+
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
39
|
+
/** `rustup which` is a lookup, not work. Measured at 3 ms; anything near this is a hung host. */
|
|
40
|
+
const QUERY_TIMEOUT_MS = 10_000;
|
|
41
|
+
|
|
42
|
+
/** Where the Install button puts servers. Inside the PPM dir, so `PPM_HOME` isolates it. */
|
|
43
|
+
export function lspInstallDir(): string {
|
|
44
|
+
return path.join(getPpmDir(), "lsp-servers");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RunResult {
|
|
48
|
+
code: number;
|
|
49
|
+
stdout: string;
|
|
50
|
+
stderr: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* How this module runs a command.
|
|
55
|
+
*
|
|
56
|
+
* One injectable seam for all of it, so a test can drive an install with no toolchain on the
|
|
57
|
+
* machine — and named rather than `typeof Bun.spawn`, whose type widens the pipes back to a
|
|
58
|
+
* union with a raw file descriptor that `new Response()` will not take.
|
|
59
|
+
*/
|
|
60
|
+
export type Runner = (
|
|
61
|
+
cmd: string[],
|
|
62
|
+
options: { cwd: string; env?: Record<string, string | undefined>; timeoutMs: number },
|
|
63
|
+
) => Promise<RunResult>;
|
|
64
|
+
|
|
65
|
+
const runCommand: Runner = async (cmd, { cwd, env, timeoutMs }) => {
|
|
66
|
+
const proc = Bun.spawn(cmd, { cwd, env: env as Record<string, string> | undefined, stdout: "pipe", stderr: "pipe" });
|
|
67
|
+
const timer = setTimeout(() => proc.kill(), timeoutMs);
|
|
68
|
+
// Drained while it runs: a pipe nobody reads fills up, and a full pipe blocks the child
|
|
69
|
+
// instead of failing it, so awaiting the exit first can wait forever on a noisy install.
|
|
70
|
+
const [code, stdout, stderr] = await Promise.all([
|
|
71
|
+
proc.exited,
|
|
72
|
+
new Response(proc.stdout).text(),
|
|
73
|
+
new Response(proc.stderr).text(),
|
|
74
|
+
]);
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
return { code, stdout, stderr };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The binary that would carry out this plan, or null where the host has none.
|
|
81
|
+
*
|
|
82
|
+
* The lookup is `PATH`, deliberately: it is the same PATH a language server PPM starts will
|
|
83
|
+
* inherit, so a host where PPM cannot see the Go toolchain is also a host where gopls could
|
|
84
|
+
* not run `go list`. Offering the button there would install a server that cannot work.
|
|
85
|
+
*/
|
|
86
|
+
export function installToolPath(plan: LanguageServerInstall): string | null {
|
|
87
|
+
if (plan.with === "bun") {
|
|
88
|
+
try {
|
|
89
|
+
return resolveBunPath();
|
|
90
|
+
} catch {
|
|
91
|
+
return null; // a compiled PPM on a host with no bun
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// The PATH is passed explicitly because `Bun.which` otherwise reads the one the *process
|
|
95
|
+
// started with* and never looks at `process.env.PATH` again — measured: adding a directory to
|
|
96
|
+
// it changes nothing, and neither does emptying it. The tool is then spawned by the absolute
|
|
97
|
+
// path this returns, so the lookup and the run can never disagree.
|
|
98
|
+
return Bun.which(plan.with === "go" ? "go" : "rustup", { PATH: process.env.PATH ?? "" });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Whether the Install button can work here. The editor draws the button from this. */
|
|
102
|
+
export function canInstall(definition: LanguageServerDefinition): boolean {
|
|
103
|
+
return definition.install ? installToolPath(definition.install) !== null : false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Where rustup says this server is, or null when the component is not installed.
|
|
108
|
+
*
|
|
109
|
+
* `rustup which` is both the lookup and the installed-check, and it has to be: the proxy in
|
|
110
|
+
* `~/.cargo/bin` exists whether or not the component does — measured on this host, with no
|
|
111
|
+
* rust-analyzer installed, `~/.cargo/bin/rust-analyzer` is right there pointing at rustup — so
|
|
112
|
+
* a path test would report a server that cannot start. It answers in 3 ms, and it answers for
|
|
113
|
+
* the toolchain `cwd` selects, which a symlink made at install time could not.
|
|
114
|
+
*/
|
|
115
|
+
export async function rustupServerPath(
|
|
116
|
+
definition: LanguageServerDefinition,
|
|
117
|
+
cwd: string,
|
|
118
|
+
run: Runner = runCommand,
|
|
119
|
+
): Promise<string | null> {
|
|
120
|
+
if (definition.install?.with !== "rustup") return null;
|
|
121
|
+
const rustup = installToolPath(definition.install);
|
|
122
|
+
if (!rustup) return null;
|
|
123
|
+
try {
|
|
124
|
+
const result = await run([rustup, "which", definition.command], { cwd, timeoutMs: QUERY_TIMEOUT_MS });
|
|
125
|
+
const found = result.stdout.trim();
|
|
126
|
+
return result.code === 0 && found ? found : null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** One at a time: every install and uninstall shares one `package.json`, lockfile and toolchain. */
|
|
133
|
+
let queue: Promise<unknown> = Promise.resolve();
|
|
134
|
+
/** Keyed by operation *and* id, so a second Uninstall click joins that uninstall — never an install. */
|
|
135
|
+
const inFlight = new Map<string, Promise<void>>();
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Install one server, and resolve once it is really there.
|
|
139
|
+
*
|
|
140
|
+
* Two clicks on one button — a second tab, an impatient user — join the same install rather
|
|
141
|
+
* than starting a second one over the first one's lockfile.
|
|
142
|
+
*/
|
|
143
|
+
export function installLanguageServer(
|
|
144
|
+
definition: LanguageServerDefinition,
|
|
145
|
+
options: { projectPath?: string; run?: Runner } = {},
|
|
146
|
+
): Promise<void> {
|
|
147
|
+
return enqueue(`install:${definition.id}`, () => install(definition, options.projectPath, options.run ?? runCommand));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Remove one server, running the exact inverse of what installed it.
|
|
152
|
+
*
|
|
153
|
+
* Offered only for a server PPM itself put there, which is a question about where it was
|
|
154
|
+
* *found* rather than about what is on this machine — `lsp-manager.availability` answers it
|
|
155
|
+
* with `origin`, and the route gates on that. A copy on `PATH` is the user's, one in a
|
|
156
|
+
* project's `node_modules` is the repository's, and the one PPM ships is its own dependency:
|
|
157
|
+
* deleting any of the three would be PPM tidying up after someone else.
|
|
158
|
+
*
|
|
159
|
+
* rustup is again the exception that reaches outside PPM's directory, for the same reason
|
|
160
|
+
* installing it did — the component belongs to a toolchain and there is no copy of PPM's to
|
|
161
|
+
* delete. Removing it is one `rustup component add` away from being back.
|
|
162
|
+
*/
|
|
163
|
+
export function uninstallLanguageServer(
|
|
164
|
+
definition: LanguageServerDefinition,
|
|
165
|
+
options: { projectPath?: string; run?: Runner } = {},
|
|
166
|
+
): Promise<void> {
|
|
167
|
+
return enqueue(`uninstall:${definition.id}`, () => uninstall(definition, options.projectPath, options.run ?? runCommand));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function enqueue(key: string, work: () => Promise<void>): Promise<void> {
|
|
171
|
+
const existing = inFlight.get(key);
|
|
172
|
+
if (existing) return existing;
|
|
173
|
+
|
|
174
|
+
const started = queue.then(work);
|
|
175
|
+
queue = started.catch(() => {}); // a failure must not poison the queue behind it
|
|
176
|
+
const tracked = started.finally(() => inFlight.delete(key));
|
|
177
|
+
inFlight.set(key, tracked);
|
|
178
|
+
return tracked;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function install(definition: LanguageServerDefinition, projectPath: string | undefined, run: Runner): Promise<void> {
|
|
182
|
+
const plan = definition.install;
|
|
183
|
+
if (!plan) throw new Error(`PPM cannot install ${definition.displayName}`);
|
|
184
|
+
const tool = installToolPath(plan);
|
|
185
|
+
if (!tool) {
|
|
186
|
+
throw new Error(`${plan.with} is not installed on this host, so PPM cannot install ${definition.displayName}.`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const dir = lspInstallDir();
|
|
190
|
+
const failed = (result: RunResult, what: string) => new Error(`${what} failed to install: ${output(result)}`);
|
|
191
|
+
|
|
192
|
+
if (plan.with === "bun") {
|
|
193
|
+
ensureInstallDir(dir);
|
|
194
|
+
const result = await run([tool, "add", ...plan.packages], { cwd: dir, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
195
|
+
if (result.code !== 0) throw failed(result, plan.packages.join(" "));
|
|
196
|
+
// bun can succeed and still leave nothing to run — a package that dropped its binary, or a
|
|
197
|
+
// name that now points somewhere else. Saying so beats a second "not installed".
|
|
198
|
+
if (!installedServerEntry(definition, dir)) {
|
|
199
|
+
throw new Error(`Installed ${plan.packages.join(" ")}, but it provides no ${definition.command}`);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (plan.with === "go") {
|
|
205
|
+
const binDir = path.join(dir, "bin");
|
|
206
|
+
mkdirSync(binDir, { recursive: true });
|
|
207
|
+
// `GOBIN` is the whole reason this lands in PPM's directory rather than `~/go/bin`. The
|
|
208
|
+
// module and build caches stay where the user's Go already keeps them — those are the
|
|
209
|
+
// toolchain's, and re-downloading the world into the PPM directory would be rude.
|
|
210
|
+
const result = await run([tool, "install", plan.module], {
|
|
211
|
+
cwd: dir,
|
|
212
|
+
env: { ...process.env, GOBIN: binDir },
|
|
213
|
+
timeoutMs: INSTALL_TIMEOUT_MS,
|
|
214
|
+
});
|
|
215
|
+
if (result.code !== 0) throw failed(result, plan.module);
|
|
216
|
+
const built = installedBinaryPath(definition, dir);
|
|
217
|
+
if (!built || !existsSync(built)) {
|
|
218
|
+
throw new Error(`Built ${plan.module}, but no ${definition.command} appeared in ${binDir}`);
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// rustup. Run where the project is, so a repository pinned to its own toolchain gets the
|
|
224
|
+
// component added to *that* one rather than to whichever is default.
|
|
225
|
+
const cwd = projectPath && existsSync(projectPath) ? projectPath : process.cwd();
|
|
226
|
+
const result = await run([tool, "component", "add", plan.component], { cwd, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
227
|
+
if (result.code !== 0) throw failed(result, plan.component);
|
|
228
|
+
if (!(await rustupServerPath(definition, cwd, run))) {
|
|
229
|
+
throw new Error(`rustup reported success but still has no ${definition.command} for this toolchain`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function uninstall(definition: LanguageServerDefinition, projectPath: string | undefined, run: Runner): Promise<void> {
|
|
234
|
+
const plan = definition.install;
|
|
235
|
+
// Both of these are the route's job to prevent; they are here because this function deletes
|
|
236
|
+
// things, and "the caller checked" is not a property a deleting function should rely on.
|
|
237
|
+
if (!plan) throw new Error(`PPM did not install ${definition.displayName}, so there is nothing to remove.`);
|
|
238
|
+
const dir = lspInstallDir();
|
|
239
|
+
|
|
240
|
+
if (plan.with === "bun") {
|
|
241
|
+
if (!installedServerEntry(definition, dir)) {
|
|
242
|
+
throw new Error(`${definition.displayName} is not in PPM's own folder, so there is nothing for PPM to remove.`);
|
|
243
|
+
}
|
|
244
|
+
const tool = installToolPath(plan);
|
|
245
|
+
if (!tool) throw new Error(`bun is not installed on this host, so PPM cannot remove ${definition.displayName}.`);
|
|
246
|
+
// By name, never by the spec that installed it: `bun remove typescript@5` is not the same
|
|
247
|
+
// request as `bun remove typescript`, and the manifest holds the name.
|
|
248
|
+
const names = plan.packages.map(packageName);
|
|
249
|
+
const result = await run([tool, "remove", ...names], { cwd: dir, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
250
|
+
if (result.code !== 0) throw new Error(`Could not remove ${names.join(" ")}: ${output(result)}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (plan.with === "go") {
|
|
255
|
+
const binary = installedBinaryPath(definition, dir);
|
|
256
|
+
if (!binary || !existsSync(binary)) {
|
|
257
|
+
throw new Error(`${definition.displayName} is not in PPM's own folder, so there is nothing for PPM to remove.`);
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
rmSync(binary);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
// Windows refuses to unlink a running executable, which is the one platform where an
|
|
263
|
+
// uninstall fails for a reason the user can act on.
|
|
264
|
+
throw new Error(`Could not delete ${binary}: ${(e as Error).message}`);
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// rustup, in the same directory the install would have used, so the component leaves the
|
|
270
|
+
// toolchain it was added to rather than whichever happens to be default.
|
|
271
|
+
const cwd = projectPath && existsSync(projectPath) ? projectPath : process.cwd();
|
|
272
|
+
const tool = installToolPath(plan);
|
|
273
|
+
if (!tool) throw new Error(`rustup is not installed on this host, so PPM cannot remove ${definition.displayName}.`);
|
|
274
|
+
const result = await run([tool, "component", "remove", plan.component], { cwd, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
275
|
+
if (result.code !== 0) throw new Error(`Could not remove ${plan.component}: ${output(result)}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** What a failed command said, bounded — a build log's last 400 characters hold the reason. */
|
|
279
|
+
function output(result: RunResult): string {
|
|
280
|
+
return (result.stderr.trim() || result.stdout.trim()).slice(-400) || `exit ${result.code}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function ensureInstallDir(dir: string): void {
|
|
284
|
+
mkdirSync(dir, { recursive: true });
|
|
285
|
+
const manifest = path.join(dir, "package.json");
|
|
286
|
+
if (!existsSync(manifest)) {
|
|
287
|
+
writeFileSync(manifest, `${JSON.stringify({ name: "ppm-lsp-servers", private: true, dependencies: {} }, null, 2)}\n`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -16,14 +16,20 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { stat } from "node:fs/promises";
|
|
18
18
|
import path from "node:path";
|
|
19
|
+
import { resolveBunPath } from "../autostart-generator.ts";
|
|
20
|
+
import { canInstall, lspInstallDir, rustupServerPath } from "./lsp-install.ts";
|
|
19
21
|
import { LspSession, type LspSessionState } from "./lsp-session.ts";
|
|
20
22
|
import {
|
|
21
23
|
LANGUAGE_SERVERS,
|
|
22
24
|
ancestorDirs,
|
|
23
25
|
bundledServerEntry,
|
|
24
26
|
candidateCommandPaths,
|
|
27
|
+
installedBinaryPath,
|
|
28
|
+
installedServerEntry,
|
|
25
29
|
lspLanguageForPath,
|
|
30
|
+
serversSharingInstall,
|
|
26
31
|
type LanguageServerDefinition,
|
|
32
|
+
type LanguageServerInstall,
|
|
27
33
|
} from "./server-registry.ts";
|
|
28
34
|
|
|
29
35
|
/** How long a session with no subscribers is kept before being shut down. */
|
|
@@ -52,10 +58,34 @@ export type LspUnavailableReason = "no-language" | "not-installed" | "failed";
|
|
|
52
58
|
export interface LspUnavailable {
|
|
53
59
|
reason: LspUnavailableReason;
|
|
54
60
|
/** The server that would have served it, when one is known. */
|
|
55
|
-
server?: {
|
|
61
|
+
server?: {
|
|
62
|
+
id: string;
|
|
63
|
+
displayName: string;
|
|
64
|
+
installHint: string;
|
|
65
|
+
installable: boolean;
|
|
66
|
+
/** What the button would use, so the editor can say what pressing it does. */
|
|
67
|
+
installWith?: "bun" | "go" | "rustup";
|
|
68
|
+
};
|
|
56
69
|
message: string;
|
|
57
70
|
}
|
|
58
71
|
|
|
72
|
+
/**
|
|
73
|
+
* What the editor needs to name a missing server, and to decide whether to offer to install it.
|
|
74
|
+
*
|
|
75
|
+
* `installable` is the host's answer rather than the editor's guess, and it is not the registry's
|
|
76
|
+
* either: `go install` needs a Go on this machine and `rustup component add` needs a rustup, so
|
|
77
|
+
* the same server is installable on one host and a command to copy on the next.
|
|
78
|
+
*/
|
|
79
|
+
function serverSummary(definition: LanguageServerDefinition): NonNullable<LspUnavailable["server"]> {
|
|
80
|
+
return {
|
|
81
|
+
id: definition.id,
|
|
82
|
+
displayName: definition.displayName,
|
|
83
|
+
installHint: definition.installHint,
|
|
84
|
+
installable: canInstall(definition),
|
|
85
|
+
installWith: definition.install?.with,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
59
89
|
export function isUnavailable(result: LspHandle | LspUnavailable): result is LspUnavailable {
|
|
60
90
|
return "reason" in result;
|
|
61
91
|
}
|
|
@@ -68,6 +98,33 @@ interface Entry {
|
|
|
68
98
|
lastUsed: number;
|
|
69
99
|
}
|
|
70
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The bun that runs a server's entry script.
|
|
103
|
+
*
|
|
104
|
+
* Not `process.execPath`: that is bun only while PPM runs from source. A compiled PPM is its
|
|
105
|
+
* own executable, so spawning it with a script path reaches PPM's *CLI* — the same trap that
|
|
106
|
+
* made a compiled PPM answer `<ppm> x @openai/codex app-server` with "unknown command". `null`
|
|
107
|
+
* on a host with no bun at all, where such a copy cannot be run and must not be called
|
|
108
|
+
* installed.
|
|
109
|
+
*/
|
|
110
|
+
function bunRuntime(): string | null {
|
|
111
|
+
try {
|
|
112
|
+
return resolveBunPath();
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Where a server was found, in the order `resolveCommand` looks.
|
|
120
|
+
*
|
|
121
|
+
* It exists for the Remove button: PPM may delete what PPM installed and nothing else.
|
|
122
|
+
* `project` is the repository's copy, `path` is one the user installed themselves, and
|
|
123
|
+
* `bundled` is PPM's own dependency — removing any of those would be PPM tidying up after
|
|
124
|
+
* someone else, and for `bundled` it would break the TypeScript server a fresh install has.
|
|
125
|
+
*/
|
|
126
|
+
export type ServerOrigin = "project" | "rustup" | "path" | "ppm" | "bundled";
|
|
127
|
+
|
|
71
128
|
async function exists(candidate: string): Promise<boolean> {
|
|
72
129
|
try {
|
|
73
130
|
await stat(candidate);
|
|
@@ -107,6 +164,8 @@ export class LspManager {
|
|
|
107
164
|
private readonly servers: LanguageServerDefinition[] = LANGUAGE_SERVERS,
|
|
108
165
|
private readonly idleGraceMs: number = IDLE_GRACE_MS,
|
|
109
166
|
private readonly maxSessions: number = MAX_SESSIONS,
|
|
167
|
+
/** Called rather than held, so `PPM_HOME` is read when a server is looked for, not at import. */
|
|
168
|
+
private readonly installDir: () => string = lspInstallDir,
|
|
110
169
|
) {}
|
|
111
170
|
|
|
112
171
|
onNotification(listener: (key: string, method: string, params: unknown) => void): () => void {
|
|
@@ -136,8 +195,8 @@ export class LspManager {
|
|
|
136
195
|
|
|
137
196
|
let lastMissing: LanguageServerDefinition | null = null;
|
|
138
197
|
for (const definition of candidates) {
|
|
139
|
-
const
|
|
140
|
-
if (!
|
|
198
|
+
const resolved = await this.resolveCommand(definition, dirs);
|
|
199
|
+
if (!resolved) {
|
|
141
200
|
lastMissing = definition;
|
|
142
201
|
continue;
|
|
143
202
|
}
|
|
@@ -146,7 +205,7 @@ export class LspManager {
|
|
|
146
205
|
|
|
147
206
|
this.claim(key);
|
|
148
207
|
try {
|
|
149
|
-
const session = await this.startOrReuse(key, definition, command, rootPath);
|
|
208
|
+
const session = await this.startOrReuse(key, definition, resolved.command, rootPath);
|
|
150
209
|
// Subscribe *then* trim, so the session this call is about to hand out is never the
|
|
151
210
|
// one the cap takes away.
|
|
152
211
|
if (!this.subscribe(key, subscriber)) {
|
|
@@ -161,7 +220,7 @@ export class LspManager {
|
|
|
161
220
|
} catch (e) {
|
|
162
221
|
return {
|
|
163
222
|
reason: "failed",
|
|
164
|
-
server:
|
|
223
|
+
server: serverSummary(definition),
|
|
165
224
|
message: e instanceof Error ? e.message : String(e),
|
|
166
225
|
};
|
|
167
226
|
} finally {
|
|
@@ -172,7 +231,7 @@ export class LspManager {
|
|
|
172
231
|
const missing = lastMissing ?? candidates[0]!;
|
|
173
232
|
return {
|
|
174
233
|
reason: "not-installed",
|
|
175
|
-
server:
|
|
234
|
+
server: serverSummary(missing),
|
|
176
235
|
message: `${missing.displayName} is not installed.`,
|
|
177
236
|
};
|
|
178
237
|
}
|
|
@@ -322,25 +381,51 @@ export class LspManager {
|
|
|
322
381
|
*
|
|
323
382
|
* The project's own copy first, because a repository pinned to TypeScript 4 has to be
|
|
324
383
|
* analysed by its own server — the same reason VS Code offers "Use Workspace Version".
|
|
325
|
-
* Then
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
384
|
+
* Then rustup, which owns its answer completely (below). Then `PATH`, which is whatever the
|
|
385
|
+
* user deliberately installed. Then PPM's own directory: what the Install button put there,
|
|
386
|
+
* and then the copy PPM ships, which is the floor rather than a preference — it is how a
|
|
387
|
+
* fresh install has a working TypeScript server with nothing else done, and it must never win
|
|
388
|
+
* over either of the two choices someone actually made.
|
|
329
389
|
*/
|
|
330
|
-
private async resolveCommand(
|
|
390
|
+
private async resolveCommand(
|
|
391
|
+
definition: LanguageServerDefinition,
|
|
392
|
+
dirs: string[],
|
|
393
|
+
): Promise<{ command: string[]; origin: ServerOrigin } | null> {
|
|
331
394
|
const candidates = candidateCommandPaths(definition.command, dirs);
|
|
332
395
|
for (const candidate of candidates.slice(0, -1)) {
|
|
333
|
-
if (await exists(candidate)) return [candidate];
|
|
396
|
+
if (await exists(candidate)) return { command: [candidate], origin: "project" };
|
|
334
397
|
}
|
|
398
|
+
// rust-analyzer is a toolchain component rather than a file PPM owns, so rustup is asked
|
|
399
|
+
// where it is — in the file's own directory, so a repository pinning a toolchain gets that
|
|
400
|
+
// toolchain's server. This has to come *before* PATH, because `~/.cargo/bin/rust-analyzer`
|
|
401
|
+
// is a rustup proxy that exists whether or not the component does: found on PATH it reports
|
|
402
|
+
// an installed server, and spawning it prints "unknown binary" and exits, which is a broken
|
|
403
|
+
// server rather than the missing one the Install button is offered for. Costs nothing for
|
|
404
|
+
// every other server — it answers null on sight of a definition that is not rustup's.
|
|
405
|
+
const fromRustup = await rustupServerPath(definition, dirs[0] ?? process.cwd());
|
|
406
|
+
if (fromRustup) return { command: [fromRustup], origin: "rustup" };
|
|
407
|
+
|
|
335
408
|
// The last candidate is the bare command, which means PATH.
|
|
336
409
|
const onPath = Bun.which(definition.command);
|
|
337
|
-
if (onPath) return [onPath];
|
|
410
|
+
if (onPath) return { command: [onPath], origin: "path" };
|
|
411
|
+
|
|
412
|
+
// What the Install button built with the host's Go: a real binary in PPM's own directory,
|
|
413
|
+
// spawned as itself.
|
|
414
|
+
const binary = installedBinaryPath(definition, this.installDir());
|
|
415
|
+
if (binary && (await exists(binary))) return { command: [binary], origin: "ppm" };
|
|
338
416
|
|
|
339
|
-
|
|
340
|
-
// Run by the runtime PPM is already using, not through npm's `.bin` shim: that shim is
|
|
417
|
+
// Both of PPM's npm copies are entry scripts rather than npm's `.bin` shims: that shim is
|
|
341
418
|
// `#!/usr/bin/env node`, and someone who installed PPM with bun may have no node at all —
|
|
342
419
|
// which fails as exit code 127 at spawn time, long after the server looked installed.
|
|
343
|
-
|
|
420
|
+
for (const [entry, origin] of [
|
|
421
|
+
[installedServerEntry(definition, this.installDir()), "ppm"],
|
|
422
|
+
[bundledServerEntry(definition), "bundled"],
|
|
423
|
+
] as const) {
|
|
424
|
+
if (!entry || !(await exists(entry))) continue;
|
|
425
|
+
const runtime = bunRuntime();
|
|
426
|
+
return runtime ? { command: [runtime, entry], origin } : null;
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
344
429
|
}
|
|
345
430
|
|
|
346
431
|
/**
|
|
@@ -382,24 +467,70 @@ export class LspManager {
|
|
|
382
467
|
}
|
|
383
468
|
|
|
384
469
|
/**
|
|
385
|
-
* Which of the registered servers
|
|
470
|
+
* Which of the registered servers could actually be used.
|
|
386
471
|
*
|
|
387
|
-
* Answers the editor's "why is nothing happening" question directly, with the
|
|
388
|
-
*
|
|
472
|
+
* Answers the editor's "why is nothing happening" question directly, with the install
|
|
473
|
+
* command for anything missing. With no `projectPath` it answers for the *machine* — no
|
|
474
|
+
* project `node_modules/.bin` is consulted — which is what the Settings pane asks, since it
|
|
475
|
+
* is not open on any one project.
|
|
389
476
|
*/
|
|
390
|
-
async availability(projectPath
|
|
391
|
-
Array<{
|
|
477
|
+
async availability(projectPath?: string): Promise<
|
|
478
|
+
Array<{
|
|
479
|
+
id: string;
|
|
480
|
+
displayName: string;
|
|
481
|
+
languages: string[];
|
|
482
|
+
installed: boolean;
|
|
483
|
+
installHint: string;
|
|
484
|
+
installable: boolean;
|
|
485
|
+
installWith?: LanguageServerInstall["with"];
|
|
486
|
+
origin?: ServerOrigin;
|
|
487
|
+
/** Whether PPM may remove it — that is, whether PPM is what put it there. */
|
|
488
|
+
removable: boolean;
|
|
489
|
+
/** Servers the same removal would also take, because one npm package provides several. */
|
|
490
|
+
alsoRemoves?: string[];
|
|
491
|
+
}>
|
|
392
492
|
> {
|
|
393
|
-
const dirs = [projectPath];
|
|
493
|
+
const dirs = projectPath ? [projectPath] : [];
|
|
394
494
|
return Promise.all(
|
|
395
|
-
this.servers.map(async (definition) =>
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
495
|
+
this.servers.map(async (definition) => {
|
|
496
|
+
const resolved = await this.resolveCommand(definition, dirs);
|
|
497
|
+
const origin = resolved?.origin;
|
|
498
|
+
const removable = origin === "ppm" || origin === "rustup";
|
|
499
|
+
const shared = removable ? serversSharingInstall(definition).map((s) => s.displayName) : [];
|
|
500
|
+
return {
|
|
501
|
+
id: definition.id,
|
|
502
|
+
displayName: definition.displayName,
|
|
503
|
+
languages: definition.languages,
|
|
504
|
+
installed: resolved !== null,
|
|
505
|
+
installHint: definition.installHint,
|
|
506
|
+
installable: canInstall(definition),
|
|
507
|
+
installWith: definition.install?.with,
|
|
508
|
+
origin,
|
|
509
|
+
removable,
|
|
510
|
+
...(shared.length > 0 ? { alsoRemoves: shared } : {}),
|
|
511
|
+
};
|
|
512
|
+
}),
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Stop this server's idle sessions, for an uninstall that is about to delete it.
|
|
518
|
+
*
|
|
519
|
+
* Only the ones nobody is holding. A session with a subscriber is an editor open on it, and
|
|
520
|
+
* taking that away answers every later request with "the language server is no longer
|
|
521
|
+
* running" until the tab is closed and reopened — the same reason `enforceSessionCap` leaves
|
|
522
|
+
* held sessions alone. One that keeps running is running on files it already opened, which
|
|
523
|
+
* is harmless everywhere except Windows, where it is also what refuses the unlink.
|
|
524
|
+
*/
|
|
525
|
+
async stopIdle(serverId: string): Promise<void> {
|
|
526
|
+
const doomed = [...this.entries.entries()].filter(
|
|
527
|
+
([, entry]) => entry.session.definition.id === serverId && entry.subscribers.size === 0,
|
|
402
528
|
);
|
|
529
|
+
for (const [key, entry] of doomed) {
|
|
530
|
+
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
531
|
+
this.entries.delete(key);
|
|
532
|
+
}
|
|
533
|
+
await Promise.all(doomed.map(([, entry]) => entry.session.dispose()));
|
|
403
534
|
}
|
|
404
535
|
|
|
405
536
|
/**
|