@hienlh/ppm 0.18.7 → 0.18.9
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/.spike/s.err +63 -0
- package/.spike/tunnel.err +42 -0
- package/.spike/w.err +96 -0
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +5 -3
- package/dist/web/assets/{ai-resource-editor-DZQUyrsU.js → ai-resource-editor-DFCuNe-P.js} +1 -1
- package/dist/web/assets/{angular-html-jfMqUuRd.js → angular-html-Ctz0zN01.js} +1 -1
- package/dist/web/assets/{angular-ts-wytn9PMm.js → angular-ts-QXHpgB1C.js} +1 -1
- package/dist/web/assets/{api-settings-DlcoR5QT.js → api-settings-nblja3Rk.js} +1 -1
- package/dist/web/assets/{apl-i-ahKx6O.js → apl-b9KVk-O4.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-C7ra20CE.js +1 -0
- package/dist/web/assets/{astro-DVBCJfG6.js → astro-BlFEeGYr.js} +1 -1
- package/dist/web/assets/{audio-preview-GGksv6U8.js → audio-preview-CZLmhf__.js} +1 -1
- package/dist/web/assets/{blade-BHlL8SqU.js → blade-CGkj2MUI.js} +1 -1
- package/dist/web/assets/{bsl-D-UOfR1F.js → bsl-CVsbb6ap.js} +1 -1
- package/dist/web/assets/button-BA_fJOtX.js +1 -0
- package/dist/web/assets/{cairo-Bn6rucli.js → cairo-BbIHl2iE.js} +1 -1
- package/dist/web/assets/chat-tab-flMPLoKb.js +15 -0
- package/dist/web/assets/circle-hjUGru6R.js +1 -0
- package/dist/web/assets/circle-question-mark-CzR7YRc-.js +1 -0
- package/dist/web/assets/{cobol-Byn9FYKu.js → cobol-BWSGjmWb.js} +1 -1
- package/dist/web/assets/code-editor-C2VQbXzw.js +10 -0
- package/dist/web/assets/{coffee-DjBl4gm-.js → coffee-CWGNOxds.js} +1 -1
- package/dist/web/assets/{conflict-editor-Dg_JIcKq.js → conflict-editor-CdH_h0vD.js} +3 -3
- package/dist/web/assets/{context-menu-DkQdt6YA.js → context-menu-Cq0u3SK9.js} +1 -1
- package/dist/web/assets/{cpp-CKzic-3E.js → cpp-DHGV24J2.js} +1 -1
- package/dist/web/assets/{crystal-Bkg-Ly_g.js → crystal-Vlwf2UMP.js} +1 -1
- package/dist/web/assets/{csv-preview-DsJ_a2Op.js → csv-preview-CY_454vd.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-B1mTl3KS.js → data-grid-overlay-editor-D9il5DZS.js} +1 -1
- package/dist/web/assets/{database-viewer-CzjnrnMz.js → database-viewer-BCZOTrPT.js} +1 -1
- package/dist/web/assets/{diff-viewer-DUNkO-ll.js → diff-viewer-BwfMN-nW.js} +1 -1
- package/dist/web/assets/{docx-preview-DujpUDkE.js → docx-preview-D3Sdh_aT.js} +1 -1
- package/dist/web/assets/{edge-DVbZnd3Y.js → edge-Bhqk64zS.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-BivQwZMD.js → edit-diff-preview-gq6ALf2B.js} +1 -1
- package/dist/web/assets/{elixir-Dwi8Wu7W.js → elixir-D466WCFm.js} +1 -1
- package/dist/web/assets/{elm-DtIv-HXJ.js → elm-Bfs2qNPR.js} +1 -1
- package/dist/web/assets/{erb-D3HEDz1x.js → erb-WI7jUaB6.js} +1 -1
- package/dist/web/assets/{erlang-2MjTCzGz.js → erlang-BCBe8nCc.js} +1 -1
- package/dist/web/assets/{esm-CZ8B8Ba2.js → esm-CZs9WVDO.js} +1 -1
- package/dist/web/assets/{explorer-body-DSDJUQs7.js → explorer-body-BKOMM566.js} +2 -2
- package/dist/web/assets/{explorer-window-content-CWYBapkj.js → explorer-window-content-Bbzijk8t.js} +1 -1
- package/dist/web/assets/{extension-webview-DBTFznQp.js → extension-webview-CuOQrHIS.js} +1 -1
- package/dist/web/assets/format-bytes-Dk7B73B7.js +1 -0
- package/dist/web/assets/{fortran-fixed-form-BLyAtyPM.js → fortran-fixed-form-CXLIv8Ox.js} +1 -1
- package/dist/web/assets/{fsharp-Bxbn_gbr.js → fsharp-CZsYmiw9.js} +1 -1
- package/dist/web/assets/{gdresource-sFMY4D2Q.js → gdresource-BGRvoWfC.js} +1 -1
- package/dist/web/assets/{git-commit-BzB9Lio5.js → git-commit-DH6mY3Km.js} +1 -1
- package/dist/web/assets/{git-log-panel-D8ZQdoYl.js → git-log-panel-DcV7PhqW.js} +1 -1
- package/dist/web/assets/{git-rebase-CKSVd848.js → git-rebase-C5Gql6Ma.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-D4hYLNJU.js +1 -0
- package/dist/web/assets/{glide-data-grid-uIjdTT7c.js → glide-data-grid-Cehfgrzc.js} +7 -7
- package/dist/web/assets/{glimmer-js-BY8aExAJ.js → glimmer-js-1-6JMJlj.js} +1 -1
- package/dist/web/assets/{glimmer-ts-Bak54dBX.js → glimmer-ts-C_Njj0nK.js} +1 -1
- package/dist/web/assets/{glsl-nR2eaJQe.js → glsl-DbkxE1TN.js} +1 -1
- package/dist/web/assets/{graphql-CGPPFAU6.js → graphql-BK75fiXl.js} +1 -1
- package/dist/web/assets/group-chat-tab-BVPYxjz-.js +1 -0
- package/dist/web/assets/{hack-CXIMX-Ol.js → hack-DG151vit.js} +1 -1
- package/dist/web/assets/{haml-tPY-9Pfm.js → haml-DKeIPQWM.js} +1 -1
- package/dist/web/assets/{handlebars-BuW1LT7T.js → handlebars-398uCI13.js} +1 -1
- package/dist/web/assets/{html-bgVcwZ-t.js → html-NLWdst5q.js} +1 -1
- package/dist/web/assets/{html-derivative-D6-IxTgn.js → html-derivative-CJf_Le2z.js} +1 -1
- package/dist/web/assets/{http-CJ1zyB-h.js → http-G1K2FozA.js} +1 -1
- package/dist/web/assets/{hurl-CmoT7l3T.js → hurl-WDC6sSBz.js} +1 -1
- package/dist/web/assets/{hxml-CztOsL8o.js → hxml-Dc3jBXuf.js} +1 -1
- package/dist/web/assets/{image-preview-B0VXSpYu.js → image-preview-D-Ok10eZ.js} +1 -1
- package/dist/web/assets/index-CerSxGdf.js +52 -0
- package/dist/web/assets/index-DRA_OpWY.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-lRlrTlkb.js +1 -0
- package/dist/web/assets/{jinja-B1pq3jJq.js → jinja-CWsQj2y0.js} +1 -1
- package/dist/web/assets/{jison-D8B5pCwA.js → jison-CciJg2NW.js} +1 -1
- package/dist/web/assets/{julia-D0K3bwpU.js → julia-IOQNJAB7.js} +1 -1
- package/dist/web/assets/{just-B9Jlddu-.js → just-BGdHchwS.js} +1 -1
- package/dist/web/assets/{keybindings-store-FyolpNND.js → keybindings-store-aIY40k2o.js} +1 -1
- package/dist/web/assets/{latex-BPJSSE6j.js → latex-D6TOl-L2.js} +1 -1
- package/dist/web/assets/{liquid-lv10fo4U.js → liquid-CVSvu_Wn.js} +1 -1
- package/dist/web/assets/{lua-PNuaIBEF.js → lua-CcKiU6HW.js} +1 -1
- package/dist/web/assets/markdown-renderer-I89kyQhe.js +1 -0
- package/dist/web/assets/{markdown-renderer-BaBQ6Wm6.js → markdown-renderer-pVzBdkpl.js} +3 -3
- package/dist/web/assets/{marko-Du3frD6X.js → marko--UOicMGo.js} +1 -1
- package/dist/web/assets/{mdc-BeDVcFRl.js → mdc-D690aU2C.js} +1 -1
- package/dist/web/assets/media-url-CylYQld1.js +1 -0
- package/dist/web/assets/{mobile-explorer-sheet-Zxjl-e4c.js → mobile-explorer-sheet-5Uj9ZMM9.js} +1 -1
- package/dist/web/assets/{nextflow-DkcXP71s.js → nextflow-DR8PhpF8.js} +1 -1
- package/dist/web/assets/{nginx-C5n8N_bt.js → nginx-Qb9pUk1g.js} +1 -1
- package/dist/web/assets/{nim-CIhyfs-A.js → nim-DLa8R3NS.js} +1 -1
- package/dist/web/assets/{notification-store-DnGpxgkF.js → notification-store-DJ-OtAG3.js} +1 -1
- package/dist/web/assets/{number-overlay-editor-NgOsWJ7x.js → number-overlay-editor-D9SkFg1Y.js} +1 -1
- package/dist/web/assets/overview-panel-Cgi3UPY1.js +1 -0
- package/dist/web/assets/packet-RMMSAZCW-BIhk1mvp.js +1 -0
- package/dist/web/assets/{pdf-preview-xzAA4-kD.js → pdf-preview-Di5xHFyT.js} +1 -1
- package/dist/web/assets/{perl-Cf31sZYE.js → perl-AMNGIneF.js} +1 -1
- package/dist/web/assets/{php-Dzl_MDIW.js → php-CDsOOxXn.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-SLS6i76l.js +1 -0
- package/dist/web/assets/{postgres-viewer-Dkec6GyK.js → postgres-viewer-CR6VpTrX.js} +3 -3
- package/dist/web/assets/process-table-DL6shbzt.js +1 -0
- package/dist/web/assets/{pug-CxSYHrB3.js → pug-C2kdkhQ6.js} +1 -1
- package/dist/web/assets/{qml-DlEjGhwN.js → qml-DRDkUp75.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-JJ6O1Er_.js +1 -0
- package/dist/web/assets/{razor-KVt5uW14.js → razor-wY_6wUeC.js} +1 -1
- package/dist/web/assets/{rst-D5BwNTnw.js → rst-BRpofyNs.js} +1 -1
- package/dist/web/assets/{ruby-D5fejQwd.js → ruby-ieATBhWm.js} +1 -1
- package/dist/web/assets/{sas-Bj7B1CB2.js → sas-CoOOEbOO.js} +1 -1
- package/dist/web/assets/{scss-BB0CUoCL.js → scss-_nWV0bsb.js} +1 -1
- package/dist/web/assets/{settings-store-C9YT1Hir.js → settings-store-C20lqh0G.js} +2 -2
- package/dist/web/assets/settings-tab-jbiQwozX.js +1 -0
- package/dist/web/assets/{shaderlab-BdjEZUMc.js → shaderlab-XkqfIfO8.js} +1 -1
- package/dist/web/assets/{shellsession-Bp1OA1rR.js → shellsession-BKBIqTlM.js} +1 -1
- package/dist/web/assets/{soy-DlYz3wsC.js → soy-oVDnPzzh.js} +1 -1
- package/dist/web/assets/{sparql-CdfII5Jd.js → sparql-N2VclEsF.js} +1 -1
- package/dist/web/assets/{sql-query-editor-Bz1XrUqI.js → sql-query-editor-BPYsJDsn.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-D5-NZw6b.js → sqlite-viewer-Bq9ZJA2y.js} +1 -1
- package/dist/web/assets/{stata-BvUA6GUW.js → stata-BuuClVDg.js} +1 -1
- package/dist/web/assets/{surrealql--DLNbTyI.js → surrealql-DE5_63MS.js} +1 -1
- package/dist/web/assets/{svelte-CBfSsG4n.js → svelte--YWjlOrK.js} +1 -1
- package/dist/web/assets/system-monitor-body-CKEYWyN9.js +2 -0
- package/dist/web/assets/system-monitor-tab-WjhFFHyM.js +1 -0
- package/dist/web/assets/system-monitor-window-content-CMiISeoC.js +1 -0
- package/dist/web/assets/team-member-sheet-oY5EkH53.js +2 -0
- package/dist/web/assets/{team-member-window-content-DSpZ2zhL.js → team-member-window-content-yCBeI_5X.js} +1 -1
- package/dist/web/assets/{templ-Dv8zpKZW.js → templ-MlpvJx4a.js} +1 -1
- package/dist/web/assets/{terminal-tab-DXazqejW.js → terminal-tab-BxHtMR7h.js} +3 -3
- package/dist/web/assets/{tex-CsrOiqh1.js → tex-0loAoXJG.js} +1 -1
- package/dist/web/assets/{tool-cards-Bgp8iceS.js → tool-cards-BcGrk1zk.js} +5 -5
- package/dist/web/assets/treemap-KZPCXAKY-Be3KNmns.js +1 -0
- package/dist/web/assets/{ts-tags-3T98k4EJ.js → ts-tags-pt-Ug0hK.js} +1 -1
- package/dist/web/assets/{twig-u6QZ45FH.js → twig-DOePpLOL.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-CpF3A7JL.js → use-monaco-theme-CGucu3fb.js} +1 -1
- package/dist/web/assets/{use-open-team-member-CUvV02fA.js → use-open-team-member-DdrRXj-M.js} +1 -1
- package/dist/web/assets/{use-websocket-TnL2I1io.js → use-websocket-C-Rxv8JN.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-Bg4vE8QA.js → vendor-mermaid-DrwG0NYt.js} +1 -1
- package/dist/web/assets/video-preview-DYAbbmd0.js +1 -0
- package/dist/web/assets/{vue-CxIsxtwn.js → vue-D6f-rprj.js} +1 -1
- package/dist/web/assets/{vue-html-BGL5qnuH.js → vue-html-DEYRkS1V.js} +1 -1
- package/dist/web/assets/{vue-vine-DHPh_gMr.js → vue-vine-CxSG2ojf.js} +1 -1
- package/dist/web/assets/{xml-C-vM2m7J.js → xml-DhrxbATj.js} +1 -1
- package/dist/web/assets/{xsl-DvTXNwuO.js → xsl-CU0ZndUO.js} +1 -1
- package/dist/web/index.html +18 -17
- package/dist/web/sw.js +1 -1
- package/docs/system-architecture.md +2 -2
- package/package.json +1 -1
- package/src/server/helpers/media-route-handlers.ts +22 -3
- package/src/server/index.ts +2 -8
- package/src/server/routes/files.ts +5 -2
- package/src/server/routes/fs-browse.ts +5 -2
- package/src/server/routes/resources.ts +119 -73
- package/src/services/autostart-generator.ts +79 -7
- package/src/services/autostart-register.ts +15 -2
- package/src/services/fs-path-guard.service.ts +17 -1
- package/src/services/media-transcode/transcode-stream.ts +27 -5
- package/src/services/proc-table-linux.ts +4 -0
- package/src/services/redact-secrets.ts +33 -0
- package/src/services/system-metrics/cpu-memory-collector.ts +101 -0
- package/src/services/system-metrics/disk-net-collector-darwin.ts +75 -0
- package/src/services/system-metrics/disk-net-collector-linux.ts +88 -0
- package/src/services/system-metrics/disk-net-collector-windows.ts +57 -0
- package/src/services/system-metrics/gpu-collector-nvidia.ts +78 -0
- package/src/services/system-metrics/gpu-process-memory-nvidia.ts +66 -0
- package/src/services/system-metrics/gpu-process-usage-windows.ts +138 -0
- package/src/services/system-metrics/kill-executor.ts +85 -0
- package/src/services/system-metrics/kill-guard.ts +124 -0
- package/src/services/system-metrics/kill-identity-resolver.ts +72 -0
- package/src/services/system-metrics/kill-request-handler.ts +79 -0
- package/src/services/system-metrics/metrics-collector-lock.ts +36 -0
- package/src/services/system-metrics/metrics-poll-scheduler.ts +83 -0
- package/src/services/system-metrics/metrics-subscriber-registry.ts +85 -0
- package/src/services/system-metrics/powershell-session-bootstrap.ts +48 -0
- package/src/services/system-metrics/powershell-session.ts +182 -0
- package/src/services/system-metrics/ppm-process-ownership.ts +34 -0
- package/src/services/system-metrics/ppm-protected-pids.ts +84 -0
- package/src/services/system-metrics/process-collector-darwin.ts +141 -0
- package/src/services/system-metrics/process-collector-linux.ts +85 -0
- package/src/services/system-metrics/process-collector-types.ts +80 -0
- package/src/services/system-metrics/process-collector-windows-parse.ts +153 -0
- package/src/services/system-metrics/process-collector-windows.ts +136 -0
- package/src/services/system-metrics/process-cpu-delta.ts +61 -0
- package/src/services/system-metrics/process-grouping.ts +142 -0
- package/src/services/system-metrics/process-io-delta.ts +84 -0
- package/src/services/system-metrics/process-io-linux.ts +46 -0
- package/src/services/system-metrics/process-net-collector-darwin.ts +103 -0
- package/src/services/system-metrics/process-rows-builder.ts +111 -0
- package/src/services/system-metrics/rate-delta.ts +47 -0
- package/src/services/system-metrics/system-metrics-platform.ts +51 -0
- package/src/services/system-metrics/system-metrics-tick.ts +178 -0
- package/src/services/system-metrics/system-metrics.service.ts +212 -0
- package/src/types/system-metrics.ts +199 -0
- package/src/web/components/editor/video-player/video-player.tsx +54 -22
- package/src/web/components/floating-window/window-content-registry.ts +2 -0
- package/src/web/components/floating-window/window-persistence.ts +1 -1
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/system/build-kill-request.ts +9 -0
- package/src/web/components/system/chart-scale.ts +28 -0
- package/src/web/components/system/cpu-core-bars.tsx +33 -0
- package/src/web/components/system/kill-confirm-dialog.tsx +89 -0
- package/src/web/components/system/metric-chart-canvas.tsx +108 -0
- package/src/web/components/system/overview-cards/cpu-card.tsx +39 -0
- package/src/web/components/system/overview-cards/disk-card.tsx +41 -0
- package/src/web/components/system/overview-cards/gpu-card.tsx +33 -0
- package/src/web/components/system/overview-cards/mem-card.tsx +32 -0
- package/src/web/components/system/overview-cards/net-card.tsx +41 -0
- package/src/web/components/system/overview-panel.tsx +97 -0
- package/src/web/components/system/process-columns-grid.ts +93 -0
- package/src/web/components/system/process-group-row.tsx +71 -0
- package/src/web/components/system/process-row-format.ts +56 -0
- package/src/web/components/system/process-row.tsx +82 -0
- package/src/web/components/system/process-table-model.ts +127 -0
- package/src/web/components/system/process-table-sort.ts +90 -0
- package/src/web/components/system/process-table-toolbar.tsx +190 -0
- package/src/web/components/system/process-table-totals.ts +49 -0
- package/src/web/components/system/process-table.tsx +187 -0
- package/src/web/components/system/resolve-open-system-monitor-action.ts +29 -0
- package/src/web/components/system/resource-status-bar.tsx +17 -16
- package/src/web/components/system/sortable-header.tsx +65 -0
- package/src/web/components/system/sparkline-canvas.tsx +8 -9
- package/src/web/components/system/system-monitor-body.tsx +146 -0
- package/src/web/components/system/system-monitor-tab.tsx +2 -185
- package/src/web/components/system/system-monitor-window-content.tsx +18 -0
- package/src/web/components/system/use-open-system-monitor.ts +34 -0
- package/src/web/hooks/resource-monitor-lease.ts +55 -0
- package/src/web/hooks/use-element-width.ts +27 -0
- package/src/web/hooks/use-resource-monitor.ts +113 -35
- package/src/web/lib/format-bytes.ts +16 -0
- package/src/web/lib/media-url.ts +30 -4
- package/.release-run.sh +0 -5
- package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +0 -1
- package/dist/web/assets/arrow-right-DubGFkN8.js +0 -1
- package/dist/web/assets/button-B4MWPjWj.js +0 -1
- package/dist/web/assets/chat-tab-Xd2WMjdX.js +0 -15
- package/dist/web/assets/circle-question-mark-CENpx4oA.js +0 -1
- package/dist/web/assets/code-editor-DqXYJWAB.js +0 -10
- package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +0 -1
- package/dist/web/assets/group-chat-tab-B3sryFdA.js +0 -1
- package/dist/web/assets/index-DgbOF3xa.js +0 -52
- package/dist/web/assets/index-oxsyfDa0.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +0 -1
- package/dist/web/assets/markdown-renderer-feiuhJ9n.js +0 -1
- package/dist/web/assets/media-url-CUpN5vI1.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +0 -1
- package/dist/web/assets/settings-tab-Bd8e6iqY.js +0 -1
- package/dist/web/assets/system-monitor-tab-A0jvt5Qw.js +0 -1
- package/dist/web/assets/team-member-sheet-5Oaqm8I_.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +0 -1
- package/dist/web/assets/video-preview-CwtLPELG.js +0 -1
- package/src/services/resource-monitor-utils.ts +0 -141
- package/src/services/resource-monitor.service.ts +0 -147
- package/src/web/components/system/system-monitor-group-row.tsx +0 -153
- /package/dist/web/assets/{abap-C1K0-wzc.js → abap-MRbmsXOs.js} +0 -0
- /package/dist/web/assets/{actionscript-3-CJhQVjLA.js → actionscript-3-BDDHKqa4.js} +0 -0
- /package/dist/web/assets/{ada-Bt8r9fME.js → ada-DpuV9KwV.js} +0 -0
- /package/dist/web/assets/{andromeeda-DF1I8qi3.js → andromeeda-D6qUCKJo.js} +0 -0
- /package/dist/web/assets/{apache-CnlleE6P.js → apache-CX1NU_Tl.js} +0 -0
- /package/dist/web/assets/{apex-j0C6Xmk6.js → apex-B4-aoQ3K.js} +0 -0
- /package/dist/web/assets/{api-settings-DfNRPr1q.js → api-settings-kQHV6pYZ.js} +0 -0
- /package/dist/web/assets/{api-themes-8WTH6KpV.js → api-themes-CrK0tkW_.js} +0 -0
- /package/dist/web/assets/{applescript-DO7AnO8U.js → applescript-Dm9NI6KR.js} +0 -0
- /package/dist/web/assets/{apply-theme-CNj1iA-v.js → apply-theme-BH_d4amk.js} +0 -0
- /package/dist/web/assets/{ara-i83AydZj.js → ara-BGN8GAYn.js} +0 -0
- /package/dist/web/assets/{asciidoc-DbBA6-sF.js → asciidoc-s_yVuR3t.js} +0 -0
- /package/dist/web/assets/{asm-B5S47Rgr.js → asm-BzQdSvTe.js} +0 -0
- /package/dist/web/assets/{aurora-x-De_IaSKw.js → aurora-x-BJTLETG_.js} +0 -0
- /package/dist/web/assets/{awk-CfyLHXqS.js → awk-BQEGWxvX.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DTLdZ1Si.js → ayu-dark-CpTORmLc.js} +0 -0
- /package/dist/web/assets/{ayu-light-DTOfkzhu.js → ayu-light-D8PVj-e6.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-DsAECCMv.js → ayu-mirage-DyElQHtm.js} +0 -0
- /package/dist/web/assets/{ballerina-CumGp9A7.js → ballerina-C82NkRtj.js} +0 -0
- /package/dist/web/assets/{bat-Ca2SBbYN.js → bat-0X7DtGI-.js} +0 -0
- /package/dist/web/assets/{beancount-CqyXBwwF.js → beancount-Xgvy6YRl.js} +0 -0
- /package/dist/web/assets/{berry-yl-kJr_H.js → berry-CCT2pZcQ.js} +0 -0
- /package/dist/web/assets/{bibtex-DoFFszjV.js → bibtex-CpXgZ9M_.js} +0 -0
- /package/dist/web/assets/{bicep-CWPi4nfK.js → bicep-fkeSA62l.js} +0 -0
- /package/dist/web/assets/{bird2-DXew-ut0.js → bird2-Co7SeAI8.js} +0 -0
- /package/dist/web/assets/{c-clbySDvd.js → c-BpmrTvJh.js} +0 -0
- /package/dist/web/assets/{c3-B65jvSM1.js → c3-BZ_Z0RrR.js} +0 -0
- /package/dist/web/assets/{cadence-Cm8tQTHN.js → cadence-B0GBC8OF.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DXGOSUQc.js → can-open-in-ppm-omBzUBBq.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DLeGslh_.js → catppuccin-frappe-DIt_xR7m.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CR5GeHws.js → catppuccin-latte-Dw40AcqA.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-p-XgjxhP.js → catppuccin-macchiato-BJ7DfVEF.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-B8QOAZLl.js → catppuccin-mocha-NI2lfXzU.js} +0 -0
- /package/dist/web/assets/{clarity-DPulbvKH.js → clarity-F39L0xMJ.js} +0 -0
- /package/dist/web/assets/{clock-D493FU_D.js → clock-CvhF4DJP.js} +0 -0
- /package/dist/web/assets/{clojure-DFuWS20u.js → clojure-DloMkd8K.js} +0 -0
- /package/dist/web/assets/{cmake-DejCqcu4.js → cmake-CwerJun_.js} +0 -0
- /package/dist/web/assets/{code-CLF5x7KK.js → code-DDPoqVri.js} +0 -0
- /package/dist/web/assets/{codeowners-BwAyqWIH.js → codeowners-CQyVz4Qf.js} +0 -0
- /package/dist/web/assets/{codeql-DgSwFjqI.js → codeql-Bb229zuY.js} +0 -0
- /package/dist/web/assets/{common-lisp-CxIVJCtf.js → common-lisp-KMlIwq5I.js} +0 -0
- /package/dist/web/assets/{copy-BApI9v7Z.js → copy-DKn11Pye.js} +0 -0
- /package/dist/web/assets/{coq-DLSjSnmf.js → coq-DHX_3IIo.js} +0 -0
- /package/dist/web/assets/{csharp-C1HHzXBp.js → csharp-CEmCZv9Q.js} +0 -0
- /package/dist/web/assets/{css-VXsMY--y.js → css-CqZqBD05.js} +0 -0
- /package/dist/web/assets/{csv-BEbcJdHz.js → csv-l1g_V5MV.js} +0 -0
- /package/dist/web/assets/{csv-parser-BRtnJCfv.js → csv-parser-CnS0wqr6.js} +0 -0
- /package/dist/web/assets/{cue-BkDnFqUD.js → cue-BrxRmilg.js} +0 -0
- /package/dist/web/assets/{cypher-1cMqojSQ.js → cypher-rZkKwqZc.js} +0 -0
- /package/dist/web/assets/{d-BnYjRXtH.js → d-BALR-QuP.js} +0 -0
- /package/dist/web/assets/{dark-plus-XfbaH7kZ.js → dark-plus-Cj7rVwh-.js} +0 -0
- /package/dist/web/assets/{dart-Ca7s-8hK.js → dart-CWQcdkXk.js} +0 -0
- /package/dist/web/assets/{data-grid-types-BXwlFMzs.js → data-grid-types-CYvF-xK5.js} +0 -0
- /package/dist/web/assets/{database-B9okYLx6.js → database-BH0urvbs.js} +0 -0
- /package/dist/web/assets/{dax-CmuOw-mY.js → dax-BNHD-QBA.js} +0 -0
- /package/dist/web/assets/{desktop-C2gJ6Ovb.js → desktop-CN0IXPrP.js} +0 -0
- /package/dist/web/assets/{diff-BCEo1Ej7.js → diff-BtOXapES.js} +0 -0
- /package/dist/web/assets/{dist-CUPbqNnL.js → dist-DB1Oaqcc.js} +0 -0
- /package/dist/web/assets/{dist-DOlBnxFX.js → dist-Di6G_5jU.js} +0 -0
- /package/dist/web/assets/{docker-DN2jlyAh.js → docker-DNsAo_qO.js} +0 -0
- /package/dist/web/assets/{dotenv-NExMY2kS.js → dotenv-D6Pl6aOU.js} +0 -0
- /package/dist/web/assets/{download-D9UYwY0-.js → download-zCATy4x4.js} +0 -0
- /package/dist/web/assets/{dracula-B9lbE01y.js → dracula-C3m-w12D.js} +0 -0
- /package/dist/web/assets/{dracula-soft-CMBBfKF_.js → dracula-soft-BYqvXm6Z.js} +0 -0
- /package/dist/web/assets/{dream-maker-DEyDwFsd.js → dream-maker-DZAPbPTi.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-D_qP2zS6.js → emacs-lisp-Ao5SW8ay.js} +0 -0
- /package/dist/web/assets/{everforest-dark-DbHa2U-p.js → everforest-dark-uC8QYotQ.js} +0 -0
- /package/dist/web/assets/{everforest-light-BISkJchP.js → everforest-light-CTgsVNMr.js} +0 -0
- /package/dist/web/assets/{fennel-6B22SfJR.js → fennel-WuBw6IR1.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-a8f3_w7a.js → file-exclamation-point-COAwgNGw.js} +0 -0
- /package/dist/web/assets/{fish-CubIIvJD.js → fish-Byy9dusu.js} +0 -0
- /package/dist/web/assets/{fluent-DF55zQ09.js → fluent-CDxb_sgw.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DvOu8jAt.js → fortran-free-form-DKWSTamT.js} +0 -0
- /package/dist/web/assets/{gdscript-DCPxdLpq.js → gdscript-prYHMgtD.js} +0 -0
- /package/dist/web/assets/{gdshader-CTZ381FO.js → gdshader-D-RM-SQk.js} +0 -0
- /package/dist/web/assets/{genie-C_QDtvgU.js → genie-CowkJhej.js} +0 -0
- /package/dist/web/assets/{gherkin-BcvK42tF.js → gherkin-CLFpoVYJ.js} +0 -0
- /package/dist/web/assets/{github-dark-default-CKGVrl3Z.js → github-dark-default-BK_1Qgb8.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-C3dQZ_LC.js → github-dark-dimmed-Cv9lj6f-.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-DW78QukY.js → github-dark-high-contrast-CW5ZgIDh.js} +0 -0
- /package/dist/web/assets/{github-dark-C9afs_4w.js → github-dark-nW2F-RLd.js} +0 -0
- /package/dist/web/assets/{github-light-BnP2dznS.js → github-light-D4PqBHGZ.js} +0 -0
- /package/dist/web/assets/{github-light-default-C2fPZzPG.js → github-light-default-B-cHtJJV.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-BzIuUFR2.js → github-light-high-contrast-Dt0HJq50.js} +0 -0
- /package/dist/web/assets/{gleam-BpeKwEIO.js → gleam-DwSyXJWP.js} +0 -0
- /package/dist/web/assets/{gn-nUb_Mit0.js → gn-Cv5WEzHc.js} +0 -0
- /package/dist/web/assets/{gnuplot-D7I0v20p.js → gnuplot-D7bRdY_M.js} +0 -0
- /package/dist/web/assets/{go-D7Qqska7.js → go-DiRVJ22U.js} +0 -0
- /package/dist/web/assets/{groovy-DS-pne5-.js → groovy-BHAgLTeO.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-C0W01fsq.js → gruvbox-dark-hard-C8QZ3gzY.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Bqo6fQbg.js → gruvbox-dark-medium-Dp1eeid2.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-D-XSM_fu.js → gruvbox-dark-soft-Cwq0dWVj.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-IVAwMJRl.js → gruvbox-light-hard-CLQCq2-w.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-38ATiG7i.js → gruvbox-light-medium-APHsh6Mo.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DnZr3c_0.js → gruvbox-light-soft-CX-fYc64.js} +0 -0
- /package/dist/web/assets/{haskell-uQcywkDH.js → haskell-C4Tw-TDa.js} +0 -0
- /package/dist/web/assets/{haxe-c6bI4_GT.js → haxe-B7fSduTQ.js} +0 -0
- /package/dist/web/assets/{hcl-CQXTiEXq.js → hcl-Bi3ho6yC.js} +0 -0
- /package/dist/web/assets/{hjson-ChETf2bz.js → hjson-BXwae2M1.js} +0 -0
- /package/dist/web/assets/{hlsl-BMC4JGoE.js → hlsl-CRuQLBSa.js} +0 -0
- /package/dist/web/assets/{horizon-BusFlhLW.js → horizon-DtdQ6qjv.js} +0 -0
- /package/dist/web/assets/{horizon-bright-2oD0Mz7W.js → horizon-bright-BXSF_uKq.js} +0 -0
- /package/dist/web/assets/{houston-DdbbjqFg.js → houston-C6ASpUjT.js} +0 -0
- /package/dist/web/assets/{hy-D_SOuqw9.js → hy-BrWBHtHY.js} +0 -0
- /package/dist/web/assets/{image-gallery-CrM5fHKy.js → image-gallery-CGvlv8zR.js} +0 -0
- /package/dist/web/assets/{imba-BxLOHO5I.js → imba-C_JJXckG.js} +0 -0
- /package/dist/web/assets/{ini-ox-CFcg0.js → ini-DEE_vSHQ.js} +0 -0
- /package/dist/web/assets/{java-Bzo8yIal.js → java-CnmpcjcT.js} +0 -0
- /package/dist/web/assets/{javascript-DHuz9VXn.js → javascript-C-M7sa2e.js} +0 -0
- /package/dist/web/assets/{json-0_y80FD5.js → json-BjZ3dAk6.js} +0 -0
- /package/dist/web/assets/{json5-CEQ50pGM.js → json5-BQ2vpqjp.js} +0 -0
- /package/dist/web/assets/{jsonc-CfrLEAKA.js → jsonc-C5MjoWGx.js} +0 -0
- /package/dist/web/assets/{jsonl-fKbrANC2.js → jsonl-JvhrmQJI.js} +0 -0
- /package/dist/web/assets/{jsonnet-DKbkPaQ2.js → jsonnet-C5pdZVuo.js} +0 -0
- /package/dist/web/assets/{jssm-Dxxx1Hqu.js → jssm-Db17uybX.js} +0 -0
- /package/dist/web/assets/{jsx-DlJL-Oj3.js → jsx-tJp2Cyqu.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-CwN4SV1c.js → kanagawa-dragon-BculT_Y1.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-BHltayiE.js → kanagawa-lotus-yQgHUGwU.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-D-KXPjw6.js → kanagawa-wave-CyGoWN4d.js} +0 -0
- /package/dist/web/assets/{kdl-DvtoJdFo.js → kdl-Bn8R1REF.js} +0 -0
- /package/dist/web/assets/{kotlin-CTS4MLcx.js → kotlin-DWughK73.js} +0 -0
- /package/dist/web/assets/{kusto-HtSE_sKm.js → kusto--serPb0x.js} +0 -0
- /package/dist/web/assets/{laserwave-DaXZG9ge.js → laserwave-DfAPJ1tt.js} +0 -0
- /package/dist/web/assets/{lean-BYtGUfWx.js → lean-UAm1HOKl.js} +0 -0
- /package/dist/web/assets/{less-BoxmU_Mk.js → less-Xj6Ka0fF.js} +0 -0
- /package/dist/web/assets/{lib-B1clP4ai.js → lib-DgNcCtj3.js} +0 -0
- /package/dist/web/assets/{libesm-ah-mr-sh.js → libesm-CPh1Av0r.js} +0 -0
- /package/dist/web/assets/{light-plus-Bt5CyUuo.js → light-plus-BfS7gPcj.js} +0 -0
- /package/dist/web/assets/{llvm-CSdk9q9V.js → llvm-DvWiVwvU.js} +0 -0
- /package/dist/web/assets/{log-B-kp_3CL.js → log-Bo5z9bNz.js} +0 -0
- /package/dist/web/assets/{logo-DdIsXFDO.js → logo-CrIjcMZT.js} +0 -0
- /package/dist/web/assets/{luau-DUBpLlzQ.js → luau-C9TsYwNt.js} +0 -0
- /package/dist/web/assets/{make-B3JVjoW_.js → make-GYq4qVbx.js} +0 -0
- /package/dist/web/assets/{markdown-Seon0vHs.js → markdown-p5mNledS.js} +0 -0
- /package/dist/web/assets/{material-theme-Nq-ChX1P.js → material-theme-CWYOVxsP.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CPV4ayXJ.js → material-theme-darker-BDGty7YH.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-0kyjYqEd.js → material-theme-lighter-JbgOgV3n.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-DGpAp5co.js → material-theme-ocean-EMOpLpa2.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-B0AUn0NG.js → material-theme-palenight-CY5hgm7h.js} +0 -0
- /package/dist/web/assets/{matlab-C2waytxs.js → matlab-C-He_NTH.js} +0 -0
- /package/dist/web/assets/{mdx-DAis6R6I.js → mdx-Cr2TR5fj.js} +0 -0
- /package/dist/web/assets/{mermaid-_rnT2YGD.js → mermaid-BYc0-B0U.js} +0 -0
- /package/dist/web/assets/{min-dark-BSSjchUB.js → min-dark-CMJwqJqB.js} +0 -0
- /package/dist/web/assets/{min-light-DomAFQsa.js → min-light-Cx6vFiEX.js} +0 -0
- /package/dist/web/assets/{mipsasm-bBgtvPyG.js → mipsasm-Bjer5HwK.js} +0 -0
- /package/dist/web/assets/{mobile-bottom-sheet-CS3XMwFw.js → mobile-bottom-sheet-BUOJgiaZ.js} +0 -0
- /package/dist/web/assets/{mojo-Cp9BayW0.js → mojo-t90PR-Y2.js} +0 -0
- /package/dist/web/assets/{monokai-CL_9gbmn.js → monokai-dci_PdCm.js} +0 -0
- /package/dist/web/assets/{moonbit-BS_fcr-P.js → moonbit-AAzI-Hd-.js} +0 -0
- /package/dist/web/assets/{move-C79AH45U.js → move-kwBhnhrG.js} +0 -0
- /package/dist/web/assets/{narrat-CJ5s3BDf.js → narrat-CXww2YQL.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-ZHa4JrPJ.js → nextflow-groovy-CJXsuXGB.js} +0 -0
- /package/dist/web/assets/{night-owl-Jkf9zFQK.js → night-owl-_GD204Wr.js} +0 -0
- /package/dist/web/assets/{night-owl-light-DRPeaG5J.js → night-owl-light-DNgkw3kl.js} +0 -0
- /package/dist/web/assets/{nix-D2Jet9DN.js → nix-BgiorKFd.js} +0 -0
- /package/dist/web/assets/{nord-CREVs22j.js → nord-CaBmcxFC.js} +0 -0
- /package/dist/web/assets/{nushell-CAiDi_mR.js → nushell-DUh4UGx1.js} +0 -0
- /package/dist/web/assets/{objective-c-CVkmJkC1.js → objective-c-Cl9KU86S.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DIKDO1Ly.js → objective-cpp-DVkIA8A0.js} +0 -0
- /package/dist/web/assets/{ocaml-R9qtGQhs.js → ocaml-vvoHTak0.js} +0 -0
- /package/dist/web/assets/{odin-DTYNQqVr.js → odin-Cyjw8woZ.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-BSLWUJn5.js → one-dark-pro-B1cviILi.js} +0 -0
- /package/dist/web/assets/{one-light-DbkRXG3t.js → one-light-BPGUJeYI.js} +0 -0
- /package/dist/web/assets/{openscad-BlPcSnmT.js → openscad-BLMdTUU8.js} +0 -0
- /package/dist/web/assets/{pascal-B1Y46ZZB.js → pascal-i5niy3e7.js} +0 -0
- /package/dist/web/assets/{pkl-BhJSJbFv.js → pkl-CuOV7SsU.js} +0 -0
- /package/dist/web/assets/{plastic-BzxsCTpi.js → plastic-Bf_zE5KL.js} +0 -0
- /package/dist/web/assets/{plsql-DBba7dyP.js → plsql-cfqEtMD3.js} +0 -0
- /package/dist/web/assets/{plus-DmjC2iRB.js → plus-BM-Ty0fp.js} +0 -0
- /package/dist/web/assets/{po-BvgLlnNo.js → po-CFJcWgjr.js} +0 -0
- /package/dist/web/assets/{poimandres-DUaI_1JP.js → poimandres-G5YoaIFf.js} +0 -0
- /package/dist/web/assets/{polar-C7uILwJn.js → polar-rcjs3i9R.js} +0 -0
- /package/dist/web/assets/{postcss-BgJlpFdw.js → postcss-CBKY1FTZ.js} +0 -0
- /package/dist/web/assets/{powerquery-Dn1MIpZu.js → powerquery-Db7SXLbO.js} +0 -0
- /package/dist/web/assets/{powershell-Bo-DYzOS.js → powershell-LFy3MUTP.js} +0 -0
- /package/dist/web/assets/{prisma-CTLs5IdT.js → prisma-BE8GR1lH.js} +0 -0
- /package/dist/web/assets/{prolog-QRGun3eG.js → prolog-DOn5lR1e.js} +0 -0
- /package/dist/web/assets/{proto-B7PI7apn.js → proto-wr-TpAK_.js} +0 -0
- /package/dist/web/assets/{puppet-CBq0tLrP.js → puppet-D2Dkv62u.js} +0 -0
- /package/dist/web/assets/{purescript-DFewOQb2.js → purescript-DvgJgPdy.js} +0 -0
- /package/dist/web/assets/{python-iWuxKXvT.js → python-C7TlkHhK.js} +0 -0
- /package/dist/web/assets/{qmldir-cXiW3AsF.js → qmldir-CtZMwhcW.js} +0 -0
- /package/dist/web/assets/{qss-DASwAbBL.js → qss-dNZ2btnl.js} +0 -0
- /package/dist/web/assets/{r-TU2eaIPw.js → r-BlqnNkHS.js} +0 -0
- /package/dist/web/assets/{racket-SkRKqrLY.js → racket-CzRQrGc4.js} +0 -0
- /package/dist/web/assets/{raku-D31lthkZ.js → raku-BwutWhBF.js} +0 -0
- /package/dist/web/assets/{red-CTcQZevb.js → red-BMXMywmL.js} +0 -0
- /package/dist/web/assets/{reg-D8YAsTGf.js → reg-BqOKrIOw.js} +0 -0
- /package/dist/web/assets/{regexp-CD2uvAWx.js → regexp-Bero1BN2.js} +0 -0
- /package/dist/web/assets/{rel-CAaI-1fg.js → rel-BXKWIElA.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CJvkL8hU.js → resolve-theme-IdhSR-gx.js} +0 -0
- /package/dist/web/assets/{riscv-DR-hJVIj.js → riscv-BbbBYJBo.js} +0 -0
- /package/dist/web/assets/{ron-CuUCJ0n4.js → ron-BtVTWW4b.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-Bqz2sBRY.js → rose-pine-dawn-D7lwhUei.js} +0 -0
- /package/dist/web/assets/{rose-pine-aFHxW48i.js → rose-pine-hvkvQzvU.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-wqyh0an4.js → rose-pine-moon-CoHDmsIw.js} +0 -0
- /package/dist/web/assets/{rosmsg-CI1ywimO.js → rosmsg-CdaZFgI8.js} +0 -0
- /package/dist/web/assets/{run-in-terminal-CGW6o37_.js → run-in-terminal-CyS5Itj-.js} +0 -0
- /package/dist/web/assets/{rust-p7Imk4G_.js → rust-Dy2qsphD.js} +0 -0
- /package/dist/web/assets/{sass-C8JfaqCV.js → sass-Bz7NVxDb.js} +0 -0
- /package/dist/web/assets/{save-DRPrDfTt.js → save-C4z1p51e.js} +0 -0
- /package/dist/web/assets/{scala-DNxCW1By.js → scala-sXSwsNyq.js} +0 -0
- /package/dist/web/assets/{scheme-BYuUPJoL.js → scheme-DS7TUbN7.js} +0 -0
- /package/dist/web/assets/{sdbl-DbPeJPUr.js → sdbl-iISFvIep.js} +0 -0
- /package/dist/web/assets/{send-to-chat-YXHdAyLG.js → send-to-chat-BcO39BDR.js} +0 -0
- /package/dist/web/assets/{shellscript-DMiHlR3q.js → shellscript-BkIl4ApC.js} +0 -0
- /package/dist/web/assets/{shield-check-8GObuUAn.js → shield-check-C5laObJv.js} +0 -0
- /package/dist/web/assets/{shield-off-egKAZEwR.js → shield-off-DULFtyLS.js} +0 -0
- /package/dist/web/assets/{slack-dark-C4xGcQXI.js → slack-dark-B1eg0_m2.js} +0 -0
- /package/dist/web/assets/{slack-ochin-BzgRt_wm.js → slack-ochin-mdkc1ldu.js} +0 -0
- /package/dist/web/assets/{smalltalk-LDeL9PYH.js → smalltalk-CORtd03t.js} +0 -0
- /package/dist/web/assets/{snazzy-light-CXE7a5j_.js → snazzy-light-Bc6Q2G89.js} +0 -0
- /package/dist/web/assets/{solarized-dark-DgUhY74x.js → solarized-dark-CF83zRXM.js} +0 -0
- /package/dist/web/assets/{solarized-light-DD9C-H-9.js → solarized-light-BoKQZaYU.js} +0 -0
- /package/dist/web/assets/{solidity-CAbDB4D4.js → solidity-DOgi1N4w.js} +0 -0
- /package/dist/web/assets/{sparkles-n1hzh65o.js → sparkles-Dqsf3hq6.js} +0 -0
- /package/dist/web/assets/{splunk-kQREPfIc.js → splunk-BXfw5ZLy.js} +0 -0
- /package/dist/web/assets/{sql-CBzeNAD8.js → sql-BcVs-eX4.js} +0 -0
- /package/dist/web/assets/{ssh-config-L1euxrsM.js → ssh-config-CLCtNkkd.js} +0 -0
- /package/dist/web/assets/{stylus-DvBnYniP.js → stylus-DdP992aO.js} +0 -0
- /package/dist/web/assets/{swift-DF5NDQp6.js → swift-BnGb6Lgc.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DbBhB_Hq.js → synthwave-84-cg9264l9.js} +0 -0
- /package/dist/web/assets/{system-verilog-D8s2ZW_k.js → system-verilog-GbHpszs3.js} +0 -0
- /package/dist/web/assets/{systemd-a5-XZJsG.js → systemd-Dz-bNsDQ.js} +0 -0
- /package/dist/web/assets/{table-BX2ZrJfM.js → table-CHmxuh6M.js} +0 -0
- /package/dist/web/assets/{talonscript-BLa0URTW.js → talonscript-CFIidoBA.js} +0 -0
- /package/dist/web/assets/{tasl-DIoV4XcC.js → tasl-Cu-TRTKx.js} +0 -0
- /package/dist/web/assets/{tcl-ztYAdXfQ.js → tcl-DwCHCB1K.js} +0 -0
- /package/dist/web/assets/{terraform-DvpCCiOZ.js → terraform-BzZw4T1z.js} +0 -0
- /package/dist/web/assets/{tokyo-night-BkHlYUv0.js → tokyo-night-RKsj2-tv.js} +0 -0
- /package/dist/web/assets/{toml-acIrxos6.js → toml-CirbOgDP.js} +0 -0
- /package/dist/web/assets/{tsv-BJB5EVCT.js → tsv-CraRcWdU.js} +0 -0
- /package/dist/web/assets/{tsx-B1MmXGT3.js → tsx-CjFg0lgw.js} +0 -0
- /package/dist/web/assets/{turtle-DFceTiqd.js → turtle-B-WBIOa6.js} +0 -0
- /package/dist/web/assets/{typescript-Dz9wU5Y1.js → typescript-MpLarYn1.js} +0 -0
- /package/dist/web/assets/{typespec-CPuYZijj.js → typespec-B7QlHMZ_.js} +0 -0
- /package/dist/web/assets/{typst-CfsCIYv8.js → typst-CcHSKFZj.js} +0 -0
- /package/dist/web/assets/{use-blob-url-BmlYYZ8L.js → use-blob-url-BKyVxOZw.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-B8FA4kQf.js → use-is-mobile-BXGVvczu.js} +0 -0
- /package/dist/web/assets/{v-CGo0jcXA.js → v-xTGhWZuD.js} +0 -0
- /package/dist/web/assets/{vala-AsSR0hrb.js → vala-DwebjH3H.js} +0 -0
- /package/dist/web/assets/{vb-C7tvbXv1.js → vb-BxJT2RAy.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-C1diXHJl.js → vendor-xterm-CU6zIYMB.js} +0 -0
- /package/dist/web/assets/{verilog--a33ND-p.js → verilog-CvV6RHPD.js} +0 -0
- /package/dist/web/assets/{vesper-B_Z0h4pn.js → vesper-CY3qd7E_.js} +0 -0
- /package/dist/web/assets/{vhdl-CWL5dZPN.js → vhdl-B9ZX-sUL.js} +0 -0
- /package/dist/web/assets/{viml-BglmT14Y.js → viml-Y42h_ExQ.js} +0 -0
- /package/dist/web/assets/{vitesse-black-RKFsSqQI.js → vitesse-black-DkXP6Sv-.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-D7zDWNmc.js → vitesse-dark-B9CkIB6r.js} +0 -0
- /package/dist/web/assets/{vitesse-light-MAp87W5b.js → vitesse-light-C7DHrJCK.js} +0 -0
- /package/dist/web/assets/{vyper-CgCBYg1G.js → vyper-Cnd9s5j-.js} +0 -0
- /package/dist/web/assets/{wasm-Bwl4txwp.js → wasm-DQE71XTB.js} +0 -0
- /package/dist/web/assets/{wasm-BOG7ZeTT.js → wasm-iWe7Fzjc.js} +0 -0
- /package/dist/web/assets/{wenyan-DpShPKXZ.js → wenyan-Bc5Pnm9l.js} +0 -0
- /package/dist/web/assets/{wgsl-BakcCblK.js → wgsl-D-YUAWkP.js} +0 -0
- /package/dist/web/assets/{wikitext-NroRWVAO.js → wikitext-DYQDUYf2.js} +0 -0
- /package/dist/web/assets/{wit-B5CM2pg6.js → wit-P6u7mfwy.js} +0 -0
- /package/dist/web/assets/{wolfram-7MTFBJpR.js → wolfram-6SXLUqao.js} +0 -0
- /package/dist/web/assets/{yaml-Bwr01G2K.js → yaml-CQiMLVcZ.js} +0 -0
- /package/dist/web/assets/{zenscript-Dj8GATvi.js → zenscript-DFMywo_P.js} +0 -0
- /package/dist/web/assets/{zig-LAE-fbS-.js → zig-CtAjaIiF.js} +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-IO parser for the tagged, tab-separated lines one Windows tick returns:
|
|
3
|
+
*
|
|
4
|
+
* P<TAB>pid<TAB>ppid<TAB>Name<TAB>KernelModeTime<TAB>UserModeTime<TAB>WorkingSetSize<TAB>CreationDateUtcTicks<TAB>ReadTransferCount<TAB>WriteTransferCount
|
|
5
|
+
* C<TAB>pid<TAB>CreationDateUtcTicks<TAB>CommandLine (every 30 s only)
|
|
6
|
+
* D… / N… (disk/net counters)
|
|
7
|
+
* G… / M… (per-process GPU)
|
|
8
|
+
* __ERR__ <message> (a failed block)
|
|
9
|
+
*
|
|
10
|
+
* `CommandLine` is always the LAST field and may itself contain tabs, so rows
|
|
11
|
+
* are split with a field limit — a naive `split("\t")` corrupts them.
|
|
12
|
+
*
|
|
13
|
+
* The two transfer counts are appended AFTER the creation ticks so a `P` row
|
|
14
|
+
* from an older shape (or a host where the properties are empty) still parses,
|
|
15
|
+
* it just reports no disk figures.
|
|
16
|
+
*/
|
|
17
|
+
import { parsePerfRawDiskNet } from "./disk-net-collector-windows.ts";
|
|
18
|
+
import { parseGpuLines, type ParsedGpuLines } from "./gpu-process-usage-windows.ts";
|
|
19
|
+
import type { CounterSample } from "./rate-delta.ts";
|
|
20
|
+
import { stripExecutableExtension, type RawProcessRow } from "./process-collector-types.ts";
|
|
21
|
+
|
|
22
|
+
export interface WindowsCommandLine {
|
|
23
|
+
pid: number;
|
|
24
|
+
startedAt: number;
|
|
25
|
+
command: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ParsedWindowsTick {
|
|
29
|
+
/** PID 0 (System Idle Process) is excluded — its kernel time is idle time and
|
|
30
|
+
* inflates a naive CPU roll-up to ~99 %. */
|
|
31
|
+
processes: RawProcessRow[];
|
|
32
|
+
/** Null when the tick carried no `C` section. */
|
|
33
|
+
commands: WindowsCommandLine[] | null;
|
|
34
|
+
disk: CounterSample | null;
|
|
35
|
+
net: CounterSample | null;
|
|
36
|
+
/** Raw per-process GPU samples; the collector turns them into rates. */
|
|
37
|
+
gpu: ParsedGpuLines;
|
|
38
|
+
errors: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** .NET ticks (100 ns since 0001-01-01) → Unix epoch ms. Input MUST already be
|
|
42
|
+
* UTC — the query calls `.ToUniversalTime()` because only .NET on that machine
|
|
43
|
+
* knows the DST rule in force when the process started. BigInt because the
|
|
44
|
+
* tick value exceeds 2^53. */
|
|
45
|
+
export function ticksToEpochMs(ticks: string): number {
|
|
46
|
+
if (!/^\d+$/.test(ticks)) return 0;
|
|
47
|
+
const EPOCH_TICKS = 621355968000000000n;
|
|
48
|
+
const ms = (BigInt(ticks) - EPOCH_TICKS) / 10000n;
|
|
49
|
+
return ms > 0n ? Number(ms) : 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Split into at most `limit` fields; the remainder stays glued to the last one. */
|
|
53
|
+
export function splitLimited(line: string, limit: number): string[] {
|
|
54
|
+
const out: string[] = [];
|
|
55
|
+
let start = 0;
|
|
56
|
+
while (out.length < limit - 1) {
|
|
57
|
+
const i = line.indexOf("\t", start);
|
|
58
|
+
if (i < 0) break;
|
|
59
|
+
out.push(line.slice(start, i));
|
|
60
|
+
start = i + 1;
|
|
61
|
+
}
|
|
62
|
+
out.push(line.slice(start));
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const HUNDRED_NS_PER_MS = 10_000;
|
|
67
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
68
|
+
|
|
69
|
+
/** An empty (property absent / access denied) or non-numeric counter field is
|
|
70
|
+
* "the OS did not say", which must stay `undefined` rather than become 0. */
|
|
71
|
+
function optionalCount(field: string | undefined): number | undefined {
|
|
72
|
+
const text = (field ?? "").trim();
|
|
73
|
+
if (!/^\d+$/.test(text)) return undefined;
|
|
74
|
+
const n = Number(text);
|
|
75
|
+
return Number.isFinite(n) ? n : undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseProcessLine(f: string[]): RawProcessRow | null {
|
|
79
|
+
const pid = Number(f[1]);
|
|
80
|
+
const ppid = Number(f[2]);
|
|
81
|
+
const kernel = Number(f[4]);
|
|
82
|
+
const user = Number(f[5]);
|
|
83
|
+
const ws = Number(f[6]);
|
|
84
|
+
if (![pid, ppid, kernel, user, ws].every(Number.isFinite)) return null;
|
|
85
|
+
if (pid === 0) return null;
|
|
86
|
+
return {
|
|
87
|
+
pid,
|
|
88
|
+
ppid: ppid >= 0 ? ppid : -1,
|
|
89
|
+
name: stripExecutableExtension((f[3] ?? "").trim()) || String(pid),
|
|
90
|
+
command: null,
|
|
91
|
+
cpuMs: (kernel + user) / HUNDRED_NS_PER_MS,
|
|
92
|
+
ramMB: ws / BYTES_PER_MB,
|
|
93
|
+
startedAt: ticksToEpochMs((f[7] ?? "").trim()),
|
|
94
|
+
diskReadBytes: optionalCount(f[8]),
|
|
95
|
+
diskWriteBytes: optionalCount(f[9]),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `CommandLine` arrives base64(UTF-8) because argv is attacker-controlled and
|
|
100
|
+
* the transport is line-framed. Anything that is not valid base64 is dropped —
|
|
101
|
+
* a raw string here means the field was tampered with, not that it is a command. */
|
|
102
|
+
export function decodeCommandLine(b64: string): string {
|
|
103
|
+
if (!b64 || !/^[A-Za-z0-9+/]+={0,2}$/.test(b64)) return "";
|
|
104
|
+
try {
|
|
105
|
+
return Buffer.from(b64, "base64").toString("utf-8").trim();
|
|
106
|
+
} catch {
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function parseWindowsTick(text: string): ParsedWindowsTick {
|
|
112
|
+
const processes: RawProcessRow[] = [];
|
|
113
|
+
let commands: WindowsCommandLine[] | null = null;
|
|
114
|
+
const counterLines: string[] = [];
|
|
115
|
+
const gpuLines: string[] = [];
|
|
116
|
+
const errors: string[] = [];
|
|
117
|
+
|
|
118
|
+
for (const raw of text.split("\n")) {
|
|
119
|
+
const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
|
|
120
|
+
if (!line) continue;
|
|
121
|
+
switch (line.slice(0, 2)) {
|
|
122
|
+
case "P\t": {
|
|
123
|
+
const row = parseProcessLine(splitLimited(line, 10));
|
|
124
|
+
if (row) processes.push(row);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "C\t": {
|
|
128
|
+
const f = splitLimited(line, 4);
|
|
129
|
+
const pid = Number(f[1]);
|
|
130
|
+
// A `C` section that exists but is all-null still counts as fetched.
|
|
131
|
+
commands ??= [];
|
|
132
|
+
if (!Number.isFinite(pid) || f.length < 4) break;
|
|
133
|
+
const command = decodeCommandLine((f[3] ?? "").trim());
|
|
134
|
+
if (command) commands.push({ pid, startedAt: ticksToEpochMs((f[2] ?? "").trim()), command });
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "D\t":
|
|
138
|
+
case "N\t":
|
|
139
|
+
counterLines.push(line);
|
|
140
|
+
break;
|
|
141
|
+
case "G\t":
|
|
142
|
+
case "M\t":
|
|
143
|
+
gpuLines.push(line);
|
|
144
|
+
break;
|
|
145
|
+
default:
|
|
146
|
+
if (line.startsWith("__ERR__")) errors.push(line.slice("__ERR__".length).trim());
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const { disk, net } = parsePerfRawDiskNet(counterLines);
|
|
152
|
+
return { processes, commands, disk, net, gpu: parseGpuLines(gpuLines), errors };
|
|
153
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows process collector: one `Win32_Process` + disk + net round trip per
|
|
3
|
+
* tick through the long-lived PowerShell session (measured 172-206 ms), plus
|
|
4
|
+
* the expensive `CommandLine` pass only every 30 s.
|
|
5
|
+
*
|
|
6
|
+
* `Get-Process` is not used at all: unelevated it silently returns null CPU and
|
|
7
|
+
* start time for ~45 % of rows. `Win32_Process` with this property list has
|
|
8
|
+
* zero nulls. All scripts are static strings — no user input ever reaches them.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProcessCollection, ProcessCollector, RawProcessRow } from "./process-collector-types.ts";
|
|
11
|
+
import { createStickyColumns } from "./process-collector-types.ts";
|
|
12
|
+
import { PowerShellSession, PsSessionDisabledError, PS_DISABLED_WARNING } from "./powershell-session.ts";
|
|
13
|
+
import { parseWindowsTick, type WindowsCommandLine } from "./process-collector-windows-parse.ts";
|
|
14
|
+
import { computeGpuPercents, memBytesToMB, type GpuUsageState } from "./gpu-process-usage-windows.ts";
|
|
15
|
+
|
|
16
|
+
/** `.ToUniversalTime()` here, not in JS: only .NET on this machine knows which
|
|
17
|
+
* DST rule applied at the instant each process started. */
|
|
18
|
+
const CREATION_UTC_TICKS = "$($_.CreationDate.ToUniversalTime().Ticks)";
|
|
19
|
+
|
|
20
|
+
export const WINDOWS_TICK_SCRIPT = [
|
|
21
|
+
"Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,Name,KernelModeTime,UserModeTime,WorkingSetSize,CreationDate,ReadTransferCount,WriteTransferCount | " +
|
|
22
|
+
`ForEach-Object { "P\`t$($_.ProcessId)\`t$($_.ParentProcessId)\`t$($_.Name)\`t$($_.KernelModeTime)\`t$($_.UserModeTime)\`t$($_.WorkingSetSize)\`t${CREATION_UTC_TICKS}\`t$($_.ReadTransferCount)\`t$($_.WriteTransferCount)" }`,
|
|
23
|
+
"Get-CimInstance Win32_PerfRawData_PerfDisk_PhysicalDisk -Filter \"Name='_Total'\" | " +
|
|
24
|
+
'ForEach-Object { "D`t$($_.DiskReadBytesPerSec)`t$($_.DiskWriteBytesPerSec)`t$($_.Timestamp_PerfTime)`t$($_.Frequency_PerfTime)" }',
|
|
25
|
+
"Get-CimInstance Win32_PerfRawData_Tcpip_NetworkInterface | " +
|
|
26
|
+
'ForEach-Object { "N`t$($_.Name)`t$($_.BytesReceivedPersec)`t$($_.BytesSentPersec)`t$($_.Timestamp_Sys100NS)" }',
|
|
27
|
+
// 505 engine instances on a dev box, of which ~50 are busy: filtering in
|
|
28
|
+
// PowerShell keeps the reply an order of magnitude smaller than the class.
|
|
29
|
+
"Get-CimInstance Win32_PerfRawData_GPUPerformanceCounters_GPUEngine | Where-Object { $_.UtilizationPercentage -gt 0 } | " +
|
|
30
|
+
'ForEach-Object { "G`t$($_.Name)`t$($_.UtilizationPercentage)`t$($_.Timestamp_Sys100NS)" }',
|
|
31
|
+
"Get-CimInstance Win32_PerfRawData_GPUPerformanceCounters_GPUProcessMemory | " +
|
|
32
|
+
'ForEach-Object { "M`t$($_.Name)`t$($_.DedicatedUsage)" }',
|
|
33
|
+
].join("\n");
|
|
34
|
+
|
|
35
|
+
/** +10 ms and +95 KB per fetch, and ~half the rows are access-denied (null)
|
|
36
|
+
* unelevated — so it runs on its own slow cadence and merges by identity.
|
|
37
|
+
* `CommandLine` is the only attacker-controlled field in the whole round trip
|
|
38
|
+
* (any local process picks its own argv), so it travels base64-encoded: raw,
|
|
39
|
+
* an embedded CR/LF would let it forge a `P` row for another pid or inject an
|
|
40
|
+
* `__END_<id>__` line that truncates the reply. */
|
|
41
|
+
export const WINDOWS_COMMANDLINE_SCRIPT =
|
|
42
|
+
"Get-CimInstance Win32_Process -Property ProcessId,CommandLine,CreationDate | " +
|
|
43
|
+
`ForEach-Object { "C\`t$($_.ProcessId)\`t${CREATION_UTC_TICKS}\`t$([Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes([string]$_.CommandLine)))" }`;
|
|
44
|
+
|
|
45
|
+
export const COMMANDLINE_REFRESH_MS = 30_000;
|
|
46
|
+
|
|
47
|
+
export interface WindowsProcessCollectorOptions {
|
|
48
|
+
session?: PowerShellSession;
|
|
49
|
+
now?: () => number;
|
|
50
|
+
commandRefreshMs?: number;
|
|
51
|
+
/** Seam for the one-shot startup timing line, so tests stay silent. */
|
|
52
|
+
log?: (message: string) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface WindowsProcessCollector extends ProcessCollector {
|
|
56
|
+
readonly session: PowerShellSession;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createWindowsProcessCollector(opts: WindowsProcessCollectorOptions = {}): WindowsProcessCollector {
|
|
60
|
+
const session = opts.session ?? new PowerShellSession();
|
|
61
|
+
const now = opts.now ?? Date.now;
|
|
62
|
+
const refreshMs = opts.commandRefreshMs ?? COMMANDLINE_REFRESH_MS;
|
|
63
|
+
const log = opts.log ?? ((m: string) => console.log(m));
|
|
64
|
+
const observeColumns = createStickyColumns();
|
|
65
|
+
let commandByPid = new Map<number, WindowsCommandLine>();
|
|
66
|
+
let lastCommandFetchAt = Number.NEGATIVE_INFINITY;
|
|
67
|
+
let gpuState: GpuUsageState | null = null;
|
|
68
|
+
let logged = false;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
session,
|
|
72
|
+
stop: () => session.stop(),
|
|
73
|
+
async collect(): Promise<ProcessCollection> {
|
|
74
|
+
if (session.isDisabled()) return { rows: [], columns: observeColumns({}), warnings: [PS_DISABLED_WARNING] };
|
|
75
|
+
|
|
76
|
+
const wantCommands = now() - lastCommandFetchAt >= refreshMs;
|
|
77
|
+
const script = wantCommands ? `${WINDOWS_TICK_SCRIPT}\n${WINDOWS_COMMANDLINE_SCRIPT}` : WINDOWS_TICK_SCRIPT;
|
|
78
|
+
const startedRequestAt = now();
|
|
79
|
+
let text: string;
|
|
80
|
+
try {
|
|
81
|
+
text = await session.request(script);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
if (e instanceof PsSessionDisabledError) {
|
|
84
|
+
return { rows: [], columns: observeColumns({}), warnings: [PS_DISABLED_WARNING] };
|
|
85
|
+
}
|
|
86
|
+
throw e;
|
|
87
|
+
}
|
|
88
|
+
if (!logged) {
|
|
89
|
+
logged = true;
|
|
90
|
+
// One line, once: the round trip now also carries the two GPU classes,
|
|
91
|
+
// and the 2 s tick budget is the thing that would break first.
|
|
92
|
+
log(`[system-metrics] windows tick round trip (incl. GPU pass): ${now() - startedRequestAt} ms`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const parsed = parseWindowsTick(text);
|
|
96
|
+
if (parsed.commands) {
|
|
97
|
+
commandByPid = new Map(parsed.commands.map((c) => [c.pid, c]));
|
|
98
|
+
lastCommandFetchAt = now();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// First row for a pid wins, like the guard maps: a forged duplicate must
|
|
102
|
+
// not decide which start time the GPU delta key is anchored to.
|
|
103
|
+
const startedAtByPid = new Map<number, number>();
|
|
104
|
+
for (const p of parsed.processes) if (!startedAtByPid.has(p.pid)) startedAtByPid.set(p.pid, p.startedAt);
|
|
105
|
+
const gpu = computeGpuPercents(gpuState, parsed.gpu.engines, (pid) => startedAtByPid.get(pid));
|
|
106
|
+
gpuState = gpu.next;
|
|
107
|
+
|
|
108
|
+
const rows: RawProcessRow[] = parsed.processes.map((r) => ({
|
|
109
|
+
...r,
|
|
110
|
+
command: mergeCommand(r, commandByPid.get(r.pid)),
|
|
111
|
+
// Windows reports GPU per pid for the whole machine, so a pid with no
|
|
112
|
+
// engine instance is measured-and-idle (0), not unmeasurable.
|
|
113
|
+
gpuPct: parsed.gpu.present ? gpu.pctByPid.get(r.pid) ?? 0 : undefined,
|
|
114
|
+
gpuMemMB: parsed.gpu.present ? memBytesToMB(parsed.gpu.memBytesByPid.get(r.pid) ?? 0) : undefined,
|
|
115
|
+
}));
|
|
116
|
+
return {
|
|
117
|
+
rows,
|
|
118
|
+
disk: parsed.disk,
|
|
119
|
+
net: parsed.net,
|
|
120
|
+
columns: observeColumns({
|
|
121
|
+
disk: rows.some((r) => r.diskReadBytes !== undefined),
|
|
122
|
+
gpu: parsed.gpu.present,
|
|
123
|
+
}),
|
|
124
|
+
warnings: parsed.errors.map((e) => `PowerShell: ${e}`),
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** A pid new since the last 30 s fetch — or recycled since — has no command
|
|
131
|
+
* yet; the row falls back to its name until the next fetch. */
|
|
132
|
+
export function mergeCommand(row: RawProcessRow, cached: WindowsCommandLine | undefined): string | null {
|
|
133
|
+
if (!cached) return null;
|
|
134
|
+
if (cached.startedAt && row.startedAt && cached.startedAt !== row.startedAt) return null;
|
|
135
|
+
return cached.command;
|
|
136
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instantaneous per-process CPU% from cumulative CPU time, shared by all OSes.
|
|
3
|
+
*
|
|
4
|
+
* Every OS's "CPU%" column is a lifetime average (`ps %cpu`, `/proc` utime over
|
|
5
|
+
* elapsed) — exactly why the old sparkline was a flat line. Machine-normalised
|
|
6
|
+
* like Task Manager: `deltaCpuMs / (wallMs × coreCount) × 100`, so a busy single
|
|
7
|
+
* thread on a 20-core box reads 5 % and group roll-ups sum to roughly the
|
|
8
|
+
* system total.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface CpuTimeSample {
|
|
12
|
+
/** `${pid}:${startedAt}` — the same pid with a new start time is a new
|
|
13
|
+
* process and gets a fresh baseline. `${pid}:0` when the start is unknown,
|
|
14
|
+
* an accepted hole that mis-attributes at most one tick. */
|
|
15
|
+
key: string;
|
|
16
|
+
cpuMs: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CpuDeltaState {
|
|
20
|
+
at: number;
|
|
21
|
+
byKey: Map<string, number>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function cpuSampleKey(pid: number, startedAt: number): string {
|
|
25
|
+
return `${pid}:${startedAt}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const round1 = (n: number): number => Math.round(n * 10) / 10;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Unknown key → 0 (a first observation is never a guess). Counter going
|
|
32
|
+
* backwards → 0 and the baseline is reset. Dead keys are dropped from `next`
|
|
33
|
+
* every tick so a long session cannot grow the map without bound.
|
|
34
|
+
*/
|
|
35
|
+
export function computeCpuPercents(
|
|
36
|
+
prev: CpuDeltaState | null,
|
|
37
|
+
samples: readonly CpuTimeSample[],
|
|
38
|
+
now: number,
|
|
39
|
+
coreCount: number,
|
|
40
|
+
): { percentByKey: Map<string, number>; next: CpuDeltaState } {
|
|
41
|
+
const percentByKey = new Map<string, number>();
|
|
42
|
+
const nextByKey = new Map<string, number>();
|
|
43
|
+
const wallMs = prev ? now - prev.at : 0;
|
|
44
|
+
const cores = Math.max(1, coreCount);
|
|
45
|
+
const usable = prev !== null && wallMs > 0;
|
|
46
|
+
|
|
47
|
+
for (const s of samples) {
|
|
48
|
+
nextByKey.set(s.key, s.cpuMs);
|
|
49
|
+
let pct = 0;
|
|
50
|
+
if (usable) {
|
|
51
|
+
const before = prev!.byKey.get(s.key);
|
|
52
|
+
if (before !== undefined) {
|
|
53
|
+
const delta = s.cpuMs - before;
|
|
54
|
+
if (delta >= 0) pct = round1(Math.min(100, (delta / (wallMs * cores)) * 100));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
percentByKey.set(s.key, pct);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { percentByKey, next: { at: now, byKey: nextByKey } };
|
|
61
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App grouping: roll every process up to its nearest ancestor below an OS
|
|
3
|
+
* "shell/service boundary", so Chrome is one row with 40 children and a
|
|
4
|
+
* terminal is one row with its shells. Exe-name grouping survives only as the
|
|
5
|
+
* bucket for orphans — it would merge every unrelated `node` on the machine
|
|
6
|
+
* into one row, which is worse than Task Manager.
|
|
7
|
+
*/
|
|
8
|
+
import type { MetricsPlatform, ProcessGroup, ProcessInfo } from "../../types/system-metrics.ts";
|
|
9
|
+
|
|
10
|
+
/** Names are extension-free per the contract, so one literal matches every OS. */
|
|
11
|
+
const WIN32_BOUNDARY_NAMES: ReadonlySet<string> = new Set([
|
|
12
|
+
"services", "svchost", "explorer", "wininit", "winlogon", "csrss", "userinit", "smss",
|
|
13
|
+
]);
|
|
14
|
+
const POSIX_BOUNDARY_NAMES: ReadonlySet<string> = new Set(["launchd", "systemd", "init"]);
|
|
15
|
+
|
|
16
|
+
const round1 = (n: number): number => Math.round(n * 10) / 10;
|
|
17
|
+
|
|
18
|
+
/** The optional per-process columns, which roll up by summation. */
|
|
19
|
+
type OptionalMetric = "diskReadBps" | "diskWriteBps" | "gpuPct" | "gpuMemMB" | "netInBps" | "netOutBps";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Sum over the members that HAVE a value; `undefined` when none does, so a
|
|
23
|
+
* group on a host that cannot measure a column stays "—" instead of claiming a
|
|
24
|
+
* confident 0. A member without a value contributes nothing rather than
|
|
25
|
+
* suppressing its siblings' figures — on Linux that is the common case (an
|
|
26
|
+
* access-denied `/proc/<pid>/io` next to readable ones).
|
|
27
|
+
*/
|
|
28
|
+
function sumOptional(procs: readonly ProcessInfo[], field: OptionalMetric): number | undefined {
|
|
29
|
+
let total = 0;
|
|
30
|
+
let measured = false;
|
|
31
|
+
for (const p of procs) {
|
|
32
|
+
const v = p[field];
|
|
33
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
34
|
+
total += v;
|
|
35
|
+
measured = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!measured) return undefined;
|
|
39
|
+
// A group's engine busy is still a share of one GPU, so it cannot exceed 100 %.
|
|
40
|
+
return round1(field === "gpuPct" ? Math.min(100, total) : total);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isBoundary(p: ProcessInfo, platform: MetricsPlatform): boolean {
|
|
44
|
+
const name = p.name.toLowerCase();
|
|
45
|
+
if (platform === "win32") return p.pid === 0 || p.pid === 4 || WIN32_BOUNDARY_NAMES.has(name);
|
|
46
|
+
return p.pid <= 1 || POSIX_BOUNDARY_NAMES.has(name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Climb `ppid` until the parent is absent, is a boundary, or started AFTER the
|
|
51
|
+
* child (Windows never clears a dead parent's ppid, so a recycled pid would
|
|
52
|
+
* otherwise adopt an unrelated subtree). Returns the chain from `p` upward.
|
|
53
|
+
*/
|
|
54
|
+
function ancestorChain(p: ProcessInfo, byPid: ReadonlyMap<number, ProcessInfo>, platform: MetricsPlatform): ProcessInfo[] {
|
|
55
|
+
const chain = [p];
|
|
56
|
+
const visited = new Set<number>([p.pid]);
|
|
57
|
+
let cur = p;
|
|
58
|
+
while (true) {
|
|
59
|
+
const parent = byPid.get(cur.ppid);
|
|
60
|
+
if (!parent || visited.has(parent.pid) || isBoundary(parent, platform)) break;
|
|
61
|
+
if (parent.startedAt && cur.startedAt && parent.startedAt > cur.startedAt) break;
|
|
62
|
+
visited.add(parent.pid);
|
|
63
|
+
chain.push(parent);
|
|
64
|
+
cur = parent;
|
|
65
|
+
}
|
|
66
|
+
return chain;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The PPM subtree always splits out of whatever shell chain launched it: on a
|
|
71
|
+
* dev host that chain is explorer → WindowsTerminal → bun → server, and without
|
|
72
|
+
* this rule PPM would roll up under "WindowsTerminal" and hand the UI a tree-kill
|
|
73
|
+
* button whose blast radius includes the server. The topmost PPM root in the
|
|
74
|
+
* chain wins so supervisor + server + children form ONE group.
|
|
75
|
+
*/
|
|
76
|
+
function resolveRoot(chain: ProcessInfo[], ppmRoots: ReadonlySet<number>): ProcessInfo {
|
|
77
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
78
|
+
if (ppmRoots.has(chain[i]!.pid)) return chain[i]!;
|
|
79
|
+
}
|
|
80
|
+
return chain[chain.length - 1]!;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param ppmRoots PPM's own roots (server + supervisor/parent): grouping boundaries.
|
|
85
|
+
* @param selfPid This server's pid. Two PPM instances can share a machine (dev
|
|
86
|
+
* beside prod), so a PPM group is labelled by which one it is.
|
|
87
|
+
*/
|
|
88
|
+
export function groupProcesses(
|
|
89
|
+
procs: readonly ProcessInfo[],
|
|
90
|
+
platform: MetricsPlatform,
|
|
91
|
+
ppmRoots: ReadonlySet<number> = new Set(),
|
|
92
|
+
selfPid: number = -1,
|
|
93
|
+
): ProcessGroup[] {
|
|
94
|
+
const byPid = new Map<number, ProcessInfo>();
|
|
95
|
+
for (const p of procs) byPid.set(p.pid, p);
|
|
96
|
+
|
|
97
|
+
const members = new Map<string, { label: string; rootPid: number | null; procs: ProcessInfo[] }>();
|
|
98
|
+
for (const p of procs) {
|
|
99
|
+
let key: string;
|
|
100
|
+
let label: string;
|
|
101
|
+
let rootPid: number | null;
|
|
102
|
+
if (p.ppid === -1) {
|
|
103
|
+
key = `exe:${p.name.toLowerCase()}`;
|
|
104
|
+
label = p.name;
|
|
105
|
+
rootPid = null;
|
|
106
|
+
} else {
|
|
107
|
+
const root = resolveRoot(ancestorChain(p, byPid, platform), ppmRoots);
|
|
108
|
+
key = `root:${root.pid}`;
|
|
109
|
+
label = ppmRoots.has(root.pid) ? `PPM (pid ${root.pid})` : root.name;
|
|
110
|
+
rootPid = root.pid;
|
|
111
|
+
}
|
|
112
|
+
const g = members.get(key);
|
|
113
|
+
if (g) g.procs.push(p);
|
|
114
|
+
else members.set(key, { label, rootPid, procs: [p] });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const groups: ProcessGroup[] = [];
|
|
118
|
+
for (const [key, g] of members) {
|
|
119
|
+
if (g.rootPid !== null && ppmRoots.has(g.rootPid) && g.procs.some((p) => p.pid === selfPid)) {
|
|
120
|
+
g.label = "PPM (this server)";
|
|
121
|
+
}
|
|
122
|
+
g.procs.sort((a, b) => b.cpu - a.cpu || b.ramMB - a.ramMB || a.pid - b.pid);
|
|
123
|
+
groups.push({
|
|
124
|
+
key,
|
|
125
|
+
label: g.label,
|
|
126
|
+
rootPid: g.rootPid,
|
|
127
|
+
cpu: round1(g.procs.reduce((s, p) => s + p.cpu, 0)),
|
|
128
|
+
ramMB: round1(g.procs.reduce((s, p) => s + p.ramMB, 0)),
|
|
129
|
+
count: g.procs.length,
|
|
130
|
+
ppm: g.procs.some((p) => p.ppm),
|
|
131
|
+
pids: g.procs.map((p) => p.pid),
|
|
132
|
+
diskReadBps: sumOptional(g.procs, "diskReadBps"),
|
|
133
|
+
diskWriteBps: sumOptional(g.procs, "diskWriteBps"),
|
|
134
|
+
gpuPct: sumOptional(g.procs, "gpuPct"),
|
|
135
|
+
gpuMemMB: sumOptional(g.procs, "gpuMemMB"),
|
|
136
|
+
netInBps: sumOptional(g.procs, "netInBps"),
|
|
137
|
+
netOutBps: sumOptional(g.procs, "netOutBps"),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
groups.sort((a, b) => b.cpu - a.cpu || b.ramMB - a.ramMB || a.label.localeCompare(b.label));
|
|
141
|
+
return groups;
|
|
142
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process cumulative byte counters → bytes/second, shared by every OS.
|
|
3
|
+
*
|
|
4
|
+
* The whole-machine helper in `rate-delta.ts` cannot serve here: it works on a
|
|
5
|
+
* counter's OWN clock and corrects 32-bit wraps, whereas these counters are
|
|
6
|
+
* 64-bit per-process totals sampled on the tick's wall clock. So the identity
|
|
7
|
+
* rules are the same as the CPU delta's (`pid:startedAt` key, first observation
|
|
8
|
+
* is 0, dead keys dropped) and only the units differ.
|
|
9
|
+
*
|
|
10
|
+
* `undefined` in, `undefined` out: a field the OS refused (an access-denied
|
|
11
|
+
* `/proc/<pid>/io`, a Windows property that came back empty) must never become
|
|
12
|
+
* a confident 0.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface ProcIoCounters {
|
|
16
|
+
diskReadBytes?: number;
|
|
17
|
+
diskWriteBytes?: number;
|
|
18
|
+
netInBytes?: number;
|
|
19
|
+
netOutBytes?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ProcIoSample extends ProcIoCounters {
|
|
23
|
+
/** `${pid}:${startedAt}` — see `cpuSampleKey`. */
|
|
24
|
+
key: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProcIoRates {
|
|
28
|
+
diskReadBps?: number;
|
|
29
|
+
diskWriteBps?: number;
|
|
30
|
+
netInBps?: number;
|
|
31
|
+
netOutBps?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ProcIoDeltaState {
|
|
35
|
+
at: number;
|
|
36
|
+
byKey: Map<string, ProcIoCounters>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Faster than any bus this code will meet; a delta implying more is a counter
|
|
40
|
+
* reset the OS did not flag, not a transfer. */
|
|
41
|
+
const MAX_PLAUSIBLE_BPS = 100 * 1024 ** 3;
|
|
42
|
+
|
|
43
|
+
export function computeProcIoRates(
|
|
44
|
+
prev: ProcIoDeltaState | null,
|
|
45
|
+
samples: readonly ProcIoSample[],
|
|
46
|
+
now: number,
|
|
47
|
+
): { ratesByKey: Map<string, ProcIoRates>; next: ProcIoDeltaState } {
|
|
48
|
+
const ratesByKey = new Map<string, ProcIoRates>();
|
|
49
|
+
const nextByKey = new Map<string, ProcIoCounters>();
|
|
50
|
+
const wallSec = prev ? (now - prev.at) / 1000 : 0;
|
|
51
|
+
const usable = prev !== null && wallSec > 0;
|
|
52
|
+
|
|
53
|
+
for (const s of samples) {
|
|
54
|
+
const counters: ProcIoCounters = {
|
|
55
|
+
diskReadBytes: s.diskReadBytes,
|
|
56
|
+
diskWriteBytes: s.diskWriteBytes,
|
|
57
|
+
netInBytes: s.netInBytes,
|
|
58
|
+
netOutBytes: s.netOutBytes,
|
|
59
|
+
};
|
|
60
|
+
nextByKey.set(s.key, counters);
|
|
61
|
+
const before = usable ? prev!.byKey.get(s.key) : undefined;
|
|
62
|
+
ratesByKey.set(s.key, {
|
|
63
|
+
diskReadBps: bps(before?.diskReadBytes, s.diskReadBytes, wallSec),
|
|
64
|
+
diskWriteBps: bps(before?.diskWriteBytes, s.diskWriteBytes, wallSec),
|
|
65
|
+
netInBps: bps(before?.netInBytes, s.netInBytes, wallSec),
|
|
66
|
+
netOutBps: bps(before?.netOutBytes, s.netOutBytes, wallSec),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { ratesByKey, next: { at: now, byKey: nextByKey } };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* - counter not reported → `undefined` (unmeasurable, the UI shows "—")
|
|
75
|
+
* - no baseline yet, or the counter went backwards → 0
|
|
76
|
+
*/
|
|
77
|
+
function bps(before: number | undefined, current: number | undefined, wallSec: number): number | undefined {
|
|
78
|
+
if (current === undefined || !Number.isFinite(current)) return undefined;
|
|
79
|
+
if (before === undefined || wallSec <= 0) return 0;
|
|
80
|
+
const delta = current - before;
|
|
81
|
+
if (!Number.isFinite(delta) || delta <= 0) return 0;
|
|
82
|
+
const rate = Math.round(delta / wallSec);
|
|
83
|
+
return rate > MAX_PLAUSIBLE_BPS ? 0 : rate;
|
|
84
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process disk bytes on Linux from `/proc/<pid>/io` — no subprocess.
|
|
3
|
+
*
|
|
4
|
+
* `read_bytes`/`write_bytes` are the counters that actually reached the block
|
|
5
|
+
* layer, which is what a Task-Manager "Disk" column means; `rchar`/`wchar`
|
|
6
|
+
* would also count page-cache hits and make every process look busy.
|
|
7
|
+
*
|
|
8
|
+
* The file is PTRACE_MODE_READ-protected, so unprivileged PPM sees it only for
|
|
9
|
+
* its own uid: an EACCES is the NORMAL case for other users' processes and must
|
|
10
|
+
* leave those rows unmeasured (`undefined`) rather than 0 or an exception.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
|
|
14
|
+
export interface ProcIoBytes {
|
|
15
|
+
readBytes: number;
|
|
16
|
+
writeBytes: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const FIELD = /^(read_bytes|write_bytes):\s+(\d+)$/;
|
|
20
|
+
|
|
21
|
+
/** Null when neither counter is present (a kernel thread reports zeros only in
|
|
22
|
+
* some kernels, and a truncated read must not be guessed at). */
|
|
23
|
+
export function parseProcIo(text: string): ProcIoBytes | null {
|
|
24
|
+
let read: number | undefined;
|
|
25
|
+
let write: number | undefined;
|
|
26
|
+
for (const line of text.split("\n")) {
|
|
27
|
+
const m = FIELD.exec(line.trim());
|
|
28
|
+
if (!m) continue;
|
|
29
|
+
const value = Number(m[2]);
|
|
30
|
+
if (!Number.isFinite(value)) continue;
|
|
31
|
+
if (m[1] === "read_bytes") read = value;
|
|
32
|
+
else write = value;
|
|
33
|
+
}
|
|
34
|
+
if (read === undefined && write === undefined) return null;
|
|
35
|
+
return { readBytes: read ?? 0, writeBytes: write ?? 0 };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Null on EACCES (another user's process), ENOENT (exited between readdir and
|
|
39
|
+
* read) or an unparseable file. Never throws. */
|
|
40
|
+
export function readProcIoBytes(pid: number): ProcIoBytes | null {
|
|
41
|
+
try {
|
|
42
|
+
return parseProcIo(readFileSync(`/proc/${pid}/io`, "utf-8"));
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|