@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
|
@@ -1,88 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-machine metrics routes, mounted under `/api/system` behind auth.
|
|
3
|
+
*
|
|
4
|
+
* GET /resources?processes=0|1 latest snapshot for that tier (or null)
|
|
5
|
+
* GET /resources/stream?processes=0|1 SSE: `session` frame, then `snapshot` frames
|
|
6
|
+
* POST /resources/stream/:sid/ping renew the subscriber lease
|
|
7
|
+
* DELETE /resources/stream/:sid drop the subscriber
|
|
8
|
+
* POST /resources/kill guarded kill (JSON + X-PPM-Request header)
|
|
9
|
+
*/
|
|
1
10
|
import { Hono } from "hono";
|
|
2
|
-
import {
|
|
11
|
+
import type { MetricsSnapshot, MetricsTier } from "../../types/system-metrics.ts";
|
|
3
12
|
import { ok, err } from "../../types/api.ts";
|
|
13
|
+
import {
|
|
14
|
+
systemMetricsService, MAX_STREAM_SUBSCRIBERS, type SystemMetricsService,
|
|
15
|
+
} from "../../services/system-metrics/system-metrics.service.ts";
|
|
16
|
+
import { isValidSid } from "../../services/system-metrics/metrics-subscriber-registry.ts";
|
|
4
17
|
|
|
5
|
-
|
|
18
|
+
/** Frames a stalled client failed to take before the stream is closed. Metrics
|
|
19
|
+
* are lossy by nature; `ReadableStream`'s queue is not, and 30-60 KB every 2 s
|
|
20
|
+
* toward a dead mobile client is ~54 MB/hour of heap. */
|
|
21
|
+
export const MAX_DROPPED_FRAMES = 10;
|
|
6
22
|
|
|
7
|
-
const
|
|
8
|
-
let sseClientCount = 0;
|
|
23
|
+
const tierOf = (raw: string | undefined): MetricsTier => (raw === "1" || raw === "true" ? "full" : "light");
|
|
9
24
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return c.json(ok(resourceMonitor.getLatest()));
|
|
13
|
-
});
|
|
25
|
+
export function createResourceRoutes(service: SystemMetricsService = systemMetricsService): Hono {
|
|
26
|
+
const routes = new Hono();
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
resourceRoutes.get("/resources/history", (c) => {
|
|
17
|
-
return c.json(ok(resourceMonitor.getHistory()));
|
|
18
|
-
});
|
|
28
|
+
routes.get("/resources", (c) => c.json(ok(service.getLatest(tierOf(c.req.query("processes"))))));
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return c.json(err("No resource data available"), 400);
|
|
30
|
-
}
|
|
31
|
-
const allPids = snapshot.groups.flatMap((g) => g.processes.map((p) => p.pid));
|
|
32
|
-
if (!allPids.includes(pid)) {
|
|
33
|
-
return c.json(err("PID not in PPM process tree"), 403);
|
|
34
|
-
}
|
|
35
|
-
// Don't allow killing the server itself
|
|
36
|
-
if (pid === process.pid) {
|
|
37
|
-
return c.json(err("Cannot kill PPM server process"), 403);
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
40
|
-
process.kill(pid, "SIGTERM");
|
|
41
|
-
return c.json(ok({ pid, signal: "SIGTERM" }));
|
|
42
|
-
} catch (e: any) {
|
|
43
|
-
return c.json(err(`Failed to kill PID ${pid}: ${e.message}`), 500);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
30
|
+
routes.get("/resources/stream", (c) => {
|
|
31
|
+
const tier = tierOf(c.req.query("processes"));
|
|
32
|
+
// A 429 is only possible before the body starts, so reap expired leases and
|
|
33
|
+
// check the cap here — BEFORE the stream is built, because `start()` runs
|
|
34
|
+
// synchronously at construction. subscribe() re-checks in case two opens race.
|
|
35
|
+
service.reapExpired();
|
|
36
|
+
if (service.liveCount() >= MAX_STREAM_SUBSCRIBERS) {
|
|
37
|
+
return c.json(err("Too many metrics subscribers"), 429);
|
|
38
|
+
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return c.json({ ok: false, error: "Too many SSE clients" }, 429);
|
|
51
|
-
}
|
|
40
|
+
const encoder = new TextEncoder();
|
|
41
|
+
let sid: string | null = null;
|
|
42
|
+
let drops = 0;
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
45
|
+
start(controller) {
|
|
46
|
+
const close = () => { try { controller.close(); } catch { /* already closed */ } };
|
|
47
|
+
const result = service.subscribe({
|
|
48
|
+
tier,
|
|
49
|
+
close,
|
|
50
|
+
deliver: (snapshot: MetricsSnapshot) => {
|
|
51
|
+
// `enqueue` never throws on backpressure, only on a closed controller,
|
|
52
|
+
// so `desiredSize` is the only signal that the client stopped reading.
|
|
53
|
+
if ((controller.desiredSize ?? 1) <= 0) {
|
|
54
|
+
if (++drops >= MAX_DROPPED_FRAMES) {
|
|
55
|
+
if (sid) service.unsubscribe(sid);
|
|
56
|
+
close();
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
drops = 0;
|
|
61
|
+
try {
|
|
62
|
+
controller.enqueue(encoder.encode(`event: snapshot\ndata: ${JSON.stringify(snapshot)}\n\n`));
|
|
63
|
+
} catch {
|
|
64
|
+
if (sid) service.unsubscribe(sid);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
if (!result) {
|
|
69
|
+
controller.enqueue(encoder.encode(`event: error\ndata: ${JSON.stringify({ error: "Too many metrics subscribers" })}\n\n`));
|
|
70
|
+
close();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
sid = result.sid;
|
|
74
|
+
controller.enqueue(encoder.encode("retry: 5000\n\n"));
|
|
75
|
+
controller.enqueue(encoder.encode(`event: session\ndata: ${JSON.stringify(result)}\n\n`));
|
|
76
|
+
},
|
|
77
|
+
cancel() {
|
|
78
|
+
// Best effort only — a proxy may keep this request alive after the
|
|
79
|
+
// browser left, which is what the lease + DELETE route are for.
|
|
80
|
+
if (sid) service.unsubscribe(sid);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
return new Response(stream, {
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "text/event-stream",
|
|
87
|
+
"Cache-Control": "no-cache",
|
|
88
|
+
"Connection": "keep-alive",
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
});
|
|
62
92
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Client disconnected — cleanup happens in cancel
|
|
69
|
-
}
|
|
70
|
-
};
|
|
93
|
+
routes.post("/resources/stream/:sid/ping", (c) => {
|
|
94
|
+
const sid = c.req.param("sid");
|
|
95
|
+
if (!isValidSid(sid) || !service.ping(sid)) return c.json(err("Unknown or expired stream session"), 404);
|
|
96
|
+
return c.json(ok({ alive: true }));
|
|
97
|
+
});
|
|
71
98
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (callbackRef) resourceMonitor.unsubscribe(callbackRef);
|
|
77
|
-
callbackRef = null;
|
|
78
|
-
},
|
|
99
|
+
routes.delete("/resources/stream/:sid", (c) => {
|
|
100
|
+
const sid = c.req.param("sid");
|
|
101
|
+
const stopped = isValidSid(sid) ? service.unsubscribe(sid) : false;
|
|
102
|
+
return c.json(ok({ stopped }));
|
|
79
103
|
});
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
routes.post("/resources/kill", async (c) => {
|
|
106
|
+
// CSRF hardening for the auth-disabled configuration: a cross-origin HTML
|
|
107
|
+
// form can set neither a JSON content type nor a custom header, so both
|
|
108
|
+
// force a preflight that the browser will refuse.
|
|
109
|
+
const contentType = c.req.header("content-type") ?? "";
|
|
110
|
+
if (!contentType.toLowerCase().startsWith("application/json")) {
|
|
111
|
+
return c.json(err("Content-Type must be application/json"), 400);
|
|
112
|
+
}
|
|
113
|
+
if (c.req.header("x-ppm-request") !== "1") return c.json(err("Missing X-PPM-Request header"), 400);
|
|
114
|
+
let body: unknown;
|
|
115
|
+
try {
|
|
116
|
+
body = await c.req.json();
|
|
117
|
+
} catch {
|
|
118
|
+
return c.json(err("Invalid JSON body"), 400);
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const outcome = await service.kill(body);
|
|
122
|
+
return c.json(outcome.body, outcome.status);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
// The live re-query itself failed (collector down, session restarting);
|
|
125
|
+
// nothing was signalled, so the client can simply retry.
|
|
126
|
+
console.error("[SystemMetrics] kill re-query failed:", (e as Error)?.message ?? e);
|
|
127
|
+
return c.json(err("Could not verify the process — try again"), 500);
|
|
128
|
+
}
|
|
87
129
|
});
|
|
88
|
-
|
|
130
|
+
|
|
131
|
+
return routes;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const resourceRoutes = createResourceRoutes();
|
|
@@ -205,18 +205,90 @@ export function buildRegDeleteCommand(): string[] {
|
|
|
205
205
|
// by third-party "startup cleaner" utilities that only target Run keys and
|
|
206
206
|
// the Startup folder. No admin required for a current-user logon task.
|
|
207
207
|
|
|
208
|
-
/**
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
/**
|
|
209
|
+
* The account the task runs as, and whose logon fires it — `DOMAIN\user`, or
|
|
210
|
+
* bare user name when the machine reports no domain.
|
|
211
|
+
*/
|
|
212
|
+
export function getCurrentWindowsUserId(): string {
|
|
213
|
+
const user = process.env.USERNAME ?? "";
|
|
214
|
+
const domain = process.env.USERDOMAIN ?? process.env.COMPUTERNAME ?? "";
|
|
215
|
+
return domain ? `${domain}\\${user}` : user;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function getTaskXmlPath(): string {
|
|
219
|
+
return resolve(homedir(), ".ppm", "ppm-task.xml");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Task Scheduler XML for the At-logon task.
|
|
224
|
+
*
|
|
225
|
+
* `schtasks /SC ONLOGON` cannot express *which* user's logon fires the task:
|
|
226
|
+
* it always emits a LogonTrigger with no UserId, meaning "any user logs on",
|
|
227
|
+
* and registering that is an all-users change the Task Scheduler refuses for a
|
|
228
|
+
* standard account — the create fails with "ERROR: Access is denied." even
|
|
229
|
+
* when /RU names the caller, because /RU sets the run-as principal, not the
|
|
230
|
+
* trigger scope. Registering from XML lets the trigger carry a UserId, which
|
|
231
|
+
* scopes it to this account and needs no elevation.
|
|
232
|
+
*
|
|
233
|
+
* ExecutionTimeLimit must be PT0S (unlimited): schtasks would have defaulted to
|
|
234
|
+
* PT72H and killed the supervisor after three days of uptime.
|
|
235
|
+
*/
|
|
236
|
+
export function generateTaskXml(vbsPath: string, userId = getCurrentWindowsUserId()): string {
|
|
237
|
+
const user = escapeXml(userId);
|
|
238
|
+
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
239
|
+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
240
|
+
<RegistrationInfo>
|
|
241
|
+
<Description>PPM - starts the PPM supervisor at logon</Description>
|
|
242
|
+
<URI>\\${TASK_NAME}</URI>
|
|
243
|
+
</RegistrationInfo>
|
|
244
|
+
<Triggers>
|
|
245
|
+
<LogonTrigger>
|
|
246
|
+
<Enabled>true</Enabled>
|
|
247
|
+
<UserId>${user}</UserId>
|
|
248
|
+
</LogonTrigger>
|
|
249
|
+
</Triggers>
|
|
250
|
+
<Principals>
|
|
251
|
+
<Principal id="Author">
|
|
252
|
+
<UserId>${user}</UserId>
|
|
253
|
+
<LogonType>InteractiveToken</LogonType>
|
|
254
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
255
|
+
</Principal>
|
|
256
|
+
</Principals>
|
|
257
|
+
<Settings>
|
|
258
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
259
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
260
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
261
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
262
|
+
<StartWhenAvailable>false</StartWhenAvailable>
|
|
263
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
264
|
+
<IdleSettings>
|
|
265
|
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
266
|
+
<RestartOnIdle>false</RestartOnIdle>
|
|
267
|
+
</IdleSettings>
|
|
268
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
269
|
+
<Enabled>true</Enabled>
|
|
270
|
+
<Hidden>false</Hidden>
|
|
271
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
272
|
+
<WakeToRun>false</WakeToRun>
|
|
273
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
274
|
+
<Priority>7</Priority>
|
|
275
|
+
</Settings>
|
|
276
|
+
<Actions Context="Author">
|
|
277
|
+
<Exec>
|
|
278
|
+
<Command>wscript.exe</Command>
|
|
279
|
+
<Arguments>"${escapeXml(vbsPath)}"</Arguments>
|
|
280
|
+
</Exec>
|
|
281
|
+
</Actions>
|
|
282
|
+
</Task>
|
|
283
|
+
`;
|
|
211
284
|
}
|
|
212
285
|
|
|
213
|
-
/** Build schtasks command to create the At-logon task (no admin) */
|
|
214
|
-
export function buildSchtasksCreateCommand(
|
|
286
|
+
/** Build schtasks command to create the At-logon task from XML (no admin) */
|
|
287
|
+
export function buildSchtasksCreateCommand(xmlPath: string): string[] {
|
|
215
288
|
return [
|
|
216
289
|
"schtasks", "/Create",
|
|
217
290
|
"/TN", TASK_NAME,
|
|
218
|
-
"/
|
|
219
|
-
"/SC", "ONLOGON",
|
|
291
|
+
"/XML", xmlPath,
|
|
220
292
|
"/F",
|
|
221
293
|
];
|
|
222
294
|
}
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
getServicePath,
|
|
12
12
|
generateVbsWrapper,
|
|
13
13
|
getVbsPath,
|
|
14
|
+
generateTaskXml,
|
|
15
|
+
getTaskXmlPath,
|
|
14
16
|
buildRegDeleteCommand,
|
|
15
17
|
buildSchtasksCreateCommand,
|
|
16
18
|
buildSchtasksDeleteCommand,
|
|
@@ -300,18 +302,27 @@ function statusLinux(): AutoStartStatus {
|
|
|
300
302
|
|
|
301
303
|
async function enableWindows(config: AutoStartConfig): Promise<string> {
|
|
302
304
|
const vbsPath = getVbsPath();
|
|
305
|
+
const xmlPath = getTaskXmlPath();
|
|
303
306
|
const vbsDir = dirname(vbsPath);
|
|
304
307
|
|
|
305
308
|
if (!existsSync(vbsDir)) mkdirSync(vbsDir, { recursive: true });
|
|
306
309
|
writeFileSync(vbsPath, generateVbsWrapper(config));
|
|
307
310
|
|
|
311
|
+
// Task Scheduler only accepts a definition file encoded as UTF-16 and reads
|
|
312
|
+
// the BOM to detect it; a UTF-8 file is rejected as malformed XML.
|
|
313
|
+
writeFileSync(xmlPath, `\ufeff${generateTaskXml(vbsPath)}`, "utf16le");
|
|
314
|
+
|
|
308
315
|
// Register an At-logon scheduled task (no admin). More reliable than the
|
|
309
316
|
// HKCU Run key, which startup-cleaner utilities sweep and which can silently
|
|
310
317
|
// fail to fire at logon.
|
|
311
|
-
const cmd = buildSchtasksCreateCommand(
|
|
318
|
+
const cmd = buildSchtasksCreateCommand(xmlPath);
|
|
312
319
|
const result = Bun.spawnSync({ cmd, stdout: "pipe", stderr: "pipe" });
|
|
313
320
|
|
|
314
321
|
if (result.exitCode !== 0) {
|
|
322
|
+
// Leave nothing half-registered: a stale wrapper and definition on disk
|
|
323
|
+
// make the next `ppm autostart status` look configured when no task exists.
|
|
324
|
+
try { unlinkSync(vbsPath); } catch {}
|
|
325
|
+
try { unlinkSync(xmlPath); } catch {}
|
|
315
326
|
const err = result.stderr.toString().trim() || result.stdout.toString().trim();
|
|
316
327
|
throw new Error(`schtasks create failed: ${err}`);
|
|
317
328
|
}
|
|
@@ -336,9 +347,11 @@ async function disableWindows(): Promise<void> {
|
|
|
336
347
|
Bun.spawnSync({ cmd: buildSchtasksDeleteCommand(), stdout: "ignore", stderr: "ignore" });
|
|
337
348
|
Bun.spawnSync({ cmd: buildRegDeleteCommand(), stdout: "ignore", stderr: "ignore" });
|
|
338
349
|
|
|
339
|
-
// Remove VBS wrapper
|
|
350
|
+
// Remove VBS wrapper and task definition
|
|
340
351
|
const vbsPath = getVbsPath();
|
|
341
352
|
try { if (existsSync(vbsPath)) unlinkSync(vbsPath); } catch {}
|
|
353
|
+
const xmlPath = getTaskXmlPath();
|
|
354
|
+
try { if (existsSync(xmlPath)) unlinkSync(xmlPath); } catch {}
|
|
342
355
|
|
|
343
356
|
removeMetadata();
|
|
344
357
|
}
|
|
@@ -2,6 +2,7 @@ import { resolve, sep } from "node:path";
|
|
|
2
2
|
import { lstat, realpath } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { getPpmDir } from "./ppm-dir.ts";
|
|
5
|
+
import { getUploadsDir } from "./chat-upload-storage.service.ts";
|
|
5
6
|
import { realPathOrSelf, realPathOrSelfSync } from "./fs-ops/fs-real-path.ts";
|
|
6
7
|
|
|
7
8
|
// Re-exported so callers reach both the guards and the resolver they build on
|
|
@@ -84,13 +85,28 @@ export function isPpmDirPath(resolved: string): boolean {
|
|
|
84
85
|
return isInside(resolved, getPpmDir());
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Chat attachments the user uploaded through the UI. They sit under the PPM
|
|
90
|
+
* dir so chat history keeps resolving them across reboots, but they are
|
|
91
|
+
* ordinary user content, not credential material: the transcript has to render
|
|
92
|
+
* the very image the assistant just read back from that directory.
|
|
93
|
+
*/
|
|
94
|
+
export function isChatUploadPath(resolved: string): boolean {
|
|
95
|
+
return isInside(resolved, getUploadsDir());
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
/**
|
|
88
99
|
* Refuse the PPM directory subtree on read-style doors. It stores the config
|
|
89
100
|
* database with provider credentials and auth tokens, which must never be
|
|
90
101
|
* downloadable through a generic file route.
|
|
102
|
+
*
|
|
103
|
+
* Chat uploads are the one exception. Every caller applies this to the
|
|
104
|
+
* requested path *and* to its real path, so a symlink parked in the uploads
|
|
105
|
+
* directory still fails on the second call and cannot reach the rest of the
|
|
106
|
+
* PPM dir through the exception.
|
|
91
107
|
*/
|
|
92
108
|
export function assertNotPpmDir(resolved: string): void {
|
|
93
|
-
if (isPpmDirPath(resolved)) {
|
|
109
|
+
if (isPpmDirPath(resolved) && !isChatUploadPath(resolved)) {
|
|
94
110
|
throw Object.assign(new Error("Access denied"), { status: 403, code: "EDENIED" });
|
|
95
111
|
}
|
|
96
112
|
}
|
|
@@ -8,21 +8,36 @@
|
|
|
8
8
|
* by re-requesting with `start`, which becomes `-ss` *before* `-i` (fast keyframe
|
|
9
9
|
* seek; exact-frame seeking would decode from the file start every time).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Process lifetime is controlled explicitly, not only by the connection: a job is
|
|
12
|
+
* keyed by the player's `sessionId`, a new request for the same session kills the
|
|
13
|
+
* previous job, and the player sends a stop when it unmounts. Relying on the client
|
|
14
|
+
* disconnect alone is not enough — Cloudflare Tunnel (and other proxies) keep the
|
|
15
|
+
* origin request open and drain the body after the browser has gone, which left
|
|
16
|
+
* every seek's ffmpeg running to the end of the file and exhausted the job slots.
|
|
17
|
+
* Only the job's own PID is ever killed — never a blanket kill by image name.
|
|
14
18
|
*/
|
|
15
19
|
import { encoderArgs, getFfmpegCapabilities } from "./ffmpeg-capabilities.ts";
|
|
16
20
|
|
|
17
|
-
/** Parallel ffmpeg jobs allowed
|
|
18
|
-
export const MAX_CONCURRENT_TRANSCODES =
|
|
21
|
+
/** Parallel ffmpeg jobs allowed across all players (one per player after session replacement). */
|
|
22
|
+
export const MAX_CONCURRENT_TRANSCODES = 4;
|
|
19
23
|
let active = 0;
|
|
20
24
|
|
|
25
|
+
/** Live jobs by player session so a seek or unmount can stop the right one. */
|
|
26
|
+
const jobsBySession = new Map<string, () => void>();
|
|
27
|
+
|
|
21
28
|
/** How many transcodes are running right now (exposed for tests/diagnostics). */
|
|
22
29
|
export function activeTranscodeCount(): number {
|
|
23
30
|
return active;
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/** Kill the job a player owns. Returns false when nothing was running for it. */
|
|
34
|
+
export function stopTranscode(sessionId: string): boolean {
|
|
35
|
+
const kill = jobsBySession.get(sessionId);
|
|
36
|
+
if (!kill) return false;
|
|
37
|
+
kill();
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
export class TranscodeUnavailableError extends Error {
|
|
27
42
|
constructor(msg = "ffmpeg is not installed or has no working H.264 encoder") {
|
|
28
43
|
super(msg);
|
|
@@ -42,6 +57,8 @@ export interface TranscodeOptions {
|
|
|
42
57
|
start?: number;
|
|
43
58
|
/** Aborts the job when the HTTP request is aborted. */
|
|
44
59
|
signal?: AbortSignal;
|
|
60
|
+
/** Player instance id; a new job for the same id replaces (kills) the previous one. */
|
|
61
|
+
sessionId?: string;
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
/** Build the ffmpeg argv; pure so tests can assert on it without spawning. */
|
|
@@ -72,6 +89,8 @@ export interface TranscodeJob {
|
|
|
72
89
|
export async function startTranscode(absPath: string, opts: TranscodeOptions = {}): Promise<TranscodeJob> {
|
|
73
90
|
const caps = await getFfmpegCapabilities();
|
|
74
91
|
if (!caps.ffmpeg || !caps.encoder) throw new TranscodeUnavailableError();
|
|
92
|
+
// A seek is a replacement, not an addition: free this player's slot before counting.
|
|
93
|
+
if (opts.sessionId) stopTranscode(opts.sessionId);
|
|
75
94
|
if (active >= MAX_CONCURRENT_TRANSCODES) throw new TranscodeBusyError();
|
|
76
95
|
|
|
77
96
|
const proc = Bun.spawn(buildTranscodeArgs(caps.ffmpeg, absPath, caps.encoder, opts.start), {
|
|
@@ -85,6 +104,8 @@ export async function startTranscode(absPath: string, opts: TranscodeOptions = {
|
|
|
85
104
|
if (finished) return;
|
|
86
105
|
finished = true;
|
|
87
106
|
active--;
|
|
107
|
+
// Only drop the registration if it is still ours — a replacement may already own the key.
|
|
108
|
+
if (opts.sessionId && jobsBySession.get(opts.sessionId) === kill) jobsBySession.delete(opts.sessionId);
|
|
88
109
|
};
|
|
89
110
|
|
|
90
111
|
// Drain stderr so ffmpeg never blocks on a full pipe; keep the tail for the log.
|
|
@@ -102,6 +123,7 @@ export async function startTranscode(absPath: string, opts: TranscodeOptions = {
|
|
|
102
123
|
finish();
|
|
103
124
|
};
|
|
104
125
|
opts.signal?.addEventListener("abort", kill, { once: true });
|
|
126
|
+
if (opts.sessionId) jobsBySession.set(opts.sessionId, kill);
|
|
105
127
|
|
|
106
128
|
// Wrap stdout so a cancelled response body (client went away) kills this process.
|
|
107
129
|
//
|
|
@@ -28,6 +28,9 @@ export interface ProcEntry {
|
|
|
28
28
|
ppid: number;
|
|
29
29
|
/** Average CPU% over the process lifetime — the same figure `ps %cpu` prints. */
|
|
30
30
|
cpuPercent: number;
|
|
31
|
+
/** Cumulative CPU time (utime + stime), milliseconds. Diff two reads for an
|
|
32
|
+
* instantaneous figure — `cpuPercent` is a lifetime average and never moves. */
|
|
33
|
+
cpuMs: number;
|
|
31
34
|
rssKB: number;
|
|
32
35
|
/** Seconds since the process started. */
|
|
33
36
|
elapsedSec: number;
|
|
@@ -121,6 +124,7 @@ export function readProcTable(): ProcEntry[] | null {
|
|
|
121
124
|
pid,
|
|
122
125
|
ppid: f.ppid,
|
|
123
126
|
cpuPercent: Math.round((cpuSec / elapsedSec) * 1000) / 10,
|
|
127
|
+
cpuMs: cpuSec * 1000,
|
|
124
128
|
rssKB: f.rssPages * PAGE_SIZE_KB,
|
|
125
129
|
elapsedSec,
|
|
126
130
|
startedAtMs: bootTimeMs + startSec * 1000,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared secret redactor for text that leaves the server: the public
|
|
3
|
+
* `/api/logs/recent` tail and process command lines in the metrics stream.
|
|
4
|
+
*
|
|
5
|
+
* Kept in one place so both consumers agree on what counts as a secret. Apply
|
|
6
|
+
* it BEFORE any truncation — secrets sit at the front of argv (`--token=…`,
|
|
7
|
+
* `postgres://user:pass@…`, `ANTHROPIC_API_KEY=… node x.js`), so truncating
|
|
8
|
+
* first would keep the secret and leave the redactor nothing to find.
|
|
9
|
+
*
|
|
10
|
+
* Rules: the six log-line forms (`Token: x`, `Bearer x`, `password: x`,
|
|
11
|
+
* `api_key: x`, `ANTHROPIC_API_KEY=x`, `secret: x`), then the argv forms —
|
|
12
|
+
* `key=value`, `--key value`, and URL userinfo `scheme://user:pass@host`.
|
|
13
|
+
*/
|
|
14
|
+
const RULES: ReadonlyArray<[RegExp, string]> = [
|
|
15
|
+
[/Token:\s*\S+/gi, "Token: [REDACTED]"],
|
|
16
|
+
[/Bearer\s+\S+/gi, "Bearer [REDACTED]"],
|
|
17
|
+
[/password['":\s]+\S+/gi, "password: [REDACTED]"],
|
|
18
|
+
[/api[_-]?key['":\s]+\S+/gi, "api_key: [REDACTED]"],
|
|
19
|
+
[/ANTHROPIC_API_KEY=\S+/gi, "ANTHROPIC_API_KEY=[REDACTED]"],
|
|
20
|
+
[/secret['":\s]+\S+/gi, "secret: [REDACTED]"],
|
|
21
|
+
// argv `--token=abc`, `API_KEY=abc`, `DB_PASSWORD=abc`.
|
|
22
|
+
[/((?:token|api[_-]?key|secret|password)=)\S+/gi, "$1[REDACTED]"],
|
|
23
|
+
// argv space form `--token abc`, `-p abc` is too ambiguous and is left alone.
|
|
24
|
+
[/(--?(?:token|api[_-]?key|secret|password)\s+)\S+/gi, "$1[REDACTED]"],
|
|
25
|
+
// URL userinfo `postgres://user:pass@host` → keep the user, drop the password.
|
|
26
|
+
[/(:\/\/[^/\s:@]+:)[^@\s]+@/g, "$1[REDACTED]@"],
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function redactSecrets(text: string): string {
|
|
30
|
+
let out = text;
|
|
31
|
+
for (const [re, replacement] of RULES) out = out.replace(re, replacement);
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-machine CPU + memory from `node:os`. Runs in both tiers, spawns nothing.
|
|
3
|
+
*/
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import type { CpuMetrics, MemoryMetrics } from "../../types/system-metrics.ts";
|
|
7
|
+
|
|
8
|
+
export interface CoreTimes {
|
|
9
|
+
user: number;
|
|
10
|
+
nice: number;
|
|
11
|
+
sys: number;
|
|
12
|
+
idle: number;
|
|
13
|
+
irq: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CpuTimesSample {
|
|
17
|
+
times: CoreTimes[];
|
|
18
|
+
/** Wall clock of the sample, epoch ms. */
|
|
19
|
+
at: number;
|
|
20
|
+
model: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read `os.cpus()` and copy the numbers out immediately. Bun 1.3.10 on Windows
|
|
25
|
+
* returns the SAME `times` on a later call while the array from the previous
|
|
26
|
+
* call is still referenced — the delta then reads 0 % forever, with no error
|
|
27
|
+
* anywhere. Keeping only plain copies between ticks avoids it.
|
|
28
|
+
*/
|
|
29
|
+
export function sampleCpuTimes(now: number = Date.now()): CpuTimesSample {
|
|
30
|
+
const cpus = os.cpus();
|
|
31
|
+
const times = cpus.map((c) => ({ ...c.times }));
|
|
32
|
+
return { times, at: now, model: cpus[0]?.model?.trim() ?? "" };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const round1 = (n: number): number => Math.round(n * 10) / 10;
|
|
36
|
+
|
|
37
|
+
/** Busy % total + per core between two samples. Pure. First sample → all zeros. */
|
|
38
|
+
export function computeCpuFromSamples(prev: CpuTimesSample | null, next: CpuTimesSample): CpuMetrics {
|
|
39
|
+
const zeros: CpuMetrics = { total: 0, cores: next.times.map(() => 0), model: next.model };
|
|
40
|
+
if (!prev || prev.times.length !== next.times.length) return zeros;
|
|
41
|
+
|
|
42
|
+
let busySum = 0;
|
|
43
|
+
let totalSum = 0;
|
|
44
|
+
const cores = next.times.map((n, i) => {
|
|
45
|
+
const p = prev.times[i]!;
|
|
46
|
+
const idle = n.idle - p.idle;
|
|
47
|
+
const total = (n.user - p.user) + (n.nice - p.nice) + (n.sys - p.sys) + idle + (n.irq - p.irq);
|
|
48
|
+
if (!(total > 0)) return 0;
|
|
49
|
+
busySum += total - idle;
|
|
50
|
+
totalSum += total;
|
|
51
|
+
return clampPercent((total - idle) / total * 100);
|
|
52
|
+
});
|
|
53
|
+
const total = totalSum > 0 ? clampPercent(busySum / totalSum * 100) : 0;
|
|
54
|
+
return { total, cores, model: next.model };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function clampPercent(v: number): number {
|
|
58
|
+
if (!Number.isFinite(v)) return 0;
|
|
59
|
+
return round1(Math.min(100, Math.max(0, v)));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Read `/proc/meminfo` on Linux, null elsewhere or on failure. Injectable for tests. */
|
|
63
|
+
export function readMeminfo(): string | null {
|
|
64
|
+
if (process.platform !== "linux") return null;
|
|
65
|
+
try {
|
|
66
|
+
return readFileSync("/proc/meminfo", "utf-8");
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `MemAvailable` in bytes from a `/proc/meminfo` dump, or null when absent. */
|
|
73
|
+
export function parseMemAvailableBytes(meminfo: string): number | null {
|
|
74
|
+
const m = /^MemAvailable:\s+(\d+)\s*kB/m.exec(meminfo);
|
|
75
|
+
if (!m) return null;
|
|
76
|
+
const kb = Number(m[1]);
|
|
77
|
+
return Number.isFinite(kb) ? kb * 1024 : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* `os.totalmem()` matches `Win32_OperatingSystem.TotalVisibleMemorySize` exactly
|
|
82
|
+
* and `os.freemem()` is within 0.2 GiB of CIM's `FreePhysicalMemory`. On Linux
|
|
83
|
+
* `freemem()` is `MemFree`, which makes a warm page cache look like a nearly
|
|
84
|
+
* full machine — `MemAvailable` is what `free` and Task-Manager-like tools show.
|
|
85
|
+
*/
|
|
86
|
+
export function collectMemory(meminfo: () => string | null = readMeminfo): MemoryMetrics {
|
|
87
|
+
const totalBytes = os.totalmem();
|
|
88
|
+
let availableBytes = os.freemem();
|
|
89
|
+
const info = meminfo();
|
|
90
|
+
if (info) {
|
|
91
|
+
const avail = parseMemAvailableBytes(info);
|
|
92
|
+
if (avail !== null) availableBytes = avail;
|
|
93
|
+
}
|
|
94
|
+
availableBytes = Math.min(Math.max(availableBytes, 0), totalBytes);
|
|
95
|
+
const MB = 1024 * 1024;
|
|
96
|
+
const totalMB = round1(totalBytes / MB);
|
|
97
|
+
const availableMB = round1(availableBytes / MB);
|
|
98
|
+
const usedMB = round1(Math.max(totalMB - availableMB, 0));
|
|
99
|
+
const percent = totalMB > 0 ? round1(usedMB / totalMB * 100) : 0;
|
|
100
|
+
return { totalMB, usedMB, availableMB, percent };
|
|
101
|
+
}
|