@hienlh/ppm 0.18.8 → 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 +9 -0
- package/CLAUDE.md +1 -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 +4 -3
- package/dist/web/assets/{ai-resource-editor-BydGrxjb.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-DzBGq9Lk.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-CRConSGG.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-BtCGfmrq.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-2M5sKRqb.js → database-viewer-BCZOTrPT.js} +1 -1
- package/dist/web/assets/{diff-viewer-BMr7vOvZ.js → diff-viewer-BwfMN-nW.js} +1 -1
- package/dist/web/assets/{docx-preview-BpG2kr_-.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-DXufyj-9.js → explorer-body-BKOMM566.js} +2 -2
- package/dist/web/assets/{explorer-window-content-B3krk4G1.js → explorer-window-content-Bbzijk8t.js} +1 -1
- package/dist/web/assets/{extension-webview-12M45CGq.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-Cp4k3tX_.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-B0BB0VDl.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-3HfuxA0N.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-oMJmtpH4.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-DPcoPjmO.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/{mobile-explorer-sheet-CEVcU964.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-B8AqxwRT.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-BslNoDFw.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-BtSXQ3S4.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-CkvpmXyA.js → sql-query-editor-BPYsJDsn.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CJbP6ruh.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-Cxh-HF2V.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-gQCs_fyx.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-BG2fz83z.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-DaqI7O-a.js → use-monaco-theme-CGucu3fb.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DKpstvLS.js → use-open-team-member-DdrRXj-M.js} +1 -1
- package/dist/web/assets/{use-websocket-KVhDtbwK.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-CVEUDZBj.js → video-preview-DYAbbmd0.js} +1 -1
- 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/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/src/server/index.ts +2 -8
- 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/edge-forwarder.ts +191 -191
- 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/services/windows-process-tree.ts +351 -351
- package/src/types/system-metrics.ts +199 -0
- 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/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-pBGQ9SCY.js +0 -15
- package/dist/web/assets/circle-question-mark-CENpx4oA.js +0 -1
- package/dist/web/assets/code-editor-XviNXdzi.js +0 -10
- package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +0 -1
- package/dist/web/assets/group-chat-tab-CcG_NXIS.js +0 -1
- package/dist/web/assets/index-BAVBuKmT.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-Dg5HE9Zq.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-COSUzGl8.js +0 -1
- package/dist/web/assets/system-monitor-tab-DThAFSd_.js +0 -1
- package/dist/web/assets/team-member-sheet-M83B4znI.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.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/{media-url-DlYsHq0S.js → media-url-CylYQld1.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,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
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-process network bytes on macOS via `nettop`. This is the ONLY mainstream
|
|
3
|
+
* OS with an unprivileged per-process network source: Windows needs an ETW
|
|
4
|
+
* session and Linux needs packet capture, so both leave the Net column hidden.
|
|
5
|
+
*
|
|
6
|
+
* nettop -P -x -L 1 -J bytes_in,bytes_out
|
|
7
|
+
* -P aggregate per process instead of per connection
|
|
8
|
+
* -x raw byte counts, no "1.2M" suffixes
|
|
9
|
+
* -L 1 one CSV sample, then exit (no interactive curses mode)
|
|
10
|
+
* -J emit only these two columns
|
|
11
|
+
*
|
|
12
|
+
* The CSV shape varies between macOS releases (a leading timestamp column comes
|
|
13
|
+
* and goes), so the parser hunts for the `<name>.<pid>` cell and takes the next
|
|
14
|
+
* two numeric cells rather than trusting fixed column indexes.
|
|
15
|
+
*
|
|
16
|
+
* UNVERIFIED on real macOS hardware — fixture-driven only.
|
|
17
|
+
*/
|
|
18
|
+
import type { Runner } from "../host-info/spawn-runner.ts";
|
|
19
|
+
import { defaultRunner } from "../host-info/spawn-runner.ts";
|
|
20
|
+
|
|
21
|
+
export const NETTOP_ARGV = ["nettop", "-P", "-x", "-L", "1", "-J", "bytes_in,bytes_out"];
|
|
22
|
+
const NETTOP_TIMEOUT_MS = 5000;
|
|
23
|
+
|
|
24
|
+
export interface ProcNetBytes {
|
|
25
|
+
inBytes: number;
|
|
26
|
+
outBytes: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** `<process name>.<pid>` — the name may itself contain dots
|
|
30
|
+
* ("com.apple.WebKit.Networking.431"), so the pid is the LAST segment. The
|
|
31
|
+
* name must contain a letter: the leading timestamp cell some macOS releases
|
|
32
|
+
* emit ("14:33:05.976") is otherwise a perfect match and would invent a pid
|
|
33
|
+
* out of the fractional seconds. */
|
|
34
|
+
const NAME_PID_CELL = /^(.+)\.(\d+)$/;
|
|
35
|
+
const NUMERIC_CELL = /^\d+$/;
|
|
36
|
+
|
|
37
|
+
function pidCell(cell: string): number | null {
|
|
38
|
+
const m = NAME_PID_CELL.exec(cell);
|
|
39
|
+
if (!m || !/[A-Za-z]/.test(m[1]!)) return null;
|
|
40
|
+
const pid = Number(m[2]);
|
|
41
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseNettopCsv(text: string): Map<number, ProcNetBytes> {
|
|
45
|
+
const byPid = new Map<number, ProcNetBytes>();
|
|
46
|
+
for (const line of text.split("\n")) {
|
|
47
|
+
const cells = line.split(",").map((c) => c.trim());
|
|
48
|
+
const at = cells.findIndex((c) => pidCell(c) !== null);
|
|
49
|
+
if (at < 0) continue;
|
|
50
|
+
const pid = pidCell(cells[at]!)!;
|
|
51
|
+
const numbers = cells.slice(at + 1).filter((c) => NUMERIC_CELL.test(c));
|
|
52
|
+
if (numbers.length < 2) continue;
|
|
53
|
+
const inBytes = Number(numbers[0]);
|
|
54
|
+
const outBytes = Number(numbers[1]);
|
|
55
|
+
if (!Number.isFinite(inBytes) || !Number.isFinite(outBytes)) continue;
|
|
56
|
+
// A pid can appear twice when nettop splits interfaces; sum them.
|
|
57
|
+
const prev = byPid.get(pid);
|
|
58
|
+
byPid.set(pid, {
|
|
59
|
+
inBytes: (prev?.inBytes ?? 0) + inBytes,
|
|
60
|
+
outBytes: (prev?.outBytes ?? 0) + outBytes,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return byPid;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ProcessNetCollector {
|
|
67
|
+
/** Null when the sample failed — the rows then carry no net figures at all
|
|
68
|
+
* rather than a fabricated 0. */
|
|
69
|
+
collect(): Promise<Map<number, ProcNetBytes> | null>;
|
|
70
|
+
isDisabled(): boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const MAX_FAILURES = 3;
|
|
74
|
+
|
|
75
|
+
export function createDarwinProcessNetCollector(
|
|
76
|
+
run: Runner = defaultRunner,
|
|
77
|
+
log: (message: string) => void = (m) => console.log(m),
|
|
78
|
+
): ProcessNetCollector {
|
|
79
|
+
let failures = 0;
|
|
80
|
+
let disabled = false;
|
|
81
|
+
let logged = false;
|
|
82
|
+
return {
|
|
83
|
+
isDisabled: () => disabled,
|
|
84
|
+
async collect() {
|
|
85
|
+
if (disabled) return null;
|
|
86
|
+
const startedAt = Date.now();
|
|
87
|
+
try {
|
|
88
|
+
const r = await run(NETTOP_ARGV, NETTOP_TIMEOUT_MS);
|
|
89
|
+
if (r.code !== 0 || r.timedOut) throw new Error(r.stderr || `exit ${r.code}`);
|
|
90
|
+
failures = 0;
|
|
91
|
+
if (!logged) {
|
|
92
|
+
logged = true;
|
|
93
|
+
// The 2 s tick budget is what breaks first if nettop turns out slow.
|
|
94
|
+
log(`[system-metrics] nettop sample: ${Date.now() - startedAt} ms`);
|
|
95
|
+
}
|
|
96
|
+
return parseNettopCsv(r.stdout);
|
|
97
|
+
} catch {
|
|
98
|
+
if (++failures >= MAX_FAILURES) disabled = true;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw collector rows → `ProcessInfo[]` for one tick: instantaneous CPU%, the
|
|
3
|
+
* cosmetic `ppm` flag, the guard-derived `protected` flag and a redacted,
|
|
4
|
+
* truncated command line.
|
|
5
|
+
*/
|
|
6
|
+
import type { MetricsPlatform, ProcessInfo } from "../../types/system-metrics.ts";
|
|
7
|
+
import { redactSecrets } from "../redact-secrets.ts";
|
|
8
|
+
import type { RawProcessRow } from "./process-collector-types.ts";
|
|
9
|
+
import { computeCpuPercents, cpuSampleKey, type CpuDeltaState } from "./process-cpu-delta.ts";
|
|
10
|
+
import { computeProcIoRates, type ProcIoDeltaState } from "./process-io-delta.ts";
|
|
11
|
+
import { checkKillAllowed, type KillGuardContext } from "./kill-guard.ts";
|
|
12
|
+
import { buildGuardMaps, type GuardMaps } from "./kill-identity-resolver.ts";
|
|
13
|
+
import { computePpmPids } from "./ppm-process-ownership.ts";
|
|
14
|
+
import type { ProtectedPids } from "./ppm-protected-pids.ts";
|
|
15
|
+
|
|
16
|
+
/** Command lines are for recognising a process, not for archiving it. A 400-row
|
|
17
|
+
* frame measured ~120 KB at 300 chars; 160 keeps it recognisable in a table
|
|
18
|
+
* column while cutting roughly a quarter of every 2 s frame over the tunnel. */
|
|
19
|
+
export const COMMAND_MAX_CHARS = 160;
|
|
20
|
+
|
|
21
|
+
export interface BuildRowsInput {
|
|
22
|
+
rows: readonly RawProcessRow[];
|
|
23
|
+
platform: MetricsPlatform;
|
|
24
|
+
coreCount: number;
|
|
25
|
+
now: number;
|
|
26
|
+
prevCpu: CpuDeltaState | null;
|
|
27
|
+
prevIo: ProcIoDeltaState | null;
|
|
28
|
+
/** Resolved against THIS tick's rows, so liveness means "present in the table". */
|
|
29
|
+
resolveProtected: (isAlive: (pid: number) => boolean, nameOf: (pid: number) => string | undefined) => ProtectedPids;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface BuiltRows {
|
|
33
|
+
processes: ProcessInfo[];
|
|
34
|
+
nextCpu: CpuDeltaState;
|
|
35
|
+
nextIo: ProcIoDeltaState;
|
|
36
|
+
guardCtx: KillGuardContext;
|
|
37
|
+
protectedPids: ProtectedPids;
|
|
38
|
+
maps: GuardMaps;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Redact first, truncate second: secrets live at the front of argv. */
|
|
42
|
+
export function sanitizeCommand(command: string | null, fallback: string): string {
|
|
43
|
+
if (!command) return fallback;
|
|
44
|
+
const redacted = redactSecrets(command);
|
|
45
|
+
return redacted.length > COMMAND_MAX_CHARS ? redacted.slice(0, COMMAND_MAX_CHARS) : redacted;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function buildProcessRows(input: BuildRowsInput): BuiltRows {
|
|
49
|
+
const { platform } = input;
|
|
50
|
+
const maps = buildGuardMaps(input.rows);
|
|
51
|
+
// Only the de-duplicated rows are trusted from here on.
|
|
52
|
+
const rows = [...maps.byPid.values()];
|
|
53
|
+
const protectedPids = input.resolveProtected(
|
|
54
|
+
(pid) => maps.byPid.has(pid),
|
|
55
|
+
(pid) => maps.byPid.get(pid)?.name.toLowerCase(),
|
|
56
|
+
);
|
|
57
|
+
const guardCtx: KillGuardContext = {
|
|
58
|
+
platform,
|
|
59
|
+
protectedPids: protectedPids.pids,
|
|
60
|
+
ppidOf: maps.ppidOf,
|
|
61
|
+
startedAtOf: maps.startedAtOf,
|
|
62
|
+
};
|
|
63
|
+
const ppmPids = computePpmPids({
|
|
64
|
+
roots: protectedPids.roots,
|
|
65
|
+
extraPids: [...protectedPids.pids],
|
|
66
|
+
ppidOf: maps.ppidOf,
|
|
67
|
+
startedAtOf: maps.startedAtOf,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const samples = rows.map((r) => ({ key: cpuSampleKey(r.pid, r.startedAt), cpuMs: r.cpuMs }));
|
|
71
|
+
const { percentByKey, next } = computeCpuPercents(input.prevCpu, samples, input.now, input.coreCount);
|
|
72
|
+
// Same key and the same wall interval as the CPU delta, so a row's CPU% and
|
|
73
|
+
// its Bps figures can never describe two different spans of time.
|
|
74
|
+
const io = computeProcIoRates(
|
|
75
|
+
input.prevIo,
|
|
76
|
+
rows.map((r) => ({
|
|
77
|
+
key: cpuSampleKey(r.pid, r.startedAt),
|
|
78
|
+
diskReadBytes: r.diskReadBytes,
|
|
79
|
+
diskWriteBytes: r.diskWriteBytes,
|
|
80
|
+
netInBytes: r.netInBytes,
|
|
81
|
+
netOutBytes: r.netOutBytes,
|
|
82
|
+
})),
|
|
83
|
+
input.now,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const processes: ProcessInfo[] = rows.map((r) => {
|
|
87
|
+
const key = cpuSampleKey(r.pid, r.startedAt);
|
|
88
|
+
const rates = io.ratesByKey.get(key);
|
|
89
|
+
return {
|
|
90
|
+
pid: r.pid,
|
|
91
|
+
ppid: r.ppid,
|
|
92
|
+
name: r.name,
|
|
93
|
+
command: sanitizeCommand(r.command, r.name),
|
|
94
|
+
cpu: percentByKey.get(key) ?? 0,
|
|
95
|
+
ramMB: Math.round(r.ramMB * 10) / 10,
|
|
96
|
+
startedAt: r.startedAt,
|
|
97
|
+
ppm: ppmPids.has(r.pid),
|
|
98
|
+
protected: !checkKillAllowed({ pid: r.pid, name: r.name }, false, guardCtx).allowed,
|
|
99
|
+
// Left undefined (and therefore absent from the JSON frame) whenever the
|
|
100
|
+
// OS gave nothing for that counter.
|
|
101
|
+
diskReadBps: rates?.diskReadBps,
|
|
102
|
+
diskWriteBps: rates?.diskWriteBps,
|
|
103
|
+
netInBps: rates?.netInBps,
|
|
104
|
+
netOutBps: rates?.netOutBps,
|
|
105
|
+
gpuPct: r.gpuPct,
|
|
106
|
+
gpuMemMB: r.gpuMemMB,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return { processes, nextCpu: next, nextIo: io.next, guardCtx, protectedPids, maps };
|
|
111
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cumulative byte counters → bytes/second. Disk and net are the same problem
|
|
3
|
+
* twice, so one helper serves both.
|
|
4
|
+
*/
|
|
5
|
+
import type { RateMetrics } from "../../types/system-metrics.ts";
|
|
6
|
+
|
|
7
|
+
export interface CounterSample {
|
|
8
|
+
inBytes: number;
|
|
9
|
+
outBytes: number;
|
|
10
|
+
/** The counter's OWN clock, in seconds — never `Date.now()` when the source
|
|
11
|
+
* carries a timestamp, because the tick's scheduling jitter (a Windows CIM
|
|
12
|
+
* round trip alone is ~174 ms and can slip) would otherwise leak into the rate. */
|
|
13
|
+
atSec: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const UINT32 = 2 ** 32;
|
|
17
|
+
/** Faster than any bus this code will meet; a corrected wrap that lands above
|
|
18
|
+
* it is a counter reset, not a transfer. */
|
|
19
|
+
const MAX_PLAUSIBLE_BPS = 100 * 1024 ** 3;
|
|
20
|
+
|
|
21
|
+
export const UNAVAILABLE_RATE: RateMetrics = { inBps: 0, outBps: 0, available: false };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* - first sample → `available:false` (no baseline)
|
|
25
|
+
* - non-positive interval → `available:false`
|
|
26
|
+
* - negative delta → retry with +2^32 (`BytesReceivedPersec` is a 32-bit counter
|
|
27
|
+
* that wraps roughly every 34 s of sustained gigabit); accept only when the
|
|
28
|
+
* corrected rate is physically plausible, else `available:false`
|
|
29
|
+
*/
|
|
30
|
+
export function toRate(prev: CounterSample | null, next: CounterSample): RateMetrics {
|
|
31
|
+
if (!prev) return UNAVAILABLE_RATE;
|
|
32
|
+
const dt = next.atSec - prev.atSec;
|
|
33
|
+
if (!Number.isFinite(dt) || dt <= 0) return UNAVAILABLE_RATE;
|
|
34
|
+
|
|
35
|
+
const inBps = deltaRate(next.inBytes - prev.inBytes, dt);
|
|
36
|
+
const outBps = deltaRate(next.outBytes - prev.outBytes, dt);
|
|
37
|
+
if (inBps === null || outBps === null) return UNAVAILABLE_RATE;
|
|
38
|
+
return { inBps, outBps, available: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function deltaRate(delta: number, dtSec: number): number | null {
|
|
42
|
+
if (!Number.isFinite(delta)) return null;
|
|
43
|
+
const corrected = delta < 0 ? delta + UINT32 : delta;
|
|
44
|
+
if (corrected < 0) return null;
|
|
45
|
+
const bps = Math.round(corrected / dtSec);
|
|
46
|
+
return bps > MAX_PLAUSIBLE_BPS ? null : bps;
|
|
47
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Per-OS collector wiring for the snapshot service. Nothing here spawns until
|
|
2
|
+
* a full-tier tick actually calls a collector. */
|
|
3
|
+
import type { MetricsPlatform } from "../../types/system-metrics.ts";
|
|
4
|
+
import type { ProcessCollector } from "./process-collector-types.ts";
|
|
5
|
+
import { EMPTY_PROCESS_COLLECTOR } from "./process-collector-types.ts";
|
|
6
|
+
import type { DiskNetCounters } from "./disk-net-collector-linux.ts";
|
|
7
|
+
import { collectLinuxDiskNet } from "./disk-net-collector-linux.ts";
|
|
8
|
+
import { collectDarwinDiskNet } from "./disk-net-collector-darwin.ts";
|
|
9
|
+
import { createNvidiaGpuCollector, type GpuCollector } from "./gpu-collector-nvidia.ts";
|
|
10
|
+
import { createNvidiaProcessMemoryCollector } from "./gpu-process-memory-nvidia.ts";
|
|
11
|
+
import { createLinuxProcessCollector } from "./process-collector-linux.ts";
|
|
12
|
+
import { createDarwinProcessCollector } from "./process-collector-darwin.ts";
|
|
13
|
+
import { createDarwinProcessNetCollector } from "./process-net-collector-darwin.ts";
|
|
14
|
+
import { createWindowsProcessCollector } from "./process-collector-windows.ts";
|
|
15
|
+
import { readProcTable } from "../proc-table-linux.ts";
|
|
16
|
+
|
|
17
|
+
export interface PlatformCollectors {
|
|
18
|
+
platform: MetricsPlatform;
|
|
19
|
+
processes: ProcessCollector;
|
|
20
|
+
/** Null on win32: the counters ride along in the process round trip. */
|
|
21
|
+
diskNet: (() => Promise<DiskNetCounters>) | null;
|
|
22
|
+
gpus: GpuCollector;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function toMetricsPlatform(p: NodeJS.Platform = process.platform): MetricsPlatform {
|
|
26
|
+
return p === "win32" || p === "darwin" ? p : "linux";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createPlatformCollectors(platform: MetricsPlatform = toMetricsPlatform()): PlatformCollectors {
|
|
30
|
+
const gpus = createNvidiaGpuCollector();
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case "win32":
|
|
33
|
+
return { platform, processes: createWindowsProcessCollector(), diskNet: null, gpus };
|
|
34
|
+
case "darwin":
|
|
35
|
+
return {
|
|
36
|
+
platform,
|
|
37
|
+
processes: createDarwinProcessCollector(undefined, undefined, { net: createDarwinProcessNetCollector() }),
|
|
38
|
+
diskNet: () => collectDarwinDiskNet(),
|
|
39
|
+
gpus,
|
|
40
|
+
};
|
|
41
|
+
case "linux":
|
|
42
|
+
return {
|
|
43
|
+
platform,
|
|
44
|
+
processes: createLinuxProcessCollector(readProcTable, { gpuMemory: createNvidiaProcessMemoryCollector() }),
|
|
45
|
+
diskNet: async () => collectLinuxDiskNet(),
|
|
46
|
+
gpus,
|
|
47
|
+
};
|
|
48
|
+
default:
|
|
49
|
+
return { platform, processes: EMPTY_PROCESS_COLLECTOR, diskNet: null, gpus };
|
|
50
|
+
}
|
|
51
|
+
}
|