@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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installing Whisper on the host: download the prebuilt whisper.cpp for this
|
|
3
|
+
* platform, the chosen speech model, and the VAD model, then prove the binary
|
|
4
|
+
* actually runs here.
|
|
5
|
+
*
|
|
6
|
+
* That last step is not ceremony. The published Linux build needs glibc 2.34
|
|
7
|
+
* and libstdc++ from GCC 11, so on an older host (Ubuntu 20.04 ships glibc
|
|
8
|
+
* 2.31) every file downloads fine and the binary only fails when someone first
|
|
9
|
+
* speaks into the chat box. Running `--help` once at install time turns that
|
|
10
|
+
* into an install error naming the cause.
|
|
11
|
+
*
|
|
12
|
+
* One model is kept on disk at a time: they are 60–574 MB, and "which one is
|
|
13
|
+
* active" then has no state to drift — it is whichever file is there.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
16
|
+
import { resolve } from "node:path";
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_MODEL_ID,
|
|
19
|
+
MACOS_INSTALL_HINT,
|
|
20
|
+
VAD_MODEL,
|
|
21
|
+
WHISPER_MODELS,
|
|
22
|
+
WHISPER_VERSION,
|
|
23
|
+
modelById,
|
|
24
|
+
modelUrl,
|
|
25
|
+
vadModelUrl,
|
|
26
|
+
whisperAssetFor,
|
|
27
|
+
type WhisperAsset,
|
|
28
|
+
} from "./whisper-catalog.ts";
|
|
29
|
+
import { downloadVerified, type FetchFn } from "./whisper-download.ts";
|
|
30
|
+
import {
|
|
31
|
+
findWhisperBinary,
|
|
32
|
+
whisperBinDir,
|
|
33
|
+
whisperDir,
|
|
34
|
+
whisperModelDir,
|
|
35
|
+
whisperTmpDir,
|
|
36
|
+
type WhisperBinary,
|
|
37
|
+
} from "./whisper-paths.ts";
|
|
38
|
+
|
|
39
|
+
export type InstallPhase = "binary" | "model" | "vad" | "verify";
|
|
40
|
+
|
|
41
|
+
export interface InstallJobState {
|
|
42
|
+
modelId: string;
|
|
43
|
+
phase: InstallPhase;
|
|
44
|
+
receivedBytes: number;
|
|
45
|
+
totalBytes: number;
|
|
46
|
+
done: boolean;
|
|
47
|
+
error: string | null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface WhisperStatusModel {
|
|
51
|
+
id: string;
|
|
52
|
+
label: string;
|
|
53
|
+
note: string;
|
|
54
|
+
bytes: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WhisperStatus {
|
|
58
|
+
/** False only when this platform has no prebuilt build AND no binary on the host. */
|
|
59
|
+
installable: boolean;
|
|
60
|
+
/** Manual command for a platform PPM cannot install for (macOS). */
|
|
61
|
+
installHint: string | null;
|
|
62
|
+
binary: WhisperBinary | null;
|
|
63
|
+
version: string;
|
|
64
|
+
/** Model currently on disk, if any. */
|
|
65
|
+
model: WhisperStatusModel | null;
|
|
66
|
+
/** Binary + model + VAD model all present — the mic can use it. */
|
|
67
|
+
ready: boolean;
|
|
68
|
+
models: WhisperStatusModel[];
|
|
69
|
+
install: InstallJobState | null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let currentJob: InstallJobState | null = null;
|
|
73
|
+
|
|
74
|
+
const publicModel = (m: (typeof WHISPER_MODELS)[number]): WhisperStatusModel => ({
|
|
75
|
+
id: m.id,
|
|
76
|
+
label: m.label,
|
|
77
|
+
note: m.note,
|
|
78
|
+
bytes: m.bytes,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/** The installed model, found by which catalog file exists on disk. */
|
|
82
|
+
export function installedModel(): (typeof WHISPER_MODELS)[number] | null {
|
|
83
|
+
return WHISPER_MODELS.find((m) => existsSync(resolve(whisperModelDir(), m.file))) ?? null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function vadModelPath(): string {
|
|
87
|
+
return resolve(whisperModelDir(), VAD_MODEL.file);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getWhisperStatus(
|
|
91
|
+
platform: NodeJS.Platform = process.platform,
|
|
92
|
+
arch: string = process.arch,
|
|
93
|
+
): WhisperStatus {
|
|
94
|
+
const binary = findWhisperBinary(platform);
|
|
95
|
+
const model = installedModel();
|
|
96
|
+
const hasAsset = whisperAssetFor(platform, arch) !== null;
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
installable: hasAsset || binary !== null,
|
|
100
|
+
installHint: !hasAsset && platform === "darwin" ? MACOS_INSTALL_HINT : null,
|
|
101
|
+
binary,
|
|
102
|
+
version: WHISPER_VERSION,
|
|
103
|
+
model: model ? publicModel(model) : null,
|
|
104
|
+
ready: binary !== null && model !== null && existsSync(vadModelPath()),
|
|
105
|
+
models: WHISPER_MODELS.map(publicModel),
|
|
106
|
+
install: currentJob,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function extractArchive(archive: string, asset: WhisperAsset, destDir: string, platform: NodeJS.Platform) {
|
|
111
|
+
const cmd =
|
|
112
|
+
asset.ext === "tar.gz"
|
|
113
|
+
? ["tar", "-xzf", archive, "-C", destDir]
|
|
114
|
+
: platform === "win32"
|
|
115
|
+
? // Windows 10+ ships bsdtar as tar.exe, which reads zip.
|
|
116
|
+
["tar", "-xf", archive, "-C", destDir]
|
|
117
|
+
: ["unzip", "-o", "-q", archive, "-d", destDir];
|
|
118
|
+
|
|
119
|
+
const proc = Bun.spawn({ cmd, stdout: "ignore", stderr: "pipe" });
|
|
120
|
+
const code = await proc.exited;
|
|
121
|
+
if (code !== 0) {
|
|
122
|
+
const stderr = await new Response(proc.stderr).text();
|
|
123
|
+
throw new Error(`extract failed (${cmd[0]} exit ${code}): ${stderr.slice(0, 200)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Run the binary once so a host that cannot execute it fails here, not mid-sentence. */
|
|
128
|
+
async function verifyBinaryRuns(path: string): Promise<void> {
|
|
129
|
+
let proc;
|
|
130
|
+
try {
|
|
131
|
+
proc = Bun.spawn({ cmd: [path, "--help"], stdout: "ignore", stderr: "pipe", windowsHide: true });
|
|
132
|
+
} catch (e: any) {
|
|
133
|
+
throw new Error(`whisper-cli could not be started: ${e?.message ?? e}`);
|
|
134
|
+
}
|
|
135
|
+
const [code, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
136
|
+
if (code !== 0) {
|
|
137
|
+
const detail = stderr.trim().split("\n")[0] ?? `exit ${code}`;
|
|
138
|
+
throw new Error(`whisper-cli does not run on this host: ${detail}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Keep only the model just installed — the others are hundreds of MB of nothing. */
|
|
143
|
+
function pruneOtherModels(keepFile: string) {
|
|
144
|
+
for (const m of WHISPER_MODELS) {
|
|
145
|
+
if (m.file !== keepFile) rmSync(resolve(whisperModelDir(), m.file), { force: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function runInstall(modelId: string, platform: NodeJS.Platform, arch: string, fetchFn?: FetchFn) {
|
|
150
|
+
const model = modelById(modelId);
|
|
151
|
+
if (!model) throw new Error(`unknown model: ${modelId}`);
|
|
152
|
+
const job = currentJob!;
|
|
153
|
+
const progress = (received: number, total: number) => {
|
|
154
|
+
job.receivedBytes = received;
|
|
155
|
+
job.totalBytes = total || 0;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
mkdirSync(whisperModelDir(), { recursive: true });
|
|
159
|
+
|
|
160
|
+
// A host that already has whisper-cli (Homebrew, distro package) needs models only.
|
|
161
|
+
if (!findWhisperBinary(platform)) {
|
|
162
|
+
const asset = whisperAssetFor(platform, arch);
|
|
163
|
+
if (!asset) throw new Error(`no whisper.cpp build for ${platform}/${arch} — install it manually`);
|
|
164
|
+
job.phase = "binary";
|
|
165
|
+
mkdirSync(whisperBinDir(), { recursive: true });
|
|
166
|
+
mkdirSync(whisperTmpDir(), { recursive: true });
|
|
167
|
+
const archive = resolve(whisperTmpDir(), asset.file);
|
|
168
|
+
await downloadVerified({ url: asset.url, dest: archive, sha256: asset.sha256, onProgress: progress, fetchFn });
|
|
169
|
+
await extractArchive(archive, asset, whisperBinDir(), platform);
|
|
170
|
+
rmSync(archive, { force: true });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
job.phase = "model";
|
|
174
|
+
progress(0, model.bytes);
|
|
175
|
+
await downloadVerified({
|
|
176
|
+
url: modelUrl(model),
|
|
177
|
+
dest: resolve(whisperModelDir(), model.file),
|
|
178
|
+
sha256: model.sha256,
|
|
179
|
+
onProgress: progress,
|
|
180
|
+
fetchFn,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
job.phase = "vad";
|
|
184
|
+
progress(0, VAD_MODEL.bytes);
|
|
185
|
+
await downloadVerified({
|
|
186
|
+
url: vadModelUrl(),
|
|
187
|
+
dest: vadModelPath(),
|
|
188
|
+
sha256: VAD_MODEL.sha256,
|
|
189
|
+
onProgress: progress,
|
|
190
|
+
fetchFn,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
job.phase = "verify";
|
|
194
|
+
const binary = findWhisperBinary(platform);
|
|
195
|
+
if (!binary) throw new Error("whisper-cli missing after install");
|
|
196
|
+
await verifyBinaryRuns(binary.path);
|
|
197
|
+
|
|
198
|
+
pruneOtherModels(model.file);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Start an install in the background and return immediately — the UI polls
|
|
203
|
+
* `getWhisperStatus()`. One at a time: a second call while a job runs throws.
|
|
204
|
+
*/
|
|
205
|
+
export function startWhisperInstall(
|
|
206
|
+
modelId: string = DEFAULT_MODEL_ID,
|
|
207
|
+
opts: { platform?: NodeJS.Platform; arch?: string; fetchFn?: FetchFn } = {},
|
|
208
|
+
): InstallJobState {
|
|
209
|
+
if (currentJob && !currentJob.done) throw new Error("An install is already running");
|
|
210
|
+
const platform = opts.platform ?? process.platform;
|
|
211
|
+
const arch = opts.arch ?? process.arch;
|
|
212
|
+
if (!modelById(modelId)) throw new Error(`unknown model: ${modelId}`);
|
|
213
|
+
|
|
214
|
+
const job: InstallJobState = {
|
|
215
|
+
modelId,
|
|
216
|
+
phase: "binary",
|
|
217
|
+
receivedBytes: 0,
|
|
218
|
+
totalBytes: 0,
|
|
219
|
+
done: false,
|
|
220
|
+
error: null,
|
|
221
|
+
};
|
|
222
|
+
currentJob = job;
|
|
223
|
+
|
|
224
|
+
runInstall(modelId, platform, arch, opts.fetchFn)
|
|
225
|
+
.then(() => {
|
|
226
|
+
job.done = true;
|
|
227
|
+
})
|
|
228
|
+
.catch((e: any) => {
|
|
229
|
+
job.error = e?.message ?? String(e);
|
|
230
|
+
job.done = true;
|
|
231
|
+
console.error("[whisper] install failed:", job.error);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return job;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Remove everything PPM downloaded. A system-installed whisper-cli is left alone. */
|
|
238
|
+
export function uninstallWhisper(): void {
|
|
239
|
+
if (currentJob && !currentJob.done) throw new Error("An install is running");
|
|
240
|
+
rmSync(whisperDir(), { recursive: true, force: true });
|
|
241
|
+
currentJob = null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Test seam: forget any finished job so a suite starts from a clean status. */
|
|
245
|
+
export function _resetWhisperInstallState(): void {
|
|
246
|
+
currentJob = null;
|
|
247
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the Whisper install lives, and how the binary is found.
|
|
3
|
+
*
|
|
4
|
+
* Everything PPM downloads sits under `<ppm dir>/whisper/` so uninstall is one
|
|
5
|
+
* `rm -rf` and an isolated `PPM_HOME` (tests) never touches a real install.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { getPpmDir } from "../ppm-dir.ts";
|
|
10
|
+
|
|
11
|
+
export const whisperDir = () => resolve(getPpmDir(), "whisper");
|
|
12
|
+
export const whisperBinDir = () => resolve(whisperDir(), "bin");
|
|
13
|
+
export const whisperModelDir = () => resolve(whisperDir(), "models");
|
|
14
|
+
export const whisperTmpDir = () => resolve(whisperDir(), "tmp");
|
|
15
|
+
|
|
16
|
+
export const cliName = (platform: NodeJS.Platform = process.platform) =>
|
|
17
|
+
platform === "win32" ? "whisper-cli.exe" : "whisper-cli";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The extracted archive keeps its own top-level directory — `whisper-bin-ubuntu-x64/`
|
|
21
|
+
* on Linux, `Release/` on Windows — and those names carry the build tag, so the
|
|
22
|
+
* binary is located by looking one level down rather than assumed.
|
|
23
|
+
*/
|
|
24
|
+
export function findBundledBinary(platform: NodeJS.Platform = process.platform): string | null {
|
|
25
|
+
const root = whisperBinDir();
|
|
26
|
+
if (!existsSync(root)) return null;
|
|
27
|
+
const name = cliName(platform);
|
|
28
|
+
if (existsSync(resolve(root, name))) return resolve(root, name);
|
|
29
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
30
|
+
if (!entry.isDirectory()) continue;
|
|
31
|
+
const candidate = resolve(root, entry.name, name);
|
|
32
|
+
if (existsSync(candidate)) return candidate;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Homebrew's `whisper.cpp` is how a Mac gets a `whisper-cli` (the release has no
|
|
39
|
+
* macOS CLI to download), and a PPM started by launchd/systemd inherits a PATH
|
|
40
|
+
* that usually has neither Homebrew prefix in it — so both are checked directly
|
|
41
|
+
* rather than trusting `PATH` alone.
|
|
42
|
+
*/
|
|
43
|
+
const SYSTEM_PATHS = ["/opt/homebrew/bin/whisper-cli", "/usr/local/bin/whisper-cli", "/usr/bin/whisper-cli"];
|
|
44
|
+
|
|
45
|
+
export function findSystemBinary(): string | null {
|
|
46
|
+
const onPath = Bun.which("whisper-cli");
|
|
47
|
+
if (onPath) return onPath;
|
|
48
|
+
return SYSTEM_PATHS.find((p) => existsSync(p)) ?? null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface WhisperBinary {
|
|
52
|
+
path: string;
|
|
53
|
+
/** `bundled` = installed by PPM and removable from Settings; `system` = the host's own. */
|
|
54
|
+
source: "bundled" | "system";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function findWhisperBinary(platform: NodeJS.Platform = process.platform): WhisperBinary | null {
|
|
58
|
+
const bundled = findBundledBinary(platform);
|
|
59
|
+
if (bundled) return { path: bundled, source: "bundled" };
|
|
60
|
+
const system = findSystemBinary();
|
|
61
|
+
return system ? { path: system, source: "system" } : null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One transcription = one `whisper-cli` run over a temp WAV.
|
|
3
|
+
*
|
|
4
|
+
* Per-request process rather than a long-lived `whisper-server`, for two
|
|
5
|
+
* reasons: Homebrew's formula builds with `WHISPER_BUILD_SERVER=OFF`, so a Mac
|
|
6
|
+
* has no server binary to talk to; and a resident server holds the model
|
|
7
|
+
* (574 MB) in RAM between sentences that are minutes apart.
|
|
8
|
+
*
|
|
9
|
+
* Runs are serialised. Two at once would each load the model and fight for the
|
|
10
|
+
* same cores, turning two five-second waits into two twelve-second ones.
|
|
11
|
+
*/
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
14
|
+
import { availableParallelism } from "node:os";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { isInventedLine } from "./whisper-hallucinations.ts";
|
|
17
|
+
import { installedModel, vadModelPath } from "./whisper-install.service.ts";
|
|
18
|
+
import { findWhisperBinary, whisperModelDir, whisperTmpDir } from "./whisper-paths.ts";
|
|
19
|
+
|
|
20
|
+
const TIMEOUT_MS = 180_000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* whisper.cpp defaults to 4 threads. Measured on a 24-thread i9-12900K with
|
|
24
|
+
* large-v3-turbo over a 10s clip: 4 threads 12.0s, 8 threads 8.1s, 16 threads
|
|
25
|
+
* 5.6s, 24 threads 5.4s — so it is worth raising, and worth capping where the
|
|
26
|
+
* curve flattens instead of taking every core the host has.
|
|
27
|
+
*/
|
|
28
|
+
export function threadCount(cpus: number = availableParallelism()): number {
|
|
29
|
+
return Math.max(1, Math.min(16, cpus));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A non-speech annotation on a line of its own: "[BLANK_AUDIO]", "(nhạc)", "*gunshot*". */
|
|
33
|
+
const ANNOTATION = /^[[(*][^)\]*]*[\])*]$/;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `-nt` prints text without timestamps, one line per segment. Three kinds of
|
|
37
|
+
* line are not something anyone dictated: an annotation, a line with no letter
|
|
38
|
+
* or digit in it at all (a bare "." is what this model makes of a hum in
|
|
39
|
+
* English), and a sentence it invented out of non-speech — see
|
|
40
|
+
* whisper-hallucinations.ts.
|
|
41
|
+
*/
|
|
42
|
+
export function parseWhisperText(stdout: string): string {
|
|
43
|
+
return stdout
|
|
44
|
+
.split("\n")
|
|
45
|
+
.map((line) => line.trim())
|
|
46
|
+
.filter(
|
|
47
|
+
(line) =>
|
|
48
|
+
line.length > 0 &&
|
|
49
|
+
!ANNOTATION.test(line) &&
|
|
50
|
+
/[\p{L}\p{N}]/u.test(line) &&
|
|
51
|
+
!isInventedLine(line),
|
|
52
|
+
)
|
|
53
|
+
.join(" ")
|
|
54
|
+
.replace(/\s+/g, " ")
|
|
55
|
+
.trim();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function whisperArgs(binary: string, wavPath: string, modelPath: string, language: string): string[] {
|
|
59
|
+
return [
|
|
60
|
+
binary,
|
|
61
|
+
"-m", modelPath,
|
|
62
|
+
"-f", wavPath,
|
|
63
|
+
"-l", language,
|
|
64
|
+
"-t", String(threadCount()),
|
|
65
|
+
"-nt",
|
|
66
|
+
"-np",
|
|
67
|
+
// Without VAD, silence is transcribed as an invented sentence — see whisper-catalog.ts.
|
|
68
|
+
"--vad",
|
|
69
|
+
"--vad-model", vadModelPath(),
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let queue: Promise<unknown> = Promise.resolve();
|
|
74
|
+
|
|
75
|
+
export interface TranscribeResult {
|
|
76
|
+
text: string;
|
|
77
|
+
/** Wall time of the whisper-cli run, for the UI's "took Ns" and for tuning. */
|
|
78
|
+
ms: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Throws when Whisper is not installed, the run fails, or it outruns the timeout. */
|
|
82
|
+
export async function transcribeWav(wav: Uint8Array, language = "vi"): Promise<TranscribeResult> {
|
|
83
|
+
const run = queue.then(() => runOnce(wav, language));
|
|
84
|
+
// Keep the chain alive after a failure, and never leak the rejection through it.
|
|
85
|
+
queue = run.catch(() => {});
|
|
86
|
+
return run;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function runOnce(wav: Uint8Array, language: string): Promise<TranscribeResult> {
|
|
90
|
+
const binary = findWhisperBinary();
|
|
91
|
+
const model = installedModel();
|
|
92
|
+
if (!binary || !model) throw new Error("Whisper is not installed on this host");
|
|
93
|
+
|
|
94
|
+
mkdirSync(whisperTmpDir(), { recursive: true });
|
|
95
|
+
const wavPath = resolve(whisperTmpDir(), `${randomUUID()}.wav`);
|
|
96
|
+
await Bun.write(wavPath, wav);
|
|
97
|
+
|
|
98
|
+
const started = Date.now();
|
|
99
|
+
try {
|
|
100
|
+
const cmd = whisperArgs(binary.path, wavPath, resolve(whisperModelDir(), model.file), language);
|
|
101
|
+
const proc = Bun.spawn({ cmd, stdout: "pipe", stderr: "pipe", stdin: "ignore", windowsHide: true });
|
|
102
|
+
const timer = setTimeout(() => proc.kill(), TIMEOUT_MS);
|
|
103
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
104
|
+
new Response(proc.stdout).text(),
|
|
105
|
+
new Response(proc.stderr).text(),
|
|
106
|
+
proc.exited,
|
|
107
|
+
]);
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
if (code !== 0) {
|
|
110
|
+
const detail = stderr.trim().split("\n").slice(-1)[0] ?? `exit ${code}`;
|
|
111
|
+
throw new Error(`whisper-cli failed: ${detail}`);
|
|
112
|
+
}
|
|
113
|
+
return { text: parseWhisperText(stdout), ms: Date.now() - started };
|
|
114
|
+
} finally {
|
|
115
|
+
rmSync(wavPath, { force: true });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
readAndDeleteCmd, readStatus, updateStatus, writeStatus,
|
|
21
21
|
STATUS_FILE, PID_FILE,
|
|
22
22
|
} from "./supervisor-state.ts";
|
|
23
|
-
import type
|
|
23
|
+
import { resolveTunnelConfig, type ResolvedTunnelConfig, type TunnelMode } from "./named-tunnel/named-tunnel-config.ts";
|
|
24
24
|
import { readTunnelConfigFresh, chooseTunnelSpawn } from "./named-tunnel/named-tunnel-runtime.ts";
|
|
25
25
|
import { waitForLogLine } from "./named-tunnel/named-tunnel-readiness.ts";
|
|
26
26
|
import { nextNamedRetryDelayMs } from "./named-tunnel/named-tunnel-retry.ts";
|
|
@@ -101,6 +101,11 @@ const TUNNEL_REGEN_MIN_INTERVAL_MS = 300_000; // 5min
|
|
|
101
101
|
// cache, never `configService` (a separate process, stale here) nor a fresh
|
|
102
102
|
// async DB read (would reorder the startup adopt/probe-before-spawn race).
|
|
103
103
|
let namedTunnelMode: ResolvedTunnelConfig | null = null;
|
|
104
|
+
// Effective master switch: the argv flag AND `tunnel.enabled` from config.
|
|
105
|
+
// Module-level rather than a local because the SIGUSR1 and Windows-command
|
|
106
|
+
// handlers close over it before startup reaches the config read, and because
|
|
107
|
+
// `retunnel` flips it live when the user toggles the switch.
|
|
108
|
+
let tunnelSharingEnabled = false;
|
|
104
109
|
// The mode of the tunnel actually spawned (as opposed to `namedTunnelMode`,
|
|
105
110
|
// which is merely the persisted intent) — a token failure leaves config=named
|
|
106
111
|
// but live=quick, and every status write / throttle / restart decision must
|
|
@@ -430,6 +435,26 @@ function restartTunnel(port: number) {
|
|
|
430
435
|
spawnTunnel(port).catch((e) => log("ERROR", `restartTunnel failed: ${e}`));
|
|
431
436
|
}
|
|
432
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Tear the tunnel down for good because the master switch went off — as opposed
|
|
440
|
+
* to `restartTunnel`, which kills in order to respawn.
|
|
441
|
+
*
|
|
442
|
+
* Bumping the generation is the load-bearing part: the running spawn loop is
|
|
443
|
+
* awaiting its child's `exited` and would otherwise treat the kill as a crash
|
|
444
|
+
* and respawn. The probe needs no stopping — it returns early once there is no
|
|
445
|
+
* `tunnelUrl` and no live process.
|
|
446
|
+
*/
|
|
447
|
+
function stopTunnelForDisable(): void {
|
|
448
|
+
tunnelGeneration++;
|
|
449
|
+
cancelNamedRetry();
|
|
450
|
+
namedRetryAttempt = 0;
|
|
451
|
+
if (tunnelChild) { try { tunnelChild.kill(); } catch {} tunnelChild = null; }
|
|
452
|
+
if (adoptedTunnelPid) { try { process.kill(adoptedTunnelPid, "SIGTERM"); } catch {} adoptedTunnelPid = null; }
|
|
453
|
+
tunnelUrl = null;
|
|
454
|
+
tunnelPort = null;
|
|
455
|
+
updateStatus({ shareUrl: null, tunnelPid: null, tunnelPort: null });
|
|
456
|
+
}
|
|
457
|
+
|
|
433
458
|
// ─── Server shutdown ───────────────────────────────────────────────────
|
|
434
459
|
// On Windows the server's Claude SDK grandchildren are node-spawned (the
|
|
435
460
|
// provider forces `executable: "node"`), so they live OUTSIDE Bun's job
|
|
@@ -855,6 +880,14 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
|
|
|
855
880
|
// setup, disable, or token rotation since the last spawn must be picked up
|
|
856
881
|
// before deciding what to spawn next.
|
|
857
882
|
namedTunnelMode = await readTunnelConfigFresh();
|
|
883
|
+
// The freshest possible view of the master switch, so consult it here rather than
|
|
884
|
+
// relying on all six call sites remembering to. Two of them — the Windows
|
|
885
|
+
// leaked-probe-handle restart and the edge probe's respawns — do not, and a
|
|
886
|
+
// respawn is exactly when a switch flipped since the last spawn should take hold.
|
|
887
|
+
if (!namedTunnelMode.enabled) {
|
|
888
|
+
log("INFO", "Public tunnel is switched off — not spawning cloudflared");
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
858
891
|
|
|
859
892
|
let bin: string;
|
|
860
893
|
try {
|
|
@@ -1206,6 +1239,10 @@ async function probeNamedTunnelHealth(): Promise<boolean> {
|
|
|
1206
1239
|
}
|
|
1207
1240
|
|
|
1208
1241
|
function startTunnelProbe() {
|
|
1242
|
+
// Idempotent: a supervisor that booted with the tunnel switched off never
|
|
1243
|
+
// started this, and turning the switch on has to be able to start it then —
|
|
1244
|
+
// without leaving a second interval behind on the boots that did start it.
|
|
1245
|
+
if (tunnelProbeTimer) return;
|
|
1209
1246
|
tunnelProbeTimer = setInterval(async () => {
|
|
1210
1247
|
if (shuttingDown || !tunnelUrl) { tunnelFailCount = 0; return; }
|
|
1211
1248
|
if (!tunnelChild && !adoptedTunnelPid) { tunnelFailCount = 0; return; }
|
|
@@ -2049,7 +2086,16 @@ export async function runSupervisor(opts: {
|
|
|
2049
2086
|
// restarts the ladder so a later transient failure gets a full budget.
|
|
2050
2087
|
cancelNamedRetry();
|
|
2051
2088
|
namedRetryAttempt = 0;
|
|
2052
|
-
|
|
2089
|
+
// `retunnel` is also how the master switch is applied without a
|
|
2090
|
+
// restart, so it has to be able to mean "stop" and not only "respawn".
|
|
2091
|
+
tunnelSharingEnabled = _opts.share && namedTunnelMode.enabled;
|
|
2092
|
+
if (tunnelSharingEnabled) {
|
|
2093
|
+
startTunnelProbe();
|
|
2094
|
+
restartTunnel(_opts.port);
|
|
2095
|
+
} else {
|
|
2096
|
+
log("INFO", "Tunnel switched off — tearing down the running tunnel");
|
|
2097
|
+
stopTunnelForDisable();
|
|
2098
|
+
}
|
|
2053
2099
|
// Deliberate fall-through (no return): a bare `ppm restart` sends a
|
|
2054
2100
|
// bare SIGUSR2 with no command file of its own, and a `retunnel` that
|
|
2055
2101
|
// happens to still be unclaimed at that instant must not silently
|
|
@@ -2091,7 +2137,7 @@ export async function runSupervisor(opts: {
|
|
|
2091
2137
|
log("ERROR", `Self-replace failed: ${result.error}, restarting children`);
|
|
2092
2138
|
spawnServer(serverArgs, logFd);
|
|
2093
2139
|
// Tunnel was kept alive during selfReplace; only respawn if dead
|
|
2094
|
-
if (
|
|
2140
|
+
if (tunnelSharingEnabled && !tunnelChild && !tunnelUrl) spawnTunnel(_opts.port);
|
|
2095
2141
|
}
|
|
2096
2142
|
});
|
|
2097
2143
|
|
|
@@ -2200,7 +2246,16 @@ export async function runSupervisor(opts: {
|
|
|
2200
2246
|
tunnelFailCount = 0;
|
|
2201
2247
|
cancelNamedRetry();
|
|
2202
2248
|
namedRetryAttempt = 0;
|
|
2203
|
-
|
|
2249
|
+
// Same dual meaning as the POSIX path: this is how the master switch
|
|
2250
|
+
// is applied live, so it must also be able to mean "stop".
|
|
2251
|
+
tunnelSharingEnabled = _opts.share && cfg.enabled;
|
|
2252
|
+
if (tunnelSharingEnabled) {
|
|
2253
|
+
startTunnelProbe();
|
|
2254
|
+
restartTunnel(_opts.port);
|
|
2255
|
+
} else {
|
|
2256
|
+
log("INFO", "Tunnel switched off — tearing down the running tunnel");
|
|
2257
|
+
stopTunnelForDisable();
|
|
2258
|
+
}
|
|
2204
2259
|
});
|
|
2205
2260
|
}
|
|
2206
2261
|
else if (cmd.action === "upgrade") {
|
|
@@ -2209,7 +2264,7 @@ export async function runSupervisor(opts: {
|
|
|
2209
2264
|
if (!result.success) {
|
|
2210
2265
|
log("ERROR", `Self-replace failed: ${result.error}, restarting children`);
|
|
2211
2266
|
spawnServer(serverArgs, logFd);
|
|
2212
|
-
if (
|
|
2267
|
+
if (tunnelSharingEnabled && !tunnelChild && !tunnelUrl) spawnTunnel(_opts.port);
|
|
2213
2268
|
}
|
|
2214
2269
|
});
|
|
2215
2270
|
}
|
|
@@ -2229,12 +2284,34 @@ export async function runSupervisor(opts: {
|
|
|
2229
2284
|
// tunnel's origin port (public URL continuity), so adoption state must be
|
|
2230
2285
|
// settled first — the old order raced spawnServer's port selection.
|
|
2231
2286
|
let tunnelAdopted = false;
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2287
|
+
// Populate the cache BEFORE adoptTunnel/probe — an adopted tunnel skips
|
|
2288
|
+
// spawnTunnel entirely for this whole generation, so without this the
|
|
2289
|
+
// cache would stay null (adoptTunnel's named-hostname gate always refuses,
|
|
2290
|
+
// and the probe can never tell named from quick) until the next spawn.
|
|
2291
|
+
// It also carries the master switch, so it is read before the gate rather
|
|
2292
|
+
// than inside it.
|
|
2293
|
+
// This read must not be able to decide whether the edge below gets spawned.
|
|
2294
|
+
// It already did once: a `systemctl restart` brought this process up while
|
|
2295
|
+
// the outgoing one still held the database's write lock, the rejection ended
|
|
2296
|
+
// startup right here, and PPM ran for eight minutes with nothing listening on
|
|
2297
|
+
// its public port — the server child came back on the health check, which has
|
|
2298
|
+
// no equivalent for an edge that was never started in the first place.
|
|
2299
|
+
//
|
|
2300
|
+
// The fallback is sharing OFF, not the resolver's default: an absent config
|
|
2301
|
+
// row means "on" (it predates the master switch), so defaulting would publish
|
|
2302
|
+
// a tunnel for someone who had switched sharing off and whose config merely
|
|
2303
|
+
// could not be read.
|
|
2304
|
+
try {
|
|
2237
2305
|
namedTunnelMode = await readTunnelConfigFresh();
|
|
2306
|
+
} catch (e) {
|
|
2307
|
+
log("ERROR", `Tunnel config unreadable — continuing with sharing off: ${e}`);
|
|
2308
|
+
namedTunnelMode = resolveTunnelConfig({ enabled: false });
|
|
2309
|
+
}
|
|
2310
|
+
tunnelSharingEnabled = opts.share && namedTunnelMode.enabled;
|
|
2311
|
+
if (opts.share && !tunnelSharingEnabled) {
|
|
2312
|
+
log("INFO", "Tunnel off (tunnel.enabled = false) — no cloudflared will be started");
|
|
2313
|
+
}
|
|
2314
|
+
if (tunnelSharingEnabled) {
|
|
2238
2315
|
// Defensive — already `false` at module init, but a fresh boot must never
|
|
2239
2316
|
// start with an inherited restart-once budget "spent".
|
|
2240
2317
|
namedProbeRestartAttempted = false;
|
|
@@ -2250,6 +2327,12 @@ export async function runSupervisor(opts: {
|
|
|
2250
2327
|
// Sweep leftover cloudflared orphans (crashed supervisors / pre-fix stale
|
|
2251
2328
|
// loops) so they don't saturate the network and false-trigger regeneration.
|
|
2252
2329
|
await reapOrphanedTunnels(tunnelAdopted ? adoptedTunnelPid : null);
|
|
2330
|
+
} else if (opts.share) {
|
|
2331
|
+
// Switched off while a tunnel from an earlier boot is still alive: nothing
|
|
2332
|
+
// adopts or probes it now, so it would go on serving a stale origin with no
|
|
2333
|
+
// supervisor watching it.
|
|
2334
|
+
killStaleTunnel();
|
|
2335
|
+
await reapOrphanedTunnels(null);
|
|
2253
2336
|
}
|
|
2254
2337
|
|
|
2255
2338
|
// The edge owns the public port, so it must exist before anything else tries
|
|
@@ -2298,7 +2381,7 @@ export async function runSupervisor(opts: {
|
|
|
2298
2381
|
|
|
2299
2382
|
// Spawn server + (fresh) tunnel in parallel
|
|
2300
2383
|
const promises: Promise<void>[] = [spawnServer(serverArgs, logFd)];
|
|
2301
|
-
if (
|
|
2384
|
+
if (tunnelSharingEnabled && !tunnelAdopted) promises.push(spawnTunnel(_opts.port));
|
|
2302
2385
|
|
|
2303
2386
|
await Promise.all(promises);
|
|
2304
2387
|
|