@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
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-disk figures for the Performance page's drive entries, from
|
|
3
|
+
* `/sys/block/<dev>/stat`. The arithmetic is Mission Center's
|
|
4
|
+
* (`platform-linux/src/disks/stats.rs`): busy% is iostat's %util, response time
|
|
5
|
+
* is weighted ticks over completed requests, and throughput is sectors x 512.
|
|
6
|
+
*
|
|
7
|
+
* Every device is sampled against its OWN previous sample, so a drive that
|
|
8
|
+
* appears mid-session reports `available:false` for one tick instead of a spike
|
|
9
|
+
* measured from zero.
|
|
10
|
+
*/
|
|
11
|
+
import type { DiskMetrics } from "../../types/system-metrics.ts";
|
|
12
|
+
import { readNumber, realLinuxFs, type LinuxFs } from "./linux-fs.ts";
|
|
13
|
+
|
|
14
|
+
export const SYS_BLOCK = "/sys/block";
|
|
15
|
+
const SECTOR_BYTES = 512;
|
|
16
|
+
|
|
17
|
+
/** Mission Center's list, matched on a 2-4 character prefix: virtual devices
|
|
18
|
+
* whose counters double-count the real drive underneath, or are not a drive. */
|
|
19
|
+
const IGNORED_PREFIXES = ["loop", "ram", "zram", "fd", "md", "dm", "zd"];
|
|
20
|
+
|
|
21
|
+
/** `/sys/block/<dev>/stat` field indices. The line has 17 fields since 5.5; the
|
|
22
|
+
* discard group (12-16) and flush group (17-18) are absent on older kernels,
|
|
23
|
+
* which reads as 0 here rather than as an error. */
|
|
24
|
+
const IDX = {
|
|
25
|
+
readIos: 0, readSectors: 2, readTicks: 3,
|
|
26
|
+
writeIos: 4, writeSectors: 6, writeTicks: 7,
|
|
27
|
+
ioTicks: 9,
|
|
28
|
+
discardIos: 11, discardTicks: 14,
|
|
29
|
+
flushIos: 15, flushTicks: 16,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
export interface DiskSample {
|
|
33
|
+
/** Wall clock in seconds — the sysfs stat line carries no timestamp. */
|
|
34
|
+
atSec: number;
|
|
35
|
+
readIos: number; readSectors: number; readTicks: number;
|
|
36
|
+
writeIos: number; writeSectors: number; writeTicks: number;
|
|
37
|
+
ioTicks: number;
|
|
38
|
+
discardIos: number; discardTicks: number;
|
|
39
|
+
flushIos: number; flushTicks: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Previous sample per device id. */
|
|
43
|
+
export type DiskSampleState = Map<string, DiskSample>;
|
|
44
|
+
|
|
45
|
+
export function isIgnoredDisk(id: string): boolean {
|
|
46
|
+
return IGNORED_PREFIXES.some((p) => id.startsWith(p));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Whole devices udev would call disks: skips the virtual prefixes and anything
|
|
50
|
+
* reporting a zero size (an empty card reader, a detached loop device). */
|
|
51
|
+
export function listDiskDevices(fs: Pick<LinuxFs, "list" | "read"> = realLinuxFs): string[] {
|
|
52
|
+
const names = fs.list(SYS_BLOCK);
|
|
53
|
+
if (!names) return [];
|
|
54
|
+
return names
|
|
55
|
+
.filter((n) => !isIgnoredDisk(n))
|
|
56
|
+
.filter((n) => (readNumber(fs, `${SYS_BLOCK}/${n}/size`) ?? 0) > 0)
|
|
57
|
+
.sort();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function parseDiskStat(text: string, atSec: number): DiskSample | null {
|
|
61
|
+
const f = text.trim().split(/\s+/).map(Number);
|
|
62
|
+
if (f.length < 11 || f.some((v) => !Number.isFinite(v))) return null;
|
|
63
|
+
const at = (i: number) => f[i] ?? 0;
|
|
64
|
+
return {
|
|
65
|
+
atSec,
|
|
66
|
+
readIos: at(IDX.readIos), readSectors: at(IDX.readSectors), readTicks: at(IDX.readTicks),
|
|
67
|
+
writeIos: at(IDX.writeIos), writeSectors: at(IDX.writeSectors), writeTicks: at(IDX.writeTicks),
|
|
68
|
+
ioTicks: at(IDX.ioTicks),
|
|
69
|
+
discardIos: at(IDX.discardIos), discardTicks: at(IDX.discardTicks),
|
|
70
|
+
flushIos: at(IDX.flushIos), flushTicks: at(IDX.flushTicks),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** NVMe exposes a composite sensor at `device/hwmon<N>/temp1_input`; a SATA drive
|
|
75
|
+
* exposes the same path only once `drivetemp` is loaded, which it usually is not
|
|
76
|
+
* — hence undefined rather than 0. Millidegrees, one decimal kept. */
|
|
77
|
+
export function readDiskTempC(id: string, fs: Pick<LinuxFs, "list" | "read"> = realLinuxFs): number | undefined {
|
|
78
|
+
const dir = `${SYS_BLOCK}/${id}/device`;
|
|
79
|
+
const hwmon = fs.list(dir)?.find((n) => n.startsWith("hwmon"));
|
|
80
|
+
if (!hwmon) return undefined;
|
|
81
|
+
const milli = readNumber(fs, `${dir}/${hwmon}/temp1_input`);
|
|
82
|
+
return milli === undefined ? undefined : Math.round(milli / 100) / 10;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface DiskDeviceCollection {
|
|
86
|
+
disks: DiskMetrics[];
|
|
87
|
+
next: DiskSampleState;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One tick's worth of per-device figures. Devices absent from `prev` report
|
|
91
|
+
* `available:false` — totals are still real, they are absolutes. */
|
|
92
|
+
export function collectLinuxDiskDevices(
|
|
93
|
+
prev: DiskSampleState,
|
|
94
|
+
fs: Pick<LinuxFs, "list" | "read"> = realLinuxFs,
|
|
95
|
+
now: () => number = Date.now,
|
|
96
|
+
): DiskDeviceCollection {
|
|
97
|
+
const atSec = now() / 1000;
|
|
98
|
+
const next: DiskSampleState = new Map();
|
|
99
|
+
const disks: DiskMetrics[] = [];
|
|
100
|
+
|
|
101
|
+
for (const id of listDiskDevices(fs)) {
|
|
102
|
+
const text = fs.read(`${SYS_BLOCK}/${id}/stat`);
|
|
103
|
+
const sample = text ? parseDiskStat(text, atSec) : null;
|
|
104
|
+
if (!sample) continue;
|
|
105
|
+
next.set(id, sample);
|
|
106
|
+
disks.push(toDiskMetrics(id, prev.get(id) ?? null, sample, readDiskTempC(id, fs)));
|
|
107
|
+
}
|
|
108
|
+
return { disks, next };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function toDiskMetrics(
|
|
112
|
+
id: string,
|
|
113
|
+
prev: DiskSample | null,
|
|
114
|
+
next: DiskSample,
|
|
115
|
+
tempC: number | undefined,
|
|
116
|
+
): DiskMetrics {
|
|
117
|
+
const base: DiskMetrics = {
|
|
118
|
+
id,
|
|
119
|
+
available: false,
|
|
120
|
+
busyPercent: 0,
|
|
121
|
+
responseMs: 0,
|
|
122
|
+
readBps: 0,
|
|
123
|
+
writeBps: 0,
|
|
124
|
+
readTotal: next.readSectors * SECTOR_BYTES,
|
|
125
|
+
writeTotal: next.writeSectors * SECTOR_BYTES,
|
|
126
|
+
...(tempC === undefined ? {} : { tempC }),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const dt = prev ? next.atSec - prev.atSec : 0;
|
|
130
|
+
// A device reset (hot-replug reusing the name) walks the counters backwards;
|
|
131
|
+
// one unavailable tick is the honest answer, not a negative rate.
|
|
132
|
+
if (!prev || !Number.isFinite(dt) || dt <= 0 || next.ioTicks < prev.ioTicks) return base;
|
|
133
|
+
|
|
134
|
+
const ticks = delta(next.readTicks, prev.readTicks) + delta(next.writeTicks, prev.writeTicks)
|
|
135
|
+
+ delta(next.discardTicks, prev.discardTicks) + delta(next.flushTicks, prev.flushTicks);
|
|
136
|
+
const ios = delta(next.readIos, prev.readIos) + delta(next.writeIos, prev.writeIos)
|
|
137
|
+
+ delta(next.discardIos, prev.discardIos) + delta(next.flushIos, prev.flushIos);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...base,
|
|
141
|
+
available: true,
|
|
142
|
+
busyPercent: round1(Math.min(100, delta(next.ioTicks, prev.ioTicks) / (dt * 10))),
|
|
143
|
+
responseMs: ios > 0 ? round2(ticks / ios) : 0,
|
|
144
|
+
readBps: Math.round(delta(next.readSectors, prev.readSectors) * SECTOR_BYTES / dt),
|
|
145
|
+
writeBps: Math.round(delta(next.writeSectors, prev.writeSectors) * SECTOR_BYTES / dt),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Saturating, as Mission Center's `saturating_sub`: these counters only ever
|
|
150
|
+
* grow, so a negative delta is a reset and contributes nothing. */
|
|
151
|
+
function delta(next: number, prev: number): number {
|
|
152
|
+
return next > prev ? next - prev : 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const round1 = (n: number) => Math.round(n * 10) / 10;
|
|
156
|
+
const round2 = (n: number) => Math.round(n * 100) / 100;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static facts about each whole disk: model, kind, capacity, serial, and which
|
|
3
|
+
* one carries the root filesystem. Read once per inventory request, not per tick.
|
|
4
|
+
*
|
|
5
|
+
* Mission Center gets all of this from udisks2 over D-Bus and SKIPS any drive
|
|
6
|
+
* udisks2 has no object for, which on a headless or container host is every
|
|
7
|
+
* drive. PPM reads sysfs plus udev's own database instead, so the list is the
|
|
8
|
+
* same one the kernel has and needs neither a system bus nor root.
|
|
9
|
+
*/
|
|
10
|
+
import type { DiskInfo, DiskKind } from "../../types/system-hardware.ts";
|
|
11
|
+
import { readAttr, readNumber, realLinuxFs, type LinuxFs } from "./linux-fs.ts";
|
|
12
|
+
import { readUdevProperties, udevValue } from "./udev-db.ts";
|
|
13
|
+
import { listDiskDevices, SYS_BLOCK } from "./disk-devices-linux.ts";
|
|
14
|
+
import { parseMountInfo, readPartitions } from "./partitions-linux.ts";
|
|
15
|
+
|
|
16
|
+
const SECTOR_BYTES = 512;
|
|
17
|
+
/** How far to follow `slaves` links from the root filesystem's device down to a
|
|
18
|
+
* real drive (partition -> LUKS -> LVM -> RAID is already four). */
|
|
19
|
+
const MAX_SLAVE_DEPTH = 8;
|
|
20
|
+
|
|
21
|
+
/** The kernel fills SCSI's vendor field with the transport name for a SATA drive,
|
|
22
|
+
* so "ATA" is never a manufacturer. Mission Center prints it; PPM drops it. */
|
|
23
|
+
const GENERIC_VENDORS = new Set(["ATA", "SATA", "NVME", "USB", "SCSI"]);
|
|
24
|
+
|
|
25
|
+
export function readDiskInventory(fs: LinuxFs = realLinuxFs): DiskInfo[] {
|
|
26
|
+
const systemDisk = findSystemDisk(fs);
|
|
27
|
+
// Parsed once for the whole inventory rather than per disk: mountinfo is one
|
|
28
|
+
// file describing every mount on the host, and re-reading it per drive is the
|
|
29
|
+
// same work times however many drives there are.
|
|
30
|
+
const mounts = parseMountInfo(fs.read("/proc/self/mountinfo") ?? "");
|
|
31
|
+
return listDiskDevices(fs).map((id) => readDiskInfo(id, fs, systemDisk === id, mounts));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function readDiskInfo(
|
|
35
|
+
id: string,
|
|
36
|
+
fs: LinuxFs,
|
|
37
|
+
systemDisk: boolean,
|
|
38
|
+
mounts = parseMountInfo(fs.read("/proc/self/mountinfo") ?? ""),
|
|
39
|
+
): DiskInfo {
|
|
40
|
+
const udev = readUdevProperties(blockDeviceKey(id, fs), fs.read);
|
|
41
|
+
const sectors = readNumber(fs, `${SYS_BLOCK}/${id}/size`) ?? 0;
|
|
42
|
+
const rpm = Number(udevValue(udev, "ID_ATA_ROTATION_RATE_RPM"));
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
id,
|
|
46
|
+
...optional("model", diskModel(id, fs, udev)),
|
|
47
|
+
kind: diskKind(id, fs, udev),
|
|
48
|
+
capacityBytes: sectors * SECTOR_BYTES,
|
|
49
|
+
systemDisk,
|
|
50
|
+
removable: readAttr(fs, `${SYS_BLOCK}/${id}/removable`) === "1",
|
|
51
|
+
...optional("serial", readAttr(fs, `${SYS_BLOCK}/${id}/device/serial`) ?? udevValue(udev, "ID_SERIAL_SHORT")),
|
|
52
|
+
...optional("wwn", diskWwn(id, fs, udev)),
|
|
53
|
+
...(Number.isFinite(rpm) && rpm > 0 ? { rotationRpm: rpm } : {}),
|
|
54
|
+
partitions: readPartitions(id, fs, mounts),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** udev keys a block device by `b<major>:<minor>`, which sysfs reports in `dev`. */
|
|
59
|
+
export function blockDeviceKey(id: string, fs: Pick<LinuxFs, "read">): string {
|
|
60
|
+
return `b${readAttr(fs, `${SYS_BLOCK}/${id}/dev`) ?? ""}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** "vendor model", with the vendor dropped when it is generic or already the
|
|
64
|
+
* start of the model — Mission Center's rule plus the ATA case. */
|
|
65
|
+
export function diskModel(id: string, fs: Pick<LinuxFs, "read">, udev: ReadonlyMap<string, string>): string | undefined {
|
|
66
|
+
const model = readAttr(fs, `${SYS_BLOCK}/${id}/device/model`) ?? udevValue(udev, "ID_MODEL")?.replace(/_/g, " ");
|
|
67
|
+
const vendor = readAttr(fs, `${SYS_BLOCK}/${id}/device/vendor`);
|
|
68
|
+
if (!vendor || GENERIC_VENDORS.has(vendor.toUpperCase())) return model;
|
|
69
|
+
if (!model) return vendor;
|
|
70
|
+
return model.startsWith(vendor) ? model : `${vendor} ${model}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function diskKind(id: string, fs: Pick<LinuxFs, "read" | "list">, udev: ReadonlyMap<string, string>): DiskKind {
|
|
74
|
+
if (id.startsWith("nvme")) return "nvme";
|
|
75
|
+
if (id.startsWith("mmcblk")) return mmcKind(id, fs);
|
|
76
|
+
if (id.startsWith("sr")) return "optical";
|
|
77
|
+
const removable = readAttr(fs, `${SYS_BLOCK}/${id}/removable`) === "1";
|
|
78
|
+
// udisks2 answers MediaCompatibility::Thumb for these; the same drives are the
|
|
79
|
+
// removable ones on the USB bus, which udev already knows.
|
|
80
|
+
if (removable && udevValue(udev, "ID_BUS") === "usb") return "thumb";
|
|
81
|
+
const rotational = readAttr(fs, `${SYS_BLOCK}/${id}/queue/rotational`);
|
|
82
|
+
if (rotational === "0") return "ssd";
|
|
83
|
+
if (rotational === "1") return "hdd";
|
|
84
|
+
return "unknown";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The mmc host's `type` attribute is "SD" or "MMC". */
|
|
88
|
+
function mmcKind(id: string, fs: Pick<LinuxFs, "read" | "list">): DiskKind {
|
|
89
|
+
const index = id.slice("mmcblk".length);
|
|
90
|
+
const host = `/sys/class/mmc_host/mmc${index}`;
|
|
91
|
+
for (const entry of fs.list(host) ?? []) {
|
|
92
|
+
if (!entry.startsWith(`mmc${index}`)) continue;
|
|
93
|
+
const type = readAttr(fs, `${host}/${entry}/type`);
|
|
94
|
+
if (type === "SD") return "sd";
|
|
95
|
+
if (type === "MMC") return "emmc";
|
|
96
|
+
}
|
|
97
|
+
return "unknown";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** NVMe writes `wwid` on the namespace, SATA on the device; udev's `ID_WWN` is
|
|
101
|
+
* cleaner than either and is preferred when present. A T10 identifier is built
|
|
102
|
+
* by concatenating fixed-width ATA fields, so it arrives padded — the runs are
|
|
103
|
+
* collapsed, since the value is shown to a person, not compared byte for byte. */
|
|
104
|
+
function diskWwn(id: string, fs: Pick<LinuxFs, "read">, udev: ReadonlyMap<string, string>): string | undefined {
|
|
105
|
+
const raw = udevValue(udev, "ID_WWN")
|
|
106
|
+
?? readAttr(fs, `${SYS_BLOCK}/${id}/wwid`)
|
|
107
|
+
?? readAttr(fs, `${SYS_BLOCK}/${id}/device/wwid`);
|
|
108
|
+
return raw?.replace(/\s+/g, " ");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The drive behind `/`. mountinfo names the SOURCE path (`/dev/nvme0n1p2`), not
|
|
112
|
+
* a disk, so it is resolved through sysfs: a partition's parent directory is its
|
|
113
|
+
* disk, and a mapper device (LUKS, LVM, RAID) is followed through `slaves`. */
|
|
114
|
+
export function findSystemDisk(fs: LinuxFs): string | undefined {
|
|
115
|
+
const source = rootMountSource(fs.read("/proc/self/mountinfo") ?? "");
|
|
116
|
+
if (!source) return undefined;
|
|
117
|
+
const node = fs.realpath(source) ?? source;
|
|
118
|
+
return wholeDiskOf(basename(node), fs, 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** The `/` line's source device, or undefined when `/` is not a block mount. */
|
|
122
|
+
export function rootMountSource(mountinfo: string): string | undefined {
|
|
123
|
+
for (const line of mountinfo.split("\n")) {
|
|
124
|
+
const fields = line.split(" ");
|
|
125
|
+
if (fields[4] !== "/") continue;
|
|
126
|
+
const sep = fields.indexOf("-", 6);
|
|
127
|
+
const source = sep >= 0 ? fields[sep + 2] : undefined;
|
|
128
|
+
if (source?.startsWith("/dev/")) return source;
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function wholeDiskOf(name: string, fs: LinuxFs, depth: number): string | undefined {
|
|
134
|
+
if (!name || depth > MAX_SLAVE_DEPTH) return undefined;
|
|
135
|
+
const real = fs.realpath(`/sys/class/block/${name}`);
|
|
136
|
+
if (!real) return undefined;
|
|
137
|
+
if (fs.exists(`${real}/partition`)) return wholeDiskOf(basename(dirname(real)), fs, depth + 1);
|
|
138
|
+
for (const slave of fs.list(`${real}/slaves`) ?? []) {
|
|
139
|
+
const disk = wholeDiskOf(slave, fs, depth + 1);
|
|
140
|
+
if (disk) return disk;
|
|
141
|
+
}
|
|
142
|
+
return fs.exists(`${SYS_BLOCK}/${name}`) ? name : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const basename = (p: string) => p.slice(p.lastIndexOf("/") + 1);
|
|
146
|
+
const dirname = (p: string) => p.slice(0, p.lastIndexOf("/"));
|
|
147
|
+
|
|
148
|
+
function optional<K extends string>(key: K, value: string | undefined): Partial<Record<K, string>> {
|
|
149
|
+
return value === undefined ? {} : ({ [key]: value } as Record<K, string>);
|
|
150
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mission Center's Fan page, from `/sys/class/hwmon`. A chip exposes `fanN_input`
|
|
3
|
+
* in RPM, an optional `pwmN` (0-255 duty) and often a `tempN_input` with the same
|
|
4
|
+
* index — Mission Center pairs them by index, which is what the board's own
|
|
5
|
+
* labelling means.
|
|
6
|
+
*
|
|
7
|
+
* Fans reading 0 are kept: a header with nothing plugged into it is information,
|
|
8
|
+
* and hiding it would make the list change length as a fan spins down.
|
|
9
|
+
*/
|
|
10
|
+
import type { FanMetrics } from "../../types/system-metrics.ts";
|
|
11
|
+
import { readAttr, readNumber, realLinuxFs, type LinuxFs } from "./linux-fs.ts";
|
|
12
|
+
|
|
13
|
+
export const HWMON_DIR = "/sys/class/hwmon";
|
|
14
|
+
const PWM_MAX = 255;
|
|
15
|
+
|
|
16
|
+
export function collectLinuxFans(fs: Pick<LinuxFs, "list" | "read"> = realLinuxFs): FanMetrics[] {
|
|
17
|
+
const fans: FanMetrics[] = [];
|
|
18
|
+
for (const chip of (fs.list(HWMON_DIR) ?? []).sort()) {
|
|
19
|
+
const dir = `${HWMON_DIR}/${chip}`;
|
|
20
|
+
const name = readAttr(fs, `${dir}/name`) ?? chip;
|
|
21
|
+
for (const index of fanIndices(fs, dir)) {
|
|
22
|
+
const rpm = readNumber(fs, `${dir}/fan${index}_input`);
|
|
23
|
+
if (rpm === undefined) continue;
|
|
24
|
+
const pwm = readNumber(fs, `${dir}/pwm${index}`);
|
|
25
|
+
const milliC = readNumber(fs, `${dir}/temp${index}_input`);
|
|
26
|
+
fans.push({
|
|
27
|
+
id: `${name}/fan${index}`,
|
|
28
|
+
label: readAttr(fs, `${dir}/fan${index}_label`) ?? `Fan ${index}`,
|
|
29
|
+
rpm: Math.max(0, Math.round(rpm)),
|
|
30
|
+
...(pwm === undefined ? {} : { pwmPercent: Math.round(pwm / PWM_MAX * 1000) / 10 }),
|
|
31
|
+
...(milliC === undefined ? {} : { tempC: Math.round(milliC / 100) / 10 }),
|
|
32
|
+
...defined("tempName", readAttr(fs, `${dir}/temp${index}_label`)),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return fans;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The `N` of every `fanN_input` the chip publishes, ascending. */
|
|
40
|
+
export function fanIndices(fs: Pick<LinuxFs, "list">, dir: string): number[] {
|
|
41
|
+
const indices: number[] = [];
|
|
42
|
+
for (const entry of fs.list(dir) ?? []) {
|
|
43
|
+
const m = /^fan(\d+)_input$/.exec(entry);
|
|
44
|
+
if (m) indices.push(Number(m[1]));
|
|
45
|
+
}
|
|
46
|
+
return indices.sort((a, b) => a - b);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function defined<K extends string, V>(key: K, value: V | undefined): Partial<Record<K, V>> {
|
|
50
|
+
return value === undefined ? {} : ({ [key]: value } as Record<K, V>);
|
|
51
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenGL and Vulkan versions for the GPU page. Both come from a one-shot tool
|
|
3
|
+
* and neither ever changes while the machine is up, so the result is cached for
|
|
4
|
+
* the process lifetime and the tools are never spawned from a tick.
|
|
5
|
+
*
|
|
6
|
+
* `eglinfo` is asked with `EGL_PLATFORM=surfaceless`, which is what lets this
|
|
7
|
+
* answer at all on a headless host or from a systemd unit with no display —
|
|
8
|
+
* `glxinfo` needs an X connection and would simply fail there.
|
|
9
|
+
*/
|
|
10
|
+
import type { Runner } from "../host-info/spawn-runner.ts";
|
|
11
|
+
import { defaultRunner } from "../host-info/spawn-runner.ts";
|
|
12
|
+
|
|
13
|
+
/** `env` carries the platform override because the shared `Runner` takes an argv
|
|
14
|
+
* and no environment — and without it `eglinfo` needs a display, which a PPM
|
|
15
|
+
* started from a systemd unit does not have. */
|
|
16
|
+
export const EGLINFO_ARGV = ["env", "EGL_PLATFORM=surfaceless", "eglinfo"];
|
|
17
|
+
export const VULKANINFO_ARGV = ["vulkaninfo", "--summary"];
|
|
18
|
+
const SPAWN_TIMEOUT_MS = 8000;
|
|
19
|
+
|
|
20
|
+
export interface GpuApiVersions {
|
|
21
|
+
/** Highest context the driver offers: "4.6", or "ES 3.2" when that is all. */
|
|
22
|
+
opengl?: string;
|
|
23
|
+
/** Vulkan API version, e.g. "1.4.354". */
|
|
24
|
+
vulkan?: string;
|
|
25
|
+
/** Mesa's own version, which is the meaningful "driver version" for i915,
|
|
26
|
+
* xe and amdgpu — none of those modules publish one in sysfs. */
|
|
27
|
+
mesa?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** "OpenGL core profile version string: 4.6 (Core Profile) Mesa 26.2.2" → "4.6".
|
|
31
|
+
* A driver with no desktop GL at all reports its ES version instead, which is
|
|
32
|
+
* what Mission Center shows for those. */
|
|
33
|
+
export function parseOpenglVersion(text: string): string | undefined {
|
|
34
|
+
const core = /^OpenGL core profile version(?: string)?:\s*(\d+\.\d+)/m.exec(text);
|
|
35
|
+
if (core) return core[1];
|
|
36
|
+
const compat = /^OpenGL version(?: string)?:\s*(\d+\.\d+)/m.exec(text);
|
|
37
|
+
if (compat) return compat[1];
|
|
38
|
+
const es = /^OpenGL ES profile version(?: string)?:\s*OpenGL ES (\d+\.\d+)/m.exec(text);
|
|
39
|
+
return es ? `ES ${es[1]}` : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `vulkaninfo --summary` prints one `apiVersion = 1.4.354` per device; the
|
|
43
|
+
* highest is what the machine supports. */
|
|
44
|
+
export function parseVulkanVersion(text: string): string | undefined {
|
|
45
|
+
const versions = [...text.matchAll(/apiVersion\s*[=:]\s*(?:0x[0-9a-f]+\s*\()?(\d+\.\d+\.\d+)/gi)]
|
|
46
|
+
.map((m) => m[1]!)
|
|
47
|
+
.sort(compareVersions);
|
|
48
|
+
return versions.at(-1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function compareVersions(a: string, b: string): number {
|
|
52
|
+
const pa = a.split(".").map(Number);
|
|
53
|
+
const pb = b.split(".").map(Number);
|
|
54
|
+
for (let i = 0; i < 3; i++) {
|
|
55
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
56
|
+
if (d !== 0) return d;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The Mesa build out of an OpenGL version line: "OpenGL core profile version:
|
|
62
|
+
* 4.6 (Core Profile) Mesa 26.2.2-arch3.2" -> "Mesa 26.2.2-arch3.2".
|
|
63
|
+
*
|
|
64
|
+
* Anchored to that line on purpose. eglinfo also prints "EGL vendor string: Mesa
|
|
65
|
+
* Project" much earlier, so an unanchored search reports the version as
|
|
66
|
+
* "Mesa Project" — well-formed, plausible, and wrong. */
|
|
67
|
+
export function parseMesaVersion(text: string): string | undefined {
|
|
68
|
+
const m = /^OpenGL [^\n]*version(?: string)?:[^\n]*\bMesa ([\w.+-]+)/m.exec(text);
|
|
69
|
+
return m ? `Mesa ${m[1]}` : undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface GpuApiVersionReader {
|
|
73
|
+
read(): Promise<GpuApiVersions>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createGpuApiVersionReader(run: Runner = defaultRunner): GpuApiVersionReader {
|
|
77
|
+
let cached: Promise<GpuApiVersions> | null = null;
|
|
78
|
+
return {
|
|
79
|
+
read() {
|
|
80
|
+
// One in-flight promise, reused forever: two windows opening at once must
|
|
81
|
+
// not spawn `vulkaninfo` twice, and the answer cannot change afterwards.
|
|
82
|
+
cached ??= probe(run);
|
|
83
|
+
return cached;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function probe(run: Runner): Promise<GpuApiVersions> {
|
|
89
|
+
const [gl, vk] = await Promise.all([
|
|
90
|
+
tryRun(run, EGLINFO_ARGV),
|
|
91
|
+
tryRun(run, VULKANINFO_ARGV),
|
|
92
|
+
]);
|
|
93
|
+
return {
|
|
94
|
+
...(gl ? optional("opengl", parseOpenglVersion(gl)) : {}),
|
|
95
|
+
...(gl ? optional("mesa", parseMesaVersion(gl)) : {}),
|
|
96
|
+
...(vk ? optional("vulkan", parseVulkanVersion(vk)) : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A missing tool is not an error worth surfacing — the page hides the row.
|
|
102
|
+
*
|
|
103
|
+
* The exit code is deliberately NOT a gate. `eglinfo` sweeps every EGL platform
|
|
104
|
+
* it knows and exits 1 when any of them fails, which on a headless host means
|
|
105
|
+
* every run — while still printing a complete, correct surfaceless report to
|
|
106
|
+
* stdout. Gating on the code threw away 2784 good lines and left the OpenGL row
|
|
107
|
+
* permanently blank. Output is the signal; the parsers reject anything else.
|
|
108
|
+
*/
|
|
109
|
+
async function tryRun(run: Runner, argv: readonly string[]): Promise<string | null> {
|
|
110
|
+
try {
|
|
111
|
+
const r = await run([...argv], SPAWN_TIMEOUT_MS);
|
|
112
|
+
return !r.timedOut && r.stdout.trim() ? r.stdout : null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function optional<K extends string>(key: K, value: string | undefined): Partial<Record<K, string>> {
|
|
119
|
+
return value === undefined ? {} : ({ [key]: value } as Record<K, string>);
|
|
120
|
+
}
|