@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,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-machine metrics: tiered subscribers, leases, one poll timer, atomic
|
|
3
|
+
* delta-state commits and demand-gated children.
|
|
4
|
+
*
|
|
5
|
+
* The tier gates COLLECTION, not just serialisation: the status bar mounts on
|
|
6
|
+
* every page, and if its stream drove full collection PPM would run a 174 ms
|
|
7
|
+
* PowerShell round trip plus a 400-process grouping pass every 2 s forever to
|
|
8
|
+
* render two numbers. So the PowerShell child, `nvidia-smi` and the disk/net
|
|
9
|
+
* queries run only while ≥1 full subscriber exists, and the child is torn down
|
|
10
|
+
* 60 s after the last one leaves (a window reopen then skips the bootstrap).
|
|
11
|
+
*/
|
|
12
|
+
import type { KillProcessResult, MetricsSnapshot, MetricsTier } from "../../types/system-metrics.ts";
|
|
13
|
+
import { METRICS_INTERVAL_MS, METRICS_LEASE_TIMEOUT_MS, METRICS_LIGHT_INTERVAL_MS } from "../../types/system-metrics.ts";
|
|
14
|
+
import { collectMemory, sampleCpuTimes } from "./cpu-memory-collector.ts";
|
|
15
|
+
import { assembleTick, EMPTY_DELTA_STATE, projectLight, type TickDeltaState, type TickDeps } from "./system-metrics-tick.ts";
|
|
16
|
+
import { createPlatformCollectors, type PlatformCollectors } from "./system-metrics-platform.ts";
|
|
17
|
+
import { resolveProtectedPidsLive } from "./ppm-protected-pids.ts";
|
|
18
|
+
import { SubscriberRegistry, type MetricsSubscriber } from "./metrics-subscriber-registry.ts";
|
|
19
|
+
import { PollScheduler, installProcessExitHooks } from "./metrics-poll-scheduler.ts";
|
|
20
|
+
import { handleKillRequest, type KillOutcome } from "./kill-request-handler.ts";
|
|
21
|
+
import { executeKill } from "./kill-executor.ts";
|
|
22
|
+
import { CollectorLock } from "./metrics-collector-lock.ts";
|
|
23
|
+
|
|
24
|
+
/** Over a proxy a closed window's lease lives until the reap; 8 leaves room for
|
|
25
|
+
* a few open/close cycles across devices without a spurious 429. */
|
|
26
|
+
export const MAX_STREAM_SUBSCRIBERS = 8;
|
|
27
|
+
/** Grace before the PowerShell child is torn down after the last full subscriber leaves. */
|
|
28
|
+
export const FULL_IDLE_TEARDOWN_MS = 60_000;
|
|
29
|
+
|
|
30
|
+
export interface SystemMetricsServiceOptions {
|
|
31
|
+
collectors?: PlatformCollectors;
|
|
32
|
+
intervals?: { full: number; light: number };
|
|
33
|
+
idleTeardownMs?: number;
|
|
34
|
+
leaseTimeoutMs?: number;
|
|
35
|
+
now?: () => number;
|
|
36
|
+
resolveProtected?: TickDeps["resolveProtected"];
|
|
37
|
+
execute?: (pid: number, tree: boolean) => Promise<KillProcessResult>;
|
|
38
|
+
log?: (line: string) => void;
|
|
39
|
+
/** Register process exit/signal teardown of children (off in unit tests). */
|
|
40
|
+
exitHooks?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type SubscribeInit = Pick<MetricsSubscriber, "tier" | "deliver" | "close">;
|
|
44
|
+
export interface SubscribeResult { sid: string; tier: MetricsTier; intervalMs: number }
|
|
45
|
+
|
|
46
|
+
export class SystemMetricsService {
|
|
47
|
+
private readonly registry = new SubscriberRegistry();
|
|
48
|
+
private readonly scheduler: PollScheduler;
|
|
49
|
+
private readonly collectors: PlatformCollectors;
|
|
50
|
+
private readonly intervals: { full: number; light: number };
|
|
51
|
+
private readonly leaseTimeoutMs: number;
|
|
52
|
+
private readonly now: () => number;
|
|
53
|
+
private readonly tickDeps: TickDeps;
|
|
54
|
+
private readonly execute: NonNullable<SystemMetricsServiceOptions["execute"]>;
|
|
55
|
+
private readonly log: (line: string) => void;
|
|
56
|
+
private readonly exitHooks: boolean;
|
|
57
|
+
|
|
58
|
+
/** Serialises the one tick or kill collection allowed to hold the collector. */
|
|
59
|
+
private readonly collectorLock = new CollectorLock();
|
|
60
|
+
private state: TickDeltaState = EMPTY_DELTA_STATE;
|
|
61
|
+
private latest: { light: MetricsSnapshot | null; full: MetricsSnapshot | null } = { light: null, full: null };
|
|
62
|
+
|
|
63
|
+
constructor(opts: SystemMetricsServiceOptions = {}) {
|
|
64
|
+
this.collectors = opts.collectors ?? createPlatformCollectors();
|
|
65
|
+
this.intervals = opts.intervals ?? { full: METRICS_INTERVAL_MS, light: METRICS_LIGHT_INTERVAL_MS };
|
|
66
|
+
this.leaseTimeoutMs = opts.leaseTimeoutMs ?? METRICS_LEASE_TIMEOUT_MS;
|
|
67
|
+
this.now = opts.now ?? Date.now;
|
|
68
|
+
this.execute = opts.execute ?? executeKill;
|
|
69
|
+
this.log = opts.log ?? ((line) => console.log(line));
|
|
70
|
+
this.exitHooks = opts.exitHooks ?? true;
|
|
71
|
+
this.tickDeps = {
|
|
72
|
+
platform: this.collectors.platform,
|
|
73
|
+
memory: () => collectMemory(),
|
|
74
|
+
processes: this.collectors.processes,
|
|
75
|
+
diskNet: this.collectors.diskNet,
|
|
76
|
+
gpus: this.collectors.gpus,
|
|
77
|
+
resolveProtected: opts.resolveProtected ?? resolveProtectedPidsLive,
|
|
78
|
+
now: this.now,
|
|
79
|
+
sampleCpu: sampleCpuTimes,
|
|
80
|
+
};
|
|
81
|
+
this.scheduler = new PollScheduler({
|
|
82
|
+
intervals: this.intervals,
|
|
83
|
+
idleTeardownMs: opts.idleTeardownMs ?? FULL_IDLE_TEARDOWN_MS,
|
|
84
|
+
onTick: () => void this.runTick(),
|
|
85
|
+
onIdle: () => this.collectors.processes.stop(),
|
|
86
|
+
onFullStart: () => this.ensureExitHooks(),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Null when the cap of live subscribers is reached (expired leases are reaped first). */
|
|
91
|
+
subscribe(init: SubscribeInit): SubscribeResult | null {
|
|
92
|
+
this.reapExpired();
|
|
93
|
+
if (this.registry.count() >= MAX_STREAM_SUBSCRIBERS) return null;
|
|
94
|
+
const sid = crypto.randomUUID().replace(/-/g, "").slice(0, 8);
|
|
95
|
+
this.registry.add({ sid, ...init, lastPingAt: this.now() });
|
|
96
|
+
this.reconcile();
|
|
97
|
+
// A newcomer on an already-running tier would otherwise wait a whole
|
|
98
|
+
// interval. Deferred so the caller can write its `session` frame first.
|
|
99
|
+
const latest = this.latest[init.tier];
|
|
100
|
+
if (latest) queueMicrotask(() => { if (this.registry.has(sid)) init.deliver(latest); });
|
|
101
|
+
return { sid, tier: init.tier, intervalMs: this.intervals[init.tier] };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
unsubscribe(sid: string): boolean {
|
|
105
|
+
const removed = this.registry.remove(sid);
|
|
106
|
+
if (removed) this.reconcile();
|
|
107
|
+
return removed;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ping(sid: string): boolean {
|
|
111
|
+
return this.registry.ping(sid, this.now());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getLatest(tier: MetricsTier): MetricsSnapshot | null {
|
|
115
|
+
return this.latest[tier];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
liveCount(tier?: MetricsTier): number {
|
|
119
|
+
return this.registry.count(tier);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
activeTier(): MetricsTier | null {
|
|
123
|
+
return this.scheduler.activeTier();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Drop leases silent for longer than the timeout; returns how many. */
|
|
127
|
+
reapExpired(): number {
|
|
128
|
+
const expired = this.registry.reap(this.now(), this.leaseTimeoutMs);
|
|
129
|
+
if (expired.length > 0) this.reconcile();
|
|
130
|
+
return expired.length;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A kill re-collects the process table through the same collector the tick
|
|
135
|
+
* uses, and the PowerShell session accepts one request at a time — so the
|
|
136
|
+
* kill waits for an in-flight tick, and ticks skip while a kill holds the
|
|
137
|
+
* collector, instead of one of them failing with "request already in flight".
|
|
138
|
+
*/
|
|
139
|
+
async kill(body: unknown): Promise<KillOutcome> {
|
|
140
|
+
this.ensureExitHooks();
|
|
141
|
+
try {
|
|
142
|
+
return await this.collectorLock.runExclusive(() => handleKillRequest(body, {
|
|
143
|
+
platform: this.collectors.platform,
|
|
144
|
+
collector: this.collectors.processes,
|
|
145
|
+
resolveProtected: this.tickDeps.resolveProtected,
|
|
146
|
+
execute: this.execute,
|
|
147
|
+
log: this.log,
|
|
148
|
+
}));
|
|
149
|
+
} finally {
|
|
150
|
+
// The re-query may have started the PowerShell child with no window open.
|
|
151
|
+
if (this.registry.count("full") === 0) this.scheduler.armIdle();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** One poll: assemble for the active tier, commit the baseline, publish. */
|
|
156
|
+
async runTick(): Promise<void> {
|
|
157
|
+
// A Windows tick is ~174 ms and can slip, and a kill re-collects through the
|
|
158
|
+
// same collector: an overlapping collection would consume the delta baseline
|
|
159
|
+
// twice (CPU% in the hundreds) or fail with "request already in flight".
|
|
160
|
+
// Dropping a poll instead is harmless — metrics are lossy by nature.
|
|
161
|
+
await this.collectorLock.tryRun(() => this.assembleAndPublish());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private async assembleAndPublish(): Promise<void> {
|
|
165
|
+
try {
|
|
166
|
+
this.reapExpired();
|
|
167
|
+
const tier = this.scheduler.activeTier();
|
|
168
|
+
if (!tier) return;
|
|
169
|
+
const { snapshot, nextState } = await assembleTick(tier, this.state, this.tickDeps, this.latest.full);
|
|
170
|
+
this.state = nextState;
|
|
171
|
+
this.publish(snapshot);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
console.error("[SystemMetrics] tick failed:", (e as Error)?.message ?? e);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Drop every subscriber, stop timers and kill children. */
|
|
178
|
+
shutdown(): void {
|
|
179
|
+
this.registry.clear();
|
|
180
|
+
this.scheduler.stop();
|
|
181
|
+
this.teardownChildren();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private publish(snapshot: MetricsSnapshot): void {
|
|
185
|
+
if (snapshot.tier === "full") {
|
|
186
|
+
this.latest.full = snapshot;
|
|
187
|
+
this.latest.light = projectLight(snapshot);
|
|
188
|
+
} else {
|
|
189
|
+
this.latest.light = snapshot;
|
|
190
|
+
}
|
|
191
|
+
for (const s of this.registry.list("full")) if (this.latest.full) s.deliver(this.latest.full);
|
|
192
|
+
for (const s of this.registry.list("light")) if (this.latest.light) s.deliver(this.latest.light);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private reconcile(): void {
|
|
196
|
+
const changed = this.scheduler.reconcile(this.registry.count("full"), this.registry.count("light"));
|
|
197
|
+
// Leaving the full tier: its baselines would be stale by the time it resumes.
|
|
198
|
+
if (changed && this.scheduler.activeTier() !== "full") {
|
|
199
|
+
this.state = { ...this.state, procCpu: null, procIo: null, disk: null, net: null };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private teardownChildren = (): void => {
|
|
204
|
+
try { this.collectors.processes.stop(); } catch { /* already gone */ }
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
private ensureExitHooks(): void {
|
|
208
|
+
if (this.exitHooks) installProcessExitHooks(this.teardownChildren);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const systemMetricsService = new SystemMetricsService();
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** Whole-machine metrics contract shared by the collectors, the REST/SSE routes
|
|
2
|
+
* and the web client. Types + constants only, no imports, so both bundles can
|
|
3
|
+
* take it. Import RELATIVELY — the "@" alias points at src/web, not src. */
|
|
4
|
+
|
|
5
|
+
export type MetricsPlatform = "win32" | "darwin" | "linux";
|
|
6
|
+
|
|
7
|
+
/** `light` = node:os only, no child processes, status-bar cadence.
|
|
8
|
+
* `full` = every collector, process rows, groups. */
|
|
9
|
+
export type MetricsTier = "light" | "full";
|
|
10
|
+
|
|
11
|
+
/** Aggregate + per-core busy percentage over the last tick (0-100, 1 decimal). */
|
|
12
|
+
export interface CpuMetrics {
|
|
13
|
+
total: number;
|
|
14
|
+
/** Index matches `os.cpus()` order. Core count is `cores.length`. */
|
|
15
|
+
cores: number[];
|
|
16
|
+
model: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MemoryMetrics {
|
|
20
|
+
totalMB: number;
|
|
21
|
+
usedMB: number;
|
|
22
|
+
availableMB: number;
|
|
23
|
+
/** usedMB / totalMB × 100. */
|
|
24
|
+
percent: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Whole-machine throughput, bytes/second over the last tick. */
|
|
28
|
+
export interface RateMetrics {
|
|
29
|
+
/** Disk: read. Net: received/down. */
|
|
30
|
+
inBps: number;
|
|
31
|
+
/** Disk: write. Net: sent/up. */
|
|
32
|
+
outBps: number;
|
|
33
|
+
/** False in the light tier, on a missing OS source, and on the first tick
|
|
34
|
+
* (no baseline). The UI must render "n/a", never 0 B/s. */
|
|
35
|
+
available: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface GpuMetrics {
|
|
39
|
+
name: string;
|
|
40
|
+
/** 0-100. */
|
|
41
|
+
utilPercent: number;
|
|
42
|
+
vramUsedMB: number;
|
|
43
|
+
vramTotalMB: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SystemMetrics {
|
|
47
|
+
cpu: CpuMetrics;
|
|
48
|
+
mem: MemoryMetrics;
|
|
49
|
+
/** light tier: available:false. */
|
|
50
|
+
disk: RateMetrics;
|
|
51
|
+
/** light tier: available:false. */
|
|
52
|
+
net: RateMetrics;
|
|
53
|
+
/** light tier: []. Empty also when no NVIDIA GPU — the UI hides the card. */
|
|
54
|
+
gpus: GpuMetrics[];
|
|
55
|
+
/** light tier: 0. Processes the collector could see this tick. */
|
|
56
|
+
processCount: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ProcessInfo {
|
|
60
|
+
pid: number;
|
|
61
|
+
/** -1 when the parent is unknown. */
|
|
62
|
+
ppid: number;
|
|
63
|
+
/** Executable basename WITHOUT extension, lowercased for comparison by the
|
|
64
|
+
* guard: "explorer", "node", "chrome". Never "explorer.exe". */
|
|
65
|
+
name: string;
|
|
66
|
+
/** Command line, secrets redacted then truncated to 160 chars. Falls back to
|
|
67
|
+
* `name` when unreadable (Windows: ~53% of rows are unreadable unelevated). */
|
|
68
|
+
command: string;
|
|
69
|
+
/** Instantaneous machine-normalised CPU%: deltaCpuMs / (wallMs × coreCount) × 100.
|
|
70
|
+
* Always 0 on a process's first observed tick. */
|
|
71
|
+
cpu: number;
|
|
72
|
+
ramMB: number;
|
|
73
|
+
/** Epoch ms UTC; 0 when unknown. Identity guard for CPU deltas, grouping and kill. */
|
|
74
|
+
startedAt: number;
|
|
75
|
+
/** PPM server, supervisor, edge forwarder, their descendants, PPM-managed
|
|
76
|
+
* cloudflared. Drives the "PPM only" filter — NOT a safety mechanism. */
|
|
77
|
+
ppm: boolean;
|
|
78
|
+
/** The server will refuse to kill it. Produced by the same guard the route
|
|
79
|
+
* enforces, so the disabled button and the 403 cannot disagree. */
|
|
80
|
+
protected: boolean;
|
|
81
|
+
/** Per-process throughput/GPU. `undefined` means this OS or tier cannot
|
|
82
|
+
* measure it — the UI must render "—", never 0. All are omitted from the
|
|
83
|
+
* JSON frame when undefined, so an unsupported host pays nothing.
|
|
84
|
+
*
|
|
85
|
+
* Bytes/second, delta of a cumulative OS counter over the tick's wall
|
|
86
|
+
* interval; 0 on a process's first observed tick, like `cpu`. */
|
|
87
|
+
diskReadBps?: number;
|
|
88
|
+
diskWriteBps?: number;
|
|
89
|
+
/** Sum of this pid's GPU engine busy percentages (3D + Copy + Video + …),
|
|
90
|
+
* clamped 0-100. Rate over the counter's OWN clock, not the wall tick. */
|
|
91
|
+
gpuPct?: number;
|
|
92
|
+
/** Dedicated (on-card) VRAM in MB attributed to this pid. */
|
|
93
|
+
gpuMemMB?: number;
|
|
94
|
+
/** macOS only: per-process network has no supported source on Windows (ETW
|
|
95
|
+
* only) or Linux (packet capture). */
|
|
96
|
+
netInBps?: number;
|
|
97
|
+
netOutBps?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ProcessGroup {
|
|
101
|
+
/** Stable across ticks: "root:<pid>" for an ancestor roll-up, "exe:<name>" for
|
|
102
|
+
* the orphan bucket. React key and history-series key. */
|
|
103
|
+
key: string;
|
|
104
|
+
label: string;
|
|
105
|
+
/** Roll-up root pid; null for an "exe:" bucket. */
|
|
106
|
+
rootPid: number | null;
|
|
107
|
+
cpu: number;
|
|
108
|
+
ramMB: number;
|
|
109
|
+
count: number;
|
|
110
|
+
/** True when any member is PPM-owned. */
|
|
111
|
+
ppm: boolean;
|
|
112
|
+
/** Member pids, CPU-desc. Full rows are in `MetricsSnapshot.processes`. */
|
|
113
|
+
pids: number[];
|
|
114
|
+
/** Roll-ups of the optional per-process columns, with the same optionality:
|
|
115
|
+
* the sum over the members that HAVE a value, and `undefined` when no member
|
|
116
|
+
* has one — so "nothing measurable" never renders as a hard 0. */
|
|
117
|
+
diskReadBps?: number;
|
|
118
|
+
diskWriteBps?: number;
|
|
119
|
+
/** Summed engine busy across members, clamped 0-100. */
|
|
120
|
+
gpuPct?: number;
|
|
121
|
+
gpuMemMB?: number;
|
|
122
|
+
netInBps?: number;
|
|
123
|
+
netOutBps?: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Per-host availability of the optional process columns. A column is offered
|
|
127
|
+
* once the collector has actually produced a value for it on this host. */
|
|
128
|
+
export interface ProcessColumnAvailability {
|
|
129
|
+
disk: boolean;
|
|
130
|
+
gpu: boolean;
|
|
131
|
+
net: boolean;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** CLIENT-SIDE history element: aggregates only. There is no server ring. */
|
|
135
|
+
export interface MetricsHistoryPoint {
|
|
136
|
+
ts: number;
|
|
137
|
+
system: SystemMetrics;
|
|
138
|
+
/** group.key → roll-up, so group sparklines survive across ticks. */
|
|
139
|
+
groups: Record<string, { cpu: number; ramMB: number }>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface MetricsSnapshot {
|
|
143
|
+
ts: number;
|
|
144
|
+
platform: MetricsPlatform;
|
|
145
|
+
tier: MetricsTier;
|
|
146
|
+
/** Poll cadence in ms for THIS tier, so the client can label chart axes. */
|
|
147
|
+
intervalMs: number;
|
|
148
|
+
system: SystemMetrics;
|
|
149
|
+
/** Empty in the light tier. */
|
|
150
|
+
groups: ProcessGroup[];
|
|
151
|
+
/** Empty in the light tier. Latest snapshot only — never retained. */
|
|
152
|
+
processes: ProcessInfo[];
|
|
153
|
+
/** Which optional process columns THIS host can actually fill, so the UI can
|
|
154
|
+
* hide the unavailable ones instead of showing a wall of "—". All false in
|
|
155
|
+
* the light tier (no process rows at all). */
|
|
156
|
+
processColumns: ProcessColumnAvailability;
|
|
157
|
+
/** DEPRECATED, remove one release after this ships. Mirrors cpu/mem/count so a
|
|
158
|
+
* PWA-cached old bundle's `resource-status-bar.tsx:41` destructure of
|
|
159
|
+
* `latest.total` does not TypeError on every page. */
|
|
160
|
+
total: { cpu: number; ramMB: number; processCount: number };
|
|
161
|
+
/** Non-fatal collector failures, human readable. Rendered in the UI. */
|
|
162
|
+
warnings: string[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface KillProcessRequest {
|
|
166
|
+
pid: number;
|
|
167
|
+
/** Identity guard: the `startedAt` the client saw. The server re-queries the
|
|
168
|
+
* live process and returns 409 on mismatch, so a recycled pid cannot be
|
|
169
|
+
* killed against a stale name. */
|
|
170
|
+
startedAt: number;
|
|
171
|
+
/** Kill descendants too (`taskkill /T` on Windows, collected tree on POSIX). */
|
|
172
|
+
tree?: boolean;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface KillProcessResult {
|
|
176
|
+
pid: number;
|
|
177
|
+
tree: boolean;
|
|
178
|
+
method: "taskkill" | "signal";
|
|
179
|
+
/** Pids actually signalled. On win32 with tree:true this is always `[pid]` —
|
|
180
|
+
* `taskkill /T` walks and kills the tree inside the OS and reports no member
|
|
181
|
+
* list, so the real count is unknowable from here. */
|
|
182
|
+
killed: number[];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Sort columns offered by the process table. `disk` sorts by read + write,
|
|
186
|
+
* `net` by in + out; rows without a value sort last. */
|
|
187
|
+
export type SortKey = "cpu" | "ram" | "disk" | "gpu" | "gpuMem" | "net" | "name" | null;
|
|
188
|
+
export type SortDir = "asc" | "desc";
|
|
189
|
+
|
|
190
|
+
/** Full-tier poll cadence. A Windows tick (one CIM round trip) costs ~175-200 ms,
|
|
191
|
+
* so 2 s leaves an order of magnitude of headroom before ticks would overlap. */
|
|
192
|
+
export const METRICS_INTERVAL_MS = 2000;
|
|
193
|
+
/** Light-tier poll cadence — node:os only, costs <0.1 ms. */
|
|
194
|
+
export const METRICS_LIGHT_INTERVAL_MS = 5000;
|
|
195
|
+
/** Client history cap: 30 min at METRICS_INTERVAL_MS. */
|
|
196
|
+
export const METRICS_HISTORY_MAX = 900;
|
|
197
|
+
/** Subscriber lease: client pings every 10 s, server reaps after 30 s silence. */
|
|
198
|
+
export const METRICS_PING_INTERVAL_MS = 10_000;
|
|
199
|
+
export const METRICS_LEASE_TIMEOUT_MS = 30_000;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Loader2 } from "lucide-react";
|
|
3
3
|
import { Button } from "@/components/ui/button";
|
|
4
|
-
import { rawMediaUrl, transcodeMediaUrl } from "@/lib/media-url";
|
|
4
|
+
import { newMediaSessionId, rawMediaUrl, stopTranscode, transcodeMediaUrl } from "@/lib/media-url";
|
|
5
5
|
import { VideoPlayerControls } from "./video-player-controls";
|
|
6
6
|
import { SPEED_STEPS } from "./video-settings-menu";
|
|
7
7
|
import { useVideoKeyboardShortcuts, type VideoPlayerActions } from "./use-video-keyboard-shortcuts";
|
|
@@ -30,7 +30,13 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
30
30
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
31
31
|
const stageRef = useRef<HTMLDivElement>(null);
|
|
32
32
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
33
|
-
|
|
33
|
+
// Whether the user wants playback. Read after a transcode restart to decide between
|
|
34
|
+
// resuming and staying paused; derived from user actions, not `video.paused`, because
|
|
35
|
+
// the element reports paused while a fresh stream is still loading.
|
|
36
|
+
const wantPlayRef = useRef(true);
|
|
37
|
+
// One id per mounted player: the server replaces (kills) this player's previous
|
|
38
|
+
// ffmpeg job on every seek and stops it when told the player is gone.
|
|
39
|
+
const [sessionId] = useState(newMediaSessionId);
|
|
34
40
|
|
|
35
41
|
const [start, setStart] = useState(0);
|
|
36
42
|
const [elapsed, setElapsed] = useState(0);
|
|
@@ -47,7 +53,7 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
47
53
|
const [error, setError] = useState<string | null>(null);
|
|
48
54
|
|
|
49
55
|
const isTranscode = mode === "transcode";
|
|
50
|
-
const src = isTranscode ? transcodeMediaUrl(filePath, projectName, start) : rawMediaUrl(filePath, projectName);
|
|
56
|
+
const src = isTranscode ? transcodeMediaUrl(filePath, projectName, start, sessionId) : rawMediaUrl(filePath, projectName);
|
|
51
57
|
const duration = isTranscode ? probeDuration : nativeDuration;
|
|
52
58
|
const position = scrub ?? (isTranscode ? start + elapsed : elapsed);
|
|
53
59
|
|
|
@@ -59,14 +65,26 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
59
65
|
useEffect(() => { const v = videoRef.current; if (v) { v.volume = volume; v.muted = muted; } }, [volume, muted]);
|
|
60
66
|
useEffect(() => { const v = videoRef.current; if (v) v.playbackRate = speed; }, [speed, src]);
|
|
61
67
|
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
68
|
+
// Leaving the player must stop its ffmpeg job explicitly: a detached <video> keeps its
|
|
69
|
+
// fetch alive until garbage-collected, and a proxy such as Cloudflare Tunnel keeps the
|
|
70
|
+
// origin request open even after the browser has dropped it. Emptying the source aborts
|
|
71
|
+
// the local load; the DELETE tells the server. `isConnected` guards StrictMode's
|
|
72
|
+
// simulated unmount, where the element stays in the DOM and React would not re-apply
|
|
73
|
+
// the src attribute afterwards. `pagehide` covers reload/close, where cleanups never run.
|
|
66
74
|
useEffect(() => {
|
|
67
75
|
const v = videoRef.current;
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
const stop = () => { if (isTranscode) stopTranscode(filePath, projectName, sessionId); };
|
|
77
|
+
window.addEventListener("pagehide", stop);
|
|
78
|
+
return () => {
|
|
79
|
+
window.removeEventListener("pagehide", stop);
|
|
80
|
+
if (v && !v.isConnected) {
|
|
81
|
+
v.pause();
|
|
82
|
+
v.removeAttribute("src");
|
|
83
|
+
v.load();
|
|
84
|
+
stop();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}, [filePath, projectName, isTranscode, sessionId]);
|
|
70
88
|
|
|
71
89
|
useEffect(() => {
|
|
72
90
|
const onFs = () => setFullscreen(document.fullscreenElement === rootRef.current);
|
|
@@ -90,12 +108,17 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
90
108
|
if (!isTranscode) { if (v) v.currentTime = clamped; return; }
|
|
91
109
|
// Restarting ffmpeg for a sub-second nudge is wasteful.
|
|
92
110
|
if (Math.abs(clamped - (start + (v?.currentTime ?? 0))) < 0.5) return;
|
|
93
|
-
resumeRef.current = !(v?.paused ?? false);
|
|
94
111
|
setStart(clamped);
|
|
95
112
|
}, [duration, isTranscode, start]);
|
|
96
113
|
|
|
97
114
|
const actions = useMemo<VideoPlayerActions>(() => ({
|
|
98
|
-
togglePlay: () => {
|
|
115
|
+
togglePlay: () => {
|
|
116
|
+
const v = videoRef.current;
|
|
117
|
+
if (!v) return;
|
|
118
|
+
wantPlayRef.current = v.paused;
|
|
119
|
+
if (v.paused) void v.play().catch(() => {});
|
|
120
|
+
else v.pause();
|
|
121
|
+
},
|
|
99
122
|
seekBy: (d) => seekTo((isTranscode ? start : 0) + (videoRef.current?.currentTime ?? 0) + d),
|
|
100
123
|
toggleFullscreen: () => {
|
|
101
124
|
if (document.fullscreenElement) void document.exitFullscreen();
|
|
@@ -113,14 +136,8 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
113
136
|
const onKeyDown = useVideoKeyboardShortcuts(actions);
|
|
114
137
|
const fitted = fittedMaxSize(transform, box);
|
|
115
138
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
<div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary p-4 text-center">
|
|
119
|
-
<p className="text-sm">{error}</p>
|
|
120
|
-
<Button variant="outline" size="sm" onClick={() => { setError(null); setStart((s) => s + 0.001); }}>Retry</Button>
|
|
121
|
-
</div>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
139
|
+
// Retry re-requests the same position; the nudge makes the URL differ so the element reloads.
|
|
140
|
+
const retry = () => { setError(null); setStart((s) => s + 0.001); };
|
|
124
141
|
|
|
125
142
|
return (
|
|
126
143
|
<div
|
|
@@ -144,10 +161,13 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
144
161
|
preload={isTranscode ? "auto" : "metadata"}
|
|
145
162
|
style={{ transform: toCssTransform(transform), maxWidth: fitted.maxWidth || undefined, maxHeight: fitted.maxHeight || undefined }}
|
|
146
163
|
onLoadedMetadata={(e) => {
|
|
164
|
+
// Re-apply user settings: a fresh load (seek restart, retry) resets the element.
|
|
147
165
|
const v = e.currentTarget;
|
|
148
166
|
v.playbackRate = speed;
|
|
167
|
+
v.volume = volume;
|
|
168
|
+
v.muted = muted;
|
|
149
169
|
if (!isTranscode && Number.isFinite(v.duration)) setNativeDuration(v.duration);
|
|
150
|
-
if (isTranscode && !
|
|
170
|
+
if (isTranscode && !wantPlayRef.current) v.pause();
|
|
151
171
|
}}
|
|
152
172
|
onPlay={() => setPlaying(true)}
|
|
153
173
|
onPause={() => setPlaying(false)}
|
|
@@ -155,13 +175,25 @@ export function VideoPlayer({ filePath, projectName, mode, probeDuration = null,
|
|
|
155
175
|
onPlaying={() => setWaiting(false)}
|
|
156
176
|
onCanPlay={() => setWaiting(false)}
|
|
157
177
|
onTimeUpdate={(e) => setElapsed(e.currentTarget.currentTime)}
|
|
158
|
-
onEnded={() => setPlaying(false)}
|
|
178
|
+
onEnded={() => { setPlaying(false); wantPlayRef.current = false; }}
|
|
159
179
|
onError={() => {
|
|
160
180
|
if (!isTranscode) onNativeError?.();
|
|
161
181
|
else setError("Transcoding failed. ffmpeg may have stopped or the file is corrupt.");
|
|
162
182
|
}}
|
|
163
183
|
/>
|
|
164
|
-
{waiting && <Loader2 className="absolute size-8 animate-spin text-white/80 pointer-events-none" />}
|
|
184
|
+
{waiting && !error && <Loader2 className="absolute size-8 animate-spin text-white/80 pointer-events-none" />}
|
|
185
|
+
{/* Overlay rather than a replacement tree: the <video> stays mounted so volume,
|
|
186
|
+
mute and speed survive a retry. */}
|
|
187
|
+
{error && (
|
|
188
|
+
<div
|
|
189
|
+
className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-black/85 text-text-secondary p-4 text-center"
|
|
190
|
+
onClick={(e) => e.stopPropagation()}
|
|
191
|
+
onDoubleClick={(e) => e.stopPropagation()}
|
|
192
|
+
>
|
|
193
|
+
<p className="text-sm">{error}</p>
|
|
194
|
+
<Button variant="outline" size="sm" className="h-11 px-4" onClick={retry}>Retry</Button>
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
165
197
|
</div>
|
|
166
198
|
|
|
167
199
|
<VideoPlayerControls
|
|
@@ -22,6 +22,7 @@ export interface WindowContentProps {
|
|
|
22
22
|
export const WINDOW_CONTENT: Record<WindowKind, LazyExoticComponent<ComponentType<WindowContentProps>>> = {
|
|
23
23
|
explorer: lazy(() => import("@/components/os-explorer/explorer-window-content")),
|
|
24
24
|
"team-member": lazy(() => import("@/components/chat/team-member-window-content")),
|
|
25
|
+
"system-monitor": lazy(() => import("@/components/system/system-monitor-window-content")),
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -46,6 +47,7 @@ export function windowTitle(kind: WindowKind, payload?: Record<string, unknown>)
|
|
|
46
47
|
const member = payload?.memberName;
|
|
47
48
|
return typeof member === "string" && member ? `Session — ${member}` : "Team member";
|
|
48
49
|
}
|
|
50
|
+
if (kind === "system-monitor") return "System Monitor";
|
|
49
51
|
const path = payload?.path;
|
|
50
52
|
if (kind === "explorer" && typeof path === "string" && path) {
|
|
51
53
|
const segments = path.split(/[\\/]/).filter(Boolean);
|
|
@@ -20,7 +20,7 @@ export interface PersistedWindow {
|
|
|
20
20
|
payload?: Record<string, unknown>;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const KINDS: WindowKind[] = ["explorer"];
|
|
23
|
+
const KINDS: WindowKind[] = ["explorer", "system-monitor"];
|
|
24
24
|
const STATES: WindowVisualState[] = ["normal", "maximized", "minimized"];
|
|
25
25
|
|
|
26
26
|
function isRect(v: unknown): v is Rect {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { Rect } from "./window-geometry";
|
|
4
4
|
|
|
5
5
|
/** Content families the layer can host. The registry maps each kind to its body component. */
|
|
6
|
-
export type WindowKind = "explorer" | "team-member";
|
|
6
|
+
export type WindowKind = "explorer" | "team-member" | "system-monitor";
|
|
7
7
|
|
|
8
8
|
export type WindowVisualState = "normal" | "maximized" | "minimized";
|
|
9
9
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { KillProcessRequest, ProcessInfo } from "../../../types/system-metrics";
|
|
2
|
+
|
|
3
|
+
/** Builds the kill request body from a process row's own identity fields — pulled out
|
|
4
|
+
* of the dialog's confirm handler so the payload shape is unit-testable without
|
|
5
|
+
* mounting the dialog. `startedAt` travels unchanged so the server can 409 a pid the
|
|
6
|
+
* OS recycled between the snapshot and the click. */
|
|
7
|
+
export function buildKillRequest(proc: ProcessInfo, tree: boolean): KillProcessRequest {
|
|
8
|
+
return { pid: proc.pid, startedAt: proc.startedAt, tree };
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Shared scale resolution for the canvas charts: a fixed ceiling (percentage axes)
|
|
2
|
+
* or autoscale to the max of the visible window (byte-rate axes) — pulled out of
|
|
3
|
+
* `sparkline-canvas.tsx`/`metric-chart-canvas.tsx` so it is unit-testable without a
|
|
4
|
+
* canvas context. This is the fix for the bug where a 3% CPU reading rendered as a
|
|
5
|
+
* full-height sparkline: the old code always did `Math.max(...data, 1)`. */
|
|
6
|
+
export function resolveScaleMax(data: number[], maxValue?: number): number {
|
|
7
|
+
if (maxValue !== undefined) return maxValue;
|
|
8
|
+
return Math.max(...data, 1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** The minimal surface of `CSSStyleDeclaration` this needs — lets a test pass a plain
|
|
12
|
+
* fake object instead of a real computed style. */
|
|
13
|
+
export interface ComputedColorSource {
|
|
14
|
+
getPropertyValue(property: string): string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Canvas 2D's `strokeStyle`/`fillStyle` parse as a standalone CSS `<color>` — `var(...)`
|
|
18
|
+
* is not resolvable without an element context, so assigning `"var(--color-primary)"`
|
|
19
|
+
* directly is silently ignored and the shape draws black. Resolve once per draw call
|
|
20
|
+
* against the canvas's own computed style (custom properties inherit from the
|
|
21
|
+
* document, so any element's computed style works) and fall back to the original
|
|
22
|
+
* string when it is not a `var(...)` reference or resolves to nothing. */
|
|
23
|
+
export function resolveCssColor(color: string, styles: ComputedColorSource): string {
|
|
24
|
+
const match = /^var\((--[\w-]+)\)$/.exec(color.trim());
|
|
25
|
+
if (!match) return color;
|
|
26
|
+
const resolved = styles.getPropertyValue(match[1]!).trim();
|
|
27
|
+
return resolved || color;
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
|
|
3
|
+
function loadColor(pct: number): string {
|
|
4
|
+
if (pct > 80) return "bg-error";
|
|
5
|
+
if (pct > 50) return "bg-warning";
|
|
6
|
+
return "bg-success";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** One vertical bar per core, tooltip shows the exact percentage. `min-h-11` keeps the
|
|
10
|
+
* strip out of sub-44px touch-row territory even though nothing here is clickable —
|
|
11
|
+
* a future click-to-isolate-core affordance should not need a layout change. */
|
|
12
|
+
export function CpuCoreBars({ cores }: { cores: number[] }) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className="flex items-end gap-0.5 min-h-11 w-full"
|
|
16
|
+
data-testid="sysmon-core-bars"
|
|
17
|
+
data-core-count={cores.length}
|
|
18
|
+
>
|
|
19
|
+
{cores.map((pct, i) => (
|
|
20
|
+
<div
|
|
21
|
+
key={i}
|
|
22
|
+
className="flex-1 h-11 flex items-end rounded-sm overflow-hidden bg-surface-hover"
|
|
23
|
+
title={`Core ${i}: ${pct.toFixed(0)}%`}
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
className={cn("w-full transition-[height]", loadColor(pct))}
|
|
27
|
+
style={{ height: `${Math.max(2, Math.min(100, pct))}%` }}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|