termdock 1.4.296 → 1.4.297

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 (364) hide show
  1. package/.desktop-dist/main.js +35 -2
  2. package/README.en.md +257 -0
  3. package/README.md +284 -133
  4. package/dist/client/assets/App-BbqtNY2x.js +177 -0
  5. package/dist/client/assets/App-BbqtNY2x.js.br +0 -0
  6. package/dist/client/assets/App-BbqtNY2x.js.gz +0 -0
  7. package/dist/client/assets/{ChangeWalkthroughPanel-DXprT-7m.js → ChangeWalkthroughPanel-CvQJIp_C.js} +1 -1
  8. package/dist/client/assets/ChangeWalkthroughPanel-CvQJIp_C.js.br +0 -0
  9. package/dist/client/assets/ChangeWalkthroughPanel-CvQJIp_C.js.gz +0 -0
  10. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js +1 -0
  11. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js.br +0 -0
  12. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js.gz +0 -0
  13. package/dist/client/assets/CollaborationTaskContent-C01GXOEq.js +2 -0
  14. package/dist/client/assets/ComputerControlView-CW1uA133.js +4 -0
  15. package/dist/client/assets/ComputerControlView-CW1uA133.js.br +0 -0
  16. package/dist/client/assets/ComputerControlView-CW1uA133.js.gz +0 -0
  17. package/dist/client/assets/{DagPlayground-DZcUunT5.js → DagPlayground-CdE3tQsl.js} +1 -1
  18. package/dist/client/assets/DagPlayground-CdE3tQsl.js.br +0 -0
  19. package/dist/client/assets/DagPlayground-CdE3tQsl.js.gz +0 -0
  20. package/dist/client/assets/{DiffLab-Brv4DCTb.js → DiffLab-BLGNnazd.js} +1 -1
  21. package/dist/client/assets/DiffLab-BLGNnazd.js.br +0 -0
  22. package/dist/client/assets/DiffLab-BLGNnazd.js.gz +0 -0
  23. package/dist/client/assets/{DiffReview-BuLu1qCi.js → DiffReview-CYw3ASI6.js} +1 -1
  24. package/dist/client/assets/DiffReview-CYw3ASI6.js.br +0 -0
  25. package/dist/client/assets/DiffReview-CYw3ASI6.js.gz +0 -0
  26. package/dist/client/assets/{DiffReviewLab-DSGamuAD.js → DiffReviewLab-CySIuWq_.js} +1 -1
  27. package/dist/client/assets/DiffReviewLab-CySIuWq_.js.br +0 -0
  28. package/dist/client/assets/DiffReviewLab-CySIuWq_.js.gz +0 -0
  29. package/dist/client/assets/{DiffViewer-BUCYxg27.js → DiffViewer-WsYvN75z.js} +13 -13
  30. package/dist/client/assets/DiffViewer-WsYvN75z.js.br +0 -0
  31. package/dist/client/assets/DiffViewer-WsYvN75z.js.gz +0 -0
  32. package/dist/client/assets/{ModelPreview-Bnlo-oVE.js → ModelPreview-Dnlx9ByX.js} +1 -1
  33. package/dist/client/assets/ModelPreview-Dnlx9ByX.js.br +0 -0
  34. package/dist/client/assets/ModelPreview-Dnlx9ByX.js.gz +0 -0
  35. package/dist/client/assets/RightSidebar-Cp5b_y0u.js +1678 -0
  36. package/dist/client/assets/RightSidebar-Cp5b_y0u.js.br +0 -0
  37. package/dist/client/assets/RightSidebar-Cp5b_y0u.js.gz +0 -0
  38. package/dist/client/assets/{abnfDiagram-VCTEODGH-K6Ppkrbt.js → abnfDiagram-VCTEODGH-CK1mGFRQ.js} +1 -1
  39. package/dist/client/assets/abnfDiagram-VCTEODGH-CK1mGFRQ.js.br +0 -0
  40. package/dist/client/assets/abnfDiagram-VCTEODGH-CK1mGFRQ.js.gz +0 -0
  41. package/dist/client/assets/app-DfACMixk.js +35 -0
  42. package/dist/client/assets/app-DfACMixk.js.br +0 -0
  43. package/dist/client/assets/app-DfACMixk.js.gz +0 -0
  44. package/dist/client/assets/{arc-Co2OQqtH.js → arc-yZE1gqDl.js} +1 -1
  45. package/dist/client/assets/arc-yZE1gqDl.js.br +0 -0
  46. package/dist/client/assets/arc-yZE1gqDl.js.gz +0 -0
  47. package/dist/client/assets/{architectureDiagram-5GKGNRK7-BQnl7tVF.js → architectureDiagram-5GKGNRK7-U5XSC7cY.js} +1 -1
  48. package/dist/client/assets/architectureDiagram-5GKGNRK7-U5XSC7cY.js.br +0 -0
  49. package/dist/client/assets/architectureDiagram-5GKGNRK7-U5XSC7cY.js.gz +0 -0
  50. package/dist/client/assets/{blockDiagram-I7D4REHJ-Bm5D5-qq.js → blockDiagram-I7D4REHJ-pVJnntSG.js} +1 -1
  51. package/dist/client/assets/blockDiagram-I7D4REHJ-pVJnntSG.js.br +0 -0
  52. package/dist/client/assets/blockDiagram-I7D4REHJ-pVJnntSG.js.gz +0 -0
  53. package/dist/client/assets/{c4Diagram-7LVT6UL2-B9oPpey8.js → c4Diagram-7LVT6UL2-Bwz0AJJV.js} +1 -1
  54. package/dist/client/assets/c4Diagram-7LVT6UL2-Bwz0AJJV.js.br +0 -0
  55. package/dist/client/assets/c4Diagram-7LVT6UL2-Bwz0AJJV.js.gz +0 -0
  56. package/dist/client/assets/channel-qqjDJtcZ.js +1 -0
  57. package/dist/client/assets/{chunk-2Q5K7J3B-TMaWHH0U.js → chunk-2Q5K7J3B-CXJ1kP4X.js} +1 -1
  58. package/dist/client/assets/{chunk-5VM5RSS4-CH7XpknV.js → chunk-5VM5RSS4-Dpdum_2z.js} +1 -1
  59. package/dist/client/assets/{chunk-F27PBJKO-CJ1rotBe.js → chunk-F27PBJKO-BCG5Zi_-.js} +1 -1
  60. package/dist/client/assets/chunk-F27PBJKO-BCG5Zi_-.js.br +0 -0
  61. package/dist/client/assets/chunk-F27PBJKO-BCG5Zi_-.js.gz +0 -0
  62. package/dist/client/assets/{chunk-IMKFNOWR-UYKZ5uOG.js → chunk-IMKFNOWR-BM-GcNcv.js} +1 -1
  63. package/dist/client/assets/chunk-IMKFNOWR-BM-GcNcv.js.br +0 -0
  64. package/dist/client/assets/chunk-IMKFNOWR-BM-GcNcv.js.gz +0 -0
  65. package/dist/client/assets/{chunk-JWPE2WC7-BYL8aXjQ.js → chunk-JWPE2WC7-BWiXpQT9.js} +1 -1
  66. package/dist/client/assets/{chunk-POPQ4Y6H-C3E8yfU2.js → chunk-POPQ4Y6H-vQSslMNv.js} +1 -1
  67. package/dist/client/assets/{chunk-SVP7TREG-DCTDYa1E.js → chunk-SVP7TREG-Aqfz6nPA.js} +1 -1
  68. package/dist/client/assets/chunk-SVP7TREG-Aqfz6nPA.js.br +0 -0
  69. package/dist/client/assets/chunk-SVP7TREG-Aqfz6nPA.js.gz +0 -0
  70. package/dist/client/assets/{chunk-TICWLB2K-wHiaOsBh.js → chunk-TICWLB2K-DDjekMd0.js} +1 -1
  71. package/dist/client/assets/chunk-TICWLB2K-DDjekMd0.js.br +0 -0
  72. package/dist/client/assets/chunk-TICWLB2K-DDjekMd0.js.gz +0 -0
  73. package/dist/client/assets/{chunk-XXDRQBXY-D_d479hK.js → chunk-XXDRQBXY-BegIBcFg.js} +1 -1
  74. package/dist/client/assets/classDiagram-ZZMXUADV-BDuBrfNz.js +1 -0
  75. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BDuBrfNz.js +1 -0
  76. package/dist/client/assets/{cose-bilkent-JH36ORCC-DnhlPBLr.js → cose-bilkent-JH36ORCC-B2izFsBY.js} +1 -1
  77. package/dist/client/assets/cose-bilkent-JH36ORCC-B2izFsBY.js.br +0 -0
  78. package/dist/client/assets/cose-bilkent-JH36ORCC-B2izFsBY.js.gz +0 -0
  79. package/dist/client/assets/{cynefin-OW5HDTMX-BsjzNqJZ.js → cynefin-OW5HDTMX-C7HA99Zc.js} +1 -1
  80. package/dist/client/assets/cynefin-OW5HDTMX-C7HA99Zc.js.br +0 -0
  81. package/dist/client/assets/cynefin-OW5HDTMX-C7HA99Zc.js.gz +0 -0
  82. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-sVUb0zY5.js → cynefinDiagram-5FMLGOSQ-vNIzcCYl.js} +1 -1
  83. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-vNIzcCYl.js.br +0 -0
  84. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-vNIzcCYl.js.gz +0 -0
  85. package/dist/client/assets/{dagre-GXQ25YYZ-YErk8S-c.js → dagre-GXQ25YYZ-Bv9WHNT9.js} +1 -1
  86. package/dist/client/assets/dagre-GXQ25YYZ-Bv9WHNT9.js.br +0 -0
  87. package/dist/client/assets/dagre-GXQ25YYZ-Bv9WHNT9.js.gz +0 -0
  88. package/dist/client/assets/{diagram-S7CK7UJ4-BaHbV5ho.js → diagram-S7CK7UJ4-DFg5UNic.js} +1 -1
  89. package/dist/client/assets/diagram-S7CK7UJ4-DFg5UNic.js.br +0 -0
  90. package/dist/client/assets/diagram-S7CK7UJ4-DFg5UNic.js.gz +0 -0
  91. package/dist/client/assets/{diagram-UQ7AKVKN-BocCYx3V.js → diagram-UQ7AKVKN-BFQsCAs7.js} +1 -1
  92. package/dist/client/assets/diagram-UQ7AKVKN-BFQsCAs7.js.br +0 -0
  93. package/dist/client/assets/diagram-UQ7AKVKN-BFQsCAs7.js.gz +0 -0
  94. package/dist/client/assets/{diagram-VSXAHHWV-ZMTYwWOQ.js → diagram-VSXAHHWV-C4R8TKEv.js} +1 -1
  95. package/dist/client/assets/diagram-VSXAHHWV-C4R8TKEv.js.br +0 -0
  96. package/dist/client/assets/diagram-VSXAHHWV-C4R8TKEv.js.gz +0 -0
  97. package/dist/client/assets/{diagram-VX7I27RA-xmk_EY1h.js → diagram-VX7I27RA-CL75HIf0.js} +1 -1
  98. package/dist/client/assets/diagram-VX7I27RA-CL75HIf0.js.br +0 -0
  99. package/dist/client/assets/diagram-VX7I27RA-CL75HIf0.js.gz +0 -0
  100. package/dist/client/assets/{diagram-Z3DM3KII-DKDY_cG6.js → diagram-Z3DM3KII-CfJampuO.js} +1 -1
  101. package/dist/client/assets/diagram-Z3DM3KII-CfJampuO.js.br +0 -0
  102. package/dist/client/assets/diagram-Z3DM3KII-CfJampuO.js.gz +0 -0
  103. package/dist/client/assets/{ebnfDiagram-PWID7BFC-C_0z-Jx9.js → ebnfDiagram-PWID7BFC-Ct53jhNd.js} +1 -1
  104. package/dist/client/assets/ebnfDiagram-PWID7BFC-Ct53jhNd.js.br +0 -0
  105. package/dist/client/assets/ebnfDiagram-PWID7BFC-Ct53jhNd.js.gz +0 -0
  106. package/dist/client/assets/{erDiagram-RLTQ6QDP-DHUaG7mt.js → erDiagram-RLTQ6QDP-BgL2xOOa.js} +1 -1
  107. package/dist/client/assets/erDiagram-RLTQ6QDP-BgL2xOOa.js.br +0 -0
  108. package/dist/client/assets/erDiagram-RLTQ6QDP-BgL2xOOa.js.gz +0 -0
  109. package/dist/client/assets/{flowDiagram-HODETNUW-BMNsdMBD.js → flowDiagram-HODETNUW-Cp4Nu5jY.js} +1 -1
  110. package/dist/client/assets/flowDiagram-HODETNUW-Cp4Nu5jY.js.br +0 -0
  111. package/dist/client/assets/flowDiagram-HODETNUW-Cp4Nu5jY.js.gz +0 -0
  112. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BH1AiKuF.js → ganttDiagram-EL5Y4UJY-DI0rdyPX.js} +1 -1
  113. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DI0rdyPX.js.br +0 -0
  114. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DI0rdyPX.js.gz +0 -0
  115. package/dist/client/assets/{gestureArbiter-DvupkPra.js → gestureArbiter-Belsq4xq.js} +1 -1
  116. package/dist/client/assets/gestureArbiter-Belsq4xq.js.br +0 -0
  117. package/dist/client/assets/gestureArbiter-Belsq4xq.js.gz +0 -0
  118. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DAgV0MV-.js → gitGraphDiagram-WWUBYQGX-C0H4loQP.js} +1 -1
  119. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0H4loQP.js.br +0 -0
  120. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0H4loQP.js.gz +0 -0
  121. package/dist/client/assets/index-CYmI1EsU.css +1 -0
  122. package/dist/client/assets/index-CYmI1EsU.css.br +0 -0
  123. package/dist/client/assets/index-CYmI1EsU.css.gz +0 -0
  124. package/dist/client/assets/{infoDiagram-27XIBGKW-C7iSg0cB.js → infoDiagram-27XIBGKW-BN4RpQTL.js} +1 -1
  125. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BpZwsQvw.js → ishikawaDiagram-5VMMS53U-BZQ5qZB_.js} +1 -1
  126. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BZQ5qZB_.js.br +0 -0
  127. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BZQ5qZB_.js.gz +0 -0
  128. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DXEeFLvw.js → journeyDiagram-3NMN7TZE-Q8bx0_N4.js} +1 -1
  129. package/dist/client/assets/journeyDiagram-3NMN7TZE-Q8bx0_N4.js.br +0 -0
  130. package/dist/client/assets/journeyDiagram-3NMN7TZE-Q8bx0_N4.js.gz +0 -0
  131. package/dist/client/assets/{kanban-definition-UXKFOSKX-CV9ivL5u.js → kanban-definition-UXKFOSKX-D7JhGtpO.js} +1 -1
  132. package/dist/client/assets/kanban-definition-UXKFOSKX-D7JhGtpO.js.br +0 -0
  133. package/dist/client/assets/kanban-definition-UXKFOSKX-D7JhGtpO.js.gz +0 -0
  134. package/dist/client/assets/{layout-DsKKsJKX.js → layout-DERUg0xU.js} +1 -1
  135. package/dist/client/assets/layout-DERUg0xU.js.br +0 -0
  136. package/dist/client/assets/layout-DERUg0xU.js.gz +0 -0
  137. package/dist/client/assets/{linear-Dl9MDNV3.js → linear-lNTe4x00.js} +1 -1
  138. package/dist/client/assets/linear-lNTe4x00.js.br +0 -0
  139. package/dist/client/assets/linear-lNTe4x00.js.gz +0 -0
  140. package/dist/client/assets/{mermaid.core-CxZFe3as.js → mermaid.core-WHCwLQDQ.js} +5 -5
  141. package/dist/client/assets/mermaid.core-WHCwLQDQ.js.br +0 -0
  142. package/dist/client/assets/mermaid.core-WHCwLQDQ.js.gz +0 -0
  143. package/dist/client/assets/{mindmap-definition-YA3MSWOX-C3ormYdL.js → mindmap-definition-YA3MSWOX-BouapA4v.js} +1 -1
  144. package/dist/client/assets/mindmap-definition-YA3MSWOX-BouapA4v.js.br +0 -0
  145. package/dist/client/assets/mindmap-definition-YA3MSWOX-BouapA4v.js.gz +0 -0
  146. package/dist/client/assets/{passwordBootstrap-a8z6jJNY.js → passwordBootstrap-E0piP814.js} +2 -2
  147. package/dist/client/assets/passwordBootstrap-E0piP814.js.br +0 -0
  148. package/dist/client/assets/passwordBootstrap-E0piP814.js.gz +0 -0
  149. package/dist/client/assets/{pegDiagram-XKGWAZYB-CG0wNylH.js → pegDiagram-XKGWAZYB-Curpo5Rq.js} +1 -1
  150. package/dist/client/assets/pegDiagram-XKGWAZYB-Curpo5Rq.js.br +0 -0
  151. package/dist/client/assets/pegDiagram-XKGWAZYB-Curpo5Rq.js.gz +0 -0
  152. package/dist/client/assets/{pieDiagram-E7YTZNPT-D2-ydqyX.js → pieDiagram-E7YTZNPT-BLQCHaOG.js} +1 -1
  153. package/dist/client/assets/pieDiagram-E7YTZNPT-BLQCHaOG.js.br +0 -0
  154. package/dist/client/assets/pieDiagram-E7YTZNPT-BLQCHaOG.js.gz +0 -0
  155. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-CK3fRC18.js → quadrantDiagram-AXDQQJYC-PViFqGao.js} +1 -1
  156. package/dist/client/assets/quadrantDiagram-AXDQQJYC-PViFqGao.js.br +0 -0
  157. package/dist/client/assets/quadrantDiagram-AXDQQJYC-PViFqGao.js.gz +0 -0
  158. package/dist/client/assets/{railroadDiagram-O6MQD6OU-V4EFlydu.js → railroadDiagram-O6MQD6OU-BtHo1LpT.js} +1 -1
  159. package/dist/client/assets/railroadDiagram-O6MQD6OU-BtHo1LpT.js.br +0 -0
  160. package/dist/client/assets/railroadDiagram-O6MQD6OU-BtHo1LpT.js.gz +0 -0
  161. package/dist/client/assets/rdpSession-O_f9ejOI.js +1 -0
  162. package/dist/client/assets/rdpSession-O_f9ejOI.js.br +0 -0
  163. package/dist/client/assets/rdpSession-O_f9ejOI.js.gz +0 -0
  164. package/dist/client/assets/{referenceLongPress-BjmYd1vB.js → referenceLongPress-BZr7Lqzh.js} +1 -1
  165. package/dist/client/assets/referenceLongPress-BZr7Lqzh.js.br +0 -0
  166. package/dist/client/assets/referenceLongPress-BZr7Lqzh.js.gz +0 -0
  167. package/dist/client/assets/{requirementDiagram-BXWQKSXE-Di1_tyAX.js → requirementDiagram-BXWQKSXE-BUm81HUU.js} +1 -1
  168. package/dist/client/assets/requirementDiagram-BXWQKSXE-BUm81HUU.js.br +0 -0
  169. package/dist/client/assets/requirementDiagram-BXWQKSXE-BUm81HUU.js.gz +0 -0
  170. package/dist/client/assets/rfb-CxsXsMUX.js +5 -0
  171. package/dist/client/assets/rfb-CxsXsMUX.js.br +0 -0
  172. package/dist/client/assets/rfb-CxsXsMUX.js.gz +0 -0
  173. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-D-qpWNza.js → sankeyDiagram-P5KCCOFB-B-_-aZ_y.js} +1 -1
  174. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B-_-aZ_y.js.br +0 -0
  175. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B-_-aZ_y.js.gz +0 -0
  176. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DmIxafrB.js → sequenceDiagram-WJ2MYXX4-DT6LDIfx.js} +1 -1
  177. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DT6LDIfx.js.br +0 -0
  178. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DT6LDIfx.js.gz +0 -0
  179. package/dist/client/assets/{sizeCapture-INFHLROL-4MTh3tTt.js → sizeCapture-INFHLROL-CTYerd9j.js} +1 -1
  180. package/dist/client/assets/sizeCapture-INFHLROL-CTYerd9j.js.br +0 -0
  181. package/dist/client/assets/sizeCapture-INFHLROL-CTYerd9j.js.gz +0 -0
  182. package/dist/client/assets/{sparkles-Dk93lHg0.js → sparkles-CgaUBIwH.js} +1 -1
  183. package/dist/client/assets/{stateDiagram-D77RDMKH-CgYgneIv.js → stateDiagram-D77RDMKH-CQbb5x2_.js} +1 -1
  184. package/dist/client/assets/stateDiagram-D77RDMKH-CQbb5x2_.js.br +0 -0
  185. package/dist/client/assets/stateDiagram-D77RDMKH-CQbb5x2_.js.gz +0 -0
  186. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CJAVXcx1.js +1 -0
  187. package/dist/client/assets/{swimlanes-42K2YHIH-OWA9HFVf.js → swimlanes-42K2YHIH-BHZ4X2Fd.js} +1 -1
  188. package/dist/client/assets/swimlanes-42K2YHIH-BHZ4X2Fd.js.br +0 -0
  189. package/dist/client/assets/swimlanes-42K2YHIH-BHZ4X2Fd.js.gz +0 -0
  190. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-VL2nyI27.js +8 -0
  191. package/dist/client/assets/{timeline-definition-24CTP7MA-BAzx5DyO.js → timeline-definition-24CTP7MA-BrjH6R6w.js} +1 -1
  192. package/dist/client/assets/timeline-definition-24CTP7MA-BrjH6R6w.js.br +0 -0
  193. package/dist/client/assets/timeline-definition-24CTP7MA-BrjH6R6w.js.gz +0 -0
  194. package/dist/client/assets/{triangle-alert-CUIBOwqW.js → triangle-alert-C9IHD_eF.js} +1 -1
  195. package/dist/client/assets/{vennDiagram-4TSXK5OY-CBZ-PhTQ.js → vennDiagram-4TSXK5OY-C9f5jYgk.js} +1 -1
  196. package/dist/client/assets/vennDiagram-4TSXK5OY-C9f5jYgk.js.br +0 -0
  197. package/dist/client/assets/vennDiagram-4TSXK5OY-C9f5jYgk.js.gz +0 -0
  198. package/dist/client/assets/{videoCompression-BCnqCkRK.js → videoCompression-DJaU8exd.js} +1 -1
  199. package/dist/client/assets/videoCompression-DJaU8exd.js.br +0 -0
  200. package/dist/client/assets/videoCompression-DJaU8exd.js.gz +0 -0
  201. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-DbWzC6l5.js → wardleyDiagram-VM6X3IG4-6ljFvtiC.js} +1 -1
  202. package/dist/client/assets/wardleyDiagram-VM6X3IG4-6ljFvtiC.js.br +0 -0
  203. package/dist/client/assets/wardleyDiagram-VM6X3IG4-6ljFvtiC.js.gz +0 -0
  204. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BmApnTWE.js → xychartDiagram-S5SC5T6Z-BaR2hm3e.js} +1 -1
  205. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BaR2hm3e.js.br +0 -0
  206. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BaR2hm3e.js.gz +0 -0
  207. package/dist/client/index.html +2 -2
  208. package/dist/client/index.html.br +0 -0
  209. package/dist/client/index.html.gz +0 -0
  210. package/dist/client/sw.js +1 -1
  211. package/dist/client/sw.js.br +0 -0
  212. package/dist/client/sw.js.gz +0 -0
  213. package/dist/server/agent/collaborationCli.js +3 -3
  214. package/dist/server/agent/collaborationGroupRoutes.js +0 -2
  215. package/dist/server/agent/collaborationResultPresentation.js +14 -0
  216. package/dist/server/agent/collaborationService.js +3 -1
  217. package/dist/server/agent/collaborationStore.js +12 -21
  218. package/dist/server/agent/collaborationTaskBundles.js +2 -2
  219. package/dist/server/agent/collaborationTaskCli.js +4 -4
  220. package/dist/server/agent/collaborationTaskStore.js +87 -25
  221. package/dist/server/agent/collaborationTaskWorkspace.js +66 -1
  222. package/dist/server/agent/collaborationTeam.js +16 -0
  223. package/dist/server/agent/executionArchives.js +52 -0
  224. package/dist/server/entry.js +6 -1
  225. package/dist/server/federation/runtime.js +24 -6
  226. package/dist/server/routes/computer.js +198 -0
  227. package/dist/server/routes/computerTarget.js +23 -0
  228. package/dist/server/routes/guacamoleProtocol.js +55 -0
  229. package/dist/server/routes/rdp.js +162 -0
  230. package/dist/server/routes/terminal.js +375 -360
  231. package/dist/server/shared/computerPreferences.js +29 -0
  232. package/dist/server/utils/computerCredentials.js +88 -0
  233. package/dist/server/utils/sharedSampling.js +93 -0
  234. package/dist/server/utils/terminalOutputDelivery.js +8 -1
  235. package/dist/server/utils/tmuxMetadataWriter.js +61 -0
  236. package/package.json +4 -1
  237. package/runtime-manifest.json +4 -4
  238. package/dist/client/assets/App-BiLjlrk-.js +0 -171
  239. package/dist/client/assets/App-BiLjlrk-.js.br +0 -0
  240. package/dist/client/assets/App-BiLjlrk-.js.gz +0 -0
  241. package/dist/client/assets/ChangeWalkthroughPanel-DXprT-7m.js.br +0 -0
  242. package/dist/client/assets/ChangeWalkthroughPanel-DXprT-7m.js.gz +0 -0
  243. package/dist/client/assets/DagPlayground-DZcUunT5.js.br +0 -0
  244. package/dist/client/assets/DagPlayground-DZcUunT5.js.gz +0 -0
  245. package/dist/client/assets/DiffLab-Brv4DCTb.js.br +0 -0
  246. package/dist/client/assets/DiffLab-Brv4DCTb.js.gz +0 -0
  247. package/dist/client/assets/DiffReview-BuLu1qCi.js.br +0 -0
  248. package/dist/client/assets/DiffReview-BuLu1qCi.js.gz +0 -0
  249. package/dist/client/assets/DiffReviewLab-DSGamuAD.js.br +0 -0
  250. package/dist/client/assets/DiffReviewLab-DSGamuAD.js.gz +0 -0
  251. package/dist/client/assets/DiffViewer-BUCYxg27.js.br +0 -0
  252. package/dist/client/assets/DiffViewer-BUCYxg27.js.gz +0 -0
  253. package/dist/client/assets/ModelPreview-Bnlo-oVE.js.br +0 -0
  254. package/dist/client/assets/ModelPreview-Bnlo-oVE.js.gz +0 -0
  255. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js +0 -1678
  256. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js.br +0 -0
  257. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js.gz +0 -0
  258. package/dist/client/assets/abnfDiagram-VCTEODGH-K6Ppkrbt.js.br +0 -0
  259. package/dist/client/assets/abnfDiagram-VCTEODGH-K6Ppkrbt.js.gz +0 -0
  260. package/dist/client/assets/app-CATnTUZF.js +0 -35
  261. package/dist/client/assets/app-CATnTUZF.js.br +0 -0
  262. package/dist/client/assets/app-CATnTUZF.js.gz +0 -0
  263. package/dist/client/assets/arc-Co2OQqtH.js.br +0 -0
  264. package/dist/client/assets/arc-Co2OQqtH.js.gz +0 -0
  265. package/dist/client/assets/architectureDiagram-5GKGNRK7-BQnl7tVF.js.br +0 -0
  266. package/dist/client/assets/architectureDiagram-5GKGNRK7-BQnl7tVF.js.gz +0 -0
  267. package/dist/client/assets/blockDiagram-I7D4REHJ-Bm5D5-qq.js.br +0 -0
  268. package/dist/client/assets/blockDiagram-I7D4REHJ-Bm5D5-qq.js.gz +0 -0
  269. package/dist/client/assets/c4Diagram-7LVT6UL2-B9oPpey8.js.br +0 -0
  270. package/dist/client/assets/c4Diagram-7LVT6UL2-B9oPpey8.js.gz +0 -0
  271. package/dist/client/assets/channel-Czv2W4qA.js +0 -1
  272. package/dist/client/assets/chunk-F27PBJKO-CJ1rotBe.js.br +0 -0
  273. package/dist/client/assets/chunk-F27PBJKO-CJ1rotBe.js.gz +0 -0
  274. package/dist/client/assets/chunk-IMKFNOWR-UYKZ5uOG.js.br +0 -0
  275. package/dist/client/assets/chunk-IMKFNOWR-UYKZ5uOG.js.gz +0 -0
  276. package/dist/client/assets/chunk-SVP7TREG-DCTDYa1E.js.br +0 -0
  277. package/dist/client/assets/chunk-SVP7TREG-DCTDYa1E.js.gz +0 -0
  278. package/dist/client/assets/chunk-TICWLB2K-wHiaOsBh.js.br +0 -0
  279. package/dist/client/assets/chunk-TICWLB2K-wHiaOsBh.js.gz +0 -0
  280. package/dist/client/assets/classDiagram-ZZMXUADV-BdvOOL9k.js +0 -1
  281. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BdvOOL9k.js +0 -1
  282. package/dist/client/assets/cose-bilkent-JH36ORCC-DnhlPBLr.js.br +0 -0
  283. package/dist/client/assets/cose-bilkent-JH36ORCC-DnhlPBLr.js.gz +0 -0
  284. package/dist/client/assets/cynefin-OW5HDTMX-BsjzNqJZ.js.br +0 -0
  285. package/dist/client/assets/cynefin-OW5HDTMX-BsjzNqJZ.js.gz +0 -0
  286. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-sVUb0zY5.js.br +0 -0
  287. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-sVUb0zY5.js.gz +0 -0
  288. package/dist/client/assets/dagre-GXQ25YYZ-YErk8S-c.js.br +0 -0
  289. package/dist/client/assets/dagre-GXQ25YYZ-YErk8S-c.js.gz +0 -0
  290. package/dist/client/assets/diagram-S7CK7UJ4-BaHbV5ho.js.br +0 -0
  291. package/dist/client/assets/diagram-S7CK7UJ4-BaHbV5ho.js.gz +0 -0
  292. package/dist/client/assets/diagram-UQ7AKVKN-BocCYx3V.js.br +0 -0
  293. package/dist/client/assets/diagram-UQ7AKVKN-BocCYx3V.js.gz +0 -0
  294. package/dist/client/assets/diagram-VSXAHHWV-ZMTYwWOQ.js.br +0 -0
  295. package/dist/client/assets/diagram-VSXAHHWV-ZMTYwWOQ.js.gz +0 -0
  296. package/dist/client/assets/diagram-VX7I27RA-xmk_EY1h.js.br +0 -0
  297. package/dist/client/assets/diagram-VX7I27RA-xmk_EY1h.js.gz +0 -0
  298. package/dist/client/assets/diagram-Z3DM3KII-DKDY_cG6.js.br +0 -0
  299. package/dist/client/assets/diagram-Z3DM3KII-DKDY_cG6.js.gz +0 -0
  300. package/dist/client/assets/ebnfDiagram-PWID7BFC-C_0z-Jx9.js.br +0 -0
  301. package/dist/client/assets/ebnfDiagram-PWID7BFC-C_0z-Jx9.js.gz +0 -0
  302. package/dist/client/assets/erDiagram-RLTQ6QDP-DHUaG7mt.js.br +0 -0
  303. package/dist/client/assets/erDiagram-RLTQ6QDP-DHUaG7mt.js.gz +0 -0
  304. package/dist/client/assets/flowDiagram-HODETNUW-BMNsdMBD.js.br +0 -0
  305. package/dist/client/assets/flowDiagram-HODETNUW-BMNsdMBD.js.gz +0 -0
  306. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BH1AiKuF.js.br +0 -0
  307. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BH1AiKuF.js.gz +0 -0
  308. package/dist/client/assets/gestureArbiter-DvupkPra.js.br +0 -0
  309. package/dist/client/assets/gestureArbiter-DvupkPra.js.gz +0 -0
  310. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DAgV0MV-.js.br +0 -0
  311. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DAgV0MV-.js.gz +0 -0
  312. package/dist/client/assets/index-Bi9A6QUP.css +0 -1
  313. package/dist/client/assets/index-Bi9A6QUP.css.br +0 -0
  314. package/dist/client/assets/index-Bi9A6QUP.css.gz +0 -0
  315. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BpZwsQvw.js.br +0 -0
  316. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BpZwsQvw.js.gz +0 -0
  317. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXEeFLvw.js.br +0 -0
  318. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXEeFLvw.js.gz +0 -0
  319. package/dist/client/assets/kanban-definition-UXKFOSKX-CV9ivL5u.js.br +0 -0
  320. package/dist/client/assets/kanban-definition-UXKFOSKX-CV9ivL5u.js.gz +0 -0
  321. package/dist/client/assets/layout-DsKKsJKX.js.br +0 -0
  322. package/dist/client/assets/layout-DsKKsJKX.js.gz +0 -0
  323. package/dist/client/assets/linear-Dl9MDNV3.js.br +0 -0
  324. package/dist/client/assets/linear-Dl9MDNV3.js.gz +0 -0
  325. package/dist/client/assets/mermaid.core-CxZFe3as.js.br +0 -0
  326. package/dist/client/assets/mermaid.core-CxZFe3as.js.gz +0 -0
  327. package/dist/client/assets/mindmap-definition-YA3MSWOX-C3ormYdL.js.br +0 -0
  328. package/dist/client/assets/mindmap-definition-YA3MSWOX-C3ormYdL.js.gz +0 -0
  329. package/dist/client/assets/passwordBootstrap-a8z6jJNY.js.br +0 -0
  330. package/dist/client/assets/passwordBootstrap-a8z6jJNY.js.gz +0 -0
  331. package/dist/client/assets/pegDiagram-XKGWAZYB-CG0wNylH.js.br +0 -0
  332. package/dist/client/assets/pegDiagram-XKGWAZYB-CG0wNylH.js.gz +0 -0
  333. package/dist/client/assets/pieDiagram-E7YTZNPT-D2-ydqyX.js.br +0 -0
  334. package/dist/client/assets/pieDiagram-E7YTZNPT-D2-ydqyX.js.gz +0 -0
  335. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CK3fRC18.js.br +0 -0
  336. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CK3fRC18.js.gz +0 -0
  337. package/dist/client/assets/railroadDiagram-O6MQD6OU-V4EFlydu.js.br +0 -0
  338. package/dist/client/assets/railroadDiagram-O6MQD6OU-V4EFlydu.js.gz +0 -0
  339. package/dist/client/assets/referenceLongPress-BjmYd1vB.js.br +0 -0
  340. package/dist/client/assets/referenceLongPress-BjmYd1vB.js.gz +0 -0
  341. package/dist/client/assets/requirementDiagram-BXWQKSXE-Di1_tyAX.js.br +0 -0
  342. package/dist/client/assets/requirementDiagram-BXWQKSXE-Di1_tyAX.js.gz +0 -0
  343. package/dist/client/assets/sankeyDiagram-P5KCCOFB-D-qpWNza.js.br +0 -0
  344. package/dist/client/assets/sankeyDiagram-P5KCCOFB-D-qpWNza.js.gz +0 -0
  345. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DmIxafrB.js.br +0 -0
  346. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DmIxafrB.js.gz +0 -0
  347. package/dist/client/assets/sizeCapture-INFHLROL-4MTh3tTt.js.br +0 -0
  348. package/dist/client/assets/sizeCapture-INFHLROL-4MTh3tTt.js.gz +0 -0
  349. package/dist/client/assets/stateDiagram-D77RDMKH-CgYgneIv.js.br +0 -0
  350. package/dist/client/assets/stateDiagram-D77RDMKH-CgYgneIv.js.gz +0 -0
  351. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CvPf0fxf.js +0 -1
  352. package/dist/client/assets/swimlanes-42K2YHIH-OWA9HFVf.js.br +0 -0
  353. package/dist/client/assets/swimlanes-42K2YHIH-OWA9HFVf.js.gz +0 -0
  354. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CZ_OauyW.js +0 -8
  355. package/dist/client/assets/timeline-definition-24CTP7MA-BAzx5DyO.js.br +0 -0
  356. package/dist/client/assets/timeline-definition-24CTP7MA-BAzx5DyO.js.gz +0 -0
  357. package/dist/client/assets/vennDiagram-4TSXK5OY-CBZ-PhTQ.js.br +0 -0
  358. package/dist/client/assets/vennDiagram-4TSXK5OY-CBZ-PhTQ.js.gz +0 -0
  359. package/dist/client/assets/videoCompression-BCnqCkRK.js.br +0 -0
  360. package/dist/client/assets/videoCompression-BCnqCkRK.js.gz +0 -0
  361. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DbWzC6l5.js.br +0 -0
  362. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DbWzC6l5.js.gz +0 -0
  363. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmApnTWE.js.br +0 -0
  364. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmApnTWE.js.gz +0 -0
@@ -0,0 +1,198 @@
1
+ import { Router } from 'express';
2
+ import { isDeepStrictEqual } from 'node:util';
3
+ import { hostname } from 'node:os';
4
+ import { Socket } from 'node:net';
5
+ import { resolveComputerHost } from './computerTarget.js';
6
+ import { handleRdpWebSocket, rdpBackendAvailable, stopRdpBridges } from './rdp.js';
7
+ import { computerLoginKey, computerPreferences } from '../shared/computerPreferences.js';
8
+ import { loadSettings, updateSettings } from '../utils/settings.js';
9
+ import { ComputerCredentialStore } from '../utils/computerCredentials.js';
10
+ export function createComputerRouter(read = loadSettings, update = updateSettings, credentials = new ComputerCredentialStore()) {
11
+ const router = Router();
12
+ router.get('/status', async (_req, res) => res.json({ platform: process.platform, hostname: hostname(), rdpAvailable: await rdpBackendAvailable() }));
13
+ router.get('/preferences', (_req, res) => {
14
+ try {
15
+ const saved = read().computerControl;
16
+ const preferences = computerPreferences(saved, process.platform);
17
+ let credentialKeys = [], credentialError = false;
18
+ try {
19
+ credentialKeys = [preferences.local, preferences.remote].filter(profile => profile.rememberLogin && credentials.has(profile)).map(computerLoginKey);
20
+ }
21
+ catch {
22
+ credentialError = true;
23
+ }
24
+ res.set('Cache-Control', 'no-store').json({ preferences, configured: Boolean(saved), credentialKeys, credentialError });
25
+ }
26
+ catch {
27
+ res.status(500).json({ error: 'COMPUTER_PREFERENCES_READ_FAILED' });
28
+ }
29
+ });
30
+ // The full-service encrypted API boundary applies to every credential route.
31
+ router.post('/credentials/:action', (req, res) => {
32
+ res.set('Cache-Control', 'no-store');
33
+ try {
34
+ const { profile, password } = req.body ?? {};
35
+ const preferences = computerPreferences(read().computerControl, process.platform);
36
+ const matched = [preferences.local, preferences.remote].find(value => isDeepStrictEqual(value, profile));
37
+ const action = req.params.action;
38
+ const allowedKeys = action === 'save' ? ['profile', 'password'] : ['profile'];
39
+ if (!matched || !['save', 'use', 'forget'].includes(action) || Object.keys(req.body).some(key => !allowedKeys.includes(key))) {
40
+ res.status(400).json({ error: 'INVALID_COMPUTER_CREDENTIALS' });
41
+ return;
42
+ }
43
+ if (action === 'forget') {
44
+ credentials.forget(matched);
45
+ res.json({ saved: false });
46
+ return;
47
+ }
48
+ if (!matched.rememberLogin) {
49
+ res.status(400).json({ error: 'COMPUTER_LOGIN_NOT_REMEMBERED' });
50
+ return;
51
+ }
52
+ if (action === 'save') {
53
+ if (typeof password !== 'string' || !password || password.length > 1024 || password.includes('\0')) {
54
+ res.status(400).json({ error: 'INVALID_COMPUTER_CREDENTIALS' });
55
+ return;
56
+ }
57
+ credentials.save(matched, password);
58
+ res.json({ saved: true });
59
+ return;
60
+ }
61
+ const secret = credentials.get(matched);
62
+ if (!secret) {
63
+ res.status(404).json({ error: 'COMPUTER_CREDENTIALS_MISSING' });
64
+ return;
65
+ }
66
+ // Only the authenticated service page receives it, in memory, over Noise.
67
+ res.json({ password: secret });
68
+ }
69
+ catch {
70
+ res.status(500).json({ error: 'COMPUTER_CREDENTIALS_FAILED' });
71
+ }
72
+ });
73
+ router.put('/preferences', (req, res) => {
74
+ const preferences = computerPreferences(req.body, process.platform);
75
+ // Strict projection prevents credentials and unknown fields entering settings.
76
+ if (!req.body || !isDeepStrictEqual(preferences, req.body)) {
77
+ res.status(400).json({ error: 'INVALID_COMPUTER_PREFERENCES' });
78
+ return;
79
+ }
80
+ try {
81
+ update((settings) => { settings.computerControl = preferences; });
82
+ res.json({ preferences });
83
+ }
84
+ catch {
85
+ res.status(500).json({ error: 'COMPUTER_PREFERENCES_WRITE_FAILED' });
86
+ }
87
+ });
88
+ return router;
89
+ }
90
+ export default createComputerRouter();
91
+ const bridges = new Set();
92
+ export function stopComputerBridges() {
93
+ stopRdpBridges();
94
+ for (const bridge of bridges)
95
+ bridge.destroy();
96
+ bridges.clear();
97
+ }
98
+ /** RFB bytes are base64 records inside the existing authenticated Noise tunnel.
99
+ * The eight-record receive window applies TCP backpressure without dropping bytes. */
100
+ export function handleComputerWebSocket(socket, host, options) {
101
+ if (options?.protocol === 'rdp') {
102
+ handleRdpWebSocket(socket, host, Number(options.port ?? 3389));
103
+ return;
104
+ }
105
+ if (options?.protocol && options.protocol !== 'vnc') {
106
+ socket.close(4400, 'COMPUTER_INVALID_PROTOCOL');
107
+ return;
108
+ }
109
+ let closed = false;
110
+ let tcp;
111
+ let sent = 0;
112
+ let acked = 0;
113
+ let lastAckAt = Date.now();
114
+ let connected = false;
115
+ const finish = (code = 1000, reason = '') => {
116
+ if (closed)
117
+ return;
118
+ closed = true;
119
+ clearTimeout(connectTimer);
120
+ clearInterval(flowTimer);
121
+ if (tcp) {
122
+ bridges.delete(tcp);
123
+ tcp.destroy();
124
+ }
125
+ socket.close(code, reason);
126
+ };
127
+ const connectTimer = setTimeout(() => finish(4408, 'COMPUTER_CONNECT_TIMEOUT'), 10_000);
128
+ const flowTimer = setInterval(() => {
129
+ if (sent > acked && Date.now() - lastAckAt > 30_000)
130
+ finish(4408, 'COMPUTER_STREAM_TIMEOUT');
131
+ }, 5000);
132
+ const pump = () => {
133
+ if (closed || !tcp)
134
+ return;
135
+ while (sent - acked < 8 && tcp.readableLength > 0) {
136
+ const bytes = tcp.read(Math.min(tcp.readableLength, 32 * 1024));
137
+ if (!bytes)
138
+ break;
139
+ if (sent === acked)
140
+ lastAckAt = Date.now();
141
+ socket.send(JSON.stringify({ type: 'data', seq: ++sent, data: bytes.toString('base64') }));
142
+ if (closed)
143
+ break;
144
+ }
145
+ };
146
+ socket.on('close', () => finish());
147
+ socket.on('error', () => finish());
148
+ socket.on('message', raw => {
149
+ if (closed)
150
+ return;
151
+ try {
152
+ const message = JSON.parse(raw.toString());
153
+ if (message.type === 'ack') {
154
+ if (!Number.isSafeInteger(message.seq) || message.seq <= acked || message.seq > sent)
155
+ throw new Error('INVALID_ACK');
156
+ acked = message.seq;
157
+ lastAckAt = Date.now();
158
+ pump();
159
+ }
160
+ else if (message.type === 'data') {
161
+ if (!connected || !tcp || typeof message.data !== 'string' || message.data.length > 88_000
162
+ || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(message.data))
163
+ throw new Error('INVALID_DATA');
164
+ const bytes = Buffer.from(message.data, 'base64');
165
+ if (!bytes.length || bytes.length > 64 * 1024 || tcp.writableLength + bytes.length > 1024 * 1024)
166
+ throw new Error('INPUT_LIMIT');
167
+ tcp.write(bytes);
168
+ }
169
+ else
170
+ throw new Error('INVALID_MESSAGE');
171
+ }
172
+ catch {
173
+ finish(4400, 'COMPUTER_INVALID_MESSAGE');
174
+ }
175
+ });
176
+ void (async () => {
177
+ try {
178
+ const address = await resolveComputerHost(host);
179
+ if (closed)
180
+ return;
181
+ tcp = new Socket();
182
+ bridges.add(tcp);
183
+ tcp.setNoDelay(true);
184
+ tcp.on('readable', pump);
185
+ tcp.on('connect', () => { connected = true; clearTimeout(connectTimer); });
186
+ tcp.on('end', () => finish(1000, 'COMPUTER_DISCONNECTED'));
187
+ tcp.on('close', () => finish(1000, 'COMPUTER_DISCONNECTED'));
188
+ tcp.on('error', error => {
189
+ const code = error.code;
190
+ finish(4502, code === 'ECONNREFUSED' ? 'COMPUTER_SCREEN_SHARING_OFF' : 'COMPUTER_UNREACHABLE');
191
+ });
192
+ tcp.connect({ host: address.address, family: address.family, port: 5900 });
193
+ }
194
+ catch (reason) {
195
+ finish(4502, reason instanceof Error && /^COMPUTER_(?:INVALID_HOST|PRIVATE_HOST_ONLY)$/.test(reason.message) ? reason.message : 'COMPUTER_UNREACHABLE');
196
+ }
197
+ })();
198
+ }
@@ -0,0 +1,23 @@
1
+ import { lookup } from 'node:dns/promises';
2
+ import { isIP } from 'node:net';
3
+ export function privateComputerAddress(address) {
4
+ if (isIP(address) === 4) {
5
+ const [a, b] = address.split('.').map(Number);
6
+ return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168);
7
+ }
8
+ const value = address.toLowerCase();
9
+ if (value.startsWith('::ffff:'))
10
+ return privateComputerAddress(value.slice(7));
11
+ return isIP(value) === 6 && (value === '::1' || value.startsWith('fc') || value.startsWith('fd'));
12
+ }
13
+ /** Resolve once, reject mixed public/private DNS answers, and dial the pinned IP. */
14
+ export async function resolveComputerHost(host) {
15
+ host = host.trim();
16
+ if (!host || host.length > 253 || (!isIP(host) && !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/i.test(host))) {
17
+ throw new Error('COMPUTER_INVALID_HOST');
18
+ }
19
+ const addresses = await lookup(host, { all: true });
20
+ if (!addresses.length || addresses.some(item => !privateComputerAddress(item.address)))
21
+ throw new Error('COMPUTER_PRIVATE_HOST_ONLY');
22
+ return addresses[0];
23
+ }
@@ -0,0 +1,55 @@
1
+ export function guacamoleInstruction(...values) {
2
+ return values.map(value => `${Array.from(value).length}.${value}`).join(',') + ';';
3
+ }
4
+ /** Incremental UTF-8-decoded Guacamole parser. Lengths count Unicode code points. */
5
+ export class GuacamoleParser {
6
+ instruction;
7
+ digits = '';
8
+ remaining = 0;
9
+ value = '';
10
+ values = [];
11
+ phase = 'length';
12
+ size = 0;
13
+ constructor(instruction) {
14
+ this.instruction = instruction;
15
+ }
16
+ receive(chunk) {
17
+ for (const char of chunk) {
18
+ if (++this.size > 2 * 1024 * 1024)
19
+ throw new Error('GUAC_INSTRUCTION_LIMIT');
20
+ if (this.phase === 'length') {
21
+ if (/^[0-9]$/.test(char) && this.digits.length < 7)
22
+ this.digits += char;
23
+ else if (char === '.' && this.digits) {
24
+ this.remaining = Number(this.digits);
25
+ this.digits = '';
26
+ if (this.remaining > 1024 * 1024)
27
+ throw new Error('GUAC_ELEMENT_LIMIT');
28
+ this.phase = this.remaining ? 'value' : 'separator';
29
+ }
30
+ else
31
+ throw new Error('GUAC_INVALID_LENGTH');
32
+ }
33
+ else if (this.phase === 'value') {
34
+ this.value += char;
35
+ if (--this.remaining === 0)
36
+ this.phase = 'separator';
37
+ }
38
+ else {
39
+ if (char !== ',' && char !== ';')
40
+ throw new Error('GUAC_INVALID_SEPARATOR');
41
+ this.values.push(this.value);
42
+ this.value = '';
43
+ this.phase = 'length';
44
+ if (this.values.length > 256)
45
+ throw new Error('GUAC_ARGUMENT_LIMIT');
46
+ if (char === ';') {
47
+ const values = this.values;
48
+ this.values = [];
49
+ this.size = 0;
50
+ this.instruction(values);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,162 @@
1
+ import { Socket } from 'node:net';
2
+ import { StringDecoder } from 'node:string_decoder';
3
+ import { resolveComputerHost } from './computerTarget.js';
4
+ import { GuacamoleParser, guacamoleInstruction } from './guacamoleProtocol.js';
5
+ const guacdPort = () => Number(process.env.TERMDOCK_GUACD_PORT) || 4822;
6
+ const bridges = new Set();
7
+ export function stopRdpBridges() { for (const tcp of bridges)
8
+ tcp.destroy(); bridges.clear(); }
9
+ export function rdpBackendAvailable() {
10
+ return new Promise(resolve => {
11
+ const tcp = new Socket();
12
+ const done = (available) => { tcp.destroy(); resolve(available); };
13
+ tcp.setTimeout(500, () => done(false));
14
+ tcp.once('error', () => done(false));
15
+ tcp.once('connect', () => done(true));
16
+ tcp.connect(guacdPort(), '127.0.0.1');
17
+ });
18
+ }
19
+ /** Only this server builds guacd's handshake. Credentials arrive inside the
20
+ * authenticated encrypted logical socket, never query strings or logs. */
21
+ export function handleRdpWebSocket(socket, host, port) {
22
+ let closed = false;
23
+ let started = false;
24
+ let ready = false;
25
+ let tcp;
26
+ let sent = 0;
27
+ let acked = 0;
28
+ let lastAckAt = Date.now();
29
+ let credentials = {};
30
+ const finish = (code = 1000, reason = '') => {
31
+ if (closed)
32
+ return;
33
+ closed = true;
34
+ credentials = {};
35
+ clearTimeout(timer);
36
+ clearInterval(flowTimer);
37
+ if (tcp) {
38
+ bridges.delete(tcp);
39
+ tcp.destroy();
40
+ }
41
+ socket.close(code, reason);
42
+ };
43
+ const timer = setTimeout(() => finish(4408, 'COMPUTER_CONNECT_TIMEOUT'), 15_000);
44
+ const flowTimer = setInterval(() => {
45
+ if (sent > acked && Date.now() - lastAckAt > 30_000)
46
+ finish(4408, 'COMPUTER_STREAM_TIMEOUT');
47
+ }, 5000);
48
+ const decoder = new StringDecoder('utf8');
49
+ let instructions = [];
50
+ let handshakeSent = false;
51
+ const parser = new GuacamoleParser(values => {
52
+ const [opcode, ...args] = values;
53
+ if (!ready && opcode === 'args' && !handshakeSent) {
54
+ handshakeSent = true;
55
+ tcp.write(guacamoleInstruction('size', credentials.width, credentials.height, '96')
56
+ + guacamoleInstruction('audio') + guacamoleInstruction('video')
57
+ + guacamoleInstruction('image', 'image/png', 'image/jpeg')
58
+ + guacamoleInstruction('connect', ...args.map(name => name.startsWith('VERSION_') ? 'VERSION_1_5_0' : credentials[name] ?? '')));
59
+ credentials = {};
60
+ }
61
+ else if (opcode === 'ready') {
62
+ if (!handshakeSent || ready)
63
+ throw new Error('GUAC_INVALID_HANDSHAKE');
64
+ ready = true;
65
+ clearTimeout(timer);
66
+ instructions.push(values);
67
+ }
68
+ else if (ready || opcode === 'error')
69
+ instructions.push(values);
70
+ else
71
+ throw new Error('GUAC_INVALID_HANDSHAKE');
72
+ });
73
+ const pump = () => {
74
+ if (closed || !tcp)
75
+ return;
76
+ try {
77
+ while (sent - acked < 8 && tcp.readableLength > 0) {
78
+ const bytes = tcp.read(Math.min(tcp.readableLength, 32 * 1024));
79
+ if (!bytes)
80
+ break;
81
+ parser.receive(decoder.write(bytes));
82
+ if (instructions.length) {
83
+ if (sent === acked)
84
+ lastAckAt = Date.now();
85
+ socket.send(JSON.stringify({ type: 'instructions', seq: ++sent, instructions }));
86
+ instructions = [];
87
+ }
88
+ }
89
+ }
90
+ catch {
91
+ finish(4502, 'COMPUTER_INVALID_MESSAGE');
92
+ }
93
+ };
94
+ socket.on('close', () => finish());
95
+ socket.on('error', () => finish());
96
+ socket.on('message', raw => {
97
+ if (closed)
98
+ return;
99
+ try {
100
+ if (raw.toString().length > 128 * 1024)
101
+ throw new Error('INPUT_LIMIT');
102
+ const message = JSON.parse(raw.toString());
103
+ if (message.type === 'start' && !started) {
104
+ started = true;
105
+ for (const key of ['username', 'password', 'domain']) {
106
+ if (typeof message[key] !== 'string' || message[key].length > 1024 || message[key].includes('\0'))
107
+ throw new Error('INVALID_CREDENTIALS');
108
+ }
109
+ if (!message.username.trim() || !message.password || typeof message.ignoreCert !== 'boolean')
110
+ throw new Error('INVALID_CREDENTIALS');
111
+ if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
112
+ throw new Error('INVALID_PORT');
113
+ credentials = {
114
+ username: message.username, password: message.password, domain: message.domain,
115
+ port: String(port), security: 'any', 'ignore-cert': String(message.ignoreCert),
116
+ width: String(Math.max(640, Math.min(2560, Number(message.width) || 1280))),
117
+ height: String(Math.max(480, Math.min(1600, Number(message.height) || 800))),
118
+ 'color-depth': '32', 'disable-audio': 'true', 'enable-drive': 'false',
119
+ 'disable-copy': 'true', 'server-layout': 'en-us-qwerty', 'resize-method': 'display-update',
120
+ };
121
+ void resolveComputerHost(host).then(address => {
122
+ if (closed)
123
+ return;
124
+ credentials.hostname = address.address;
125
+ tcp = new Socket();
126
+ bridges.add(tcp);
127
+ tcp.setNoDelay(true);
128
+ tcp.on('readable', pump);
129
+ tcp.once('connect', () => { tcp.write(guacamoleInstruction('select', 'rdp')); });
130
+ tcp.once('error', () => finish(4503, 'COMPUTER_RDP_BACKEND_UNAVAILABLE'));
131
+ tcp.once('end', () => finish(1000, 'COMPUTER_DISCONNECTED'));
132
+ tcp.once('close', () => finish(1000, 'COMPUTER_DISCONNECTED'));
133
+ tcp.connect(guacdPort(), '127.0.0.1');
134
+ }).catch(reason => finish(4400, reason instanceof Error && /^COMPUTER_(?:INVALID_HOST|PRIVATE_HOST_ONLY)$/.test(reason.message) ? reason.message : 'COMPUTER_UNREACHABLE'));
135
+ }
136
+ else if (message.type === 'ack') {
137
+ if (!Number.isSafeInteger(message.seq) || message.seq <= acked || message.seq > sent)
138
+ throw new Error('INVALID_ACK');
139
+ acked = message.seq;
140
+ lastAckAt = Date.now();
141
+ pump();
142
+ }
143
+ else if (message.type === 'instruction' && ready && tcp) {
144
+ // Disallow re-handshakes, filesystem, recording, arbitrary connection
145
+ // parameters, and other protocols through this full-service bridge.
146
+ if (!['sync', 'key', 'mouse', 'clipboard', 'blob', 'end', 'ack', 'nop', 'size', 'disconnect'].includes(message.opcode)
147
+ || !Array.isArray(message.args) || message.args.length > 8
148
+ || message.args.some((arg) => typeof arg !== 'string' || arg.length > 64 * 1024)
149
+ || tcp.writableLength > 1024 * 1024)
150
+ throw new Error('INVALID_INSTRUCTION');
151
+ 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))))
152
+ throw new Error('INVALID_SIZE');
153
+ tcp.write(guacamoleInstruction(message.opcode, ...message.args));
154
+ }
155
+ else
156
+ throw new Error('INVALID_MESSAGE');
157
+ }
158
+ catch {
159
+ finish(4400, 'COMPUTER_INVALID_MESSAGE');
160
+ }
161
+ });
162
+ }