@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.
Files changed (510) hide show
  1. package/.spike/s.err +63 -0
  2. package/.spike/tunnel.err +42 -0
  3. package/.spike/w.err +96 -0
  4. package/CHANGELOG.md +16 -0
  5. package/CLAUDE.md +1 -0
  6. package/assets/skills/ppm/SKILL.md +1 -1
  7. package/assets/skills/ppm/references/http-api.md +5 -3
  8. package/dist/web/assets/{ai-resource-editor-DZQUyrsU.js → ai-resource-editor-DFCuNe-P.js} +1 -1
  9. package/dist/web/assets/{angular-html-jfMqUuRd.js → angular-html-Ctz0zN01.js} +1 -1
  10. package/dist/web/assets/{angular-ts-wytn9PMm.js → angular-ts-QXHpgB1C.js} +1 -1
  11. package/dist/web/assets/{api-settings-DlcoR5QT.js → api-settings-nblja3Rk.js} +1 -1
  12. package/dist/web/assets/{apl-i-ahKx6O.js → apl-b9KVk-O4.js} +1 -1
  13. package/dist/web/assets/architecture-PBZL5I3N-C7ra20CE.js +1 -0
  14. package/dist/web/assets/{astro-DVBCJfG6.js → astro-BlFEeGYr.js} +1 -1
  15. package/dist/web/assets/{audio-preview-GGksv6U8.js → audio-preview-CZLmhf__.js} +1 -1
  16. package/dist/web/assets/{blade-BHlL8SqU.js → blade-CGkj2MUI.js} +1 -1
  17. package/dist/web/assets/{bsl-D-UOfR1F.js → bsl-CVsbb6ap.js} +1 -1
  18. package/dist/web/assets/button-BA_fJOtX.js +1 -0
  19. package/dist/web/assets/{cairo-Bn6rucli.js → cairo-BbIHl2iE.js} +1 -1
  20. package/dist/web/assets/chat-tab-flMPLoKb.js +15 -0
  21. package/dist/web/assets/circle-hjUGru6R.js +1 -0
  22. package/dist/web/assets/circle-question-mark-CzR7YRc-.js +1 -0
  23. package/dist/web/assets/{cobol-Byn9FYKu.js → cobol-BWSGjmWb.js} +1 -1
  24. package/dist/web/assets/code-editor-C2VQbXzw.js +10 -0
  25. package/dist/web/assets/{coffee-DjBl4gm-.js → coffee-CWGNOxds.js} +1 -1
  26. package/dist/web/assets/{conflict-editor-Dg_JIcKq.js → conflict-editor-CdH_h0vD.js} +3 -3
  27. package/dist/web/assets/{context-menu-DkQdt6YA.js → context-menu-Cq0u3SK9.js} +1 -1
  28. package/dist/web/assets/{cpp-CKzic-3E.js → cpp-DHGV24J2.js} +1 -1
  29. package/dist/web/assets/{crystal-Bkg-Ly_g.js → crystal-Vlwf2UMP.js} +1 -1
  30. package/dist/web/assets/{csv-preview-DsJ_a2Op.js → csv-preview-CY_454vd.js} +1 -1
  31. package/dist/web/assets/{data-grid-overlay-editor-B1mTl3KS.js → data-grid-overlay-editor-D9il5DZS.js} +1 -1
  32. package/dist/web/assets/{database-viewer-CzjnrnMz.js → database-viewer-BCZOTrPT.js} +1 -1
  33. package/dist/web/assets/{diff-viewer-DUNkO-ll.js → diff-viewer-BwfMN-nW.js} +1 -1
  34. package/dist/web/assets/{docx-preview-DujpUDkE.js → docx-preview-D3Sdh_aT.js} +1 -1
  35. package/dist/web/assets/{edge-DVbZnd3Y.js → edge-Bhqk64zS.js} +1 -1
  36. package/dist/web/assets/{edit-diff-preview-BivQwZMD.js → edit-diff-preview-gq6ALf2B.js} +1 -1
  37. package/dist/web/assets/{elixir-Dwi8Wu7W.js → elixir-D466WCFm.js} +1 -1
  38. package/dist/web/assets/{elm-DtIv-HXJ.js → elm-Bfs2qNPR.js} +1 -1
  39. package/dist/web/assets/{erb-D3HEDz1x.js → erb-WI7jUaB6.js} +1 -1
  40. package/dist/web/assets/{erlang-2MjTCzGz.js → erlang-BCBe8nCc.js} +1 -1
  41. package/dist/web/assets/{esm-CZ8B8Ba2.js → esm-CZs9WVDO.js} +1 -1
  42. package/dist/web/assets/{explorer-body-DSDJUQs7.js → explorer-body-BKOMM566.js} +2 -2
  43. package/dist/web/assets/{explorer-window-content-CWYBapkj.js → explorer-window-content-Bbzijk8t.js} +1 -1
  44. package/dist/web/assets/{extension-webview-DBTFznQp.js → extension-webview-CuOQrHIS.js} +1 -1
  45. package/dist/web/assets/format-bytes-Dk7B73B7.js +1 -0
  46. package/dist/web/assets/{fortran-fixed-form-BLyAtyPM.js → fortran-fixed-form-CXLIv8Ox.js} +1 -1
  47. package/dist/web/assets/{fsharp-Bxbn_gbr.js → fsharp-CZsYmiw9.js} +1 -1
  48. package/dist/web/assets/{gdresource-sFMY4D2Q.js → gdresource-BGRvoWfC.js} +1 -1
  49. package/dist/web/assets/{git-commit-BzB9Lio5.js → git-commit-DH6mY3Km.js} +1 -1
  50. package/dist/web/assets/{git-log-panel-D8ZQdoYl.js → git-log-panel-DcV7PhqW.js} +1 -1
  51. package/dist/web/assets/{git-rebase-CKSVd848.js → git-rebase-C5Gql6Ma.js} +1 -1
  52. package/dist/web/assets/gitGraph-HDMCJU4V-D4hYLNJU.js +1 -0
  53. package/dist/web/assets/{glide-data-grid-uIjdTT7c.js → glide-data-grid-Cehfgrzc.js} +7 -7
  54. package/dist/web/assets/{glimmer-js-BY8aExAJ.js → glimmer-js-1-6JMJlj.js} +1 -1
  55. package/dist/web/assets/{glimmer-ts-Bak54dBX.js → glimmer-ts-C_Njj0nK.js} +1 -1
  56. package/dist/web/assets/{glsl-nR2eaJQe.js → glsl-DbkxE1TN.js} +1 -1
  57. package/dist/web/assets/{graphql-CGPPFAU6.js → graphql-BK75fiXl.js} +1 -1
  58. package/dist/web/assets/group-chat-tab-BVPYxjz-.js +1 -0
  59. package/dist/web/assets/{hack-CXIMX-Ol.js → hack-DG151vit.js} +1 -1
  60. package/dist/web/assets/{haml-tPY-9Pfm.js → haml-DKeIPQWM.js} +1 -1
  61. package/dist/web/assets/{handlebars-BuW1LT7T.js → handlebars-398uCI13.js} +1 -1
  62. package/dist/web/assets/{html-bgVcwZ-t.js → html-NLWdst5q.js} +1 -1
  63. package/dist/web/assets/{html-derivative-D6-IxTgn.js → html-derivative-CJf_Le2z.js} +1 -1
  64. package/dist/web/assets/{http-CJ1zyB-h.js → http-G1K2FozA.js} +1 -1
  65. package/dist/web/assets/{hurl-CmoT7l3T.js → hurl-WDC6sSBz.js} +1 -1
  66. package/dist/web/assets/{hxml-CztOsL8o.js → hxml-Dc3jBXuf.js} +1 -1
  67. package/dist/web/assets/{image-preview-B0VXSpYu.js → image-preview-D-Ok10eZ.js} +1 -1
  68. package/dist/web/assets/index-CerSxGdf.js +52 -0
  69. package/dist/web/assets/index-DRA_OpWY.css +2 -0
  70. package/dist/web/assets/info-3K5VOQVL-lRlrTlkb.js +1 -0
  71. package/dist/web/assets/{jinja-B1pq3jJq.js → jinja-CWsQj2y0.js} +1 -1
  72. package/dist/web/assets/{jison-D8B5pCwA.js → jison-CciJg2NW.js} +1 -1
  73. package/dist/web/assets/{julia-D0K3bwpU.js → julia-IOQNJAB7.js} +1 -1
  74. package/dist/web/assets/{just-B9Jlddu-.js → just-BGdHchwS.js} +1 -1
  75. package/dist/web/assets/{keybindings-store-FyolpNND.js → keybindings-store-aIY40k2o.js} +1 -1
  76. package/dist/web/assets/{latex-BPJSSE6j.js → latex-D6TOl-L2.js} +1 -1
  77. package/dist/web/assets/{liquid-lv10fo4U.js → liquid-CVSvu_Wn.js} +1 -1
  78. package/dist/web/assets/{lua-PNuaIBEF.js → lua-CcKiU6HW.js} +1 -1
  79. package/dist/web/assets/markdown-renderer-I89kyQhe.js +1 -0
  80. package/dist/web/assets/{markdown-renderer-BaBQ6Wm6.js → markdown-renderer-pVzBdkpl.js} +3 -3
  81. package/dist/web/assets/{marko-Du3frD6X.js → marko--UOicMGo.js} +1 -1
  82. package/dist/web/assets/{mdc-BeDVcFRl.js → mdc-D690aU2C.js} +1 -1
  83. package/dist/web/assets/media-url-CylYQld1.js +1 -0
  84. package/dist/web/assets/{mobile-explorer-sheet-Zxjl-e4c.js → mobile-explorer-sheet-5Uj9ZMM9.js} +1 -1
  85. package/dist/web/assets/{nextflow-DkcXP71s.js → nextflow-DR8PhpF8.js} +1 -1
  86. package/dist/web/assets/{nginx-C5n8N_bt.js → nginx-Qb9pUk1g.js} +1 -1
  87. package/dist/web/assets/{nim-CIhyfs-A.js → nim-DLa8R3NS.js} +1 -1
  88. package/dist/web/assets/{notification-store-DnGpxgkF.js → notification-store-DJ-OtAG3.js} +1 -1
  89. package/dist/web/assets/{number-overlay-editor-NgOsWJ7x.js → number-overlay-editor-D9SkFg1Y.js} +1 -1
  90. package/dist/web/assets/overview-panel-Cgi3UPY1.js +1 -0
  91. package/dist/web/assets/packet-RMMSAZCW-BIhk1mvp.js +1 -0
  92. package/dist/web/assets/{pdf-preview-xzAA4-kD.js → pdf-preview-Di5xHFyT.js} +1 -1
  93. package/dist/web/assets/{perl-Cf31sZYE.js → perl-AMNGIneF.js} +1 -1
  94. package/dist/web/assets/{php-Dzl_MDIW.js → php-CDsOOxXn.js} +1 -1
  95. package/dist/web/assets/pie-UPGHQEXC-SLS6i76l.js +1 -0
  96. package/dist/web/assets/{postgres-viewer-Dkec6GyK.js → postgres-viewer-CR6VpTrX.js} +3 -3
  97. package/dist/web/assets/process-table-DL6shbzt.js +1 -0
  98. package/dist/web/assets/{pug-CxSYHrB3.js → pug-C2kdkhQ6.js} +1 -1
  99. package/dist/web/assets/{qml-DlEjGhwN.js → qml-DRDkUp75.js} +1 -1
  100. package/dist/web/assets/radar-KQ55EAFF-JJ6O1Er_.js +1 -0
  101. package/dist/web/assets/{razor-KVt5uW14.js → razor-wY_6wUeC.js} +1 -1
  102. package/dist/web/assets/{rst-D5BwNTnw.js → rst-BRpofyNs.js} +1 -1
  103. package/dist/web/assets/{ruby-D5fejQwd.js → ruby-ieATBhWm.js} +1 -1
  104. package/dist/web/assets/{sas-Bj7B1CB2.js → sas-CoOOEbOO.js} +1 -1
  105. package/dist/web/assets/{scss-BB0CUoCL.js → scss-_nWV0bsb.js} +1 -1
  106. package/dist/web/assets/{settings-store-C9YT1Hir.js → settings-store-C20lqh0G.js} +2 -2
  107. package/dist/web/assets/settings-tab-jbiQwozX.js +1 -0
  108. package/dist/web/assets/{shaderlab-BdjEZUMc.js → shaderlab-XkqfIfO8.js} +1 -1
  109. package/dist/web/assets/{shellsession-Bp1OA1rR.js → shellsession-BKBIqTlM.js} +1 -1
  110. package/dist/web/assets/{soy-DlYz3wsC.js → soy-oVDnPzzh.js} +1 -1
  111. package/dist/web/assets/{sparql-CdfII5Jd.js → sparql-N2VclEsF.js} +1 -1
  112. package/dist/web/assets/{sql-query-editor-Bz1XrUqI.js → sql-query-editor-BPYsJDsn.js} +1 -1
  113. package/dist/web/assets/{sqlite-viewer-D5-NZw6b.js → sqlite-viewer-Bq9ZJA2y.js} +1 -1
  114. package/dist/web/assets/{stata-BvUA6GUW.js → stata-BuuClVDg.js} +1 -1
  115. package/dist/web/assets/{surrealql--DLNbTyI.js → surrealql-DE5_63MS.js} +1 -1
  116. package/dist/web/assets/{svelte-CBfSsG4n.js → svelte--YWjlOrK.js} +1 -1
  117. package/dist/web/assets/system-monitor-body-CKEYWyN9.js +2 -0
  118. package/dist/web/assets/system-monitor-tab-WjhFFHyM.js +1 -0
  119. package/dist/web/assets/system-monitor-window-content-CMiISeoC.js +1 -0
  120. package/dist/web/assets/team-member-sheet-oY5EkH53.js +2 -0
  121. package/dist/web/assets/{team-member-window-content-DSpZ2zhL.js → team-member-window-content-yCBeI_5X.js} +1 -1
  122. package/dist/web/assets/{templ-Dv8zpKZW.js → templ-MlpvJx4a.js} +1 -1
  123. package/dist/web/assets/{terminal-tab-DXazqejW.js → terminal-tab-BxHtMR7h.js} +3 -3
  124. package/dist/web/assets/{tex-CsrOiqh1.js → tex-0loAoXJG.js} +1 -1
  125. package/dist/web/assets/{tool-cards-Bgp8iceS.js → tool-cards-BcGrk1zk.js} +5 -5
  126. package/dist/web/assets/treemap-KZPCXAKY-Be3KNmns.js +1 -0
  127. package/dist/web/assets/{ts-tags-3T98k4EJ.js → ts-tags-pt-Ug0hK.js} +1 -1
  128. package/dist/web/assets/{twig-u6QZ45FH.js → twig-DOePpLOL.js} +1 -1
  129. package/dist/web/assets/{use-monaco-theme-CpF3A7JL.js → use-monaco-theme-CGucu3fb.js} +1 -1
  130. package/dist/web/assets/{use-open-team-member-CUvV02fA.js → use-open-team-member-DdrRXj-M.js} +1 -1
  131. package/dist/web/assets/{use-websocket-TnL2I1io.js → use-websocket-C-Rxv8JN.js} +1 -1
  132. package/dist/web/assets/{vendor-mermaid-Bg4vE8QA.js → vendor-mermaid-DrwG0NYt.js} +1 -1
  133. package/dist/web/assets/video-preview-DYAbbmd0.js +1 -0
  134. package/dist/web/assets/{vue-CxIsxtwn.js → vue-D6f-rprj.js} +1 -1
  135. package/dist/web/assets/{vue-html-BGL5qnuH.js → vue-html-DEYRkS1V.js} +1 -1
  136. package/dist/web/assets/{vue-vine-DHPh_gMr.js → vue-vine-CxSG2ojf.js} +1 -1
  137. package/dist/web/assets/{xml-C-vM2m7J.js → xml-DhrxbATj.js} +1 -1
  138. package/dist/web/assets/{xsl-DvTXNwuO.js → xsl-CU0ZndUO.js} +1 -1
  139. package/dist/web/index.html +18 -17
  140. package/dist/web/sw.js +1 -1
  141. package/docs/system-architecture.md +2 -2
  142. package/package.json +1 -1
  143. package/src/server/helpers/media-route-handlers.ts +22 -3
  144. package/src/server/index.ts +2 -8
  145. package/src/server/routes/files.ts +5 -2
  146. package/src/server/routes/fs-browse.ts +5 -2
  147. package/src/server/routes/resources.ts +119 -73
  148. package/src/services/autostart-generator.ts +79 -7
  149. package/src/services/autostart-register.ts +15 -2
  150. package/src/services/fs-path-guard.service.ts +17 -1
  151. package/src/services/media-transcode/transcode-stream.ts +27 -5
  152. package/src/services/proc-table-linux.ts +4 -0
  153. package/src/services/redact-secrets.ts +33 -0
  154. package/src/services/system-metrics/cpu-memory-collector.ts +101 -0
  155. package/src/services/system-metrics/disk-net-collector-darwin.ts +75 -0
  156. package/src/services/system-metrics/disk-net-collector-linux.ts +88 -0
  157. package/src/services/system-metrics/disk-net-collector-windows.ts +57 -0
  158. package/src/services/system-metrics/gpu-collector-nvidia.ts +78 -0
  159. package/src/services/system-metrics/gpu-process-memory-nvidia.ts +66 -0
  160. package/src/services/system-metrics/gpu-process-usage-windows.ts +138 -0
  161. package/src/services/system-metrics/kill-executor.ts +85 -0
  162. package/src/services/system-metrics/kill-guard.ts +124 -0
  163. package/src/services/system-metrics/kill-identity-resolver.ts +72 -0
  164. package/src/services/system-metrics/kill-request-handler.ts +79 -0
  165. package/src/services/system-metrics/metrics-collector-lock.ts +36 -0
  166. package/src/services/system-metrics/metrics-poll-scheduler.ts +83 -0
  167. package/src/services/system-metrics/metrics-subscriber-registry.ts +85 -0
  168. package/src/services/system-metrics/powershell-session-bootstrap.ts +48 -0
  169. package/src/services/system-metrics/powershell-session.ts +182 -0
  170. package/src/services/system-metrics/ppm-process-ownership.ts +34 -0
  171. package/src/services/system-metrics/ppm-protected-pids.ts +84 -0
  172. package/src/services/system-metrics/process-collector-darwin.ts +141 -0
  173. package/src/services/system-metrics/process-collector-linux.ts +85 -0
  174. package/src/services/system-metrics/process-collector-types.ts +80 -0
  175. package/src/services/system-metrics/process-collector-windows-parse.ts +153 -0
  176. package/src/services/system-metrics/process-collector-windows.ts +136 -0
  177. package/src/services/system-metrics/process-cpu-delta.ts +61 -0
  178. package/src/services/system-metrics/process-grouping.ts +142 -0
  179. package/src/services/system-metrics/process-io-delta.ts +84 -0
  180. package/src/services/system-metrics/process-io-linux.ts +46 -0
  181. package/src/services/system-metrics/process-net-collector-darwin.ts +103 -0
  182. package/src/services/system-metrics/process-rows-builder.ts +111 -0
  183. package/src/services/system-metrics/rate-delta.ts +47 -0
  184. package/src/services/system-metrics/system-metrics-platform.ts +51 -0
  185. package/src/services/system-metrics/system-metrics-tick.ts +178 -0
  186. package/src/services/system-metrics/system-metrics.service.ts +212 -0
  187. package/src/types/system-metrics.ts +199 -0
  188. package/src/web/components/editor/video-player/video-player.tsx +54 -22
  189. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  190. package/src/web/components/floating-window/window-persistence.ts +1 -1
  191. package/src/web/components/floating-window/window-store-types.ts +1 -1
  192. package/src/web/components/system/build-kill-request.ts +9 -0
  193. package/src/web/components/system/chart-scale.ts +28 -0
  194. package/src/web/components/system/cpu-core-bars.tsx +33 -0
  195. package/src/web/components/system/kill-confirm-dialog.tsx +89 -0
  196. package/src/web/components/system/metric-chart-canvas.tsx +108 -0
  197. package/src/web/components/system/overview-cards/cpu-card.tsx +39 -0
  198. package/src/web/components/system/overview-cards/disk-card.tsx +41 -0
  199. package/src/web/components/system/overview-cards/gpu-card.tsx +33 -0
  200. package/src/web/components/system/overview-cards/mem-card.tsx +32 -0
  201. package/src/web/components/system/overview-cards/net-card.tsx +41 -0
  202. package/src/web/components/system/overview-panel.tsx +97 -0
  203. package/src/web/components/system/process-columns-grid.ts +93 -0
  204. package/src/web/components/system/process-group-row.tsx +71 -0
  205. package/src/web/components/system/process-row-format.ts +56 -0
  206. package/src/web/components/system/process-row.tsx +82 -0
  207. package/src/web/components/system/process-table-model.ts +127 -0
  208. package/src/web/components/system/process-table-sort.ts +90 -0
  209. package/src/web/components/system/process-table-toolbar.tsx +190 -0
  210. package/src/web/components/system/process-table-totals.ts +49 -0
  211. package/src/web/components/system/process-table.tsx +187 -0
  212. package/src/web/components/system/resolve-open-system-monitor-action.ts +29 -0
  213. package/src/web/components/system/resource-status-bar.tsx +17 -16
  214. package/src/web/components/system/sortable-header.tsx +65 -0
  215. package/src/web/components/system/sparkline-canvas.tsx +8 -9
  216. package/src/web/components/system/system-monitor-body.tsx +146 -0
  217. package/src/web/components/system/system-monitor-tab.tsx +2 -185
  218. package/src/web/components/system/system-monitor-window-content.tsx +18 -0
  219. package/src/web/components/system/use-open-system-monitor.ts +34 -0
  220. package/src/web/hooks/resource-monitor-lease.ts +55 -0
  221. package/src/web/hooks/use-element-width.ts +27 -0
  222. package/src/web/hooks/use-resource-monitor.ts +113 -35
  223. package/src/web/lib/format-bytes.ts +16 -0
  224. package/src/web/lib/media-url.ts +30 -4
  225. package/.release-run.sh +0 -5
  226. package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +0 -1
  227. package/dist/web/assets/arrow-right-DubGFkN8.js +0 -1
  228. package/dist/web/assets/button-B4MWPjWj.js +0 -1
  229. package/dist/web/assets/chat-tab-Xd2WMjdX.js +0 -15
  230. package/dist/web/assets/circle-question-mark-CENpx4oA.js +0 -1
  231. package/dist/web/assets/code-editor-DqXYJWAB.js +0 -10
  232. package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +0 -1
  233. package/dist/web/assets/group-chat-tab-B3sryFdA.js +0 -1
  234. package/dist/web/assets/index-DgbOF3xa.js +0 -52
  235. package/dist/web/assets/index-oxsyfDa0.css +0 -2
  236. package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +0 -1
  237. package/dist/web/assets/markdown-renderer-feiuhJ9n.js +0 -1
  238. package/dist/web/assets/media-url-CUpN5vI1.js +0 -1
  239. package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +0 -1
  240. package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +0 -1
  241. package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +0 -1
  242. package/dist/web/assets/settings-tab-Bd8e6iqY.js +0 -1
  243. package/dist/web/assets/system-monitor-tab-A0jvt5Qw.js +0 -1
  244. package/dist/web/assets/team-member-sheet-5Oaqm8I_.js +0 -2
  245. package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +0 -1
  246. package/dist/web/assets/video-preview-CwtLPELG.js +0 -1
  247. package/src/services/resource-monitor-utils.ts +0 -141
  248. package/src/services/resource-monitor.service.ts +0 -147
  249. package/src/web/components/system/system-monitor-group-row.tsx +0 -153
  250. /package/dist/web/assets/{abap-C1K0-wzc.js → abap-MRbmsXOs.js} +0 -0
  251. /package/dist/web/assets/{actionscript-3-CJhQVjLA.js → actionscript-3-BDDHKqa4.js} +0 -0
  252. /package/dist/web/assets/{ada-Bt8r9fME.js → ada-DpuV9KwV.js} +0 -0
  253. /package/dist/web/assets/{andromeeda-DF1I8qi3.js → andromeeda-D6qUCKJo.js} +0 -0
  254. /package/dist/web/assets/{apache-CnlleE6P.js → apache-CX1NU_Tl.js} +0 -0
  255. /package/dist/web/assets/{apex-j0C6Xmk6.js → apex-B4-aoQ3K.js} +0 -0
  256. /package/dist/web/assets/{api-settings-DfNRPr1q.js → api-settings-kQHV6pYZ.js} +0 -0
  257. /package/dist/web/assets/{api-themes-8WTH6KpV.js → api-themes-CrK0tkW_.js} +0 -0
  258. /package/dist/web/assets/{applescript-DO7AnO8U.js → applescript-Dm9NI6KR.js} +0 -0
  259. /package/dist/web/assets/{apply-theme-CNj1iA-v.js → apply-theme-BH_d4amk.js} +0 -0
  260. /package/dist/web/assets/{ara-i83AydZj.js → ara-BGN8GAYn.js} +0 -0
  261. /package/dist/web/assets/{asciidoc-DbBA6-sF.js → asciidoc-s_yVuR3t.js} +0 -0
  262. /package/dist/web/assets/{asm-B5S47Rgr.js → asm-BzQdSvTe.js} +0 -0
  263. /package/dist/web/assets/{aurora-x-De_IaSKw.js → aurora-x-BJTLETG_.js} +0 -0
  264. /package/dist/web/assets/{awk-CfyLHXqS.js → awk-BQEGWxvX.js} +0 -0
  265. /package/dist/web/assets/{ayu-dark-DTLdZ1Si.js → ayu-dark-CpTORmLc.js} +0 -0
  266. /package/dist/web/assets/{ayu-light-DTOfkzhu.js → ayu-light-D8PVj-e6.js} +0 -0
  267. /package/dist/web/assets/{ayu-mirage-DsAECCMv.js → ayu-mirage-DyElQHtm.js} +0 -0
  268. /package/dist/web/assets/{ballerina-CumGp9A7.js → ballerina-C82NkRtj.js} +0 -0
  269. /package/dist/web/assets/{bat-Ca2SBbYN.js → bat-0X7DtGI-.js} +0 -0
  270. /package/dist/web/assets/{beancount-CqyXBwwF.js → beancount-Xgvy6YRl.js} +0 -0
  271. /package/dist/web/assets/{berry-yl-kJr_H.js → berry-CCT2pZcQ.js} +0 -0
  272. /package/dist/web/assets/{bibtex-DoFFszjV.js → bibtex-CpXgZ9M_.js} +0 -0
  273. /package/dist/web/assets/{bicep-CWPi4nfK.js → bicep-fkeSA62l.js} +0 -0
  274. /package/dist/web/assets/{bird2-DXew-ut0.js → bird2-Co7SeAI8.js} +0 -0
  275. /package/dist/web/assets/{c-clbySDvd.js → c-BpmrTvJh.js} +0 -0
  276. /package/dist/web/assets/{c3-B65jvSM1.js → c3-BZ_Z0RrR.js} +0 -0
  277. /package/dist/web/assets/{cadence-Cm8tQTHN.js → cadence-B0GBC8OF.js} +0 -0
  278. /package/dist/web/assets/{can-open-in-ppm-DXGOSUQc.js → can-open-in-ppm-omBzUBBq.js} +0 -0
  279. /package/dist/web/assets/{catppuccin-frappe-DLeGslh_.js → catppuccin-frappe-DIt_xR7m.js} +0 -0
  280. /package/dist/web/assets/{catppuccin-latte-CR5GeHws.js → catppuccin-latte-Dw40AcqA.js} +0 -0
  281. /package/dist/web/assets/{catppuccin-macchiato-p-XgjxhP.js → catppuccin-macchiato-BJ7DfVEF.js} +0 -0
  282. /package/dist/web/assets/{catppuccin-mocha-B8QOAZLl.js → catppuccin-mocha-NI2lfXzU.js} +0 -0
  283. /package/dist/web/assets/{clarity-DPulbvKH.js → clarity-F39L0xMJ.js} +0 -0
  284. /package/dist/web/assets/{clock-D493FU_D.js → clock-CvhF4DJP.js} +0 -0
  285. /package/dist/web/assets/{clojure-DFuWS20u.js → clojure-DloMkd8K.js} +0 -0
  286. /package/dist/web/assets/{cmake-DejCqcu4.js → cmake-CwerJun_.js} +0 -0
  287. /package/dist/web/assets/{code-CLF5x7KK.js → code-DDPoqVri.js} +0 -0
  288. /package/dist/web/assets/{codeowners-BwAyqWIH.js → codeowners-CQyVz4Qf.js} +0 -0
  289. /package/dist/web/assets/{codeql-DgSwFjqI.js → codeql-Bb229zuY.js} +0 -0
  290. /package/dist/web/assets/{common-lisp-CxIVJCtf.js → common-lisp-KMlIwq5I.js} +0 -0
  291. /package/dist/web/assets/{copy-BApI9v7Z.js → copy-DKn11Pye.js} +0 -0
  292. /package/dist/web/assets/{coq-DLSjSnmf.js → coq-DHX_3IIo.js} +0 -0
  293. /package/dist/web/assets/{csharp-C1HHzXBp.js → csharp-CEmCZv9Q.js} +0 -0
  294. /package/dist/web/assets/{css-VXsMY--y.js → css-CqZqBD05.js} +0 -0
  295. /package/dist/web/assets/{csv-BEbcJdHz.js → csv-l1g_V5MV.js} +0 -0
  296. /package/dist/web/assets/{csv-parser-BRtnJCfv.js → csv-parser-CnS0wqr6.js} +0 -0
  297. /package/dist/web/assets/{cue-BkDnFqUD.js → cue-BrxRmilg.js} +0 -0
  298. /package/dist/web/assets/{cypher-1cMqojSQ.js → cypher-rZkKwqZc.js} +0 -0
  299. /package/dist/web/assets/{d-BnYjRXtH.js → d-BALR-QuP.js} +0 -0
  300. /package/dist/web/assets/{dark-plus-XfbaH7kZ.js → dark-plus-Cj7rVwh-.js} +0 -0
  301. /package/dist/web/assets/{dart-Ca7s-8hK.js → dart-CWQcdkXk.js} +0 -0
  302. /package/dist/web/assets/{data-grid-types-BXwlFMzs.js → data-grid-types-CYvF-xK5.js} +0 -0
  303. /package/dist/web/assets/{database-B9okYLx6.js → database-BH0urvbs.js} +0 -0
  304. /package/dist/web/assets/{dax-CmuOw-mY.js → dax-BNHD-QBA.js} +0 -0
  305. /package/dist/web/assets/{desktop-C2gJ6Ovb.js → desktop-CN0IXPrP.js} +0 -0
  306. /package/dist/web/assets/{diff-BCEo1Ej7.js → diff-BtOXapES.js} +0 -0
  307. /package/dist/web/assets/{dist-CUPbqNnL.js → dist-DB1Oaqcc.js} +0 -0
  308. /package/dist/web/assets/{dist-DOlBnxFX.js → dist-Di6G_5jU.js} +0 -0
  309. /package/dist/web/assets/{docker-DN2jlyAh.js → docker-DNsAo_qO.js} +0 -0
  310. /package/dist/web/assets/{dotenv-NExMY2kS.js → dotenv-D6Pl6aOU.js} +0 -0
  311. /package/dist/web/assets/{download-D9UYwY0-.js → download-zCATy4x4.js} +0 -0
  312. /package/dist/web/assets/{dracula-B9lbE01y.js → dracula-C3m-w12D.js} +0 -0
  313. /package/dist/web/assets/{dracula-soft-CMBBfKF_.js → dracula-soft-BYqvXm6Z.js} +0 -0
  314. /package/dist/web/assets/{dream-maker-DEyDwFsd.js → dream-maker-DZAPbPTi.js} +0 -0
  315. /package/dist/web/assets/{emacs-lisp-D_qP2zS6.js → emacs-lisp-Ao5SW8ay.js} +0 -0
  316. /package/dist/web/assets/{everforest-dark-DbHa2U-p.js → everforest-dark-uC8QYotQ.js} +0 -0
  317. /package/dist/web/assets/{everforest-light-BISkJchP.js → everforest-light-CTgsVNMr.js} +0 -0
  318. /package/dist/web/assets/{fennel-6B22SfJR.js → fennel-WuBw6IR1.js} +0 -0
  319. /package/dist/web/assets/{file-exclamation-point-a8f3_w7a.js → file-exclamation-point-COAwgNGw.js} +0 -0
  320. /package/dist/web/assets/{fish-CubIIvJD.js → fish-Byy9dusu.js} +0 -0
  321. /package/dist/web/assets/{fluent-DF55zQ09.js → fluent-CDxb_sgw.js} +0 -0
  322. /package/dist/web/assets/{fortran-free-form-DvOu8jAt.js → fortran-free-form-DKWSTamT.js} +0 -0
  323. /package/dist/web/assets/{gdscript-DCPxdLpq.js → gdscript-prYHMgtD.js} +0 -0
  324. /package/dist/web/assets/{gdshader-CTZ381FO.js → gdshader-D-RM-SQk.js} +0 -0
  325. /package/dist/web/assets/{genie-C_QDtvgU.js → genie-CowkJhej.js} +0 -0
  326. /package/dist/web/assets/{gherkin-BcvK42tF.js → gherkin-CLFpoVYJ.js} +0 -0
  327. /package/dist/web/assets/{github-dark-default-CKGVrl3Z.js → github-dark-default-BK_1Qgb8.js} +0 -0
  328. /package/dist/web/assets/{github-dark-dimmed-C3dQZ_LC.js → github-dark-dimmed-Cv9lj6f-.js} +0 -0
  329. /package/dist/web/assets/{github-dark-high-contrast-DW78QukY.js → github-dark-high-contrast-CW5ZgIDh.js} +0 -0
  330. /package/dist/web/assets/{github-dark-C9afs_4w.js → github-dark-nW2F-RLd.js} +0 -0
  331. /package/dist/web/assets/{github-light-BnP2dznS.js → github-light-D4PqBHGZ.js} +0 -0
  332. /package/dist/web/assets/{github-light-default-C2fPZzPG.js → github-light-default-B-cHtJJV.js} +0 -0
  333. /package/dist/web/assets/{github-light-high-contrast-BzIuUFR2.js → github-light-high-contrast-Dt0HJq50.js} +0 -0
  334. /package/dist/web/assets/{gleam-BpeKwEIO.js → gleam-DwSyXJWP.js} +0 -0
  335. /package/dist/web/assets/{gn-nUb_Mit0.js → gn-Cv5WEzHc.js} +0 -0
  336. /package/dist/web/assets/{gnuplot-D7I0v20p.js → gnuplot-D7bRdY_M.js} +0 -0
  337. /package/dist/web/assets/{go-D7Qqska7.js → go-DiRVJ22U.js} +0 -0
  338. /package/dist/web/assets/{groovy-DS-pne5-.js → groovy-BHAgLTeO.js} +0 -0
  339. /package/dist/web/assets/{gruvbox-dark-hard-C0W01fsq.js → gruvbox-dark-hard-C8QZ3gzY.js} +0 -0
  340. /package/dist/web/assets/{gruvbox-dark-medium-Bqo6fQbg.js → gruvbox-dark-medium-Dp1eeid2.js} +0 -0
  341. /package/dist/web/assets/{gruvbox-dark-soft-D-XSM_fu.js → gruvbox-dark-soft-Cwq0dWVj.js} +0 -0
  342. /package/dist/web/assets/{gruvbox-light-hard-IVAwMJRl.js → gruvbox-light-hard-CLQCq2-w.js} +0 -0
  343. /package/dist/web/assets/{gruvbox-light-medium-38ATiG7i.js → gruvbox-light-medium-APHsh6Mo.js} +0 -0
  344. /package/dist/web/assets/{gruvbox-light-soft-DnZr3c_0.js → gruvbox-light-soft-CX-fYc64.js} +0 -0
  345. /package/dist/web/assets/{haskell-uQcywkDH.js → haskell-C4Tw-TDa.js} +0 -0
  346. /package/dist/web/assets/{haxe-c6bI4_GT.js → haxe-B7fSduTQ.js} +0 -0
  347. /package/dist/web/assets/{hcl-CQXTiEXq.js → hcl-Bi3ho6yC.js} +0 -0
  348. /package/dist/web/assets/{hjson-ChETf2bz.js → hjson-BXwae2M1.js} +0 -0
  349. /package/dist/web/assets/{hlsl-BMC4JGoE.js → hlsl-CRuQLBSa.js} +0 -0
  350. /package/dist/web/assets/{horizon-BusFlhLW.js → horizon-DtdQ6qjv.js} +0 -0
  351. /package/dist/web/assets/{horizon-bright-2oD0Mz7W.js → horizon-bright-BXSF_uKq.js} +0 -0
  352. /package/dist/web/assets/{houston-DdbbjqFg.js → houston-C6ASpUjT.js} +0 -0
  353. /package/dist/web/assets/{hy-D_SOuqw9.js → hy-BrWBHtHY.js} +0 -0
  354. /package/dist/web/assets/{image-gallery-CrM5fHKy.js → image-gallery-CGvlv8zR.js} +0 -0
  355. /package/dist/web/assets/{imba-BxLOHO5I.js → imba-C_JJXckG.js} +0 -0
  356. /package/dist/web/assets/{ini-ox-CFcg0.js → ini-DEE_vSHQ.js} +0 -0
  357. /package/dist/web/assets/{java-Bzo8yIal.js → java-CnmpcjcT.js} +0 -0
  358. /package/dist/web/assets/{javascript-DHuz9VXn.js → javascript-C-M7sa2e.js} +0 -0
  359. /package/dist/web/assets/{json-0_y80FD5.js → json-BjZ3dAk6.js} +0 -0
  360. /package/dist/web/assets/{json5-CEQ50pGM.js → json5-BQ2vpqjp.js} +0 -0
  361. /package/dist/web/assets/{jsonc-CfrLEAKA.js → jsonc-C5MjoWGx.js} +0 -0
  362. /package/dist/web/assets/{jsonl-fKbrANC2.js → jsonl-JvhrmQJI.js} +0 -0
  363. /package/dist/web/assets/{jsonnet-DKbkPaQ2.js → jsonnet-C5pdZVuo.js} +0 -0
  364. /package/dist/web/assets/{jssm-Dxxx1Hqu.js → jssm-Db17uybX.js} +0 -0
  365. /package/dist/web/assets/{jsx-DlJL-Oj3.js → jsx-tJp2Cyqu.js} +0 -0
  366. /package/dist/web/assets/{kanagawa-dragon-CwN4SV1c.js → kanagawa-dragon-BculT_Y1.js} +0 -0
  367. /package/dist/web/assets/{kanagawa-lotus-BHltayiE.js → kanagawa-lotus-yQgHUGwU.js} +0 -0
  368. /package/dist/web/assets/{kanagawa-wave-D-KXPjw6.js → kanagawa-wave-CyGoWN4d.js} +0 -0
  369. /package/dist/web/assets/{kdl-DvtoJdFo.js → kdl-Bn8R1REF.js} +0 -0
  370. /package/dist/web/assets/{kotlin-CTS4MLcx.js → kotlin-DWughK73.js} +0 -0
  371. /package/dist/web/assets/{kusto-HtSE_sKm.js → kusto--serPb0x.js} +0 -0
  372. /package/dist/web/assets/{laserwave-DaXZG9ge.js → laserwave-DfAPJ1tt.js} +0 -0
  373. /package/dist/web/assets/{lean-BYtGUfWx.js → lean-UAm1HOKl.js} +0 -0
  374. /package/dist/web/assets/{less-BoxmU_Mk.js → less-Xj6Ka0fF.js} +0 -0
  375. /package/dist/web/assets/{lib-B1clP4ai.js → lib-DgNcCtj3.js} +0 -0
  376. /package/dist/web/assets/{libesm-ah-mr-sh.js → libesm-CPh1Av0r.js} +0 -0
  377. /package/dist/web/assets/{light-plus-Bt5CyUuo.js → light-plus-BfS7gPcj.js} +0 -0
  378. /package/dist/web/assets/{llvm-CSdk9q9V.js → llvm-DvWiVwvU.js} +0 -0
  379. /package/dist/web/assets/{log-B-kp_3CL.js → log-Bo5z9bNz.js} +0 -0
  380. /package/dist/web/assets/{logo-DdIsXFDO.js → logo-CrIjcMZT.js} +0 -0
  381. /package/dist/web/assets/{luau-DUBpLlzQ.js → luau-C9TsYwNt.js} +0 -0
  382. /package/dist/web/assets/{make-B3JVjoW_.js → make-GYq4qVbx.js} +0 -0
  383. /package/dist/web/assets/{markdown-Seon0vHs.js → markdown-p5mNledS.js} +0 -0
  384. /package/dist/web/assets/{material-theme-Nq-ChX1P.js → material-theme-CWYOVxsP.js} +0 -0
  385. /package/dist/web/assets/{material-theme-darker-CPV4ayXJ.js → material-theme-darker-BDGty7YH.js} +0 -0
  386. /package/dist/web/assets/{material-theme-lighter-0kyjYqEd.js → material-theme-lighter-JbgOgV3n.js} +0 -0
  387. /package/dist/web/assets/{material-theme-ocean-DGpAp5co.js → material-theme-ocean-EMOpLpa2.js} +0 -0
  388. /package/dist/web/assets/{material-theme-palenight-B0AUn0NG.js → material-theme-palenight-CY5hgm7h.js} +0 -0
  389. /package/dist/web/assets/{matlab-C2waytxs.js → matlab-C-He_NTH.js} +0 -0
  390. /package/dist/web/assets/{mdx-DAis6R6I.js → mdx-Cr2TR5fj.js} +0 -0
  391. /package/dist/web/assets/{mermaid-_rnT2YGD.js → mermaid-BYc0-B0U.js} +0 -0
  392. /package/dist/web/assets/{min-dark-BSSjchUB.js → min-dark-CMJwqJqB.js} +0 -0
  393. /package/dist/web/assets/{min-light-DomAFQsa.js → min-light-Cx6vFiEX.js} +0 -0
  394. /package/dist/web/assets/{mipsasm-bBgtvPyG.js → mipsasm-Bjer5HwK.js} +0 -0
  395. /package/dist/web/assets/{mobile-bottom-sheet-CS3XMwFw.js → mobile-bottom-sheet-BUOJgiaZ.js} +0 -0
  396. /package/dist/web/assets/{mojo-Cp9BayW0.js → mojo-t90PR-Y2.js} +0 -0
  397. /package/dist/web/assets/{monokai-CL_9gbmn.js → monokai-dci_PdCm.js} +0 -0
  398. /package/dist/web/assets/{moonbit-BS_fcr-P.js → moonbit-AAzI-Hd-.js} +0 -0
  399. /package/dist/web/assets/{move-C79AH45U.js → move-kwBhnhrG.js} +0 -0
  400. /package/dist/web/assets/{narrat-CJ5s3BDf.js → narrat-CXww2YQL.js} +0 -0
  401. /package/dist/web/assets/{nextflow-groovy-ZHa4JrPJ.js → nextflow-groovy-CJXsuXGB.js} +0 -0
  402. /package/dist/web/assets/{night-owl-Jkf9zFQK.js → night-owl-_GD204Wr.js} +0 -0
  403. /package/dist/web/assets/{night-owl-light-DRPeaG5J.js → night-owl-light-DNgkw3kl.js} +0 -0
  404. /package/dist/web/assets/{nix-D2Jet9DN.js → nix-BgiorKFd.js} +0 -0
  405. /package/dist/web/assets/{nord-CREVs22j.js → nord-CaBmcxFC.js} +0 -0
  406. /package/dist/web/assets/{nushell-CAiDi_mR.js → nushell-DUh4UGx1.js} +0 -0
  407. /package/dist/web/assets/{objective-c-CVkmJkC1.js → objective-c-Cl9KU86S.js} +0 -0
  408. /package/dist/web/assets/{objective-cpp-DIKDO1Ly.js → objective-cpp-DVkIA8A0.js} +0 -0
  409. /package/dist/web/assets/{ocaml-R9qtGQhs.js → ocaml-vvoHTak0.js} +0 -0
  410. /package/dist/web/assets/{odin-DTYNQqVr.js → odin-Cyjw8woZ.js} +0 -0
  411. /package/dist/web/assets/{one-dark-pro-BSLWUJn5.js → one-dark-pro-B1cviILi.js} +0 -0
  412. /package/dist/web/assets/{one-light-DbkRXG3t.js → one-light-BPGUJeYI.js} +0 -0
  413. /package/dist/web/assets/{openscad-BlPcSnmT.js → openscad-BLMdTUU8.js} +0 -0
  414. /package/dist/web/assets/{pascal-B1Y46ZZB.js → pascal-i5niy3e7.js} +0 -0
  415. /package/dist/web/assets/{pkl-BhJSJbFv.js → pkl-CuOV7SsU.js} +0 -0
  416. /package/dist/web/assets/{plastic-BzxsCTpi.js → plastic-Bf_zE5KL.js} +0 -0
  417. /package/dist/web/assets/{plsql-DBba7dyP.js → plsql-cfqEtMD3.js} +0 -0
  418. /package/dist/web/assets/{plus-DmjC2iRB.js → plus-BM-Ty0fp.js} +0 -0
  419. /package/dist/web/assets/{po-BvgLlnNo.js → po-CFJcWgjr.js} +0 -0
  420. /package/dist/web/assets/{poimandres-DUaI_1JP.js → poimandres-G5YoaIFf.js} +0 -0
  421. /package/dist/web/assets/{polar-C7uILwJn.js → polar-rcjs3i9R.js} +0 -0
  422. /package/dist/web/assets/{postcss-BgJlpFdw.js → postcss-CBKY1FTZ.js} +0 -0
  423. /package/dist/web/assets/{powerquery-Dn1MIpZu.js → powerquery-Db7SXLbO.js} +0 -0
  424. /package/dist/web/assets/{powershell-Bo-DYzOS.js → powershell-LFy3MUTP.js} +0 -0
  425. /package/dist/web/assets/{prisma-CTLs5IdT.js → prisma-BE8GR1lH.js} +0 -0
  426. /package/dist/web/assets/{prolog-QRGun3eG.js → prolog-DOn5lR1e.js} +0 -0
  427. /package/dist/web/assets/{proto-B7PI7apn.js → proto-wr-TpAK_.js} +0 -0
  428. /package/dist/web/assets/{puppet-CBq0tLrP.js → puppet-D2Dkv62u.js} +0 -0
  429. /package/dist/web/assets/{purescript-DFewOQb2.js → purescript-DvgJgPdy.js} +0 -0
  430. /package/dist/web/assets/{python-iWuxKXvT.js → python-C7TlkHhK.js} +0 -0
  431. /package/dist/web/assets/{qmldir-cXiW3AsF.js → qmldir-CtZMwhcW.js} +0 -0
  432. /package/dist/web/assets/{qss-DASwAbBL.js → qss-dNZ2btnl.js} +0 -0
  433. /package/dist/web/assets/{r-TU2eaIPw.js → r-BlqnNkHS.js} +0 -0
  434. /package/dist/web/assets/{racket-SkRKqrLY.js → racket-CzRQrGc4.js} +0 -0
  435. /package/dist/web/assets/{raku-D31lthkZ.js → raku-BwutWhBF.js} +0 -0
  436. /package/dist/web/assets/{red-CTcQZevb.js → red-BMXMywmL.js} +0 -0
  437. /package/dist/web/assets/{reg-D8YAsTGf.js → reg-BqOKrIOw.js} +0 -0
  438. /package/dist/web/assets/{regexp-CD2uvAWx.js → regexp-Bero1BN2.js} +0 -0
  439. /package/dist/web/assets/{rel-CAaI-1fg.js → rel-BXKWIElA.js} +0 -0
  440. /package/dist/web/assets/{resolve-theme-CJvkL8hU.js → resolve-theme-IdhSR-gx.js} +0 -0
  441. /package/dist/web/assets/{riscv-DR-hJVIj.js → riscv-BbbBYJBo.js} +0 -0
  442. /package/dist/web/assets/{ron-CuUCJ0n4.js → ron-BtVTWW4b.js} +0 -0
  443. /package/dist/web/assets/{rose-pine-dawn-Bqz2sBRY.js → rose-pine-dawn-D7lwhUei.js} +0 -0
  444. /package/dist/web/assets/{rose-pine-aFHxW48i.js → rose-pine-hvkvQzvU.js} +0 -0
  445. /package/dist/web/assets/{rose-pine-moon-wqyh0an4.js → rose-pine-moon-CoHDmsIw.js} +0 -0
  446. /package/dist/web/assets/{rosmsg-CI1ywimO.js → rosmsg-CdaZFgI8.js} +0 -0
  447. /package/dist/web/assets/{run-in-terminal-CGW6o37_.js → run-in-terminal-CyS5Itj-.js} +0 -0
  448. /package/dist/web/assets/{rust-p7Imk4G_.js → rust-Dy2qsphD.js} +0 -0
  449. /package/dist/web/assets/{sass-C8JfaqCV.js → sass-Bz7NVxDb.js} +0 -0
  450. /package/dist/web/assets/{save-DRPrDfTt.js → save-C4z1p51e.js} +0 -0
  451. /package/dist/web/assets/{scala-DNxCW1By.js → scala-sXSwsNyq.js} +0 -0
  452. /package/dist/web/assets/{scheme-BYuUPJoL.js → scheme-DS7TUbN7.js} +0 -0
  453. /package/dist/web/assets/{sdbl-DbPeJPUr.js → sdbl-iISFvIep.js} +0 -0
  454. /package/dist/web/assets/{send-to-chat-YXHdAyLG.js → send-to-chat-BcO39BDR.js} +0 -0
  455. /package/dist/web/assets/{shellscript-DMiHlR3q.js → shellscript-BkIl4ApC.js} +0 -0
  456. /package/dist/web/assets/{shield-check-8GObuUAn.js → shield-check-C5laObJv.js} +0 -0
  457. /package/dist/web/assets/{shield-off-egKAZEwR.js → shield-off-DULFtyLS.js} +0 -0
  458. /package/dist/web/assets/{slack-dark-C4xGcQXI.js → slack-dark-B1eg0_m2.js} +0 -0
  459. /package/dist/web/assets/{slack-ochin-BzgRt_wm.js → slack-ochin-mdkc1ldu.js} +0 -0
  460. /package/dist/web/assets/{smalltalk-LDeL9PYH.js → smalltalk-CORtd03t.js} +0 -0
  461. /package/dist/web/assets/{snazzy-light-CXE7a5j_.js → snazzy-light-Bc6Q2G89.js} +0 -0
  462. /package/dist/web/assets/{solarized-dark-DgUhY74x.js → solarized-dark-CF83zRXM.js} +0 -0
  463. /package/dist/web/assets/{solarized-light-DD9C-H-9.js → solarized-light-BoKQZaYU.js} +0 -0
  464. /package/dist/web/assets/{solidity-CAbDB4D4.js → solidity-DOgi1N4w.js} +0 -0
  465. /package/dist/web/assets/{sparkles-n1hzh65o.js → sparkles-Dqsf3hq6.js} +0 -0
  466. /package/dist/web/assets/{splunk-kQREPfIc.js → splunk-BXfw5ZLy.js} +0 -0
  467. /package/dist/web/assets/{sql-CBzeNAD8.js → sql-BcVs-eX4.js} +0 -0
  468. /package/dist/web/assets/{ssh-config-L1euxrsM.js → ssh-config-CLCtNkkd.js} +0 -0
  469. /package/dist/web/assets/{stylus-DvBnYniP.js → stylus-DdP992aO.js} +0 -0
  470. /package/dist/web/assets/{swift-DF5NDQp6.js → swift-BnGb6Lgc.js} +0 -0
  471. /package/dist/web/assets/{synthwave-84-DbBhB_Hq.js → synthwave-84-cg9264l9.js} +0 -0
  472. /package/dist/web/assets/{system-verilog-D8s2ZW_k.js → system-verilog-GbHpszs3.js} +0 -0
  473. /package/dist/web/assets/{systemd-a5-XZJsG.js → systemd-Dz-bNsDQ.js} +0 -0
  474. /package/dist/web/assets/{table-BX2ZrJfM.js → table-CHmxuh6M.js} +0 -0
  475. /package/dist/web/assets/{talonscript-BLa0URTW.js → talonscript-CFIidoBA.js} +0 -0
  476. /package/dist/web/assets/{tasl-DIoV4XcC.js → tasl-Cu-TRTKx.js} +0 -0
  477. /package/dist/web/assets/{tcl-ztYAdXfQ.js → tcl-DwCHCB1K.js} +0 -0
  478. /package/dist/web/assets/{terraform-DvpCCiOZ.js → terraform-BzZw4T1z.js} +0 -0
  479. /package/dist/web/assets/{tokyo-night-BkHlYUv0.js → tokyo-night-RKsj2-tv.js} +0 -0
  480. /package/dist/web/assets/{toml-acIrxos6.js → toml-CirbOgDP.js} +0 -0
  481. /package/dist/web/assets/{tsv-BJB5EVCT.js → tsv-CraRcWdU.js} +0 -0
  482. /package/dist/web/assets/{tsx-B1MmXGT3.js → tsx-CjFg0lgw.js} +0 -0
  483. /package/dist/web/assets/{turtle-DFceTiqd.js → turtle-B-WBIOa6.js} +0 -0
  484. /package/dist/web/assets/{typescript-Dz9wU5Y1.js → typescript-MpLarYn1.js} +0 -0
  485. /package/dist/web/assets/{typespec-CPuYZijj.js → typespec-B7QlHMZ_.js} +0 -0
  486. /package/dist/web/assets/{typst-CfsCIYv8.js → typst-CcHSKFZj.js} +0 -0
  487. /package/dist/web/assets/{use-blob-url-BmlYYZ8L.js → use-blob-url-BKyVxOZw.js} +0 -0
  488. /package/dist/web/assets/{use-is-mobile-B8FA4kQf.js → use-is-mobile-BXGVvczu.js} +0 -0
  489. /package/dist/web/assets/{v-CGo0jcXA.js → v-xTGhWZuD.js} +0 -0
  490. /package/dist/web/assets/{vala-AsSR0hrb.js → vala-DwebjH3H.js} +0 -0
  491. /package/dist/web/assets/{vb-C7tvbXv1.js → vb-BxJT2RAy.js} +0 -0
  492. /package/dist/web/assets/{vendor-xterm-C1diXHJl.js → vendor-xterm-CU6zIYMB.js} +0 -0
  493. /package/dist/web/assets/{verilog--a33ND-p.js → verilog-CvV6RHPD.js} +0 -0
  494. /package/dist/web/assets/{vesper-B_Z0h4pn.js → vesper-CY3qd7E_.js} +0 -0
  495. /package/dist/web/assets/{vhdl-CWL5dZPN.js → vhdl-B9ZX-sUL.js} +0 -0
  496. /package/dist/web/assets/{viml-BglmT14Y.js → viml-Y42h_ExQ.js} +0 -0
  497. /package/dist/web/assets/{vitesse-black-RKFsSqQI.js → vitesse-black-DkXP6Sv-.js} +0 -0
  498. /package/dist/web/assets/{vitesse-dark-D7zDWNmc.js → vitesse-dark-B9CkIB6r.js} +0 -0
  499. /package/dist/web/assets/{vitesse-light-MAp87W5b.js → vitesse-light-C7DHrJCK.js} +0 -0
  500. /package/dist/web/assets/{vyper-CgCBYg1G.js → vyper-Cnd9s5j-.js} +0 -0
  501. /package/dist/web/assets/{wasm-Bwl4txwp.js → wasm-DQE71XTB.js} +0 -0
  502. /package/dist/web/assets/{wasm-BOG7ZeTT.js → wasm-iWe7Fzjc.js} +0 -0
  503. /package/dist/web/assets/{wenyan-DpShPKXZ.js → wenyan-Bc5Pnm9l.js} +0 -0
  504. /package/dist/web/assets/{wgsl-BakcCblK.js → wgsl-D-YUAWkP.js} +0 -0
  505. /package/dist/web/assets/{wikitext-NroRWVAO.js → wikitext-DYQDUYf2.js} +0 -0
  506. /package/dist/web/assets/{wit-B5CM2pg6.js → wit-P6u7mfwy.js} +0 -0
  507. /package/dist/web/assets/{wolfram-7MTFBJpR.js → wolfram-6SXLUqao.js} +0 -0
  508. /package/dist/web/assets/{yaml-Bwr01G2K.js → yaml-CQiMLVcZ.js} +0 -0
  509. /package/dist/web/assets/{zenscript-Dj8GATvi.js → zenscript-DFMywo_P.js} +0 -0
  510. /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 { resourceMonitor, type ResourceSnapshot } from "../../services/resource-monitor.service.ts";
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
- export const resourceRoutes = new Hono();
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 MAX_SSE_CLIENTS = 5;
8
- let sseClientCount = 0;
23
+ const tierOf = (raw: string | undefined): MetricsTier => (raw === "1" || raw === "true" ? "full" : "light");
9
24
 
10
- /** GET /resources latest snapshot as JSON */
11
- resourceRoutes.get("/resources", (c) => {
12
- return c.json(ok(resourceMonitor.getLatest()));
13
- });
25
+ export function createResourceRoutes(service: SystemMetricsService = systemMetricsService): Hono {
26
+ const routes = new Hono();
14
27
 
15
- /** GET /resources/history full ring buffer */
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
- /** POST /resources/kill/:pid send SIGTERM to a process in PPM's tree */
21
- resourceRoutes.post("/resources/kill/:pid", async (c) => {
22
- const pid = parseInt(c.req.param("pid"), 10);
23
- if (isNaN(pid) || pid <= 0) {
24
- return c.json(err("Invalid PID"), 400);
25
- }
26
- // Safety: only allow killing processes in PPM's own tree
27
- const snapshot = resourceMonitor.getLatest();
28
- if (!snapshot) {
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
- /** GET /resources/stream SSE stream of snapshots every 3s */
48
- resourceRoutes.get("/resources/stream", (c) => {
49
- if (sseClientCount >= MAX_SSE_CLIENTS) {
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
- let callbackRef: ((s: ResourceSnapshot) => void) | null = null;
54
-
55
- const stream = new ReadableStream({
56
- start(controller) {
57
- sseClientCount++;
58
- const encoder = new TextEncoder();
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
- // Send retry hint for reconnect interval
61
- controller.enqueue(encoder.encode("retry: 5000\n\n"));
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
- callbackRef = (snapshot: ResourceSnapshot) => {
64
- try {
65
- const data = `event: snapshot\ndata: ${JSON.stringify(snapshot)}\n\n`;
66
- controller.enqueue(encoder.encode(data));
67
- } catch {
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
- resourceMonitor.subscribe(callbackRef);
73
- },
74
- cancel() {
75
- sseClientCount = Math.max(0, sseClientCount - 1);
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
- return new Response(stream, {
82
- headers: {
83
- "Content-Type": "text/event-stream",
84
- "Cache-Control": "no-cache",
85
- "Connection": "keep-alive",
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
- /** wscript runs the VBS without a console window; VBS in turn launches bun hidden. */
209
- function buildTaskRunString(vbsPath: string): string {
210
- return `wscript.exe "${vbsPath}"`;
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(vbsPath: string): string[] {
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
- "/TR", buildTaskRunString(vbsPath),
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(vbsPath);
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
- * Every spawned process is tied to the response body: when the browser drops the
12
- * connection (tab closed, seek, next file) the stream is cancelled and only that PID
13
- * is killed never a blanket kill by image name.
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. A seek briefly overlaps old + new job, so allow a little slack. */
18
- export const MAX_CONCURRENT_TRANSCODES = 3;
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
+ }