termdock 1.4.350 → 1.4.352

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 (448) hide show
  1. package/dist/client/assets/AndroidMirrorView-DkOZxYLg.js +3 -0
  2. package/dist/client/assets/AndroidMirrorView-DkOZxYLg.js.br +0 -0
  3. package/dist/client/assets/AndroidMirrorView-DkOZxYLg.js.gz +0 -0
  4. package/dist/client/assets/App-DqRkfVSm.js +178 -0
  5. package/dist/client/assets/App-DqRkfVSm.js.br +0 -0
  6. package/dist/client/assets/App-DqRkfVSm.js.gz +0 -0
  7. package/dist/client/assets/ArchitecturePanel-m9GKl3cl.js +9 -0
  8. package/dist/client/assets/ArchitecturePanel-m9GKl3cl.js.br +0 -0
  9. package/dist/client/assets/ArchitecturePanel-m9GKl3cl.js.gz +0 -0
  10. package/dist/client/assets/ChangeWalkthroughPanel-HZ0zvFvi.js +2 -0
  11. package/dist/client/assets/ChangeWalkthroughPanel-HZ0zvFvi.js.br +0 -0
  12. package/dist/client/assets/ChangeWalkthroughPanel-HZ0zvFvi.js.gz +0 -0
  13. package/dist/client/assets/CollaborationMainWorkspace-B4OMk92m.js +1 -0
  14. package/dist/client/assets/CollaborationMainWorkspace-B4OMk92m.js.br +0 -0
  15. package/dist/client/assets/CollaborationMainWorkspace-B4OMk92m.js.gz +0 -0
  16. package/dist/client/assets/CollaborationTaskContent-Cbg3iH6E.js +2 -0
  17. package/dist/client/assets/ComputerControlView-CSs54lf0.js +6 -0
  18. package/dist/client/assets/ComputerControlView-CSs54lf0.js.br +0 -0
  19. package/dist/client/assets/ComputerControlView-CSs54lf0.js.gz +0 -0
  20. package/dist/client/assets/DagPlayground-yoS2llnB.js +1 -0
  21. package/dist/client/assets/DagPlayground-yoS2llnB.js.br +0 -0
  22. package/dist/client/assets/DagPlayground-yoS2llnB.js.gz +0 -0
  23. package/dist/client/assets/{DiffLab-DEw4y4Hk.js → DiffLab-Dj1MT9pY.js} +2 -2
  24. package/dist/client/assets/DiffLab-Dj1MT9pY.js.br +0 -0
  25. package/dist/client/assets/DiffLab-Dj1MT9pY.js.gz +0 -0
  26. package/dist/client/assets/DiffReview-BX2S9hNp.js +1 -0
  27. package/dist/client/assets/DiffReview-BX2S9hNp.js.br +0 -0
  28. package/dist/client/assets/DiffReview-BX2S9hNp.js.gz +0 -0
  29. package/dist/client/assets/{DiffReviewLab-CDXsy0jE.js → DiffReviewLab-DqI14GEk.js} +2 -2
  30. package/dist/client/assets/DiffReviewLab-DqI14GEk.js.br +0 -0
  31. package/dist/client/assets/DiffReviewLab-DqI14GEk.js.gz +0 -0
  32. package/dist/client/assets/DiffViewer-AZvdDGfH.js +34 -0
  33. package/dist/client/assets/DiffViewer-AZvdDGfH.js.br +0 -0
  34. package/dist/client/assets/DiffViewer-AZvdDGfH.js.gz +0 -0
  35. package/dist/client/assets/GitLoadingSkeleton-CBPykkZP.js +1 -0
  36. package/dist/client/assets/GitLoadingSkeleton-CBPykkZP.js.br +0 -0
  37. package/dist/client/assets/GitLoadingSkeleton-CBPykkZP.js.gz +0 -0
  38. package/dist/client/assets/MarkdownPreview-DpvTnKkj.js +19 -0
  39. package/dist/client/assets/MarkdownPreview-DpvTnKkj.js.br +0 -0
  40. package/dist/client/assets/MarkdownPreview-DpvTnKkj.js.gz +0 -0
  41. package/dist/client/assets/{ModelPreview-BEiOgE0G.js → ModelPreview-Du_AyPr9.js} +166 -166
  42. package/dist/client/assets/ModelPreview-Du_AyPr9.js.br +0 -0
  43. package/dist/client/assets/ModelPreview-Du_AyPr9.js.gz +0 -0
  44. package/dist/client/assets/RecordingSaveDialog-CtXaGgIo.js +1 -0
  45. package/dist/client/assets/RecordingSaveDialog-CtXaGgIo.js.br +0 -0
  46. package/dist/client/assets/RecordingSaveDialog-CtXaGgIo.js.gz +0 -0
  47. package/dist/client/assets/RightSidebar-DU3gwdzg.js +1662 -0
  48. package/dist/client/assets/RightSidebar-DU3gwdzg.js.br +0 -0
  49. package/dist/client/assets/RightSidebar-DU3gwdzg.js.gz +0 -0
  50. package/dist/client/assets/SessionChatView-CxfwJMuK.js +13 -0
  51. package/dist/client/assets/SessionChatView-CxfwJMuK.js.br +0 -0
  52. package/dist/client/assets/SessionChatView-CxfwJMuK.js.gz +0 -0
  53. package/dist/client/assets/SessionChatView-D8GoESjb.css +1 -0
  54. package/dist/client/assets/SessionChatView-D8GoESjb.css.br +0 -0
  55. package/dist/client/assets/SessionChatView-D8GoESjb.css.gz +0 -0
  56. package/dist/client/assets/SessionResourceSidebar-B31PU57J.js +2 -0
  57. package/dist/client/assets/SessionResourceSidebar-B31PU57J.js.br +0 -0
  58. package/dist/client/assets/SessionResourceSidebar-B31PU57J.js.gz +0 -0
  59. package/dist/client/assets/TodoWorkspace-j9BLgk94.js +1 -0
  60. package/dist/client/assets/TodoWorkspace-j9BLgk94.js.br +0 -0
  61. package/dist/client/assets/TodoWorkspace-j9BLgk94.js.gz +0 -0
  62. package/dist/client/assets/{abnfDiagram-VCTEODGH-ByzlUu_B.js → abnfDiagram-VCTEODGH-LfmMIpPX.js} +1 -1
  63. package/dist/client/assets/abnfDiagram-VCTEODGH-LfmMIpPX.js.br +0 -0
  64. package/dist/client/assets/abnfDiagram-VCTEODGH-LfmMIpPX.js.gz +0 -0
  65. package/dist/client/assets/app-BMJesFDU.js +36 -0
  66. package/dist/client/assets/app-BMJesFDU.js.br +0 -0
  67. package/dist/client/assets/app-BMJesFDU.js.gz +0 -0
  68. package/dist/client/assets/{arc-BA6JM6vl.js → arc-DMzcmH2U.js} +1 -1
  69. package/dist/client/assets/arc-DMzcmH2U.js.br +0 -0
  70. package/dist/client/assets/arc-DMzcmH2U.js.gz +0 -0
  71. package/dist/client/assets/{architectureDiagram-5GKGNRK7-hCHxOfKw.js → architectureDiagram-5GKGNRK7-CphS_S5Q.js} +1 -1
  72. package/dist/client/assets/architectureDiagram-5GKGNRK7-CphS_S5Q.js.br +0 -0
  73. package/dist/client/assets/{architectureDiagram-5GKGNRK7-hCHxOfKw.js.gz → architectureDiagram-5GKGNRK7-CphS_S5Q.js.gz} +0 -0
  74. package/dist/client/assets/arrow-down-D-WZS308.js +1 -0
  75. package/dist/client/assets/arrow-up-CelbB9FE.js +1 -0
  76. package/dist/client/assets/{blockDiagram-I7D4REHJ-OmEN9hUB.js → blockDiagram-I7D4REHJ-Dx_4qkcL.js} +1 -1
  77. package/dist/client/assets/blockDiagram-I7D4REHJ-Dx_4qkcL.js.br +0 -0
  78. package/dist/client/assets/blockDiagram-I7D4REHJ-Dx_4qkcL.js.gz +0 -0
  79. package/dist/client/assets/{c4Diagram-7LVT6UL2-DfLTv8zh.js → c4Diagram-7LVT6UL2-DiGdnPo4.js} +1 -1
  80. package/dist/client/assets/c4Diagram-7LVT6UL2-DiGdnPo4.js.br +0 -0
  81. package/dist/client/assets/c4Diagram-7LVT6UL2-DiGdnPo4.js.gz +0 -0
  82. package/dist/client/assets/channel-BxAJ9Qmh.js +1 -0
  83. package/dist/client/assets/chevron-left-5sk7gL3u.js +1 -0
  84. package/dist/client/assets/{chunk-2Q5K7J3B-C3O76PNZ.js → chunk-2Q5K7J3B-M50KNmR8.js} +1 -1
  85. package/dist/client/assets/{chunk-5VM5RSS4-CsWZd30k.js → chunk-5VM5RSS4-wXVM-Max.js} +1 -1
  86. package/dist/client/assets/{chunk-F27PBJKO-yxCv2Uqk.js → chunk-F27PBJKO-BfQILS9u.js} +1 -1
  87. package/dist/client/assets/chunk-F27PBJKO-BfQILS9u.js.br +0 -0
  88. package/dist/client/assets/chunk-F27PBJKO-BfQILS9u.js.gz +0 -0
  89. package/dist/client/assets/{chunk-IMKFNOWR-CdPjlb9f.js → chunk-IMKFNOWR-D111Jpl3.js} +1 -1
  90. package/dist/client/assets/chunk-IMKFNOWR-D111Jpl3.js.br +0 -0
  91. package/dist/client/assets/chunk-IMKFNOWR-D111Jpl3.js.gz +0 -0
  92. package/dist/client/assets/{chunk-JWPE2WC7-BiiOLq2R.js → chunk-JWPE2WC7-BBDyly5c.js} +1 -1
  93. package/dist/client/assets/{chunk-POPQ4Y6H-BOu_1Brt.js → chunk-POPQ4Y6H-BaI-_yc7.js} +1 -1
  94. package/dist/client/assets/{chunk-SVP7TREG-D1tOJung.js → chunk-SVP7TREG-BvQafCMJ.js} +1 -1
  95. package/dist/client/assets/chunk-SVP7TREG-BvQafCMJ.js.br +0 -0
  96. package/dist/client/assets/chunk-SVP7TREG-BvQafCMJ.js.gz +0 -0
  97. package/dist/client/assets/{chunk-TICWLB2K-DZjDfYNw.js → chunk-TICWLB2K-CQXPwGBz.js} +1 -1
  98. package/dist/client/assets/chunk-TICWLB2K-CQXPwGBz.js.br +0 -0
  99. package/dist/client/assets/chunk-TICWLB2K-CQXPwGBz.js.gz +0 -0
  100. package/dist/client/assets/{chunk-XXDRQBXY-CtGPyXWh.js → chunk-XXDRQBXY-CmwZc4xV.js} +1 -1
  101. package/dist/client/assets/classDiagram-ZZMXUADV-D0oXVFxd.js +1 -0
  102. package/dist/client/assets/classDiagram-v2-VYDZK3BY-D0oXVFxd.js +1 -0
  103. package/dist/client/assets/clipboard-uSv3AiJM.js +1 -0
  104. package/dist/client/assets/clipboardText-5puxqqm_.js +1 -0
  105. package/dist/client/assets/clipboardText-5puxqqm_.js.br +0 -0
  106. package/dist/client/assets/clipboardText-5puxqqm_.js.gz +0 -0
  107. package/dist/client/assets/{cose-bilkent-JH36ORCC-C4Wb5lqD.js → cose-bilkent-JH36ORCC-CqWarkUj.js} +1 -1
  108. package/dist/client/assets/cose-bilkent-JH36ORCC-CqWarkUj.js.br +0 -0
  109. package/dist/client/assets/cose-bilkent-JH36ORCC-CqWarkUj.js.gz +0 -0
  110. package/dist/client/assets/{cynefin-OW5HDTMX-BvWKB1Xk.js → cynefin-OW5HDTMX-CFW2ifig.js} +1 -1
  111. package/dist/client/assets/cynefin-OW5HDTMX-CFW2ifig.js.br +0 -0
  112. package/dist/client/assets/cynefin-OW5HDTMX-CFW2ifig.js.gz +0 -0
  113. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-7tAkHD2P.js → cynefinDiagram-5FMLGOSQ-yggxM55S.js} +1 -1
  114. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-yggxM55S.js.br +0 -0
  115. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-yggxM55S.js.gz +0 -0
  116. package/dist/client/assets/{dagre-GXQ25YYZ-Bs6IPwks.js → dagre-GXQ25YYZ-inq7o--x.js} +1 -1
  117. package/dist/client/assets/dagre-GXQ25YYZ-inq7o--x.js.br +0 -0
  118. package/dist/client/assets/dagre-GXQ25YYZ-inq7o--x.js.gz +0 -0
  119. package/dist/client/assets/{diagram-S7CK7UJ4-CmYXaTya.js → diagram-S7CK7UJ4-BUXMnyQN.js} +1 -1
  120. package/dist/client/assets/diagram-S7CK7UJ4-BUXMnyQN.js.br +0 -0
  121. package/dist/client/assets/diagram-S7CK7UJ4-BUXMnyQN.js.gz +0 -0
  122. package/dist/client/assets/{diagram-UQ7AKVKN-CqxoefB7.js → diagram-UQ7AKVKN-BJbw-Imf.js} +1 -1
  123. package/dist/client/assets/diagram-UQ7AKVKN-BJbw-Imf.js.br +0 -0
  124. package/dist/client/assets/diagram-UQ7AKVKN-BJbw-Imf.js.gz +0 -0
  125. package/dist/client/assets/{diagram-VSXAHHWV-Dxp1MKPT.js → diagram-VSXAHHWV-CSfl8yS7.js} +1 -1
  126. package/dist/client/assets/diagram-VSXAHHWV-CSfl8yS7.js.br +0 -0
  127. package/dist/client/assets/diagram-VSXAHHWV-CSfl8yS7.js.gz +0 -0
  128. package/dist/client/assets/{diagram-VX7I27RA-BKbWvvEr.js → diagram-VX7I27RA-zV-Vrv89.js} +1 -1
  129. package/dist/client/assets/diagram-VX7I27RA-zV-Vrv89.js.br +0 -0
  130. package/dist/client/assets/{diagram-VX7I27RA-BKbWvvEr.js.gz → diagram-VX7I27RA-zV-Vrv89.js.gz} +0 -0
  131. package/dist/client/assets/{diagram-Z3DM3KII-BZnNaP7F.js → diagram-Z3DM3KII-CRnE-snw.js} +1 -1
  132. package/dist/client/assets/diagram-Z3DM3KII-CRnE-snw.js.br +0 -0
  133. package/dist/client/assets/diagram-Z3DM3KII-CRnE-snw.js.gz +0 -0
  134. package/dist/client/assets/{ebnfDiagram-PWID7BFC-BXVlN_P7.js → ebnfDiagram-PWID7BFC-CE30oImc.js} +1 -1
  135. package/dist/client/assets/ebnfDiagram-PWID7BFC-CE30oImc.js.br +0 -0
  136. package/dist/client/assets/ebnfDiagram-PWID7BFC-CE30oImc.js.gz +0 -0
  137. package/dist/client/assets/{erDiagram-RLTQ6QDP-CEdYDZU7.js → erDiagram-RLTQ6QDP-D-5bIySI.js} +1 -1
  138. package/dist/client/assets/erDiagram-RLTQ6QDP-D-5bIySI.js.br +0 -0
  139. package/dist/client/assets/erDiagram-RLTQ6QDP-D-5bIySI.js.gz +0 -0
  140. package/dist/client/assets/{flowDiagram-HODETNUW-Bbw8GPCZ.js → flowDiagram-HODETNUW-E_jPKtQR.js} +1 -1
  141. package/dist/client/assets/flowDiagram-HODETNUW-E_jPKtQR.js.br +0 -0
  142. package/dist/client/assets/flowDiagram-HODETNUW-E_jPKtQR.js.gz +0 -0
  143. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CcFbtP2y.js → ganttDiagram-EL5Y4UJY-BWvLOUzC.js} +1 -1
  144. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BWvLOUzC.js.br +0 -0
  145. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BWvLOUzC.js.gz +0 -0
  146. package/dist/client/assets/gestureArbiter-DszagC-d.js +1 -0
  147. package/dist/client/assets/gestureArbiter-DszagC-d.js.br +0 -0
  148. package/dist/client/assets/gestureArbiter-DszagC-d.js.gz +0 -0
  149. package/dist/client/assets/git-compare-BlcHEuBI.js +1 -0
  150. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-Eiust154.js → gitGraphDiagram-WWUBYQGX-nOlVCBmL.js} +1 -1
  151. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-nOlVCBmL.js.br +0 -0
  152. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-nOlVCBmL.js.gz +0 -0
  153. package/dist/client/assets/index-CA1fR_jI.css +1 -0
  154. package/dist/client/assets/index-CA1fR_jI.css.br +0 -0
  155. package/dist/client/assets/index-CA1fR_jI.css.gz +0 -0
  156. package/dist/client/assets/{infoDiagram-27XIBGKW-DUmaNZJW.js → infoDiagram-27XIBGKW-CjVm9Lzz.js} +1 -1
  157. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-qTwgXfx_.js → ishikawaDiagram-5VMMS53U-DmVMaGVp.js} +1 -1
  158. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-qTwgXfx_.js.br → ishikawaDiagram-5VMMS53U-DmVMaGVp.js.br} +0 -0
  159. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DmVMaGVp.js.gz +0 -0
  160. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DKy1msWo.js → journeyDiagram-3NMN7TZE-DLwkoAy9.js} +1 -1
  161. package/dist/client/assets/journeyDiagram-3NMN7TZE-DLwkoAy9.js.br +0 -0
  162. package/dist/client/assets/journeyDiagram-3NMN7TZE-DLwkoAy9.js.gz +0 -0
  163. package/dist/client/assets/{kanban-definition-UXKFOSKX-BxibybDd.js → kanban-definition-UXKFOSKX-GWkp_QiO.js} +1 -1
  164. package/dist/client/assets/kanban-definition-UXKFOSKX-GWkp_QiO.js.br +0 -0
  165. package/dist/client/assets/kanban-definition-UXKFOSKX-GWkp_QiO.js.gz +0 -0
  166. package/dist/client/assets/{layout-Bh6fKHRM.js → layout-DFg5a61y.js} +1 -1
  167. package/dist/client/assets/layout-DFg5a61y.js.br +0 -0
  168. package/dist/client/assets/layout-DFg5a61y.js.gz +0 -0
  169. package/dist/client/assets/{linear-SRlL3VWX.js → linear-B0OdJLe1.js} +1 -1
  170. package/dist/client/assets/{linear-SRlL3VWX.js.br → linear-B0OdJLe1.js.br} +0 -0
  171. package/dist/client/assets/linear-B0OdJLe1.js.gz +0 -0
  172. package/dist/client/assets/link-BnxdKJ7b.js +8 -0
  173. package/dist/client/assets/link-BnxdKJ7b.js.br +0 -0
  174. package/dist/client/assets/link-BnxdKJ7b.js.gz +0 -0
  175. package/dist/client/assets/list-tree-Bou8VSzi.js +1 -0
  176. package/dist/client/assets/loader-circle-q7h8uwFI.js +1 -0
  177. package/dist/client/assets/maximize-2-BGTBw-Ku.js +1 -0
  178. package/dist/client/assets/{mermaid.core-D0bcNdw3.js → mermaid.core-DQA3ykXv.js} +5 -5
  179. package/dist/client/assets/mermaid.core-DQA3ykXv.js.br +0 -0
  180. package/dist/client/assets/mermaid.core-DQA3ykXv.js.gz +0 -0
  181. package/dist/client/assets/{mindmap-definition-YA3MSWOX-CImZ_aI0.js → mindmap-definition-YA3MSWOX-DsZAVAGP.js} +2 -2
  182. package/dist/client/assets/mindmap-definition-YA3MSWOX-DsZAVAGP.js.br +0 -0
  183. package/dist/client/assets/mindmap-definition-YA3MSWOX-DsZAVAGP.js.gz +0 -0
  184. package/dist/client/assets/passwordBootstrap-CEZSL94g.js +2 -0
  185. package/dist/client/assets/passwordBootstrap-CEZSL94g.js.br +0 -0
  186. package/dist/client/assets/passwordBootstrap-CEZSL94g.js.gz +0 -0
  187. package/dist/client/assets/{pegDiagram-XKGWAZYB-BT4pTf8O.js → pegDiagram-XKGWAZYB-C3ar01Lf.js} +1 -1
  188. package/dist/client/assets/pegDiagram-XKGWAZYB-C3ar01Lf.js.br +0 -0
  189. package/dist/client/assets/pegDiagram-XKGWAZYB-C3ar01Lf.js.gz +0 -0
  190. package/dist/client/assets/{pieDiagram-E7YTZNPT-C5K58ien.js → pieDiagram-E7YTZNPT-C8LIa9gE.js} +1 -1
  191. package/dist/client/assets/pieDiagram-E7YTZNPT-C8LIa9gE.js.br +0 -0
  192. package/dist/client/assets/pieDiagram-E7YTZNPT-C8LIa9gE.js.gz +0 -0
  193. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-DzSugYXz.js → quadrantDiagram-AXDQQJYC-BuRJ1f9F.js} +1 -1
  194. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BuRJ1f9F.js.br +0 -0
  195. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BuRJ1f9F.js.gz +0 -0
  196. package/dist/client/assets/quote-qXNI_NTi.js +1 -0
  197. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BY07SouQ.js → railroadDiagram-O6MQD6OU-D7iZy3dO.js} +1 -1
  198. package/dist/client/assets/railroadDiagram-O6MQD6OU-D7iZy3dO.js.br +0 -0
  199. package/dist/client/assets/railroadDiagram-O6MQD6OU-D7iZy3dO.js.gz +0 -0
  200. package/dist/client/assets/rdpSession-DUBlG26K.js +1 -0
  201. package/dist/client/assets/rdpSession-DUBlG26K.js.br +0 -0
  202. package/dist/client/assets/rdpSession-DUBlG26K.js.gz +0 -0
  203. package/dist/client/assets/{requirementDiagram-BXWQKSXE-C7WieVuE.js → requirementDiagram-BXWQKSXE-DhY8g8qK.js} +1 -1
  204. package/dist/client/assets/requirementDiagram-BXWQKSXE-DhY8g8qK.js.br +0 -0
  205. package/dist/client/assets/requirementDiagram-BXWQKSXE-DhY8g8qK.js.gz +0 -0
  206. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-BJsDWNs4.js → sankeyDiagram-P5KCCOFB-DDA_LQcg.js} +1 -1
  207. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DDA_LQcg.js.br +0 -0
  208. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DDA_LQcg.js.gz +0 -0
  209. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BeJ_7853.js → sequenceDiagram-WJ2MYXX4-bWgfdfwY.js} +1 -1
  210. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BeJ_7853.js.br → sequenceDiagram-WJ2MYXX4-bWgfdfwY.js.br} +0 -0
  211. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-bWgfdfwY.js.gz +0 -0
  212. package/dist/client/assets/settings-2-B-sr3wQM.js +1 -0
  213. package/dist/client/assets/{sizeCapture-INFHLROL-DOsjXKPV.js → sizeCapture-INFHLROL-BYxi7e0R.js} +1 -1
  214. package/dist/client/assets/sizeCapture-INFHLROL-BYxi7e0R.js.br +0 -0
  215. package/dist/client/assets/sizeCapture-INFHLROL-BYxi7e0R.js.gz +0 -0
  216. package/dist/client/assets/{sparkles-e9SkpP7a.js → sparkles-CCLflSFy.js} +1 -1
  217. package/dist/client/assets/{stateDiagram-D77RDMKH-6s709_RT.js → stateDiagram-D77RDMKH-6vqC2h36.js} +1 -1
  218. package/dist/client/assets/stateDiagram-D77RDMKH-6vqC2h36.js.br +0 -0
  219. package/dist/client/assets/stateDiagram-D77RDMKH-6vqC2h36.js.gz +0 -0
  220. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-Bb23lB9W.js +1 -0
  221. package/dist/client/assets/sun-B9_TfxlA.js +1 -0
  222. package/dist/client/assets/{swimlanes-42K2YHIH-5R5dw9_R.js → swimlanes-42K2YHIH-B4qrXam7.js} +1 -1
  223. package/dist/client/assets/swimlanes-42K2YHIH-B4qrXam7.js.br +0 -0
  224. package/dist/client/assets/swimlanes-42K2YHIH-B4qrXam7.js.gz +0 -0
  225. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CQ1XJUVU.js +8 -0
  226. package/dist/client/assets/swiper-DJjq1TYT.js +1 -0
  227. package/dist/client/assets/swiper-DJjq1TYT.js.br +0 -0
  228. package/dist/client/assets/swiper-DJjq1TYT.js.gz +0 -0
  229. package/dist/client/assets/syntaxHighlight-bcOB6oww.js +4 -0
  230. package/dist/client/assets/syntaxHighlight-bcOB6oww.js.br +0 -0
  231. package/dist/client/assets/syntaxHighlight-bcOB6oww.js.gz +0 -0
  232. package/dist/client/assets/{timeline-definition-24CTP7MA-B4YN0lsx.js → timeline-definition-24CTP7MA-D0_004MF.js} +1 -1
  233. package/dist/client/assets/timeline-definition-24CTP7MA-D0_004MF.js.br +0 -0
  234. package/dist/client/assets/timeline-definition-24CTP7MA-D0_004MF.js.gz +0 -0
  235. package/dist/client/assets/triangle-alert-D3w7mA17.js +1 -0
  236. package/dist/client/assets/unplug-BSblgecr.js +1 -0
  237. package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js +1 -0
  238. package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js.br +0 -0
  239. package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js.gz +0 -0
  240. package/dist/client/assets/v4-EwEgHOG0.js +1 -0
  241. package/dist/client/assets/{vennDiagram-4TSXK5OY-Wd83SmWI.js → vennDiagram-4TSXK5OY-Bqrx-47p.js} +1 -1
  242. package/dist/client/assets/vennDiagram-4TSXK5OY-Bqrx-47p.js.br +0 -0
  243. package/dist/client/assets/vennDiagram-4TSXK5OY-Bqrx-47p.js.gz +0 -0
  244. package/dist/client/assets/videoCompression-df1Ej3AR.js +3 -0
  245. package/dist/client/assets/videoCompression-df1Ej3AR.js.br +0 -0
  246. package/dist/client/assets/videoCompression-df1Ej3AR.js.gz +0 -0
  247. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-CGsYvqWN.js → wardleyDiagram-VM6X3IG4-C4LCUdfd.js} +1 -1
  248. package/dist/client/assets/wardleyDiagram-VM6X3IG4-C4LCUdfd.js.br +0 -0
  249. package/dist/client/assets/wardleyDiagram-VM6X3IG4-C4LCUdfd.js.gz +0 -0
  250. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-ednJq1_u.js → xychartDiagram-S5SC5T6Z-E5AQcZVI.js} +1 -1
  251. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-ednJq1_u.js.br → xychartDiagram-S5SC5T6Z-E5AQcZVI.js.br} +0 -0
  252. package/dist/client/assets/xychartDiagram-S5SC5T6Z-E5AQcZVI.js.gz +0 -0
  253. package/dist/client/index.html +2 -2
  254. package/dist/client/index.html.br +0 -0
  255. package/dist/client/index.html.gz +0 -0
  256. package/dist/client/sw-encrypted-background.js +1 -1
  257. package/dist/client/sw-encrypted-background.js.br +0 -0
  258. package/dist/client/sw-encrypted-background.js.gz +0 -0
  259. package/dist/client/sw.js +1 -1
  260. package/dist/client/sw.js.br +0 -0
  261. package/dist/client/sw.js.gz +0 -0
  262. package/dist/server/agent/collaborationDeliveryWorker.js +11 -0
  263. package/dist/server/agent/collaborationPrompt.js +4 -2
  264. package/dist/server/agent/collaborationRoutes.js +2 -0
  265. package/dist/server/agent/collaborationStore.js +7 -1
  266. package/dist/server/agent/sessionAttachmentRoutes.js +39 -0
  267. package/dist/server/agent/sessionChatRoutes.js +64 -0
  268. package/dist/server/agent/sessionViewModeRoutes.js +72 -0
  269. package/dist/server/android/recording.js +19 -17
  270. package/dist/server/cli.js +34 -0
  271. package/dist/server/entry.js +26 -17
  272. package/dist/server/federation/accessPolicy.js +8 -3
  273. package/dist/server/federation/authorization.js +26 -3
  274. package/dist/server/federation/invitations.js +27 -6
  275. package/dist/server/federation/relay.js +21 -3
  276. package/dist/server/federation/requestContext.js +5 -1
  277. package/dist/server/federation/resourceAccess.js +63 -0
  278. package/dist/server/federation/runtime.js +67 -18
  279. package/dist/server/federation/sessionSharing.js +39 -0
  280. package/dist/server/federation/socketDuplex.js +3 -3
  281. package/dist/server/routes/android.js +36 -7
  282. package/dist/server/routes/computer.js +14 -4
  283. package/dist/server/routes/filesystem.js +6 -3
  284. package/dist/server/routes/rdp.js +4 -3
  285. package/dist/server/routes/sharedFiles.js +51 -0
  286. package/dist/server/routes/terminal.js +83 -14
  287. package/dist/server/routes/vncReadOnly.js +134 -0
  288. package/dist/server/shared/shareResources.js +37 -0
  289. package/dist/server/todos/cli.js +91 -0
  290. package/dist/server/todos/routes.js +73 -0
  291. package/dist/server/todos/store.js +240 -0
  292. package/dist/server/todos/types.js +1 -0
  293. package/dist/server/utils/autoUpdate.js +7 -2
  294. package/dist/server/utils/clientSnapshotMaintenance.js +200 -0
  295. package/dist/server/utils/runtimeClient.js +79 -0
  296. package/dist/server/utils/serverCleanup.js +24 -0
  297. package/dist/server/utils/storageMaintenance.js +60 -0
  298. package/dist/server/utils/version.js +2 -0
  299. package/dist/server/utils/workspaceCacheMaintenance.js +158 -0
  300. package/docs/integrations.md +23 -0
  301. package/package.json +1 -1
  302. package/runtime-manifest.json +3 -3
  303. package/dist/client/assets/App-TPJoJbGD.js +0 -187
  304. package/dist/client/assets/App-TPJoJbGD.js.br +0 -0
  305. package/dist/client/assets/App-TPJoJbGD.js.gz +0 -0
  306. package/dist/client/assets/ArchitecturePanel-DzVy71gM.js +0 -9
  307. package/dist/client/assets/ArchitecturePanel-DzVy71gM.js.br +0 -0
  308. package/dist/client/assets/ArchitecturePanel-DzVy71gM.js.gz +0 -0
  309. package/dist/client/assets/ChangeWalkthroughPanel-DN7BCzqC.js +0 -2
  310. package/dist/client/assets/ChangeWalkthroughPanel-DN7BCzqC.js.br +0 -0
  311. package/dist/client/assets/ChangeWalkthroughPanel-DN7BCzqC.js.gz +0 -0
  312. package/dist/client/assets/CollaborationMainWorkspace-B1PZuCC6.js +0 -1
  313. package/dist/client/assets/CollaborationMainWorkspace-B1PZuCC6.js.br +0 -0
  314. package/dist/client/assets/CollaborationMainWorkspace-B1PZuCC6.js.gz +0 -0
  315. package/dist/client/assets/CollaborationTaskContent-CG7g1LMH.js +0 -2
  316. package/dist/client/assets/ComputerControlView-BlI9Ai_k.js +0 -6
  317. package/dist/client/assets/ComputerControlView-BlI9Ai_k.js.br +0 -0
  318. package/dist/client/assets/ComputerControlView-BlI9Ai_k.js.gz +0 -0
  319. package/dist/client/assets/DagPlayground-V0hTKB-S.js +0 -1
  320. package/dist/client/assets/DagPlayground-V0hTKB-S.js.br +0 -0
  321. package/dist/client/assets/DagPlayground-V0hTKB-S.js.gz +0 -0
  322. package/dist/client/assets/DiffLab-DEw4y4Hk.js.br +0 -0
  323. package/dist/client/assets/DiffLab-DEw4y4Hk.js.gz +0 -0
  324. package/dist/client/assets/DiffReview-Cnnt3TMK.js +0 -1
  325. package/dist/client/assets/DiffReview-Cnnt3TMK.js.br +0 -0
  326. package/dist/client/assets/DiffReview-Cnnt3TMK.js.gz +0 -0
  327. package/dist/client/assets/DiffReviewLab-CDXsy0jE.js.br +0 -0
  328. package/dist/client/assets/DiffReviewLab-CDXsy0jE.js.gz +0 -0
  329. package/dist/client/assets/DiffViewer-Dh40XiCM.js +0 -37
  330. package/dist/client/assets/DiffViewer-Dh40XiCM.js.br +0 -0
  331. package/dist/client/assets/DiffViewer-Dh40XiCM.js.gz +0 -0
  332. package/dist/client/assets/ModelPreview-BEiOgE0G.js.br +0 -0
  333. package/dist/client/assets/ModelPreview-BEiOgE0G.js.gz +0 -0
  334. package/dist/client/assets/RightSidebar-DiECnX5D.js +0 -1679
  335. package/dist/client/assets/RightSidebar-DiECnX5D.js.br +0 -0
  336. package/dist/client/assets/RightSidebar-DiECnX5D.js.gz +0 -0
  337. package/dist/client/assets/abnfDiagram-VCTEODGH-ByzlUu_B.js.br +0 -0
  338. package/dist/client/assets/abnfDiagram-VCTEODGH-ByzlUu_B.js.gz +0 -0
  339. package/dist/client/assets/app-CKAFlgMD.js +0 -35
  340. package/dist/client/assets/app-CKAFlgMD.js.br +0 -0
  341. package/dist/client/assets/app-CKAFlgMD.js.gz +0 -0
  342. package/dist/client/assets/arc-BA6JM6vl.js.br +0 -0
  343. package/dist/client/assets/arc-BA6JM6vl.js.gz +0 -0
  344. package/dist/client/assets/architectureDiagram-5GKGNRK7-hCHxOfKw.js.br +0 -0
  345. package/dist/client/assets/blockDiagram-I7D4REHJ-OmEN9hUB.js.br +0 -0
  346. package/dist/client/assets/blockDiagram-I7D4REHJ-OmEN9hUB.js.gz +0 -0
  347. package/dist/client/assets/c4Diagram-7LVT6UL2-DfLTv8zh.js.br +0 -0
  348. package/dist/client/assets/c4Diagram-7LVT6UL2-DfLTv8zh.js.gz +0 -0
  349. package/dist/client/assets/channel-gcmN0N0m.js +0 -1
  350. package/dist/client/assets/chunk-F27PBJKO-yxCv2Uqk.js.br +0 -0
  351. package/dist/client/assets/chunk-F27PBJKO-yxCv2Uqk.js.gz +0 -0
  352. package/dist/client/assets/chunk-IMKFNOWR-CdPjlb9f.js.br +0 -0
  353. package/dist/client/assets/chunk-IMKFNOWR-CdPjlb9f.js.gz +0 -0
  354. package/dist/client/assets/chunk-SVP7TREG-D1tOJung.js.br +0 -0
  355. package/dist/client/assets/chunk-SVP7TREG-D1tOJung.js.gz +0 -0
  356. package/dist/client/assets/chunk-TICWLB2K-DZjDfYNw.js.br +0 -0
  357. package/dist/client/assets/chunk-TICWLB2K-DZjDfYNw.js.gz +0 -0
  358. package/dist/client/assets/classDiagram-ZZMXUADV-k4BRymfG.js +0 -1
  359. package/dist/client/assets/classDiagram-v2-VYDZK3BY-k4BRymfG.js +0 -1
  360. package/dist/client/assets/cose-bilkent-JH36ORCC-C4Wb5lqD.js.br +0 -0
  361. package/dist/client/assets/cose-bilkent-JH36ORCC-C4Wb5lqD.js.gz +0 -0
  362. package/dist/client/assets/cynefin-OW5HDTMX-BvWKB1Xk.js.br +0 -0
  363. package/dist/client/assets/cynefin-OW5HDTMX-BvWKB1Xk.js.gz +0 -0
  364. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-7tAkHD2P.js.br +0 -0
  365. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-7tAkHD2P.js.gz +0 -0
  366. package/dist/client/assets/dagre-GXQ25YYZ-Bs6IPwks.js.br +0 -0
  367. package/dist/client/assets/dagre-GXQ25YYZ-Bs6IPwks.js.gz +0 -0
  368. package/dist/client/assets/diagram-S7CK7UJ4-CmYXaTya.js.br +0 -0
  369. package/dist/client/assets/diagram-S7CK7UJ4-CmYXaTya.js.gz +0 -0
  370. package/dist/client/assets/diagram-UQ7AKVKN-CqxoefB7.js.br +0 -0
  371. package/dist/client/assets/diagram-UQ7AKVKN-CqxoefB7.js.gz +0 -0
  372. package/dist/client/assets/diagram-VSXAHHWV-Dxp1MKPT.js.br +0 -0
  373. package/dist/client/assets/diagram-VSXAHHWV-Dxp1MKPT.js.gz +0 -0
  374. package/dist/client/assets/diagram-VX7I27RA-BKbWvvEr.js.br +0 -0
  375. package/dist/client/assets/diagram-Z3DM3KII-BZnNaP7F.js.br +0 -0
  376. package/dist/client/assets/diagram-Z3DM3KII-BZnNaP7F.js.gz +0 -0
  377. package/dist/client/assets/ebnfDiagram-PWID7BFC-BXVlN_P7.js.br +0 -0
  378. package/dist/client/assets/ebnfDiagram-PWID7BFC-BXVlN_P7.js.gz +0 -0
  379. package/dist/client/assets/erDiagram-RLTQ6QDP-CEdYDZU7.js.br +0 -0
  380. package/dist/client/assets/erDiagram-RLTQ6QDP-CEdYDZU7.js.gz +0 -0
  381. package/dist/client/assets/flowDiagram-HODETNUW-Bbw8GPCZ.js.br +0 -0
  382. package/dist/client/assets/flowDiagram-HODETNUW-Bbw8GPCZ.js.gz +0 -0
  383. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CcFbtP2y.js.br +0 -0
  384. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CcFbtP2y.js.gz +0 -0
  385. package/dist/client/assets/gestureArbiter-sdzs-66-.js +0 -1
  386. package/dist/client/assets/gestureArbiter-sdzs-66-.js.br +0 -0
  387. package/dist/client/assets/gestureArbiter-sdzs-66-.js.gz +0 -0
  388. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-Eiust154.js.br +0 -0
  389. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-Eiust154.js.gz +0 -0
  390. package/dist/client/assets/index-DtPsPZ7W.css +0 -1
  391. package/dist/client/assets/index-DtPsPZ7W.css.br +0 -0
  392. package/dist/client/assets/index-DtPsPZ7W.css.gz +0 -0
  393. package/dist/client/assets/ishikawaDiagram-5VMMS53U-qTwgXfx_.js.gz +0 -0
  394. package/dist/client/assets/journeyDiagram-3NMN7TZE-DKy1msWo.js.br +0 -0
  395. package/dist/client/assets/journeyDiagram-3NMN7TZE-DKy1msWo.js.gz +0 -0
  396. package/dist/client/assets/kanban-definition-UXKFOSKX-BxibybDd.js.br +0 -0
  397. package/dist/client/assets/kanban-definition-UXKFOSKX-BxibybDd.js.gz +0 -0
  398. package/dist/client/assets/layout-Bh6fKHRM.js.br +0 -0
  399. package/dist/client/assets/layout-Bh6fKHRM.js.gz +0 -0
  400. package/dist/client/assets/linear-SRlL3VWX.js.gz +0 -0
  401. package/dist/client/assets/mermaid.core-D0bcNdw3.js.br +0 -0
  402. package/dist/client/assets/mermaid.core-D0bcNdw3.js.gz +0 -0
  403. package/dist/client/assets/mindmap-definition-YA3MSWOX-CImZ_aI0.js.br +0 -0
  404. package/dist/client/assets/mindmap-definition-YA3MSWOX-CImZ_aI0.js.gz +0 -0
  405. package/dist/client/assets/passwordBootstrap-f7-mFb1b.js +0 -2
  406. package/dist/client/assets/passwordBootstrap-f7-mFb1b.js.br +0 -0
  407. package/dist/client/assets/passwordBootstrap-f7-mFb1b.js.gz +0 -0
  408. package/dist/client/assets/pegDiagram-XKGWAZYB-BT4pTf8O.js.br +0 -0
  409. package/dist/client/assets/pegDiagram-XKGWAZYB-BT4pTf8O.js.gz +0 -0
  410. package/dist/client/assets/pieDiagram-E7YTZNPT-C5K58ien.js.br +0 -0
  411. package/dist/client/assets/pieDiagram-E7YTZNPT-C5K58ien.js.gz +0 -0
  412. package/dist/client/assets/quadrantDiagram-AXDQQJYC-DzSugYXz.js.br +0 -0
  413. package/dist/client/assets/quadrantDiagram-AXDQQJYC-DzSugYXz.js.gz +0 -0
  414. package/dist/client/assets/railroadDiagram-O6MQD6OU-BY07SouQ.js.br +0 -0
  415. package/dist/client/assets/railroadDiagram-O6MQD6OU-BY07SouQ.js.gz +0 -0
  416. package/dist/client/assets/rdpSession-RNjO6yLw.js +0 -1
  417. package/dist/client/assets/rdpSession-RNjO6yLw.js.br +0 -0
  418. package/dist/client/assets/rdpSession-RNjO6yLw.js.gz +0 -0
  419. package/dist/client/assets/referenceLongPress-B9K9yPKR.js +0 -1
  420. package/dist/client/assets/referenceLongPress-B9K9yPKR.js.br +0 -0
  421. package/dist/client/assets/referenceLongPress-B9K9yPKR.js.gz +0 -0
  422. package/dist/client/assets/requirementDiagram-BXWQKSXE-C7WieVuE.js.br +0 -0
  423. package/dist/client/assets/requirementDiagram-BXWQKSXE-C7WieVuE.js.gz +0 -0
  424. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BJsDWNs4.js.br +0 -0
  425. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BJsDWNs4.js.gz +0 -0
  426. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BeJ_7853.js.gz +0 -0
  427. package/dist/client/assets/sizeCapture-INFHLROL-DOsjXKPV.js.br +0 -0
  428. package/dist/client/assets/sizeCapture-INFHLROL-DOsjXKPV.js.gz +0 -0
  429. package/dist/client/assets/stateDiagram-D77RDMKH-6s709_RT.js.br +0 -0
  430. package/dist/client/assets/stateDiagram-D77RDMKH-6s709_RT.js.gz +0 -0
  431. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-B43A9Hsz.js +0 -1
  432. package/dist/client/assets/swimlanes-42K2YHIH-5R5dw9_R.js.br +0 -0
  433. package/dist/client/assets/swimlanes-42K2YHIH-5R5dw9_R.js.gz +0 -0
  434. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DoFbqN_4.js +0 -8
  435. package/dist/client/assets/timeline-definition-24CTP7MA-B4YN0lsx.js.br +0 -0
  436. package/dist/client/assets/timeline-definition-24CTP7MA-B4YN0lsx.js.gz +0 -0
  437. package/dist/client/assets/triangle-alert-BzBDJvJX.js +0 -1
  438. package/dist/client/assets/vennDiagram-4TSXK5OY-Wd83SmWI.js.br +0 -0
  439. package/dist/client/assets/vennDiagram-4TSXK5OY-Wd83SmWI.js.gz +0 -0
  440. package/dist/client/assets/videoCompression-Bsw--tMn.js +0 -3
  441. package/dist/client/assets/videoCompression-Bsw--tMn.js.br +0 -0
  442. package/dist/client/assets/videoCompression-Bsw--tMn.js.gz +0 -0
  443. package/dist/client/assets/wardleyDiagram-VM6X3IG4-CGsYvqWN.js.br +0 -0
  444. package/dist/client/assets/wardleyDiagram-VM6X3IG4-CGsYvqWN.js.gz +0 -0
  445. package/dist/client/assets/xychartDiagram-S5SC5T6Z-ednJq1_u.js.gz +0 -0
  446. /package/dist/client/assets/{gestureArbiter-C2J0D0kV.css → swiper-C2J0D0kV.css} +0 -0
  447. /package/dist/client/assets/{gestureArbiter-C2J0D0kV.css.br → swiper-C2J0D0kV.css.br} +0 -0
  448. /package/dist/client/assets/{gestureArbiter-C2J0D0kV.css.gz → swiper-C2J0D0kV.css.gz} +0 -0
@@ -1,3 +1,5 @@
1
+ import { encryptedRequestResources, encryptedRequestSubject, hasEncryptedServiceAccess } from '../federation/requestContext.js';
2
+ import { directoryResource } from '../federation/resourceAccess.js';
1
3
  import { Router } from 'express';
2
4
  import { connectDevice, disconnectDevice, listDevices, resolveAdbBinary } from '../android/adb.js';
3
5
  import { createScrcpySession, resolveScrcpyBinaries } from '../android/scrcpy.js';
@@ -10,7 +12,7 @@ const DEBUG_ANDROID = process.env.TERMDOCK_ANDROID_DEBUG === '1';
10
12
  export function isValidAndroidSerial(serial) {
11
13
  return serial.length <= 128 && SERIAL_PATTERN.test(serial);
12
14
  }
13
- router.get('/devices', async (_req, res) => {
15
+ router.get('/devices', async (req, res) => {
14
16
  try {
15
17
  const devices = await listDevices();
16
18
  let scrcpyVersion = null;
@@ -21,7 +23,8 @@ router.get('/devices', async (_req, res) => {
21
23
  catch {
22
24
  scrcpyVersion = null;
23
25
  }
24
- res.json({ adbAvailable, scrcpyVersion, devices });
26
+ const shared = encryptedRequestResources(req);
27
+ res.json({ adbAvailable, scrcpyVersion, devices: shared && !hasEncryptedServiceAccess(req) ? devices.filter(d => shared.android?.some(a => a.serial === d.serial)) : devices });
25
28
  }
26
29
  catch (error) {
27
30
  res.status(503).json({ error: error instanceof Error ? error.message : 'ADB_UNAVAILABLE', code: 'ADB_UNAVAILABLE' });
@@ -48,6 +51,32 @@ router.post('/disconnect', async (req, res) => {
48
51
  res.status(400).json({ error: message, code: message });
49
52
  }
50
53
  });
54
+ const recordingOwners = new WeakMap();
55
+ router.use('/recordings', async (req, res, next) => {
56
+ const resources = encryptedRequestResources(req);
57
+ if (!resources || hasEncryptedServiceAccess(req)) {
58
+ next();
59
+ return;
60
+ }
61
+ const subject = encryptedRequestSubject(req);
62
+ if (!subject) {
63
+ res.sendStatus(403);
64
+ return;
65
+ }
66
+ try {
67
+ const id = /^\/([^/]+)(?:\/(?:stop|save))?$/.exec(req.path)?.[1];
68
+ const serial = id ? (await androidRecordings.info(id, subject)).serial : req.method === 'GET' ? req.query.serial : req.body?.serial;
69
+ if (typeof serial !== 'string' || !resources.android?.some(device => device.serial === serial))
70
+ throw new Error('PHONE_NOT_SHARED');
71
+ if (req.body?.directory !== undefined && (typeof req.body.directory !== 'string' || !directoryResource(resources, req.body.directory, true)))
72
+ throw new Error('DIRECTORY_NOT_SHARED');
73
+ recordingOwners.set(req, subject);
74
+ next();
75
+ }
76
+ catch {
77
+ res.status(403).json({ error: '此手机或录像未获授权', code: 'SHARE_ACCESS_DENIED' });
78
+ }
79
+ });
51
80
  router.get('/recordings', async (req, res) => {
52
81
  const serial = typeof req.query.serial === 'string' ? req.query.serial : '';
53
82
  if (!isValidAndroidSerial(serial)) {
@@ -55,7 +84,7 @@ router.get('/recordings', async (req, res) => {
55
84
  return;
56
85
  }
57
86
  try {
58
- res.json({ recordings: await androidRecordings.list(serial) });
87
+ res.json({ recordings: await androidRecordings.list(serial, recordingOwners.get(req)) });
59
88
  }
60
89
  catch (error) {
61
90
  res.status(500).json({ error: String(error) });
@@ -68,7 +97,7 @@ router.post('/recordings', async (req, res) => {
68
97
  return;
69
98
  }
70
99
  try {
71
- res.json(await androidRecordings.start(serial));
100
+ res.json(await androidRecordings.start(serial, recordingOwners.get(req)));
72
101
  }
73
102
  catch (error) {
74
103
  res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
@@ -76,7 +105,7 @@ router.post('/recordings', async (req, res) => {
76
105
  });
77
106
  router.post('/recordings/:id/stop', async (req, res) => {
78
107
  try {
79
- res.json(await androidRecordings.stop(req.params.id));
108
+ res.json(await androidRecordings.stop(req.params.id, recordingOwners.get(req)));
80
109
  }
81
110
  catch (error) {
82
111
  res.status(400).json({ error: String(error) });
@@ -86,7 +115,7 @@ router.post('/recordings/:id/save', async (req, res) => {
86
115
  try {
87
116
  const directory = req.body?.directory === undefined ? undefined
88
117
  : await (req.pathValidator ?? pathValidator).validateAsync(req.body.directory);
89
- res.json({ path: await androidRecordings.save(req.params.id, directory) });
118
+ res.json({ path: await androidRecordings.save(req.params.id, directory, recordingOwners.get(req)) });
90
119
  }
91
120
  catch (error) {
92
121
  res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
@@ -94,7 +123,7 @@ router.post('/recordings/:id/save', async (req, res) => {
94
123
  });
95
124
  router.delete('/recordings/:id', async (req, res) => {
96
125
  try {
97
- await androidRecordings.discard(req.params.id);
126
+ await androidRecordings.discard(req.params.id, recordingOwners.get(req));
98
127
  res.json({ ok: true });
99
128
  }
100
129
  catch (error) {
@@ -1,3 +1,4 @@
1
+ import { VncReadOnlyFilter } from './vncReadOnly.js';
1
2
  import { Router } from 'express';
2
3
  import { isDeepStrictEqual } from 'node:util';
3
4
  import { hostname } from 'node:os';
@@ -99,13 +100,14 @@ export function stopComputerBridges() {
99
100
  * The eight-record receive window applies TCP backpressure without dropping bytes. */
100
101
  export function handleComputerWebSocket(socket, host, options) {
101
102
  if (options?.protocol === 'rdp') {
102
- handleRdpWebSocket(socket, host, Number(options.port ?? 3389));
103
+ handleRdpWebSocket(socket, host, Number(options.port ?? 3389), options.readOnly);
103
104
  return;
104
105
  }
105
106
  if (options?.protocol && options.protocol !== 'vnc') {
106
107
  socket.close(4400, 'COMPUTER_INVALID_PROTOCOL');
107
108
  return;
108
109
  }
110
+ const filter = options?.readOnly ? new VncReadOnlyFilter() : undefined;
109
111
  let closed = false;
110
112
  let tcp;
111
113
  let sent = 0;
@@ -138,6 +140,13 @@ export function handleComputerWebSocket(socket, host, options) {
138
140
  break;
139
141
  if (sent === acked)
140
142
  lastAckAt = Date.now();
143
+ try {
144
+ filter?.serverData(bytes);
145
+ }
146
+ catch {
147
+ finish(4403, 'COMPUTER_READ_ONLY_PROTOCOL');
148
+ return;
149
+ }
141
150
  socket.send(JSON.stringify({ type: 'data', seq: ++sent, data: bytes.toString('base64') }));
142
151
  if (closed)
143
152
  break;
@@ -164,13 +173,14 @@ export function handleComputerWebSocket(socket, host, options) {
164
173
  const bytes = Buffer.from(message.data, 'base64');
165
174
  if (!bytes.length || bytes.length > 64 * 1024 || tcp.writableLength + bytes.length > 1024 * 1024)
166
175
  throw new Error('INPUT_LIMIT');
167
- tcp.write(bytes);
176
+ for (const permitted of filter ? filter.clientData(bytes) : [bytes])
177
+ tcp.write(permitted);
168
178
  }
169
179
  else
170
180
  throw new Error('INVALID_MESSAGE');
171
181
  }
172
- catch {
173
- finish(4400, 'COMPUTER_INVALID_MESSAGE');
182
+ catch (error) {
183
+ finish(4400, error instanceof Error && error.message === 'COMPUTER_READ_ONLY_PROTOCOL' ? error.message : 'COMPUTER_INVALID_MESSAGE');
174
184
  }
175
185
  });
176
186
  void (async () => {
@@ -1,3 +1,4 @@
1
+ import { sharedFileBoundary } from './sharedFiles.js';
1
2
  import { getHideGitIgnoredRootsSetting, getGitIgnoreExceptionsSetting } from '../utils/settings.js';
2
3
  import { readRecentCommitHistory } from '../utils/recentCommitHistory.js';
3
4
  import { AUTH_COOKIE, isSessionValid } from '../utils/authProtection.js';
@@ -26,6 +27,7 @@ import { convertHeicPreview, HEIC_PREVIEW_MIME_TYPES, isHeicPreviewPath } from '
26
27
  import { getWatchErrorCode, diffWatchSnapshots, enqueueLatestWatchEvent, inspectLinuxInotifyUsage, isWatchResourceExhaustion, normalizeDirectoryWatchRoots, resolveDirectWatchEventPath, WATCH_IGNORED_NAMES, WATCH_RESOURCE_BACKOFF_MS, } from '../utils/fileWatchPolicy.js';
27
28
  import { isResponseWritable, writeResponseChunk } from '../utils/httpResponse.js';
28
29
  const router = Router();
30
+ router.use(sharedFileBoundary);
29
31
  const MAX_FILE_SIZE = 1024 * 1024; // 1MB
30
32
  const MAX_IMAGE_PREVIEW_SIZE = 20 * 1024 * 1024; // 20MB
31
33
  const MAX_HTML_PREVIEW_SIZE = 20 * 1024 * 1024; // 20MB
@@ -5057,9 +5059,9 @@ router.post('/open-in-file-browser', async (req, res) => {
5057
5059
  res.status(500).json({ error: message, code: 'OPEN_FILE_BROWSER_FAILED' });
5058
5060
  }
5059
5061
  });
5060
- router.post('/upload', async (req, res) => {
5062
+ export async function handleFileUpload(req, res, destinationDirectory) {
5061
5063
  try {
5062
- const dir = typeof req.query.dir === 'string' ? req.query.dir : '';
5064
+ const dir = destinationDirectory ?? (typeof req.query.dir === 'string' ? req.query.dir : '');
5063
5065
  if (!dir) {
5064
5066
  res.status(400).json({ error: 'Missing dir query parameter', code: 'MISSING_DIR' });
5065
5067
  return;
@@ -5203,7 +5205,8 @@ router.post('/upload', async (req, res) => {
5203
5205
  const message = error instanceof Error ? error.message : 'Upload failed';
5204
5206
  res.status(500).json({ error: message, code: 'UPLOAD_FAILED' });
5205
5207
  }
5206
- });
5208
+ }
5209
+ router.post('/upload', (req, res) => handleFileUpload(req, res));
5207
5210
  async function createUniqueUploadFile(filePath) {
5208
5211
  const ext = path.extname(filePath);
5209
5212
  const base = filePath.slice(0, filePath.length - ext.length);
@@ -19,7 +19,7 @@ export function rdpBackendAvailable() {
19
19
  }
20
20
  /** Only this server builds guacd's handshake. Credentials arrive inside the
21
21
  * authenticated encrypted logical socket, never query strings or logs. */
22
- export function handleRdpWebSocket(socket, host, port) {
22
+ export function handleRdpWebSocket(socket, host, port, readOnly = false) {
23
23
  let closed = false;
24
24
  let started = false;
25
25
  let ready = false;
@@ -119,7 +119,7 @@ export function handleRdpWebSocket(socket, host, port) {
119
119
  width: String(Math.max(640, Math.min(2560, Number(message.width) || 1280))),
120
120
  height: String(Math.max(480, Math.min(1600, Number(message.height) || 800))),
121
121
  'color-depth': '32', 'disable-audio': 'true', 'enable-drive': 'false',
122
- 'disable-copy': 'false', 'disable-paste': 'false', 'server-layout': 'en-us-qwerty', 'resize-method': 'display-update',
122
+ 'read-only': String(readOnly), 'disable-copy': String(readOnly), 'disable-paste': String(readOnly), 'server-layout': 'en-us-qwerty', 'resize-method': 'display-update',
123
123
  };
124
124
  void resolveComputerHost(host).then(async (address) => {
125
125
  if (closed)
@@ -174,7 +174,8 @@ export function handleRdpWebSocket(socket, host, port) {
174
174
  throw new Error('INVALID_INSTRUCTION');
175
175
  if (message.opcode === 'size' && (message.args.length !== 2 || message.args.some((value, index) => !/^\d{1,4}$/.test(value) || Number(value) < (index ? 480 : 640) || Number(value) > (index ? 1600 : 2560))))
176
176
  throw new Error('INVALID_SIZE');
177
- tcp.write(guacamoleInstruction(message.opcode, ...message.args));
177
+ if (!readOnly || ['sync', 'ack', 'nop', 'disconnect'].includes(message.opcode))
178
+ tcp.write(guacamoleInstruction(message.opcode, ...message.args));
178
179
  }
179
180
  else
180
181
  throw new Error('INVALID_MESSAGE');
@@ -0,0 +1,51 @@
1
+ import { Router } from 'express';
2
+ import { constants, promises as fs } from 'node:fs';
3
+ import { encryptedRequestResources, hasEncryptedServiceAccess } from '../federation/requestContext.js';
4
+ import { resourceHttpAllowed } from '../federation/resourceAccess.js';
5
+ import { pathValidator } from '../utils/pathValidator.js';
6
+ /** Applied before filesystem handlers as a second boundary on their exact URL. */
7
+ export const sharedFileBoundary = Router();
8
+ sharedFileBoundary.use((req, res, next) => {
9
+ const resources = encryptedRequestResources(req);
10
+ if (resources && !hasEncryptedServiceAccess(req) && !resourceHttpAllowed(resources, req.method, req.originalUrl)) {
11
+ res.status(403).json({ error: '此路径或操作未获分享授权', code: 'SHARE_ACCESS_DENIED' });
12
+ return;
13
+ }
14
+ next();
15
+ });
16
+ sharedFileBoundary.put('/write', async (req, res) => {
17
+ let handle;
18
+ try {
19
+ const requested = typeof req.query.path === 'string' ? req.query.path : '';
20
+ if (typeof req.body?.content !== 'string' || Buffer.byteLength(req.body.content) > 1024 * 1024 || typeof req.body?.original !== 'string') {
21
+ res.status(400).json({ error: '仅支持修改 1 MB 以内的文本文件' });
22
+ return;
23
+ }
24
+ const resolved = await pathValidator.validatePathAsync(requested);
25
+ handle = await fs.open(resolved, constants.O_RDWR | constants.O_NOFOLLOW);
26
+ const stat = await handle.stat();
27
+ if (!stat.isFile() || stat.nlink !== 1 || stat.size > 1024 * 1024) {
28
+ res.status(403).json({ error: '此文件无法安全修改' });
29
+ return;
30
+ }
31
+ const before = await handle.readFile('utf8');
32
+ if (before.includes('\0') || before !== req.body.original) {
33
+ res.status(409).json({ error: '文件已更新,请重新打开后再编辑' });
34
+ return;
35
+ }
36
+ // Explicit position is required after readFile advances the descriptor.
37
+ const bytes = Buffer.from(req.body.content);
38
+ let written = 0;
39
+ while (written < bytes.length)
40
+ written += (await handle.write(bytes, written, bytes.length - written, written)).bytesWritten;
41
+ await handle.truncate(bytes.length);
42
+ await handle.sync();
43
+ res.json({ ok: true });
44
+ }
45
+ catch {
46
+ res.status(400).json({ error: '无法保存文件,请确认目录授权和文件状态' });
47
+ }
48
+ finally {
49
+ await handle?.close();
50
+ }
51
+ });
@@ -1,3 +1,7 @@
1
+ import { sessionViewModeRoutes, SessionViewModeStore } from '../agent/sessionViewModeRoutes.js';
2
+ import { sessionAttachmentRoutes } from '../agent/sessionAttachmentRoutes.js';
3
+ import { TodoStore, TodoError } from '../todos/store.js';
4
+ import { TodoRunner, todoRoutes } from '../todos/routes.js';
1
5
  import { TerminalInputQueue } from '../utils/terminalInputQueue.js';
2
6
  import { copyTmuxSelection } from '../utils/tmuxCopySelection.js';
3
7
  import { isConfirmedEmptyTmuxServer } from '../utils/tmuxEmptyServerProbe.js';
@@ -66,6 +70,7 @@ import { prepareSupervisionEnable } from '../utils/enableSupervision.js';
66
70
  import { buildServerHealthState, dismissServerHealth } from '../utils/serverHealth.js';
67
71
  import { collaborationRoutes } from '../agent/collaborationRoutes.js';
68
72
  import { extrasFromBody } from '../agent/collaborationProtocol.js';
73
+ import { sessionChatRoutes } from '../agent/sessionChatRoutes.js';
69
74
  import { CollaborationStore } from '../agent/collaborationStore.js';
70
75
  import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
71
76
  import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
@@ -93,7 +98,7 @@ import { clearPluginAgents, registerPluginAgents } from '../agent/registry.js';
93
98
  import { notifyAgentTransition, notifyTerminalExit } from '../notifications/pushService.js';
94
99
  import { setClientViewingSession } from '../notifications/pushViewers.js';
95
100
  import { getQuotaStatus, refreshQuota, startQuotaManager, } from '../quota/QuotaManager.js';
96
- import { TermdockAutoUpdateManager } from '../utils/autoUpdate.js';
101
+ import { TermdockAutoUpdateManager, TermdockUpdateStateChangedError } from '../utils/autoUpdate.js';
97
102
  import { AUTO_TITLE_LONG_RUNNING_DELAY_MS, AUTO_TITLE_MIN_CONTEXT_CHARS, cleanTerminalContext, generateAgentTitle, hasSubstantiveAutoTitleContext, isLongRunningAutoTitleTurnEligible, isNewAgentSessionId, isAutoTitleReevaluationDue, shouldReplaceAutoTitle, } from '../agent/autoTitle.js';
98
103
  import { getTitleNamerCatalog, invalidateTitleNamerCatalog, probePluginTitleNamer } from '../agent/titleNamerCatalog.js';
99
104
  import { RenderedTerminalContext } from '../agent/renderedTerminalContext.js';
@@ -105,6 +110,7 @@ const router = express.Router();
105
110
  const execFileAsync = promisify(execFile);
106
111
  const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
107
112
  const automationStore = new AutomationStore(`${TERMDOCK_DIR}/automations.json`);
113
+ const todoStore = new TodoStore(`${TERMDOCK_DIR}/todos.json`);
108
114
  export const collaborationTaskStore = new CollaborationTaskStore(`${TERMDOCK_DIR}/collaboration-tasks.json`);
109
115
  export const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
110
116
  const collaborationRouting = new CollaborationRoutingStore(`${TERMDOCK_DIR}/collaboration-routing.json`);
@@ -1210,7 +1216,20 @@ async function markAllPersistedTmuxSessionsDetached() {
1210
1216
  console.warn('[session-inventory] failed to list tmux sessions for clear-all:', errorMessage);
1211
1217
  }
1212
1218
  }
1213
- await Promise.all(Array.from(tmuxSessionNames).map((tmuxSessionName) => setTmuxOption(tmuxSessionName, TERMDOCK_GUI_DETACHED_AT_OPTION, detachedAt)));
1219
+ await Promise.all(Array.from(tmuxSessionNames).map((tmuxSessionName) => markTmuxSessionClosed(tmuxSessionName, detachedAt)));
1220
+ }
1221
+ // Record user intent before removing either the backend or its inventory entry.
1222
+ // Unlike display metadata, failure here must stop the close: otherwise a later
1223
+ // scan mistakes the surviving tmux process for an unexpectedly lost GUI session.
1224
+ async function markTmuxSessionClosed(name, closedAt = String(Date.now())) {
1225
+ try {
1226
+ await runTmux(['set-option', '-t', name, TERMDOCK_GUI_DETACHED_AT_OPTION, closedAt]);
1227
+ }
1228
+ catch (error) {
1229
+ const message = getErrorMessage(error);
1230
+ if (!isTmuxSessionMissingMessage(message) && !isTmuxServerMissingMessage(message))
1231
+ throw error;
1232
+ }
1214
1233
  }
1215
1234
  function getClientSessionView(inventory, frontendSessionId) {
1216
1235
  const session = inventory.clientSessions.find((entry) => entry.sessionId === frontendSessionId);
@@ -6590,6 +6609,28 @@ router.get('/session-inventory', async (_req, res) => {
6590
6609
  res.status(500).json({ error: errorMessage || 'Failed to build session inventory' });
6591
6610
  }
6592
6611
  });
6612
+ const todoRuntime = {
6613
+ async validate(config, req) {
6614
+ const launcher = (await listDetectedAgentLaunchers()).find(agent => agent.slug === config.agentSlug);
6615
+ if (!launcher)
6616
+ throw new TodoError('请选择当前服务可用的 Agent');
6617
+ const command = config.command || launcher.command;
6618
+ const cwd = await resolveWorkingDirectory(req ?? {}, config.cwd || os.homedir());
6619
+ return { ...config, command, cwd };
6620
+ },
6621
+ async launch(run, prompt, onSession, req) {
6622
+ const opened = await openInventorySession(req ?? {}, {
6623
+ name: `${run.kind === 'collect' ? '收集' : '待办'} · ${run.name}`.slice(0, 120), customName: true, mode: 'tmux', cwd: run.config.cwd,
6624
+ });
6625
+ const sessionId = opened.session.sessionId;
6626
+ onSession(sessionId);
6627
+ const launchGuard = await beginAgentLaunchCheck(sessionId, run.config.agentSlug);
6628
+ await writeAutomaticTerminalPrompt(sessionId, run.config.command, undefined, launchGuard);
6629
+ await deliverAutomationPromptWhenReady(opened.terminalSession.sessionId, prompt);
6630
+ },
6631
+ };
6632
+ const todoRunner = new TodoRunner(todoStore, todoRuntime);
6633
+ router.use('/operations/todos', todoRoutes(todoStore, todoRunner, todoRuntime));
6593
6634
  router.get('/operations/automations', (_req, res) => {
6594
6635
  res.json({ automations: automationStore.list(), runs: automationStore.listRuns() });
6595
6636
  });
@@ -6713,6 +6754,12 @@ export function collaborationLocalActivity() {
6713
6754
  });
6714
6755
  }
6715
6756
  export function deliverPeerCollaboration(sessionId) { void tryDeliverCollaborationInbox(sessionId); }
6757
+ router.use(sessionViewModeRoutes(new SessionViewModeStore(`${TERMDOCK_DIR}/session-view-modes.json`), resolveStableFederationSessionId));
6758
+ router.use(sessionAttachmentRoutes(`${TERMDOCK_DIR}/chat-attachments`, resolveStableFederationSessionId));
6759
+ router.use(sessionChatRoutes({ store: collaborationStore, resolve: id => {
6760
+ const stable = resolveStableFederationSessionId(id);
6761
+ return stable ? collaborationDirectorySessions().find(s => s.sessionId === stable) : undefined;
6762
+ }, deliver: deliverPeerCollaboration }));
6716
6763
  router.use('/operations/collaboration-tasks', collaborationTaskRoutes({ agent: false, store: collaborationStore, resolveSession: resolveFrontendSessionId }));
6717
6764
  router.use('/operations/orchestration/tasks', collaborationTaskRoutes({ agent: true, store: collaborationStore, resolveSession: resolveFrontendSessionId }));
6718
6765
  router.use('/operations', collaborationGroupRoutes({ store: collaborationStore,
@@ -7519,12 +7566,12 @@ router.delete('/session-inventory/sessions/:frontendSessionId', async (req, res)
7519
7566
  return res.status(400).json({ error: 'frontendSessionId is required' });
7520
7567
  }
7521
7568
  const removedSession = globalSessionState.sessions.find((session) => session.sessionId === frontendSessionId) ?? null;
7569
+ if (removedSession?.mode === 'tmux' && removedSession.tmuxSessionName) {
7570
+ await markTmuxSessionClosed(removedSession.tmuxSessionName);
7571
+ }
7522
7572
  archiveAgentResumeRecord(removedSession, 'closed');
7523
7573
  const changed = removeGlobalSessionRecord(frontendSessionId);
7524
7574
  if (changed) {
7525
- if (removedSession?.mode === 'tmux' && removedSession.tmuxSessionName) {
7526
- await setTmuxOption(removedSession.tmuxSessionName, TERMDOCK_GUI_DETACHED_AT_OPTION, String(Date.now()));
7527
- }
7528
7575
  await persistGlobalStateNow();
7529
7576
  broadcastClientState();
7530
7577
  }
@@ -7651,13 +7698,20 @@ router.post('/update/check', async (_req, res) => {
7651
7698
  res.status(503).json({ error: error instanceof Error ? error.message : String(error) });
7652
7699
  }
7653
7700
  });
7654
- router.post('/update/restart', async (_req, res) => {
7701
+ router.post('/update/restart', async (req, res) => {
7655
7702
  try {
7656
- const state = npmAutoUpdateManager.confirmRestart();
7703
+ const version = req.body?.version;
7704
+ if (version !== undefined && typeof version !== 'string')
7705
+ return res.status(400).json({ error: 'Invalid update version' });
7706
+ const state = npmAutoUpdateManager.confirmRestart(version);
7657
7707
  res.status(202).json(state);
7658
7708
  }
7659
7709
  catch (error) {
7660
- res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
7710
+ res.status(409).json({
7711
+ error: error instanceof Error ? error.message : String(error),
7712
+ code: error instanceof TermdockUpdateStateChangedError ? 'UPDATE_STATE_CHANGED' : 'UPDATE_RESTART_FAILED',
7713
+ state: npmAutoUpdateManager.getState(),
7714
+ });
7661
7715
  }
7662
7716
  });
7663
7717
  // 「服务最近出过什么事」:红点与设置面板的唯一数据源。
@@ -8742,16 +8796,30 @@ router.post('/:sessionId/tmux', async (req, res) => {
8742
8796
  return res.status(500).json({ error: errorMessage || 'Failed to execute tmux action' });
8743
8797
  }
8744
8798
  });
8745
- router.delete('/:sessionId', (req, res) => {
8799
+ router.delete('/:sessionId', async (req, res) => {
8746
8800
  const { sessionId } = req.params;
8747
8801
  const session = terminalSessions.get(sessionId);
8748
- if (!session) {
8749
- return res.json({ success: true, alreadyGone: true });
8750
- }
8802
+ const records = globalSessionState.sessions.filter(record => record.backendSessionId === sessionId);
8751
8803
  try {
8752
- cleanupSession(sessionId, { killProcess: true });
8804
+ const tmuxNames = new Set(records.flatMap(record => record.mode === 'tmux' && record.tmuxSessionName ? [record.tmuxSessionName] : []));
8805
+ if (session?.mode === 'tmux' && session.tmuxSessionName)
8806
+ tmuxNames.add(session.tmuxSessionName);
8807
+ for (const name of tmuxNames)
8808
+ await markTmuxSessionClosed(name);
8809
+ if (session)
8810
+ cleanupSession(sessionId, { killProcess: true });
8811
+ // Complete the close on the server even if the page exits before its second
8812
+ // DELETE (inventory removal). A stale/missing wrapper can still own a record.
8813
+ for (const record of records) {
8814
+ archiveAgentResumeRecord(record, 'closed');
8815
+ removeGlobalSessionRecord(record.sessionId);
8816
+ }
8817
+ if (records.length) {
8818
+ await persistGlobalStateNow();
8819
+ broadcastClientState();
8820
+ }
8753
8821
  console.log(`Closed terminal session: ${sessionId}`);
8754
- res.json({ success: true });
8822
+ res.json({ success: true, ...(!session ? { alreadyGone: true } : {}) });
8755
8823
  }
8756
8824
  catch (error) {
8757
8825
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -9587,6 +9655,7 @@ const reconcileTimer = setInterval(() => {
9587
9655
  // Don't keep the event loop alive for housekeeping.
9588
9656
  reconcileTimer.unref?.();
9589
9657
  const automationTimer = setInterval(() => {
9658
+ void todoRunner.tick().catch(error => console.warn('[todo-collector]', getErrorMessage(error)));
9590
9659
  for (const automation of automationStore.due()) {
9591
9660
  void runAgentAutomation(automation).catch((error) => {
9592
9661
  console.warn(`[automation] ${automation.id} failed:`, getErrorMessage(error));
@@ -0,0 +1,134 @@
1
+ /** Stateful RFB client filter. Handshake bytes are length-delimited; after
2
+ * ClientInit only framebuffer negotiation/requests pass. Never scan raw bytes
3
+ * for opcodes: credentials and fragmented records can contain any byte. */
4
+ export class VncReadOnlyFilter {
5
+ client = Buffer.alloc(0);
6
+ server = Buffer.alloc(0);
7
+ stage = 'version';
8
+ serverStage = 'version';
9
+ security = 0;
10
+ authBytes = 0;
11
+ offered = [];
12
+ serverData(bytes) {
13
+ if (this.serverStage === 'done')
14
+ return;
15
+ this.server = Buffer.concat([this.server, bytes]);
16
+ if (this.server.length > 65536)
17
+ throw new Error('COMPUTER_INVALID_MESSAGE');
18
+ if (this.serverStage === 'version' && this.server.length >= 12) {
19
+ this.server = this.server.subarray(12);
20
+ this.serverStage = 'security';
21
+ }
22
+ if (this.serverStage === 'security' && this.server.length >= 1 && this.server.length >= 1 + this.server[0]) {
23
+ this.offered = [...this.server.subarray(1, 1 + this.server[0])];
24
+ this.server = this.server.subarray(1 + this.server[0]);
25
+ this.serverStage = 'challenge';
26
+ }
27
+ if (this.serverStage === 'challenge' && this.security === 30 && this.server.length >= 4) {
28
+ const length = this.server.readUInt16BE(2);
29
+ if (length < 16 || length > 512)
30
+ throw new Error('COMPUTER_INVALID_MESSAGE');
31
+ this.authBytes = 128 + length;
32
+ this.serverStage = 'done';
33
+ this.server = Buffer.alloc(0);
34
+ }
35
+ else if (this.serverStage === 'challenge' && [1, 2].includes(this.security)) {
36
+ this.serverStage = 'done';
37
+ this.server = Buffer.alloc(0);
38
+ }
39
+ }
40
+ clientData(bytes) {
41
+ this.client = Buffer.concat([this.client, bytes]);
42
+ if (this.client.length > 1024 * 1024)
43
+ throw new Error('COMPUTER_INVALID_MESSAGE');
44
+ const output = [];
45
+ const consume = (n, forward = true) => { if (forward)
46
+ output.push(this.client.subarray(0, n)); this.client = this.client.subarray(n); };
47
+ while (this.client.length) {
48
+ if (this.stage === 'version') {
49
+ if (this.client.length < 12)
50
+ break;
51
+ // 3.8 has explicit security selection/results; fail closed for legacy.
52
+ if (this.client.subarray(0, 12).toString() !== 'RFB 003.008\n')
53
+ throw new Error('COMPUTER_READ_ONLY_PROTOCOL');
54
+ consume(12);
55
+ this.stage = 'security';
56
+ }
57
+ else if (this.stage === 'security') {
58
+ this.security = this.client[0];
59
+ if (![1, 2, 30].includes(this.security) || !this.offered.includes(this.security))
60
+ throw new Error('COMPUTER_READ_ONLY_PROTOCOL');
61
+ this.authBytes = this.security === 2 ? 16 : 0;
62
+ consume(1);
63
+ this.stage = this.security === 1 ? 'init' : 'auth';
64
+ this.serverData(Buffer.alloc(0));
65
+ }
66
+ else if (this.stage === 'auth') {
67
+ if (!this.authBytes || this.client.length < this.authBytes)
68
+ break;
69
+ consume(this.authBytes);
70
+ this.stage = 'init';
71
+ }
72
+ else if (this.stage === 'init') {
73
+ if (this.client[0] !== 1)
74
+ throw new Error('COMPUTER_READ_ONLY_PROTOCOL');
75
+ consume(1);
76
+ this.stage = 'messages';
77
+ }
78
+ else {
79
+ const type = this.client[0];
80
+ let length;
81
+ if (type === 0)
82
+ length = 20;
83
+ else if (type === 2) {
84
+ if (this.client.length < 4)
85
+ break;
86
+ length = 4 + 4 * this.client.readUInt16BE(2);
87
+ }
88
+ else if (type === 3 || type === 150)
89
+ length = 10;
90
+ else if (type === 4)
91
+ length = 8;
92
+ else if (type === 5)
93
+ length = 6;
94
+ else if (type === 6) {
95
+ if (this.client.length < 8)
96
+ break;
97
+ length = 8 + Math.abs(this.client.readInt32BE(4));
98
+ }
99
+ else if (type === 248) {
100
+ if (this.client.length < 9)
101
+ break;
102
+ length = 9 + this.client[8];
103
+ }
104
+ else if (type === 251) {
105
+ if (this.client.length < 8)
106
+ break;
107
+ length = 8 + 16 * this.client[6];
108
+ }
109
+ else if (type === 255)
110
+ length = 12;
111
+ else
112
+ throw new Error('COMPUTER_READ_ONLY_PROTOCOL');
113
+ if (length > 1024 * 1024)
114
+ throw new Error('COMPUTER_INVALID_MESSAGE');
115
+ if (this.client.length < length)
116
+ break;
117
+ if (type === 2) {
118
+ // Do not advertise clipboard extensions to a view-only client.
119
+ const encodings = [];
120
+ for (let i = 4; i < length; i += 4)
121
+ if (this.client.readUInt32BE(i) !== 0xc0a1e5ce)
122
+ encodings.push(this.client.subarray(i, i + 4));
123
+ const header = Buffer.from([2, 0, 0, 0]);
124
+ header.writeUInt16BE(encodings.length, 2);
125
+ output.push(Buffer.concat([header, ...encodings]));
126
+ consume(length, false);
127
+ }
128
+ else
129
+ consume(length, [0, 3, 150, 248].includes(type));
130
+ }
131
+ }
132
+ return output;
133
+ }
134
+ }
@@ -0,0 +1,37 @@
1
+ const record = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
2
+ const text = (v, max) => typeof v === 'string' && v.length > 0 && v.length <= max && !/[\x00-\x1f\x7f]/.test(v);
3
+ export function validShareResources(value) {
4
+ if (!record(value) || Object.keys(value).some(key => !['android', 'computers', 'directories'].includes(key)))
5
+ return false;
6
+ return Object.entries(value).every(([key, items]) => Array.isArray(items) && items.length <= 64 && items.every(item => {
7
+ if (!record(item))
8
+ return false;
9
+ if (key === 'android')
10
+ return Object.keys(item).every(k => ['serial', 'access'].includes(k)) && text(item.serial, 128) && /^(?:[0-9a-zA-Z_.:\-]+|\[[0-9a-fA-F:.]+\]:\d{1,5})$/.test(item.serial) && ['view', 'control'].includes(String(item.access));
11
+ if (key === 'computers')
12
+ return Object.keys(item).every(k => ['host', 'protocol', 'port', 'access'].includes(k)) && text(item.host, 253) && /^[a-zA-Z0-9:.\-]+$/.test(item.host) && ['vnc', 'rdp'].includes(String(item.protocol)) && Number.isInteger(item.port) && Number(item.port) > 0 && Number(item.port) <= 65535 && (item.protocol !== 'vnc' || item.port === 5900) && ['view', 'control'].includes(String(item.access));
13
+ return Object.keys(item).every(k => ['path', 'access'].includes(k)) && text(item.path, 4096) && (item.path.startsWith('/') || /^[A-Za-z]:[\\/]/.test(item.path)) && ['read', 'write'].includes(String(item.access));
14
+ }));
15
+ }
16
+ export function resourceSubset(child, parent) {
17
+ return (!child?.android?.length || child.android.every(c => parent?.android?.some(p => p.serial === c.serial && (p.access === 'control' || c.access === 'view'))))
18
+ && (!child?.computers?.length || child.computers.every(c => parent?.computers?.some(p => p.host === c.host && p.protocol === c.protocol && p.port === c.port && (p.access === 'control' || c.access === 'view'))))
19
+ && (!child?.directories?.length || child.directories.every(c => parent?.directories?.some(p => p.path === c.path && (p.access === 'write' || c.access === 'read'))));
20
+ }
21
+ export function mergeShareResources(resources) {
22
+ const android = new Map(), computers = new Map(), directories = new Map();
23
+ for (const r of resources) {
24
+ for (const item of r?.android ?? [])
25
+ if (android.get(item.serial)?.access !== 'control')
26
+ android.set(item.serial, item);
27
+ for (const item of r?.computers ?? []) {
28
+ const key = JSON.stringify([item.host.toLowerCase(), item.protocol, item.port]);
29
+ if (computers.get(key)?.access !== 'control')
30
+ computers.set(key, item);
31
+ }
32
+ for (const item of r?.directories ?? [])
33
+ if (directories.get(item.path)?.access !== 'write')
34
+ directories.set(item.path, item);
35
+ }
36
+ return { android: [...android.values()], computers: [...computers.values()], directories: [...directories.values()] };
37
+ }