@hienlh/ppm 0.18.2 → 0.18.3

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 (517) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/http-api.md +6 -1
  5. package/dist/web/assets/{ai-resource-editor-B6CODu6u.js → ai-resource-editor-3Zk3gYSZ.js} +1 -1
  6. package/dist/web/assets/{angular-html-C-Kjr43S.js → angular-html-jfMqUuRd.js} +1 -1
  7. package/dist/web/assets/{angular-ts-CaDDbZqm.js → angular-ts-wytn9PMm.js} +1 -1
  8. package/dist/web/assets/api-client-CzABTreM.js +1 -0
  9. package/dist/web/assets/{api-settings-CAhcNpj5.js → api-settings-DfNRPr1q.js} +1 -1
  10. package/dist/web/assets/api-settings-DlcoR5QT.js +1 -0
  11. package/dist/web/assets/{api-themes-CAjg_Bt9.js → api-themes-CEyYK7AK.js} +1 -1
  12. package/dist/web/assets/{apl-B1WsQFAb.js → apl-i-ahKx6O.js} +1 -1
  13. package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +1 -0
  14. package/dist/web/assets/arrow-right-DubGFkN8.js +1 -0
  15. package/dist/web/assets/{astro-Bdnz7nVp.js → astro-DVBCJfG6.js} +1 -1
  16. package/dist/web/assets/audio-preview-DG2Tw0cO.js +1 -0
  17. package/dist/web/assets/{blade-B2euh8sS.js → blade-BHlL8SqU.js} +1 -1
  18. package/dist/web/assets/{bsl-GNeMaxd0.js → bsl-D-UOfR1F.js} +1 -1
  19. package/dist/web/assets/button-B4MWPjWj.js +1 -0
  20. package/dist/web/assets/{cairo-D7G0W7-s.js → cairo-Bn6rucli.js} +1 -1
  21. package/dist/web/assets/chat-tab-DvbbVMBi.js +15 -0
  22. package/dist/web/assets/{cobol-Q6YgysnI.js → cobol-Byn9FYKu.js} +1 -1
  23. package/dist/web/assets/code-editor-BKg7Li-L.js +10 -0
  24. package/dist/web/assets/{coffee-BjufWxVc.js → coffee-DjBl4gm-.js} +1 -1
  25. package/dist/web/assets/{conflict-editor-DnSltcyC.js → conflict-editor-CJNtovCh.js} +3 -3
  26. package/dist/web/assets/{context-menu-q-RHHAWz.js → context-menu-DkQdt6YA.js} +1 -1
  27. package/dist/web/assets/{cpp-CqWU0gCR.js → cpp-CKzic-3E.js} +1 -1
  28. package/dist/web/assets/{crystal-ylayVajF.js → crystal-Bkg-Ly_g.js} +1 -1
  29. package/dist/web/assets/{csv-preview-DnaFdgOB.js → csv-preview-BR6gCSZJ.js} +1 -1
  30. package/dist/web/assets/{data-grid-overlay-editor-BzPfsCfd.js → data-grid-overlay-editor-B1mTl3KS.js} +1 -1
  31. package/dist/web/assets/{database-viewer-C7uLy8Z1.js → database-viewer-DzuwuFSr.js} +1 -1
  32. package/dist/web/assets/diff-viewer-DL3_tISz.js +4 -0
  33. package/dist/web/assets/{docx-preview-Dd2nTW2v.js → docx-preview-BzgyssBV.js} +1 -1
  34. package/dist/web/assets/download-D9UYwY0-.js +1 -0
  35. package/dist/web/assets/{edge-DX4NxQfh.js → edge-DVbZnd3Y.js} +1 -1
  36. package/dist/web/assets/{edit-diff-preview-DfX-9om9.js → edit-diff-preview-HTZPC9VP.js} +1 -1
  37. package/dist/web/assets/{elixir-CSh3NjbV.js → elixir-Dwi8Wu7W.js} +1 -1
  38. package/dist/web/assets/{elm-DBzwFYq9.js → elm-DtIv-HXJ.js} +1 -1
  39. package/dist/web/assets/{erb-yxVOb8aB.js → erb-D3HEDz1x.js} +1 -1
  40. package/dist/web/assets/{erlang-5XIDkYrM.js → erlang-2MjTCzGz.js} +1 -1
  41. package/dist/web/assets/{esm-DO4el2OJ.js → esm-CZ8B8Ba2.js} +1 -1
  42. package/dist/web/assets/explorer-body-CrrY1HbH.js +2 -0
  43. package/dist/web/assets/explorer-window-content-CvQPeiwU.js +1 -0
  44. package/dist/web/assets/{extension-webview-Dk_zyoXZ.js → extension-webview-DH1PfQF2.js} +1 -1
  45. package/dist/web/assets/{fortran-fixed-form-D4tE5VWY.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
  46. package/dist/web/assets/{fsharp-By8soj6-.js → fsharp-Bxbn_gbr.js} +1 -1
  47. package/dist/web/assets/{gdresource-DoSwM7Hv.js → gdresource-sFMY4D2Q.js} +1 -1
  48. package/dist/web/assets/{git-commit-_ZiciHIx.js → git-commit-BzB9Lio5.js} +1 -1
  49. package/dist/web/assets/{git-log-panel-BLQSy6rC.js → git-log-panel-gUm4E3wo.js} +1 -1
  50. package/dist/web/assets/{git-rebase-C3Y5N9-s.js → git-rebase-CKSVd848.js} +1 -1
  51. package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +1 -0
  52. package/dist/web/assets/glide-data-grid-D8J5lwUP.js +138 -0
  53. package/dist/web/assets/{glimmer-js-BPBTXVlC.js → glimmer-js-BY8aExAJ.js} +1 -1
  54. package/dist/web/assets/{glimmer-ts-BQSZdy_D.js → glimmer-ts-Bak54dBX.js} +1 -1
  55. package/dist/web/assets/{glsl-CbQdxpU-.js → glsl-nR2eaJQe.js} +1 -1
  56. package/dist/web/assets/{graphql-GMlKikpY.js → graphql-CGPPFAU6.js} +1 -1
  57. package/dist/web/assets/group-chat-tab-DihiQPSi.js +1 -0
  58. package/dist/web/assets/{hack-DyshcLec.js → hack-CXIMX-Ol.js} +1 -1
  59. package/dist/web/assets/{haml-rqCqWzsq.js → haml-tPY-9Pfm.js} +1 -1
  60. package/dist/web/assets/{handlebars-BCKwSWjR.js → handlebars-BuW1LT7T.js} +1 -1
  61. package/dist/web/assets/{html-Cg29xPR1.js → html-bgVcwZ-t.js} +1 -1
  62. package/dist/web/assets/{html-derivative-geltIVg8.js → html-derivative-D6-IxTgn.js} +1 -1
  63. package/dist/web/assets/{http-pG7Vp237.js → http-CJ1zyB-h.js} +1 -1
  64. package/dist/web/assets/{hurl-Di3Uu_Mc.js → hurl-CmoT7l3T.js} +1 -1
  65. package/dist/web/assets/{hxml-CRy4lbv8.js → hxml-CztOsL8o.js} +1 -1
  66. package/dist/web/assets/{image-preview-KubLSLNO.js → image-preview-DOQBNgwl.js} +1 -1
  67. package/dist/web/assets/index-BtncTUyM.js +52 -0
  68. package/dist/web/assets/index-Bucbpby-.css +2 -0
  69. package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +1 -0
  70. package/dist/web/assets/{jinja-C8GyVnvd.js → jinja-B1pq3jJq.js} +1 -1
  71. package/dist/web/assets/{jison-BywnqPn6.js → jison-D8B5pCwA.js} +1 -1
  72. package/dist/web/assets/{julia-DTrO3T3B.js → julia-D0K3bwpU.js} +1 -1
  73. package/dist/web/assets/{just-CA3KSHPi.js → just-B9Jlddu-.js} +1 -1
  74. package/dist/web/assets/keybindings-store-AynYQVnV.js +1 -0
  75. package/dist/web/assets/{latex-BC5xUMpc.js → latex-BPJSSE6j.js} +1 -1
  76. package/dist/web/assets/{liquid-Chd8tSyQ.js → liquid-lv10fo4U.js} +1 -1
  77. package/dist/web/assets/{lua-DHaEtwLv.js → lua-PNuaIBEF.js} +1 -1
  78. package/dist/web/assets/markdown-renderer-BBlGpszb.js +1 -0
  79. package/dist/web/assets/{markdown-renderer-B4uL6GVA.js → markdown-renderer-CVGub2iS.js} +3 -3
  80. package/dist/web/assets/{marko-Desx_bC-.js → marko-Du3frD6X.js} +1 -1
  81. package/dist/web/assets/{mdc-DahWHc1t.js → mdc-BeDVcFRl.js} +1 -1
  82. package/dist/web/assets/media-url-C5vA0ha9.js +1 -0
  83. package/dist/web/assets/{mobile-bottom-sheet-BQt45q9T.js → mobile-bottom-sheet-CS3XMwFw.js} +1 -1
  84. package/dist/web/assets/mobile-explorer-sheet-CSgc5JOk.js +1 -0
  85. package/dist/web/assets/{nextflow-3A7lutCA.js → nextflow-DkcXP71s.js} +1 -1
  86. package/dist/web/assets/{nginx-XQda6Hdy.js → nginx-C5n8N_bt.js} +1 -1
  87. package/dist/web/assets/{nim-DHqlpwP1.js → nim-CIhyfs-A.js} +1 -1
  88. package/dist/web/assets/notification-store-1ksB0YwR.js +1 -0
  89. package/dist/web/assets/{number-overlay-editor-DgZtViPO.js → number-overlay-editor-NgOsWJ7x.js} +1 -1
  90. package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +1 -0
  91. package/dist/web/assets/{panel-store-s6Qx49RK.js → panel-store-DCD82zc1.js} +1 -1
  92. package/dist/web/assets/{pdf-preview-xZQVUtaV.js → pdf-preview-DSAkWbxY.js} +1 -1
  93. package/dist/web/assets/{perl-M-ncjACb.js → perl-Cf31sZYE.js} +1 -1
  94. package/dist/web/assets/{php-CKIajrZi.js → php-Dzl_MDIW.js} +1 -1
  95. package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +1 -0
  96. package/dist/web/assets/plus-DmjC2iRB.js +1 -0
  97. package/dist/web/assets/{postgres-viewer-Ds0FxBel.js → postgres-viewer-DOiM_5Nv.js} +3 -3
  98. package/dist/web/assets/{project-store-DyaEpHVG.js → project-store-B8BkHXy5.js} +1 -1
  99. package/dist/web/assets/{pug-DO0u_gO4.js → pug-CxSYHrB3.js} +1 -1
  100. package/dist/web/assets/{qml-UEje1PAA.js → qml-DlEjGhwN.js} +1 -1
  101. package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +1 -0
  102. package/dist/web/assets/{razor-lCs5T1Ib.js → razor-KVt5uW14.js} +1 -1
  103. package/dist/web/assets/{rst-CZX_O0z4.js → rst-D5BwNTnw.js} +1 -1
  104. package/dist/web/assets/{ruby-idycVAyE.js → ruby-D5fejQwd.js} +1 -1
  105. package/dist/web/assets/run-in-terminal-B8TDtaM_.js +1 -0
  106. package/dist/web/assets/{sas-o5gTfUwp.js → sas-Bj7B1CB2.js} +1 -1
  107. package/dist/web/assets/{scss-CRTk3iBQ.js → scss-BB0CUoCL.js} +1 -1
  108. package/dist/web/assets/send-to-chat-CuBAVC8l.js +1 -0
  109. package/dist/web/assets/{settings-store-ChqHdT2V.js → settings-store-CY-4_U3H.js} +2 -2
  110. package/dist/web/assets/settings-tab-D_oruJB9.js +1 -0
  111. package/dist/web/assets/{shaderlab-3e5ln8ci.js → shaderlab-BdjEZUMc.js} +1 -1
  112. package/dist/web/assets/{shellsession-CGQjQ75I.js → shellsession-Bp1OA1rR.js} +1 -1
  113. package/dist/web/assets/{soy-CxY0VwX2.js → soy-DlYz3wsC.js} +1 -1
  114. package/dist/web/assets/{sparql-CBAGAFuq.js → sparql-CdfII5Jd.js} +1 -1
  115. package/dist/web/assets/sql-query-editor-PWpuw-O5.js +1 -0
  116. package/dist/web/assets/{sqlite-viewer-CZYDknNa.js → sqlite-viewer-1S6cwVWa.js} +1 -1
  117. package/dist/web/assets/{stata-BoezhOvk.js → stata-BvUA6GUW.js} +1 -1
  118. package/dist/web/assets/{surrealql-CHEzAhu7.js → surrealql--DLNbTyI.js} +1 -1
  119. package/dist/web/assets/{svelte-y6xtu2zX.js → svelte-CBfSsG4n.js} +1 -1
  120. package/dist/web/assets/system-monitor-tab-U0Jh9-2v.js +1 -0
  121. package/dist/web/assets/{tab-store-C42sktPo.js → tab-store-DAsvpx4t.js} +1 -1
  122. package/dist/web/assets/team-member-window-content-DSZL4GIj.js +1 -0
  123. package/dist/web/assets/{templ-DbG2y5yD.js → templ-Dv8zpKZW.js} +1 -1
  124. package/dist/web/assets/terminal-tab-uVhX8AGG.js +3 -0
  125. package/dist/web/assets/{tex-CeKQhrQG.js → tex-CsrOiqh1.js} +1 -1
  126. package/dist/web/assets/tool-cards-QuTCNfYR.js +8 -0
  127. package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +1 -0
  128. package/dist/web/assets/{ts-tags-iKKOXJjG.js → ts-tags-3T98k4EJ.js} +1 -1
  129. package/dist/web/assets/{twig-DK8Vrp-G.js → twig-u6QZ45FH.js} +1 -1
  130. package/dist/web/assets/{use-blob-url-DYtOHK7u.js → use-blob-url-BmlYYZ8L.js} +1 -1
  131. package/dist/web/assets/use-is-mobile-B8FA4kQf.js +1 -0
  132. package/dist/web/assets/use-monaco-theme-DvmINdhI.js +1 -0
  133. package/dist/web/assets/use-websocket-DoZdQS0G.js +1 -0
  134. package/dist/web/assets/{vendor-mermaid-C977xNeE.js → vendor-mermaid-Bg4vE8QA.js} +1 -1
  135. package/dist/web/assets/video-preview-CzU09X0C.js +1 -0
  136. package/dist/web/assets/{vue-JkIEObMV.js → vue-CxIsxtwn.js} +1 -1
  137. package/dist/web/assets/{vue-html-D8SqnU86.js → vue-html-BGL5qnuH.js} +1 -1
  138. package/dist/web/assets/{vue-vine-DJj7N4WD.js → vue-vine-DHPh_gMr.js} +1 -1
  139. package/dist/web/assets/{xml-C-00w9Rv.js → xml-C-vM2m7J.js} +1 -1
  140. package/dist/web/assets/{xsl-CVdCwPHV.js → xsl-DvTXNwuO.js} +1 -1
  141. package/dist/web/index.html +21 -20
  142. package/dist/web/sw.js +1 -1
  143. package/docs/codebase-summary.md +1 -0
  144. package/package.json +1 -1
  145. package/scripts/dev-quick-tunnel-retry.mjs +51 -0
  146. package/src/providers/claude-agent-sdk.ts +6 -0
  147. package/src/server/helpers/media-route-handlers.ts +66 -0
  148. package/src/server/helpers/range-file-response.ts +86 -0
  149. package/src/server/index.ts +24 -17
  150. package/src/server/middleware/auth.ts +3 -2
  151. package/src/server/routes/files.ts +40 -7
  152. package/src/server/routes/fs-browse.ts +43 -5
  153. package/src/server/routes/fs-upload.ts +32 -0
  154. package/src/server/routes/teams.ts +34 -0
  155. package/src/server/ws/chat.ts +64 -16
  156. package/src/server/ws/team-inbox-watcher.ts +61 -22
  157. package/src/server/ws/terminal.ts +24 -6
  158. package/src/services/fs-ops/fs-ops-upload.service.ts +120 -0
  159. package/src/services/media-transcode/ffmpeg-capabilities.ts +89 -0
  160. package/src/services/media-transcode/media-probe.ts +63 -0
  161. package/src/services/media-transcode/transcode-stream.ts +129 -0
  162. package/src/services/subagent-transcript-merger.ts +1 -1
  163. package/src/services/team-member-activity/member-activity.service.ts +117 -0
  164. package/src/services/team-member-activity/outbound-message-scanner.ts +121 -0
  165. package/src/services/team-member-activity/subagent-transcript-index.ts +95 -0
  166. package/src/services/team-member-activity/transcript-tail-summary.ts +125 -0
  167. package/src/shared/background-agent-status.ts +79 -0
  168. package/src/types/api.ts +1 -0
  169. package/src/types/chat.ts +6 -1
  170. package/src/web/components/chat/chat-history-bar.tsx +4 -2
  171. package/src/web/components/chat/chat-tab.tsx +1 -0
  172. package/src/web/components/chat/message-input.tsx +13 -6
  173. package/src/web/components/chat/mini-markdown.tsx +17 -0
  174. package/src/web/components/chat/send-message-card.tsx +134 -0
  175. package/src/web/components/chat/send-message-parse.ts +159 -0
  176. package/src/web/components/chat/team-activity-panel.tsx +107 -121
  177. package/src/web/components/chat/team-member-activity-row.tsx +92 -0
  178. package/src/web/components/chat/team-member-list.tsx +43 -0
  179. package/src/web/components/chat/team-member-window-content.tsx +97 -0
  180. package/src/web/components/chat/team-message-badges.ts +18 -0
  181. package/src/web/components/chat/team-message-list.tsx +88 -0
  182. package/src/web/components/chat/team-message-preview.ts +87 -0
  183. package/src/web/components/chat/tool-cards.tsx +44 -18
  184. package/src/web/components/editor/audio-preview.tsx +6 -7
  185. package/src/web/components/editor/video-player/use-video-keyboard-shortcuts.ts +62 -0
  186. package/src/web/components/editor/video-player/video-player-controls.tsx +103 -0
  187. package/src/web/components/editor/video-player/video-player.tsx +190 -0
  188. package/src/web/components/editor/video-player/video-settings-menu.tsx +92 -0
  189. package/src/web/components/editor/video-player/video-transform.ts +49 -0
  190. package/src/web/components/editor/video-preview.tsx +75 -14
  191. package/src/web/components/explorer/file-tree.tsx +6 -0
  192. package/src/web/components/explorer/tree-node-context-menu.tsx +5 -1
  193. package/src/web/components/floating-window/window-content-registry.ts +5 -0
  194. package/src/web/components/floating-window/window-store-types.ts +1 -1
  195. package/src/web/components/os-explorer/actions/explorer-actions-upload.ts +96 -0
  196. package/src/web/components/os-explorer/actions/use-explorer-actions.ts +23 -2
  197. package/src/web/components/os-explorer/actions/use-explorer-upload-actions.tsx +41 -0
  198. package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +10 -1
  199. package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +8 -1
  200. package/src/web/components/os-explorer/dnd/use-external-file-drop.ts +84 -0
  201. package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +46 -3
  202. package/src/web/components/os-explorer/explorer-body.tsx +1 -1
  203. package/src/web/components/os-explorer/explorer-context-menu.tsx +10 -0
  204. package/src/web/components/os-explorer/explorer-dialogs.tsx +2 -0
  205. package/src/web/components/os-explorer/explorer-sidebar.tsx +10 -5
  206. package/src/web/components/os-explorer/explorer-toolbar.tsx +32 -2
  207. package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +5 -1
  208. package/src/web/components/os-explorer/upload/collect-dropped-entries.ts +81 -0
  209. package/src/web/components/os-explorer/upload/upload-queue.ts +79 -0
  210. package/src/web/components/os-explorer/upload/use-upload-picker.tsx +74 -0
  211. package/src/web/components/os-explorer/views/column-row.tsx +3 -1
  212. package/src/web/components/os-explorer/views/column-view-column.tsx +6 -1
  213. package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
  214. package/src/web/components/os-explorer/views/icons-view.tsx +5 -1
  215. package/src/web/components/os-explorer/views/list-row.tsx +1 -1
  216. package/src/web/components/os-explorer/views/list-view.tsx +5 -1
  217. package/src/web/components/shared/markdown-code-block.tsx +3 -7
  218. package/src/web/components/terminal/terminal-tab.tsx +19 -25
  219. package/src/web/hooks/use-chat.ts +77 -15
  220. package/src/web/hooks/use-team-activity-feed.ts +92 -0
  221. package/src/web/hooks/use-terminal-command-queue.ts +108 -0
  222. package/src/web/hooks/use-terminal.ts +45 -3
  223. package/src/web/lib/fs-api.ts +9 -0
  224. package/src/web/lib/fs-upload-xhr.ts +62 -0
  225. package/src/web/lib/media-url.ts +51 -0
  226. package/src/web/lib/open-terminal-at.ts +22 -0
  227. package/src/web/lib/run-in-terminal.ts +55 -0
  228. package/src/web/lib/send-to-chat.ts +118 -0
  229. package/src/web/lib/ws-auth.ts +13 -0
  230. package/src/web/lib/ws-client.ts +10 -5
  231. package/.release-run.sh +0 -5
  232. package/dist/web/assets/api-client-CaRE4HYh.js +0 -1
  233. package/dist/web/assets/api-settings-DBcXfeJ8.js +0 -1
  234. package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +0 -1
  235. package/dist/web/assets/audio-preview-BkNAEqcB.js +0 -1
  236. package/dist/web/assets/chat-tab-B-MEsHYp.js +0 -13
  237. package/dist/web/assets/code-editor-bC1KAEUA.js +0 -10
  238. package/dist/web/assets/dialog-CkI7rwrh.js +0 -1
  239. package/dist/web/assets/diff-viewer-BwElk4q9.js +0 -4
  240. package/dist/web/assets/explorer-body-DzdXBlWw.js +0 -2
  241. package/dist/web/assets/explorer-window-content-Dgl1WTNs.js +0 -1
  242. package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +0 -1
  243. package/dist/web/assets/glide-data-grid-QZ_fgjIa.js +0 -138
  244. package/dist/web/assets/group-chat-tab-BbdVlKFv.js +0 -1
  245. package/dist/web/assets/index-D-xwjNyX.css +0 -2
  246. package/dist/web/assets/index-wd3FSHHn.js +0 -52
  247. package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +0 -1
  248. package/dist/web/assets/keybindings-store-Bp_c_C3z.js +0 -1
  249. package/dist/web/assets/markdown-renderer-2asl-3Kk.js +0 -1
  250. package/dist/web/assets/mobile-explorer-sheet-LWUDaNBV.js +0 -1
  251. package/dist/web/assets/notification-store-BasUnpt7.js +0 -1
  252. package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +0 -1
  253. package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +0 -1
  254. package/dist/web/assets/plus-9LatwJMx.js +0 -1
  255. package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +0 -1
  256. package/dist/web/assets/settings-tab-vhatQ51-.js +0 -1
  257. package/dist/web/assets/sql-query-editor-BblB2Imt.js +0 -1
  258. package/dist/web/assets/system-monitor-tab-D8dy-RT_.js +0 -1
  259. package/dist/web/assets/terminal-tab-CfD22q5x.js +0 -3
  260. package/dist/web/assets/tool-cards-DNgw1yI6.js +0 -7
  261. package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +0 -1
  262. package/dist/web/assets/use-is-mobile-DnFSkUG2.js +0 -1
  263. package/dist/web/assets/use-monaco-theme-DEbn-QT3.js +0 -1
  264. package/dist/web/assets/video-preview-DcUaFYyv.js +0 -1
  265. package/src/web/components/chat/team-activity-popover.tsx +0 -202
  266. /package/dist/web/assets/{abap-DrMyiWmK.js → abap-C1K0-wzc.js} +0 -0
  267. /package/dist/web/assets/{actionscript-3-BlSOyi0y.js → actionscript-3-CJhQVjLA.js} +0 -0
  268. /package/dist/web/assets/{ada-B_m96icB.js → ada-Bt8r9fME.js} +0 -0
  269. /package/dist/web/assets/{andromeeda-Ch3vOiZB.js → andromeeda-DF1I8qi3.js} +0 -0
  270. /package/dist/web/assets/{apache-CdsaXc1n.js → apache-CnlleE6P.js} +0 -0
  271. /package/dist/web/assets/{apex-0AnsBevk.js → apex-j0C6Xmk6.js} +0 -0
  272. /package/dist/web/assets/{api-client-rTAGdEKo.js → api-client-BSB14J4w.js} +0 -0
  273. /package/dist/web/assets/{applescript-CSxLolzy.js → applescript-DO7AnO8U.js} +0 -0
  274. /package/dist/web/assets/{apply-theme-CMQXnUAa.js → apply-theme-CNj1iA-v.js} +0 -0
  275. /package/dist/web/assets/{ara-DXZS0jA5.js → ara-i83AydZj.js} +0 -0
  276. /package/dist/web/assets/{asciidoc-DDy3pH2A.js → asciidoc-DbBA6-sF.js} +0 -0
  277. /package/dist/web/assets/{asm-Bd17hhqg.js → asm-B5S47Rgr.js} +0 -0
  278. /package/dist/web/assets/{aurora-x-C62HJRs-.js → aurora-x-De_IaSKw.js} +0 -0
  279. /package/dist/web/assets/{awk-Cc-nazni.js → awk-CfyLHXqS.js} +0 -0
  280. /package/dist/web/assets/{ayu-dark-BsfmNWCU.js → ayu-dark-DTLdZ1Si.js} +0 -0
  281. /package/dist/web/assets/{ayu-light-BZ4HLa4X.js → ayu-light-DTOfkzhu.js} +0 -0
  282. /package/dist/web/assets/{ayu-mirage-Bhyjyiqr.js → ayu-mirage-DsAECCMv.js} +0 -0
  283. /package/dist/web/assets/{ballerina-QiWgir3v.js → ballerina-CumGp9A7.js} +0 -0
  284. /package/dist/web/assets/{bat-DxtlkwN3.js → bat-Ca2SBbYN.js} +0 -0
  285. /package/dist/web/assets/{beancount-Bt21X7M_.js → beancount-CqyXBwwF.js} +0 -0
  286. /package/dist/web/assets/{berry-Cksre7lO.js → berry-yl-kJr_H.js} +0 -0
  287. /package/dist/web/assets/{bibtex-DkakgDlR.js → bibtex-DoFFszjV.js} +0 -0
  288. /package/dist/web/assets/{bicep-8crmcyXU.js → bicep-CWPi4nfK.js} +0 -0
  289. /package/dist/web/assets/{bird2-CewQ2I_Z.js → bird2-DXew-ut0.js} +0 -0
  290. /package/dist/web/assets/{bot-nk1k3ELu.js → bot-CunPuEdW.js} +0 -0
  291. /package/dist/web/assets/{c-C_6zE8Ha.js → c-clbySDvd.js} +0 -0
  292. /package/dist/web/assets/{c3-DLWB9-dp.js → c3-B65jvSM1.js} +0 -0
  293. /package/dist/web/assets/{cadence-D8qYS6pR.js → cadence-Cm8tQTHN.js} +0 -0
  294. /package/dist/web/assets/{can-open-in-ppm-BzVXoV-d.js → can-open-in-ppm-Cu-XYIgu.js} +0 -0
  295. /package/dist/web/assets/{catppuccin-frappe-Bh95lNRy.js → catppuccin-frappe-DLeGslh_.js} +0 -0
  296. /package/dist/web/assets/{catppuccin-latte-C1PlN3G0.js → catppuccin-latte-CR5GeHws.js} +0 -0
  297. /package/dist/web/assets/{catppuccin-macchiato-CldtgdK4.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
  298. /package/dist/web/assets/{catppuccin-mocha-BiwqxX9A.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
  299. /package/dist/web/assets/{clarity-CGiU22jY.js → clarity-DPulbvKH.js} +0 -0
  300. /package/dist/web/assets/{clojure-COIC3doh.js → clojure-DFuWS20u.js} +0 -0
  301. /package/dist/web/assets/{cmake-DWUEQfHv.js → cmake-DejCqcu4.js} +0 -0
  302. /package/dist/web/assets/{codeowners-BNHiAQea.js → codeowners-BwAyqWIH.js} +0 -0
  303. /package/dist/web/assets/{codeql-BuKaoS0j.js → codeql-DgSwFjqI.js} +0 -0
  304. /package/dist/web/assets/{common-lisp-D5IKbxsd.js → common-lisp-CxIVJCtf.js} +0 -0
  305. /package/dist/web/assets/{coq-BMVS-p94.js → coq-DLSjSnmf.js} +0 -0
  306. /package/dist/web/assets/{csharp-DMlbXbM4.js → csharp-C1HHzXBp.js} +0 -0
  307. /package/dist/web/assets/{css-CjkJC89X.js → css-VXsMY--y.js} +0 -0
  308. /package/dist/web/assets/{csv-DoNVBBf0.js → csv-BEbcJdHz.js} +0 -0
  309. /package/dist/web/assets/{csv-parser-DHvoTjMF.js → csv-parser-r-Nk1oHT.js} +0 -0
  310. /package/dist/web/assets/{cue-DoTRbBg-.js → cue-BkDnFqUD.js} +0 -0
  311. /package/dist/web/assets/{cypher-DVsNdTT3.js → cypher-1cMqojSQ.js} +0 -0
  312. /package/dist/web/assets/{d-EX7BBSrv.js → d-BnYjRXtH.js} +0 -0
  313. /package/dist/web/assets/{dark-plus-P_YTHwlh.js → dark-plus-XfbaH7kZ.js} +0 -0
  314. /package/dist/web/assets/{dart-Do-rgU4S.js → dart-Ca7s-8hK.js} +0 -0
  315. /package/dist/web/assets/{data-grid-types-CUx8cusN.js → data-grid-types-BXwlFMzs.js} +0 -0
  316. /package/dist/web/assets/{dax-DxOieacu.js → dax-CmuOw-mY.js} +0 -0
  317. /package/dist/web/assets/{desktop-CvdV7SBf.js → desktop-C2gJ6Ovb.js} +0 -0
  318. /package/dist/web/assets/{diff-CNOetGen.js → diff-BCEo1Ej7.js} +0 -0
  319. /package/dist/web/assets/{dist-CMfLpPW_.js → dist-CUPbqNnL.js} +0 -0
  320. /package/dist/web/assets/{dist-DduBIVkD.js → dist-DOlBnxFX.js} +0 -0
  321. /package/dist/web/assets/{docker-DAlG4_q-.js → docker-DN2jlyAh.js} +0 -0
  322. /package/dist/web/assets/{dotenv-Dh3qf6yx.js → dotenv-NExMY2kS.js} +0 -0
  323. /package/dist/web/assets/{dracula-baAGmJYG.js → dracula-B9lbE01y.js} +0 -0
  324. /package/dist/web/assets/{dracula-soft-l7ZIyums.js → dracula-soft-CMBBfKF_.js} +0 -0
  325. /package/dist/web/assets/{dream-maker-DeRjdBn8.js → dream-maker-DEyDwFsd.js} +0 -0
  326. /package/dist/web/assets/{emacs-lisp-CB05-jQA.js → emacs-lisp-D_qP2zS6.js} +0 -0
  327. /package/dist/web/assets/{everforest-dark-CT-llQBT.js → everforest-dark-DbHa2U-p.js} +0 -0
  328. /package/dist/web/assets/{everforest-light-ByPJkTT7.js → everforest-light-BISkJchP.js} +0 -0
  329. /package/dist/web/assets/{fennel-CeMarbvn.js → fennel-6B22SfJR.js} +0 -0
  330. /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-a8f3_w7a.js} +0 -0
  331. /package/dist/web/assets/{fish-BYrt6Xtb.js → fish-CubIIvJD.js} +0 -0
  332. /package/dist/web/assets/{fluent-Cr5VcAwh.js → fluent-DF55zQ09.js} +0 -0
  333. /package/dist/web/assets/{fortran-free-form-DQmKtrLa.js → fortran-free-form-DvOu8jAt.js} +0 -0
  334. /package/dist/web/assets/{gdscript-QTDKjzhY.js → gdscript-DCPxdLpq.js} +0 -0
  335. /package/dist/web/assets/{gdshader-Chr-5UYt.js → gdshader-CTZ381FO.js} +0 -0
  336. /package/dist/web/assets/{genie-kWHMWnt0.js → genie-C_QDtvgU.js} +0 -0
  337. /package/dist/web/assets/{gherkin-DtEBQFUo.js → gherkin-BcvK42tF.js} +0 -0
  338. /package/dist/web/assets/{github-dark-B5aiUta2.js → github-dark-C9afs_4w.js} +0 -0
  339. /package/dist/web/assets/{github-dark-default-BkxtsTqR.js → github-dark-default-CKGVrl3Z.js} +0 -0
  340. /package/dist/web/assets/{github-dark-dimmed-B9QjR_0K.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
  341. /package/dist/web/assets/{github-dark-high-contrast-D6bO-5c0.js → github-dark-high-contrast-DW78QukY.js} +0 -0
  342. /package/dist/web/assets/{github-light-Cg2ZW_CJ.js → github-light-BnP2dznS.js} +0 -0
  343. /package/dist/web/assets/{github-light-default-eD7qUVlQ.js → github-light-default-C2fPZzPG.js} +0 -0
  344. /package/dist/web/assets/{github-light-high-contrast-CFnlSVjd.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
  345. /package/dist/web/assets/{gleam-BpYoOqIT.js → gleam-BpeKwEIO.js} +0 -0
  346. /package/dist/web/assets/{gn-BxwtZPau.js → gn-nUb_Mit0.js} +0 -0
  347. /package/dist/web/assets/{gnuplot-CRyiZDfl.js → gnuplot-D7I0v20p.js} +0 -0
  348. /package/dist/web/assets/{go-Dw1Qx94W.js → go-D7Qqska7.js} +0 -0
  349. /package/dist/web/assets/{groovy-zq_SbWk-.js → groovy-DS-pne5-.js} +0 -0
  350. /package/dist/web/assets/{gruvbox-dark-hard-DbbNy8-Q.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
  351. /package/dist/web/assets/{gruvbox-dark-medium-BQZ7Kj6C.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
  352. /package/dist/web/assets/{gruvbox-dark-soft-vNavErXi.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
  353. /package/dist/web/assets/{gruvbox-light-hard-AOFbCd7i.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-light-medium-DqtxND6H.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-light-soft-Cg0basu6.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
  356. /package/dist/web/assets/{haskell-CcsmcOez.js → haskell-uQcywkDH.js} +0 -0
  357. /package/dist/web/assets/{haxe-BimYY9X4.js → haxe-c6bI4_GT.js} +0 -0
  358. /package/dist/web/assets/{hcl-CTrXCK40.js → hcl-CQXTiEXq.js} +0 -0
  359. /package/dist/web/assets/{hjson-CmqqU6Qg.js → hjson-ChETf2bz.js} +0 -0
  360. /package/dist/web/assets/{hlsl-CuJtX_uf.js → hlsl-BMC4JGoE.js} +0 -0
  361. /package/dist/web/assets/{horizon-BGeSjDYJ.js → horizon-BusFlhLW.js} +0 -0
  362. /package/dist/web/assets/{horizon-bright-GYMgd3R0.js → horizon-bright-2oD0Mz7W.js} +0 -0
  363. /package/dist/web/assets/{houston--JD_5r2q.js → houston-DdbbjqFg.js} +0 -0
  364. /package/dist/web/assets/{hy-C9ILOiqk.js → hy-D_SOuqw9.js} +0 -0
  365. /package/dist/web/assets/{image-gallery-BWFpZAAP.js → image-gallery-CrM5fHKy.js} +0 -0
  366. /package/dist/web/assets/{imba-B6F3yus5.js → imba-BxLOHO5I.js} +0 -0
  367. /package/dist/web/assets/{ini-BfkQswMZ.js → ini-ox-CFcg0.js} +0 -0
  368. /package/dist/web/assets/{java-DEH5avQ1.js → java-Bzo8yIal.js} +0 -0
  369. /package/dist/web/assets/{javascript-Dr4yHuV2.js → javascript-DHuz9VXn.js} +0 -0
  370. /package/dist/web/assets/{json-BuR35ewf.js → json-0_y80FD5.js} +0 -0
  371. /package/dist/web/assets/{json5-Ny9ZiJAC.js → json5-CEQ50pGM.js} +0 -0
  372. /package/dist/web/assets/{jsonc-zYVuoZkR.js → jsonc-CfrLEAKA.js} +0 -0
  373. /package/dist/web/assets/{jsonl-B9xthb_H.js → jsonl-fKbrANC2.js} +0 -0
  374. /package/dist/web/assets/{jsonnet-CCoLJbMP.js → jsonnet-DKbkPaQ2.js} +0 -0
  375. /package/dist/web/assets/{jssm-W0GDlQXi.js → jssm-Dxxx1Hqu.js} +0 -0
  376. /package/dist/web/assets/{jsx-BrLJpWC-.js → jsx-DlJL-Oj3.js} +0 -0
  377. /package/dist/web/assets/{kanagawa-dragon-CuVwt_MO.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
  378. /package/dist/web/assets/{kanagawa-lotus-B3W_617P.js → kanagawa-lotus-BHltayiE.js} +0 -0
  379. /package/dist/web/assets/{kanagawa-wave-CEx4MP5W.js → kanagawa-wave-D-KXPjw6.js} +0 -0
  380. /package/dist/web/assets/{kdl-DilRejnB.js → kdl-DvtoJdFo.js} +0 -0
  381. /package/dist/web/assets/{kotlin-Bq5JtKaa.js → kotlin-CTS4MLcx.js} +0 -0
  382. /package/dist/web/assets/{kusto-Bhp2KYog.js → kusto-HtSE_sKm.js} +0 -0
  383. /package/dist/web/assets/{laserwave-WA_7c5hu.js → laserwave-DaXZG9ge.js} +0 -0
  384. /package/dist/web/assets/{lean-AEGcXwS-.js → lean-BYtGUfWx.js} +0 -0
  385. /package/dist/web/assets/{less-NPqH-26u.js → less-BoxmU_Mk.js} +0 -0
  386. /package/dist/web/assets/{lib-BFetOULu.js → lib-BzxL1ARZ.js} +0 -0
  387. /package/dist/web/assets/{libesm-tR4XlhMj.js → libesm-ah-mr-sh.js} +0 -0
  388. /package/dist/web/assets/{light-plus-B4xW7cgY.js → light-plus-Bt5CyUuo.js} +0 -0
  389. /package/dist/web/assets/{llvm-CPyWNcDE.js → llvm-CSdk9q9V.js} +0 -0
  390. /package/dist/web/assets/{log-CeQmiQSz.js → log-B-kp_3CL.js} +0 -0
  391. /package/dist/web/assets/{logo-BO0VrjBm.js → logo-DdIsXFDO.js} +0 -0
  392. /package/dist/web/assets/{luau-B6GT9kA1.js → luau-DUBpLlzQ.js} +0 -0
  393. /package/dist/web/assets/{make-CwfmN7e_.js → make-B3JVjoW_.js} +0 -0
  394. /package/dist/web/assets/{markdown-Bbe4FlLp.js → markdown-Seon0vHs.js} +0 -0
  395. /package/dist/web/assets/{material-theme-iD9XbfsB.js → material-theme-Nq-ChX1P.js} +0 -0
  396. /package/dist/web/assets/{material-theme-darker-u7M3XGe7.js → material-theme-darker-CPV4ayXJ.js} +0 -0
  397. /package/dist/web/assets/{material-theme-lighter-BGdI48zn.js → material-theme-lighter-0kyjYqEd.js} +0 -0
  398. /package/dist/web/assets/{material-theme-ocean-_mk62F5q.js → material-theme-ocean-DGpAp5co.js} +0 -0
  399. /package/dist/web/assets/{material-theme-palenight-CxFbDyAu.js → material-theme-palenight-B0AUn0NG.js} +0 -0
  400. /package/dist/web/assets/{matlab-Z0RUE3V_.js → matlab-C2waytxs.js} +0 -0
  401. /package/dist/web/assets/{mdx-BZ5wPYzV.js → mdx-DAis6R6I.js} +0 -0
  402. /package/dist/web/assets/{mermaid-CY1_aP-h.js → mermaid-_rnT2YGD.js} +0 -0
  403. /package/dist/web/assets/{min-dark-DKWtxOre.js → min-dark-BSSjchUB.js} +0 -0
  404. /package/dist/web/assets/{min-light-e1iwHDfP.js → min-light-DomAFQsa.js} +0 -0
  405. /package/dist/web/assets/{mipsasm-BiDfQ4ir.js → mipsasm-bBgtvPyG.js} +0 -0
  406. /package/dist/web/assets/{mojo-DteFEHsj.js → mojo-Cp9BayW0.js} +0 -0
  407. /package/dist/web/assets/{monokai-BgPbE9xZ.js → monokai-CL_9gbmn.js} +0 -0
  408. /package/dist/web/assets/{moonbit-mEbRp-mA.js → moonbit-BS_fcr-P.js} +0 -0
  409. /package/dist/web/assets/{move-CogqZw7A.js → move-C79AH45U.js} +0 -0
  410. /package/dist/web/assets/{narrat-BD2qjmy1.js → narrat-CJ5s3BDf.js} +0 -0
  411. /package/dist/web/assets/{nextflow-groovy-BNX8GOD_.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
  412. /package/dist/web/assets/{night-owl-ed3GbziD.js → night-owl-Jkf9zFQK.js} +0 -0
  413. /package/dist/web/assets/{night-owl-light-B0j7O0Wn.js → night-owl-light-DRPeaG5J.js} +0 -0
  414. /package/dist/web/assets/{nix-DFDbcQQm.js → nix-D2Jet9DN.js} +0 -0
  415. /package/dist/web/assets/{nord-D93TxEQi.js → nord-CREVs22j.js} +0 -0
  416. /package/dist/web/assets/{nushell-3_H_XKja.js → nushell-CAiDi_mR.js} +0 -0
  417. /package/dist/web/assets/{objective-c-BM3u6nlm.js → objective-c-CVkmJkC1.js} +0 -0
  418. /package/dist/web/assets/{objective-cpp-DZ73h42g.js → objective-cpp-DIKDO1Ly.js} +0 -0
  419. /package/dist/web/assets/{ocaml-B1PU5gmh.js → ocaml-R9qtGQhs.js} +0 -0
  420. /package/dist/web/assets/{odin-D14L6aRL.js → odin-DTYNQqVr.js} +0 -0
  421. /package/dist/web/assets/{one-dark-pro-Bj-94Z8v.js → one-dark-pro-BSLWUJn5.js} +0 -0
  422. /package/dist/web/assets/{one-light-DTdGWyOZ.js → one-light-DbkRXG3t.js} +0 -0
  423. /package/dist/web/assets/{openscad-Ciq554Sj.js → openscad-BlPcSnmT.js} +0 -0
  424. /package/dist/web/assets/{pascal-Bfd4qdGi.js → pascal-B1Y46ZZB.js} +0 -0
  425. /package/dist/web/assets/{pkl-CTurDUfj.js → pkl-BhJSJbFv.js} +0 -0
  426. /package/dist/web/assets/{plastic-BLQfZXQp.js → plastic-BzxsCTpi.js} +0 -0
  427. /package/dist/web/assets/{plsql-DyYfP9kQ.js → plsql-DBba7dyP.js} +0 -0
  428. /package/dist/web/assets/{po-CgXdT8vQ.js → po-BvgLlnNo.js} +0 -0
  429. /package/dist/web/assets/{poimandres-DOg5fK6s.js → poimandres-DUaI_1JP.js} +0 -0
  430. /package/dist/web/assets/{polar-Dv4aoL6X.js → polar-C7uILwJn.js} +0 -0
  431. /package/dist/web/assets/{postcss-DxugHnJD.js → postcss-BgJlpFdw.js} +0 -0
  432. /package/dist/web/assets/{powerquery-CR3kzRyX.js → powerquery-Dn1MIpZu.js} +0 -0
  433. /package/dist/web/assets/{powershell-BE91xJQB.js → powershell-Bo-DYzOS.js} +0 -0
  434. /package/dist/web/assets/{prisma-B_3yUlRQ.js → prisma-CTLs5IdT.js} +0 -0
  435. /package/dist/web/assets/{prolog-DcT781Ht.js → prolog-QRGun3eG.js} +0 -0
  436. /package/dist/web/assets/{proto-CSf-w5k2.js → proto-B7PI7apn.js} +0 -0
  437. /package/dist/web/assets/{puppet-CLGO6QWl.js → puppet-CBq0tLrP.js} +0 -0
  438. /package/dist/web/assets/{purescript-Bah2McKd.js → purescript-DFewOQb2.js} +0 -0
  439. /package/dist/web/assets/{python-DUH8qy7N.js → python-iWuxKXvT.js} +0 -0
  440. /package/dist/web/assets/{qmldir-YIsHnjKq.js → qmldir-cXiW3AsF.js} +0 -0
  441. /package/dist/web/assets/{qss-CLmoVNA4.js → qss-DASwAbBL.js} +0 -0
  442. /package/dist/web/assets/{r-Pon-b-vz.js → r-TU2eaIPw.js} +0 -0
  443. /package/dist/web/assets/{racket-BA09P5Kg.js → racket-SkRKqrLY.js} +0 -0
  444. /package/dist/web/assets/{raku-DlpzU7Fe.js → raku-D31lthkZ.js} +0 -0
  445. /package/dist/web/assets/{react-w6wINGnd.js → react-I_EwPIZi.js} +0 -0
  446. /package/dist/web/assets/{red-l7VuCR6d.js → red-CTcQZevb.js} +0 -0
  447. /package/dist/web/assets/{reg-g_k7gQEr.js → reg-D8YAsTGf.js} +0 -0
  448. /package/dist/web/assets/{regexp-CT17B7I7.js → regexp-CD2uvAWx.js} +0 -0
  449. /package/dist/web/assets/{rel-DkPCS7Bp.js → rel-CAaI-1fg.js} +0 -0
  450. /package/dist/web/assets/{resolve-theme-DdB7Yo5Z.js → resolve-theme-CJvkL8hU.js} +0 -0
  451. /package/dist/web/assets/{riscv-CYuFTw30.js → riscv-DR-hJVIj.js} +0 -0
  452. /package/dist/web/assets/{ron-cY7u_yEu.js → ron-CuUCJ0n4.js} +0 -0
  453. /package/dist/web/assets/{rose-pine-Cr_h_Ubo.js → rose-pine-aFHxW48i.js} +0 -0
  454. /package/dist/web/assets/{rose-pine-dawn-rRewaSZg.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
  455. /package/dist/web/assets/{rose-pine-moon-3VX5Xwca.js → rose-pine-moon-wqyh0an4.js} +0 -0
  456. /package/dist/web/assets/{rosmsg-Du4ie5PU.js → rosmsg-CI1ywimO.js} +0 -0
  457. /package/dist/web/assets/{rust-B7xWL-Eh.js → rust-p7Imk4G_.js} +0 -0
  458. /package/dist/web/assets/{sass-oXbOG8de.js → sass-C8JfaqCV.js} +0 -0
  459. /package/dist/web/assets/{save-C4z1p51e.js → save-DRPrDfTt.js} +0 -0
  460. /package/dist/web/assets/{scala-nlZJhKHf.js → scala-DNxCW1By.js} +0 -0
  461. /package/dist/web/assets/{scheme-FZ9_Zy8D.js → scheme-BYuUPJoL.js} +0 -0
  462. /package/dist/web/assets/{sdbl-D_lyvXiu.js → sdbl-DbPeJPUr.js} +0 -0
  463. /package/dist/web/assets/{shellscript-DRWRboAd.js → shellscript-DMiHlR3q.js} +0 -0
  464. /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-8GObuUAn.js} +0 -0
  465. /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-egKAZEwR.js} +0 -0
  466. /package/dist/web/assets/{slack-dark-BGk2uVHu.js → slack-dark-C4xGcQXI.js} +0 -0
  467. /package/dist/web/assets/{slack-ochin-PAoSX041.js → slack-ochin-BzgRt_wm.js} +0 -0
  468. /package/dist/web/assets/{smalltalk-qP0zvICo.js → smalltalk-LDeL9PYH.js} +0 -0
  469. /package/dist/web/assets/{snazzy-light-B7uccpH6.js → snazzy-light-CXE7a5j_.js} +0 -0
  470. /package/dist/web/assets/{solarized-dark-CbICLZs3.js → solarized-dark-DgUhY74x.js} +0 -0
  471. /package/dist/web/assets/{solarized-light-CprWhyjj.js → solarized-light-DD9C-H-9.js} +0 -0
  472. /package/dist/web/assets/{solidity-B7pcm2K6.js → solidity-CAbDB4D4.js} +0 -0
  473. /package/dist/web/assets/{splunk-Co2LL4J_.js → splunk-kQREPfIc.js} +0 -0
  474. /package/dist/web/assets/{sql-cETIdGnT.js → sql-CBzeNAD8.js} +0 -0
  475. /package/dist/web/assets/{ssh-config-DETG6JCD.js → ssh-config-L1euxrsM.js} +0 -0
  476. /package/dist/web/assets/{stylus-tmTre2-O.js → stylus-DvBnYniP.js} +0 -0
  477. /package/dist/web/assets/{swift-BeKQbULV.js → swift-DF5NDQp6.js} +0 -0
  478. /package/dist/web/assets/{synthwave-84-eCL4-Xdn.js → synthwave-84-DbBhB_Hq.js} +0 -0
  479. /package/dist/web/assets/{system-verilog-CviwWvEx.js → system-verilog-D8s2ZW_k.js} +0 -0
  480. /package/dist/web/assets/{systemd-WuoFIbBS.js → systemd-a5-XZJsG.js} +0 -0
  481. /package/dist/web/assets/{table-CHmxuh6M.js → table-BX2ZrJfM.js} +0 -0
  482. /package/dist/web/assets/{talonscript-B9VBu7VI.js → talonscript-BLa0URTW.js} +0 -0
  483. /package/dist/web/assets/{tasl-CN7r11ky.js → tasl-DIoV4XcC.js} +0 -0
  484. /package/dist/web/assets/{tcl-e1cdhe3h.js → tcl-ztYAdXfQ.js} +0 -0
  485. /package/dist/web/assets/{terraform-BEP0mfa_.js → terraform-DvpCCiOZ.js} +0 -0
  486. /package/dist/web/assets/{tokyo-night-BfA_dA37.js → tokyo-night-BkHlYUv0.js} +0 -0
  487. /package/dist/web/assets/{toml-BrDIfNnt.js → toml-acIrxos6.js} +0 -0
  488. /package/dist/web/assets/{tsv-CyfGyE4i.js → tsv-BJB5EVCT.js} +0 -0
  489. /package/dist/web/assets/{tsx-BrFA8Iwf.js → tsx-B1MmXGT3.js} +0 -0
  490. /package/dist/web/assets/{turtle-9GBtR0mP.js → turtle-DFceTiqd.js} +0 -0
  491. /package/dist/web/assets/{typescript-BuTRZ65V.js → typescript-Dz9wU5Y1.js} +0 -0
  492. /package/dist/web/assets/{typespec-CRiNNRXF.js → typespec-CPuYZijj.js} +0 -0
  493. /package/dist/web/assets/{typst-CmpTnnkl.js → typst-CfsCIYv8.js} +0 -0
  494. /package/dist/web/assets/{utils-B9H0d3Fh.js → utils-DoY4yTP-.js} +0 -0
  495. /package/dist/web/assets/{v-DnIi2aab.js → v-CGo0jcXA.js} +0 -0
  496. /package/dist/web/assets/{vala-B7VxCNZP.js → vala-AsSR0hrb.js} +0 -0
  497. /package/dist/web/assets/{vb-DGoQ3pBT.js → vb-C7tvbXv1.js} +0 -0
  498. /package/dist/web/assets/{vendor-xterm-ClRVIJ-k.js → vendor-xterm-BeaIjRrv.js} +0 -0
  499. /package/dist/web/assets/{verilog-zGOC4qwV.js → verilog--a33ND-p.js} +0 -0
  500. /package/dist/web/assets/{vesper-DXR9-06q.js → vesper-B_Z0h4pn.js} +0 -0
  501. /package/dist/web/assets/{vhdl-CCO0oz0l.js → vhdl-CWL5dZPN.js} +0 -0
  502. /package/dist/web/assets/{viml-Dxwr-QNN.js → viml-BglmT14Y.js} +0 -0
  503. /package/dist/web/assets/{vitesse-black-Bhah7RxE.js → vitesse-black-RKFsSqQI.js} +0 -0
  504. /package/dist/web/assets/{vitesse-dark-CGK6XVwc.js → vitesse-dark-D7zDWNmc.js} +0 -0
  505. /package/dist/web/assets/{vitesse-light-ne5kfw8h.js → vitesse-light-MAp87W5b.js} +0 -0
  506. /package/dist/web/assets/{vyper-ErDFqVoP.js → vyper-CgCBYg1G.js} +0 -0
  507. /package/dist/web/assets/{wasm-DtWvUWGr.js → wasm-BOG7ZeTT.js} +0 -0
  508. /package/dist/web/assets/{wasm-Bd258nS1.js → wasm-Bwl4txwp.js} +0 -0
  509. /package/dist/web/assets/{wenyan-EZe1IEYT.js → wenyan-DpShPKXZ.js} +0 -0
  510. /package/dist/web/assets/{wgsl-B1o5_I_9.js → wgsl-BakcCblK.js} +0 -0
  511. /package/dist/web/assets/{wikitext-C_rwp8An.js → wikitext-NroRWVAO.js} +0 -0
  512. /package/dist/web/assets/{wit-gbYtF3rY.js → wit-B5CM2pg6.js} +0 -0
  513. /package/dist/web/assets/{wolfram-DrcQFe2n.js → wolfram-7MTFBJpR.js} +0 -0
  514. /package/dist/web/assets/{x-B1qHmLdR.js → x-BcqfTY_7.js} +0 -0
  515. /package/dist/web/assets/{yaml-CzDAObLX.js → yaml-Bwr01G2K.js} +0 -0
  516. /package/dist/web/assets/{zenscript-B-J1phtg.js → zenscript-Dj8GATvi.js} +0 -0
  517. /package/dist/web/assets/{zig-BlzNia0O.js → zig-LAE-fbS-.js} +0 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Serve a local file with HTTP Range support (RFC 9110 §14).
3
+ *
4
+ * `<video>`/`<audio>` and pdf.js fetch media in byte ranges: the browser asks for
5
+ * `Range: bytes=N-` when the user seeks and expects `206 Partial Content` back.
6
+ * Without `Accept-Ranges` + `Content-Length` on the 200 response the browser cannot
7
+ * even show the duration, so a `file.stream()` body (chunked, no length) breaks seeking.
8
+ *
9
+ * Bun ≥ 1.3.13 handles Range itself when the body is a `BunFile`, but PPM runs on
10
+ * whatever Bun the user has installed, so the parsing is done here and works on
11
+ * every version. Multi-range requests (`bytes=0-1,5-9`) fall back to the whole file,
12
+ * which the spec allows.
13
+ */
14
+
15
+ export interface ByteRange {
16
+ start: number;
17
+ /** Inclusive last byte. */
18
+ end: number;
19
+ }
20
+
21
+ /**
22
+ * Parse a single-range `Range` header against a resource of `size` bytes.
23
+ * Returns `null` when there is no usable range (absent header, malformed, multi-range)
24
+ * — caller should answer 200 with the whole file. Returns `"unsatisfiable"` when the
25
+ * range lies entirely outside the file — caller should answer 416.
26
+ */
27
+ export function parseRangeHeader(header: string | null | undefined, size: number): ByteRange | "unsatisfiable" | null {
28
+ if (!header) return null;
29
+ const m = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
30
+ if (!m) return null;
31
+ const [, rawStart, rawEnd] = m;
32
+ if (rawStart === "" && rawEnd === "") return null;
33
+ if (size <= 0) return "unsatisfiable";
34
+
35
+ // Suffix range `bytes=-500` → last 500 bytes.
36
+ if (rawStart === "") {
37
+ const suffix = Number(rawEnd);
38
+ if (suffix <= 0) return "unsatisfiable";
39
+ return { start: Math.max(0, size - suffix), end: size - 1 };
40
+ }
41
+
42
+ const start = Number(rawStart);
43
+ if (start >= size) return "unsatisfiable";
44
+ // Open-ended `bytes=1024-` or an end past EOF are both clamped to the last byte.
45
+ const end = rawEnd === "" ? size - 1 : Math.min(Number(rawEnd), size - 1);
46
+ if (end < start) return "unsatisfiable";
47
+ return { start, end };
48
+ }
49
+
50
+ /**
51
+ * Build the `Response` for `absPath`, honouring the request's `Range` header.
52
+ * `extraHeaders` are merged onto every response (Content-Disposition, Cache-Control…).
53
+ * `contentType` overrides the sniffed MIME type (used for forced downloads).
54
+ */
55
+ export function rangeFileResponse(
56
+ absPath: string,
57
+ req: Request,
58
+ extraHeaders: Record<string, string> = {},
59
+ contentType?: string,
60
+ ): Response {
61
+ const file = Bun.file(absPath);
62
+ const size = file.size;
63
+ const base: Record<string, string> = {
64
+ "Accept-Ranges": "bytes",
65
+ "Content-Type": contentType ?? (file.type || "application/octet-stream"),
66
+ ...extraHeaders,
67
+ };
68
+
69
+ const range = parseRangeHeader(req.headers.get("range"), size);
70
+ if (range === null) {
71
+ // Whole file. Bun derives Content-Length from the BunFile body.
72
+ return new Response(file, { status: 200, headers: { ...base, "Content-Length": String(size) } });
73
+ }
74
+ if (range === "unsatisfiable") {
75
+ return new Response(null, { status: 416, headers: { ...base, "Content-Range": `bytes */${size}` } });
76
+ }
77
+ const { start, end } = range;
78
+ return new Response(file.slice(start, end + 1), {
79
+ status: 206,
80
+ headers: {
81
+ ...base,
82
+ "Content-Range": `bytes ${start}-${end}/${size}`,
83
+ "Content-Length": String(end - start + 1),
84
+ },
85
+ });
86
+ }
@@ -17,6 +17,7 @@ import { postgresRoutes } from "./routes/postgres.ts";
17
17
  import { databaseRoutes } from "./routes/database.ts";
18
18
  import { fsBrowseRoutes } from "./routes/fs-browse.ts";
19
19
  import { fsOpsRoutes } from "./routes/fs-ops.ts";
20
+ import { fsUploadRoutes } from "./routes/fs-upload.ts";
20
21
  import { fsSqliteRoutes } from "./routes/fs-sqlite.ts";
21
22
  import { accountsRoutes } from "./routes/accounts.ts";
22
23
  import { proxyRoutes } from "./routes/proxy.ts";
@@ -94,6 +95,16 @@ async function setupLogFile() {
94
95
  });
95
96
  }
96
97
 
98
+ /**
99
+ * WebSocket upgrades carry the session token as `?token=` (no headers on a
100
+ * browser handshake). When auth is disabled every upgrade is allowed.
101
+ */
102
+ export function isWsUpgradeAuthorized(url: URL): boolean {
103
+ const authConfig = configService.get("auth");
104
+ if (!authConfig.enabled) return true;
105
+ return url.searchParams.get("token") === authConfig.token;
106
+ }
107
+
97
108
  // Register database adapters at module load time
98
109
  initAdapters();
99
110
 
@@ -155,6 +166,7 @@ app.route("/api/tunnels", tunnelRegistryRoutes);
155
166
  // Filesystem operations (browse, list, read, write) — consolidated in fs-browse route
156
167
  app.route("/api/fs", fsBrowseRoutes);
157
168
  app.route("/api/fs", fsOpsRoutes);
169
+ app.route("/api/fs", fsUploadRoutes);
158
170
  app.route("/api/fs/sqlite", fsSqliteRoutes);
159
171
 
160
172
  // System resource monitoring (SSE + JSON)
@@ -818,30 +830,23 @@ if (process.argv.includes("__serve__")) {
818
830
  return new Response("WebSocket upgrade failed", { status: 400 });
819
831
  }
820
832
 
833
+ // Every socket except the health probe is authenticated at upgrade time.
834
+ // Browsers cannot send headers on a WebSocket handshake, so the session
835
+ // token travels as `?token=`; the terminal socket in particular hands out
836
+ // a shell, so an unauthenticated upgrade must never reach the handlers.
837
+ if (url.pathname.startsWith("/ws/") && !isWsUpgradeAuthorized(url)) {
838
+ return new Response("Unauthorized", { status: 401 });
839
+ }
840
+
821
841
  if (url.pathname === "/ws/global") {
822
842
  // App-wide event bus: owns file watching + cross-cutting broadcasts, so
823
- // they no longer depend on a chat tab being mounted. Same auth as below.
824
- const authConfig = configService.get("auth");
825
- if (authConfig.enabled) {
826
- const token = url.searchParams.get("token");
827
- if (token !== authConfig.token) {
828
- return new Response("Unauthorized", { status: 401 });
829
- }
830
- }
843
+ // they no longer depend on a chat tab being mounted.
831
844
  const upgraded = server.upgrade(req, { data: { type: "global" } });
832
845
  if (upgraded) return undefined;
833
846
  return new Response("WebSocket upgrade failed", { status: 400 });
834
847
  }
835
848
 
836
849
  if (url.pathname === "/ws/extensions") {
837
- // Auth check for extension WS
838
- const authConfig = configService.get("auth");
839
- if (authConfig.enabled) {
840
- const token = url.searchParams.get("token");
841
- if (token !== authConfig.token) {
842
- return new Response("Unauthorized", { status: 401 });
843
- }
844
- }
845
850
  const upgraded = server.upgrade(req, { data: { type: "extensions" } });
846
851
  if (upgraded) return undefined;
847
852
  return new Response("WebSocket upgrade failed", { status: 400 });
@@ -854,8 +859,10 @@ if (process.argv.includes("__serve__")) {
854
859
  const id = parts[5] ?? "";
855
860
 
856
861
  if (wsType === "terminal") {
862
+ // Optional start directory (explorer "Open in Terminal"); validated in the handler.
863
+ const cwd = url.searchParams.get("cwd") ?? undefined;
857
864
  const upgraded = server.upgrade(req, {
858
- data: { type: "terminal", id, projectName },
865
+ data: { type: "terminal", id, projectName, cwd },
859
866
  });
860
867
  if (upgraded) return undefined;
861
868
  return new Response("WebSocket upgrade failed", { status: 400 });
@@ -26,10 +26,11 @@ export async function authMiddleware(c: Context, next: Next) {
26
26
  }
27
27
 
28
28
  // Fallback: ?token= query param for SSE/EventSource & iframe embeds (can't set custom headers)
29
- // Scoped to /stream and /files/raw paths to avoid leaking token on all GET routes
29
+ // Scoped to /stream, /files/raw, /files/transcode (<video src> cannot set headers) to avoid leaking token on all GET routes
30
30
  if (c.req.method === "GET") {
31
31
  const p = c.req.path;
32
- if (p.endsWith("/stream") || p.endsWith("/files/raw") || p === "/api/fs/raw" || p.endsWith("/image")) {
32
+ const isMediaPath = p.endsWith("/files/raw") || p.endsWith("/files/transcode") || p === "/api/fs/raw" || p === "/api/fs/transcode";
33
+ if (p.endsWith("/stream") || isMediaPath || p.endsWith("/image")) {
33
34
  const queryToken = c.req.query("token");
34
35
  if (queryToken && queryToken === authConfig.token) {
35
36
  return next();
@@ -5,6 +5,8 @@ import { fileService, SecurityError, NotFoundError, ValidationError } from "../.
5
5
  import { readSystemFileSync } from "../../services/fs-browse.service.ts";
6
6
  import { ok, err } from "../../types/api.ts";
7
7
  import { errorStatus } from "../helpers/error-status.ts";
8
+ import { rangeFileResponse } from "../helpers/range-file-response.ts";
9
+ import { handleMediaProbe, handleMediaTranscode } from "../helpers/media-route-handlers.ts";
8
10
  import mammoth from "mammoth";
9
11
 
10
12
  type Env = { Variables: { projectPath: string; projectName: string } };
@@ -103,16 +105,47 @@ fileRoutes.get("/raw", (c) => {
103
105
  }
104
106
  if (!existsSync(absPath)) return c.json(err("File not found"), 404);
105
107
 
106
- const file = Bun.file(absPath);
107
108
  const download = c.req.query("download") === "true";
108
109
  const filename = filePath.split("/").pop() ?? "download";
109
110
 
110
- return new Response(file.stream(), {
111
- headers: {
112
- "Content-Type": download ? "application/octet-stream" : (file.type || "application/octet-stream"),
113
- "Content-Disposition": download ? `attachment; filename="${filename}"` : "inline",
114
- },
115
- });
111
+ // Range-aware so <video>/<audio>/pdf.js can seek instead of buffering the whole file.
112
+ return rangeFileResponse(
113
+ absPath,
114
+ c.req.raw,
115
+ { "Content-Disposition": download ? `attachment; filename="${filename}"` : "inline" },
116
+ download ? "application/octet-stream" : undefined,
117
+ );
118
+ } catch (e) {
119
+ return c.json(err((e as Error).message), errorStatus(e));
120
+ }
121
+ });
122
+
123
+ /** Resolve `?path=` inside the project or return the error response to send. */
124
+ function resolveProjectFile(c: { get(k: "projectPath"): string; req: { query(k: string): string | undefined } }): string | Response {
125
+ const projectPath = c.get("projectPath");
126
+ const filePath = c.req.query("path");
127
+ if (!filePath) return Response.json(err("Missing query parameter: path"), { status: 400 });
128
+ const absPath = resolve(projectPath, filePath);
129
+ if (!absPath.startsWith(projectPath)) return Response.json(err("Access denied"), { status: 403 });
130
+ if (!existsSync(absPath)) return Response.json(err("File not found"), { status: 404 });
131
+ return absPath;
132
+ }
133
+
134
+ /** GET /files/probe?path=... — codec/duration facts + whether ffmpeg transcoding is available */
135
+ fileRoutes.get("/probe", async (c) => {
136
+ try {
137
+ const abs = resolveProjectFile(c);
138
+ return abs instanceof Response ? abs : await handleMediaProbe(abs);
139
+ } catch (e) {
140
+ return c.json(err((e as Error).message), errorStatus(e));
141
+ }
142
+ });
143
+
144
+ /** GET /files/transcode?path=...&start=<sec> — ffmpeg → fragmented MP4 for videos the browser cannot decode */
145
+ fileRoutes.get("/transcode", async (c) => {
146
+ try {
147
+ const abs = resolveProjectFile(c);
148
+ return abs instanceof Response ? abs : await handleMediaTranscode(abs, c.req.raw, c.req.query("start"));
116
149
  } catch (e) {
117
150
  return c.json(err((e as Error).message), errorStatus(e));
118
151
  }
@@ -21,6 +21,8 @@ import { attachmentDisposition } from "../../services/fs-ops/fs-content-disposit
21
21
  import { fsErrorBody } from "../../services/fs-ops/fs-error-response.ts";
22
22
  import { isImageExtension } from "../../shared/image-extensions.ts";
23
23
  import { ok, err } from "../../types/api.ts";
24
+ import { rangeFileResponse } from "../helpers/range-file-response.ts";
25
+ import { handleMediaProbe, handleMediaTranscode } from "../helpers/media-route-handlers.ts";
24
26
  import {
25
27
  createDownloadToken,
26
28
  consumeDownloadToken,
@@ -121,16 +123,52 @@ fsBrowseRoutes.get("/raw", async (c) => {
121
123
 
122
124
  const download = c.req.query("download") === "true";
123
125
  const filename = basename(resolved) || "download";
124
- const file = Bun.file(resolved);
125
- return new Response(file.stream(), {
126
- headers: {
127
- "Content-Type": download ? "application/octet-stream" : (file.type || "application/octet-stream"),
126
+ // Range-aware so <video>/<audio>/pdf.js can seek instead of buffering the whole file.
127
+ return rangeFileResponse(
128
+ resolved,
129
+ c.req.raw,
130
+ {
128
131
  "Content-Disposition": download ? attachmentDisposition(filename) : "inline",
129
132
  // A download is a one-shot transfer of a file the user may edit right
130
133
  // after; caching it would hand out stale bytes on the next download.
131
134
  "Cache-Control": download ? "no-store" : "private, max-age=3600",
132
135
  },
133
- });
136
+ download ? "application/octet-stream" : undefined,
137
+ );
138
+ } catch (e) {
139
+ const { body, status } = fail(e);
140
+ return c.json(body, status);
141
+ }
142
+ });
143
+
144
+ /** Resolve `?path=` for media routes: allowlisted, not inside ~/.ppm, and existing. */
145
+ async function resolveMediaPath(raw: string | undefined): Promise<string | Response> {
146
+ if (!raw) return Response.json(err("path is required"), { status: 400 });
147
+ const resolved = resolvePath(raw);
148
+ assertAllowed(resolved);
149
+ assertNotPpmDir(resolved);
150
+ assertNotPpmDir(await realPathOrSelf(resolved));
151
+ const info = await stat(resolved).catch(() => null);
152
+ if (!info?.isFile()) return Response.json(err("File not found"), { status: 404 });
153
+ return resolved;
154
+ }
155
+
156
+ /** GET /api/fs/probe?path=/some/video — codec/duration facts + whether ffmpeg transcoding is available */
157
+ fsBrowseRoutes.get("/probe", async (c) => {
158
+ try {
159
+ const abs = await resolveMediaPath(c.req.query("path"));
160
+ return abs instanceof Response ? abs : await handleMediaProbe(abs);
161
+ } catch (e) {
162
+ const { body, status } = fail(e);
163
+ return c.json(body, status);
164
+ }
165
+ });
166
+
167
+ /** GET /api/fs/transcode?path=/some/video&start=<sec> — ffmpeg → fragmented MP4 */
168
+ fsBrowseRoutes.get("/transcode", async (c) => {
169
+ try {
170
+ const abs = await resolveMediaPath(c.req.query("path"));
171
+ return abs instanceof Response ? abs : await handleMediaTranscode(abs, c.req.raw, c.req.query("start"));
134
172
  } catch (e) {
135
173
  const { body, status } = fail(e);
136
174
  return c.json(body, status);
@@ -0,0 +1,32 @@
1
+ import { Hono, type Context } from "hono";
2
+ import { err } from "../../types/api.ts";
3
+ import { fsErrorBody } from "../../services/fs-ops/fs-error-response.ts";
4
+ import { uploadFile } from "../../services/fs-ops/fs-ops-upload.service.ts";
5
+
6
+ /**
7
+ * Streaming upload door, mounted on the same `/api/fs` prefix as the browse/mutate routes.
8
+ * Split out of `fs-ops.ts` because a request body stream is a very different shape of
9
+ * handler than the JSON in/JSON out routes there.
10
+ */
11
+ export const fsUploadRoutes = new Hono();
12
+
13
+ /**
14
+ * PUT /api/fs/upload?path=<absolute file path>&overwrite=0|1
15
+ *
16
+ * Body: the raw file bytes. Streamed straight to disk (see the service) — never buffered —
17
+ * so there is no per-file size limit here; the host disk is the only bound. Answers 409
18
+ * `EEXIST` when the target exists and `overwrite` was not set, matching every other
19
+ * `/api/fs` mutation's collision contract.
20
+ */
21
+ fsUploadRoutes.put("/upload", async (c: Context) => {
22
+ try {
23
+ const path = c.req.query("path");
24
+ if (!path) return c.json(err("path is required"), 400);
25
+ const overwrite = c.req.query("overwrite") === "1";
26
+ const result = await uploadFile(path, c.req.raw.body, overwrite);
27
+ return c.json({ ok: true, data: result }, 201);
28
+ } catch (e) {
29
+ const { body, status } = fsErrorBody(e);
30
+ return c.json(body, status);
31
+ }
32
+ });
@@ -25,6 +25,40 @@ teamRoutes.get("/:name", async (c) => {
25
25
  return c.json(ok(detail));
26
26
  });
27
27
 
28
+ /**
29
+ * Live per-member activity. Separate from `/:name` because it is polled while the
30
+ * panel is open, and because its source is the agent transcripts rather than the
31
+ * team inboxes — the inboxes cannot say who is working right now.
32
+ */
33
+ teamRoutes.get("/:name/activity", async (c) => {
34
+ const name = c.req.param("name");
35
+ if (!VALID_TEAM_NAME.test(name)) return c.json(err("Invalid team name"), 400);
36
+ const { readTeamMemberActivity, readTeamOutboundMessages } = await import(
37
+ "../../services/team-member-activity/member-activity.service.ts"
38
+ );
39
+ const projectPath = c.req.query("projectPath") ?? null;
40
+ const [members, outbound] = await Promise.all([
41
+ readTeamMemberActivity(name, projectPath),
42
+ readTeamOutboundMessages(name, projectPath),
43
+ ]);
44
+ return c.json(ok({ members, outbound }));
45
+ });
46
+
47
+ /** Full transcript of one teammate's work session, for the member window. */
48
+ teamRoutes.get("/:name/members/:member/transcript", async (c) => {
49
+ const name = c.req.param("name");
50
+ const member = c.req.param("member");
51
+ if (!VALID_TEAM_NAME.test(name)) return c.json(err("Invalid team name"), 400);
52
+ if (!VALID_TEAM_NAME.test(member)) return c.json(err("Invalid member name"), 400);
53
+ const { resolveMemberTranscript } = await import(
54
+ "../../services/team-member-activity/member-activity.service.ts"
55
+ );
56
+ const path = resolveMemberTranscript(name, member, c.req.query("projectPath") ?? null);
57
+ if (!path) return c.json(err("Member transcript not found"), 404);
58
+ const { parseAgentTranscript } = await import("../../services/subagent-transcript-merger.ts");
59
+ return c.json(ok({ member, events: parseAgentTranscript(path) }));
60
+ });
61
+
28
62
  teamRoutes.delete("/:name", async (c) => {
29
63
  const name = c.req.param("name");
30
64
  if (!VALID_TEAM_NAME.test(name)) {
@@ -14,6 +14,7 @@ import { backgroundShellRegistry } from "../../services/background-shell-registr
14
14
  import { basename } from "node:path";
15
15
  import { configService } from "../../services/config.service.ts";
16
16
  import { formatTurnUsageLog } from "../../shared/turn-usage.ts";
17
+ import { isTerminalAgentStatus } from "../../shared/background-agent-status.ts";
17
18
  import { cacheReleaseDelayMs, selectWarmIdleEvictions } from "../../services/subprocess-retention.ts";
18
19
 
19
20
  /** Resolve the SESSION's provider config — not the global default provider's.
@@ -86,6 +87,8 @@ interface SessionEntry {
86
87
  teamNames: Set<string>;
87
88
  /** toolUseId of a pending TeamCreate call */
88
89
  pendingTeamCreate?: string;
90
+ /** Throttle marker for the filesystem probe that finds implicitly-created teams */
91
+ lastImplicitTeamProbe?: number;
89
92
  /** Compact indicator state — sticky until turn ends or boundary received, synced on reconnect */
90
93
  compactStatus?: "compacting" | null;
91
94
  /** toolUseIds of Bash calls launched with run_in_background — spy is kept alive for these */
@@ -268,6 +271,47 @@ function bufferAndBroadcast(sessionId: string, event: unknown): void {
268
271
  broadcast(sessionId, event);
269
272
  }
270
273
 
274
+ /** How often a session may stat ~/.claude/teams looking for an implicit team. */
275
+ const IMPLICIT_TEAM_PROBE_INTERVAL_MS = 3_000;
276
+
277
+ /** Watch a team's inboxes and announce it to the session's clients. Idempotent. */
278
+ async function attachTeamWatcher(sessionId: string, teamName: string): Promise<void> {
279
+ const entry = activeSessions.get(sessionId);
280
+ if (!entry || entry.teamNames.has(teamName)) return;
281
+ entry.teamNames.add(teamName);
282
+ const { startTeamInboxWatcher } = await import("./team-inbox-watcher.ts");
283
+ const watcher = await startTeamInboxWatcher(teamName, {
284
+ onInboxUpdate: (tn, agent, msgs) => broadcast(sessionId, {
285
+ type: "team_inbox", teamName: tn, agent, messages: msgs,
286
+ }),
287
+ onConfigUpdate: (tn, config) => broadcast(sessionId, {
288
+ type: "team_updated", teamName: tn, team: config,
289
+ }),
290
+ });
291
+ // The session may have been torn down while the watcher was starting.
292
+ const live = activeSessions.get(sessionId);
293
+ if (!live) { watcher.cleanup(); return; }
294
+ live.teamWatchers.set(teamName, watcher);
295
+ bufferAndBroadcast(sessionId, { type: "team_detected", teamName });
296
+ console.log(`[chat] session=${sessionId} team detected: ${teamName}`);
297
+ }
298
+
299
+ /** Attach to the team Claude Code creates implicitly for this session.
300
+ * Current releases no longer expose a TeamCreate tool — a team materialises as
301
+ * ~/.claude/teams/<sessionId>/inboxes/ with no tool call to hook and no
302
+ * config.json, so the directory itself is the only reliable signal. */
303
+ async function detectImplicitTeam(sessionId: string): Promise<void> {
304
+ const entry = activeSessions.get(sessionId);
305
+ if (!entry || entry.teamNames.has(sessionId)) return;
306
+ const now = Date.now();
307
+ if (entry.lastImplicitTeamProbe && now - entry.lastImplicitTeamProbe < IMPLICIT_TEAM_PROBE_INTERVAL_MS) return;
308
+ entry.lastImplicitTeamProbe = now;
309
+ try {
310
+ const { teamExists } = await import("./team-inbox-watcher.ts");
311
+ if (await teamExists(sessionId)) await attachTeamWatcher(sessionId, sessionId);
312
+ } catch { /* teams dir unreadable — nothing to attach */ }
313
+ }
314
+
271
315
  /** Transition session phase — guards same-phase, broadcasts phase_changed */
272
316
  function setPhase(sessionId: string, phase: SessionPhase, elapsed?: number): void {
273
317
  const entry = activeSessions.get(sessionId);
@@ -435,6 +479,12 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
435
479
  const taskStatus = (ev as any).taskStatus as string | undefined;
436
480
  const taskToolUseId = (ev as any).taskToolUseId as string | undefined;
437
481
  const outputFile = (ev as any).outputFile as string | undefined;
482
+ // A backgrounded Agent reports its outcome only here — its tool_result was a
483
+ // launch ack the card must not read as "finished". Forward the terminal state so
484
+ // the card can settle. Harmless for background bash tasks: no card matches them.
485
+ if (sub === "task_notification" && taskToolUseId && isTerminalAgentStatus(taskStatus)) {
486
+ broadcast(sessionId, { type: "subagent_status", toolUseId: taskToolUseId, status: taskStatus });
487
+ }
438
488
  if (taskId) {
439
489
  // Ensure the shell is registered even if the spy missed the file (fallback).
440
490
  if (!backgroundShellRegistry.get(sessionId, taskId) && outputFile) {
@@ -555,27 +605,17 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
555
605
  if (ev.toolUseId && !entry.backgroundToolUseIds?.has(ev.toolUseId)) {
556
606
  bashOutputSpy.stopSpy(ev.toolUseId);
557
607
  }
558
- // Detect team creation from TeamCreate tool_result
608
+ // Detect team creation from TeamCreate tool_result (legacy explicit teams)
559
609
  if (entry.pendingTeamCreate && entry.pendingTeamCreate === ev.toolUseId) {
560
- const { extractTeamName, startTeamInboxWatcher } = await import("./team-inbox-watcher.ts");
610
+ const { extractTeamName } = await import("./team-inbox-watcher.ts");
561
611
  const teamName = extractTeamName(ev.output ?? "");
562
612
  console.log(`[chat] session=${sessionId} TeamCreate result matched, extracted teamName=${teamName}`);
563
- if (teamName && !entry.teamNames.has(teamName)) {
564
- entry.teamNames.add(teamName);
565
- const watcher = startTeamInboxWatcher(teamName, {
566
- onInboxUpdate: (tn, agent, msgs) => broadcast(sessionId, {
567
- type: "team_inbox", teamName: tn, agent, messages: msgs,
568
- }),
569
- onConfigUpdate: (tn, config) => broadcast(sessionId, {
570
- type: "team_updated", teamName: tn, team: config,
571
- }),
572
- });
573
- entry.teamWatchers.set(teamName, watcher);
574
- bufferAndBroadcast(sessionId, { type: "team_detected", teamName });
575
- console.log(`[chat] session=${sessionId} team detected: ${teamName}`);
576
- }
613
+ if (teamName) await attachTeamWatcher(sessionId, teamName);
577
614
  entry.pendingTeamCreate = undefined;
578
615
  }
616
+ // Implicit teams have no tool result to key off — the session's own team
617
+ // directory can appear after any Agent/SendMessage call, so poll (throttled).
618
+ void detectImplicitTeam(sessionId);
579
619
  } else if (evType === "error") {
580
620
  const errorDetail = ev.message ?? JSON.stringify(ev).slice(0, 500);
581
621
  console.error(`[chat] session=${sessionId} error: ${errorDetail}`);
@@ -787,6 +827,10 @@ export const chatWebSocket = {
787
827
  existing.clients.add(ws);
788
828
  setupClientPing(existing, ws);
789
829
 
830
+ // A team created in an earlier turn (or before a server restart) leaves no
831
+ // live event to replay — re-attach from disk so the UI comes back.
832
+ void detectImplicitTeam(sessionId);
833
+
790
834
  // Async: resolve title from SDK if in-memory title is generic (DB title takes priority)
791
835
  if (!session?.title || session.title === "Chat" || session.title === "Resumed Chat") {
792
836
  sdkListSessions({ dir: projectPath, limit: 50 }).then((sessions) => {
@@ -821,6 +865,10 @@ export const chatWebSocket = {
821
865
  activeSessions.set(sessionId, newEntry);
822
866
  setupClientPing(newEntry, ws);
823
867
 
868
+ // Resuming a session whose team already exists on disk (server restart, or a
869
+ // team created many turns ago) — re-attach the watcher.
870
+ void detectImplicitTeam(sessionId);
871
+
824
872
  ws.send(JSON.stringify({
825
873
  type: "session_state",
826
874
  sessionId,