@hienlh/ppm 0.18.14 → 0.18.16

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 (546) hide show
  1. package/.rdtest2-cf.out +0 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +3 -0
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/cli-reference.md +22 -4
  6. package/assets/skills/ppm/references/http-api.md +8 -1
  7. package/dist/web/assets/{adaptive-context-menu-DCZtgOL7.js → adaptive-context-menu-BbuRVTLn.js} +1 -1
  8. package/dist/web/assets/{ai-resource-editor-D5CPBg06.js → ai-resource-editor-Cm4n3VXt.js} +1 -1
  9. package/dist/web/assets/ai-settings-section-BEM1z7kp.js +1 -0
  10. package/dist/web/assets/{angular-html-CTvM4ulF.js → angular-html-BlcrLjUx.js} +1 -1
  11. package/dist/web/assets/{angular-ts-B_vSkjco.js → angular-ts-BUjtceAJ.js} +1 -1
  12. package/dist/web/assets/api-client-D1CePgQ4.js +1 -0
  13. package/dist/web/assets/api-settings-CNtKkHqW.js +1 -0
  14. package/dist/web/assets/{api-settings-B2iNXQ0c.js → api-settings-Cubuqjsu.js} +1 -1
  15. package/dist/web/assets/{api-themes-DrWnJbhs.js → api-themes-YsZgf6Xj.js} +1 -1
  16. package/dist/web/assets/{apl-DywzTry0.js → apl-B3g0v1pk.js} +1 -1
  17. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +1 -0
  18. package/dist/web/assets/{astro-1JaRgaSb.js → astro-FRnyn7cH.js} +1 -1
  19. package/dist/web/assets/{audio-preview-DAuGvOiK.js → audio-preview-DWTRm4HC.js} +1 -1
  20. package/dist/web/assets/{blade-CDeeJWBb.js → blade-NLdOqUKw.js} +1 -1
  21. package/dist/web/assets/{bsl-BfgoPVYR.js → bsl-CenHZfaU.js} +1 -1
  22. package/dist/web/assets/{button-tNy0ba3y.js → button-Cq8yixjf.js} +1 -1
  23. package/dist/web/assets/{cairo-C26gNwWW.js → cairo-B_WR0aiQ.js} +1 -1
  24. package/dist/web/assets/chat-tab-C2e6Pwc2.js +15 -0
  25. package/dist/web/assets/{cobol-D92nBnON.js → cobol-DOynhWYj.js} +1 -1
  26. package/dist/web/assets/code-editor-DWtv_VDo.js +10 -0
  27. package/dist/web/assets/{coffee-CIWH40R1.js → coffee-Bn6dbw4P.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-CDp0I3mD.js → conflict-editor-Bi2RWfKQ.js} +1 -1
  29. package/dist/web/assets/{context-menu-BO1c9jKy.js → context-menu-DfWZAxQy.js} +1 -1
  30. package/dist/web/assets/{cpp-DOrBqXCn.js → cpp-CoZR9t8r.js} +1 -1
  31. package/dist/web/assets/{crystal-BNKqmHux.js → crystal-BSheONSg.js} +1 -1
  32. package/dist/web/assets/{csv-preview-D4svNDAq.js → csv-preview-DxGZ1qg5.js} +1 -1
  33. package/dist/web/assets/{data-grid-overlay-editor-BqV0AxZg.js → data-grid-overlay-editor-gwPoVSCU.js} +1 -1
  34. package/dist/web/assets/{database-viewer-BCvv_Bwf.js → database-viewer-DSPRzZny.js} +1 -1
  35. package/dist/web/assets/{dialog-DcLGDwaX.js → dialog-BSfuiTkJ.js} +1 -1
  36. package/dist/web/assets/{diff-viewer-D41fCFAi.js → diff-viewer-YbpnNv5Y.js} +1 -1
  37. package/dist/web/assets/{docx-preview-DfaQn9v7.js → docx-preview-DqVDOMmv.js} +1 -1
  38. package/dist/web/assets/{edge-Dbjp42Pt.js → edge-DTtoslQu.js} +1 -1
  39. package/dist/web/assets/{edit-diff-preview-BqcUuFCc.js → edit-diff-preview-CXSbSUBX.js} +1 -1
  40. package/dist/web/assets/{elixir-na3b1pYy.js → elixir-CIBCZ3Zi.js} +1 -1
  41. package/dist/web/assets/{elm-_Nalil1R.js → elm-BLmb7K1H.js} +1 -1
  42. package/dist/web/assets/{erb-CW_7dubf.js → erb-BBY8ApRd.js} +1 -1
  43. package/dist/web/assets/{erlang-DmZ7Zzb0.js → erlang-D5Zb5_P0.js} +1 -1
  44. package/dist/web/assets/{esm-CwyBetO1.js → esm-6F8v1jLL.js} +1 -1
  45. package/dist/web/assets/explorer-body-DpQndBVw.js +2 -0
  46. package/dist/web/assets/explorer-window-content-m0K6Mtcg.js +1 -0
  47. package/dist/web/assets/{extension-store-DUaZINyC.js → extension-store-Bhu--fPX.js} +1 -1
  48. package/dist/web/assets/{extension-webview-BW_mccb3.js → extension-webview-BSh6KGd7.js} +1 -1
  49. package/dist/web/assets/{file-store-yReVbR8q.js → file-store-6HW2Lfl4.js} +1 -1
  50. package/dist/web/assets/{fortran-fixed-form-DB2A8L4-.js → fortran-fixed-form-CBdwkEUJ.js} +1 -1
  51. package/dist/web/assets/{fsharp-BYHrti7z.js → fsharp-AT4XdVYH.js} +1 -1
  52. package/dist/web/assets/{gdresource-D5GBin40.js → gdresource-D852fWBR.js} +1 -1
  53. package/dist/web/assets/{git-commit-CUiOBoIL.js → git-commit-DWfWAyxy.js} +1 -1
  54. package/dist/web/assets/{git-log-panel-C1QDkmgl.js → git-log-panel-CkTiR70w.js} +1 -1
  55. package/dist/web/assets/{git-rebase-DOxGnpdw.js → git-rebase-C6opk8gr.js} +1 -1
  56. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +1 -0
  57. package/dist/web/assets/glide-data-grid-BB5mCaCD.js +138 -0
  58. package/dist/web/assets/{glimmer-js-Begy4UT7.js → glimmer-js-DLi5mWYO.js} +1 -1
  59. package/dist/web/assets/{glimmer-ts-rPApRY8N.js → glimmer-ts-D-scFX30.js} +1 -1
  60. package/dist/web/assets/{glsl-wrbPvrBL.js → glsl-dbQzF4iA.js} +1 -1
  61. package/dist/web/assets/{graphql-Cu4-NR__.js → graphql-D1m5Wen2.js} +1 -1
  62. package/dist/web/assets/group-chat-tab-BPIOdFJw.js +1 -0
  63. package/dist/web/assets/{hack-Ddo93XKD.js → hack-CqYmpO7u.js} +1 -1
  64. package/dist/web/assets/{haml-BFX_1i8j.js → haml-BXzKWEag.js} +1 -1
  65. package/dist/web/assets/hand-x0ejq3fS.js +1 -0
  66. package/dist/web/assets/{handlebars-5gFKA4H5.js → handlebars-DXnrtHtq.js} +1 -1
  67. package/dist/web/assets/{html-C06I-zeO.js → html-DhgQJ-xL.js} +1 -1
  68. package/dist/web/assets/{html-derivative-e5JLAFZF.js → html-derivative-xZRft-YE.js} +1 -1
  69. package/dist/web/assets/{http-CrAAhSU7.js → http-B53mSj14.js} +1 -1
  70. package/dist/web/assets/{hurl-T1V3SX9T.js → hurl-CsomVy04.js} +1 -1
  71. package/dist/web/assets/{hxml-B056LLKN.js → hxml-LffjWwKA.js} +1 -1
  72. package/dist/web/assets/{image-preview-DWyuCYT3.js → image-preview-CNA6XMNS.js} +1 -1
  73. package/dist/web/assets/index-BHdtSKzb.js +52 -0
  74. package/dist/web/assets/index-DN9XUQ8D.css +2 -0
  75. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +1 -0
  76. package/dist/web/assets/{input-DouJ9Zvt.js → input-D0ol5jW0.js} +1 -1
  77. package/dist/web/assets/{jinja-DGdtRh43.js → jinja-CCHURl7V.js} +1 -1
  78. package/dist/web/assets/{jison-Ah6QobPS.js → jison-7qBd8dnR.js} +1 -1
  79. package/dist/web/assets/{julia-BJeGOK0K.js → julia-BY9pnTcy.js} +1 -1
  80. package/dist/web/assets/{just-iw9oRs7j.js → just-D7wRSNRr.js} +1 -1
  81. package/dist/web/assets/{keybindings-store-0Wqfa7Qp.js → keybindings-store-BGkB72gM.js} +1 -1
  82. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +1 -0
  83. package/dist/web/assets/keyboard-DBBaoQSu.js +1 -0
  84. package/dist/web/assets/{latex-DHjj5hyN.js → latex-RgsCae55.js} +1 -1
  85. package/dist/web/assets/{liquid-B-FFecDc.js → liquid-CnDFzvOZ.js} +1 -1
  86. package/dist/web/assets/{lua-DDianJp_.js → lua-0lj5W8sm.js} +1 -1
  87. package/dist/web/assets/{markdown-renderer-BHiUhQN8.js → markdown-renderer-B9wzBWm9.js} +3 -3
  88. package/dist/web/assets/markdown-renderer-DeSV1CCQ.js +1 -0
  89. package/dist/web/assets/{marko-BW5SBVng.js → marko-CQgqAhbV.js} +1 -1
  90. package/dist/web/assets/{mdc-B9gL9bjl.js → mdc-BkIcTiIy.js} +1 -1
  91. package/dist/web/assets/{media-url-DUK-k0bB.js → media-url-DDciwvLU.js} +1 -1
  92. package/dist/web/assets/mobile-bottom-sheet-pjcHKorA.js +1 -0
  93. package/dist/web/assets/mobile-explorer-sheet-DPdxpnKh.js +1 -0
  94. package/dist/web/assets/monitor-smartphone-DLZ6Q05s.js +1 -0
  95. package/dist/web/assets/{nextflow-dTluY6qy.js → nextflow-DvIW68DS.js} +1 -1
  96. package/dist/web/assets/{nginx-DlwghGI-.js → nginx-O-hft0gG.js} +1 -1
  97. package/dist/web/assets/{nim-N9jddb5D.js → nim-BxrR4HwQ.js} +1 -1
  98. package/dist/web/assets/notification-store-BtijuGez.js +1 -0
  99. package/dist/web/assets/{number-overlay-editor-BVXM7aAs.js → number-overlay-editor-BoLRYuZT.js} +1 -1
  100. package/dist/web/assets/{overview-panel-DCCDmtCQ.js → overview-panel-l3g1gT5e.js} +1 -1
  101. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +1 -0
  102. package/dist/web/assets/{panel-store-B4rijFKN.js → panel-store-UOV8wrfR.js} +1 -1
  103. package/dist/web/assets/{pdf-preview-D7yLmAZW.js → pdf-preview-B2CJ_EKv.js} +1 -1
  104. package/dist/web/assets/{perl-CkmpYKa_.js → perl-Da-uNqmL.js} +1 -1
  105. package/dist/web/assets/{php-BwZ3KDPo.js → php-JMu45mEx.js} +1 -1
  106. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +1 -0
  107. package/dist/web/assets/{postgres-viewer--CRN1fvO.js → postgres-viewer-23xdZYxp.js} +3 -3
  108. package/dist/web/assets/process-table-CUsPf7wv.js +1 -0
  109. package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-DTUCS65m.js} +1 -1
  110. package/dist/web/assets/{pug-B3rBPqQo.js → pug-CLzyQhia.js} +1 -1
  111. package/dist/web/assets/{qml-BfDbWFmg.js → qml-BHXfGB1l.js} +1 -1
  112. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +1 -0
  113. package/dist/web/assets/{razor-B1ww1r38.js → razor-BqetJLN3.js} +1 -1
  114. package/dist/web/assets/remote-desktop-mobile-sheet-FTF6IBhF.js +2 -0
  115. package/dist/web/assets/remote-desktop-mobile-view-DSwG4OSk.js +1 -0
  116. package/dist/web/assets/remote-desktop-stats-overlay-Bz6xZ5y9.js +1 -0
  117. package/dist/web/assets/remote-desktop-warning-gate-DrYySLZ5.js +1 -0
  118. package/dist/web/assets/remote-desktop-window-content-Ndvezorb.js +1 -0
  119. package/dist/web/assets/{rst-C0MSq6m-.js → rst-iQdJZ0BP.js} +1 -1
  120. package/dist/web/assets/{ruby-ClIZ1wb3.js → ruby-CNwzZGdH.js} +1 -1
  121. package/dist/web/assets/{run-in-terminal-4efbW2Xv.js → run-in-terminal-CBXmVGWa.js} +1 -1
  122. package/dist/web/assets/{sas-DR0j3o4_.js → sas-BpBIT2eR.js} +1 -1
  123. package/dist/web/assets/{scroll-area-ZP8wcwTI.js → scroll-area-lc0ZHORr.js} +1 -1
  124. package/dist/web/assets/{scss-Vh3JLayR.js → scss-D1MotuEp.js} +1 -1
  125. package/dist/web/assets/{send-to-chat-BNrEpAyC.js → send-to-chat-DJAdlvJ8.js} +1 -1
  126. package/dist/web/assets/settings-store-zbPkG7o-.js +2 -0
  127. package/dist/web/assets/settings-tab-Cn-AqUP_.js +1 -0
  128. package/dist/web/assets/settings-tab-xyRpt2gq.js +3 -0
  129. package/dist/web/assets/{shaderlab-0rHRnQ18.js → shaderlab-BNqmcPQi.js} +1 -1
  130. package/dist/web/assets/{shellsession-Dxdw6D8c.js → shellsession-BBLOVOO-.js} +1 -1
  131. package/dist/web/assets/{soy-BHlSNj1y.js → soy-BpTGo9uy.js} +1 -1
  132. package/dist/web/assets/{sparql-B2QaPpni.js → sparql-D7A3yKob.js} +1 -1
  133. package/dist/web/assets/{sql-query-editor-R_iKjPP9.js → sql-query-editor-BE3mLYyz.js} +1 -1
  134. package/dist/web/assets/{sqlite-viewer-b2onA0SP.js → sqlite-viewer-wN5RP8sd.js} +1 -1
  135. package/dist/web/assets/{stata-CwIxjsEJ.js → stata-B7N56ptS.js} +1 -1
  136. package/dist/web/assets/{surrealql-C6vo-rp-.js → surrealql-CTmnD-lv.js} +1 -1
  137. package/dist/web/assets/{svelte-LLdJ6rlH.js → svelte-BppwY9TR.js} +1 -1
  138. package/dist/web/assets/{switch-DV-BDjyk.js → switch-BmjKoIpR.js} +1 -1
  139. package/dist/web/assets/system-monitor-body-B2M0MC6y.js +2 -0
  140. package/dist/web/assets/system-monitor-tab-OYca4qaf.js +1 -0
  141. package/dist/web/assets/{system-monitor-window-content-BPif0azo.js → system-monitor-window-content-CHz9WdTm.js} +1 -1
  142. package/dist/web/assets/{tab-host-window-content-BnAN6m4K.js → tab-host-window-content-DKev3GJz.js} +1 -1
  143. package/dist/web/assets/{tab-store-tFnyPmm4.js → tab-store-DzYSsw5a.js} +1 -1
  144. package/dist/web/assets/team-member-sheet-BQ738Lli.js +2 -0
  145. package/dist/web/assets/{team-member-window-content-BSyU-5Uc.js → team-member-window-content-CK08f5cK.js} +1 -1
  146. package/dist/web/assets/{templ-CJGvhEP5.js → templ-CvTMKKra.js} +1 -1
  147. package/dist/web/assets/{terminal-tab-Ncor0Rfu.js → terminal-tab-DZ3QiYcs.js} +3 -3
  148. package/dist/web/assets/{tex-psXWhc4t.js → tex--c4POEUE.js} +1 -1
  149. package/dist/web/assets/{tool-cards-DdVP1wrD.js → tool-cards-Bw1P0MKU.js} +4 -4
  150. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +1 -0
  151. package/dist/web/assets/{ts-tags-D5Ar_ZCO.js → ts-tags--kx7D-Hw.js} +1 -1
  152. package/dist/web/assets/{twig-vJgGC-qY.js → twig-UG9uMHGd.js} +1 -1
  153. package/dist/web/assets/{use-blob-url-BOVGWg9F.js → use-blob-url-Bdxopwwc.js} +1 -1
  154. package/dist/web/assets/{use-monaco-theme-BshnLXoY.js → use-monaco-theme-BOmd2wpe.js} +1 -1
  155. package/dist/web/assets/{use-open-team-member-CY3wOdWL.js → use-open-team-member-D9EOB8-i.js} +1 -1
  156. package/dist/web/assets/use-visual-viewport-Sfb6CZ80.js +1 -0
  157. package/dist/web/assets/{use-websocket-B7xjNsyD.js → use-websocket-B7Xj-Q4W.js} +1 -1
  158. package/dist/web/assets/{vendor-mermaid-999ksD45.js → vendor-mermaid-0Nt-ew76.js} +1 -1
  159. package/dist/web/assets/{video-preview-7F72sRwf.js → video-preview-B0nbTGjQ.js} +1 -1
  160. package/dist/web/assets/{vue-html-BN5ZLu-H.js → vue-html-8KEW6-I9.js} +1 -1
  161. package/dist/web/assets/{vue-BWngFyav.js → vue-jjTomlbt.js} +1 -1
  162. package/dist/web/assets/{vue-vine-BZHnO8yV.js → vue-vine-C5tohCkU.js} +1 -1
  163. package/dist/web/assets/{window-pip-registry-BnYU83LE.js → window-pip-registry-FdZdGF7h.js} +1 -1
  164. package/dist/web/assets/window-store-D0x_fxr0.js +1 -0
  165. package/dist/web/assets/{xml-DS2beZHp.js → xml-ByFFosqL.js} +1 -1
  166. package/dist/web/assets/{xsl-Ciiku1Bq.js → xsl-CZUwiLkj.js} +1 -1
  167. package/dist/web/index.html +46 -43
  168. package/dist/web/sw.js +1 -1
  169. package/docs/architecture/ai-chat-and-providers.md +3 -0
  170. package/package.json +1 -1
  171. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  172. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  173. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  174. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  175. package/src/cli/commands/backup-cmd.ts +119 -0
  176. package/src/index.ts +3 -0
  177. package/src/providers/claude-agent-sdk.ts +29 -1
  178. package/src/server/index.ts +42 -15
  179. package/src/server/routes/accounts.ts +30 -2
  180. package/src/server/routes/chat.ts +8 -1
  181. package/src/server/routes/named-tunnel.ts +5 -2
  182. package/src/server/routes/remote-desktop.ts +64 -0
  183. package/src/server/routes/settings.ts +2 -0
  184. package/src/server/ws/chat.ts +126 -7
  185. package/src/server/ws/remote-desktop.ts +86 -0
  186. package/src/services/account.service.ts +106 -23
  187. package/src/services/db-backup/db-backup-paths.ts +66 -0
  188. package/src/services/db-backup/db-backup-restore.ts +105 -0
  189. package/src/services/db-backup/db-backup-retention.ts +96 -0
  190. package/src/services/db-backup/db-backup-sync.ts +113 -0
  191. package/src/services/db-backup/db-backup.service.ts +61 -0
  192. package/src/services/db.service.ts +29 -1
  193. package/src/services/edge-forwarder.ts +191 -191
  194. package/src/services/fs-credential-path-guard.ts +19 -1
  195. package/src/services/named-tunnel/cloudflare-dns-api.ts +20 -4
  196. package/src/services/named-tunnel/cloudflared-login.service.ts +2 -2
  197. package/src/services/named-tunnel/named-tunnel-args.ts +17 -3
  198. package/src/services/named-tunnel/named-tunnel-setup.service.ts +20 -2
  199. package/src/services/nested-subagent-spy.ts +206 -0
  200. package/src/services/remote-desktop/access-unit-assembler.ts +90 -0
  201. package/src/services/remote-desktop/avc1-codec-string.ts +18 -0
  202. package/src/services/remote-desktop/nal-splitter.ts +100 -0
  203. package/src/services/remote-desktop/remote-desktop-capture.ts +120 -0
  204. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +42 -0
  205. package/src/services/remote-desktop/remote-desktop-flag.ts +12 -0
  206. package/src/services/remote-desktop/remote-desktop-input.ts +217 -0
  207. package/src/services/remote-desktop/remote-desktop-nonce.ts +38 -0
  208. package/src/services/remote-desktop/remote-desktop-session.ts +189 -0
  209. package/src/services/remote-desktop/remote-desktop-vk-map.ts +41 -0
  210. package/src/services/subagent-transcript-merger.ts +92 -75
  211. package/src/services/subprocess-retention.ts +29 -6
  212. package/src/services/supervisor.ts +58 -0
  213. package/src/services/team-member-activity/subagent-transcript-index.ts +56 -0
  214. package/src/services/windows-process-tree.ts +351 -351
  215. package/src/types/chat.ts +2 -0
  216. package/src/web/app.tsx +8 -0
  217. package/src/web/components/chat/account-dialogs.tsx +11 -4
  218. package/src/web/components/chat/question-card.tsx +9 -2
  219. package/src/web/components/chat/usage-badge.tsx +17 -3
  220. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  221. package/src/web/components/floating-window/window-store-types.ts +1 -1
  222. package/src/web/components/layout/mobile-drawer.tsx +38 -27
  223. package/src/web/components/layout/nav-section-rail.tsx +10 -2
  224. package/src/web/components/remote-desktop/open-remote-desktop.ts +26 -0
  225. package/src/web/components/remote-desktop/remote-desktop-coords.ts +98 -0
  226. package/src/web/components/remote-desktop/remote-desktop-frame-timestamp.ts +21 -0
  227. package/src/web/components/remote-desktop/remote-desktop-gesture-classifiers.ts +56 -0
  228. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +131 -0
  229. package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +48 -0
  230. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +79 -0
  231. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +190 -0
  232. package/src/web/components/remote-desktop/remote-desktop-stats-overlay.tsx +54 -0
  233. package/src/web/components/remote-desktop/remote-desktop-stats.ts +37 -0
  234. package/src/web/components/remote-desktop/remote-desktop-touch-gesture-tracker.ts +195 -0
  235. package/src/web/components/remote-desktop/remote-desktop-warning-gate.tsx +106 -0
  236. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +86 -0
  237. package/src/web/components/remote-desktop/remote-desktop-ws-url.ts +22 -0
  238. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +175 -0
  239. package/src/web/components/remote-desktop/use-remote-desktop-available.ts +29 -0
  240. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +147 -0
  241. package/src/web/components/remote-desktop/use-remote-desktop-mobile-open-state.ts +18 -0
  242. package/src/web/components/remote-desktop/use-remote-desktop-touch.ts +224 -0
  243. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +50 -0
  244. package/src/web/components/remote-desktop/use-remote-input-capture.ts +92 -0
  245. package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +59 -59
  246. package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +2 -2
  247. package/src/web/hooks/use-notification-badge.ts +19 -11
  248. package/src/web/lib/feature-badges.ts +2 -0
  249. package/src/web/stores/settings-store.ts +25 -0
  250. package/src/web/stores/streaming-store.ts +21 -2
  251. package/dist/web/assets/ai-settings-section-DY-t1bK8.js +0 -1
  252. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  253. package/dist/web/assets/api-settings-DRF9oYkn.js +0 -1
  254. package/dist/web/assets/architecture-PBZL5I3N-nw5-j_I_.js +0 -1
  255. package/dist/web/assets/chat-tab-IcnU3nqQ.js +0 -15
  256. package/dist/web/assets/code-editor-Dno5Srkm.js +0 -10
  257. package/dist/web/assets/explorer-body-DB1L30pa.js +0 -2
  258. package/dist/web/assets/explorer-window-content-Cwlt3C3I.js +0 -1
  259. package/dist/web/assets/gitGraph-HDMCJU4V-C1xf87gN.js +0 -1
  260. package/dist/web/assets/glide-data-grid-Dz8lx6n2.js +0 -138
  261. package/dist/web/assets/group-chat-tab-DFwr-pig.js +0 -1
  262. package/dist/web/assets/index-DWpNR3nB.css +0 -2
  263. package/dist/web/assets/index-oUKV5nIA.js +0 -52
  264. package/dist/web/assets/info-3K5VOQVL-C2z7FTDe.js +0 -1
  265. package/dist/web/assets/keybindings-store-d54pv2NK.js +0 -1
  266. package/dist/web/assets/markdown-renderer-CnE36_gd.js +0 -1
  267. package/dist/web/assets/mobile-bottom-sheet-CJ7ApOkI.js +0 -1
  268. package/dist/web/assets/mobile-explorer-sheet-BzjlS1Xn.js +0 -1
  269. package/dist/web/assets/notification-store-C7UBqxYP.js +0 -1
  270. package/dist/web/assets/packet-RMMSAZCW-DQUxN0wU.js +0 -1
  271. package/dist/web/assets/pie-UPGHQEXC-BOr9ek4Q.js +0 -1
  272. package/dist/web/assets/process-table-CXC10GSy.js +0 -1
  273. package/dist/web/assets/radar-KQ55EAFF-DSErxZFb.js +0 -1
  274. package/dist/web/assets/settings-store-OLJE7dPH.js +0 -2
  275. package/dist/web/assets/settings-tab-B1rvHFc6.js +0 -3
  276. package/dist/web/assets/settings-tab-BUlmPJ2w.js +0 -1
  277. package/dist/web/assets/system-monitor-body-DPYQlBEt.js +0 -2
  278. package/dist/web/assets/system-monitor-tab-CPQdWv27.js +0 -1
  279. package/dist/web/assets/team-member-sheet-Bu2wG1sm.js +0 -2
  280. package/dist/web/assets/treemap-KZPCXAKY-C17p0LQw.js +0 -1
  281. package/dist/web/assets/window-store-CQ1lghvv.js +0 -1
  282. /package/dist/web/assets/{abap-CjzrhAS2.js → abap-C5DFF0s3.js} +0 -0
  283. /package/dist/web/assets/{actionscript-3-B59Kbl-9.js → actionscript-3-B0qyRNbc.js} +0 -0
  284. /package/dist/web/assets/{ada-B7gItKAE.js → ada-bAJzplug.js} +0 -0
  285. /package/dist/web/assets/{andromeeda-Dy5zcy0Q.js → andromeeda-DUCpISy6.js} +0 -0
  286. /package/dist/web/assets/{apache-B6T9jrc0.js → apache-BhAr84Z_.js} +0 -0
  287. /package/dist/web/assets/{apex-CTsIxDQw.js → apex-CRv8YnNq.js} +0 -0
  288. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-BzWM7XPR.js} +0 -0
  289. /package/dist/web/assets/{applescript-DuycKmmb.js → applescript-CD-Cw8bt.js} +0 -0
  290. /package/dist/web/assets/{apply-theme-r8tKDrCR.js → apply-theme-DJ794zSQ.js} +0 -0
  291. /package/dist/web/assets/{ara-CISL3599.js → ara-BUzeQYoG.js} +0 -0
  292. /package/dist/web/assets/{asciidoc-BXyE2Lpr.js → asciidoc-CT2hTg-v.js} +0 -0
  293. /package/dist/web/assets/{asm-y258phtP.js → asm-CAu35JeY.js} +0 -0
  294. /package/dist/web/assets/{aurora-x-Dq3Jc78M.js → aurora-x-BeFJoIA1.js} +0 -0
  295. /package/dist/web/assets/{awk-DxL6XSJv.js → awk-Vau-DrGN.js} +0 -0
  296. /package/dist/web/assets/{ayu-dark-DjoK6Evo.js → ayu-dark-CLe-OTsQ.js} +0 -0
  297. /package/dist/web/assets/{ayu-light-CKKXBLMI.js → ayu-light-CJbQfu4M.js} +0 -0
  298. /package/dist/web/assets/{ayu-mirage-DGXSrrXB.js → ayu-mirage-GWaKwdrx.js} +0 -0
  299. /package/dist/web/assets/{ballerina-DZSXFmUf.js → ballerina-DWu0USI5.js} +0 -0
  300. /package/dist/web/assets/{bat-BNsZk6nY.js → bat-_07Huw7E.js} +0 -0
  301. /package/dist/web/assets/{beancount-cPl4gJct.js → beancount-EcvogrcF.js} +0 -0
  302. /package/dist/web/assets/{berry-DGAodERk.js → berry-BBB87SY7.js} +0 -0
  303. /package/dist/web/assets/{bibtex-DftVoRif.js → bibtex-DbueS87A.js} +0 -0
  304. /package/dist/web/assets/{bicep-Suioyyzh.js → bicep-tcmXnUo4.js} +0 -0
  305. /package/dist/web/assets/{bird2-DjU24I2P.js → bird2-B2qwilIR.js} +0 -0
  306. /package/dist/web/assets/{c-DFi8Y_nN.js → c-Z58oivSt.js} +0 -0
  307. /package/dist/web/assets/{c3-1eSHnH5V.js → c3-U40cGT9p.js} +0 -0
  308. /package/dist/web/assets/{cadence-DLLsFwQP.js → cadence-BFcD-LyM.js} +0 -0
  309. /package/dist/web/assets/{can-open-in-ppm-in727eFQ.js → can-open-in-ppm-CdCK2c2a.js} +0 -0
  310. /package/dist/web/assets/{catppuccin-frappe-DDj0JEiY.js → catppuccin-frappe-RkC9AX6J.js} +0 -0
  311. /package/dist/web/assets/{catppuccin-latte-DPyANHyR.js → catppuccin-latte-Bsi4l-nO.js} +0 -0
  312. /package/dist/web/assets/{catppuccin-macchiato-CO-h8VTT.js → catppuccin-macchiato-B8JIdTW-.js} +0 -0
  313. /package/dist/web/assets/{catppuccin-mocha-BCmbN2IZ.js → catppuccin-mocha-oH8wwrDj.js} +0 -0
  314. /package/dist/web/assets/{clarity-C0pYsZjs.js → clarity-C3WNeRCR.js} +0 -0
  315. /package/dist/web/assets/{clipboard-DBLGKMrH.js → clipboard-B-gwJ8DF.js} +0 -0
  316. /package/dist/web/assets/{clojure-JFr6s8iZ.js → clojure-CFoAz7u9.js} +0 -0
  317. /package/dist/web/assets/{cmake-EEcLEgVy.js → cmake-Bcsqp9yR.js} +0 -0
  318. /package/dist/web/assets/{codeowners-Deh3Lx0u.js → codeowners-DC2Z7heP.js} +0 -0
  319. /package/dist/web/assets/{codeql-C317igT5.js → codeql-BfG68Al5.js} +0 -0
  320. /package/dist/web/assets/{common-lisp-DepbATZs.js → common-lisp-Z5P-lKQe.js} +0 -0
  321. /package/dist/web/assets/{coq-MxgxU5ya.js → coq-BJslqrnv.js} +0 -0
  322. /package/dist/web/assets/{csharp-eEFXdsAq.js → csharp-DhhZTOTs.js} +0 -0
  323. /package/dist/web/assets/{css-Db-syWW9.js → css-DiFIpMfE.js} +0 -0
  324. /package/dist/web/assets/{csv-DHeYcbaq.js → csv-DViLbHxI.js} +0 -0
  325. /package/dist/web/assets/{csv-parser-_JWMLGl3.js → csv-parser-B2v8MoUy.js} +0 -0
  326. /package/dist/web/assets/{cue-BmUNowrt.js → cue-CoG3mg2I.js} +0 -0
  327. /package/dist/web/assets/{cypher-XjXHgCNS.js → cypher-jTxC8NAB.js} +0 -0
  328. /package/dist/web/assets/{d-BWJnZtig.js → d-6hKIUAbU.js} +0 -0
  329. /package/dist/web/assets/{dark-plus-Db7umas1.js → dark-plus-D_-np2Sr.js} +0 -0
  330. /package/dist/web/assets/{dart-DSI4HYh1.js → dart-CVpxLpVn.js} +0 -0
  331. /package/dist/web/assets/{data-grid-types-CfVHh9gv.js → data-grid-types-Dq_H1IPa.js} +0 -0
  332. /package/dist/web/assets/{dax-yw1JsAni.js → dax-BPqJ8qIY.js} +0 -0
  333. /package/dist/web/assets/{desktop-C2QO4RvD.js → desktop-u_y5_Hr5.js} +0 -0
  334. /package/dist/web/assets/{diff-Drt8aEyp.js → diff-Duj1XMrd.js} +0 -0
  335. /package/dist/web/assets/{dist-DNRvhVdz.js → dist-CEZ4plgH.js} +0 -0
  336. /package/dist/web/assets/{dist-VBxOHLmN.js → dist-aDSxTR_v.js} +0 -0
  337. /package/dist/web/assets/{docker-ByDKbmLl.js → docker-8UGSCc7N.js} +0 -0
  338. /package/dist/web/assets/{dotenv-BkfOLytR.js → dotenv-DVnj8kyR.js} +0 -0
  339. /package/dist/web/assets/{dracula-DAmrm-Un.js → dracula-D0cEmSg7.js} +0 -0
  340. /package/dist/web/assets/{dracula-soft-BUOEnKqR.js → dracula-soft-DfGS5Yfr.js} +0 -0
  341. /package/dist/web/assets/{dream-maker-DQBIfSiO.js → dream-maker-qG8kMGTF.js} +0 -0
  342. /package/dist/web/assets/{emacs-lisp-Blh5kBD3.js → emacs-lisp-iVqyMLyT.js} +0 -0
  343. /package/dist/web/assets/{everforest-dark-CEzhDxHq.js → everforest-dark-Cruygd5U.js} +0 -0
  344. /package/dist/web/assets/{everforest-light-CPk_5Khm.js → everforest-light-BiJa8RHL.js} +0 -0
  345. /package/dist/web/assets/{fennel-B1MT_EBZ.js → fennel-H6XSEIoz.js} +0 -0
  346. /package/dist/web/assets/{fish-C_HGcewt.js → fish-ho_7CxX6.js} +0 -0
  347. /package/dist/web/assets/{fluent-Dp1Iclmx.js → fluent-1l5ssJTF.js} +0 -0
  348. /package/dist/web/assets/{format-bytes-53WBtyi2.js → format-bytes-gQw5wzqa.js} +0 -0
  349. /package/dist/web/assets/{fortran-free-form-B_-UA4XC.js → fortran-free-form-Bo1x233A.js} +0 -0
  350. /package/dist/web/assets/{gdscript-Bm_JpjS_.js → gdscript-DffFMp_k.js} +0 -0
  351. /package/dist/web/assets/{gdshader-C_PNmfYU.js → gdshader-CvnpAiAF.js} +0 -0
  352. /package/dist/web/assets/{genie-B5rmR2TF.js → genie-2eOvAEzW.js} +0 -0
  353. /package/dist/web/assets/{gherkin-BTJ34TJ9.js → gherkin-DL-5OUlV.js} +0 -0
  354. /package/dist/web/assets/{github-dark-DOkNVj9H.js → github-dark-DrTZprgG.js} +0 -0
  355. /package/dist/web/assets/{github-dark-default-DTsmiyVo.js → github-dark-default-_mDH8IMP.js} +0 -0
  356. /package/dist/web/assets/{github-dark-dimmed-CjbxAe9s.js → github-dark-dimmed-C48hJuLn.js} +0 -0
  357. /package/dist/web/assets/{github-dark-high-contrast-r_M8F_k7.js → github-dark-high-contrast-BQ-zGGNe.js} +0 -0
  358. /package/dist/web/assets/{github-light-BzfxyMyb.js → github-light-BYc90Yx8.js} +0 -0
  359. /package/dist/web/assets/{github-light-default-BOqiBIy5.js → github-light-default-Daecj4J5.js} +0 -0
  360. /package/dist/web/assets/{github-light-high-contrast-DQzBrDM4.js → github-light-high-contrast-DoafCbsU.js} +0 -0
  361. /package/dist/web/assets/{gleam-BWmuAtD8.js → gleam-hUWWjmnp.js} +0 -0
  362. /package/dist/web/assets/{gn-CNQPjiZW.js → gn-7PJ3R70o.js} +0 -0
  363. /package/dist/web/assets/{gnuplot-BlHjcPMe.js → gnuplot-V-Pxck7b.js} +0 -0
  364. /package/dist/web/assets/{go-Bn8Ba9uz.js → go-ARF6DprD.js} +0 -0
  365. /package/dist/web/assets/{groovy-C1Y1_F4V.js → groovy-CKGBIPOz.js} +0 -0
  366. /package/dist/web/assets/{gruvbox-dark-hard-DueOsHav.js → gruvbox-dark-hard-Cm9wqdI3.js} +0 -0
  367. /package/dist/web/assets/{gruvbox-dark-medium-DaQjD538.js → gruvbox-dark-medium-Cmsd6dG8.js} +0 -0
  368. /package/dist/web/assets/{gruvbox-dark-soft-CbbX2WOm.js → gruvbox-dark-soft-BBGlGGCz.js} +0 -0
  369. /package/dist/web/assets/{gruvbox-light-hard-E2M70ljZ.js → gruvbox-light-hard-BCR7KmqG.js} +0 -0
  370. /package/dist/web/assets/{gruvbox-light-medium-B_DDLY6h.js → gruvbox-light-medium-CP32bga8.js} +0 -0
  371. /package/dist/web/assets/{gruvbox-light-soft-2SZmyC1F.js → gruvbox-light-soft-qjxk8ps6.js} +0 -0
  372. /package/dist/web/assets/{haskell-D8DeoXtG.js → haskell-C5DzwGRa.js} +0 -0
  373. /package/dist/web/assets/{haxe-BmZm7B0W.js → haxe-C8f_jCVv.js} +0 -0
  374. /package/dist/web/assets/{hcl-CKnBM3ng.js → hcl-DetJkf6E.js} +0 -0
  375. /package/dist/web/assets/{hjson-Ct9t4-ci.js → hjson-DeZhwIdo.js} +0 -0
  376. /package/dist/web/assets/{hlsl-BZSASBqS.js → hlsl-SMIDLRyX.js} +0 -0
  377. /package/dist/web/assets/{horizon-CzX1AZ08.js → horizon-BMAVDxMx.js} +0 -0
  378. /package/dist/web/assets/{horizon-bright-CCVNeRIP.js → horizon-bright-3BaV_VGD.js} +0 -0
  379. /package/dist/web/assets/{houston-DBlJN7xc.js → houston-BWBbEPvb.js} +0 -0
  380. /package/dist/web/assets/{hy-Dyc6h6Xb.js → hy-DcsmE-Nm.js} +0 -0
  381. /package/dist/web/assets/{image-gallery-DujlrjOC.js → image-gallery-Bqb-wjNw.js} +0 -0
  382. /package/dist/web/assets/{imba-Bauru7s2.js → imba-CnkgnYMf.js} +0 -0
  383. /package/dist/web/assets/{ini-SjdwtaTp.js → ini-BnvTdau7.js} +0 -0
  384. /package/dist/web/assets/{java-D2r0ocpf.js → java-BLFACXat.js} +0 -0
  385. /package/dist/web/assets/{javascript-y-lof7iP.js → javascript-BKcFWENk.js} +0 -0
  386. /package/dist/web/assets/{json-DO4LSx5A.js → json-3AJtJADf.js} +0 -0
  387. /package/dist/web/assets/{json5-BEwWf-W3.js → json5-DLyDVlwe.js} +0 -0
  388. /package/dist/web/assets/{jsonc-OBEFv7gP.js → jsonc-B5DgF1jW.js} +0 -0
  389. /package/dist/web/assets/{jsonl-DOyW4Mg3.js → jsonl-CV_O71a2.js} +0 -0
  390. /package/dist/web/assets/{jsonnet-BL5R2CpP.js → jsonnet-DZfPLgYz.js} +0 -0
  391. /package/dist/web/assets/{jssm-ksPusAyg.js → jssm-Cr1zYOsY.js} +0 -0
  392. /package/dist/web/assets/{jsx-B6XEu6O-.js → jsx-VN-JS2_q.js} +0 -0
  393. /package/dist/web/assets/{kanagawa-dragon-JBVe_ji_.js → kanagawa-dragon-D9CcT3xg.js} +0 -0
  394. /package/dist/web/assets/{kanagawa-lotus-ChprO68_.js → kanagawa-lotus-DLXsB8fr.js} +0 -0
  395. /package/dist/web/assets/{kanagawa-wave-D_uB_R0Q.js → kanagawa-wave-weDs3Wjz.js} +0 -0
  396. /package/dist/web/assets/{kdl-pf5B8N_m.js → kdl-0XF56jnT.js} +0 -0
  397. /package/dist/web/assets/{kotlin-BdF0METC.js → kotlin-BPkH9vzk.js} +0 -0
  398. /package/dist/web/assets/{kusto-BneVP-OU.js → kusto-DMVpADb9.js} +0 -0
  399. /package/dist/web/assets/{laserwave-C4-UPIyG.js → laserwave-Cl6T2cGP.js} +0 -0
  400. /package/dist/web/assets/{lean-BN6Jdymq.js → lean-BvwzQGl1.js} +0 -0
  401. /package/dist/web/assets/{less-Cx-fdlDC.js → less-DI_03xkC.js} +0 -0
  402. /package/dist/web/assets/{lib-CMuBa85c.js → lib-v9x_qXgI.js} +0 -0
  403. /package/dist/web/assets/{libesm-BB74PRsH.js → libesm-dcbw4_18.js} +0 -0
  404. /package/dist/web/assets/{light-plus-DFnnn_iL.js → light-plus-hIiIsQN_.js} +0 -0
  405. /package/dist/web/assets/{llvm-BGxtW0Nw.js → llvm-B5hjjSWc.js} +0 -0
  406. /package/dist/web/assets/{loader-circle-Dpqcv0eS.js → loader-circle-DouKgIXy.js} +0 -0
  407. /package/dist/web/assets/{lock-DldbT31x.js → lock-CNxUGpTk.js} +0 -0
  408. /package/dist/web/assets/{log-kQb4eJnj.js → log-D28raQiE.js} +0 -0
  409. /package/dist/web/assets/{logo-DqAzaq7s.js → logo-6Cdd4ASb.js} +0 -0
  410. /package/dist/web/assets/{luau-Dc2cp-zS.js → luau-CK9nKpOX.js} +0 -0
  411. /package/dist/web/assets/{make-DYg9dwaJ.js → make-CVnxKCB6.js} +0 -0
  412. /package/dist/web/assets/{markdown-O4EPpS3K.js → markdown-B9mMQzD5.js} +0 -0
  413. /package/dist/web/assets/{material-theme-ClP8yfQ2.js → material-theme-BZSs2o0j.js} +0 -0
  414. /package/dist/web/assets/{material-theme-darker-LOK55hGu.js → material-theme-darker-BeAQBzQ_.js} +0 -0
  415. /package/dist/web/assets/{material-theme-lighter-j72tzZqi.js → material-theme-lighter-BAe0IXcs.js} +0 -0
  416. /package/dist/web/assets/{material-theme-ocean-C19LKClA.js → material-theme-ocean-CiPIf0Np.js} +0 -0
  417. /package/dist/web/assets/{material-theme-palenight-DJ5tN2ax.js → material-theme-palenight-DJBb4_XN.js} +0 -0
  418. /package/dist/web/assets/{matlab-DQNZ_NU8.js → matlab-CY0dbp_s.js} +0 -0
  419. /package/dist/web/assets/{mdx-CJayB7XK.js → mdx-DU9zbF7h.js} +0 -0
  420. /package/dist/web/assets/{mermaid-Cb0TRX92.js → mermaid-UmmjGwdh.js} +0 -0
  421. /package/dist/web/assets/{min-dark-CWM8Bo-m.js → min-dark-C59GkzOm.js} +0 -0
  422. /package/dist/web/assets/{min-light-BBR1sjS2.js → min-light-B9QglrdQ.js} +0 -0
  423. /package/dist/web/assets/{mipsasm-Jc7u7YSS.js → mipsasm-Dc6Jsr_D.js} +0 -0
  424. /package/dist/web/assets/{mojo-CTB0cutd.js → mojo-D5ij-nGW.js} +0 -0
  425. /package/dist/web/assets/{monitor-4vWfqzuN.js → monitor-CxPV_fQI.js} +0 -0
  426. /package/dist/web/assets/{monokai-CvM_YNKx.js → monokai-QAtE_yCP.js} +0 -0
  427. /package/dist/web/assets/{moonbit-CbFEfYYM.js → moonbit-BMtIdiTL.js} +0 -0
  428. /package/dist/web/assets/{move-BT38p0kT.js → move-BB_jMHYZ.js} +0 -0
  429. /package/dist/web/assets/{narrat-55QZ_kOG.js → narrat-BvvoKGVn.js} +0 -0
  430. /package/dist/web/assets/{nextflow-groovy-Bg8peyqg.js → nextflow-groovy-CqxtAsnF.js} +0 -0
  431. /package/dist/web/assets/{night-owl-DzS77iPK.js → night-owl-CLoE9kjt.js} +0 -0
  432. /package/dist/web/assets/{night-owl-light-DER7fGnx.js → night-owl-light-BwIFT5Xd.js} +0 -0
  433. /package/dist/web/assets/{nix-BSI8YesN.js → nix-BKlnVT9r.js} +0 -0
  434. /package/dist/web/assets/{nord-9bn7iNWo.js → nord-B7R1KRAK.js} +0 -0
  435. /package/dist/web/assets/{nushell-qDVZn_lw.js → nushell-CzUme-NJ.js} +0 -0
  436. /package/dist/web/assets/{objective-c-Blf-ATos.js → objective-c-D-UKKiOc.js} +0 -0
  437. /package/dist/web/assets/{objective-cpp-Bz8bAq47.js → objective-cpp-BucWn4Fx.js} +0 -0
  438. /package/dist/web/assets/{ocaml-B9heAsc3.js → ocaml-EJCNXMGo.js} +0 -0
  439. /package/dist/web/assets/{odin-BaFYnOwO.js → odin-CEjnRU_3.js} +0 -0
  440. /package/dist/web/assets/{one-dark-pro-D6B_hSe-.js → one-dark-pro-Bv469NX5.js} +0 -0
  441. /package/dist/web/assets/{one-light-Ba-f0wyk.js → one-light-BlzO5RWI.js} +0 -0
  442. /package/dist/web/assets/{openscad-UvYyL2MK.js → openscad-Czl6alLw.js} +0 -0
  443. /package/dist/web/assets/{pascal-B4-qvEoG.js → pascal-BI9fLvG4.js} +0 -0
  444. /package/dist/web/assets/{pkl-B146nqGx.js → pkl-ChQLsyjU.js} +0 -0
  445. /package/dist/web/assets/{plastic-L38t-Id3.js → plastic-DGL3mtdL.js} +0 -0
  446. /package/dist/web/assets/{play-CpCAoQRZ.js → play-DpEEBJTT.js} +0 -0
  447. /package/dist/web/assets/{plsql-ItOG5Cca.js → plsql-BJDjqcWp.js} +0 -0
  448. /package/dist/web/assets/{po-imiCTaNX.js → po-DK5O3OpF.js} +0 -0
  449. /package/dist/web/assets/{poimandres-Bu6sIba9.js → poimandres-CnuqZT4T.js} +0 -0
  450. /package/dist/web/assets/{polar-CTeGoFxV.js → polar-DCsUxSsQ.js} +0 -0
  451. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-D-495m5C.js} +0 -0
  452. /package/dist/web/assets/{postcss-jvFe6GNE.js → postcss-8UjdFmis.js} +0 -0
  453. /package/dist/web/assets/{powerquery-D9Dw1hJp.js → powerquery-CAp0CGQj.js} +0 -0
  454. /package/dist/web/assets/{powershell-CZqvYQtg.js → powershell-CbBsKZJP.js} +0 -0
  455. /package/dist/web/assets/{prisma-B4D2nO_7.js → prisma-BnNpXCJN.js} +0 -0
  456. /package/dist/web/assets/{prolog-CQbyUcCF.js → prolog-CC-l9E8Y.js} +0 -0
  457. /package/dist/web/assets/{proto-Cwpo0cW1.js → proto-CV_1k2rL.js} +0 -0
  458. /package/dist/web/assets/{puppet-CKxCwBod.js → puppet-2D8HU2bv.js} +0 -0
  459. /package/dist/web/assets/{purescript-B-MyXMMp.js → purescript-BEJAORgU.js} +0 -0
  460. /package/dist/web/assets/{python-D6QbSR2S.js → python-BBdbb7nn.js} +0 -0
  461. /package/dist/web/assets/{qmldir-BpTSVcw_.js → qmldir-D5ZYI4Ie.js} +0 -0
  462. /package/dist/web/assets/{qss-DBc4gbpe.js → qss-BOJVGyXE.js} +0 -0
  463. /package/dist/web/assets/{r-i9jMMi0s.js → r-B5dlEtkj.js} +0 -0
  464. /package/dist/web/assets/{racket-BWNaxU7C.js → racket-BUAEygJ6.js} +0 -0
  465. /package/dist/web/assets/{raku-fqTZLn7L.js → raku-BG0Ss0ID.js} +0 -0
  466. /package/dist/web/assets/{react-X4jUntEf.js → react-B549sK_T.js} +0 -0
  467. /package/dist/web/assets/{red-D-JEcuzA.js → red-C5yyQbtG.js} +0 -0
  468. /package/dist/web/assets/{refresh-cw-B9iv7csb.js → refresh-cw-BQU5g2CY.js} +0 -0
  469. /package/dist/web/assets/{reg-BbirlU1S.js → reg-DHoBWtLf.js} +0 -0
  470. /package/dist/web/assets/{regexp-D8VTFrtg.js → regexp-DDtJrlKv.js} +0 -0
  471. /package/dist/web/assets/{rel-ByQGKiNM.js → rel-CR6WJDRD.js} +0 -0
  472. /package/dist/web/assets/{resolve-theme-Cqb9zWYc.js → resolve-theme-4fexHWl6.js} +0 -0
  473. /package/dist/web/assets/{riscv-B5nqCfLE.js → riscv-2V9tC-nx.js} +0 -0
  474. /package/dist/web/assets/{ron-DOVh_iZR.js → ron-QLTmqGUw.js} +0 -0
  475. /package/dist/web/assets/{rose-pine-jp51ZUzi.js → rose-pine-be0__X3A.js} +0 -0
  476. /package/dist/web/assets/{rose-pine-dawn-B7hkmmWy.js → rose-pine-dawn-n_WQJwsW.js} +0 -0
  477. /package/dist/web/assets/{rose-pine-moon-Ff063yLj.js → rose-pine-moon-t2aLtzgB.js} +0 -0
  478. /package/dist/web/assets/{rosmsg-Cyk7Il8z.js → rosmsg-Ce1JD8vt.js} +0 -0
  479. /package/dist/web/assets/{rotate-ccw-DnuPzJRE.js → rotate-ccw-CpjRo90J.js} +0 -0
  480. /package/dist/web/assets/{rust-DRNM5epF.js → rust-5fHH-ZoW.js} +0 -0
  481. /package/dist/web/assets/{sass-D_cB0-M1.js → sass-DPmGFiOJ.js} +0 -0
  482. /package/dist/web/assets/{save-DxonD4y1.js → save-DYLiVzU8.js} +0 -0
  483. /package/dist/web/assets/{scala-Crl-1kKS.js → scala-BRMVFbZF.js} +0 -0
  484. /package/dist/web/assets/{scheme-DCaWyEJ0.js → scheme-BMSJWevF.js} +0 -0
  485. /package/dist/web/assets/{sdbl-BbXNqwtX.js → sdbl-DTZ-pEHI.js} +0 -0
  486. /package/dist/web/assets/{send-B-t5Mz26.js → send-C_sLFt56.js} +0 -0
  487. /package/dist/web/assets/{shallow-4zAfs2KW.js → shallow-CmPMAPte.js} +0 -0
  488. /package/dist/web/assets/{shellscript-BAgksBhW.js → shellscript-6-9yg5Cz.js} +0 -0
  489. /package/dist/web/assets/{shield-check-CdTiK1j2.js → shield-check-CUI36aSc.js} +0 -0
  490. /package/dist/web/assets/{shield-off-DuetkXKM.js → shield-off-GdkkVhb3.js} +0 -0
  491. /package/dist/web/assets/{slack-dark-GB-ElUW_.js → slack-dark-DKJgiR7e.js} +0 -0
  492. /package/dist/web/assets/{slack-ochin-tU6CFD3S.js → slack-ochin-WjehVgyK.js} +0 -0
  493. /package/dist/web/assets/{smalltalk-DSVGUbtL.js → smalltalk-BChf4SN1.js} +0 -0
  494. /package/dist/web/assets/{snazzy-light-DZhi3PoS.js → snazzy-light-C1WAwOx6.js} +0 -0
  495. /package/dist/web/assets/{solarized-dark-D6AMCco7.js → solarized-dark-BxREZrGx.js} +0 -0
  496. /package/dist/web/assets/{solarized-light-DepuTWSE.js → solarized-light-DG29B1pR.js} +0 -0
  497. /package/dist/web/assets/{solidity-CsxN618l.js → solidity-bDbtGwdR.js} +0 -0
  498. /package/dist/web/assets/{splunk-CF1m0Kkv.js → splunk-Ceqx5njI.js} +0 -0
  499. /package/dist/web/assets/{sql-DvyMDgrB.js → sql-OwMEKbz8.js} +0 -0
  500. /package/dist/web/assets/{ssh-config-a8sDo-Q8.js → ssh-config-WAR3lu2k.js} +0 -0
  501. /package/dist/web/assets/{stylus-DCJ-6AjT.js → stylus-Cjmoz3d8.js} +0 -0
  502. /package/dist/web/assets/{swift-c-ZFxyPP.js → swift-S_ZSBG1q.js} +0 -0
  503. /package/dist/web/assets/{synthwave-84-9NPgBcuv.js → synthwave-84-DiUeWMSh.js} +0 -0
  504. /package/dist/web/assets/{system-verilog-DPD02IOo.js → system-verilog-RRjqPjUV.js} +0 -0
  505. /package/dist/web/assets/{systemd-BUr5HRtU.js → systemd-BD3SkXoU.js} +0 -0
  506. /package/dist/web/assets/{table-FLU8ei8b.js → table-D5Q4ap4W.js} +0 -0
  507. /package/dist/web/assets/{talonscript-BNBQV5YJ.js → talonscript-DWefISOm.js} +0 -0
  508. /package/dist/web/assets/{tasl-BETjpSyu.js → tasl-DCjSS-09.js} +0 -0
  509. /package/dist/web/assets/{tcl-Clp_AZiC.js → tcl-DTYqT_66.js} +0 -0
  510. /package/dist/web/assets/{terraform-B0kZ0lim.js → terraform-CkbP5QnR.js} +0 -0
  511. /package/dist/web/assets/{text-wrap-B05VGRa-.js → text-wrap-DBvQD3kc.js} +0 -0
  512. /package/dist/web/assets/{tokyo-night-oOJ0a7Dk.js → tokyo-night-qVSqO9xy.js} +0 -0
  513. /package/dist/web/assets/{toml-DOPCSumy.js → toml-D9F5Osqs.js} +0 -0
  514. /package/dist/web/assets/{trash-2-CTXlTflF.js → trash-2-By4VMbC4.js} +0 -0
  515. /package/dist/web/assets/{triangle-alert-BRq0VAfd.js → triangle-alert-CUTURFbG.js} +0 -0
  516. /package/dist/web/assets/{tsv-D6qUGf84.js → tsv-C5tpHoxm.js} +0 -0
  517. /package/dist/web/assets/{tsx-B6qYhT6O.js → tsx-CMWkGtGm.js} +0 -0
  518. /package/dist/web/assets/{turtle-C0Rjzw73.js → turtle-Cn4z3xOi.js} +0 -0
  519. /package/dist/web/assets/{typescript-BiydShEu.js → typescript-DSyAEWsn.js} +0 -0
  520. /package/dist/web/assets/{typespec-DjmF6Aww.js → typespec-B93Qap7y.js} +0 -0
  521. /package/dist/web/assets/{typst-CPuscfcL.js → typst-DtlOmFlX.js} +0 -0
  522. /package/dist/web/assets/{use-is-mobile-b94q8RvF.js → use-is-mobile-B9UZNwSm.js} +0 -0
  523. /package/dist/web/assets/{utils-C9OjCrou.js → utils-QHe_Q6UF.js} +0 -0
  524. /package/dist/web/assets/{v-DE4vpLLQ.js → v-Dfzf-szS.js} +0 -0
  525. /package/dist/web/assets/{vala-CmUF2pS5.js → vala-CyiQZQ9f.js} +0 -0
  526. /package/dist/web/assets/{vb-BvcRoX4b.js → vb-C7DGSldd.js} +0 -0
  527. /package/dist/web/assets/{vendor-xterm-K8kpLxdb.js → vendor-xterm-BHKF_Nsf.js} +0 -0
  528. /package/dist/web/assets/{verilog-BGIzyH3R.js → verilog-kF-hClY2.js} +0 -0
  529. /package/dist/web/assets/{vesper-CG7IrHQQ.js → vesper-BmKcADuz.js} +0 -0
  530. /package/dist/web/assets/{vhdl-DMSumomw.js → vhdl-CDnb16KJ.js} +0 -0
  531. /package/dist/web/assets/{viml-Drt7AEmU.js → viml-B8cs6Ady.js} +0 -0
  532. /package/dist/web/assets/{vitesse-black-DmsjfN0b.js → vitesse-black-BR6o9Gto.js} +0 -0
  533. /package/dist/web/assets/{vitesse-dark-Dl55_WeJ.js → vitesse-dark-Dye14-KB.js} +0 -0
  534. /package/dist/web/assets/{vitesse-light-Crbv9aGg.js → vitesse-light-BLpswIxf.js} +0 -0
  535. /package/dist/web/assets/{vyper-CB7psEJj.js → vyper-DU51Z97W.js} +0 -0
  536. /package/dist/web/assets/{wasm-BY1tuM5m.js → wasm-CVqlJAR2.js} +0 -0
  537. /package/dist/web/assets/{wasm-Dck8KPgP.js → wasm-ChtccNfv.js} +0 -0
  538. /package/dist/web/assets/{wenyan-DhewQ68b.js → wenyan-DhCa3hCn.js} +0 -0
  539. /package/dist/web/assets/{wgsl-DlmiP456.js → wgsl-Bj1Ec4vv.js} +0 -0
  540. /package/dist/web/assets/{wikitext-cQCnqNgE.js → wikitext-C90DHNa0.js} +0 -0
  541. /package/dist/web/assets/{wit-S5MaCngB.js → wit-DUgbOD7Z.js} +0 -0
  542. /package/dist/web/assets/{wolfram-Bh8n_9zr.js → wolfram-BgwUqD1I.js} +0 -0
  543. /package/dist/web/assets/{x-YDu4m2Iq.js → x-DiOUR_mp.js} +0 -0
  544. /package/dist/web/assets/{yaml-BoK_17l9.js → yaml-DEQulj-8.js} +0 -0
  545. /package/dist/web/assets/{zenscript-DB3ZFwNJ.js → zenscript-C7Jp7TgT.js} +0 -0
  546. /package/dist/web/assets/{zig-Bv1BWmfT.js → zig-9x4qXYLg.js} +0 -0
@@ -0,0 +1,206 @@
1
+ /**
2
+ * NestedSubagentSpy — surface nested-agent activity on a live Agent card.
3
+ *
4
+ * The SDK streams a subagent's events with `parent_tool_use_id` only for
5
+ * agents the session itself spawned. When that agent spawns another one
6
+ * (a reviewer running a skill that forks its own worker, a planner calling
7
+ * researchers), the CLI emits nothing for the nested agent — the parent card
8
+ * looks frozen on its last step for as long as the grandchild works, which
9
+ * can be many minutes.
10
+ *
11
+ * The nested agent does keep writing its transcript under
12
+ * <sessionDir>/subagents/agent-<id>.jsonl
13
+ * with a meta whose `parentAgentId` chains back to the card's agent. While a
14
+ * card is open we tail those transcripts and emit their events stamped with
15
+ * the card's toolUseId, so they render flat inside the card exactly like the
16
+ * live depth-1 children do. Same shape as bashOutputSpy: start on tool_use,
17
+ * poll, stop on tool_result.
18
+ *
19
+ * One poller per session, not per card: the dir scan that finds nested agents
20
+ * is shared across every open card, so N cards cost one scan per tick.
21
+ *
22
+ * Spies live only in memory. A backgrounded agent that outlives the server
23
+ * process (or the session's consumer) loses live nested visibility until the
24
+ * next reload, where the transcript merger restores the same events from disk.
25
+ */
26
+
27
+ import { closeSync, openSync, readSync, statSync } from "node:fs";
28
+ import { join } from "node:path";
29
+ import type { ChatEvent } from "../types/chat.ts";
30
+ import { createAgentTranscriptLineParser } from "./subagent-transcript-merger.ts";
31
+ import { groupSubagentsByCard } from "./team-member-activity/subagent-transcript-index.ts";
32
+
33
+ const POLL_INTERVAL_MS = 1000;
34
+ /** Repeated per-file read failures (locked/replaced file) log once per this window. */
35
+ const WARN_THROTTLE_MS = 30_000;
36
+
37
+ interface TranscriptTail {
38
+ bytesRead: number;
39
+ /** Unterminated trailing bytes — kept raw so a split multi-byte char survives. */
40
+ pending: Buffer;
41
+ parser: ReturnType<typeof createAgentTranscriptLineParser>;
42
+ }
43
+
44
+ interface CardSpy {
45
+ /** Tails keyed by nested agentId. */
46
+ tails: Map<string, TranscriptTail>;
47
+ onEvents: (events: ChatEvent[]) => void;
48
+ }
49
+
50
+ interface SessionSpy {
51
+ sessionId: string;
52
+ subagentsDir: string;
53
+ cards: Map<string, CardSpy>;
54
+ intervalId: ReturnType<typeof setInterval>;
55
+ lastWarnAt: number;
56
+ }
57
+
58
+ /** Keyed by sessionId — one poller shared by every open card of that session. */
59
+ const sessions = new Map<string, SessionSpy>();
60
+
61
+ /** Read bytes appended since the last tick; null when nothing new. Throws on I/O error. */
62
+ function readAppended(filePath: string, tail: TranscriptTail): Buffer | null {
63
+ const size = statSync(filePath).size;
64
+ if (size <= tail.bytesRead) return null;
65
+ const fd = openSync(filePath, "r");
66
+ try {
67
+ const buf = Buffer.alloc(size - tail.bytesRead);
68
+ const n = readSync(fd, buf, 0, buf.length, tail.bytesRead);
69
+ tail.bytesRead += n;
70
+ return buf.subarray(0, n);
71
+ } finally {
72
+ closeSync(fd);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Split pending + chunk into complete lines; the unterminated remainder stays
78
+ * raw in `tail.pending` because a write can land mid-record, even mid-char.
79
+ */
80
+ function drainLines(tail: TranscriptTail, chunk: Buffer): string[] {
81
+ const all = tail.pending.length ? Buffer.concat([tail.pending, chunk]) : chunk;
82
+ const lastNl = all.lastIndexOf(0x0a);
83
+ if (lastNl === -1) {
84
+ tail.pending = all;
85
+ return [];
86
+ }
87
+ tail.pending = all.subarray(lastNl + 1);
88
+ return all.subarray(0, lastNl).toString("utf8").split("\n");
89
+ }
90
+
91
+ function warnThrottled(spy: SessionSpy, message: string): void {
92
+ const now = Date.now();
93
+ if (now - spy.lastWarnAt < WARN_THROTTLE_MS) return;
94
+ spy.lastWarnAt = now;
95
+ console.warn(`[nested-spy] session=${spy.sessionId} ${message}`);
96
+ }
97
+
98
+ /** One poll for a session: scan the dir once, then drain each open card's nested tails. */
99
+ function tick(spy: SessionSpy): void {
100
+ let groups: ReturnType<typeof groupSubagentsByCard>;
101
+ try {
102
+ groups = groupSubagentsByCard(spy.subagentsDir);
103
+ } catch (e) {
104
+ warnThrottled(spy, `scan failed: ${(e as Error).message}`);
105
+ return;
106
+ }
107
+ for (const [cardToolUseId, card] of spy.cards) {
108
+ const group = groups.get(cardToolUseId);
109
+ if (!group) continue;
110
+ const events: ChatEvent[] = [];
111
+ for (const agent of group) {
112
+ // Depth-1 (the card's own agent) is already streamed live by the SDK.
113
+ if (!agent.parentAgentId) continue;
114
+ let tail = card.tails.get(agent.agentId);
115
+ if (!tail) {
116
+ tail = { bytesRead: 0, pending: Buffer.alloc(0), parser: createAgentTranscriptLineParser() };
117
+ card.tails.set(agent.agentId, tail);
118
+ }
119
+ let chunk: Buffer | null;
120
+ try {
121
+ chunk = readAppended(agent.transcriptPath, tail);
122
+ } catch (e) {
123
+ // A locked or replaced file must not stall the other agents in the group.
124
+ warnThrottled(spy, `read failed for ${agent.agentId}: ${(e as Error).message}`);
125
+ continue;
126
+ }
127
+ if (!chunk) continue;
128
+ for (const line of drainLines(tail, chunk)) {
129
+ for (const timed of tail.parser.feed(line)) {
130
+ events.push(withParent(timed.ev, cardToolUseId));
131
+ }
132
+ }
133
+ }
134
+ if (events.length > 0) card.onEvents(events);
135
+ }
136
+ }
137
+
138
+ /** Stamp a child with the card it belongs to, keeping the event union intact. */
139
+ function withParent(ev: ChatEvent, parentToolUseId: string): ChatEvent {
140
+ switch (ev.type) {
141
+ case "text":
142
+ case "thinking":
143
+ case "tool_use":
144
+ case "tool_result":
145
+ return { ...ev, parentToolUseId };
146
+ default:
147
+ return ev;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Start tailing nested transcripts for an open Agent/Task card.
153
+ * `sessionDir` is …/projects/<slug>/<sessionId>; its subagents/ child may not
154
+ * exist yet — the CLI creates it with the first subagent write, and the scan
155
+ * simply finds nothing until then.
156
+ */
157
+ function startSpy(
158
+ sessionId: string,
159
+ cardToolUseId: string,
160
+ sessionDir: string,
161
+ onEvents: (events: ChatEvent[]) => void,
162
+ ): void {
163
+ let spy = sessions.get(sessionId);
164
+ if (!spy) {
165
+ const created: SessionSpy = {
166
+ sessionId,
167
+ subagentsDir: join(sessionDir, "subagents"),
168
+ cards: new Map(),
169
+ lastWarnAt: 0,
170
+ intervalId: setInterval(() => tick(created), POLL_INTERVAL_MS),
171
+ };
172
+ sessions.set(sessionId, created);
173
+ spy = created;
174
+ }
175
+ if (spy.cards.has(cardToolUseId)) return;
176
+ spy.cards.set(cardToolUseId, { tails: new Map(), onEvents });
177
+ console.log(`[nested-spy] session=${sessionId} started card=${cardToolUseId} (${spy.cards.size} open)`);
178
+ }
179
+
180
+ /** Stop a card's spy, draining whatever landed since the last poll first. */
181
+ function stopSpy(cardToolUseId: string): void {
182
+ for (const spy of sessions.values()) {
183
+ if (!spy.cards.has(cardToolUseId)) continue;
184
+ // Final drain covers every open card; the spare work is one scan.
185
+ tick(spy);
186
+ spy.cards.delete(cardToolUseId);
187
+ console.log(`[nested-spy] session=${spy.sessionId} stopped card=${cardToolUseId} (${spy.cards.size} open)`);
188
+ if (spy.cards.size === 0) {
189
+ clearInterval(spy.intervalId);
190
+ sessions.delete(spy.sessionId);
191
+ }
192
+ return;
193
+ }
194
+ }
195
+
196
+ /** Stop every spy for a session (turn ended, session migrated, socket closed). */
197
+ function stopAllForSession(sessionId: string): void {
198
+ const spy = sessions.get(sessionId);
199
+ if (!spy) return;
200
+ tick(spy);
201
+ clearInterval(spy.intervalId);
202
+ sessions.delete(sessionId);
203
+ console.log(`[nested-spy] session=${sessionId} stopped all (${spy.cards.size} cards)`);
204
+ }
205
+
206
+ export const nestedSubagentSpy = { startSpy, stopSpy, stopAllForSession };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Groups the raw NAL units from `NalSplitter` into WebCodecs-ready access units.
3
+ *
4
+ * `VideoDecoder` decodes one access unit (one frame) per `EncodedVideoChunk`, not one
5
+ * NAL — feeding SPS/PPS/slice as three separate chunks throws or corrupts decode. libx264
6
+ * (no B-frames, `-bf 0`) emits at most one slice NAL per frame, optionally preceded by
7
+ * SPS/PPS on a keyframe, so an AU boundary is simply "the next VCL NAL after we already
8
+ * buffered one" — everything non-VCL (SPS/PPS/AUD/SEI) that arrived first attaches to the
9
+ * AU it precedes.
10
+ */
11
+ import { NalSplitter, nalType, isVclNal, NAL_TYPE_IDR, NAL_TYPE_SPS, NAL_TYPE_PPS } from "./nal-splitter.ts";
12
+
13
+ export interface AccessUnit {
14
+ /** Annex-B encoded bytes (start codes + NAL payloads) — one `EncodedVideoChunk` worth. */
15
+ bytes: Uint8Array;
16
+ isKey: boolean;
17
+ }
18
+
19
+ const START_CODE = Uint8Array.of(0, 0, 0, 1);
20
+
21
+ function encodeAnnexB(nals: Uint8Array[]): Uint8Array {
22
+ let total = 0;
23
+ for (const n of nals) total += START_CODE.length + n.length;
24
+ const out = new Uint8Array(total);
25
+ let offset = 0;
26
+ for (const n of nals) {
27
+ out.set(START_CODE, offset);
28
+ offset += START_CODE.length;
29
+ out.set(n, offset);
30
+ offset += n.length;
31
+ }
32
+ return out;
33
+ }
34
+
35
+ export class AccessUnitAssembler {
36
+ private readonly splitter = new NalSplitter();
37
+ private pending: Uint8Array[] = [];
38
+ private pendingHasVcl = false;
39
+ private sps: Uint8Array | null = null;
40
+ private pps: Uint8Array | null = null;
41
+
42
+ /** Feed a raw ffmpeg stdout chunk; returns zero or more completed access units. */
43
+ push(chunk: Uint8Array): AccessUnit[] {
44
+ const aus: AccessUnit[] = [];
45
+ for (const nal of this.splitter.push(chunk)) {
46
+ const type = nalType(nal);
47
+ if (type === NAL_TYPE_SPS) this.sps = nal;
48
+ else if (type === NAL_TYPE_PPS) this.pps = nal;
49
+
50
+ const vcl = isVclNal(nal);
51
+ if (vcl && this.pendingHasVcl) {
52
+ const au = this.flush();
53
+ if (au) aus.push(au);
54
+ }
55
+ this.pending.push(nal);
56
+ if (vcl) this.pendingHasVcl = true;
57
+ }
58
+ return aus;
59
+ }
60
+
61
+ /** SPS bytes seen so far (NAL header byte included) — used to derive the avc1 codec
62
+ * string. Null until the encoder has emitted its first keyframe. */
63
+ cachedSps(): Uint8Array | null {
64
+ return this.sps;
65
+ }
66
+
67
+ private flush(): AccessUnit | null {
68
+ if (this.pending.length === 0) {
69
+ this.pendingHasVcl = false;
70
+ return null;
71
+ }
72
+ let nals = this.pending;
73
+ const isKey = nals.some((n) => nalType(n) === NAL_TYPE_IDR);
74
+ if (isKey) {
75
+ // A late-joining client (or a client that missed the very first AU) needs SPS+PPS
76
+ // concatenated into the same key chunk it decodes — three separate chunks is not
77
+ // decodable Annex-B input for WebCodecs. Normally libx264 already includes them; this
78
+ // is a defensive backfill from the cache, not the common path.
79
+ const hasSps = nals.some((n) => nalType(n) === NAL_TYPE_SPS);
80
+ const hasPps = nals.some((n) => nalType(n) === NAL_TYPE_PPS);
81
+ const prefix: Uint8Array[] = [];
82
+ if (!hasSps && this.sps) prefix.push(this.sps);
83
+ if (!hasPps && this.pps) prefix.push(this.pps);
84
+ if (prefix.length > 0) nals = [...prefix, ...nals];
85
+ }
86
+ this.pending = [];
87
+ this.pendingHasVcl = false;
88
+ return { bytes: encodeAnnexB(nals), isKey };
89
+ }
90
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Derive the WebCodecs `avc1.PPCCLL` codec string from a real SPS NAL rather than
3
+ * guessing. `encoderArgs()` in `ffmpeg-capabilities.ts` sets no `-profile`/`-level`, so the
4
+ * actual encoded profile/level is encoder-default and unknown until the bitstream exists —
5
+ * a hardcoded guess can mismatch and make `VideoDecoder.configure()` throw or refuse.
6
+ *
7
+ * SPS layout (after the 1-byte NAL header at index 0): profile_idc, then the
8
+ * constraint-flags/reserved byte, then level_idc — always the first three bytes of the
9
+ * RBSP, unaffected by emulation-prevention bytes (those only appear deeper in the SPS).
10
+ */
11
+ export function avc1CodecString(sps: Uint8Array): string | null {
12
+ if (sps.length < 4) return null;
13
+ const profileIdc = sps[1]!;
14
+ const constraintFlags = sps[2]!;
15
+ const levelIdc = sps[3]!;
16
+ const hex = (n: number) => n.toString(16).padStart(2, "0");
17
+ return `avc1.${hex(profileIdc)}${hex(constraintFlags)}${hex(levelIdc)}`;
18
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Incremental Annex-B start-code splitter.
3
+ *
4
+ * ffmpeg's `-f h264` stdout is a raw Annex-B byte stream: NAL units separated by
5
+ * `00 00 01` or `00 00 00 01` start codes, with no framing that respects our read
6
+ * chunk boundaries. A NAL can straddle two `stdout` reads, so this holds the tail
7
+ * of the last incomplete NAL across calls to `push()` rather than assuming a full
8
+ * NAL always arrives in one chunk.
9
+ */
10
+
11
+ /** NAL unit types relevant to access-unit assembly (ITU-T H.264 Table 7-1). */
12
+ export const NAL_TYPE_SLICE_NON_IDR = 1;
13
+ export const NAL_TYPE_IDR = 5;
14
+ export const NAL_TYPE_SPS = 7;
15
+ export const NAL_TYPE_PPS = 8;
16
+
17
+ /** Low 5 bits of the NAL header byte (the unit's first byte) is `nal_unit_type`. */
18
+ export function nalType(nal: Uint8Array): number {
19
+ return (nal[0] ?? 0) & 0x1f;
20
+ }
21
+
22
+ /** A slice NAL starts a new access unit; SPS/PPS/AUD/SEI attach to whichever AU follows them. */
23
+ export function isVclNal(nal: Uint8Array): boolean {
24
+ const t = nalType(nal);
25
+ return t === NAL_TYPE_SLICE_NON_IDR || t === NAL_TYPE_IDR;
26
+ }
27
+
28
+ /** `Bun.spawn` stdout chunks type as `Uint8Array<ArrayBufferLike>` (could in principle back
29
+ * onto a SharedArrayBuffer), while `new Uint8Array(n)` types as the narrower
30
+ * `Uint8Array<ArrayBuffer>` — pin everything here to the wider type so a plain chunk and our
31
+ * own freshly-allocated buffers can always mix without a type error. */
32
+ export type Bytes = Uint8Array<ArrayBufferLike>;
33
+
34
+ function concat(a: Bytes, b: Bytes): Bytes {
35
+ if (a.length === 0) return b;
36
+ if (b.length === 0) return a;
37
+ const out = new Uint8Array(a.length + b.length);
38
+ out.set(a, 0);
39
+ out.set(b, a.length);
40
+ return out;
41
+ }
42
+
43
+ /** Index of the next `00 00 01` marker at or after `from`, or -1. This always finds the
44
+ * *last* 3 bytes of a start code — a 4-byte `00 00 00 01` form has its 3-byte suffix match
45
+ * here too (at index+1), and H.264 emulation prevention guarantees `00 00 01`/`00 00 00`
46
+ * never occurs inside real RBSP payload data, so there is no false-positive risk scanning
47
+ * straight through NAL bodies. */
48
+ function findStartCode(buf: Bytes, from: number): number {
49
+ for (let i = from; i + 2 < buf.length; i++) {
50
+ if (buf[i] === 0 && buf[i + 1] === 0 && buf[i + 2] === 1) return i;
51
+ }
52
+ return -1;
53
+ }
54
+
55
+ /** Bytes to skip past a marker found by `findStartCode` to reach the payload — always 3
56
+ * (the marker itself), regardless of how many extra leading zero bytes preceded it. */
57
+ const MARKER_LEN = 3;
58
+
59
+ /** Guard against an unbounded buffer if ffmpeg ever emits a byte stream with no start
60
+ * code at all (would indicate a broken pipe/format, not a real Annex-B stream). */
61
+ const MAX_BUFFER_BYTES = 8 * 1024 * 1024;
62
+
63
+ export class NalSplitter {
64
+ private buffer: Bytes = new Uint8Array(0);
65
+
66
+ /** Feed a raw stdout chunk; returns zero or more complete NAL payloads (start code
67
+ * stripped). Incomplete trailing bytes are retained for the next call. */
68
+ push(chunk: Bytes): Bytes[] {
69
+ this.buffer = concat(this.buffer, chunk);
70
+ const nals: Bytes[] = [];
71
+
72
+ let start = findStartCode(this.buffer, 0);
73
+ if (start === -1) {
74
+ if (this.buffer.length > MAX_BUFFER_BYTES) this.buffer = new Uint8Array(0);
75
+ return nals;
76
+ }
77
+ // Drop any garbage before the first start code (should not happen with a clean
78
+ // ffmpeg pipe, but a partial NAL from before this splitter attached must not corrupt
79
+ // the first parsed unit).
80
+ for (;;) {
81
+ const nextStart = findStartCode(this.buffer, start + MARKER_LEN);
82
+ if (nextStart === -1) {
83
+ this.buffer = this.buffer.slice(start);
84
+ break;
85
+ }
86
+ // `nextStart` only marks the *last* 3 bytes of the following start code — a 4-byte
87
+ // `00 00 00 01` form leaves its leading zero sitting right before `nextStart`, which
88
+ // would otherwise get appended to this NAL's payload as a spurious trailing byte.
89
+ // Trim any such zero bytes back to the real payload end (standard Annex-B handling of
90
+ // `leading_zero_8bits`/`trailing_zero_8bits`).
91
+ let payloadEnd = nextStart;
92
+ const payloadStart = start + MARKER_LEN;
93
+ while (payloadEnd > payloadStart && this.buffer[payloadEnd - 1] === 0) payloadEnd--;
94
+ const payload = this.buffer.slice(payloadStart, payloadEnd);
95
+ if (payload.length > 0) nals.push(payload);
96
+ start = nextStart;
97
+ }
98
+ return nals;
99
+ }
100
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Spawn ffmpeg (`gdigrab` → H.264 Annex-B on `pipe:1`) and turn its stdout into access units.
3
+ *
4
+ * Teardown is `proc.kill()` only — never `reader.cancel()`, never `for await (chunk of
5
+ * proc.stdout) { ... break }`. Both of those cancel the underlying stream reader, which
6
+ * segfaults Bun 1.3.x on Windows the instant a consumer goes away (see
7
+ * `transcode-stream.ts:130-148` and `tests/integration/transcode-stream-client-disconnect.test.ts`
8
+ * — that guard targets a `Response` body; the WS rewrite here re-creates the same trap one
9
+ * layer down if the pump loop is written as `for await`). `stop()` only calls `proc.kill()`;
10
+ * the in-flight `reader.read()` then resolves with `done: true` and the pump loop exits on
11
+ * its own — it is never cancelled from outside.
12
+ */
13
+ import { getFfmpegCapabilities } from "../media-transcode/ffmpeg-capabilities.ts";
14
+ import { captureEncoderArgs, CAPTURE_FRAMERATE } from "./remote-desktop-encoder-args.ts";
15
+ import { AccessUnitAssembler, type AccessUnit } from "./access-unit-assembler.ts";
16
+
17
+ export class CaptureUnavailableError extends Error {
18
+ constructor(msg = "ffmpeg is not installed (gdigrab capture requires it)") {
19
+ super(msg);
20
+ this.name = "CaptureUnavailableError";
21
+ }
22
+ }
23
+
24
+ /** Build the gdigrab argv; pure so it can be asserted on without spawning a process.
25
+ * `-fflags nobuffer -flags low_delay` + `-flush_packets 1` stop ffmpeg from holding frames in
26
+ * its demux/mux buffers before emitting — on a fast/LAN transport that buffering is a big slice
27
+ * of the felt lag. `encoder` selects the H.264 encoder args (hardware NVENC/QSV/AMF when the
28
+ * capability probe found one, else libx264). */
29
+ export function buildCaptureArgs(ffmpeg: string, encoder: string = "libx264"): string[] {
30
+ return [
31
+ ffmpeg, "-hide_banner", "-loglevel", "error", "-nostdin",
32
+ "-fflags", "nobuffer", "-flags", "low_delay",
33
+ "-f", "gdigrab", "-framerate", String(CAPTURE_FRAMERATE), "-i", "desktop",
34
+ "-vf", "scale=-2:720",
35
+ ...captureEncoderArgs(encoder),
36
+ "-flush_packets", "1",
37
+ "-f", "h264", "pipe:1",
38
+ ];
39
+ }
40
+
41
+ export interface CaptureHandle {
42
+ /** SPS bytes cached from the stream (for the avc1 codec string), null before the encoder's
43
+ * first keyframe has been parsed. */
44
+ cachedSps(): Uint8Array | null;
45
+ /** Kill ffmpeg. Idempotent, safe to call from multiple teardown paths. */
46
+ stop(): void;
47
+ /** True once `stop()` has run or the process exited on its own. */
48
+ isStopped(): boolean;
49
+ }
50
+
51
+ export interface StartCaptureOptions {
52
+ onAccessUnit: (au: AccessUnit) => void;
53
+ /** Called once the process exits, whether via `stop()` or on its own (crash/killed
54
+ * externally) — lets the session registry clean up without polling. `reason` is a short
55
+ * stderr tail, present only when ffmpeg died on its own (e.g. gdigrab access denied on a
56
+ * disconnected session) so the caller can surface *why* to the client instead of a bare
57
+ * disconnect. */
58
+ onExit?: (code: number | null, reason?: string) => void;
59
+ }
60
+
61
+ /** Start gdigrab capture. Rejects immediately if ffmpeg is not on PATH. */
62
+ export async function startCapture(opts: StartCaptureOptions): Promise<CaptureHandle> {
63
+ const caps = await getFfmpegCapabilities();
64
+ if (!caps.ffmpeg) throw new CaptureUnavailableError();
65
+
66
+ const proc = Bun.spawn(buildCaptureArgs(caps.ffmpeg, caps.encoder ?? "libx264"), {
67
+ stdout: "pipe",
68
+ stderr: "pipe",
69
+ stdin: "ignore",
70
+ });
71
+
72
+ let stopped = false;
73
+ const stop = () => {
74
+ if (stopped) return;
75
+ stopped = true;
76
+ if (!proc.killed) proc.kill();
77
+ };
78
+
79
+ // Drain stderr so ffmpeg never blocks on a full pipe; keep a short tail for diagnostics.
80
+ const stderrTail = new Response(proc.stderr).text().catch(() => "");
81
+ proc.exited.then(async (code) => {
82
+ // Read `stopped` BEFORE overwriting it: this is the only reliable "did WE ask ffmpeg to
83
+ // die" signal. `proc.killed` is NOT reliable for that — Bun sets it `true` even when
84
+ // ffmpeg exits on its own with a nonzero code (verified: gdigrab "access denied" on a
85
+ // disconnected session exits with `killed: true` despite `stop()` never having been
86
+ // called), which previously suppressed this warning — and any client-facing error —
87
+ // for exactly the crash this exists to report.
88
+ const diedOnItsOwn = !stopped;
89
+ stopped = true;
90
+ let reason: string | undefined;
91
+ if (diedOnItsOwn && code !== 0 && code !== null) {
92
+ reason = (await stderrTail).trim().split("\n").slice(-3).join(" | ");
93
+ console.warn(`[remote-desktop] ffmpeg exited ${code}: ${reason}`);
94
+ }
95
+ opts.onExit?.(code, reason);
96
+ });
97
+
98
+ const assembler = new AccessUnitAssembler();
99
+
100
+ // Manual pull loop — the only safe way to drain this stdout (see file header). Runs
101
+ // detached from the caller; it terminates itself once `proc.kill()` resolves the pending
102
+ // read with `done: true`, never via an external cancel.
103
+ const reader = proc.stdout.getReader();
104
+ (async () => {
105
+ for (;;) {
106
+ const { done, value } = await reader.read();
107
+ if (done) return;
108
+ if (!value) continue;
109
+ for (const au of assembler.push(value)) opts.onAccessUnit(au);
110
+ }
111
+ })().catch((e) => {
112
+ console.error(`[remote-desktop] capture pump failed: ${(e as Error).message}`);
113
+ });
114
+
115
+ return {
116
+ cachedSps: () => assembler.cachedSps(),
117
+ stop,
118
+ isStopped: () => stopped,
119
+ };
120
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Capture-tuned libx264 args — deliberately NOT `encoderArgs()` from `ffmpeg-capabilities.ts`.
3
+ * That table is tuned for "watch a file" (nvenc VBR, libx264 `-crf 23 -maxrate 8M -bufsize
4
+ * 16M`), which fights a real-time capture: large bufsize/VBR inflates latency spikes exactly
5
+ * where `-tune zerolatency` is trying to remove them.
6
+ *
7
+ * libx264 is forced (never the hardware picks from `ffmpeg-capabilities.ts`) so profile/level
8
+ * behavior is deterministic across machines — the actual codec string is still derived from
9
+ * the real SPS bytes at runtime (`avc1-codec-string.ts`), this just keeps that derivation
10
+ * predictable during development.
11
+ */
12
+
13
+ /** 30fps + a 1s keyframe interval (30 frames): smoother than 15fps and bounds how long a
14
+ * dropped/late-joining client waits to resync (see WS backpressure handling). */
15
+ export const CAPTURE_FRAMERATE = 30;
16
+ export const CAPTURE_GOP_FRAMES = 30;
17
+
18
+ /** ffmpeg output args for the H.264 encode step (goes after `-i desktop -vf ...`).
19
+ * Prefers hardware NVENC (verified to work even under an RDP session on this class of GPU) —
20
+ * hardware encode adds ~5–15ms vs 30–100ms for libx264, the dominant slice of the felt lag on
21
+ * a fast/LAN transport. Falls back to a low-latency libx264 profile when no hardware H.264
22
+ * encoder is available. The avc1 codec string is derived from the real SPS at runtime, so the
23
+ * encoder's actual profile/level is handled regardless of which branch runs. */
24
+ export function captureEncoderArgs(encoder: string = "libx264"): string[] {
25
+ const common = ["-b:v", "6M", "-maxrate", "6M", "-g", String(CAPTURE_GOP_FRAMES), "-bf", "0", "-pix_fmt", "yuv420p"];
26
+ switch (encoder) {
27
+ case "h264_nvenc":
28
+ // p2 + `-tune ll` (low latency), CBR, no frame reordering, minimal output delay/VBV.
29
+ return ["-c:v", "h264_nvenc", "-preset", "p2", "-tune", "ll", "-rc", "cbr",
30
+ "-zerolatency", "1", "-delay", "0", "-bufsize", "512k", ...common];
31
+ case "h264_qsv":
32
+ return ["-c:v", "h264_qsv", "-preset", "veryfast", "-low_power", "1", "-bufsize", "512k", ...common];
33
+ case "h264_amf":
34
+ return ["-c:v", "h264_amf", "-quality", "speed", "-rc", "cbr", "-bufsize", "512k", ...common];
35
+ default:
36
+ // libx264 low-latency. `-tune zerolatency` enables sliced-threads (multi-slice NALs);
37
+ // access-unit-assembler.ts assumes one slice/frame, so force it back with sliced-threads=0.
38
+ return ["-c:v", "libx264", "-preset", "veryfast", "-tune", "zerolatency",
39
+ "-profile:v", "main", "-level", "4.0", "-x264-params", "sliced-threads=0:slices=1",
40
+ "-bufsize", "1M", ...common];
41
+ }
42
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Feature switch. Remote desktop is ON by default — the viewer itself shows a warning before
3
+ * the first frame is streamed (`remote-desktop-warning-gate.tsx`) and every entry point still
4
+ * sits behind PPM auth. `REMOTE_DESKTOP_ENABLED=0` (or `false`) turns the whole surface off for
5
+ * hosts that never want a live video+input channel reachable, e.g. one exposed on a public
6
+ * tunnel. Every entry point (REST routes, WS upgrade) checks this independently rather than
7
+ * trusting a single call site.
8
+ */
9
+ export function isRemoteDesktopEnabled(): boolean {
10
+ const v = process.env.REMOTE_DESKTOP_ENABLED?.trim().toLowerCase();
11
+ return !(v === "0" || v === "false");
12
+ }