@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
|
@@ -4,14 +4,20 @@
|
|
|
4
4
|
* already anchors the `/proc/<pid>/stat` parse on the last `)` so a process
|
|
5
5
|
* name containing spaces cannot shift the fields.
|
|
6
6
|
*
|
|
7
|
-
* Per-process VRAM needs the one `nvidia-smi` call
|
|
8
|
-
*
|
|
7
|
+
* Per-process VRAM needs the one `nvidia-smi` call. Per-process GPU busy % comes
|
|
8
|
+
* from DRM fdinfo where the driver publishes it (Intel, AMD) — the proprietary
|
|
9
|
+
* NVIDIA driver publishes neither, so an NVIDIA-only host still fills memory
|
|
10
|
+
* alone.
|
|
9
11
|
*/
|
|
10
12
|
import { readProcTable, type ProcEntry } from "../proc-table-linux.ts";
|
|
11
13
|
import type { ProcessCollection, ProcessCollector, RawProcessRow } from "./process-collector-types.ts";
|
|
12
14
|
import { createStickyColumns } from "./process-collector-types.ts";
|
|
13
15
|
import { readProcIoBytes, type ProcIoBytes } from "./process-io-linux.ts";
|
|
16
|
+
import { readProcSwapMB } from "./process-swap-linux.ts";
|
|
17
|
+
import { realLinuxFs } from "./linux-fs.ts";
|
|
18
|
+
import { serviceKeyFromCgroup } from "../system-services/app-cgroup-linux.ts";
|
|
14
19
|
import type { ProcessGpuMemoryCollector } from "./gpu-process-memory-nvidia.ts";
|
|
20
|
+
import type { DrmGpuCollector } from "./gpu-fdinfo-linux.ts";
|
|
15
21
|
|
|
16
22
|
const KB_PER_MB = 1024;
|
|
17
23
|
|
|
@@ -36,10 +42,20 @@ export function procEntriesToRows(entries: readonly ProcEntry[]): RawProcessRow[
|
|
|
36
42
|
export interface LinuxProcessCollectorOptions {
|
|
37
43
|
/** Injected so unit tests never touch a real `/proc`. */
|
|
38
44
|
readIo?: (pid: number) => ProcIoBytes | null;
|
|
45
|
+
/** `VmSwap` for one pid, MB. Null = gone or unreadable. */
|
|
46
|
+
readSwap?: (pid: number) => number | null;
|
|
47
|
+
/** `/proc/<pid>/cgroup` for one pid, used only to name its systemd unit. */
|
|
48
|
+
readCgroup?: (pid: number) => string | null;
|
|
49
|
+
/** Whose `systemctl --user` the Services page is reading. Defaults to this
|
|
50
|
+
* process's own uid. */
|
|
51
|
+
selfUid?: number;
|
|
39
52
|
/** Omitted or null → no per-process VRAM query at all. The production wiring
|
|
40
53
|
* in `system-metrics-platform.ts` injects it; defaulting to a real collector
|
|
41
54
|
* here would make an innocent unit test spawn `nvidia-smi`. */
|
|
42
55
|
gpuMemory?: ProcessGpuMemoryCollector | null;
|
|
56
|
+
/** Shared with the whole-GPU collector: it memoises one `/proc` walk per tick,
|
|
57
|
+
* so whichever of the two asks first pays for it and both see the same numbers. */
|
|
58
|
+
drm?: DrmGpuCollector | null;
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
export function createLinuxProcessCollector(
|
|
@@ -47,7 +63,17 @@ export function createLinuxProcessCollector(
|
|
|
47
63
|
opts: LinuxProcessCollectorOptions = {},
|
|
48
64
|
): ProcessCollector {
|
|
49
65
|
const readIo = opts.readIo ?? readProcIoBytes;
|
|
66
|
+
const readSwap = opts.readSwap ?? readProcSwapMB;
|
|
67
|
+
// The Apps page reads the same file under a DIFFERENT rule (it wants the
|
|
68
|
+
// outermost app unit, this wants the innermost unit of any kind), so the two
|
|
69
|
+
// cannot share one read without one of them getting the wrong answer. 1.3 ms
|
|
70
|
+
// for 561 pids, measured on this host.
|
|
71
|
+
const readCgroup = opts.readCgroup ?? ((pid: number) => realLinuxFs.read(`/proc/${pid}/cgroup`));
|
|
72
|
+
// Which user manager is "ours" — a unit of another uid is not on the Services
|
|
73
|
+
// page at all, so its processes must not land on our same-named row.
|
|
74
|
+
const selfUid = opts.selfUid ?? process.getuid?.() ?? 0;
|
|
50
75
|
const gpuMemory = opts.gpuMemory ?? null;
|
|
76
|
+
const drm = opts.drm ?? null;
|
|
51
77
|
const observeColumns = createStickyColumns();
|
|
52
78
|
|
|
53
79
|
return {
|
|
@@ -58,28 +84,54 @@ export function createLinuxProcessCollector(
|
|
|
58
84
|
return { rows: [], columns: observeColumns({}), warnings: ["Process list unavailable: /proc is not readable"] };
|
|
59
85
|
}
|
|
60
86
|
const gpuMemByPid = (await gpuMemory?.collect()) ?? null;
|
|
87
|
+
const drmByPid = drm?.usage().perProcess ?? null;
|
|
61
88
|
|
|
62
89
|
let anyIo = false;
|
|
90
|
+
let anySwap = false;
|
|
63
91
|
const rows = procEntriesToRows(table).map((r) => {
|
|
64
92
|
// EACCES for another user's process is expected, not an error: that row
|
|
65
93
|
// simply has no disk figures.
|
|
66
94
|
const io = readIo(r.pid);
|
|
67
95
|
if (io) anyIo = true;
|
|
96
|
+
// Both sources list only the processes actually using the GPU, so a
|
|
97
|
+
// missing pid means "measured, holding none" — not "unknown".
|
|
98
|
+
const drmRow = drmByPid?.get(r.pid);
|
|
99
|
+
const nvidiaMem = gpuMemByPid ? gpuMemByPid.get(r.pid) ?? 0 : undefined;
|
|
100
|
+
const drmMem = drmByPid ? (drmRow ? drmRow.vramMB + drmRow.sharedMB : 0) : undefined;
|
|
101
|
+
// A process that exited between the table read and now has neither; both
|
|
102
|
+
// stay absent for that row rather than being reported as zero.
|
|
103
|
+
const swapMB = readSwap(r.pid);
|
|
104
|
+
if (swapMB !== null) anySwap = true;
|
|
105
|
+
const unitKey = serviceKeyFromCgroup(readCgroup(r.pid) ?? "", selfUid);
|
|
68
106
|
return {
|
|
69
107
|
...r,
|
|
108
|
+
...(swapMB === null ? {} : { swapMB }),
|
|
109
|
+
...(unitKey === null ? {} : { unitKey }),
|
|
70
110
|
diskReadBytes: io?.readBytes,
|
|
71
111
|
diskWriteBytes: io?.writeBytes,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
gpuMemMB: gpuMemByPid ? gpuMemByPid.get(r.pid) ?? 0 : undefined,
|
|
112
|
+
...(drmByPid ? { gpuPct: drmRow?.gpuPct ?? 0 } : {}),
|
|
113
|
+
gpuMemMB: sumDefined(nvidiaMem, drmMem),
|
|
75
114
|
};
|
|
76
115
|
});
|
|
77
116
|
|
|
78
117
|
return {
|
|
79
118
|
rows,
|
|
80
|
-
columns: observeColumns({
|
|
119
|
+
columns: observeColumns({
|
|
120
|
+
disk: anyIo,
|
|
121
|
+
gpu: gpuMemByPid !== null || drmByPid !== null,
|
|
122
|
+
swap: anySwap,
|
|
123
|
+
}),
|
|
81
124
|
warnings: [],
|
|
82
125
|
};
|
|
83
126
|
},
|
|
84
127
|
};
|
|
85
128
|
}
|
|
129
|
+
|
|
130
|
+
/** Undefined only when NEITHER source measured anything — a host with one of the
|
|
131
|
+
* two must not report "unknown" for a figure it really does have. */
|
|
132
|
+
function sumDefined(a: number | undefined, b: number | undefined): number | undefined {
|
|
133
|
+
if (a === undefined && b === undefined) return undefined;
|
|
134
|
+
return round1((a ?? 0) + (b ?? 0));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const round1 = (n: number) => Math.round(n * 10) / 10;
|
|
@@ -13,6 +13,12 @@ export interface RawProcessRow {
|
|
|
13
13
|
/** Cumulative CPU time (user + kernel), milliseconds. */
|
|
14
14
|
cpuMs: number;
|
|
15
15
|
ramMB: number;
|
|
16
|
+
/** Swapped-out anonymous memory, MB. `undefined` = this OS has no per-process
|
|
17
|
+
* source; 0 is a real reading (a kernel thread has no address space). */
|
|
18
|
+
swapMB?: number;
|
|
19
|
+
/** `"<scope>:<unit>"` — which Services row owns this pid. Absent off systemd,
|
|
20
|
+
* for a pid in no unit, and for another user's units. */
|
|
21
|
+
unitKey?: string;
|
|
16
22
|
/** Epoch ms UTC; 0 when unknown. */
|
|
17
23
|
startedAt: number;
|
|
18
24
|
/** CUMULATIVE per-process byte counters. The rows builder turns them into
|
|
@@ -49,7 +55,7 @@ export interface ProcessCollector {
|
|
|
49
55
|
|
|
50
56
|
/** Frozen: it is shared by every light-tier snapshot, so an accidental mutation
|
|
51
57
|
* would rewrite history for all of them. */
|
|
52
|
-
export const NO_PROCESS_COLUMNS: ProcessColumnAvailability = Object.freeze({ disk: false, gpu: false, net: false });
|
|
58
|
+
export const NO_PROCESS_COLUMNS: ProcessColumnAvailability = Object.freeze({ disk: false, gpu: false, net: false, swap: false });
|
|
53
59
|
|
|
54
60
|
/**
|
|
55
61
|
* Sticky column availability. A capability is advertised as soon as it has
|
|
@@ -64,6 +70,7 @@ export function createStickyColumns(): (seen: Partial<ProcessColumnAvailability>
|
|
|
64
70
|
state.disk ||= seen.disk === true;
|
|
65
71
|
state.gpu ||= seen.gpu === true;
|
|
66
72
|
state.net ||= seen.net === true;
|
|
73
|
+
state.swap ||= seen.swap === true;
|
|
67
74
|
return { ...state };
|
|
68
75
|
};
|
|
69
76
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The same Details dialog as `process-details-linux.ts`, for macOS.
|
|
3
|
+
*
|
|
4
|
+
* It exists because the dialog was *lying* there: the service answered `null`
|
|
5
|
+
* for every non-Linux platform and the route turned that into a 404 reading
|
|
6
|
+
* "PID <n> is no longer running" — a sentence that is correct on Linux, where a
|
|
7
|
+
* vanished `/proc/<pid>` really does mean the process exited, and simply untrue
|
|
8
|
+
* anywhere else. A process sitting in the table at 4.2% CPU was reported dead.
|
|
9
|
+
*
|
|
10
|
+
* macOS has no `/proc`, but `ps` answers ten of the eleven fields, and two of
|
|
11
|
+
* them more directly than Linux does: `lstart` is a real timestamp rather than
|
|
12
|
+
* jiffies to be added to `btime`, and `user` is the login name already, so no
|
|
13
|
+
* `/etc/passwd` lookup is needed and it works across users. Only `cgroup` has no
|
|
14
|
+
* equivalent — macOS has no such concept — and it stays null, which the page
|
|
15
|
+
* draws as an em dash: the honest answer rather than an invented one.
|
|
16
|
+
*
|
|
17
|
+
* Measured on an M1 Max: `ps` for one pid 29.7 ms, `ps -M` 32.5 ms, `lsof` 37.5
|
|
18
|
+
* ms. The last three run concurrently, so a dialog costs about two spawns' wall
|
|
19
|
+
* time. That is affordable only because this is on-demand — it must never be
|
|
20
|
+
* moved onto the tick, which is the same rule the Linux module states.
|
|
21
|
+
*/
|
|
22
|
+
import type { ProcessDetails } from "../../types/system-metrics.ts";
|
|
23
|
+
import { redactSecrets } from "../redact-secrets.ts";
|
|
24
|
+
import { defaultRunner, type Runner } from "../host-info/spawn-runner.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* BSD's state letters, which are NOT Linux's. The one that catches people is
|
|
28
|
+
* **U**: uninterruptible wait, spelled `D` on Linux. `I` is also real here —
|
|
29
|
+
* macOS marks a process idle once it has slept more than 20 seconds — and `Z`
|
|
30
|
+
* is far from rare: this host had 126 zombies against 645 sleepers.
|
|
31
|
+
*/
|
|
32
|
+
export const DARWIN_PROCESS_STATES: Record<string, string> = {
|
|
33
|
+
R: "Running", S: "Sleeping", I: "Idle", T: "Stopped",
|
|
34
|
+
U: "Uninterruptible sleep", Z: "Zombie",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface PsFields {
|
|
38
|
+
ppid: number;
|
|
39
|
+
state: string | null;
|
|
40
|
+
nice: number | null;
|
|
41
|
+
/** Epoch ms, or 0 when `lstart` did not parse — the UI draws 0 as an em dash. */
|
|
42
|
+
startedAt: number;
|
|
43
|
+
user: string | null;
|
|
44
|
+
exe: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One line of `ps -o ppid=,stat=,nice=,lstart=,user=,comm=`, e.g.
|
|
49
|
+
* ` 1 S 0 Tue Sep 8 10:05:03 2026 thawng /System/…/Finder`
|
|
50
|
+
*
|
|
51
|
+
* Split by position, not by a delimiter, because `ps` on macOS offers none —
|
|
52
|
+
* and two fields contain spaces. `lstart` is **always five tokens** (verified
|
|
53
|
+
* across all 740 processes on a live host: a single-digit day pads with two
|
|
54
|
+
* spaces, which collapses on a `\s+` split rather than dropping a token), so
|
|
55
|
+
* every field up to `user` is at a fixed index and `comm` is whatever is left.
|
|
56
|
+
* It has to be last for that reason: a path like
|
|
57
|
+
* `/Applications/Google Chrome.app/…/Google Chrome Helper (Renderer)` holds six
|
|
58
|
+
* spaces, so anything after it could not be found again.
|
|
59
|
+
*/
|
|
60
|
+
export function parsePsLine(text: string | null | undefined): PsFields | null {
|
|
61
|
+
const line = (text ?? "").trim();
|
|
62
|
+
if (!line) return null;
|
|
63
|
+
const t = line.split(/\s+/);
|
|
64
|
+
// ppid, stat, nice, five for lstart, user — comm may legitimately be empty.
|
|
65
|
+
if (t.length < 9) return null;
|
|
66
|
+
|
|
67
|
+
const ppid = Number(t[0]);
|
|
68
|
+
if (!Number.isInteger(ppid) || ppid < 0) return null;
|
|
69
|
+
|
|
70
|
+
const nice = Number(t[2]);
|
|
71
|
+
const started = Date.parse(t.slice(3, 8).join(" "));
|
|
72
|
+
const exe = t.slice(9).join(" ");
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
ppid,
|
|
76
|
+
state: DARWIN_PROCESS_STATES[t[1]![0] ?? ""] ?? null,
|
|
77
|
+
nice: Number.isFinite(nice) ? nice : null,
|
|
78
|
+
startedAt: Number.isFinite(started) ? started : 0,
|
|
79
|
+
user: t[8] || null,
|
|
80
|
+
exe: exe || null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Thread count from a bare `ps -M -p <pid>` dump: one header line, then one line
|
|
86
|
+
* per thread.
|
|
87
|
+
*
|
|
88
|
+
* `-M` may not be combined with `-o` — asking for both prints the chosen columns
|
|
89
|
+
* AND leftovers of the thread layout on the same line, which parses as nonsense
|
|
90
|
+
* rather than failing, so this is deliberately a second call against the default
|
|
91
|
+
* format rather than a column added to the first.
|
|
92
|
+
*/
|
|
93
|
+
export function parseThreadCount(text: string | null | undefined): number | null {
|
|
94
|
+
const lines = (text ?? "").split("\n").filter((l) => l.trim().length > 0);
|
|
95
|
+
return lines.length > 1 ? lines.length - 1 : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* `lsof -a -p <pid> -d cwd -Fn` in its machine-readable form: one field per
|
|
100
|
+
* line, prefixed by its type, `n` being the name.
|
|
101
|
+
*
|
|
102
|
+
* Another user's working directory comes back as exit 0 with **no output at
|
|
103
|
+
* all** rather than an error, which is the normal case on any machine with
|
|
104
|
+
* daemons on it — the same "absent, not failed" reading the Linux module gives
|
|
105
|
+
* an EACCES on `/proc/<pid>/cwd`.
|
|
106
|
+
*/
|
|
107
|
+
export function parseLsofCwd(text: string | null | undefined): string | null {
|
|
108
|
+
for (const line of (text ?? "").split("\n")) {
|
|
109
|
+
if (line.startsWith("n")) return line.slice(1).trim() || null;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** The last path segment, for the `name` field — macOS has no `comm` truncated
|
|
115
|
+
* to 15 characters to take it from, only the executable's full path. */
|
|
116
|
+
function baseName(exe: string | null): string | null {
|
|
117
|
+
if (!exe) return null;
|
|
118
|
+
const cut = exe.lastIndexOf("/");
|
|
119
|
+
return (cut >= 0 ? exe.slice(cut + 1) : exe) || null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Null when the pid is gone, which `ps` reports as a non-zero exit with nothing
|
|
124
|
+
* on stdout — so the route's "no longer running" is finally true when it is said.
|
|
125
|
+
*/
|
|
126
|
+
export async function readProcessDetailsDarwin(
|
|
127
|
+
pid: number,
|
|
128
|
+
run: Runner = defaultRunner,
|
|
129
|
+
): Promise<ProcessDetails | null> {
|
|
130
|
+
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
131
|
+
const arg = String(pid);
|
|
132
|
+
|
|
133
|
+
const ps = await run(["ps", "-o", "ppid=,stat=,nice=,lstart=,user=,comm=", "-p", arg]);
|
|
134
|
+
if (ps.code !== 0) return null;
|
|
135
|
+
const fields = parsePsLine(ps.stdout);
|
|
136
|
+
if (!fields) return null;
|
|
137
|
+
|
|
138
|
+
// Concurrently: three independent spawns, and every one of them is allowed to
|
|
139
|
+
// fail on its own. A process whose command line or thread count cannot be read
|
|
140
|
+
// is still a process whose parent, user and state were just read successfully.
|
|
141
|
+
const [cmd, threads, cwd] = await Promise.all([
|
|
142
|
+
run(["ps", "-o", "command=", "-p", arg]),
|
|
143
|
+
run(["ps", "-M", "-p", arg]),
|
|
144
|
+
run(["lsof", "-a", "-p", arg, "-d", "cwd", "-Fn"]),
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
const command = cmd.code === 0 ? cmd.stdout.trim() : "";
|
|
148
|
+
return {
|
|
149
|
+
pid,
|
|
150
|
+
ppid: fields.ppid,
|
|
151
|
+
name: baseName(fields.exe) ?? arg,
|
|
152
|
+
startedAt: fields.startedAt,
|
|
153
|
+
// Redacted but NOT truncated, exactly as on Linux: showing the whole line is
|
|
154
|
+
// what this dialog is for.
|
|
155
|
+
command: command ? redactSecrets(command) : null,
|
|
156
|
+
exe: fields.exe,
|
|
157
|
+
cwd: cwd.code === 0 ? parseLsofCwd(cwd.stdout) : null,
|
|
158
|
+
user: fields.user,
|
|
159
|
+
state: fields.state,
|
|
160
|
+
threads: threads.code === 0 ? parseThreadCount(threads.stdout) : null,
|
|
161
|
+
nice: fields.nice,
|
|
162
|
+
// macOS has no cgroups. Null is the honest answer and renders as an em dash;
|
|
163
|
+
// inventing a value here would be the same mistake this file exists to undo.
|
|
164
|
+
cgroup: null,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-demand facts for the process Details dialog — Mission Center's process
|
|
3
|
+
* properties. Fetched when the dialog opens, never per tick: it is a handful of
|
|
4
|
+
* reads per process and nobody needs `cwd` at 0.5 Hz.
|
|
5
|
+
*
|
|
6
|
+
* Every field is null when this pid's permissions do not expose it. Another
|
|
7
|
+
* user's `exe` and `cwd` are EACCES for an unprivileged PPM, which is the normal
|
|
8
|
+
* case on a shared machine and not an error.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProcessDetails } from "../../types/system-metrics.ts";
|
|
11
|
+
import { redactSecrets } from "../redact-secrets.ts";
|
|
12
|
+
import { readAttr, realLinuxFs, type LinuxFs } from "./linux-fs.ts";
|
|
13
|
+
|
|
14
|
+
const PROC = "/proc";
|
|
15
|
+
/** Same assumption `proc-table-linux.ts` documents: 100 on every mainstream Linux. */
|
|
16
|
+
const CLOCK_TICKS_PER_SEC = 100;
|
|
17
|
+
|
|
18
|
+
/** The kernel's one-letter state, spelled out the way `ps` and Mission Center do. */
|
|
19
|
+
export const PROCESS_STATES: Record<string, string> = {
|
|
20
|
+
R: "Running", S: "Sleeping", D: "Uninterruptible sleep", Z: "Zombie",
|
|
21
|
+
T: "Stopped", t: "Tracing stop", X: "Dead", I: "Idle", W: "Paging", K: "Wakekill",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface StatFields {
|
|
25
|
+
state: string;
|
|
26
|
+
ppid: number;
|
|
27
|
+
nice: number;
|
|
28
|
+
threads: number;
|
|
29
|
+
/** Ticks since boot. */
|
|
30
|
+
startTicks: number;
|
|
31
|
+
comm: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `/proc/<pid>/stat`, anchored on the LAST `)`.
|
|
36
|
+
*
|
|
37
|
+
* The second field is the executable name in parentheses and it is not escaped,
|
|
38
|
+
* so a process called `foo) 0 (bar` shifts every field after it for any parser
|
|
39
|
+
* that splits on whitespace. Anchoring on the last `)` is the only correct
|
|
40
|
+
* reading, and it is what `readProcTable` does too.
|
|
41
|
+
*/
|
|
42
|
+
export function parseStatFields(text: string): StatFields | null {
|
|
43
|
+
const close = text.lastIndexOf(")");
|
|
44
|
+
const open = text.indexOf("(");
|
|
45
|
+
if (close < 0 || open < 0 || close < open) return null;
|
|
46
|
+
const rest = text.slice(close + 1).trim().split(/\s+/);
|
|
47
|
+
// `rest[0]` is field 3 (state), so field N is at rest[N - 3].
|
|
48
|
+
const at = (field: number) => Number(rest[field - 3]);
|
|
49
|
+
const ppid = at(4);
|
|
50
|
+
if (!Number.isFinite(ppid)) return null;
|
|
51
|
+
return {
|
|
52
|
+
comm: text.slice(open + 1, close),
|
|
53
|
+
state: rest[0] ?? "",
|
|
54
|
+
ppid,
|
|
55
|
+
nice: at(19),
|
|
56
|
+
threads: at(20),
|
|
57
|
+
startTicks: at(22),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** `/proc/stat`'s `btime` line: boot time as epoch SECONDS. */
|
|
62
|
+
export function parseBootTimeSec(procStat: string | null): number | null {
|
|
63
|
+
const m = /^btime\s+(\d+)/m.exec(procStat ?? "");
|
|
64
|
+
const n = m ? Number(m[1]) : NaN;
|
|
65
|
+
return Number.isFinite(n) ? n : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The real uid from `/proc/<pid>/status`'s `Uid: real effective saved fs`. */
|
|
69
|
+
export function parseUid(status: string | null): number | null {
|
|
70
|
+
const m = /^Uid:\s+(\d+)/m.exec(status ?? "");
|
|
71
|
+
const n = m ? Number(m[1]) : NaN;
|
|
72
|
+
return Number.isFinite(n) ? n : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** uid → login name from `/etc/passwd`, which is world-readable by design. */
|
|
76
|
+
export function lookupUser(uid: number, passwd: string | null): string | null {
|
|
77
|
+
for (const line of (passwd ?? "").split("\n")) {
|
|
78
|
+
const f = line.split(":");
|
|
79
|
+
if (f.length > 2 && Number(f[2]) === uid) return f[0] ?? null;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** cgroup v2 writes one `0::<path>` line; v1 writes several, and the systemd
|
|
85
|
+
* hierarchy is the one that names the unit. */
|
|
86
|
+
export function parseCgroup(text: string | null): string | null {
|
|
87
|
+
const lines = (text ?? "").split("\n").filter(Boolean);
|
|
88
|
+
const v2 = lines.find((l) => l.startsWith("0::"));
|
|
89
|
+
if (v2) return v2.slice(3) || null;
|
|
90
|
+
const systemd = lines.find((l) => l.includes(":name=systemd:"));
|
|
91
|
+
const chosen = systemd ?? lines[0];
|
|
92
|
+
if (!chosen) return null;
|
|
93
|
+
const idx = chosen.indexOf(":", chosen.indexOf(":") + 1);
|
|
94
|
+
return idx >= 0 ? chosen.slice(idx + 1) || null : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface ProcessDetailsOptions {
|
|
98
|
+
fs?: LinuxFs;
|
|
99
|
+
/** Injected so the lookup is testable and read once per request, not per line. */
|
|
100
|
+
passwd?: () => string | null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Null when the pid is gone (or /proc is unreadable) — the dialog then says so. */
|
|
104
|
+
export function readProcessDetails(pid: number, opts: ProcessDetailsOptions = {}): ProcessDetails | null {
|
|
105
|
+
const fs = opts.fs ?? realLinuxFs;
|
|
106
|
+
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
107
|
+
const stat = fs.read(`${PROC}/${pid}/stat`);
|
|
108
|
+
const fields = stat ? parseStatFields(stat) : null;
|
|
109
|
+
if (!fields) return null;
|
|
110
|
+
|
|
111
|
+
const status = fs.read(`${PROC}/${pid}/status`);
|
|
112
|
+
const uid = parseUid(status);
|
|
113
|
+
const bootSec = parseBootTimeSec(fs.read(`${PROC}/stat`));
|
|
114
|
+
const rawCmdline = fs.read(`${PROC}/${pid}/cmdline`) ?? "";
|
|
115
|
+
// NUL-separated argv. A kernel thread has an empty cmdline, which is not a
|
|
116
|
+
// command line of "" — it has none at all.
|
|
117
|
+
const argv = rawCmdline.split("\0").filter(Boolean).join(" ");
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
pid,
|
|
121
|
+
ppid: fields.ppid,
|
|
122
|
+
name: nameOf(status) ?? fields.comm,
|
|
123
|
+
startedAt: bootSec !== null && Number.isFinite(fields.startTicks)
|
|
124
|
+
? Math.round((bootSec + fields.startTicks / CLOCK_TICKS_PER_SEC) * 1000)
|
|
125
|
+
: 0,
|
|
126
|
+
// Redacted but NOT truncated: the dialog exists to show the whole line, and
|
|
127
|
+
// the 160-char cut on a table row is a bandwidth decision, not a privacy one.
|
|
128
|
+
command: argv ? redactSecrets(argv) : null,
|
|
129
|
+
exe: fs.readlink(`${PROC}/${pid}/exe`),
|
|
130
|
+
cwd: fs.readlink(`${PROC}/${pid}/cwd`),
|
|
131
|
+
user: uid === null ? null : lookupUser(uid, (opts.passwd ?? (() => fs.read("/etc/passwd")))()),
|
|
132
|
+
state: describeState(fields.state),
|
|
133
|
+
threads: Number.isFinite(fields.threads) ? fields.threads : null,
|
|
134
|
+
nice: Number.isFinite(fields.nice) ? fields.nice : null,
|
|
135
|
+
cgroup: parseCgroup(fs.read(`${PROC}/${pid}/cgroup`)),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** `/proc/<pid>/status`'s `Name:` is the same truncated comm, but without the
|
|
140
|
+
* parenthesis quoting — preferred when present because it needs no unwrapping. */
|
|
141
|
+
function nameOf(status: string | null): string | null {
|
|
142
|
+
const m = /^Name:\s*(.+)$/m.exec(status ?? "");
|
|
143
|
+
return m?.[1]?.trim() || null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function describeState(code: string): string | null {
|
|
147
|
+
if (!code) return null;
|
|
148
|
+
const word = PROCESS_STATES[code];
|
|
149
|
+
return word ? `${code} (${word})` : code;
|
|
150
|
+
}
|
|
@@ -16,7 +16,7 @@ const POSIX_BOUNDARY_NAMES: ReadonlySet<string> = new Set(["launchd", "systemd",
|
|
|
16
16
|
const round1 = (n: number): number => Math.round(n * 10) / 10;
|
|
17
17
|
|
|
18
18
|
/** The optional per-process columns, which roll up by summation. */
|
|
19
|
-
type OptionalMetric = "diskReadBps" | "diskWriteBps" | "gpuPct" | "gpuMemMB" | "netInBps" | "netOutBps";
|
|
19
|
+
type OptionalMetric = "swapMB" | "diskReadBps" | "diskWriteBps" | "gpuPct" | "gpuMemMB" | "netInBps" | "netOutBps";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Sum over the members that HAVE a value; `undefined` when none does, so a
|
|
@@ -129,6 +129,7 @@ export function groupProcesses(
|
|
|
129
129
|
count: g.procs.length,
|
|
130
130
|
ppm: g.procs.some((p) => p.ppm),
|
|
131
131
|
pids: g.procs.map((p) => p.pid),
|
|
132
|
+
swapMB: sumOptional(g.procs, "swapMB"),
|
|
132
133
|
diskReadBps: sumOptional(g.procs, "diskReadBps"),
|
|
133
134
|
diskWriteBps: sumOptional(g.procs, "diskWriteBps"),
|
|
134
135
|
gpuPct: sumOptional(g.procs, "gpuPct"),
|
|
@@ -93,6 +93,8 @@ export function buildProcessRows(input: BuildRowsInput): BuiltRows {
|
|
|
93
93
|
command: sanitizeCommand(r.command, r.name),
|
|
94
94
|
cpu: percentByKey.get(key) ?? 0,
|
|
95
95
|
ramMB: Math.round(r.ramMB * 10) / 10,
|
|
96
|
+
...(r.swapMB === undefined ? {} : { swapMB: Math.round(r.swapMB * 10) / 10 }),
|
|
97
|
+
...(r.unitKey === undefined ? {} : { unitKey: r.unitKey }),
|
|
96
98
|
startedAt: r.startedAt,
|
|
97
99
|
ppm: ppmPids.has(r.pid),
|
|
98
100
|
protected: !checkKillAllowed({ pid: r.pid, name: r.name }, false, guardCtx).allowed,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process swap — Mission Center's Swap column, on the process table and on
|
|
3
|
+
* every Services row.
|
|
4
|
+
*
|
|
5
|
+
* `VmSwap` in `/proc/<pid>/status` is the only cheap source: `smaps_rollup` also
|
|
6
|
+
* carries it but walks every mapping, and on a 560-process desktop that is the
|
|
7
|
+
* difference between a few milliseconds and a tick that misses its deadline.
|
|
8
|
+
* Measured here: the whole table costs 3.2 ms, beside the 1.7 ms `/proc/<pid>/io`
|
|
9
|
+
* already pays every tick.
|
|
10
|
+
*
|
|
11
|
+
* A kernel thread has no address space and so no `VmSwap` line at all. That is
|
|
12
|
+
* 0 swapped bytes, not an unmeasurable one — the distinction matters because the
|
|
13
|
+
* table renders `undefined` as an em dash, and 335 of this host's 561 rows would
|
|
14
|
+
* otherwise be dashes on a column that really does read zero for them.
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
const KB_PER_MB = 1024;
|
|
19
|
+
|
|
20
|
+
/** `VmSwap` in kB from a `/proc/<pid>/status` dump. A process with an address
|
|
21
|
+
* space always has the line; one without is a kernel thread, i.e. 0. */
|
|
22
|
+
export function parseVmSwapKB(status: string): number {
|
|
23
|
+
const m = /^VmSwap:\s+(\d+)\s*kB/m.exec(status);
|
|
24
|
+
if (!m) return 0;
|
|
25
|
+
const kb = Number(m[1]);
|
|
26
|
+
return Number.isFinite(kb) ? kb : 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Swapped MB for one pid, or null when the process is gone or unreadable —
|
|
30
|
+
* another user's process is expected to refuse, and that row simply has none. */
|
|
31
|
+
export function readProcSwapMB(pid: number): number | null {
|
|
32
|
+
try {
|
|
33
|
+
return parseVmSwapKB(readFileSync(`/proc/${pid}/status`, "utf-8")) / KB_PER_MB;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivers ONE signal to a process, or to its whole collected tree — Mission
|
|
3
|
+
* Center's "Send Signal" submenu. Separate from `executeKill` on purpose: a kill
|
|
4
|
+
* escalates SIGTERM to SIGKILL after a grace period, which is right for "end this
|
|
5
|
+
* process" and wrong for every other signal. Asking for SIGSTOP must send SIGSTOP
|
|
6
|
+
* and nothing else.
|
|
7
|
+
*
|
|
8
|
+
* Pids are validated integers passed to `process.kill` or as argv elements —
|
|
9
|
+
* never interpolated into a shell string.
|
|
10
|
+
*/
|
|
11
|
+
import type { ProcessSignal, SignalProcessResult } from "../../types/system-metrics.ts";
|
|
12
|
+
import type { Runner } from "../host-info/spawn-runner.ts";
|
|
13
|
+
import { defaultRunner } from "../host-info/spawn-runner.ts";
|
|
14
|
+
import { collectProcessTree } from "../windows-process-tree.ts";
|
|
15
|
+
|
|
16
|
+
/** Windows has no signals. `taskkill` is the only delivery there and it can only
|
|
17
|
+
* mean "end it", so those two map and the rest are refused with a reason. */
|
|
18
|
+
export const WINDOWS_SUPPORTED_SIGNALS: ReadonlySet<ProcessSignal> = new Set<ProcessSignal>(["TERM", "KILL"]);
|
|
19
|
+
|
|
20
|
+
export interface SignalExecutorDeps {
|
|
21
|
+
platform: NodeJS.Platform;
|
|
22
|
+
run: Runner;
|
|
23
|
+
signal: (pid: number, sig: NodeJS.Signals) => void;
|
|
24
|
+
collectTree: (pid: number) => number[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const defaultSignalExecutorDeps: SignalExecutorDeps = {
|
|
28
|
+
platform: process.platform,
|
|
29
|
+
run: defaultRunner,
|
|
30
|
+
signal: (pid, sig) => { process.kill(pid, sig); },
|
|
31
|
+
collectTree: collectProcessTree,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Which signals this host can actually deliver, for the client to build its menu. */
|
|
35
|
+
export function supportedSignals(platform: NodeJS.Platform = process.platform): ProcessSignal[] {
|
|
36
|
+
return platform === "win32"
|
|
37
|
+
? [...WINDOWS_SUPPORTED_SIGNALS]
|
|
38
|
+
: ["TERM", "KILL", "STOP", "CONT", "HUP", "INT", "USR1", "USR2"];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Throws on executor failure; the handler maps that to 500. */
|
|
42
|
+
export async function executeSignal(
|
|
43
|
+
pid: number,
|
|
44
|
+
signal: ProcessSignal,
|
|
45
|
+
tree: boolean,
|
|
46
|
+
deps: SignalExecutorDeps = defaultSignalExecutorDeps,
|
|
47
|
+
): Promise<SignalProcessResult> {
|
|
48
|
+
if (!Number.isInteger(pid) || pid <= 0) throw new Error(`Invalid PID ${pid}`);
|
|
49
|
+
|
|
50
|
+
if (deps.platform === "win32") {
|
|
51
|
+
if (!WINDOWS_SUPPORTED_SIGNALS.has(signal)) {
|
|
52
|
+
throw new Error(`Windows cannot deliver SIG${signal}`);
|
|
53
|
+
}
|
|
54
|
+
const argv = tree
|
|
55
|
+
? ["taskkill", "/PID", String(pid), "/T", "/F"]
|
|
56
|
+
: ["taskkill", "/PID", String(pid), "/F"];
|
|
57
|
+
const r = await deps.run(argv, 5000);
|
|
58
|
+
if (r.code !== 0 || r.timedOut) {
|
|
59
|
+
throw new Error((r.stderr || r.stdout || `taskkill exited ${r.timedOut ? "on timeout" : r.code}`).trim());
|
|
60
|
+
}
|
|
61
|
+
return { pid, signal, tree, method: "taskkill", signalled: [pid] };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const posix = `SIG${signal}` as NodeJS.Signals;
|
|
65
|
+
if (!tree) {
|
|
66
|
+
deps.signal(pid, posix);
|
|
67
|
+
return { pid, signal, tree: false, method: "signal", signalled: [pid] };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Enumerate BEFORE signalling: SIGKILL on the root reparents its children to
|
|
71
|
+
// init, and the tree can no longer be recovered afterwards. Children are
|
|
72
|
+
// signalled first so a parent cannot fork a replacement mid-sweep.
|
|
73
|
+
const pids = deps.collectTree(pid);
|
|
74
|
+
const signalled: number[] = [];
|
|
75
|
+
for (const target of pids) {
|
|
76
|
+
// A process exiting between the walk and the signal is the normal case here,
|
|
77
|
+
// not a failure of the request — it is simply not in the result.
|
|
78
|
+
try {
|
|
79
|
+
deps.signal(target, posix);
|
|
80
|
+
signalled.push(target);
|
|
81
|
+
} catch { /* already gone */ }
|
|
82
|
+
}
|
|
83
|
+
return { pid, signal, tree: true, method: "signal", signalled };
|
|
84
|
+
}
|