@hienlh/ppm 0.20.10 → 0.21.0
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 +108 -0
- package/CLAUDE.md +42 -1
- package/LICENSE +5 -0
- package/THIRD-PARTY-NOTICES.md +83 -0
- 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 +1 -1
- package/bun.lock +38 -7
- package/dist/web/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
- package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +1 -0
- package/dist/web/assets/ai-resource-editor-F09pf8Hd.js +1 -0
- package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +1 -0
- package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +1 -0
- package/dist/web/assets/{angular-html-D4qjt-75.js → angular-html-DkR4iCth.js} +1 -1
- package/dist/web/assets/{angular-ts-DvLn04T8.js → angular-ts-DcyL4Chx.js} +1 -1
- package/dist/web/assets/api-client-BUCo1lms.js +1 -0
- package/dist/web/assets/{api-client-DCzgxFNY.js → api-client-bfB7cXRM.js} +1 -1
- package/dist/web/assets/{api-settings-t5xLrBkQ.js → api-settings-BcNjZIvG.js} +1 -1
- package/dist/web/assets/{api-themes-CJmyQeMk.js → api-themes-DBfyVUtX.js} +1 -1
- package/dist/web/assets/{apl-DQOoPzqd.js → apl-BVlrVtjt.js} +1 -1
- package/dist/web/assets/appearance-settings-section-whgnq3ss.js +1 -0
- package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +1 -0
- package/dist/web/assets/{astro-C2zFJ1Er.js → astro-MjJlozLj.js} +1 -1
- package/dist/web/assets/audio-preview-Bc54GCAV.js +1 -0
- package/dist/web/assets/{blade-CBD8sEa1.js → blade-DnJWi1fz.js} +1 -1
- package/dist/web/assets/{bsl-bs2JpeTQ.js → bsl-DCmJxSIz.js} +1 -1
- package/dist/web/assets/{button-DHI8IY_N.js → button-VW4QGKCn.js} +1 -1
- package/dist/web/assets/{cairo-DT2g4i_6.js → cairo-C8dDenub.js} +1 -1
- package/dist/web/assets/chat-tab-Cr6_KSGO.js +15 -0
- package/dist/web/assets/{cobol-B2rp6LA1.js → cobol-DPx_nifl.js} +1 -1
- package/dist/web/assets/code-editor-DfmlGamv.js +7 -0
- package/dist/web/assets/codex-daily-guard-CfybHv_c.js +1 -0
- package/dist/web/assets/{coffee-D5ShQC08.js → coffee-DvFzqhgN.js} +1 -1
- package/dist/web/assets/conflict-editor-DenQUOT2.js +19 -0
- package/dist/web/assets/context-menu-DOODKZdF.js +1 -0
- package/dist/web/assets/{cpp-BmKOH-E0.js → cpp-BLJN0umi.js} +1 -1
- package/dist/web/assets/{createLucideIcon-CLMdC3Kl.js → createLucideIcon-DoiEKeJ_.js} +1 -1
- package/dist/web/assets/{crystal-B7u_ZTpu.js → crystal-BXR-9fGw.js} +1 -1
- package/dist/web/assets/csv-preview-CClrJOuR.js +2 -0
- package/dist/web/assets/{data-grid-overlay-editor-DJWgclot.js → data-grid-overlay-editor-D4fhcMfT.js} +1 -1
- package/dist/web/assets/{data-grid-types-Uu77aFvI.js → data-grid-types-OgUxMLdQ.js} +1 -1
- package/dist/web/assets/database-viewer-DItjm8wB.js +1 -0
- package/dist/web/assets/dialog-DQTn55Mt.js +1 -0
- package/dist/web/assets/diff-viewer-BpNj7pN9.js +4 -0
- package/dist/web/assets/{dist-BTdRgQig.js → dist-CrBOCtCW.js} +1 -1
- package/dist/web/assets/docx-preview-DYBkiUaE.js +12 -0
- package/dist/web/assets/{edge--MpXROh5.js → edge-rWOlCmXT.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-ESj6hMoD.js → edit-diff-preview-COtmo54G.js} +3 -3
- package/dist/web/assets/editor-font-BDrdiGf1.js +1 -0
- package/dist/web/assets/editor-language-service-DxkZ51E5.js +1 -0
- package/dist/web/assets/{elixir-DL8mOfzH.js → elixir-E7Y82okW.js} +1 -1
- package/dist/web/assets/{elm-eVlzIFEZ.js → elm-DTPGbc-w.js} +1 -1
- package/dist/web/assets/{erb-BgvaH9l0.js → erb-DHqLHrE6.js} +1 -1
- package/dist/web/assets/{erlang-CEf8Sfhs.js → erlang-Byg0nNiV.js} +1 -1
- package/dist/web/assets/{esm-C5xqy163.js → esm-D_DmOM7n.js} +2 -2
- package/dist/web/assets/explorer-body-D-5z464e.js +2 -0
- package/dist/web/assets/explorer-window-content-7-1wuRul.js +1 -0
- package/dist/web/assets/extension-manager-section-BhwB6nqr.js +1 -0
- package/dist/web/assets/extension-webview-DI4VL2tJ.js +12 -0
- package/dist/web/assets/file-icons-EzPL0q-k.js +1 -0
- package/dist/web/assets/file-icons.generated-CfaJuvZu.css +1 -0
- package/dist/web/assets/file-store-DMABxOkV.js +1 -0
- package/dist/web/assets/files-settings-section-CZzABuKz.js +1 -0
- package/dist/web/assets/format-file-meta-DOpzJJhS.js +1 -0
- package/dist/web/assets/{fortran-fixed-form-yAUsSjab.js → fortran-fixed-form-CY1n0Xf6.js} +1 -1
- package/dist/web/assets/{fsharp-BEWvY_ZR.js → fsharp-_vEDSCJz.js} +1 -1
- package/dist/web/assets/{gdresource-CYZFZ057.js → gdresource-B6XT0wAC.js} +1 -1
- package/dist/web/assets/geist-cyrillic-ext-wght-italic-BYAq_K0C.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-italic-Da0gXVk3.woff2 +0 -0
- package/dist/web/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-italic-XgKbg5n7.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-italic-dTnRTFFJ.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-italic-0fly6zkB.woff2 +0 -0
- package/dist/web/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/web/assets/general-settings-section-BcxwiZ0X.js +1 -0
- package/dist/web/assets/{git-commit-DedchUJ6.js → git-commit-Wk9ovq59.js} +1 -1
- package/dist/web/assets/git-log-panel-CRN4JVOx.js +1 -0
- package/dist/web/assets/{git-rebase-DPvHNzQr.js → git-rebase-DSarGPvK.js} +1 -1
- package/dist/web/assets/git-repo-store-Dy69YCHN.js +1 -0
- package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +1 -0
- package/dist/web/assets/glide-data-grid-BvgAqSUr.js +141 -0
- package/dist/web/assets/{glimmer-js-B4hPYBMX.js → glimmer-js-CkYI35_P.js} +1 -1
- package/dist/web/assets/{glimmer-ts-C0WfcQnO.js → glimmer-ts-BqmIZcds.js} +1 -1
- package/dist/web/assets/{glsl-DHW88hTv.js → glsl-Be-yBCF5.js} +1 -1
- package/dist/web/assets/{graphql-CEvXbo6e.js → graphql-a5DrENHY.js} +1 -1
- package/dist/web/assets/group-chat-tab-DFa5mAx5.js +1 -0
- package/dist/web/assets/{hack-lKfhn1vw.js → hack-BLAg20aD.js} +1 -1
- package/dist/web/assets/{haml-AjKlANaB.js → haml-Dj6bGOQH.js} +1 -1
- package/dist/web/assets/{handlebars-B8fZzj2G.js → handlebars-dmEa36Oh.js} +1 -1
- package/dist/web/assets/{html-derivative-VZmoMdQD.js → html-derivative-lLi3-RNT.js} +1 -1
- package/dist/web/assets/{html-H_S3FJMj.js → html-yacZYzSE.js} +1 -1
- package/dist/web/assets/{http-DBxROFKc.js → http-1KaxcE4e.js} +1 -1
- package/dist/web/assets/{hurl-Hf7XWQHN.js → hurl-ST-dEEZR.js} +1 -1
- package/dist/web/assets/{hxml-BRhRKwSo.js → hxml-CpDtuq__.js} +1 -1
- package/dist/web/assets/icons-DQjqk0lJ.js +1 -0
- package/dist/web/assets/image-preview-CJVxL0uH.js +1 -0
- package/dist/web/assets/index-D7DbXD3G.css +1 -0
- package/dist/web/assets/index-ZtfxZOyb.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +1 -0
- package/dist/web/assets/input-D-dR8RI6.js +1 -0
- package/dist/web/assets/{jinja-D3jMH0jG.js → jinja-Cjw-evAn.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-DZvWeUAW.js → jira-watcher-section-DIC1yIV3.js} +1 -1
- package/dist/web/assets/{jison-CZtpkLiY.js → jison-DfATMYoY.js} +1 -1
- package/dist/web/assets/{julia-DwFjOpos.js → julia-DDCaxiqT.js} +1 -1
- package/dist/web/assets/{just-BEfj8dHN.js → just-CJL529u6.js} +1 -1
- package/dist/web/assets/keybindings-store-tVfhPANS.js +1 -0
- package/dist/web/assets/keyboard-shortcuts-section-BW03P3AF.js +1 -0
- package/dist/web/assets/{latex-C6Rb_zGG.js → latex-Zc4EpLpW.js} +1 -1
- package/dist/web/assets/{lib-CFTK00ux.js → lib-BVwmtv2i.js} +1 -1
- package/dist/web/assets/{liquid-DTVs9cD3.js → liquid-B_6KMNYS.js} +1 -1
- package/dist/web/assets/{lua-C1zfiqhG.js → lua-BtS2idK7.js} +1 -1
- package/dist/web/assets/markdown-renderer-CvL1pWeK.js +3 -0
- package/dist/web/assets/markdown-renderer-sJNNVhlS.js +1 -0
- package/dist/web/assets/{marko-_hrXozl2.js → marko-Wk6LBnck.js} +1 -1
- package/dist/web/assets/{mdc-AX9XWu8K.js → mdc-cRaXBrzL.js} +1 -1
- package/dist/web/assets/{media-url-D43IHXnP.js → media-url-DL8OCGGg.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-GSto0_0m.js → mobile-bottom-sheet-CoUauvLF.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +1 -0
- package/dist/web/assets/monaco-dotenv-language-BDYnKcnr.js +1 -0
- package/dist/web/assets/monaspace-argon-latin-400-italic-DHf095HZ.woff2 +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-italic-DZMiIHEH.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-normal-DZUN4ACH.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-400-normal-RS-2TGNg.woff2 +0 -0
- package/dist/web/assets/monaspace-argon-latin-700-normal-4y--rLW1.woff +0 -0
- package/dist/web/assets/monaspace-argon-latin-700-normal-D9kWRRK5.woff2 +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-italic-BoBCEexI.woff2 +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-italic-LWEicbvC.woff +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-normal-D9XlxC9R.woff +0 -0
- package/dist/web/assets/monaspace-krypton-latin-400-normal-DsuVXUab.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-codicons-DSvMY_FK.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-devicons-BBab8P54.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-BzNW1pRA.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-ext-D30e--UY.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-awesome-legacy-D_qekXx9.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-font-logos-gdu3INfy.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-iec-power-Cw6TNeDf.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-material-l-CXi9Ay.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-misc-BaVfdyeB.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-octicons-CJLTTzKm.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-pomicons-D7T3Ya3H.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-powerline-Cgg0sdGS.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-seti-DYiUFq6z.woff2 +0 -0
- package/dist/web/assets/nerd-symbols-weather-D4Jrp5XD.woff2 +0 -0
- package/dist/web/assets/{nextflow-xJ5dkaQ2.js → nextflow-Bos2wvDV.js} +1 -1
- package/dist/web/assets/{nginx-BfbjvmIV.js → nginx-DMlOwzdr.js} +1 -1
- package/dist/web/assets/{nim-KrUnNSwZ.js → nim-D2dZQA88.js} +1 -1
- package/dist/web/assets/notification-store-BZbsPnW0.js +1 -0
- package/dist/web/assets/notifications-settings-section-DPUROMw-.js +1 -0
- package/dist/web/assets/{number-overlay-editor-CGZ1ObrN.js → number-overlay-editor-DZ2i_Bnb.js} +1 -1
- package/dist/web/assets/{overview-panel-Ni1Wqr3E.js → overview-panel-R146OIom.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +1 -0
- package/dist/web/assets/panel-store-BVLBRn6M.js +1 -0
- package/dist/web/assets/pdf-preview-BWUPGLp6.js +1 -0
- package/dist/web/assets/{perl-DAexWIeo.js → perl-C5-Z6fPn.js} +1 -1
- package/dist/web/assets/{php-DXc7GdU9.js → php-BXNNhkMH.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +1 -0
- package/dist/web/assets/portal-container-context-BarKmT4S.js +1 -0
- package/dist/web/assets/{postgres-viewer-DmbVhmJ8.js → postgres-viewer-CjOvaruz.js} +3 -3
- package/dist/web/assets/ppmbot-settings-section-DrvWh8l8.js +1 -0
- package/dist/web/assets/problems-panel-B7BWVhdy.js +1 -0
- package/dist/web/assets/process-table-BRMS1MrX.js +1 -0
- package/dist/web/assets/{project-store-DCDLF-x3.js → project-store-BhMbVln3.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Bv0Ny-Yy.js +1 -0
- package/dist/web/assets/{pug-CHds8FDl.js → pug-C1zf1yWy.js} +1 -1
- package/dist/web/assets/{qml-DBt6oNnU.js → qml-OW58jGpD.js} +1 -1
- package/dist/web/assets/query-audit-section-BcHJZcVC.js +1 -0
- package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +1 -0
- package/dist/web/assets/{razor-DVDe2z8Z.js → razor-DzjSaobM.js} +1 -1
- package/dist/web/assets/{react-Bf5xrFLG.js → react-Bghu0NUO.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BTzss2wX.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +1 -0
- package/dist/web/assets/{rst-s3rP0H8p.js → rst-RfuihOUW.js} +1 -1
- package/dist/web/assets/{ruby-sRcfbjlr.js → ruby-CNO3pli1.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Yt_lq4T9.js → run-in-terminal-Cb_hVU8x.js} +1 -1
- package/dist/web/assets/{sas-CuyZeQJw.js → sas-DZY5BtXx.js} +1 -1
- package/dist/web/assets/schedules-settings-section-DO8xAOVj.js +1 -0
- package/dist/web/assets/scroll-area-LtNV8g5o.js +1 -0
- package/dist/web/assets/{scss-B9m4FneT.js → scss--4g-REcN.js} +1 -1
- package/dist/web/assets/{send-to-chat-01eMNs3j.js → send-to-chat-Cnpru1M0.js} +1 -1
- package/dist/web/assets/separator-Cds1yxy8.js +1 -0
- package/dist/web/assets/settings-body-Rnu-FcX-.js +2 -0
- package/dist/web/assets/settings-store-DZPgPtEA.js +2 -0
- package/dist/web/assets/settings-tab-C6PHxs0Z.js +1 -0
- package/dist/web/assets/settings-window-content-CQ4gT2qj.js +1 -0
- package/dist/web/assets/{shaderlab-BuCltaXx.js → shaderlab-Cu4sBiVn.js} +1 -1
- package/dist/web/assets/{shellsession-DG_WIFRW.js → shellsession-DWzJfEv7.js} +1 -1
- package/dist/web/assets/{soy-CKNCwmqR.js → soy-BnqE-5Ff.js} +1 -1
- package/dist/web/assets/{sparql-BEH92-9e.js → sparql-Ctg__NhZ.js} +1 -1
- package/dist/web/assets/{sql-query-editor-BTMEy88p.js → sql-query-editor-D9k19T_M.js} +1 -1
- package/dist/web/assets/sqlite-viewer-DoV9hnWJ.js +1 -0
- package/dist/web/assets/{stata-Bt5IUWCl.js → stata-Qk2h66XW.js} +1 -1
- package/dist/web/assets/{streaming-store-Dw3ZR1E4.js → streaming-store-uDu3PoN1.js} +1 -1
- package/dist/web/assets/{surrealql-B5M94PT2.js → surrealql-8QEX7EfH.js} +1 -1
- package/dist/web/assets/{svelte-BqLoF5fB.js → svelte-D_mubC5-.js} +1 -1
- package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +2 -0
- package/dist/web/assets/system-monitor-tab-krAcvwF7.js +1 -0
- package/dist/web/assets/system-monitor-window-content-qXLcicFM.js +1 -0
- package/dist/web/assets/tab-host-window-content-7fPZNEni.js +1 -0
- package/dist/web/assets/{tab-store-Dsk4jnlN.js → tab-store-DGJGnbwv.js} +1 -1
- package/dist/web/assets/team-member-sheet-2FnRmk1a.js +2 -0
- package/dist/web/assets/team-member-window-content-BgvJFypM.js +1 -0
- package/dist/web/assets/{templ-BxGWIwYz.js → templ-C1jV0fsO.js} +1 -1
- package/dist/web/assets/terminal-tab-Cs0sGljx.js +3 -0
- package/dist/web/assets/{tex-B9CPrNBA.js → tex-D_StFXy_.js} +1 -1
- package/dist/web/assets/tool-cards-DU_S2o51.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +1 -0
- package/dist/web/assets/{ts-tags-CObhnP_s.js → ts-tags-B5m47rEm.js} +1 -1
- package/dist/web/assets/{twig-ZcDaz0E8.js → twig-C_br6GIl.js} +1 -1
- package/dist/web/assets/{use-blob-url-D-NnFGAr.js → use-blob-url-C-SGi_Hb.js} +1 -1
- package/dist/web/assets/use-inline-blame-B4trxGcR.js +16 -0
- package/dist/web/assets/{use-is-mobile-BsqeOLv4.js → use-is-mobile-BQ9Bv5Ga.js} +1 -1
- package/dist/web/assets/use-lsp-CV0vzILO.js +6 -0
- package/dist/web/assets/use-monaco-theme-DBqbWjWX.js +1 -0
- package/dist/web/assets/use-open-team-member-DENsjp0s.js +1 -0
- package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +1 -0
- package/dist/web/assets/{use-visual-viewport-hdC_hRtB.js → use-visual-viewport-CbwnktSG.js} +1 -1
- package/dist/web/assets/{use-websocket-CUKjQFAl.js → use-websocket-LgXiX1qN.js} +1 -1
- package/dist/web/assets/vendor-markdown-CuWCHIVb.js +291 -0
- package/dist/web/assets/{vendor-mermaid-BoRFiriK.js → vendor-mermaid-Cbig5zA6.js} +1 -1
- package/dist/web/assets/{vendor-ui-D1NQnrtd.js → vendor-ui-BBdkypKW.js} +1 -1
- package/dist/web/assets/video-preview-BDNwumbW.js +1 -0
- package/dist/web/assets/{vue-TgfQfAF8.js → vue-DQwCnLdr.js} +1 -1
- package/dist/web/assets/{vue-html-DbAh9aHW.js → vue-html-T_EoGpPJ.js} +1 -1
- package/dist/web/assets/{vue-vine-DQ8US6vO.js → vue-vine-DmxLqX50.js} +1 -1
- package/dist/web/assets/{window-pip-registry-DfPfSFQg.js → window-pip-registry-3l4hoxuD.js} +1 -1
- package/dist/web/assets/{window-store-BSXqIkwB.js → window-store-UC1jJeLM.js} +1 -1
- package/dist/web/assets/ws-auth-DEAJiGCR.js +1 -0
- package/dist/web/assets/{xml-CXzUNYMl.js → xml-Bs6Bw8is.js} +1 -1
- package/dist/web/assets/{xsl-CdnFww1s.js → xsl-f5ZNhRCc.js} +1 -1
- package/dist/web/index.html +107 -73
- package/dist/web/sw.js +1 -1
- package/docs/project-roadmap.md +2 -0
- package/licenses/Apache-2.0.txt +73 -0
- package/licenses/CC-BY-4.0.txt +156 -0
- package/licenses/MIT.txt +18 -0
- package/licenses/OFL-1.1.txt +43 -0
- package/licenses/Unlicense.txt +10 -0
- package/package.json +14 -7
- package/packages/ext-git-graph/src/blame-view.ts +4 -5
- package/packages/ext-git-graph/src/commit-detail-escaping.test.ts +205 -0
- package/packages/ext-git-graph/src/commit-detail-file-cap.test.ts +65 -0
- package/packages/ext-git-graph/src/commit-message-html.test.ts +214 -0
- package/packages/ext-git-graph/src/commit-message-html.ts +190 -0
- package/packages/ext-git-graph/src/commit-request-generation.test.ts +57 -0
- package/packages/ext-git-graph/src/compare-view.ts +4 -5
- package/packages/ext-git-graph/src/extension.ts +148 -36
- package/packages/ext-git-graph/src/file-history-view.ts +4 -5
- package/packages/ext-git-graph/src/log-args.test.ts +57 -0
- package/packages/ext-git-graph/src/ppm-api.ts +39 -6
- package/packages/ext-git-graph/src/project-scope.test.ts +77 -0
- package/packages/ext-git-graph/src/project-scope.ts +69 -0
- package/packages/ext-git-graph/src/shortstat-parser.test.ts +53 -0
- package/packages/ext-git-graph/src/shortstat-parser.ts +45 -0
- package/packages/ext-git-graph/src/types.ts +2 -0
- package/packages/ext-git-graph/src/webview-html.test.ts +407 -0
- package/packages/ext-git-graph/src/webview-html.ts +631 -108
- package/packages/ext-git-graph/src/webview-shell.test.ts +63 -0
- package/packages/ext-git-graph/src/webview-shell.ts +48 -7
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- package/scripts/check-ppm-dir-usage.sh +0 -0
- package/scripts/copy-monaco.ts +63 -0
- package/scripts/gen-file-icons.ts +485 -0
- package/scripts/gen-nerd-font.ts +311 -0
- package/scripts/gen-product-icons.ts +442 -0
- package/scripts/install.sh +0 -0
- package/scripts/monaco-staging.ts +32 -0
- package/scripts/precompress-web.ts +77 -0
- package/scripts/release.sh +0 -0
- package/scripts/third-party-notices.ts +160 -0
- package/scripts/vite-monaco-dev-assets.ts +63 -0
- package/src/providers/claude-agent-sdk.ts +32 -2
- package/src/server/index.ts +17 -0
- package/src/server/routes/chat.ts +16 -6
- package/src/server/routes/git.ts +113 -1
- package/src/server/routes/lsp.ts +58 -0
- package/src/server/routes/project-scoped.ts +2 -0
- package/src/server/routes/static-encoding.ts +73 -0
- package/src/server/routes/static-fallback.ts +51 -0
- package/src/server/routes/static.ts +41 -5
- package/src/server/ws/lsp.ts +433 -0
- package/src/services/chat-search.service.ts +211 -26
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-ops/fs-real-path.ts +23 -0
- package/src/services/git-blame/commit-line-diff.ts +124 -0
- package/src/services/git-blame/git-blame.service.ts +96 -1
- package/src/services/git-repos/git-repo-discovery.ts +152 -0
- package/src/services/lsp/lsp-capabilities.ts +160 -0
- package/src/services/lsp/lsp-manager.ts +442 -0
- package/src/services/lsp/lsp-protocol.ts +170 -0
- package/src/services/lsp/lsp-session.ts +466 -0
- package/src/services/lsp/server-registry.ts +401 -0
- package/src/services/search-index-db.service.ts +10 -2
- package/src/services/windows-process-tree.ts +351 -351
- package/src/shared/blame.ts +51 -0
- package/src/shared/lsp-uri.ts +108 -0
- package/src/web/app.tsx +6 -0
- package/src/web/components/ai-resources/ai-resource-editor.tsx +1 -1
- package/src/web/components/ai-resources/ai-resources-panel.tsx +1 -1
- package/src/web/components/ai-resources/mcp-group.tsx +1 -1
- package/src/web/components/ai-resources/new-resource-menu.tsx +1 -1
- package/src/web/components/ai-resources/resource-visuals.tsx +1 -1
- package/src/web/components/auth/login-screen.tsx +1 -1
- package/src/web/components/auth/support-dialog.tsx +1 -1
- package/src/web/components/chat/attachment-chips.tsx +113 -5
- package/src/web/components/chat/background-command-bar.tsx +1 -1
- package/src/web/components/chat/background-output-panel.tsx +1 -1
- package/src/web/components/chat/change-edit-list.tsx +1 -1
- package/src/web/components/chat/change-file-row.tsx +1 -1
- package/src/web/components/chat/chat-history-bar.tsx +2 -2
- package/src/web/components/chat/chat-history-panel.tsx +1 -1
- package/src/web/components/chat/chat-placeholder.tsx +1 -1
- package/src/web/components/chat/chat-scroll-nav.tsx +1 -1
- package/src/web/components/chat/chat-tab.tsx +1 -1
- package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +1 -1
- package/src/web/components/chat/chat-welcome.tsx +1 -1
- package/src/web/components/chat/codex-usage-panel.tsx +1 -1
- package/src/web/components/chat/file-picker.tsx +1 -1
- package/src/web/components/chat/image-preview-states.tsx +1 -1
- package/src/web/components/chat/message-action-bar.tsx +1 -1
- package/src/web/components/chat/message-input.tsx +1 -1
- package/src/web/components/chat/message-list.tsx +1 -1
- package/src/web/components/chat/mode-selector.tsx +1 -1
- package/src/web/components/chat/model-selector.tsx +1 -1
- package/src/web/components/chat/model-thinking-selector.tsx +1 -1
- package/src/web/components/chat/pre-compact-button.tsx +1 -1
- package/src/web/components/chat/provider-selector.tsx +1 -1
- package/src/web/components/chat/send-message-card.tsx +1 -1
- package/src/web/components/chat/session-context-menu.tsx +1 -1
- package/src/web/components/chat/session-debug-dialog.tsx +1 -1
- package/src/web/components/chat/session-history-list.tsx +1 -1
- package/src/web/components/chat/session-list-panel.tsx +1 -1
- package/src/web/components/chat/session-picker.tsx +1 -1
- package/src/web/components/chat/slash-command-picker.tsx +1 -1
- package/src/web/components/chat/task-tracker.tsx +1 -1
- package/src/web/components/chat/team-activity-panel.tsx +1 -1
- package/src/web/components/chat/team-member-activity-row.tsx +1 -1
- package/src/web/components/chat/team-member-list.tsx +1 -1
- package/src/web/components/chat/team-member-sheet.tsx +1 -1
- package/src/web/components/chat/team-member-window-content.tsx +1 -1
- package/src/web/components/chat/team-working-bar.tsx +1 -1
- package/src/web/components/chat/tool-cards.tsx +2 -2
- package/src/web/components/chat/turn-change-sheet.tsx +1 -1
- package/src/web/components/chat/turn-change-tray.tsx +1 -1
- package/src/web/components/chat/turn-cost-warning.tsx +1 -1
- package/src/web/components/chat/usage-badge.tsx +1 -1
- package/src/web/components/chat/usage-panel-shell.tsx +1 -1
- package/src/web/components/chat/version-switcher.tsx +1 -1
- package/src/web/components/database/connection-import-export.tsx +1 -1
- package/src/web/components/database/connection-list.tsx +1 -1
- package/src/web/components/database/connection-row.tsx +1 -1
- package/src/web/components/database/database-sidebar.tsx +1 -1
- package/src/web/components/database/database-viewer.tsx +1 -1
- package/src/web/components/database/export-button.tsx +1 -1
- package/src/web/components/database/glide-context-menu.tsx +1 -1
- package/src/web/components/database/glide-data-grid.tsx +1 -1
- package/src/web/components/database/glide-data-preview-panel.tsx +2 -2
- package/src/web/components/database/glide-grid-pagination.tsx +1 -1
- package/src/web/components/database/glide-grid-toolbar.tsx +1 -1
- package/src/web/components/database/glide-header-menu.tsx +1 -1
- package/src/web/components/database/glide-save-bar.tsx +1 -1
- package/src/web/components/database/schema-table-tree.tsx +1 -1
- package/src/web/components/editor/audio-preview.tsx +1 -1
- package/src/web/components/editor/code-editor.tsx +167 -21
- package/src/web/components/editor/compare-picker.tsx +1 -1
- package/src/web/components/editor/conflict-editor.tsx +15 -6
- package/src/web/components/editor/csv-preview.tsx +1 -1
- package/src/web/components/editor/diff-viewer.tsx +59 -37
- package/src/web/components/editor/docx-preview.tsx +1 -1
- package/src/web/components/editor/editor-breadcrumb-external.tsx +1 -1
- package/src/web/components/editor/editor-breadcrumb.tsx +1 -1
- package/src/web/components/editor/editor-language-picker.tsx +3 -1
- package/src/web/components/editor/editor-language-service.tsx +77 -0
- package/src/web/components/editor/editor-mobile-toolbar.tsx +13 -2
- package/src/web/components/editor/editor-placeholder.tsx +1 -1
- package/src/web/components/editor/editor-toolbar.tsx +1 -1
- package/src/web/components/editor/image-preview.tsx +1 -1
- package/src/web/components/editor/lsp-status.tsx +241 -0
- package/src/web/components/editor/pdf-preview.tsx +1 -1
- package/src/web/components/editor/video-player/video-player-controls.tsx +1 -1
- package/src/web/components/editor/video-player/video-player.tsx +1 -1
- package/src/web/components/editor/video-player/video-settings-menu.tsx +1 -1
- package/src/web/components/editor/video-preview.tsx +1 -1
- package/src/web/components/explorer/file-tree.tsx +1 -1
- package/src/web/components/explorer/inline-tree-input.tsx +1 -1
- package/src/web/components/explorer/search-panel.tsx +3 -2
- package/src/web/components/explorer/tree-node-context-menu.tsx +1 -1
- package/src/web/components/explorer/tree-node.tsx +5 -12
- package/src/web/components/extensions/extension-quickpick.tsx +1 -1
- package/src/web/components/extensions/extension-tree-view.tsx +1 -1
- package/src/web/components/extensions/extension-webview.tsx +53 -20
- package/src/web/components/extensions/webview-theme.ts +121 -0
- package/src/web/components/floating-window/pip/pip-caption-button.tsx +1 -1
- package/src/web/components/git/create-worktree-dialog.tsx +5 -3
- package/src/web/components/git/git-log-panel.tsx +4 -2
- package/src/web/components/git/git-placeholder.tsx +1 -1
- package/src/web/components/git/git-ref-badge.tsx +1 -1
- package/src/web/components/git/git-repo-picker.tsx +145 -0
- package/src/web/components/git/git-status-panel.tsx +231 -250
- package/src/web/components/git/git-worktree-panel.tsx +6 -4
- package/src/web/components/git/hunk-stage-dialog.tsx +5 -3
- package/src/web/components/group-chat/group-chat-tab.tsx +1 -1
- package/src/web/components/group-chat/group-create-dialog.tsx +1 -1
- package/src/web/components/group-chat/group-full-transcript-view.tsx +1 -1
- package/src/web/components/group-chat/group-list.tsx +1 -1
- package/src/web/components/group-chat/group-member-edit-dialog.tsx +1 -1
- package/src/web/components/group-chat/group-member-roster.tsx +1 -1
- package/src/web/components/group-chat/group-message-item.tsx +1 -1
- package/src/web/components/group-chat/group-transcript-messages.tsx +1 -1
- package/src/web/components/jira/jira-config-form.tsx +1 -1
- package/src/web/components/jira/jira-filter-builder.tsx +1 -1
- package/src/web/components/jira/jira-panel.tsx +1 -1
- package/src/web/components/jira/jira-results-panel.tsx +1 -1
- package/src/web/components/jira/jira-ticket-card.tsx +1 -1
- package/src/web/components/jira/jira-ticket-detail.tsx +1 -1
- package/src/web/components/jira/jira-watcher-form.tsx +1 -1
- package/src/web/components/jira/jira-watcher-list.tsx +1 -1
- package/src/web/components/layout/add-project-form.tsx +1 -1
- package/src/web/components/layout/cloud-alias-row.tsx +1 -1
- package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +1 -1
- package/src/web/components/layout/cloud-share-popover.tsx +1 -1
- package/src/web/components/layout/command-palette-filter-chips.tsx +1 -1
- package/src/web/components/layout/command-palette.tsx +46 -8
- package/src/web/components/layout/dock-header.tsx +4 -4
- package/src/web/components/layout/dock-panel.tsx +3 -3
- package/src/web/components/layout/draggable-tab.tsx +20 -3
- package/src/web/components/layout/editor-panel.tsx +1 -1
- package/src/web/components/layout/mobile-drawer-tab-bar.tsx +10 -1
- package/src/web/components/layout/mobile-drawer.tsx +1 -1
- package/src/web/components/layout/mobile-nav.tsx +3 -3
- package/src/web/components/layout/mobile-tab-switcher-sheet.tsx +13 -4
- package/src/web/components/layout/nav-section-rail.tsx +1 -1
- package/src/web/components/layout/notification-bell-popover.tsx +1 -1
- package/src/web/components/layout/panel-layout.tsx +1 -1
- package/src/web/components/layout/project-bottom-sheet.tsx +4 -1
- package/src/web/components/layout/project-sort.ts +1 -1
- package/src/web/components/layout/project-switcher.tsx +1 -1
- package/src/web/components/layout/reparenting-tab.tsx +1 -1
- package/src/web/components/layout/sidebar.tsx +1 -1
- package/src/web/components/layout/status-bar.tsx +4 -1
- package/src/web/components/layout/tab-bar.tsx +9 -23
- package/src/web/components/layout/tab-content.tsx +6 -1
- package/src/web/components/layout/tab-pool.tsx +1 -0
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +1 -1
- package/src/web/components/layout/upgrade-button.tsx +1 -1
- package/src/web/components/layout/wake-lock-indicator.tsx +1 -1
- package/src/web/components/os-explorer/explorer-body.tsx +1 -1
- package/src/web/components/os-explorer/explorer-breadcrumb.tsx +1 -1
- package/src/web/components/os-explorer/explorer-sidebar.tsx +1 -1
- package/src/web/components/os-explorer/explorer-sort-menu.tsx +1 -1
- package/src/web/components/os-explorer/explorer-toolbar.tsx +1 -1
- package/src/web/components/os-explorer/icons/file-type-icon-map.ts +10 -94
- package/src/web/components/os-explorer/icons/file-type-icon.tsx +17 -13
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-explorer-top-bar.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-more-sheet.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-places-sheet.tsx +1 -1
- package/src/web/components/os-explorer/mobile/mobile-places-strip.tsx +1 -1
- package/src/web/components/os-explorer/properties-dialog.tsx +1 -1
- package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +1 -1
- package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +1 -1
- package/src/web/components/os-explorer/upload/upload-batch-card.tsx +1 -1
- package/src/web/components/os-explorer/upload/upload-batch-row.tsx +1 -1
- package/src/web/components/os-explorer/use-coarse-long-press.ts +8 -0
- package/src/web/components/os-explorer/views/column-view-mobile.tsx +1 -1
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
- package/src/web/components/os-explorer/views/list-row.tsx +1 -1
- package/src/web/components/os-explorer/views/list-view.tsx +1 -1
- package/src/web/components/postgres/postgres-viewer.tsx +1 -1
- package/src/web/components/problems/problems-panel.tsx +230 -0
- package/src/web/components/problems/problems-status.tsx +53 -0
- package/src/web/components/projects/dir-suggest.tsx +1 -1
- package/src/web/components/projects/project-list.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-readiness-gate.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-warning-gate.tsx +1 -1
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +1 -1
- package/src/web/components/root-error-boundary.tsx +116 -0
- package/src/web/components/settings/accounts/account-add-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-card.tsx +1 -1
- package/src/web/components/settings/accounts/account-export-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-export-simulation.tsx +1 -1
- package/src/web/components/settings/accounts/account-import-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-profile-panel.tsx +1 -1
- package/src/web/components/settings/accounts/account-rotation-settings.tsx +1 -1
- package/src/web/components/settings/accounts/account-token-test-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/account-usage-pattern-chart.tsx +1 -1
- package/src/web/components/settings/accounts/accounts-pane-header.tsx +1 -1
- package/src/web/components/settings/accounts/accounts-settings-section.tsx +1 -1
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +9 -1
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +10 -1
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +1 -1
- package/src/web/components/settings/accounts/codex-backup-dialog.tsx +1 -1
- package/src/web/components/settings/ai-settings-section.tsx +1 -1
- package/src/web/components/settings/appearance-settings-section.tsx +56 -1
- package/src/web/components/settings/change-password-section.tsx +1 -1
- package/src/web/components/settings/extension-manager-section.tsx +1 -1
- package/src/web/components/settings/general-settings-section.tsx +1 -1
- package/src/web/components/settings/glob-list-editor.tsx +1 -1
- package/src/web/components/settings/keyboard-shortcuts-section.tsx +1 -1
- package/src/web/components/settings/mcp-server-dialog.tsx +1 -1
- package/src/web/components/settings/notifications-settings-section.tsx +1 -1
- package/src/web/components/settings/ppmbot-settings-section.tsx +1 -1
- package/src/web/components/settings/proxy-settings-section.tsx +1 -1
- package/src/web/components/settings/proxy-test-section.tsx +1 -1
- package/src/web/components/settings/query-audit-section.tsx +1 -1
- package/src/web/components/settings/schedules/schedule-row.tsx +1 -1
- package/src/web/components/settings/schedules/schedules-settings-section.tsx +1 -1
- package/src/web/components/settings/settings-body.tsx +1 -1
- package/src/web/components/settings/settings-categories.ts +14 -3
- package/src/web/components/settings/settings-category-list.tsx +1 -1
- package/src/web/components/settings/settings-section-content.tsx +1 -1
- package/src/web/components/settings/tag-settings-section.tsx +1 -1
- package/src/web/components/settings/theme-grid.tsx +1 -1
- package/src/web/components/settings/theme-manager-section.tsx +1 -1
- package/src/web/components/settings/theme-picker.tsx +1 -1
- package/src/web/components/shared/bug-report-popup.tsx +1 -1
- package/src/web/components/shared/connection-lost-overlay.tsx +1 -1
- package/src/web/components/shared/diagram-overlay.tsx +1 -1
- package/src/web/components/shared/image-overlay.tsx +1 -1
- package/src/web/components/shared/theme-mode-menu.tsx +1 -1
- package/src/web/components/sqlite/sqlite-data-grid.tsx +1 -1
- package/src/web/components/sqlite/sqlite-table-list.tsx +1 -1
- package/src/web/components/sqlite/sqlite-viewer.tsx +1 -1
- package/src/web/components/system/process-group-row.tsx +1 -1
- package/src/web/components/system/process-row.tsx +1 -1
- package/src/web/components/system/process-table-toolbar.tsx +1 -1
- package/src/web/components/system/resource-status-bar.tsx +1 -1
- package/src/web/components/system/sortable-header.tsx +1 -1
- package/src/web/components/system/system-monitor-body.tsx +1 -1
- package/src/web/components/terminal/terminal-finish-login.tsx +1 -1
- package/src/web/components/terminal/terminal-links-sheet.tsx +1 -1
- package/src/web/components/terminal/terminal-mobile-toolbar.tsx +1 -1
- package/src/web/components/terminal/terminal-placeholder.tsx +1 -1
- package/src/web/components/terminal/terminal-tab.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +1 -1
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +1 -1
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +1 -1
- package/src/web/components/ui/accordion.tsx +1 -1
- package/src/web/components/ui/adaptive-context-menu.tsx +9 -2
- package/src/web/components/ui/browse-button.tsx +1 -1
- package/src/web/components/ui/context-menu.tsx +1 -1
- package/src/web/components/ui/dialog.tsx +1 -1
- package/src/web/components/ui/dropdown-menu.tsx +1 -1
- package/src/web/components/ui/file-browser-picker.tsx +4 -6
- package/src/web/components/ui/select.tsx +1 -1
- package/src/web/components/ui/sidebar-header.tsx +1 -1
- package/src/web/components/ui/sonner.tsx +1 -1
- package/src/web/hooks/use-git-repo.ts +124 -0
- package/src/web/hooks/use-global-keybindings.ts +11 -6
- package/src/web/hooks/use-inline-blame.ts +161 -11
- package/src/web/hooks/use-is-touch-only.ts +46 -0
- package/src/web/hooks/use-lsp.ts +182 -0
- package/src/web/hooks/use-terminal.ts +60 -2
- package/src/web/hooks/use-touch-tab-drag.ts +18 -2
- package/src/web/hooks/use-url-sync.ts +15 -1
- package/src/web/index.html +74 -0
- package/src/web/lib/blame-hover-commands.ts +82 -0
- package/src/web/lib/blame-hover.ts +199 -0
- package/src/web/lib/chunk-recovery.ts +113 -0
- package/src/web/lib/editor-font.ts +70 -0
- package/src/web/lib/ext-command-dispatch.ts +24 -0
- package/src/web/lib/file-icons.generated.ts +408 -0
- package/src/web/lib/file-icons.tsx +192 -0
- package/src/web/lib/fluent-icon.tsx +62 -0
- package/src/web/lib/git-file-tree.ts +83 -0
- package/src/web/lib/git-repo-scope.ts +157 -0
- package/src/web/lib/icons.generated.tsx +428 -0
- package/src/web/lib/icons.ts +17 -0
- package/src/web/lib/lsp/lsp-client.ts +321 -0
- package/src/web/lib/lsp/lsp-documents.ts +39 -0
- package/src/web/lib/lsp/lsp-monaco.ts +214 -0
- package/src/web/lib/lsp/lsp-navigation.ts +129 -0
- package/src/web/lib/lsp/lsp-responses.ts +244 -0
- package/src/web/lib/lsp/lsp-semantic-tokens.ts +180 -0
- package/src/web/lib/lsp/lsp-shadow-models.ts +254 -0
- package/src/web/lib/lsp/monaco-builtin-typescript.ts +149 -0
- package/src/web/lib/lsp/register-providers.ts +458 -0
- package/src/web/lib/monaco-diff-language.ts +62 -0
- package/src/web/lib/monaco-dotenv-language.ts +134 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +11 -2
- package/src/web/lib/tab-type-icons.ts +32 -4
- package/src/web/main.tsx +30 -1
- package/src/web/stores/git-repo-store.ts +117 -0
- package/src/web/stores/git-status-store.ts +10 -7
- package/src/web/stores/keybindings-store.ts +1 -0
- package/src/web/stores/panel-utils.ts +5 -2
- package/src/web/stores/problems-store.ts +116 -0
- package/src/web/stores/settings-store.ts +40 -0
- package/src/web/stores/tab-store.ts +2 -1
- package/src/web/stores/window-panel-persistence.ts +1 -0
- package/src/web/styles/file-icons.generated.css +1007 -0
- package/src/web/styles/fonts/nerd-symbols-codicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-devicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome-ext.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome-legacy.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-awesome.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-font-logos.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-iec-power.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-material.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-misc.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-octicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-pomicons.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-powerline.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-seti.woff2 +0 -0
- package/src/web/styles/fonts/nerd-symbols-weather.woff2 +0 -0
- package/src/web/styles/globals.css +30 -3
- package/src/web/styles/nerd-font.generated.css +153 -0
- package/src/web/sw.ts +69 -2
- package/src/web/theme/adapters/monaco-adapter.ts +20 -1
- package/src/web/theme/diff-token-rules.ts +34 -0
- package/src/web/theme/semantic-token-rules.ts +103 -0
- package/src/web/theme/theme-mode-options.ts +1 -1
- package/vite.config.ts +24 -7
- package/dist/web/assets/accounts-settings-section-DC2A6FhQ.js +0 -1
- package/dist/web/assets/ai-resource-editor-DDhuy_AR.js +0 -1
- package/dist/web/assets/ai-settings-section-J9Rbs26g.js +0 -1
- package/dist/web/assets/ai-settings-section-OrhLykA2.js +0 -1
- package/dist/web/assets/api-client-PMwZQ4iC.js +0 -1
- package/dist/web/assets/appearance-settings-section-BQ1dH29t.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-D55g5j-p.js +0 -1
- package/dist/web/assets/arrow-left-y44wEZrw.js +0 -1
- package/dist/web/assets/arrow-right-SQ8KJIvE.js +0 -1
- package/dist/web/assets/audio-preview-CAVqEwDi.js +0 -1
- package/dist/web/assets/bot-BNu2OM_S.js +0 -1
- package/dist/web/assets/brain-BsjvCk4k.js +0 -1
- package/dist/web/assets/calendar-clock-Zydwzvp7.js +0 -1
- package/dist/web/assets/chat-tab-D2le8LxY.js +0 -15
- package/dist/web/assets/check-DczxQ5UD.js +0 -1
- package/dist/web/assets/chevron-down-BCt_kn-R.js +0 -1
- package/dist/web/assets/chevron-right-Lt-y9H_e.js +0 -1
- package/dist/web/assets/chevron-up-CRl1AF4q.js +0 -1
- package/dist/web/assets/circle-D4vp8Yky.js +0 -1
- package/dist/web/assets/circle-question-mark-BGbDcz7E.js +0 -1
- package/dist/web/assets/clock-BBIE-uf3.js +0 -1
- package/dist/web/assets/code-CvgQViOi.js +0 -1
- package/dist/web/assets/code-editor-D-8oqMBM.js +0 -7
- package/dist/web/assets/codex-daily-guard-h4ZpEemV.js +0 -1
- package/dist/web/assets/conflict-editor-B0Mj8pYR.js +0 -19
- package/dist/web/assets/context-menu-I07Ld1Yr.js +0 -1
- package/dist/web/assets/copy-Bqr2VxN5.js +0 -1
- package/dist/web/assets/csv-preview-Ds2oCW6U.js +0 -2
- package/dist/web/assets/database-BHxcoKlT.js +0 -1
- package/dist/web/assets/database-viewer-CiwQ6spt.js +0 -1
- package/dist/web/assets/diff-viewer-BomFSnpH.js +0 -4
- package/dist/web/assets/docx-preview-PsyMM8SN.js +0 -12
- package/dist/web/assets/download-hiDsGuxx.js +0 -1
- package/dist/web/assets/explorer-body-CExMoOiW.js +0 -2
- package/dist/web/assets/explorer-window-content-Hy9kB6Q7.js +0 -1
- package/dist/web/assets/extension-manager-section-DMb56aut.js +0 -1
- package/dist/web/assets/extension-webview-CQB0Uvdo.js +0 -3
- package/dist/web/assets/eye-DYS5JBeU.js +0 -1
- package/dist/web/assets/eye-off-YWCuob9F.js +0 -1
- package/dist/web/assets/file-exclamation-point-DpdJqj9-.js +0 -1
- package/dist/web/assets/files-settings-section-CVHPO3N9.js +0 -1
- package/dist/web/assets/flask-conical-BUxpYe-g.js +0 -1
- package/dist/web/assets/folder-symlink-DKTEop0L.js +0 -1
- package/dist/web/assets/general-settings-section-CWYR9F5y.js +0 -1
- package/dist/web/assets/git-log-panel-DYLF-XSq.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-At-P8hlA.js +0 -1
- package/dist/web/assets/glide-data-grid-BxFN-Z6F.js +0 -141
- package/dist/web/assets/group-chat-tab-Ch6OGMKj.js +0 -1
- package/dist/web/assets/hand-BomFk_WG.js +0 -1
- package/dist/web/assets/image-preview-DjT5Xb2L.js +0 -1
- package/dist/web/assets/index-BfDJvjH9.js +0 -52
- package/dist/web/assets/index-Di9nVG3y.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-btSsuixb.js +0 -1
- package/dist/web/assets/key-round-BhkPzOjZ.js +0 -1
- package/dist/web/assets/keybindings-store-CNqxSJxv.js +0 -1
- package/dist/web/assets/keyboard-IQMXPNIE.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-DAFNxA49.js +0 -1
- package/dist/web/assets/loader-circle-Bm-Dq_wA.js +0 -1
- package/dist/web/assets/markdown-renderer-BSiaxWjE.js +0 -1
- package/dist/web/assets/markdown-renderer-CMNnYOPG.js +0 -3
- package/dist/web/assets/mobile-explorer-sheet-BMg4Q9Bm.js +0 -1
- package/dist/web/assets/monitor-DEoVYSPl.js +0 -1
- package/dist/web/assets/notification-store-CRlOB9I9.js +0 -1
- package/dist/web/assets/notifications-settings-section-Cci_OOHx.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-B20_sIFd.js +0 -1
- package/dist/web/assets/panel-store-BcgZsiXU.js +0 -1
- package/dist/web/assets/pdf-preview-C28MEOND.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-nzfJdXCC.js +0 -1
- package/dist/web/assets/portal-container-context-DO44JpXO.js +0 -1
- package/dist/web/assets/power-BMdSjXYW.js +0 -1
- package/dist/web/assets/ppmbot-settings-section-Cef8FSS6.js +0 -1
- package/dist/web/assets/process-table-BfixzY3r.js +0 -1
- package/dist/web/assets/proxy-settings-section-BlLWfinY.js +0 -1
- package/dist/web/assets/query-audit-section-qnSL15x8.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-CS4sHKD5.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CG6h4OL2.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-D__2WLV5.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate-rL48PCUH.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-DlGUCbxq.js +0 -1
- package/dist/web/assets/save-CIuK3QI4.js +0 -1
- package/dist/web/assets/schedules-settings-section-BUqf3KTd.js +0 -1
- package/dist/web/assets/send-DQz3yIWM.js +0 -1
- package/dist/web/assets/separator-CE0YEyaB.js +0 -1
- package/dist/web/assets/settings-body-Baw2komX.js +0 -2
- package/dist/web/assets/settings-store-CuQlVCIe.js +0 -2
- package/dist/web/assets/settings-tab-DCW-3C38.js +0 -1
- package/dist/web/assets/settings-window-content-CVxOGqPV.js +0 -1
- package/dist/web/assets/shield-check-46EViZ6-.js +0 -1
- package/dist/web/assets/shield-off-Dx5mw3le.js +0 -1
- package/dist/web/assets/sparkles-Bay_CedB.js +0 -1
- package/dist/web/assets/sqlite-viewer-BWLSC3VJ.js +0 -1
- package/dist/web/assets/system-monitor-body-BqPyB0CY.js +0 -2
- package/dist/web/assets/system-monitor-tab-BXRvCThe.js +0 -1
- package/dist/web/assets/system-monitor-window-content-CvvwXkmW.js +0 -1
- package/dist/web/assets/tab-host-window-content-23IR3qzu.js +0 -1
- package/dist/web/assets/table-BbD_HNPU.js +0 -1
- package/dist/web/assets/team-member-sheet-BXJtrQAB.js +0 -2
- package/dist/web/assets/team-member-window-content-CBWnzwZ1.js +0 -1
- package/dist/web/assets/terminal-tab-uNw7Yvwg.js +0 -3
- package/dist/web/assets/text-wrap-97GXZlav.js +0 -1
- package/dist/web/assets/tool-cards-CjirOQfx.js +0 -9
- package/dist/web/assets/treemap-KZPCXAKY-BBrhTPZk.js +0 -1
- package/dist/web/assets/use-coarse-long-press-CynaAB7m.js +0 -1
- package/dist/web/assets/use-inline-blame-BLPCADZ8.js +0 -8
- package/dist/web/assets/use-monaco-theme-D2ETCHji.js +0 -1
- package/dist/web/assets/use-open-team-member-BlUqTp-l.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-B4O11KIj.js +0 -1
- package/dist/web/assets/vendor-markdown-3-bej77C.js +0 -291
- package/dist/web/assets/video-preview-BlD1DNP8.js +0 -1
- package/dist/web/assets/ws-auth-ZB8jvzhM.js +0 -1
- package/dist/web/assets/x-TEI-uJYo.js +0 -1
- package/dist/web/monacoeditorwork/css.worker.bundle.js +0 -54268
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +0 -14316
- package/dist/web/monacoeditorwork/html.worker.bundle.js +0 -30452
- package/dist/web/monacoeditorwork/json.worker.bundle.js +0 -22095
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +0 -225957
- package/src/web/components/explorer/file-icon-map.ts +0 -69
- /package/dist/web/assets/{abap-Dbc91hEB.js → abap-tkptwpsK.js} +0 -0
- /package/dist/web/assets/{actionscript-3-C14d4O5M.js → actionscript-3-Di9TMGwg.js} +0 -0
- /package/dist/web/assets/{ada-n8mPGtZF.js → ada-CB9F079D.js} +0 -0
- /package/dist/web/assets/{andromeeda-DAKL0TFq.js → andromeeda-Dcl8jpb7.js} +0 -0
- /package/dist/web/assets/{apache-BDCV8YqE.js → apache-D0Nk0qsH.js} +0 -0
- /package/dist/web/assets/{apex-BphlFNA_.js → apex-mQIoWncD.js} +0 -0
- /package/dist/web/assets/{applescript-F2pJar76.js → applescript-C6ljbcsI.js} +0 -0
- /package/dist/web/assets/{apply-theme-Abd1TNqE.js → apply-theme-BwIMNXyX.js} +0 -0
- /package/dist/web/assets/{ara-B32t5JRb.js → ara-WFqRPGPe.js} +0 -0
- /package/dist/web/assets/{asciidoc-DenFyKu-.js → asciidoc-bNd8w4nf.js} +0 -0
- /package/dist/web/assets/{asm-D5mJz41u.js → asm-BQ1_F580.js} +0 -0
- /package/dist/web/assets/{aurora-x-CUyAXiXM.js → aurora-x-Ciqs_Dbu.js} +0 -0
- /package/dist/web/assets/{awk-CUwaf-l-.js → awk-GCR-6KZV.js} +0 -0
- /package/dist/web/assets/{ayu-dark-Cgg8W7cK.js → ayu-dark-DxJXuj99.js} +0 -0
- /package/dist/web/assets/{ayu-light-B1c04Bhg.js → ayu-light-jvSyJWDV.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-CdWbuzp5.js → ayu-mirage-Cg3BPO6H.js} +0 -0
- /package/dist/web/assets/{ballerina-B2-GPRG0.js → ballerina-BPz4CrX6.js} +0 -0
- /package/dist/web/assets/{bat-Nss7Yr-0.js → bat-CSTs2hIG.js} +0 -0
- /package/dist/web/assets/{beancount-CgaWuvpQ.js → beancount-DMateAZX.js} +0 -0
- /package/dist/web/assets/{berry-DCFF4Zj1.js → berry-9lXg0GRM.js} +0 -0
- /package/dist/web/assets/{bibtex-BPNI-Gig.js → bibtex-DIbqmvFg.js} +0 -0
- /package/dist/web/assets/{bicep-qCj6DX1x.js → bicep-02WGsoJ8.js} +0 -0
- /package/dist/web/assets/{bird2-DYDjG-B_.js → bird2-BY0tbu3k.js} +0 -0
- /package/dist/web/assets/{c-CZV8SiOL.js → c-jMBX_9Je.js} +0 -0
- /package/dist/web/assets/{c3-BG7R2A4x.js → c3-B8JVbNjW.js} +0 -0
- /package/dist/web/assets/{cadence-BuiQVST4.js → cadence-DUIf1tvL.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DU5MH6XF.js → can-open-in-ppm-DGEc0s4P.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-C-nwrEbb.js → catppuccin-frappe-FhUCVxex.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-nRLBO6vg.js → catppuccin-latte-CzfrtmgL.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-42eAAqCw.js → catppuccin-macchiato-6xL0lOtj.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-4lH0_4tX.js → catppuccin-mocha-D39-xS3P.js} +0 -0
- /package/dist/web/assets/{clarity-BCPLN5zJ.js → clarity-D8TeGvoT.js} +0 -0
- /package/dist/web/assets/{clojure-Btb509Z6.js → clojure-Dx_acSX7.js} +0 -0
- /package/dist/web/assets/{cmake-CGLZgJL3.js → cmake-CyeYPq8o.js} +0 -0
- /package/dist/web/assets/{codeowners-Dgqmdm5J.js → codeowners-DrTI0lmI.js} +0 -0
- /package/dist/web/assets/{codeql-DDUO6ZHS.js → codeql-CPkFxVW-.js} +0 -0
- /package/dist/web/assets/{common-lisp-DlhZ-mM8.js → common-lisp-B0k-3LTj.js} +0 -0
- /package/dist/web/assets/{coq-CVz-5qYC.js → coq-uygpo7to.js} +0 -0
- /package/dist/web/assets/{csharp-CXL6GcvM.js → csharp-DKUYGfSp.js} +0 -0
- /package/dist/web/assets/{css-CthwUm0E.js → css-DoOUp7v4.js} +0 -0
- /package/dist/web/assets/{csv-Bsal3rDr.js → csv-B3YE13gi.js} +0 -0
- /package/dist/web/assets/{csv-parser-iow1WWML.js → csv-parser-DXePqmXe.js} +0 -0
- /package/dist/web/assets/{cue-D2bSqa4C.js → cue-DViO2wvn.js} +0 -0
- /package/dist/web/assets/{cypher-u-EM8HmU.js → cypher-4BMCIW5N.js} +0 -0
- /package/dist/web/assets/{d-BFVkgfjO.js → d-BT_sTHas.js} +0 -0
- /package/dist/web/assets/{dark-plus-BXlM9uFB.js → dark-plus-DVVXu-1N.js} +0 -0
- /package/dist/web/assets/{dart-Hxx4mUdp.js → dart-DHz6f6_6.js} +0 -0
- /package/dist/web/assets/{dax-DQwsaFe_.js → dax-ChYCjweK.js} +0 -0
- /package/dist/web/assets/{desktop-CAHGHZLk.js → desktop-D6Lt4p04.js} +0 -0
- /package/dist/web/assets/{diff-CAbIXu6l.js → diff-D8TDe4hP.js} +0 -0
- /package/dist/web/assets/{dist-C_bG2Tku.js → dist-DOTlFQJA.js} +0 -0
- /package/dist/web/assets/{docker-51FhFKmM.js → docker-Bsp0YbPx.js} +0 -0
- /package/dist/web/assets/{dotenv-C4fc3fLK.js → dotenv-B_7giSY_.js} +0 -0
- /package/dist/web/assets/{dracula-CTtuacPO.js → dracula-TrQL2iB5.js} +0 -0
- /package/dist/web/assets/{dracula-soft-B-95hu1J.js → dracula-soft-n2XOlgh-.js} +0 -0
- /package/dist/web/assets/{dream-maker-Di3hAOzX.js → dream-maker-CdIEdhpT.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-C6btr6QV.js → emacs-lisp-CSbixs6u.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CDQGzEiq.js → everforest-dark-BuZahZYp.js} +0 -0
- /package/dist/web/assets/{everforest-light-DRZC8v3w.js → everforest-light-DPH53z5H.js} +0 -0
- /package/dist/web/assets/{fennel-BZz92X8f.js → fennel-B1-K_hlJ.js} +0 -0
- /package/dist/web/assets/{fish-qpLXUfGf.js → fish-Cy12Dbua.js} +0 -0
- /package/dist/web/assets/{fluent-B3F6P-xc.js → fluent-B_82ys8E.js} +0 -0
- /package/dist/web/assets/{format-bytes-B9Dbme9a.js → format-bytes-D5sBgIqU.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DjGQrmKa.js → fortran-free-form-BRQdNzZF.js} +0 -0
- /package/dist/web/assets/{gdscript-DToF1duE.js → gdscript-XBDmLUm9.js} +0 -0
- /package/dist/web/assets/{gdshader-FZCS76-j.js → gdshader-DhVfB3u4.js} +0 -0
- /package/dist/web/assets/{genie-CN3-FS9S.js → genie-X3K29Y49.js} +0 -0
- /package/dist/web/assets/{gherkin-DT66zHzt.js → gherkin-DKIx-6cX.js} +0 -0
- /package/dist/web/assets/{github-dark-5S2AJM2m.js → github-dark-Bf7zdp5s.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DNEUWmc8.js → github-dark-default-DqsBBV11.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-BVgm_raB.js → github-dark-dimmed-X2gqRz6w.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Dai8qNkr.js → github-dark-high-contrast-Z7tvbGX4.js} +0 -0
- /package/dist/web/assets/{github-light-C49nlhDa.js → github-light-BKxt-7zZ.js} +0 -0
- /package/dist/web/assets/{github-light-default-BLQcyxQl.js → github-light-default-B0liF4d4.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-BH3u_hMd.js → github-light-high-contrast-F4encJwr.js} +0 -0
- /package/dist/web/assets/{gleam-G2907CEQ.js → gleam-Pz2bHgBB.js} +0 -0
- /package/dist/web/assets/{gn-fxgN7J9Q.js → gn-CgmdNsPU.js} +0 -0
- /package/dist/web/assets/{gnuplot-a1TK_67n.js → gnuplot-qLNF5yZ9.js} +0 -0
- /package/dist/web/assets/{go-DLqnNJpz.js → go-CSDUNhE7.js} +0 -0
- /package/dist/web/assets/{groovy-D8U-plxc.js → groovy-B3NEnOHF.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DRYdA7rg.js → gruvbox-dark-hard-DcoftiUX.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-CJgayBQH.js → gruvbox-dark-medium-D1wVSuD8.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-spQNOlQ2.js → gruvbox-dark-soft-DWpunymH.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-FoElS0Wo.js → gruvbox-light-hard-rHMvyUdS.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-9E1Brisc.js → gruvbox-light-medium-3O2KiXO7.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Coyp7RRB.js → gruvbox-light-soft-lNlXJPwH.js} +0 -0
- /package/dist/web/assets/{haskell-D1twJpgj.js → haskell-aaG3aC0h.js} +0 -0
- /package/dist/web/assets/{haxe-CppquubK.js → haxe-CPcHhUDd.js} +0 -0
- /package/dist/web/assets/{hcl-BWfTkyhR.js → hcl-CMQNlR_4.js} +0 -0
- /package/dist/web/assets/{hjson-fgd7F6t5.js → hjson-DeuklPFj.js} +0 -0
- /package/dist/web/assets/{hlsl-Ua1j-4aZ.js → hlsl-BI3hlsaM.js} +0 -0
- /package/dist/web/assets/{horizon-CTFgrxGb.js → horizon-C7-gOcjs.js} +0 -0
- /package/dist/web/assets/{horizon-bright-C4eTVZPB.js → horizon-bright-Cxt9bRW7.js} +0 -0
- /package/dist/web/assets/{houston-BeYbWPdk.js → houston-DbF2G7iD.js} +0 -0
- /package/dist/web/assets/{hy-BposFX9Q.js → hy-DRG6Hs2I.js} +0 -0
- /package/dist/web/assets/{image-gallery-COea0nSk.js → image-gallery-CObzTisL.js} +0 -0
- /package/dist/web/assets/{imba-D11-b73t.js → imba-CxKv_2TW.js} +0 -0
- /package/dist/web/assets/{ini-BCYCXf0w.js → ini-BiHsyCdO.js} +0 -0
- /package/dist/web/assets/{java-BqKIPPzY.js → java-D3RZzgDB.js} +0 -0
- /package/dist/web/assets/{javascript-CxvTx_wu.js → javascript-4FmSqG-T.js} +0 -0
- /package/dist/web/assets/{json-DiknJGdj.js → json-CjB236_E.js} +0 -0
- /package/dist/web/assets/{json5--8nFeehk.js → json5-bkfO-Rco.js} +0 -0
- /package/dist/web/assets/{jsonc-BFcq-Nx-.js → jsonc-BwaTeLyv.js} +0 -0
- /package/dist/web/assets/{jsonl-DKItgfTA.js → jsonl-CfxNSwGd.js} +0 -0
- /package/dist/web/assets/{jsonnet-sNz5wf7H.js → jsonnet-CQETCYrp.js} +0 -0
- /package/dist/web/assets/{jssm-BALAIF_E.js → jssm-D5o3Zzdz.js} +0 -0
- /package/dist/web/assets/{jsx-fXHHDCVl.js → jsx-BAMILPLo.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-nywk9qzg.js → kanagawa-dragon-BhGVxu3R.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-BmYPLC4f.js → kanagawa-lotus-FH4Y59ro.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-624mOWgc.js → kanagawa-wave-eilvP9MB.js} +0 -0
- /package/dist/web/assets/{kdl-DT8Argim.js → kdl-BQ-yvXUK.js} +0 -0
- /package/dist/web/assets/{kotlin-BvUjvPz2.js → kotlin-BuCx0-VT.js} +0 -0
- /package/dist/web/assets/{kusto-B0PONVdx.js → kusto-DAN_n_Cy.js} +0 -0
- /package/dist/web/assets/{laserwave-ByV7LuzT.js → laserwave-Cq2BlxrT.js} +0 -0
- /package/dist/web/assets/{lean-DQtq2qQC.js → lean-Dhq7uXKw.js} +0 -0
- /package/dist/web/assets/{less-DZeD4ccP.js → less-CxaMQb8j.js} +0 -0
- /package/dist/web/assets/{libesm-CcPYXCKp.js → libesm-DdtEjhIi.js} +0 -0
- /package/dist/web/assets/{light-plus-BCaKDxvd.js → light-plus-B4sMAend.js} +0 -0
- /package/dist/web/assets/{llvm-Dzok661y.js → llvm-DQ6-B_6Y.js} +0 -0
- /package/dist/web/assets/{log-C32HpwgI.js → log-Bb_vqvu7.js} +0 -0
- /package/dist/web/assets/{logo-Dtjk3XHO.js → logo-BzfYv3A_.js} +0 -0
- /package/dist/web/assets/{luau-BMbN7IqN.js → luau-BnAeNR77.js} +0 -0
- /package/dist/web/assets/{make-DIVSnx7D.js → make-B6IFuxpC.js} +0 -0
- /package/dist/web/assets/{markdown-B1JzcsJb.js → markdown-Z0xi77Fn.js} +0 -0
- /package/dist/web/assets/{material-theme-XMfEph9O.js → material-theme-1scy1SYn.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CRGzh4Ox.js → material-theme-darker-G-JykXQP.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-D6esgP3h.js → material-theme-lighter-BtKkCMVq.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-CGbfRrFG.js → material-theme-ocean-irrDC89L.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-DFW4xdPI.js → material-theme-palenight-Eq5_K4LM.js} +0 -0
- /package/dist/web/assets/{matlab-Zpc8hvmI.js → matlab-BE3oCXZC.js} +0 -0
- /package/dist/web/assets/{mdx-DLlDpnjI.js → mdx-DYiuFTGw.js} +0 -0
- /package/dist/web/assets/{mermaid-q_sEDHYr.js → mermaid-DVlaEvtw.js} +0 -0
- /package/dist/web/assets/{min-dark-DUklXai3.js → min-dark-DSkeTpQa.js} +0 -0
- /package/dist/web/assets/{min-light-Ps7CV3R0.js → min-light-CFv2Q0uF.js} +0 -0
- /package/dist/web/assets/{mipsasm-anKs0TGD.js → mipsasm-CiAFuZQ2.js} +0 -0
- /package/dist/web/assets/{mojo-CWTvit9D.js → mojo-ARshcaSQ.js} +0 -0
- /package/dist/web/assets/{monokai-DOSMe3B9.js → monokai-CPTV-SOg.js} +0 -0
- /package/dist/web/assets/{moonbit-zOAryA05.js → moonbit-D57B0dZr.js} +0 -0
- /package/dist/web/assets/{move-CA6YZdSC.js → move-qgy3G_o9.js} +0 -0
- /package/dist/web/assets/{narrat-CyMxkfZJ.js → narrat-CZ3rC-oc.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-D5beSS3L.js → nextflow-groovy-BxxIOcI4.js} +0 -0
- /package/dist/web/assets/{night-owl-3ALu1K6e.js → night-owl-B1ryGCda.js} +0 -0
- /package/dist/web/assets/{night-owl-light-_NLbQQrl.js → night-owl-light-BZvNnvMF.js} +0 -0
- /package/dist/web/assets/{nix-C_KqthNc.js → nix-BVBQi8yv.js} +0 -0
- /package/dist/web/assets/{nord-VG8J_Wfy.js → nord-3NXw613h.js} +0 -0
- /package/dist/web/assets/{nushell-Dx7K7YNI.js → nushell-BMOLRalb.js} +0 -0
- /package/dist/web/assets/{objective-c-DrEbGERB.js → objective-c-AxruEUgh.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DpiXqKhj.js → objective-cpp-cFT0qRsD.js} +0 -0
- /package/dist/web/assets/{ocaml-G6x51MAm.js → ocaml-_8JH6O8J.js} +0 -0
- /package/dist/web/assets/{odin-tK6CU-OU.js → odin-nwYvR0O6.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-581te-yF.js → one-dark-pro-C1LaYJaH.js} +0 -0
- /package/dist/web/assets/{one-light-DpYva-U1.js → one-light-B7rMc__c.js} +0 -0
- /package/dist/web/assets/{openscad-C7LYrrI4.js → openscad-Cwes8bKO.js} +0 -0
- /package/dist/web/assets/{pascal-ChqM3eJD.js → pascal-DMt-6tdZ.js} +0 -0
- /package/dist/web/assets/{pkl-BlIRJsyQ.js → pkl-QBTyYQmB.js} +0 -0
- /package/dist/web/assets/{plastic-DxceXlOD.js → plastic-BTSUp8Fk.js} +0 -0
- /package/dist/web/assets/{plsql-CVikvjWK.js → plsql-DQv3IFXP.js} +0 -0
- /package/dist/web/assets/{po-BN9bpeJD.js → po-CNwZx4tW.js} +0 -0
- /package/dist/web/assets/{poimandres-NeQA7ZSh.js → poimandres-8Os7Qequ.js} +0 -0
- /package/dist/web/assets/{polar-DtRx-uzO.js → polar-b4yK8oD8.js} +0 -0
- /package/dist/web/assets/{postcss-EDnOs84S.js → postcss-sFctwpxx.js} +0 -0
- /package/dist/web/assets/{powerquery-mc-bbl3v.js → powerquery-Cg_WrtuN.js} +0 -0
- /package/dist/web/assets/{powershell-ZqZF93a-.js → powershell-C7Ifz95h.js} +0 -0
- /package/dist/web/assets/{prisma-vhORN1Sx.js → prisma-Z52HUJzQ.js} +0 -0
- /package/dist/web/assets/{prolog-DfIbN_m8.js → prolog-DLqW-zR1.js} +0 -0
- /package/dist/web/assets/{proto-BHcnh-Db.js → proto-BOIzxCCJ.js} +0 -0
- /package/dist/web/assets/{puppet-oLpQOx2h.js → puppet-De7TjZIf.js} +0 -0
- /package/dist/web/assets/{purescript-HbM4Dt8M.js → purescript-R76ou1WT.js} +0 -0
- /package/dist/web/assets/{python-Dlxo2z6Q.js → python-Dprm0tNP.js} +0 -0
- /package/dist/web/assets/{qmldir-B5ox9idu.js → qmldir-Clp_jpNw.js} +0 -0
- /package/dist/web/assets/{qss-CyegwBrl.js → qss-eQPYYzSo.js} +0 -0
- /package/dist/web/assets/{r-CxE2X3lg.js → r-CCcRHIf5.js} +0 -0
- /package/dist/web/assets/{racket-CpguQMOj.js → racket-CzE472yp.js} +0 -0
- /package/dist/web/assets/{raku-DDf8ASYE.js → raku-DQwXrTSi.js} +0 -0
- /package/dist/web/assets/{red-C634f6x3.js → red-KCS6wyop.js} +0 -0
- /package/dist/web/assets/{reg-4gnm7BOP.js → reg-CcrAddUh.js} +0 -0
- /package/dist/web/assets/{regexp-Cs-hPAru.js → regexp-DDbud-Ri.js} +0 -0
- /package/dist/web/assets/{rel-CrGcWbwM.js → rel-99GDgaHp.js} +0 -0
- /package/dist/web/assets/{resolve-theme-Dm8ehbOv.js → resolve-theme-CW2niGV2.js} +0 -0
- /package/dist/web/assets/{riscv-B2P26ROS.js → riscv-Dm2eNv0R.js} +0 -0
- /package/dist/web/assets/{ron-DDHGmvxW.js → ron-CzFt0ygH.js} +0 -0
- /package/dist/web/assets/{rose-pine-CUYRoYal.js → rose-pine-Ck6lCrCP.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-C9JfuUh8.js → rose-pine-dawn-vtALRJNj.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-CrYHzRJm.js → rose-pine-moon-DPto8qXw.js} +0 -0
- /package/dist/web/assets/{rosmsg-iqzz1U38.js → rosmsg-NtS6429H.js} +0 -0
- /package/dist/web/assets/{rust-DVeWF3Is.js → rust-BjIXvF9h.js} +0 -0
- /package/dist/web/assets/{sass-CbVwBbUd.js → sass-CQ23wf5E.js} +0 -0
- /package/dist/web/assets/{scala-gZhcvPGv.js → scala-LfKLM143.js} +0 -0
- /package/dist/web/assets/{scheme-Z-DWgvmB.js → scheme-D3SLBXlF.js} +0 -0
- /package/dist/web/assets/{sdbl-wn_BtpSz.js → sdbl-ODRd2rGN.js} +0 -0
- /package/dist/web/assets/{shellscript-C09f889h.js → shellscript-3KB1G_oA.js} +0 -0
- /package/dist/web/assets/{slack-dark-D63Fbr3l.js → slack-dark-DTtyWmIy.js} +0 -0
- /package/dist/web/assets/{slack-ochin--Yu-v0r6.js → slack-ochin-Bu0PApk3.js} +0 -0
- /package/dist/web/assets/{smalltalk-LQDcL_tW.js → smalltalk-haMT-Sbe.js} +0 -0
- /package/dist/web/assets/{snazzy-light-bo9fX83t.js → snazzy-light-DXJzefE9.js} +0 -0
- /package/dist/web/assets/{solarized-dark-zSs85XH6.js → solarized-dark-B6LyYa18.js} +0 -0
- /package/dist/web/assets/{solarized-light-B6Q4CXBt.js → solarized-light-QLADebkv.js} +0 -0
- /package/dist/web/assets/{solidity-uUJPOjGx.js → solidity-mzfq1aDc.js} +0 -0
- /package/dist/web/assets/{splunk-C6oWgmrC.js → splunk-CyH4RWBp.js} +0 -0
- /package/dist/web/assets/{sql-DyUvXbLi.js → sql-CWVlT5D3.js} +0 -0
- /package/dist/web/assets/{ssh-config-62Vrd_7v.js → ssh-config-DNvQJ1Qx.js} +0 -0
- /package/dist/web/assets/{stylus-CmLQe7Fq.js → stylus-ByQQdtmf.js} +0 -0
- /package/dist/web/assets/{swift-DJwuT1eZ.js → swift-D1cpY4ow.js} +0 -0
- /package/dist/web/assets/{synthwave-84-CEeyTLD7.js → synthwave-84-BDF_-QiW.js} +0 -0
- /package/dist/web/assets/{system-verilog-DMqtAclw.js → system-verilog-BYrkMydC.js} +0 -0
- /package/dist/web/assets/{systemd-XnScP_8M.js → systemd-CQm3zyK0.js} +0 -0
- /package/dist/web/assets/{talonscript-CDoODzml.js → talonscript-DMW5CzE1.js} +0 -0
- /package/dist/web/assets/{tasl-DycI0-Tf.js → tasl-BhFzMCr2.js} +0 -0
- /package/dist/web/assets/{tcl-Dt8iP0sw.js → tcl-D7VEAF-9.js} +0 -0
- /package/dist/web/assets/{terraform-DXzABgqQ.js → terraform-DdT5A0Vy.js} +0 -0
- /package/dist/web/assets/{tokyo-night-DHB1Yc-3.js → tokyo-night-WGLMM6Q9.js} +0 -0
- /package/dist/web/assets/{toml-BkejsZF3.js → toml-CZ7ipXHZ.js} +0 -0
- /package/dist/web/assets/{tsv-ClmhsiiR.js → tsv-B1zVvoTr.js} +0 -0
- /package/dist/web/assets/{tsx-Cp-c_JHF.js → tsx-D4HwRUBq.js} +0 -0
- /package/dist/web/assets/{turtle-BbwC417u.js → turtle-B4r8KEJH.js} +0 -0
- /package/dist/web/assets/{typescript-DbwnrRKJ.js → typescript-BnHhxMtp.js} +0 -0
- /package/dist/web/assets/{typespec-DLL0LoZj.js → typespec-DP2q46GT.js} +0 -0
- /package/dist/web/assets/{typst-BmEouVT1.js → typst-DFsD_yaD.js} +0 -0
- /package/dist/web/assets/{utils-CwNUra6_.js → utils-Bmf_8h7Y.js} +0 -0
- /package/dist/web/assets/{v-C5rFgF81.js → v-Bji8hSVT.js} +0 -0
- /package/dist/web/assets/{vala-rBXMujLw.js → vala-D3TIgGmU.js} +0 -0
- /package/dist/web/assets/{vb-mxG_YZv2.js → vb-DvcxFS2q.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BtjgHRUe.js → vendor-xterm-BqPDsE9b.js} +0 -0
- /package/dist/web/assets/{verilog-uQ-oJRKp.js → verilog-Dy7cF35d.js} +0 -0
- /package/dist/web/assets/{vesper-SO5Hw_2X.js → vesper-BzjwUv9J.js} +0 -0
- /package/dist/web/assets/{vhdl-8IeO85hx.js → vhdl-DuFnFNmW.js} +0 -0
- /package/dist/web/assets/{viml-De-Hm57y.js → viml-yAPgKlbz.js} +0 -0
- /package/dist/web/assets/{vitesse-black-BVm23GJE.js → vitesse-black-oSSQdyIZ.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-CR0XeT-h.js → vitesse-dark-BZxwPihB.js} +0 -0
- /package/dist/web/assets/{vitesse-light-CXIFICUA.js → vitesse-light-uIigycvt.js} +0 -0
- /package/dist/web/assets/{vyper-dUuvAUFJ.js → vyper-CX3NAxm5.js} +0 -0
- /package/dist/web/assets/{wasm-DbLMmiW0.js → wasm-6mEOOvVa.js} +0 -0
- /package/dist/web/assets/{wasm-D5Vd7qV1.js → wasm-D9td37qO.js} +0 -0
- /package/dist/web/assets/{wenyan-BafBYCBu.js → wenyan-lwNIHgk6.js} +0 -0
- /package/dist/web/assets/{wgsl-DY5sHamz.js → wgsl-DQAzCfog.js} +0 -0
- /package/dist/web/assets/{wikitext-QkQQVra5.js → wikitext-DTXmt5NE.js} +0 -0
- /package/dist/web/assets/{wit-BZjaDTy9.js → wit-Bdw_7WDH.js} +0 -0
- /package/dist/web/assets/{wolfram-vrjPFD7R.js → wolfram-DqyHDZ07.js} +0 -0
- /package/dist/web/assets/{yaml-D1as76NB.js → yaml-CPXhdByd.js} +0 -0
- /package/dist/web/assets/{zenscript-D31myxQh.js → zenscript-6s_i5zek.js} +0 -0
- /package/dist/web/assets/{zig-BbLb6ieN.js → zig-B2htezJ_.js} +0 -0
|
@@ -1,351 +1,351 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Process-tree utilities shared by supervisor / stop / server.
|
|
3
|
-
*
|
|
4
|
-
* Most of this file is Windows-specific (see below), but the kill/collect
|
|
5
|
-
* helpers are cross-platform: on POSIX the server's children are NOT in their
|
|
6
|
-
* own process group (Bun.spawn does not setsid), so a `kill(-pid)` group kill
|
|
7
|
-
* targets a group that does not exist and silently no-ops. Descendants must be
|
|
8
|
-
* enumerated from `ps` and signalled individually, and the enumeration has to
|
|
9
|
-
* happen BEFORE the parent dies — once it exits, its children reparent to init
|
|
10
|
-
* and the tree is unrecoverable.
|
|
11
|
-
*
|
|
12
|
-
* The server's listening socket handle is inheritable on Windows, so every
|
|
13
|
-
* descendant the server spawns (Claude SDK node processes, their bash/python
|
|
14
|
-
* children) holds a handle to that socket. If any descendant outlives the
|
|
15
|
-
* server, the port stays in a zombie LISTENING state owned by a dead PID and
|
|
16
|
-
* no new server can ever bind it.
|
|
17
|
-
*
|
|
18
|
-
* `taskkill /T` walks parent links at kill time, so descendants whose parent
|
|
19
|
-
* chain already broke (orphans) escape the tree kill. To catch them, the
|
|
20
|
-
* supervisor periodically snapshots the server's descendant PIDs (plus
|
|
21
|
-
* creation time, to guard against PID reuse) into a file; survivors are
|
|
22
|
-
* reaped whenever the server is stopped or a new supervisor starts.
|
|
23
|
-
*/
|
|
24
|
-
import { resolve } from "node:path";
|
|
25
|
-
import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs";
|
|
26
|
-
import { execFileSync } from "node:child_process";
|
|
27
|
-
import { getPpmDir } from "./ppm-dir.ts";
|
|
28
|
-
// `ps` ships in `procps` and is absent from slim images; /proc gives the same
|
|
29
|
-
// data with no subprocess. See proc-table-linux.ts for why this matters.
|
|
30
|
-
import { readProcCmdline, readProcPpidMap } from "./proc-table-linux.ts";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Return the PID that owns the LISTENING socket for `port`, or 0 if none /
|
|
34
|
-
* undeterminable. Matches any local bind address (0.0.0.0, 127.0.0.1, [::], …)
|
|
35
|
-
* — the holder may bind a different address than the one we configured.
|
|
36
|
-
*/
|
|
37
|
-
export function findPortListenerPid(port: number): number {
|
|
38
|
-
if (process.platform !== "win32") {
|
|
39
|
-
// lsof, not pgrep/pkill: on a memory-pressured box `sysmond` can be
|
|
40
|
-
// jetsam-killed, after which pgrep/pkill fail with a misleading error.
|
|
41
|
-
try {
|
|
42
|
-
const out = execFileSync(
|
|
43
|
-
"lsof",
|
|
44
|
-
["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"],
|
|
45
|
-
{ encoding: "utf-8", timeout: 5000 },
|
|
46
|
-
);
|
|
47
|
-
for (const line of out.split("\n")) {
|
|
48
|
-
const pid = parseInt(line.trim(), 10);
|
|
49
|
-
if (!isNaN(pid) && pid > 0) return pid;
|
|
50
|
-
}
|
|
51
|
-
} catch {}
|
|
52
|
-
return 0;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
const out = execFileSync("netstat", ["-ano"], {
|
|
56
|
-
encoding: "utf-8",
|
|
57
|
-
timeout: 5000,
|
|
58
|
-
windowsHide: true,
|
|
59
|
-
});
|
|
60
|
-
for (const line of out.split("\n")) {
|
|
61
|
-
if (!line.includes("LISTENING")) continue;
|
|
62
|
-
// Columns: Proto LocalAddress ForeignAddress State PID
|
|
63
|
-
const cols = line.trim().split(/\s+/);
|
|
64
|
-
const local = cols[1] ?? "";
|
|
65
|
-
if (!local.endsWith(":" + port)) continue;
|
|
66
|
-
const pid = parseInt(cols[cols.length - 1] ?? "", 10);
|
|
67
|
-
if (!isNaN(pid)) return pid;
|
|
68
|
-
}
|
|
69
|
-
} catch {}
|
|
70
|
-
return 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Heuristic: does `pid`'s command line look like a PPM-owned process?
|
|
75
|
-
* Used to decide whether reclaiming a port held by an alive process is safe
|
|
76
|
-
* (a stale PPM orphan) vs. an unrelated app we must not kill.
|
|
77
|
-
*/
|
|
78
|
-
export function isPpmProcess(pid: number): boolean {
|
|
79
|
-
if (process.platform !== "win32") {
|
|
80
|
-
const cmdline = readProcCmdline(pid);
|
|
81
|
-
if (cmdline !== null) {
|
|
82
|
-
return cmdline.includes("__serve__") || cmdline.includes("__supervise__");
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
const out = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
86
|
-
encoding: "utf-8",
|
|
87
|
-
timeout: 5000,
|
|
88
|
-
}).toLowerCase();
|
|
89
|
-
return out.includes("__serve__") || out.includes("__supervise__");
|
|
90
|
-
} catch {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const out = execFileSync(
|
|
96
|
-
"powershell.exe",
|
|
97
|
-
[
|
|
98
|
-
"-NoProfile", "-NonInteractive", "-Command",
|
|
99
|
-
`(Get-CimInstance Win32_Process -Filter "ProcessId=${pid}").CommandLine`,
|
|
100
|
-
],
|
|
101
|
-
{ encoding: "utf-8", timeout: 5000, windowsHide: true },
|
|
102
|
-
);
|
|
103
|
-
const cmd = out.toLowerCase();
|
|
104
|
-
return cmd.includes("ppm") || cmd.includes("__serve__") || cmd.includes("__supervise__");
|
|
105
|
-
} catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
interface TrackedProc {
|
|
111
|
-
pid: number;
|
|
112
|
-
/** Win32_Process.CreationDate ticks — identity check against PID reuse */
|
|
113
|
-
ticks: string;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const trackedFile = () => resolve(getPpmDir(), "tracked-descendants.json");
|
|
117
|
-
|
|
118
|
-
// One line per process: "pid|ppid|creationTicks"
|
|
119
|
-
const PS_LIST_CMD =
|
|
120
|
-
'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate.Ticks)" }';
|
|
121
|
-
|
|
122
|
-
// Shorter than the caller's 30s poll, so a wedged PowerShell can never outlive
|
|
123
|
-
// the interval that spawned it and stack up behind the in-flight guard.
|
|
124
|
-
const LIST_TIMEOUT_MS = 20_000;
|
|
125
|
-
|
|
126
|
-
async function listProcesses(): Promise<Map<number, { ppid: number; ticks: string }>> {
|
|
127
|
-
const map = new Map<number, { ppid: number; ticks: string }>();
|
|
128
|
-
const proc = Bun.spawn(
|
|
129
|
-
["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", PS_LIST_CMD],
|
|
130
|
-
{ stdout: "pipe", stderr: "ignore", stdin: "ignore", windowsHide: true },
|
|
131
|
-
);
|
|
132
|
-
const killTimer = setTimeout(() => { try { proc.kill(); } catch {} }, LIST_TIMEOUT_MS);
|
|
133
|
-
let out: string;
|
|
134
|
-
try {
|
|
135
|
-
out = await new Response(proc.stdout).text();
|
|
136
|
-
await proc.exited;
|
|
137
|
-
} finally {
|
|
138
|
-
clearTimeout(killTimer);
|
|
139
|
-
}
|
|
140
|
-
for (const line of out.split("\n")) {
|
|
141
|
-
const [pidStr, ppidStr, ticks] = line.trim().split("|");
|
|
142
|
-
const pid = parseInt(pidStr ?? "", 10);
|
|
143
|
-
const ppid = parseInt(ppidStr ?? "", 10);
|
|
144
|
-
if (!isNaN(pid) && !isNaN(ppid)) map.set(pid, { ppid, ticks: ticks ?? "" });
|
|
145
|
-
}
|
|
146
|
-
return map;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function readTracked(): TrackedProc[] {
|
|
150
|
-
try {
|
|
151
|
-
if (!existsSync(trackedFile())) return [];
|
|
152
|
-
const data = JSON.parse(readFileSync(trackedFile(), "utf-8"));
|
|
153
|
-
return Array.isArray(data) ? data : [];
|
|
154
|
-
} catch {
|
|
155
|
-
return [];
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Kill an entire process tree rooted at `pid`.
|
|
161
|
-
* On Windows a forced single-PID kill leaves grandchildren alive; they keep
|
|
162
|
-
* the inherited TCP listening socket handle open, leaving the port in a
|
|
163
|
-
* zombie LISTENING state owned by a dead PID. Killing the whole tree releases
|
|
164
|
-
* the socket — the Windows analog of POSIX process-group kill.
|
|
165
|
-
*/
|
|
166
|
-
export function killProcessTree(pid: number): void {
|
|
167
|
-
if (process.platform === "win32") {
|
|
168
|
-
try {
|
|
169
|
-
execFileSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
170
|
-
stdio: "ignore",
|
|
171
|
-
timeout: 5000,
|
|
172
|
-
windowsHide: true,
|
|
173
|
-
});
|
|
174
|
-
} catch {
|
|
175
|
-
// Already dead, or taskkill unavailable — fall back to single-PID kill.
|
|
176
|
-
try { process.kill(pid, "SIGKILL"); } catch {}
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
killPids(collectProcessTree(pid), "SIGKILL");
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* POSIX: `pid` plus every descendant, parents before children. Windows: `[pid]`
|
|
185
|
-
* (taskkill /T walks the tree itself).
|
|
186
|
-
*
|
|
187
|
-
* Must be called while `pid` is still alive — descendants are found by walking
|
|
188
|
-
* ppid links, and a dead parent's children have already reparented to init.
|
|
189
|
-
*/
|
|
190
|
-
export function collectProcessTree(pid: number): number[] {
|
|
191
|
-
if (process.platform === "win32") return [pid];
|
|
192
|
-
|
|
193
|
-
const childrenOf = new Map<number, number[]>();
|
|
194
|
-
const addEdge = (child: number, parent: number) => {
|
|
195
|
-
const arr = childrenOf.get(parent);
|
|
196
|
-
if (arr) arr.push(child);
|
|
197
|
-
else childrenOf.set(parent, [child]);
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const fromProc = readProcPpidMap();
|
|
201
|
-
if (fromProc) {
|
|
202
|
-
for (const [p, pp] of fromProc) addEdge(p, pp);
|
|
203
|
-
} else {
|
|
204
|
-
try {
|
|
205
|
-
const out = execFileSync("ps", ["-Ao", "pid=,ppid="], {
|
|
206
|
-
encoding: "utf-8",
|
|
207
|
-
timeout: 5000,
|
|
208
|
-
});
|
|
209
|
-
for (const line of out.split("\n")) {
|
|
210
|
-
const [pidStr, ppidStr] = line.trim().split(/\s+/);
|
|
211
|
-
const p = parseInt(pidStr ?? "", 10);
|
|
212
|
-
const pp = parseInt(ppidStr ?? "", 10);
|
|
213
|
-
if (isNaN(p) || isNaN(pp)) continue;
|
|
214
|
-
addEdge(p, pp);
|
|
215
|
-
}
|
|
216
|
-
} catch {
|
|
217
|
-
return [pid];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// BFS with visited guard — PID reuse can produce bogus parent cycles.
|
|
222
|
-
const visited = new Set<number>([pid]);
|
|
223
|
-
const order = [pid];
|
|
224
|
-
const queue = [pid];
|
|
225
|
-
while (queue.length > 0) {
|
|
226
|
-
const cur = queue.shift()!;
|
|
227
|
-
for (const child of childrenOf.get(cur) ?? []) {
|
|
228
|
-
if (visited.has(child)) continue;
|
|
229
|
-
visited.add(child);
|
|
230
|
-
order.push(child);
|
|
231
|
-
queue.push(child);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return order;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/** Never signal init or ourselves, whatever the caller passes in. */
|
|
238
|
-
function killable(pids: number[]): number[] {
|
|
239
|
-
return pids.filter((p) => p > 1 && p !== process.pid);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function isAlive(pid: number): boolean {
|
|
243
|
-
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/** Signal `pids` deepest-first so a parent cannot respawn a child mid-teardown. */
|
|
247
|
-
export function killPids(pids: number[], signal: NodeJS.Signals = "SIGKILL"): void {
|
|
248
|
-
for (const p of killable(pids).reverse()) {
|
|
249
|
-
try { process.kill(p, signal); } catch {}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Graceful tree teardown: SIGTERM everything, wait up to `graceMs` for the tree
|
|
255
|
-
* to drain, then SIGKILL whatever is left. Callers must pass a tree collected
|
|
256
|
-
* before the root exited (see `collectProcessTree`).
|
|
257
|
-
*/
|
|
258
|
-
export async function terminateTree(pids: number[], graceMs: number = 2000): Promise<void> {
|
|
259
|
-
const targets = killable(pids);
|
|
260
|
-
if (targets.length === 0) return;
|
|
261
|
-
|
|
262
|
-
killPids(targets, "SIGTERM");
|
|
263
|
-
|
|
264
|
-
const deadline = Date.now() + graceMs;
|
|
265
|
-
while (Date.now() < deadline) {
|
|
266
|
-
if (!targets.some(isAlive)) return;
|
|
267
|
-
await Bun.sleep(100);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
killPids(targets, "SIGKILL");
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Snapshot all live descendants of `rootPid` into the tracked file.
|
|
275
|
-
* Previously tracked processes that are still the same process (alive with a
|
|
276
|
-
* matching creation time) are kept even if they have since orphaned out of
|
|
277
|
-
* the tree — that is exactly the case the reaper exists for.
|
|
278
|
-
*/
|
|
279
|
-
let snapshotInFlight = false;
|
|
280
|
-
|
|
281
|
-
export async function snapshotServerDescendants(rootPid: number): Promise<void> {
|
|
282
|
-
if (process.platform !== "win32") return;
|
|
283
|
-
// The supervisor fires this from a timer without awaiting. Every concurrent
|
|
284
|
-
// call spawns a PowerShell and builds a full process map, which costs a 32MiB
|
|
285
|
-
// allocator segment that is never returned to the OS — so overlapping calls
|
|
286
|
-
// ratchet the process commit charge up permanently (observed: 19GB committed
|
|
287
|
-
// against 1GB resident). One at a time; a skipped tick is harmless.
|
|
288
|
-
if (snapshotInFlight) return;
|
|
289
|
-
snapshotInFlight = true;
|
|
290
|
-
try {
|
|
291
|
-
const procs = await listProcesses();
|
|
292
|
-
|
|
293
|
-
// Keep prior entries only while they still refer to the same process.
|
|
294
|
-
const kept = readTracked().filter((t) => procs.get(t.pid)?.ticks === t.ticks);
|
|
295
|
-
const tracked = new Map<number, TrackedProc>(kept.map((t) => [t.pid, t]));
|
|
296
|
-
|
|
297
|
-
const childrenOf = new Map<number, number[]>();
|
|
298
|
-
for (const [pid, info] of procs) {
|
|
299
|
-
const arr = childrenOf.get(info.ppid);
|
|
300
|
-
if (arr) arr.push(pid);
|
|
301
|
-
else childrenOf.set(info.ppid, [pid]);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// BFS with visited guard — PID reuse can produce bogus parent cycles.
|
|
305
|
-
const visited = new Set<number>([rootPid]);
|
|
306
|
-
const queue = [rootPid];
|
|
307
|
-
while (queue.length > 0) {
|
|
308
|
-
const cur = queue.shift()!;
|
|
309
|
-
for (const child of childrenOf.get(cur) ?? []) {
|
|
310
|
-
if (visited.has(child)) continue;
|
|
311
|
-
visited.add(child);
|
|
312
|
-
tracked.set(child, { pid: child, ticks: procs.get(child)!.ticks });
|
|
313
|
-
queue.push(child);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
writeFileSync(trackedFile(), JSON.stringify([...tracked.values()]));
|
|
318
|
-
} catch {} finally {
|
|
319
|
-
snapshotInFlight = false;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Kill every tracked descendant that is still the same process it was when
|
|
325
|
-
* snapshotted (PID alive + creation time matches). Clears the tracked file.
|
|
326
|
-
* Returns the number of processes reaped.
|
|
327
|
-
*/
|
|
328
|
-
export async function reapTrackedDescendants(log?: (msg: string) => void): Promise<number> {
|
|
329
|
-
if (process.platform !== "win32") return 0;
|
|
330
|
-
const tracked = readTracked();
|
|
331
|
-
if (tracked.length === 0) return 0;
|
|
332
|
-
let killed = 0;
|
|
333
|
-
try {
|
|
334
|
-
const procs = await listProcesses();
|
|
335
|
-
for (const t of tracked) {
|
|
336
|
-
const info = procs.get(t.pid);
|
|
337
|
-
if (!info || info.ticks !== t.ticks) continue; // dead, or PID was reused
|
|
338
|
-
try {
|
|
339
|
-
execFileSync("taskkill", ["/PID", String(t.pid), "/T", "/F"], {
|
|
340
|
-
stdio: "ignore",
|
|
341
|
-
timeout: 5000,
|
|
342
|
-
windowsHide: true,
|
|
343
|
-
});
|
|
344
|
-
killed++;
|
|
345
|
-
log?.(`Reaped orphaned server descendant (PID: ${t.pid})`);
|
|
346
|
-
} catch {}
|
|
347
|
-
}
|
|
348
|
-
} catch {}
|
|
349
|
-
try { unlinkSync(trackedFile()); } catch {}
|
|
350
|
-
return killed;
|
|
351
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Process-tree utilities shared by supervisor / stop / server.
|
|
3
|
+
*
|
|
4
|
+
* Most of this file is Windows-specific (see below), but the kill/collect
|
|
5
|
+
* helpers are cross-platform: on POSIX the server's children are NOT in their
|
|
6
|
+
* own process group (Bun.spawn does not setsid), so a `kill(-pid)` group kill
|
|
7
|
+
* targets a group that does not exist and silently no-ops. Descendants must be
|
|
8
|
+
* enumerated from `ps` and signalled individually, and the enumeration has to
|
|
9
|
+
* happen BEFORE the parent dies — once it exits, its children reparent to init
|
|
10
|
+
* and the tree is unrecoverable.
|
|
11
|
+
*
|
|
12
|
+
* The server's listening socket handle is inheritable on Windows, so every
|
|
13
|
+
* descendant the server spawns (Claude SDK node processes, their bash/python
|
|
14
|
+
* children) holds a handle to that socket. If any descendant outlives the
|
|
15
|
+
* server, the port stays in a zombie LISTENING state owned by a dead PID and
|
|
16
|
+
* no new server can ever bind it.
|
|
17
|
+
*
|
|
18
|
+
* `taskkill /T` walks parent links at kill time, so descendants whose parent
|
|
19
|
+
* chain already broke (orphans) escape the tree kill. To catch them, the
|
|
20
|
+
* supervisor periodically snapshots the server's descendant PIDs (plus
|
|
21
|
+
* creation time, to guard against PID reuse) into a file; survivors are
|
|
22
|
+
* reaped whenever the server is stopped or a new supervisor starts.
|
|
23
|
+
*/
|
|
24
|
+
import { resolve } from "node:path";
|
|
25
|
+
import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs";
|
|
26
|
+
import { execFileSync } from "node:child_process";
|
|
27
|
+
import { getPpmDir } from "./ppm-dir.ts";
|
|
28
|
+
// `ps` ships in `procps` and is absent from slim images; /proc gives the same
|
|
29
|
+
// data with no subprocess. See proc-table-linux.ts for why this matters.
|
|
30
|
+
import { readProcCmdline, readProcPpidMap } from "./proc-table-linux.ts";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return the PID that owns the LISTENING socket for `port`, or 0 if none /
|
|
34
|
+
* undeterminable. Matches any local bind address (0.0.0.0, 127.0.0.1, [::], …)
|
|
35
|
+
* — the holder may bind a different address than the one we configured.
|
|
36
|
+
*/
|
|
37
|
+
export function findPortListenerPid(port: number): number {
|
|
38
|
+
if (process.platform !== "win32") {
|
|
39
|
+
// lsof, not pgrep/pkill: on a memory-pressured box `sysmond` can be
|
|
40
|
+
// jetsam-killed, after which pgrep/pkill fail with a misleading error.
|
|
41
|
+
try {
|
|
42
|
+
const out = execFileSync(
|
|
43
|
+
"lsof",
|
|
44
|
+
["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"],
|
|
45
|
+
{ encoding: "utf-8", timeout: 5000 },
|
|
46
|
+
);
|
|
47
|
+
for (const line of out.split("\n")) {
|
|
48
|
+
const pid = parseInt(line.trim(), 10);
|
|
49
|
+
if (!isNaN(pid) && pid > 0) return pid;
|
|
50
|
+
}
|
|
51
|
+
} catch {}
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const out = execFileSync("netstat", ["-ano"], {
|
|
56
|
+
encoding: "utf-8",
|
|
57
|
+
timeout: 5000,
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
});
|
|
60
|
+
for (const line of out.split("\n")) {
|
|
61
|
+
if (!line.includes("LISTENING")) continue;
|
|
62
|
+
// Columns: Proto LocalAddress ForeignAddress State PID
|
|
63
|
+
const cols = line.trim().split(/\s+/);
|
|
64
|
+
const local = cols[1] ?? "";
|
|
65
|
+
if (!local.endsWith(":" + port)) continue;
|
|
66
|
+
const pid = parseInt(cols[cols.length - 1] ?? "", 10);
|
|
67
|
+
if (!isNaN(pid)) return pid;
|
|
68
|
+
}
|
|
69
|
+
} catch {}
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Heuristic: does `pid`'s command line look like a PPM-owned process?
|
|
75
|
+
* Used to decide whether reclaiming a port held by an alive process is safe
|
|
76
|
+
* (a stale PPM orphan) vs. an unrelated app we must not kill.
|
|
77
|
+
*/
|
|
78
|
+
export function isPpmProcess(pid: number): boolean {
|
|
79
|
+
if (process.platform !== "win32") {
|
|
80
|
+
const cmdline = readProcCmdline(pid);
|
|
81
|
+
if (cmdline !== null) {
|
|
82
|
+
return cmdline.includes("__serve__") || cmdline.includes("__supervise__");
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const out = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
86
|
+
encoding: "utf-8",
|
|
87
|
+
timeout: 5000,
|
|
88
|
+
}).toLowerCase();
|
|
89
|
+
return out.includes("__serve__") || out.includes("__supervise__");
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const out = execFileSync(
|
|
96
|
+
"powershell.exe",
|
|
97
|
+
[
|
|
98
|
+
"-NoProfile", "-NonInteractive", "-Command",
|
|
99
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId=${pid}").CommandLine`,
|
|
100
|
+
],
|
|
101
|
+
{ encoding: "utf-8", timeout: 5000, windowsHide: true },
|
|
102
|
+
);
|
|
103
|
+
const cmd = out.toLowerCase();
|
|
104
|
+
return cmd.includes("ppm") || cmd.includes("__serve__") || cmd.includes("__supervise__");
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface TrackedProc {
|
|
111
|
+
pid: number;
|
|
112
|
+
/** Win32_Process.CreationDate ticks — identity check against PID reuse */
|
|
113
|
+
ticks: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const trackedFile = () => resolve(getPpmDir(), "tracked-descendants.json");
|
|
117
|
+
|
|
118
|
+
// One line per process: "pid|ppid|creationTicks"
|
|
119
|
+
const PS_LIST_CMD =
|
|
120
|
+
'Get-CimInstance Win32_Process | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate.Ticks)" }';
|
|
121
|
+
|
|
122
|
+
// Shorter than the caller's 30s poll, so a wedged PowerShell can never outlive
|
|
123
|
+
// the interval that spawned it and stack up behind the in-flight guard.
|
|
124
|
+
const LIST_TIMEOUT_MS = 20_000;
|
|
125
|
+
|
|
126
|
+
async function listProcesses(): Promise<Map<number, { ppid: number; ticks: string }>> {
|
|
127
|
+
const map = new Map<number, { ppid: number; ticks: string }>();
|
|
128
|
+
const proc = Bun.spawn(
|
|
129
|
+
["powershell.exe", "-NoProfile", "-NonInteractive", "-Command", PS_LIST_CMD],
|
|
130
|
+
{ stdout: "pipe", stderr: "ignore", stdin: "ignore", windowsHide: true },
|
|
131
|
+
);
|
|
132
|
+
const killTimer = setTimeout(() => { try { proc.kill(); } catch {} }, LIST_TIMEOUT_MS);
|
|
133
|
+
let out: string;
|
|
134
|
+
try {
|
|
135
|
+
out = await new Response(proc.stdout).text();
|
|
136
|
+
await proc.exited;
|
|
137
|
+
} finally {
|
|
138
|
+
clearTimeout(killTimer);
|
|
139
|
+
}
|
|
140
|
+
for (const line of out.split("\n")) {
|
|
141
|
+
const [pidStr, ppidStr, ticks] = line.trim().split("|");
|
|
142
|
+
const pid = parseInt(pidStr ?? "", 10);
|
|
143
|
+
const ppid = parseInt(ppidStr ?? "", 10);
|
|
144
|
+
if (!isNaN(pid) && !isNaN(ppid)) map.set(pid, { ppid, ticks: ticks ?? "" });
|
|
145
|
+
}
|
|
146
|
+
return map;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function readTracked(): TrackedProc[] {
|
|
150
|
+
try {
|
|
151
|
+
if (!existsSync(trackedFile())) return [];
|
|
152
|
+
const data = JSON.parse(readFileSync(trackedFile(), "utf-8"));
|
|
153
|
+
return Array.isArray(data) ? data : [];
|
|
154
|
+
} catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Kill an entire process tree rooted at `pid`.
|
|
161
|
+
* On Windows a forced single-PID kill leaves grandchildren alive; they keep
|
|
162
|
+
* the inherited TCP listening socket handle open, leaving the port in a
|
|
163
|
+
* zombie LISTENING state owned by a dead PID. Killing the whole tree releases
|
|
164
|
+
* the socket — the Windows analog of POSIX process-group kill.
|
|
165
|
+
*/
|
|
166
|
+
export function killProcessTree(pid: number): void {
|
|
167
|
+
if (process.platform === "win32") {
|
|
168
|
+
try {
|
|
169
|
+
execFileSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
170
|
+
stdio: "ignore",
|
|
171
|
+
timeout: 5000,
|
|
172
|
+
windowsHide: true,
|
|
173
|
+
});
|
|
174
|
+
} catch {
|
|
175
|
+
// Already dead, or taskkill unavailable — fall back to single-PID kill.
|
|
176
|
+
try { process.kill(pid, "SIGKILL"); } catch {}
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
killPids(collectProcessTree(pid), "SIGKILL");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* POSIX: `pid` plus every descendant, parents before children. Windows: `[pid]`
|
|
185
|
+
* (taskkill /T walks the tree itself).
|
|
186
|
+
*
|
|
187
|
+
* Must be called while `pid` is still alive — descendants are found by walking
|
|
188
|
+
* ppid links, and a dead parent's children have already reparented to init.
|
|
189
|
+
*/
|
|
190
|
+
export function collectProcessTree(pid: number): number[] {
|
|
191
|
+
if (process.platform === "win32") return [pid];
|
|
192
|
+
|
|
193
|
+
const childrenOf = new Map<number, number[]>();
|
|
194
|
+
const addEdge = (child: number, parent: number) => {
|
|
195
|
+
const arr = childrenOf.get(parent);
|
|
196
|
+
if (arr) arr.push(child);
|
|
197
|
+
else childrenOf.set(parent, [child]);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const fromProc = readProcPpidMap();
|
|
201
|
+
if (fromProc) {
|
|
202
|
+
for (const [p, pp] of fromProc) addEdge(p, pp);
|
|
203
|
+
} else {
|
|
204
|
+
try {
|
|
205
|
+
const out = execFileSync("ps", ["-Ao", "pid=,ppid="], {
|
|
206
|
+
encoding: "utf-8",
|
|
207
|
+
timeout: 5000,
|
|
208
|
+
});
|
|
209
|
+
for (const line of out.split("\n")) {
|
|
210
|
+
const [pidStr, ppidStr] = line.trim().split(/\s+/);
|
|
211
|
+
const p = parseInt(pidStr ?? "", 10);
|
|
212
|
+
const pp = parseInt(ppidStr ?? "", 10);
|
|
213
|
+
if (isNaN(p) || isNaN(pp)) continue;
|
|
214
|
+
addEdge(p, pp);
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
return [pid];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// BFS with visited guard — PID reuse can produce bogus parent cycles.
|
|
222
|
+
const visited = new Set<number>([pid]);
|
|
223
|
+
const order = [pid];
|
|
224
|
+
const queue = [pid];
|
|
225
|
+
while (queue.length > 0) {
|
|
226
|
+
const cur = queue.shift()!;
|
|
227
|
+
for (const child of childrenOf.get(cur) ?? []) {
|
|
228
|
+
if (visited.has(child)) continue;
|
|
229
|
+
visited.add(child);
|
|
230
|
+
order.push(child);
|
|
231
|
+
queue.push(child);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return order;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Never signal init or ourselves, whatever the caller passes in. */
|
|
238
|
+
function killable(pids: number[]): number[] {
|
|
239
|
+
return pids.filter((p) => p > 1 && p !== process.pid);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function isAlive(pid: number): boolean {
|
|
243
|
+
try { process.kill(pid, 0); return true; } catch { return false; }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Signal `pids` deepest-first so a parent cannot respawn a child mid-teardown. */
|
|
247
|
+
export function killPids(pids: number[], signal: NodeJS.Signals = "SIGKILL"): void {
|
|
248
|
+
for (const p of killable(pids).reverse()) {
|
|
249
|
+
try { process.kill(p, signal); } catch {}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Graceful tree teardown: SIGTERM everything, wait up to `graceMs` for the tree
|
|
255
|
+
* to drain, then SIGKILL whatever is left. Callers must pass a tree collected
|
|
256
|
+
* before the root exited (see `collectProcessTree`).
|
|
257
|
+
*/
|
|
258
|
+
export async function terminateTree(pids: number[], graceMs: number = 2000): Promise<void> {
|
|
259
|
+
const targets = killable(pids);
|
|
260
|
+
if (targets.length === 0) return;
|
|
261
|
+
|
|
262
|
+
killPids(targets, "SIGTERM");
|
|
263
|
+
|
|
264
|
+
const deadline = Date.now() + graceMs;
|
|
265
|
+
while (Date.now() < deadline) {
|
|
266
|
+
if (!targets.some(isAlive)) return;
|
|
267
|
+
await Bun.sleep(100);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
killPids(targets, "SIGKILL");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Snapshot all live descendants of `rootPid` into the tracked file.
|
|
275
|
+
* Previously tracked processes that are still the same process (alive with a
|
|
276
|
+
* matching creation time) are kept even if they have since orphaned out of
|
|
277
|
+
* the tree — that is exactly the case the reaper exists for.
|
|
278
|
+
*/
|
|
279
|
+
let snapshotInFlight = false;
|
|
280
|
+
|
|
281
|
+
export async function snapshotServerDescendants(rootPid: number): Promise<void> {
|
|
282
|
+
if (process.platform !== "win32") return;
|
|
283
|
+
// The supervisor fires this from a timer without awaiting. Every concurrent
|
|
284
|
+
// call spawns a PowerShell and builds a full process map, which costs a 32MiB
|
|
285
|
+
// allocator segment that is never returned to the OS — so overlapping calls
|
|
286
|
+
// ratchet the process commit charge up permanently (observed: 19GB committed
|
|
287
|
+
// against 1GB resident). One at a time; a skipped tick is harmless.
|
|
288
|
+
if (snapshotInFlight) return;
|
|
289
|
+
snapshotInFlight = true;
|
|
290
|
+
try {
|
|
291
|
+
const procs = await listProcesses();
|
|
292
|
+
|
|
293
|
+
// Keep prior entries only while they still refer to the same process.
|
|
294
|
+
const kept = readTracked().filter((t) => procs.get(t.pid)?.ticks === t.ticks);
|
|
295
|
+
const tracked = new Map<number, TrackedProc>(kept.map((t) => [t.pid, t]));
|
|
296
|
+
|
|
297
|
+
const childrenOf = new Map<number, number[]>();
|
|
298
|
+
for (const [pid, info] of procs) {
|
|
299
|
+
const arr = childrenOf.get(info.ppid);
|
|
300
|
+
if (arr) arr.push(pid);
|
|
301
|
+
else childrenOf.set(info.ppid, [pid]);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// BFS with visited guard — PID reuse can produce bogus parent cycles.
|
|
305
|
+
const visited = new Set<number>([rootPid]);
|
|
306
|
+
const queue = [rootPid];
|
|
307
|
+
while (queue.length > 0) {
|
|
308
|
+
const cur = queue.shift()!;
|
|
309
|
+
for (const child of childrenOf.get(cur) ?? []) {
|
|
310
|
+
if (visited.has(child)) continue;
|
|
311
|
+
visited.add(child);
|
|
312
|
+
tracked.set(child, { pid: child, ticks: procs.get(child)!.ticks });
|
|
313
|
+
queue.push(child);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
writeFileSync(trackedFile(), JSON.stringify([...tracked.values()]));
|
|
318
|
+
} catch {} finally {
|
|
319
|
+
snapshotInFlight = false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Kill every tracked descendant that is still the same process it was when
|
|
325
|
+
* snapshotted (PID alive + creation time matches). Clears the tracked file.
|
|
326
|
+
* Returns the number of processes reaped.
|
|
327
|
+
*/
|
|
328
|
+
export async function reapTrackedDescendants(log?: (msg: string) => void): Promise<number> {
|
|
329
|
+
if (process.platform !== "win32") return 0;
|
|
330
|
+
const tracked = readTracked();
|
|
331
|
+
if (tracked.length === 0) return 0;
|
|
332
|
+
let killed = 0;
|
|
333
|
+
try {
|
|
334
|
+
const procs = await listProcesses();
|
|
335
|
+
for (const t of tracked) {
|
|
336
|
+
const info = procs.get(t.pid);
|
|
337
|
+
if (!info || info.ticks !== t.ticks) continue; // dead, or PID was reused
|
|
338
|
+
try {
|
|
339
|
+
execFileSync("taskkill", ["/PID", String(t.pid), "/T", "/F"], {
|
|
340
|
+
stdio: "ignore",
|
|
341
|
+
timeout: 5000,
|
|
342
|
+
windowsHide: true,
|
|
343
|
+
});
|
|
344
|
+
killed++;
|
|
345
|
+
log?.(`Reaped orphaned server descendant (PID: ${t.pid})`);
|
|
346
|
+
} catch {}
|
|
347
|
+
}
|
|
348
|
+
} catch {}
|
|
349
|
+
try { unlinkSync(trackedFile()); } catch {}
|
|
350
|
+
return killed;
|
|
351
|
+
}
|