@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,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each compaction in a transcript cost and saved.
|
|
3
|
+
*
|
|
4
|
+
* Compaction is the largest single thing that happens to a chat and the only one the
|
|
5
|
+
* transcript records without the UI ever saying so: a session silently loses most of
|
|
6
|
+
* its history, the next turn's prefix collapses, and the only visible trace is a long
|
|
7
|
+
* summary message. This reads the figures back out so the divider above that summary
|
|
8
|
+
* can state them.
|
|
9
|
+
*
|
|
10
|
+
* The link between a boundary and the message it introduces is `parentUuid`. Claude
|
|
11
|
+
* Code writes the `compact_boundary` record with `parentUuid: null` — it is the root
|
|
12
|
+
* of the post-compaction segment — and the summary immediately after it carries the
|
|
13
|
+
* boundary's uuid as its parent. Keying the result by that summary's uuid is what
|
|
14
|
+
* lets both message readers stamp it on without either of them knowing the layout,
|
|
15
|
+
* since `parseSessionMessage` already uses the record uuid as `ChatMessage.id`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync } from "node:fs";
|
|
19
|
+
import type { CompactionInfo } from "../types/chat.ts";
|
|
20
|
+
import { readLines } from "./read-lines.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Cheap test for the only two lines per compaction worth parsing.
|
|
24
|
+
*
|
|
25
|
+
* A transcript reaches tens of megabytes and `JSON.parse` on every line of one is the
|
|
26
|
+
* dominant cost of reading it at all — but a compaction leaves two records in a file
|
|
27
|
+
* that holds thousands, so a substring check keeps this a scan rather than a parse.
|
|
28
|
+
*/
|
|
29
|
+
const BOUNDARY_MARKER = '"compact_boundary"';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Map of compact-summary message uuid → that compaction's figures.
|
|
33
|
+
*
|
|
34
|
+
* Answers an empty map for a missing or unreadable file: a transcript that cannot be
|
|
35
|
+
* read still has messages worth showing, and a divider is not worth failing a history
|
|
36
|
+
* load over.
|
|
37
|
+
*/
|
|
38
|
+
export async function readCompactions(jsonlPath: string): Promise<Map<string, CompactionInfo>> {
|
|
39
|
+
const byMessageId = new Map<string, CompactionInfo>();
|
|
40
|
+
if (!existsSync(jsonlPath)) return byMessageId;
|
|
41
|
+
|
|
42
|
+
// The boundary is read one line before the summary that claims it, so it waits here
|
|
43
|
+
// until a record names it as parent. Held rather than assumed adjacent: the pairing
|
|
44
|
+
// is by uuid, so a record written between the two cannot mis-attach the figures.
|
|
45
|
+
let pending: { uuid: string; info: CompactionInfo } | null = null;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
for await (const line of readLines(jsonlPath)) {
|
|
49
|
+
if (line.includes(BOUNDARY_MARKER)) {
|
|
50
|
+
const info = parseBoundary(line);
|
|
51
|
+
pending = info ? { uuid: info.uuid, info: info.compaction } : null;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (!pending) continue;
|
|
55
|
+
// Only lines that could be the child are parsed — the uuid of the boundary is a
|
|
56
|
+
// full uuid, so this substring test has no realistic false positive.
|
|
57
|
+
if (!line.includes(pending.uuid)) continue;
|
|
58
|
+
const child = parseChild(line);
|
|
59
|
+
if (child?.parentUuid === pending.uuid) {
|
|
60
|
+
byMessageId.set(child.uuid, pending.info);
|
|
61
|
+
pending = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Truncated or mid-write — keep whatever pairs were already complete.
|
|
66
|
+
}
|
|
67
|
+
return byMessageId;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The boundary's own uuid and figures, or null when the record is not one we can use. */
|
|
71
|
+
function parseBoundary(line: string): { uuid: string; compaction: CompactionInfo } | null {
|
|
72
|
+
let entry: any;
|
|
73
|
+
try {
|
|
74
|
+
entry = JSON.parse(line);
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (entry?.type !== "system" || entry.subtype !== "compact_boundary") return null;
|
|
79
|
+
const meta = entry.compactMetadata;
|
|
80
|
+
if (typeof entry.uuid !== "string" || !meta) return null;
|
|
81
|
+
|
|
82
|
+
const preTokens = numberOr(meta.preTokens, 0);
|
|
83
|
+
const postTokens = numberOr(meta.postTokens, 0);
|
|
84
|
+
// A boundary with no `preTokens` is a record shape this cannot describe — a zero
|
|
85
|
+
// saving would render as a confident "saved 0", which is worse than no divider.
|
|
86
|
+
if (preTokens <= 0) return null;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
uuid: entry.uuid,
|
|
90
|
+
compaction: {
|
|
91
|
+
trigger: meta.trigger === "manual" ? "manual" : "auto",
|
|
92
|
+
preTokens,
|
|
93
|
+
postTokens,
|
|
94
|
+
// Clamped: `postTokens` is absent on older records, and a summary that somehow
|
|
95
|
+
// measured larger than its input must not report a negative saving.
|
|
96
|
+
savedTokens: Math.max(0, preTokens - postTokens),
|
|
97
|
+
...(typeof meta.durationMs === "number" ? { durationMs: meta.durationMs } : {}),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The uuid/parentUuid pair of a candidate child record. */
|
|
103
|
+
function parseChild(line: string): { uuid: string; parentUuid: string } | null {
|
|
104
|
+
try {
|
|
105
|
+
const entry = JSON.parse(line);
|
|
106
|
+
if (typeof entry?.uuid !== "string" || typeof entry.parentUuid !== "string") return null;
|
|
107
|
+
return { uuid: entry.uuid, parentUuid: entry.parentUuid };
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function numberOr(value: unknown, fallback: number): number {
|
|
114
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Stamp each compaction onto the message that opens its segment. */
|
|
118
|
+
export function applyCompactions(
|
|
119
|
+
messages: { id: string; compaction?: CompactionInfo }[],
|
|
120
|
+
byMessageId: Map<string, CompactionInfo>,
|
|
121
|
+
): void {
|
|
122
|
+
if (byMessageId.size === 0) return;
|
|
123
|
+
for (const msg of messages) {
|
|
124
|
+
const info = byMessageId.get(msg.id);
|
|
125
|
+
if (info) msg.compaction = info;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -8,7 +8,7 @@ import { backupDbSync } from "./db-backup/db-backup-sync.ts";
|
|
|
8
8
|
import { CODEX_DEFAULT_MODEL } from "../types/config.ts";
|
|
9
9
|
// Must equal the last `PRAGMA user_version` below: the pre-migration snapshot is skipped for
|
|
10
10
|
// any database already at this version, so a stale value silently drops that backup.
|
|
11
|
-
export const CURRENT_SCHEMA_VERSION =
|
|
11
|
+
export const CURRENT_SCHEMA_VERSION = 54;
|
|
12
12
|
|
|
13
13
|
let db: Database | null = null;
|
|
14
14
|
let dbProfile: string | null = null;
|
|
@@ -36,8 +36,7 @@ export function getDb(): Database {
|
|
|
36
36
|
const ppmDir = getPpmDir();
|
|
37
37
|
if (!existsSync(ppmDir)) mkdirSync(ppmDir, { recursive: true });
|
|
38
38
|
db = new Database(getDbPath());
|
|
39
|
-
db
|
|
40
|
-
db.exec("PRAGMA foreign_keys = ON");
|
|
39
|
+
applyDbPragmas(db);
|
|
41
40
|
backupBeforeMigrations(db);
|
|
42
41
|
runMigrations(db);
|
|
43
42
|
return db;
|
|
@@ -68,6 +67,26 @@ function backupBeforeMigrations(database: Database): void {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
/**
|
|
71
|
+
* The pragmas every PPM connection to this database needs.
|
|
72
|
+
*
|
|
73
|
+
* `busy_timeout` is the one with a story. SQLite's default is to fail a
|
|
74
|
+
* contended lock INSTANTLY — measured, 0ms to `SQLiteError: database is
|
|
75
|
+
* locked` — and PPM routinely has two processes on this file for a moment: a
|
|
76
|
+
* `systemctl restart` starts the new supervisor while the outgoing server is
|
|
77
|
+
* still committing and checkpointing, which for a 300MB database is over a
|
|
78
|
+
* second. That single throw arrived as an unhandled rejection inside
|
|
79
|
+
* supervisor startup, which then stopped before spawning the edge forwarder —
|
|
80
|
+
* so PPM ran with nothing listening on its public port. 5s rather than the
|
|
81
|
+
* query audit's 2s: one snapshot of this database measures ~1.2s, so the wait
|
|
82
|
+
* has to cover a checkpoint plus whatever writes queued behind it.
|
|
83
|
+
*/
|
|
84
|
+
export function applyDbPragmas(database: Database): void {
|
|
85
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
86
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
87
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
88
|
+
}
|
|
89
|
+
|
|
71
90
|
/** Close the DB (for graceful shutdown or tests) */
|
|
72
91
|
export function closeDb(): void {
|
|
73
92
|
if (db) { db.close(); db = null; }
|
|
@@ -76,8 +95,7 @@ export function closeDb(): void {
|
|
|
76
95
|
/** For tests: open an isolated in-memory DB with schema applied */
|
|
77
96
|
export function openTestDb(): Database {
|
|
78
97
|
const testDb = new Database(":memory:");
|
|
79
|
-
testDb
|
|
80
|
-
testDb.exec("PRAGMA foreign_keys = ON");
|
|
98
|
+
applyDbPragmas(testDb);
|
|
81
99
|
runMigrations(testDb);
|
|
82
100
|
return testDb;
|
|
83
101
|
}
|
|
@@ -1131,10 +1149,33 @@ export function runMigrations(database: Database): void {
|
|
|
1131
1149
|
database.exec(`PRAGMA user_version = 48;`);
|
|
1132
1150
|
}
|
|
1133
1151
|
|
|
1152
|
+
// This checkout's prompt-cache work first shipped as migrations 46-48 on turn_usage, before
|
|
1153
|
+
// main used those numbers for accounts (it is 50-52 below now). A database that ran the local
|
|
1154
|
+
// ones sits past 45 without main's columns, so the version gates above skip them for good and
|
|
1155
|
+
// 49 then updates a column that does not exist. Added by schema rather than by version here;
|
|
1156
|
+
// on a database that followed main every statement is a no-op.
|
|
1157
|
+
if (current >= 46 && current < 49) {
|
|
1158
|
+
for (const col of [
|
|
1159
|
+
"granted_at INTEGER",
|
|
1160
|
+
"refresh_expires_at INTEGER",
|
|
1161
|
+
"reauth_required INTEGER NOT NULL DEFAULT 0",
|
|
1162
|
+
"last_refresh_attempt_at INTEGER",
|
|
1163
|
+
]) {
|
|
1164
|
+
try { database.exec(`ALTER TABLE accounts ADD COLUMN ${col}`); } catch { /* exists */ }
|
|
1165
|
+
}
|
|
1166
|
+
try { database.exec(`ALTER TABLE codex_accounts ADD COLUMN status TEXT NOT NULL DEFAULT 'active'`); } catch { /* exists */ }
|
|
1167
|
+
try { database.exec(`ALTER TABLE codex_accounts ADD COLUMN daily_guard_enabled INTEGER NOT NULL DEFAULT 0`); } catch { /* exists */ }
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1134
1170
|
if (current < 49) {
|
|
1135
1171
|
// Daily guard was introduced as an opt-in during development. Ship it enabled so every
|
|
1136
1172
|
// weekly-only account starts paced; Plus accounts simply do not surface or use it.
|
|
1137
|
-
|
|
1173
|
+
//
|
|
1174
|
+
// Guarded like every statement around it, and for the same reason: the ladder runs against
|
|
1175
|
+
// databases that do not have this table yet. Unguarded, the throw escapes `runMigrations`
|
|
1176
|
+
// and leaves the database half-migrated at whatever version the previous step wrote —
|
|
1177
|
+
// there is no transaction around the ladder to undo it.
|
|
1178
|
+
try { database.exec(`UPDATE codex_accounts SET daily_guard_enabled = 1`); } catch { /* no accounts table yet */ }
|
|
1138
1179
|
database.exec(`PRAGMA user_version = 49;`);
|
|
1139
1180
|
}
|
|
1140
1181
|
|
|
@@ -1155,6 +1196,33 @@ export function runMigrations(database: Database): void {
|
|
|
1155
1196
|
try { database.exec("ALTER TABLE claude_limit_snapshots ADD COLUMN extra_json TEXT"); } catch { /* exists */ }
|
|
1156
1197
|
database.exec(`PRAGMA user_version = 51;`);
|
|
1157
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
if (current < 52) {
|
|
1201
|
+
// The idle-cache notice had to live entirely in server memory, because nothing on disk
|
|
1202
|
+
// said when a session last cached or how much. `recorded_at` answers the first; this
|
|
1203
|
+
// column answers the second with a figure the summed token columns cannot give —
|
|
1204
|
+
// see `TurnUsage.contextTokens`. Null on every existing row: unmeasured, not zero.
|
|
1205
|
+
try { database.exec("ALTER TABLE turn_usage ADD COLUMN context_tokens INTEGER"); } catch { /* column exists */ }
|
|
1206
|
+
database.exec(`PRAGMA user_version = 52;`);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (current < 53) {
|
|
1210
|
+
// The cache window was inferred from the credential's shape — an `sk-ant-oat` prefix
|
|
1211
|
+
// meaning a subscription and therefore an hour. The API states it outright in
|
|
1212
|
+
// `usage.cache_creation`, and that answer has to outlive the process the way the rest of
|
|
1213
|
+
// the last turn's facts now do. Null on existing rows: unreported, so the guess stands.
|
|
1214
|
+
try { database.exec("ALTER TABLE turn_usage ADD COLUMN cache_ttl_ms INTEGER"); } catch { /* column exists */ }
|
|
1215
|
+
database.exec(`PRAGMA user_version = 53;`);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
if (current < 54) {
|
|
1219
|
+
// A compaction makes the cached prefix inapplicable rather than stale, and the window in
|
|
1220
|
+
// which that matters is between the compaction and the user's next message — a window a
|
|
1221
|
+
// restart lands inside often enough to be worth surviving. Null on existing rows: no
|
|
1222
|
+
// compaction is known, which is what every turn before this recorded anyway.
|
|
1223
|
+
try { database.exec("ALTER TABLE turn_usage ADD COLUMN compacted_at INTEGER"); } catch { /* column exists */ }
|
|
1224
|
+
database.exec(`PRAGMA user_version = 54;`);
|
|
1225
|
+
}
|
|
1158
1226
|
}
|
|
1159
1227
|
|
|
1160
1228
|
// ---------------------------------------------------------------------------
|
|
@@ -1729,6 +1797,9 @@ export interface TurnUsageRow {
|
|
|
1729
1797
|
cold_reason: string | null;
|
|
1730
1798
|
account_id: string | null;
|
|
1731
1799
|
account_label: string | null;
|
|
1800
|
+
context_tokens: number | null;
|
|
1801
|
+
cache_ttl_ms: number | null;
|
|
1802
|
+
compacted_at: number | null;
|
|
1732
1803
|
recorded_at: string;
|
|
1733
1804
|
}
|
|
1734
1805
|
|
|
@@ -1745,13 +1816,16 @@ export function insertTurnUsage(record: {
|
|
|
1745
1816
|
coldReason?: string;
|
|
1746
1817
|
accountId?: string;
|
|
1747
1818
|
accountLabel?: string;
|
|
1819
|
+
contextTokens?: number;
|
|
1820
|
+
cacheTtlMs?: number;
|
|
1821
|
+
compactedAt?: number;
|
|
1748
1822
|
}): void {
|
|
1749
1823
|
getDb().query(
|
|
1750
1824
|
`INSERT INTO turn_usage
|
|
1751
1825
|
(session_id, model, input_tokens, output_tokens, cache_read_tokens,
|
|
1752
1826
|
cache_write_tokens, context_window, cost_usd, cold_start, cold_reason,
|
|
1753
|
-
account_id, account_label)
|
|
1754
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1827
|
+
account_id, account_label, context_tokens, cache_ttl_ms, compacted_at)
|
|
1828
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1755
1829
|
).run(
|
|
1756
1830
|
record.sessionId, record.model ?? null,
|
|
1757
1831
|
record.inputTokens, record.outputTokens,
|
|
@@ -1759,9 +1833,50 @@ export function insertTurnUsage(record: {
|
|
|
1759
1833
|
record.contextWindow ?? null, record.costUsd ?? null,
|
|
1760
1834
|
record.coldStart ? 1 : 0, record.coldReason ?? null,
|
|
1761
1835
|
record.accountId ?? null, record.accountLabel ?? null,
|
|
1836
|
+
record.contextTokens ?? null, record.cacheTtlMs ?? null, record.compactedAt ?? null,
|
|
1762
1837
|
);
|
|
1763
1838
|
}
|
|
1764
1839
|
|
|
1840
|
+
/**
|
|
1841
|
+
* When this session last wrote its prompt cache, and what it would cost to write again.
|
|
1842
|
+
*
|
|
1843
|
+
* The idle notice used to be answerable only from the live `SessionEntry`, which is dropped
|
|
1844
|
+
* five minutes after the last tab disconnects — while the cache it describes lives for an
|
|
1845
|
+
* hour. So the notice appeared only for a session left connected and went silent for exactly
|
|
1846
|
+
* the session reopened the next morning, which is the one it exists for. These three columns
|
|
1847
|
+
* are already written on every turn; reading them back is the whole fix.
|
|
1848
|
+
*/
|
|
1849
|
+
export function getLastTurnCacheState(sessionId: string): {
|
|
1850
|
+
endedAtMs: number;
|
|
1851
|
+
prefixTokens: number;
|
|
1852
|
+
contextTokens?: number;
|
|
1853
|
+
cacheTtlMs?: number;
|
|
1854
|
+
compactedAt?: number;
|
|
1855
|
+
} | null {
|
|
1856
|
+
const row = getDb().query(
|
|
1857
|
+
`SELECT input_tokens, cache_read_tokens, cache_write_tokens, context_tokens, cache_ttl_ms, compacted_at, recorded_at
|
|
1858
|
+
FROM turn_usage WHERE session_id = ? ORDER BY id DESC LIMIT 1`,
|
|
1859
|
+
).get(sessionId) as Pick<
|
|
1860
|
+
TurnUsageRow,
|
|
1861
|
+
"input_tokens" | "cache_read_tokens" | "cache_write_tokens" | "context_tokens" | "cache_ttl_ms" | "compacted_at" | "recorded_at"
|
|
1862
|
+
> | null;
|
|
1863
|
+
if (!row?.recorded_at) return null;
|
|
1864
|
+
|
|
1865
|
+
// `datetime('now')` is UTC and writes no zone suffix, which `Date` then reads as LOCAL
|
|
1866
|
+
// time — seven hours adrift in Asia/Saigon, enough to make the idle window come out
|
|
1867
|
+
// negative and silence the notice on precisely the machines it was rebuilt for.
|
|
1868
|
+
const endedAtMs = Date.parse(`${row.recorded_at.replace(" ", "T")}Z`);
|
|
1869
|
+
if (!Number.isFinite(endedAtMs)) return null;
|
|
1870
|
+
|
|
1871
|
+
return {
|
|
1872
|
+
endedAtMs,
|
|
1873
|
+
prefixTokens: row.input_tokens + row.cache_read_tokens + row.cache_write_tokens,
|
|
1874
|
+
...(row.context_tokens != null && { contextTokens: row.context_tokens }),
|
|
1875
|
+
...(row.cache_ttl_ms != null && { cacheTtlMs: row.cache_ttl_ms }),
|
|
1876
|
+
...(row.compacted_at != null && { compactedAt: row.compacted_at }),
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1765
1880
|
/** Most recent turns first — the debug dialog shows the tail of a session. */
|
|
1766
1881
|
export function listTurnUsage(sessionId: string, limit = 30): TurnUsageRow[] {
|
|
1767
1882
|
return getDb().query(
|
|
@@ -47,28 +47,40 @@ function osaLiteral(value: string): string {
|
|
|
47
47
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* How a backend binary is found, replaceable for the same reason `TrashRunner` is.
|
|
52
|
+
*
|
|
53
|
+
* Which tool is present is as much a property of the host as what running it does, and
|
|
54
|
+
* leaving the lookup outside the seam meant a test could fake the run and still be refused
|
|
55
|
+
* before reaching it — on any machine without `gio` or `trash-put`, which includes the
|
|
56
|
+
* container the suite runs in.
|
|
57
|
+
*/
|
|
58
|
+
export type TrashLookup = (name: string) => string | null;
|
|
59
|
+
|
|
60
|
+
const defaultLookup: TrashLookup = (name) => Bun.which(name);
|
|
61
|
+
|
|
62
|
+
function windowsCommand(path: string, isDirectory: boolean, which: TrashLookup): string[] {
|
|
51
63
|
const method = isDirectory ? "DeleteDirectory" : "DeleteFile";
|
|
52
64
|
const script =
|
|
53
65
|
"Add-Type -AssemblyName Microsoft.VisualBasic; " +
|
|
54
66
|
`[Microsoft.VisualBasic.FileIO.FileSystem]::${method}(${psLiteral(path)},'OnlyErrorDialogs','SendToRecycleBin')`;
|
|
55
|
-
const shell =
|
|
67
|
+
const shell = which("powershell") ?? which("pwsh");
|
|
56
68
|
if (!shell) throw noTrash("powershell not found on PATH");
|
|
57
69
|
return [shell, "-NoProfile", "-NonInteractive", "-Command", script];
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
/** Paths are always absolute here, so no leading-dash option confusion. */
|
|
61
|
-
function posixCommand(path: string): string[] {
|
|
73
|
+
function posixCommand(path: string, which: TrashLookup): string[] {
|
|
62
74
|
if (process.platform === "darwin") {
|
|
63
|
-
const trash =
|
|
75
|
+
const trash = which("trash");
|
|
64
76
|
if (trash) return [trash, path];
|
|
65
|
-
const osascript =
|
|
77
|
+
const osascript = which("osascript");
|
|
66
78
|
if (!osascript) throw noTrash("neither trash nor osascript found on PATH");
|
|
67
79
|
return [osascript, "-e", `tell application "Finder" to delete POSIX file ${osaLiteral(path)}`];
|
|
68
80
|
}
|
|
69
|
-
const gio =
|
|
81
|
+
const gio = which("gio");
|
|
70
82
|
if (gio) return [gio, "trash", path];
|
|
71
|
-
const trashPut =
|
|
83
|
+
const trashPut = which("trash-put");
|
|
72
84
|
if (trashPut) return [trashPut, path];
|
|
73
85
|
throw noTrash("neither gio nor trash-put found on PATH");
|
|
74
86
|
}
|
|
@@ -85,7 +97,7 @@ async function isDirectoryTarget(path: string): Promise<boolean> {
|
|
|
85
97
|
|
|
86
98
|
export async function trashPath(
|
|
87
99
|
path: string,
|
|
88
|
-
options?: { run?: TrashRunner },
|
|
100
|
+
options?: { run?: TrashRunner; which?: TrashLookup },
|
|
89
101
|
): Promise<{ trashed: true; path: string }> {
|
|
90
102
|
const target = resolvePath(path);
|
|
91
103
|
assertAllowed(target);
|
|
@@ -94,8 +106,9 @@ export async function trashPath(
|
|
|
94
106
|
await assertNotProtected(target);
|
|
95
107
|
const isDir = await isDirectoryTarget(target);
|
|
96
108
|
|
|
109
|
+
const which = options?.which ?? defaultLookup;
|
|
97
110
|
const cmd =
|
|
98
|
-
process.platform === "win32" ? windowsCommand(target, isDir) : posixCommand(target);
|
|
111
|
+
process.platform === "win32" ? windowsCommand(target, isDir, which) : posixCommand(target, which);
|
|
99
112
|
|
|
100
113
|
const run = options?.run ?? spawnRunner;
|
|
101
114
|
let result: { exitCode: number; stderr: string };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every checkout target — local branches, remote-tracking branches and tags —
|
|
3
|
+
* out of a single `git for-each-ref`, with the commit each one points at.
|
|
4
|
+
*
|
|
5
|
+
* The format string is built from the field list rather than written out,
|
|
6
|
+
* because the parser reads fields *positionally*: a field added to one and not
|
|
7
|
+
* the other shifts everything after it, and git answers just as happily either
|
|
8
|
+
* way — the picker would simply show a date where the author belongs, with
|
|
9
|
+
* nothing failing.
|
|
10
|
+
*
|
|
11
|
+
* Six of the twelve fields are the `*`-dereferenced twins of six others, and
|
|
12
|
+
* they are what makes an **annotated** tag readable. `refs/tags/v1` resolves to
|
|
13
|
+
* the tag *object*, not to a commit: its `objectname` is the tag's own hash,
|
|
14
|
+
* it has a tagger rather than an author (so `%(authorname)` and
|
|
15
|
+
* `%(committerdate)` come back empty) and its `contents:subject` is the tag
|
|
16
|
+
* message. A lightweight tag has no such object and leaves every `*` field
|
|
17
|
+
* empty — which is why each pair is resolved as "dereferenced when non-empty,
|
|
18
|
+
* else plain" rather than by asking what kind of ref it is.
|
|
19
|
+
*/
|
|
20
|
+
import type { GitRef } from "../../types/git.ts";
|
|
21
|
+
|
|
22
|
+
const FIELDS = [
|
|
23
|
+
"refname",
|
|
24
|
+
"objectname",
|
|
25
|
+
"*objectname",
|
|
26
|
+
"HEAD",
|
|
27
|
+
"upstream:short",
|
|
28
|
+
"upstream:track",
|
|
29
|
+
"authorname",
|
|
30
|
+
"*authorname",
|
|
31
|
+
"committerdate:iso-strict",
|
|
32
|
+
"*committerdate:iso-strict",
|
|
33
|
+
"contents:subject",
|
|
34
|
+
"*contents:subject",
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
/** NUL between fields, newline between records: no ref name or subject may contain either. */
|
|
38
|
+
export const FOR_EACH_REF_FORMAT = FIELDS.map((f) => `%(${f})`).join("%00");
|
|
39
|
+
|
|
40
|
+
export const FOR_EACH_REF_ARGS = [
|
|
41
|
+
"for-each-ref",
|
|
42
|
+
`--format=${FOR_EACH_REF_FORMAT}`,
|
|
43
|
+
"refs/heads",
|
|
44
|
+
"refs/remotes",
|
|
45
|
+
"refs/tags",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const PREFIX: Record<GitRef["type"], string> = {
|
|
49
|
+
branch: "refs/heads/",
|
|
50
|
+
remote: "refs/remotes/",
|
|
51
|
+
tag: "refs/tags/",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* `%(upstream:track)` as git writes it: `[ahead 1, behind 26]`, either half on
|
|
56
|
+
* its own, `[gone]` when the remote branch was deleted, or empty when the
|
|
57
|
+
* branch has no upstream at all.
|
|
58
|
+
*/
|
|
59
|
+
export function parseUpstreamTrack(track: string): Pick<GitRef, "ahead" | "behind" | "gone"> {
|
|
60
|
+
if (track.includes("gone")) return { ahead: 0, behind: 0, gone: true };
|
|
61
|
+
const ahead = /ahead (\d+)/.exec(track);
|
|
62
|
+
const behind = /behind (\d+)/.exec(track);
|
|
63
|
+
return {
|
|
64
|
+
ahead: ahead ? Number(ahead[1]) : 0,
|
|
65
|
+
behind: behind ? Number(behind[1]) : 0,
|
|
66
|
+
gone: false,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function refType(refName: string): GitRef["type"] | null {
|
|
71
|
+
for (const [type, prefix] of Object.entries(PREFIX)) {
|
|
72
|
+
if (refName.startsWith(prefix)) return type as GitRef["type"];
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Newest commit first. Compared as instants, never as strings: `iso-strict` carries the author's own offset. */
|
|
78
|
+
function newestFirst(a: GitRef, b: GitRef): number {
|
|
79
|
+
const at = Date.parse(a.date);
|
|
80
|
+
const bt = Date.parse(b.date);
|
|
81
|
+
if (Number.isNaN(at) || Number.isNaN(bt)) return a.name.localeCompare(b.name);
|
|
82
|
+
return bt - at;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function parseForEachRef(stdout: string): GitRef[] {
|
|
86
|
+
const refs: GitRef[] = [];
|
|
87
|
+
|
|
88
|
+
for (const line of stdout.split("\n")) {
|
|
89
|
+
if (!line) continue;
|
|
90
|
+
const f = line.split("\0");
|
|
91
|
+
// A short record means a field carried a newline after all. Skipping it
|
|
92
|
+
// loses one row; reading it would silently shift every field of that row.
|
|
93
|
+
if (f.length < FIELDS.length) continue;
|
|
94
|
+
|
|
95
|
+
const refName = f[0]!;
|
|
96
|
+
const type = refType(refName);
|
|
97
|
+
if (!type) continue;
|
|
98
|
+
// `origin/HEAD` is a symbolic pointer at another row of this same list.
|
|
99
|
+
if (type === "remote" && refName.endsWith("/HEAD")) continue;
|
|
100
|
+
|
|
101
|
+
const hash = f[2] || f[1]!;
|
|
102
|
+
refs.push({
|
|
103
|
+
refName,
|
|
104
|
+
name: refName.slice(PREFIX[type].length),
|
|
105
|
+
type,
|
|
106
|
+
current: f[3] === "*",
|
|
107
|
+
hash,
|
|
108
|
+
shortHash: hash.slice(0, 7),
|
|
109
|
+
subject: f[11] || f[10]!,
|
|
110
|
+
author: f[7] || f[6]!,
|
|
111
|
+
date: f[9] || f[8]!,
|
|
112
|
+
upstream: f[4] || null,
|
|
113
|
+
...parseUpstreamTrack(f[5]!),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return refs.sort(newestFirst);
|
|
118
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import simpleGit, { type SimpleGit } from "simple-git";
|
|
3
3
|
import { isBinaryContent } from "./binary-content.ts";
|
|
4
|
+
import { FOR_EACH_REF_ARGS, parseForEachRef } from "./git-refs/for-each-ref.ts";
|
|
4
5
|
import type {
|
|
6
|
+
CheckoutMode,
|
|
5
7
|
FileFullDiff,
|
|
6
8
|
GitStatus,
|
|
7
9
|
GitFileChange,
|
|
8
10
|
GitCommit,
|
|
9
11
|
GitBranch,
|
|
10
12
|
GitGraphData,
|
|
13
|
+
GitRef,
|
|
11
14
|
GitWorktree,
|
|
12
15
|
} from "../types/git.ts";
|
|
13
16
|
|
|
@@ -319,8 +322,28 @@ class GitService {
|
|
|
319
322
|
await this.git(projectPath).checkoutBranch(name, from ?? "HEAD");
|
|
320
323
|
}
|
|
321
324
|
|
|
322
|
-
|
|
323
|
-
|
|
325
|
+
/**
|
|
326
|
+
* `track` is not a nicety: `git checkout origin/foo` lands on a **detached
|
|
327
|
+
* HEAD**, because the remote-tracking ref is not a branch anyone can commit
|
|
328
|
+
* to. `-t` is what creates the local `foo` that follows it. The picker
|
|
329
|
+
* chooses between the three modes; the service only has to keep them apart.
|
|
330
|
+
*/
|
|
331
|
+
async checkout(
|
|
332
|
+
projectPath: string,
|
|
333
|
+
ref: string,
|
|
334
|
+
mode: CheckoutMode = "checkout",
|
|
335
|
+
): Promise<void> {
|
|
336
|
+
const flags = mode === "detach" ? ["--detach"] : mode === "track" ? ["-t"] : [];
|
|
337
|
+
// `--` or the ref is read as a pathspec when no such ref exists: `git checkout src`
|
|
338
|
+
// in a repository with no branch named `src` restores every unstaged change under
|
|
339
|
+
// that directory, silently and with exit 0. `assertSafeRev` refuses a leading dash
|
|
340
|
+
// and a range, but a plain directory name is a legitimate ref shape.
|
|
341
|
+
await this.git(projectPath).checkout([...flags, ref, "--"]);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Every local branch, remote-tracking branch and tag, newest commit first. */
|
|
345
|
+
async refs(projectPath: string): Promise<GitRef[]> {
|
|
346
|
+
return parseForEachRef(await this.git(projectPath).raw(FOR_EACH_REF_ARGS));
|
|
324
347
|
}
|
|
325
348
|
|
|
326
349
|
async deleteBranch(
|
|
@@ -13,10 +13,37 @@ export type Runner = (argv: string[], timeoutMs?: number) => Promise<RunResult>;
|
|
|
13
13
|
|
|
14
14
|
const DEFAULT_TIMEOUT_MS = 5000;
|
|
15
15
|
|
|
16
|
-
/** Real implementation: argv array only (never string-interpolated into a shell), bounded by timeoutMs.
|
|
16
|
+
/** Real implementation: argv array only (never string-interpolated into a shell), bounded by timeoutMs.
|
|
17
|
+
*
|
|
18
|
+
* `Bun.spawn` **throws synchronously** when the binary is not on `PATH`
|
|
19
|
+
* ("Executable not found in $PATH"), and for these providers that is a normal
|
|
20
|
+
* condition rather than an error: every one of them shells out to a per-OS tool
|
|
21
|
+
* (`systemctl`, `plutil`, `powershell.exe`, `findmnt`, `xdg-user-dir`,
|
|
22
|
+
* `nvidia-smi`), so on any other OS the binary is simply absent. Left to throw,
|
|
23
|
+
* it escapes the `Promise<RunResult>` this signature promises: the Services page
|
|
24
|
+
* on macOS and Windows answered 500 and showed the exception, instead of the
|
|
25
|
+
* "this host has no service manager" the collector is written to report.
|
|
26
|
+
*
|
|
27
|
+
* So a missing binary is returned as a run that failed — `code: null` with the
|
|
28
|
+
* message on stderr, which is the shape every caller already handles for a
|
|
29
|
+
* non-zero exit. */
|
|
30
|
+
const spawnPiped = (argv: string[]) =>
|
|
31
|
+
Bun.spawn(argv, { stdout: "pipe", stderr: "pipe", stdin: "ignore", windowsHide: true });
|
|
32
|
+
|
|
17
33
|
export const defaultRunner: Runner = async (argv, timeoutMs = DEFAULT_TIMEOUT_MS) => {
|
|
18
|
-
|
|
34
|
+
// Named via `spawnPiped` rather than `ReturnType<typeof Bun.spawn>`: the bare
|
|
35
|
+
// form loses the option narrowing, and `proc.stdout` widens back to a union
|
|
36
|
+
// with `number` (a raw fd) that `new Response()` will not take.
|
|
37
|
+
let proc: ReturnType<typeof spawnPiped>;
|
|
38
|
+
try {
|
|
39
|
+
proc = spawnPiped(argv);
|
|
40
|
+
} catch (e: any) {
|
|
41
|
+
return { stdout: "", stderr: e?.message ?? String(e), code: null, timedOut: false };
|
|
42
|
+
}
|
|
19
43
|
let timedOut = false;
|
|
44
|
+
/** Resolves when the timeout fires, so the readers below stop waiting with the process. */
|
|
45
|
+
let giveUp!: () => void;
|
|
46
|
+
const abandoned = new Promise<void>((resolve) => { giveUp = resolve; });
|
|
20
47
|
const killTimer = setTimeout(() => {
|
|
21
48
|
timedOut = true;
|
|
22
49
|
try {
|
|
@@ -24,12 +51,29 @@ export const defaultRunner: Runner = async (argv, timeoutMs = DEFAULT_TIMEOUT_MS
|
|
|
24
51
|
} catch {
|
|
25
52
|
// Process already exited between the timer firing and the kill call.
|
|
26
53
|
}
|
|
54
|
+
giveUp();
|
|
27
55
|
}, timeoutMs);
|
|
28
56
|
|
|
57
|
+
/**
|
|
58
|
+
* The output, or nothing once the run has been given up on.
|
|
59
|
+
*
|
|
60
|
+
* Killing the process does **not** close its pipes. A shell that forks rather than execs
|
|
61
|
+
* leaves a grandchild holding the write end, so draining stdout goes on until *that* exits —
|
|
62
|
+
* measured, `proc.exited` came back at the 150ms timeout while the drain ran the full five
|
|
63
|
+
* seconds, which means `timeoutMs` bounded nothing at all. The pending read is left to settle
|
|
64
|
+
* on its own and discarded rather than cancelled: cancelling a subprocess stream has crashed
|
|
65
|
+
* Bun on Windows before, and there is nothing here worth that risk.
|
|
66
|
+
*/
|
|
67
|
+
const readOrAbandon = (stream: ReadableStream<Uint8Array>): Promise<string> =>
|
|
68
|
+
Promise.race([
|
|
69
|
+
new Response(stream).text().catch(() => ""),
|
|
70
|
+
abandoned.then(() => ""),
|
|
71
|
+
]);
|
|
72
|
+
|
|
29
73
|
try {
|
|
30
74
|
const [stdout, stderr, code] = await Promise.all([
|
|
31
|
-
|
|
32
|
-
|
|
75
|
+
readOrAbandon(proc.stdout),
|
|
76
|
+
readOrAbandon(proc.stderr),
|
|
33
77
|
proc.exited,
|
|
34
78
|
]);
|
|
35
79
|
return { stdout, stderr, code, timedOut };
|