termdock 1.4.233 → 1.4.234

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 (322) hide show
  1. package/.desktop-dist/main.js +1 -39
  2. package/dist/client/assets/{App-CBhXXSRZ.js → App-3pt5-ilC.js} +3 -3
  3. package/dist/client/assets/App-3pt5-ilC.js.br +0 -0
  4. package/dist/client/assets/App-3pt5-ilC.js.gz +0 -0
  5. package/dist/client/assets/{ChangeWalkthroughPanel-CeOq4iWF.js → ChangeWalkthroughPanel-BV8mOdXi.js} +1 -1
  6. package/dist/client/assets/ChangeWalkthroughPanel-BV8mOdXi.js.br +0 -0
  7. package/dist/client/assets/ChangeWalkthroughPanel-BV8mOdXi.js.gz +0 -0
  8. package/dist/client/assets/{DagPlayground-nzwoofiL.js → DagPlayground-gJS2zbWl.js} +1 -1
  9. package/dist/client/assets/DagPlayground-gJS2zbWl.js.br +0 -0
  10. package/dist/client/assets/DagPlayground-gJS2zbWl.js.gz +0 -0
  11. package/dist/client/assets/{DiffLab-BA5JJf_P.js → DiffLab-DW2zymWD.js} +1 -1
  12. package/dist/client/assets/DiffLab-DW2zymWD.js.br +0 -0
  13. package/dist/client/assets/DiffLab-DW2zymWD.js.gz +0 -0
  14. package/dist/client/assets/{DiffReview-CNnJIPtm.js → DiffReview-9KJ9EdtF.js} +1 -1
  15. package/dist/client/assets/DiffReview-9KJ9EdtF.js.br +0 -0
  16. package/dist/client/assets/DiffReview-9KJ9EdtF.js.gz +0 -0
  17. package/dist/client/assets/{DiffReviewLab-CH9k1x7c.js → DiffReviewLab-C1jzKsUR.js} +1 -1
  18. package/dist/client/assets/DiffReviewLab-C1jzKsUR.js.br +0 -0
  19. package/dist/client/assets/DiffReviewLab-C1jzKsUR.js.gz +0 -0
  20. package/dist/client/assets/{DiffViewer-D17tBMWn.js → DiffViewer-QEeO1VeZ.js} +1 -1
  21. package/dist/client/assets/DiffViewer-QEeO1VeZ.js.br +0 -0
  22. package/dist/client/assets/DiffViewer-QEeO1VeZ.js.gz +0 -0
  23. package/dist/client/assets/{ModelPreview-eUZEjzgg.js → ModelPreview-CWgs8gwV.js} +1 -1
  24. package/dist/client/assets/ModelPreview-CWgs8gwV.js.br +0 -0
  25. package/dist/client/assets/ModelPreview-CWgs8gwV.js.gz +0 -0
  26. package/dist/client/assets/{RightSidebar-DQSjBvAA.js → RightSidebar-D4iQycSs.js} +4 -4
  27. package/dist/client/assets/RightSidebar-D4iQycSs.js.br +0 -0
  28. package/dist/client/assets/RightSidebar-D4iQycSs.js.gz +0 -0
  29. package/dist/client/assets/{abnfDiagram-VCTEODGH-CeIsieUL.js → abnfDiagram-VCTEODGH-C68PyHq_.js} +1 -1
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-C68PyHq_.js.br +0 -0
  31. package/dist/client/assets/abnfDiagram-VCTEODGH-C68PyHq_.js.gz +0 -0
  32. package/dist/client/assets/app-BU7kQEH9.js +35 -0
  33. package/dist/client/assets/app-BU7kQEH9.js.br +0 -0
  34. package/dist/client/assets/app-BU7kQEH9.js.gz +0 -0
  35. package/dist/client/assets/{arc-B0iWWfl8.js → arc-BFWEvChC.js} +1 -1
  36. package/dist/client/assets/arc-BFWEvChC.js.br +0 -0
  37. package/dist/client/assets/arc-BFWEvChC.js.gz +0 -0
  38. package/dist/client/assets/{architectureDiagram-5GKGNRK7-IV8_iAgW.js → architectureDiagram-5GKGNRK7-CW9uKAFu.js} +1 -1
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-CW9uKAFu.js.br +0 -0
  40. package/dist/client/assets/architectureDiagram-5GKGNRK7-CW9uKAFu.js.gz +0 -0
  41. package/dist/client/assets/{blockDiagram-I7D4REHJ-DWdbwev4.js → blockDiagram-I7D4REHJ-C7Toh4ra.js} +1 -1
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-C7Toh4ra.js.br +0 -0
  43. package/dist/client/assets/blockDiagram-I7D4REHJ-C7Toh4ra.js.gz +0 -0
  44. package/dist/client/assets/{c4Diagram-7LVT6UL2-DL6qM9qX.js → c4Diagram-7LVT6UL2-C0qVAmVY.js} +1 -1
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-C0qVAmVY.js.br +0 -0
  46. package/dist/client/assets/c4Diagram-7LVT6UL2-C0qVAmVY.js.gz +0 -0
  47. package/dist/client/assets/channel-C_cSN9aY.js +1 -0
  48. package/dist/client/assets/{chunk-2Q5K7J3B-CADcnv26.js → chunk-2Q5K7J3B-C6Nq29YU.js} +1 -1
  49. package/dist/client/assets/{chunk-5VM5RSS4-Dy92AQjN.js → chunk-5VM5RSS4-CtbqynTp.js} +1 -1
  50. package/dist/client/assets/{chunk-F27PBJKO-CXMkCSL3.js → chunk-F27PBJKO-BpeUMCig.js} +1 -1
  51. package/dist/client/assets/chunk-F27PBJKO-BpeUMCig.js.br +0 -0
  52. package/dist/client/assets/chunk-F27PBJKO-BpeUMCig.js.gz +0 -0
  53. package/dist/client/assets/{chunk-IMKFNOWR-BHb_PEkZ.js → chunk-IMKFNOWR-CrP5u-oM.js} +1 -1
  54. package/dist/client/assets/chunk-IMKFNOWR-CrP5u-oM.js.br +0 -0
  55. package/dist/client/assets/chunk-IMKFNOWR-CrP5u-oM.js.gz +0 -0
  56. package/dist/client/assets/{chunk-JWPE2WC7-B4h9BTig.js → chunk-JWPE2WC7-BKyQo8Ez.js} +1 -1
  57. package/dist/client/assets/{chunk-POPQ4Y6H-DMc2ZxT_.js → chunk-POPQ4Y6H-C-rOKTS_.js} +1 -1
  58. package/dist/client/assets/{chunk-SVP7TREG-D2bHA6KO.js → chunk-SVP7TREG-D_el56_y.js} +1 -1
  59. package/dist/client/assets/chunk-SVP7TREG-D_el56_y.js.br +0 -0
  60. package/dist/client/assets/chunk-SVP7TREG-D_el56_y.js.gz +0 -0
  61. package/dist/client/assets/{chunk-TICWLB2K-CSV5x8Yz.js → chunk-TICWLB2K-BW4G8nfd.js} +1 -1
  62. package/dist/client/assets/chunk-TICWLB2K-BW4G8nfd.js.br +0 -0
  63. package/dist/client/assets/chunk-TICWLB2K-BW4G8nfd.js.gz +0 -0
  64. package/dist/client/assets/{chunk-XXDRQBXY-CEkyVIOo.js → chunk-XXDRQBXY-Bp0To9IO.js} +1 -1
  65. package/dist/client/assets/classDiagram-ZZMXUADV-g86YsEDj.js +1 -0
  66. package/dist/client/assets/classDiagram-v2-VYDZK3BY-g86YsEDj.js +1 -0
  67. package/dist/client/assets/{cose-bilkent-JH36ORCC-CUiXAbSC.js → cose-bilkent-JH36ORCC-BZopJ5Vk.js} +1 -1
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-BZopJ5Vk.js.br +0 -0
  69. package/dist/client/assets/cose-bilkent-JH36ORCC-BZopJ5Vk.js.gz +0 -0
  70. package/dist/client/assets/{cynefin-OW5HDTMX-BgqqNrml.js → cynefin-OW5HDTMX-DodGcb-y.js} +1 -1
  71. package/dist/client/assets/cynefin-OW5HDTMX-DodGcb-y.js.br +0 -0
  72. package/dist/client/assets/cynefin-OW5HDTMX-DodGcb-y.js.gz +0 -0
  73. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CHJg73sm.js → cynefinDiagram-5FMLGOSQ-BrfmChC2.js} +1 -1
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BrfmChC2.js.br +0 -0
  75. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BrfmChC2.js.gz +0 -0
  76. package/dist/client/assets/{dagre-GXQ25YYZ-Du6cYIDp.js → dagre-GXQ25YYZ-DDaQVt3_.js} +1 -1
  77. package/dist/client/assets/dagre-GXQ25YYZ-DDaQVt3_.js.br +0 -0
  78. package/dist/client/assets/dagre-GXQ25YYZ-DDaQVt3_.js.gz +0 -0
  79. package/dist/client/assets/{diagram-S7CK7UJ4-CNHI3hSO.js → diagram-S7CK7UJ4-BdTpDUnW.js} +1 -1
  80. package/dist/client/assets/diagram-S7CK7UJ4-BdTpDUnW.js.br +0 -0
  81. package/dist/client/assets/diagram-S7CK7UJ4-BdTpDUnW.js.gz +0 -0
  82. package/dist/client/assets/{diagram-UQ7AKVKN-BP2TAlt5.js → diagram-UQ7AKVKN-DCrCcCSc.js} +1 -1
  83. package/dist/client/assets/diagram-UQ7AKVKN-DCrCcCSc.js.br +0 -0
  84. package/dist/client/assets/diagram-UQ7AKVKN-DCrCcCSc.js.gz +0 -0
  85. package/dist/client/assets/{diagram-VSXAHHWV-BiNbCPl8.js → diagram-VSXAHHWV-CsF1Qn8l.js} +1 -1
  86. package/dist/client/assets/diagram-VSXAHHWV-CsF1Qn8l.js.br +0 -0
  87. package/dist/client/assets/diagram-VSXAHHWV-CsF1Qn8l.js.gz +0 -0
  88. package/dist/client/assets/{diagram-VX7I27RA-Doe3bOA3.js → diagram-VX7I27RA-BjGnkc9i.js} +1 -1
  89. package/dist/client/assets/diagram-VX7I27RA-BjGnkc9i.js.br +0 -0
  90. package/dist/client/assets/diagram-VX7I27RA-BjGnkc9i.js.gz +0 -0
  91. package/dist/client/assets/{diagram-Z3DM3KII-CRWmdH0s.js → diagram-Z3DM3KII-DwYFrSyB.js} +1 -1
  92. package/dist/client/assets/diagram-Z3DM3KII-DwYFrSyB.js.br +0 -0
  93. package/dist/client/assets/diagram-Z3DM3KII-DwYFrSyB.js.gz +0 -0
  94. package/dist/client/assets/{ebnfDiagram-PWID7BFC-CwItyAmD.js → ebnfDiagram-PWID7BFC-DL_jC7vl.js} +1 -1
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-DL_jC7vl.js.br +0 -0
  96. package/dist/client/assets/ebnfDiagram-PWID7BFC-DL_jC7vl.js.gz +0 -0
  97. package/dist/client/assets/{erDiagram-RLTQ6QDP-BWF0eELE.js → erDiagram-RLTQ6QDP-BJRsIcZN.js} +1 -1
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-BJRsIcZN.js.br +0 -0
  99. package/dist/client/assets/erDiagram-RLTQ6QDP-BJRsIcZN.js.gz +0 -0
  100. package/dist/client/assets/{flowDiagram-HODETNUW-Dz_4CTgZ.js → flowDiagram-HODETNUW-DtVbuyGA.js} +1 -1
  101. package/dist/client/assets/flowDiagram-HODETNUW-DtVbuyGA.js.br +0 -0
  102. package/dist/client/assets/flowDiagram-HODETNUW-DtVbuyGA.js.gz +0 -0
  103. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-V27-kSI4.js → ganttDiagram-EL5Y4UJY-Cz8ZjyFr.js} +1 -1
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-Cz8ZjyFr.js.br +0 -0
  105. package/dist/client/assets/ganttDiagram-EL5Y4UJY-Cz8ZjyFr.js.gz +0 -0
  106. package/dist/client/assets/{gestureArbiter-RzRq7gG2.js → gestureArbiter-CYnutT7u.js} +1 -1
  107. package/dist/client/assets/gestureArbiter-CYnutT7u.js.br +0 -0
  108. package/dist/client/assets/gestureArbiter-CYnutT7u.js.gz +0 -0
  109. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-C9WTgqiD.js → gitGraphDiagram-WWUBYQGX-CW73vifz.js} +1 -1
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CW73vifz.js.br +0 -0
  111. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CW73vifz.js.gz +0 -0
  112. package/dist/client/assets/{infoDiagram-27XIBGKW-DKp6kmDi.js → infoDiagram-27XIBGKW-DCaqT0R9.js} +1 -1
  113. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-B0UG9H7W.js → ishikawaDiagram-5VMMS53U-DEloHV3_.js} +1 -1
  114. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DEloHV3_.js.br +0 -0
  115. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DEloHV3_.js.gz +0 -0
  116. package/dist/client/assets/{journeyDiagram-3NMN7TZE-Cgj9wfeA.js → journeyDiagram-3NMN7TZE-BJ5Ymi6P.js} +1 -1
  117. package/dist/client/assets/journeyDiagram-3NMN7TZE-BJ5Ymi6P.js.br +0 -0
  118. package/dist/client/assets/journeyDiagram-3NMN7TZE-BJ5Ymi6P.js.gz +0 -0
  119. package/dist/client/assets/{kanban-definition-UXKFOSKX-B0Su1S1B.js → kanban-definition-UXKFOSKX-BA1Md7zM.js} +1 -1
  120. package/dist/client/assets/kanban-definition-UXKFOSKX-BA1Md7zM.js.br +0 -0
  121. package/dist/client/assets/kanban-definition-UXKFOSKX-BA1Md7zM.js.gz +0 -0
  122. package/dist/client/assets/{layout-Cte2rCfs.js → layout-BLPB7zap.js} +1 -1
  123. package/dist/client/assets/layout-BLPB7zap.js.br +0 -0
  124. package/dist/client/assets/layout-BLPB7zap.js.gz +0 -0
  125. package/dist/client/assets/{linear-CC59HR-0.js → linear-DQnd3pCW.js} +1 -1
  126. package/dist/client/assets/linear-DQnd3pCW.js.br +0 -0
  127. package/dist/client/assets/linear-DQnd3pCW.js.gz +0 -0
  128. package/dist/client/assets/{mermaid.core-DNvgq7UV.js → mermaid.core-CC0si1rU.js} +5 -5
  129. package/dist/client/assets/mermaid.core-CC0si1rU.js.br +0 -0
  130. package/dist/client/assets/mermaid.core-CC0si1rU.js.gz +0 -0
  131. package/dist/client/assets/{mindmap-definition-YA3MSWOX-CL-Too_c.js → mindmap-definition-YA3MSWOX-xj-LqgQH.js} +1 -1
  132. package/dist/client/assets/mindmap-definition-YA3MSWOX-xj-LqgQH.js.br +0 -0
  133. package/dist/client/assets/mindmap-definition-YA3MSWOX-xj-LqgQH.js.gz +0 -0
  134. package/dist/client/assets/{passwordBootstrap-DHkPEJte.js → passwordBootstrap-WkTOy597.js} +1 -1
  135. package/dist/client/assets/passwordBootstrap-WkTOy597.js.br +0 -0
  136. package/dist/client/assets/passwordBootstrap-WkTOy597.js.gz +0 -0
  137. package/dist/client/assets/{pegDiagram-XKGWAZYB-DqHkIEjl.js → pegDiagram-XKGWAZYB-C_tvk3BX.js} +1 -1
  138. package/dist/client/assets/pegDiagram-XKGWAZYB-C_tvk3BX.js.br +0 -0
  139. package/dist/client/assets/pegDiagram-XKGWAZYB-C_tvk3BX.js.gz +0 -0
  140. package/dist/client/assets/{pieDiagram-E7YTZNPT-6G1ciW1i.js → pieDiagram-E7YTZNPT-DavzgCyE.js} +1 -1
  141. package/dist/client/assets/pieDiagram-E7YTZNPT-DavzgCyE.js.br +0 -0
  142. package/dist/client/assets/pieDiagram-E7YTZNPT-DavzgCyE.js.gz +0 -0
  143. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-C08ycYeq.js → quadrantDiagram-AXDQQJYC-OQcqoEdY.js} +1 -1
  144. package/dist/client/assets/quadrantDiagram-AXDQQJYC-OQcqoEdY.js.br +0 -0
  145. package/dist/client/assets/quadrantDiagram-AXDQQJYC-OQcqoEdY.js.gz +0 -0
  146. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BsmprHau.js → railroadDiagram-O6MQD6OU-CKbyYF7J.js} +1 -1
  147. package/dist/client/assets/railroadDiagram-O6MQD6OU-CKbyYF7J.js.br +0 -0
  148. package/dist/client/assets/railroadDiagram-O6MQD6OU-CKbyYF7J.js.gz +0 -0
  149. package/dist/client/assets/{referenceLongPress-D5zkcISp.js → referenceLongPress-B7xcIddc.js} +1 -1
  150. package/dist/client/assets/referenceLongPress-B7xcIddc.js.br +0 -0
  151. package/dist/client/assets/referenceLongPress-B7xcIddc.js.gz +0 -0
  152. package/dist/client/assets/{requirementDiagram-BXWQKSXE-Ct7OZ0sX.js → requirementDiagram-BXWQKSXE-C8jPUps0.js} +1 -1
  153. package/dist/client/assets/requirementDiagram-BXWQKSXE-C8jPUps0.js.br +0 -0
  154. package/dist/client/assets/requirementDiagram-BXWQKSXE-C8jPUps0.js.gz +0 -0
  155. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-BcGS-zY5.js → sankeyDiagram-P5KCCOFB-B1XbZMfI.js} +1 -1
  156. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1XbZMfI.js.br +0 -0
  157. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1XbZMfI.js.gz +0 -0
  158. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CzGOOMf7.js → sequenceDiagram-WJ2MYXX4-TKmv1K4M.js} +1 -1
  159. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-TKmv1K4M.js.br +0 -0
  160. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-TKmv1K4M.js.gz +0 -0
  161. package/dist/client/assets/{sizeCapture-INFHLROL-B4nglf-i.js → sizeCapture-INFHLROL-Ca_kB42l.js} +1 -1
  162. package/dist/client/assets/sizeCapture-INFHLROL-Ca_kB42l.js.br +0 -0
  163. package/dist/client/assets/sizeCapture-INFHLROL-Ca_kB42l.js.gz +0 -0
  164. package/dist/client/assets/{sparkles-D2dbUVEe.js → sparkles-UE5-cQCr.js} +1 -1
  165. package/dist/client/assets/{stateDiagram-D77RDMKH-DTid36j9.js → stateDiagram-D77RDMKH-dvSeMJMI.js} +1 -1
  166. package/dist/client/assets/stateDiagram-D77RDMKH-dvSeMJMI.js.br +0 -0
  167. package/dist/client/assets/stateDiagram-D77RDMKH-dvSeMJMI.js.gz +0 -0
  168. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BgD6bkF_.js +1 -0
  169. package/dist/client/assets/{swimlanes-42K2YHIH-CfBzltgL.js → swimlanes-42K2YHIH-Csz9F-Em.js} +1 -1
  170. package/dist/client/assets/swimlanes-42K2YHIH-Csz9F-Em.js.br +0 -0
  171. package/dist/client/assets/swimlanes-42K2YHIH-Csz9F-Em.js.gz +0 -0
  172. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CtJ_HpaR.js +8 -0
  173. package/dist/client/assets/{timeline-definition-24CTP7MA-ChGIsqiN.js → timeline-definition-24CTP7MA-D_ixo9p2.js} +1 -1
  174. package/dist/client/assets/timeline-definition-24CTP7MA-D_ixo9p2.js.br +0 -0
  175. package/dist/client/assets/timeline-definition-24CTP7MA-D_ixo9p2.js.gz +0 -0
  176. package/dist/client/assets/{triangle-alert-BVxZtbSu.js → triangle-alert-C9EZlPNN.js} +1 -1
  177. package/dist/client/assets/{vennDiagram-4TSXK5OY-KovgHKV7.js → vennDiagram-4TSXK5OY-Bt3wQ7L4.js} +1 -1
  178. package/dist/client/assets/vennDiagram-4TSXK5OY-Bt3wQ7L4.js.br +0 -0
  179. package/dist/client/assets/vennDiagram-4TSXK5OY-Bt3wQ7L4.js.gz +0 -0
  180. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Br0xO1Ao.js → wardleyDiagram-VM6X3IG4-D-5G4QL_.js} +1 -1
  181. package/dist/client/assets/wardleyDiagram-VM6X3IG4-D-5G4QL_.js.br +0 -0
  182. package/dist/client/assets/wardleyDiagram-VM6X3IG4-D-5G4QL_.js.gz +0 -0
  183. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BzbDfcqJ.js → xychartDiagram-S5SC5T6Z-BcXJGPmd.js} +1 -1
  184. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BcXJGPmd.js.br +0 -0
  185. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BcXJGPmd.js.gz +0 -0
  186. package/dist/client/index.html +1 -1
  187. package/dist/client/index.html.br +0 -0
  188. package/dist/client/index.html.gz +0 -0
  189. package/dist/client/sw.js +1 -1
  190. package/dist/client/sw.js.br +0 -0
  191. package/dist/client/sw.js.gz +0 -0
  192. package/dist/server/agent/collaborationCli.js +36 -7
  193. package/dist/server/agent/collaborationGroupRoutes.js +6 -2
  194. package/dist/server/agent/collaborationPeerTransport.js +5 -2
  195. package/dist/server/agent/collaborationRoutes.js +30 -0
  196. package/dist/server/agent/collaborationService.js +275 -0
  197. package/dist/server/agent/collaborationStore.js +10 -0
  198. package/dist/server/entry.js +14 -1
  199. package/dist/server/federation/runtime.js +6 -1
  200. package/dist/server/routes/terminal.js +18 -56
  201. package/package.json +1 -1
  202. package/runtime-manifest.json +3 -3
  203. package/dist/client/assets/App-CBhXXSRZ.js.br +0 -0
  204. package/dist/client/assets/App-CBhXXSRZ.js.gz +0 -0
  205. package/dist/client/assets/ChangeWalkthroughPanel-CeOq4iWF.js.br +0 -0
  206. package/dist/client/assets/ChangeWalkthroughPanel-CeOq4iWF.js.gz +0 -0
  207. package/dist/client/assets/DagPlayground-nzwoofiL.js.br +0 -0
  208. package/dist/client/assets/DagPlayground-nzwoofiL.js.gz +0 -0
  209. package/dist/client/assets/DiffLab-BA5JJf_P.js.br +0 -0
  210. package/dist/client/assets/DiffLab-BA5JJf_P.js.gz +0 -0
  211. package/dist/client/assets/DiffReview-CNnJIPtm.js.br +0 -0
  212. package/dist/client/assets/DiffReview-CNnJIPtm.js.gz +0 -0
  213. package/dist/client/assets/DiffReviewLab-CH9k1x7c.js.br +0 -0
  214. package/dist/client/assets/DiffReviewLab-CH9k1x7c.js.gz +0 -0
  215. package/dist/client/assets/DiffViewer-D17tBMWn.js.br +0 -0
  216. package/dist/client/assets/DiffViewer-D17tBMWn.js.gz +0 -0
  217. package/dist/client/assets/ModelPreview-eUZEjzgg.js.br +0 -0
  218. package/dist/client/assets/ModelPreview-eUZEjzgg.js.gz +0 -0
  219. package/dist/client/assets/RightSidebar-DQSjBvAA.js.br +0 -0
  220. package/dist/client/assets/RightSidebar-DQSjBvAA.js.gz +0 -0
  221. package/dist/client/assets/abnfDiagram-VCTEODGH-CeIsieUL.js.br +0 -0
  222. package/dist/client/assets/abnfDiagram-VCTEODGH-CeIsieUL.js.gz +0 -0
  223. package/dist/client/assets/app-CM_Chr9o.js +0 -35
  224. package/dist/client/assets/app-CM_Chr9o.js.br +0 -0
  225. package/dist/client/assets/app-CM_Chr9o.js.gz +0 -0
  226. package/dist/client/assets/arc-B0iWWfl8.js.br +0 -0
  227. package/dist/client/assets/arc-B0iWWfl8.js.gz +0 -0
  228. package/dist/client/assets/architectureDiagram-5GKGNRK7-IV8_iAgW.js.br +0 -0
  229. package/dist/client/assets/architectureDiagram-5GKGNRK7-IV8_iAgW.js.gz +0 -0
  230. package/dist/client/assets/blockDiagram-I7D4REHJ-DWdbwev4.js.br +0 -0
  231. package/dist/client/assets/blockDiagram-I7D4REHJ-DWdbwev4.js.gz +0 -0
  232. package/dist/client/assets/c4Diagram-7LVT6UL2-DL6qM9qX.js.br +0 -0
  233. package/dist/client/assets/c4Diagram-7LVT6UL2-DL6qM9qX.js.gz +0 -0
  234. package/dist/client/assets/channel-CanBPcbJ.js +0 -1
  235. package/dist/client/assets/chunk-F27PBJKO-CXMkCSL3.js.br +0 -0
  236. package/dist/client/assets/chunk-F27PBJKO-CXMkCSL3.js.gz +0 -0
  237. package/dist/client/assets/chunk-IMKFNOWR-BHb_PEkZ.js.br +0 -0
  238. package/dist/client/assets/chunk-IMKFNOWR-BHb_PEkZ.js.gz +0 -0
  239. package/dist/client/assets/chunk-SVP7TREG-D2bHA6KO.js.br +0 -0
  240. package/dist/client/assets/chunk-SVP7TREG-D2bHA6KO.js.gz +0 -0
  241. package/dist/client/assets/chunk-TICWLB2K-CSV5x8Yz.js.br +0 -0
  242. package/dist/client/assets/chunk-TICWLB2K-CSV5x8Yz.js.gz +0 -0
  243. package/dist/client/assets/classDiagram-ZZMXUADV-Dbb9V726.js +0 -1
  244. package/dist/client/assets/classDiagram-v2-VYDZK3BY-Dbb9V726.js +0 -1
  245. package/dist/client/assets/cose-bilkent-JH36ORCC-CUiXAbSC.js.br +0 -0
  246. package/dist/client/assets/cose-bilkent-JH36ORCC-CUiXAbSC.js.gz +0 -0
  247. package/dist/client/assets/cynefin-OW5HDTMX-BgqqNrml.js.br +0 -0
  248. package/dist/client/assets/cynefin-OW5HDTMX-BgqqNrml.js.gz +0 -0
  249. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CHJg73sm.js.br +0 -0
  250. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CHJg73sm.js.gz +0 -0
  251. package/dist/client/assets/dagre-GXQ25YYZ-Du6cYIDp.js.br +0 -0
  252. package/dist/client/assets/dagre-GXQ25YYZ-Du6cYIDp.js.gz +0 -0
  253. package/dist/client/assets/diagram-S7CK7UJ4-CNHI3hSO.js.br +0 -0
  254. package/dist/client/assets/diagram-S7CK7UJ4-CNHI3hSO.js.gz +0 -0
  255. package/dist/client/assets/diagram-UQ7AKVKN-BP2TAlt5.js.br +0 -0
  256. package/dist/client/assets/diagram-UQ7AKVKN-BP2TAlt5.js.gz +0 -0
  257. package/dist/client/assets/diagram-VSXAHHWV-BiNbCPl8.js.br +0 -0
  258. package/dist/client/assets/diagram-VSXAHHWV-BiNbCPl8.js.gz +0 -0
  259. package/dist/client/assets/diagram-VX7I27RA-Doe3bOA3.js.br +0 -0
  260. package/dist/client/assets/diagram-VX7I27RA-Doe3bOA3.js.gz +0 -0
  261. package/dist/client/assets/diagram-Z3DM3KII-CRWmdH0s.js.br +0 -0
  262. package/dist/client/assets/diagram-Z3DM3KII-CRWmdH0s.js.gz +0 -0
  263. package/dist/client/assets/ebnfDiagram-PWID7BFC-CwItyAmD.js.br +0 -0
  264. package/dist/client/assets/ebnfDiagram-PWID7BFC-CwItyAmD.js.gz +0 -0
  265. package/dist/client/assets/erDiagram-RLTQ6QDP-BWF0eELE.js.br +0 -0
  266. package/dist/client/assets/erDiagram-RLTQ6QDP-BWF0eELE.js.gz +0 -0
  267. package/dist/client/assets/flowDiagram-HODETNUW-Dz_4CTgZ.js.br +0 -0
  268. package/dist/client/assets/flowDiagram-HODETNUW-Dz_4CTgZ.js.gz +0 -0
  269. package/dist/client/assets/ganttDiagram-EL5Y4UJY-V27-kSI4.js.br +0 -0
  270. package/dist/client/assets/ganttDiagram-EL5Y4UJY-V27-kSI4.js.gz +0 -0
  271. package/dist/client/assets/gestureArbiter-RzRq7gG2.js.br +0 -0
  272. package/dist/client/assets/gestureArbiter-RzRq7gG2.js.gz +0 -0
  273. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C9WTgqiD.js.br +0 -0
  274. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C9WTgqiD.js.gz +0 -0
  275. package/dist/client/assets/ishikawaDiagram-5VMMS53U-B0UG9H7W.js.br +0 -0
  276. package/dist/client/assets/ishikawaDiagram-5VMMS53U-B0UG9H7W.js.gz +0 -0
  277. package/dist/client/assets/journeyDiagram-3NMN7TZE-Cgj9wfeA.js.br +0 -0
  278. package/dist/client/assets/journeyDiagram-3NMN7TZE-Cgj9wfeA.js.gz +0 -0
  279. package/dist/client/assets/kanban-definition-UXKFOSKX-B0Su1S1B.js.br +0 -0
  280. package/dist/client/assets/kanban-definition-UXKFOSKX-B0Su1S1B.js.gz +0 -0
  281. package/dist/client/assets/layout-Cte2rCfs.js.br +0 -0
  282. package/dist/client/assets/layout-Cte2rCfs.js.gz +0 -0
  283. package/dist/client/assets/linear-CC59HR-0.js.br +0 -0
  284. package/dist/client/assets/linear-CC59HR-0.js.gz +0 -0
  285. package/dist/client/assets/mermaid.core-DNvgq7UV.js.br +0 -0
  286. package/dist/client/assets/mermaid.core-DNvgq7UV.js.gz +0 -0
  287. package/dist/client/assets/mindmap-definition-YA3MSWOX-CL-Too_c.js.br +0 -0
  288. package/dist/client/assets/mindmap-definition-YA3MSWOX-CL-Too_c.js.gz +0 -0
  289. package/dist/client/assets/passwordBootstrap-DHkPEJte.js.br +0 -0
  290. package/dist/client/assets/passwordBootstrap-DHkPEJte.js.gz +0 -0
  291. package/dist/client/assets/pegDiagram-XKGWAZYB-DqHkIEjl.js.br +0 -0
  292. package/dist/client/assets/pegDiagram-XKGWAZYB-DqHkIEjl.js.gz +0 -0
  293. package/dist/client/assets/pieDiagram-E7YTZNPT-6G1ciW1i.js.br +0 -0
  294. package/dist/client/assets/pieDiagram-E7YTZNPT-6G1ciW1i.js.gz +0 -0
  295. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C08ycYeq.js.br +0 -0
  296. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C08ycYeq.js.gz +0 -0
  297. package/dist/client/assets/railroadDiagram-O6MQD6OU-BsmprHau.js.br +0 -0
  298. package/dist/client/assets/railroadDiagram-O6MQD6OU-BsmprHau.js.gz +0 -0
  299. package/dist/client/assets/referenceLongPress-D5zkcISp.js.br +0 -0
  300. package/dist/client/assets/referenceLongPress-D5zkcISp.js.gz +0 -0
  301. package/dist/client/assets/requirementDiagram-BXWQKSXE-Ct7OZ0sX.js.br +0 -0
  302. package/dist/client/assets/requirementDiagram-BXWQKSXE-Ct7OZ0sX.js.gz +0 -0
  303. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BcGS-zY5.js.br +0 -0
  304. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BcGS-zY5.js.gz +0 -0
  305. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CzGOOMf7.js.br +0 -0
  306. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CzGOOMf7.js.gz +0 -0
  307. package/dist/client/assets/sizeCapture-INFHLROL-B4nglf-i.js.br +0 -0
  308. package/dist/client/assets/sizeCapture-INFHLROL-B4nglf-i.js.gz +0 -0
  309. package/dist/client/assets/stateDiagram-D77RDMKH-DTid36j9.js.br +0 -0
  310. package/dist/client/assets/stateDiagram-D77RDMKH-DTid36j9.js.gz +0 -0
  311. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CivC-WBT.js +0 -1
  312. package/dist/client/assets/swimlanes-42K2YHIH-CfBzltgL.js.br +0 -0
  313. package/dist/client/assets/swimlanes-42K2YHIH-CfBzltgL.js.gz +0 -0
  314. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DNedYJ8P.js +0 -8
  315. package/dist/client/assets/timeline-definition-24CTP7MA-ChGIsqiN.js.br +0 -0
  316. package/dist/client/assets/timeline-definition-24CTP7MA-ChGIsqiN.js.gz +0 -0
  317. package/dist/client/assets/vennDiagram-4TSXK5OY-KovgHKV7.js.br +0 -0
  318. package/dist/client/assets/vennDiagram-4TSXK5OY-KovgHKV7.js.gz +0 -0
  319. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Br0xO1Ao.js.br +0 -0
  320. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Br0xO1Ao.js.gz +0 -0
  321. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BzbDfcqJ.js.br +0 -0
  322. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BzbDfcqJ.js.gz +0 -0
@@ -0,0 +1,275 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { CollaborationError } from './collaborationProtocol.js';
5
+ import { COLLAB_NAME_FORBIDDEN } from './collaborationPrompt.js';
6
+ import { remoteSession, validateCollaborationNode } from './collaborationPeerTransport.js';
7
+ const hash = (value) => createHash('sha256').update(value).digest('hex');
8
+ const address = (id) => { if (!id.startsWith('remote:'))
9
+ return null; const parts = id.slice(7).split(':'); if (parts.length !== 2)
10
+ throw new Error('INVALID_SESSION'); return { origin: decodeURIComponent(parts[0]), id: decodeURIComponent(parts[1]) }; };
11
+ const local = (origin, id) => { const a = address(id); return a?.origin === origin ? a.id : id; };
12
+ const mapRoles = (value, map) => value && Object.fromEntries(Object.entries(value).map(([id, item]) => [map(id), item]));
13
+ function mapGroup(group, map) {
14
+ return { ...group, sessionIds: group.sessionIds.map(map), roles: mapRoles(group.roles, map), roleVersions: mapRoles(group.roleVersions, map),
15
+ instructions: group.instructions && { ...group.instructions, updatedBy: map(group.instructions.updatedBy) } };
16
+ }
17
+ /** Sole owner of peer discovery and group replication. UI and CLI call the same
18
+ * local API. Pairing grants collaboration directory/group management only, never
19
+ * generic HTTP, filesystem access or an Agent's credentials. */
20
+ export class CollaborationService {
21
+ options;
22
+ document = { version: 1, peers: [], offers: [] };
23
+ clients = new Map();
24
+ observations = new Map();
25
+ inFlight;
26
+ timer;
27
+ stopped = false;
28
+ constructor(options) {
29
+ this.options = options;
30
+ try {
31
+ const data = JSON.parse(readFileSync(options.file, 'utf8'));
32
+ if (data.version !== 1 || !Array.isArray(data.peers) || data.peers.length > 64 || !Array.isArray(data.offers))
33
+ throw new Error('INVALID_DIRECTORY');
34
+ for (const peer of data.peers)
35
+ validateCollaborationNode(peer);
36
+ if (data.origin)
37
+ validateCollaborationNode({ ...options.node(), origin: data.origin });
38
+ this.document = data;
39
+ }
40
+ catch (error) {
41
+ if (error.code !== 'ENOENT')
42
+ throw error;
43
+ }
44
+ }
45
+ persist() {
46
+ mkdirSync(dirname(this.options.file), { recursive: true, mode: 0o700 });
47
+ const temp = `${this.options.file}.${process.pid}.tmp`;
48
+ writeFileSync(temp, JSON.stringify(this.document), { mode: 0o600 });
49
+ renameSync(temp, this.options.file);
50
+ }
51
+ node(origin = this.document.origin) { if (!origin)
52
+ throw new Error('COLLABORATION_ORIGIN_REQUIRED'); const node = { ...this.options.node(), origin }; validateCollaborationNode(node); return node; }
53
+ setOrigin(origin) { this.node(origin); if (this.document.origin && this.document.origin !== origin)
54
+ throw new Error('COLLABORATION_ORIGIN_MISMATCH'); this.document.origin = origin; }
55
+ remember(node) {
56
+ validateCollaborationNode(node);
57
+ if (node.serviceId === this.options.node().serviceId || node.origin === this.document.origin)
58
+ throw new Error('CANNOT_PAIR_SELF');
59
+ const old = this.document.peers.find(peer => peer.serviceId === node.serviceId || peer.origin === node.origin);
60
+ if (old && (old.serviceId !== node.serviceId || old.origin !== node.origin || old.caFingerprint256 !== node.caFingerprint256))
61
+ throw new Error('PEER_IDENTITY_CHANGED');
62
+ if (!old) {
63
+ if (this.document.peers.length >= 63)
64
+ throw new Error('PEER_LIMIT');
65
+ this.document.peers.push(node);
66
+ }
67
+ this.persist();
68
+ }
69
+ invite(origin) {
70
+ this.setOrigin(origin);
71
+ const code = randomBytes(32).toString('base64url'), expiresAt = Date.now() + 10 * 60_000;
72
+ this.document.offers = this.document.offers.filter(offer => offer.expiresAt > Date.now()).slice(-15);
73
+ this.document.offers.push({ hash: hash(code), expiresAt });
74
+ this.persist();
75
+ return { version: 1, node: this.node(), code, expiresAt, scope: 'collaboration-directory-and-groups' };
76
+ }
77
+ async accept(origin, invitation) {
78
+ if (invitation?.version !== 1 || !/^[A-Za-z0-9_-]{43}$/.test(invitation.code) || !Number.isFinite(invitation.expiresAt) || invitation.expiresAt <= Date.now())
79
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
80
+ validateCollaborationNode(invitation.node);
81
+ this.setOrigin(origin);
82
+ const rpc = await this.options.connect(invitation.node);
83
+ try {
84
+ await rpc.request({ type: 'collaboration-service', action: 'pair', code: invitation.code, node: this.node() });
85
+ this.remember(invitation.node);
86
+ void this.refresh();
87
+ return { ok: true, peer: invitation.node };
88
+ }
89
+ finally {
90
+ rpc.close();
91
+ }
92
+ }
93
+ receive(subject, packet) {
94
+ if (packet.action === 'pair') {
95
+ if (typeof packet.code !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(packet.code))
96
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
97
+ const codeHash = hash(packet.code);
98
+ const offer = this.document.offers.find(item => item.hash === codeHash && item.expiresAt > Date.now());
99
+ const node = packet.node;
100
+ validateCollaborationNode(node);
101
+ if (!offer || node.serviceId !== subject || offer.acceptedBy && offer.acceptedBy !== subject)
102
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
103
+ if (offer.acceptedBy && !this.document.peers.some(peer => peer.serviceId === subject))
104
+ throw new Error('PAIRING_REVOKED');
105
+ offer.acceptedBy = subject;
106
+ this.remember(node);
107
+ void this.refresh();
108
+ return { ok: true };
109
+ }
110
+ const peer = this.document.peers.find(node => node.serviceId === subject);
111
+ if (!peer)
112
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
113
+ if (packet.action === 'directory')
114
+ return this.snapshot(peer.origin);
115
+ if (packet.action === 'group') {
116
+ const canonical = packet.group;
117
+ if (!canonical?.sessionIds?.some(id => address(id)?.origin === peer.origin) && !this.document.replicas?.[canonical?.id]?.includes(peer.origin))
118
+ throw new Error('GROUP_SENDER_NOT_MEMBER');
119
+ this.merge(canonical, packet.nodes);
120
+ return { ok: true };
121
+ }
122
+ throw new Error('COLLABORATION_OPERATION_UNSUPPORTED');
123
+ }
124
+ nodes() { return [...new Map([...this.options.transport.registeredNodes(), ...this.document.peers, this.node()].map(node => [node.origin, node])).values()]; }
125
+ snapshot(peerOrigin) {
126
+ const origin = this.node().origin;
127
+ return { sessions: this.options.sessions(), groups: this.options.store.federationSnapshot().groups
128
+ .filter(group => group.federated && (group.sessionIds.some(id => address(id)?.origin === peerOrigin) || this.document.replicas?.[group.id]?.includes(peerOrigin)))
129
+ .map(group => mapGroup(group, id => address(id) ? id : remoteSession(origin, id))), nodes: this.nodes() };
130
+ }
131
+ merge(canonical, nodes) {
132
+ if (!canonical || typeof canonical.id !== 'string' || !canonical.id.startsWith('cross-') || !Array.isArray(canonical.sessionIds)
133
+ || canonical.sessionIds.length < 2 || canonical.sessionIds.length > 500 || typeof canonical.name !== 'string' || !canonical.name.trim() || COLLAB_NAME_FORBIDDEN.test(canonical.name)
134
+ || !Number.isFinite(canonical.updatedAt) || canonical.updatedAt > Date.now() + 60_000 || !Array.isArray(nodes) || nodes.length > 65)
135
+ throw new Error('INVALID_GROUP');
136
+ for (const node of nodes) {
137
+ validateCollaborationNode(node);
138
+ const trusted = this.document.peers.find(peer => peer.serviceId === node.serviceId || peer.origin === node.origin);
139
+ if (trusted && (trusted.serviceId !== node.serviceId || trusted.origin !== node.origin || trusted.caFingerprint256 !== node.caFingerprint256))
140
+ throw new Error('PEER_IDENTITY_CHANGED');
141
+ }
142
+ const self = this.node(), own = nodes.find(node => node.serviceId === self.serviceId);
143
+ if (!own || own.origin !== self.origin || own.caFingerprint256 !== self.caFingerprint256)
144
+ throw new Error('LOCAL_NODE_MISMATCH');
145
+ const existing = this.options.store.getGroup(canonical.id);
146
+ if ((this.document.departed?.[canonical.id] ?? -1) >= canonical.updatedAt)
147
+ return;
148
+ if (!canonical.sessionIds.some(id => address(id)?.origin === self.origin)) {
149
+ (this.document.departed ??= {})[canonical.id] = canonical.updatedAt;
150
+ this.options.store.dropFederatedReplica(canonical.id, canonical.updatedAt);
151
+ this.persist();
152
+ return;
153
+ }
154
+ const origins = [...new Set([...(this.document.replicas?.[canonical.id] ?? []), ...canonical.sessionIds.map(id => address(id)?.origin).filter((origin) => !!origin)])];
155
+ if (JSON.stringify(origins) !== JSON.stringify(this.document.replicas?.[canonical.id])) {
156
+ (this.document.replicas ??= {})[canonical.id] = origins;
157
+ this.persist();
158
+ }
159
+ const localIds = new Set(this.options.sessions().map(s => s.sessionId));
160
+ const incoming = { ...mapGroup(canonical, id => local(self.origin, id)), federated: true };
161
+ for (const id of incoming.sessionIds) {
162
+ const remote = address(id);
163
+ if (remote ? !nodes.some(node => node.origin === remote.origin) : !localIds.has(id) && !existing?.sessionIds.includes(id))
164
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
165
+ }
166
+ incoming.remoteSessions = incoming.sessionIds.filter(id => address(id)).map(id => {
167
+ const a = address(id);
168
+ const observed = this.observations.get(nodes.find(node => node.origin === a.origin).serviceId)?.sessions.find(s => s.sessionId === a.id);
169
+ return { ...observed, sessionId: id, name: observed?.name ?? a.id, cwd: observed?.cwd ?? '', status: 'offline', currentTask: '', capability: observed?.capability ?? '', updatedAt: observed?.updatedAt ?? 0,
170
+ serviceOrigin: a.origin, serviceLabel: a.origin, backendSessionId: null, agentNativeSessionId: null, agent: observed?.agent ?? null };
171
+ });
172
+ this.options.store.mergeFederatedGroup(incoming);
173
+ const saved = this.options.store.getGroup(incoming.id);
174
+ if (saved && !saved.deleted && saved.updatedAt === incoming.updatedAt)
175
+ this.options.transport.configure(saved.id, self.origin, nodes);
176
+ }
177
+ start() { this.timer = setInterval(() => { void this.refresh(); }, 2000); this.timer.unref(); void this.refresh(); }
178
+ close() { this.stopped = true; clearInterval(this.timer); for (const rpc of this.clients.values())
179
+ void rpc.then(client => client.close()).catch(() => { }); this.clients.clear(); }
180
+ async client(peer) {
181
+ let pending = this.clients.get(peer.serviceId);
182
+ if (pending && (await pending).closed) {
183
+ this.clients.delete(peer.serviceId);
184
+ pending = undefined;
185
+ }
186
+ if (!pending) {
187
+ pending = this.options.connect(peer);
188
+ this.clients.set(peer.serviceId, pending);
189
+ }
190
+ try {
191
+ return await pending;
192
+ }
193
+ catch (error) {
194
+ this.clients.delete(peer.serviceId);
195
+ throw error;
196
+ }
197
+ }
198
+ refresh() {
199
+ if (this.stopped || !this.document.origin)
200
+ return Promise.resolve();
201
+ return this.inFlight ??= Promise.all(this.document.peers.map(async (peer) => {
202
+ try {
203
+ const client = await this.client(peer);
204
+ const data = await client.request({ type: 'collaboration-service', action: 'directory' });
205
+ if (!Array.isArray(data.sessions) || data.sessions.length > 5000 || !Array.isArray(data.groups))
206
+ throw new Error('INVALID_DIRECTORY');
207
+ const sessions = data.sessions.filter((s) => s && typeof s.sessionId === 'string' && !s.sessionId.startsWith('remote:'));
208
+ this.observations.set(peer.serviceId, { sessions, checkedAt: Date.now() });
209
+ for (const group of data.groups)
210
+ this.merge(group, data.nodes);
211
+ for (const group of this.options.store.federationSnapshot().groups.filter(g => g.federated && (g.sessionIds.some(id => address(id)?.origin === peer.origin) || this.document.replicas?.[g.id]?.includes(peer.origin)))) {
212
+ const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
213
+ const nodes = this.nodes();
214
+ if (canonical.sessionIds.some(id => !nodes.some(node => node.origin === address(id)?.origin)))
215
+ continue;
216
+ this.merge(canonical, nodes);
217
+ await client.request({ type: 'collaboration-service', action: 'group', group: canonical, nodes });
218
+ }
219
+ }
220
+ catch (error) {
221
+ this.observations.set(peer.serviceId, { sessions: this.observations.get(peer.serviceId)?.sessions ?? [], checkedAt: Date.now(), error: error instanceof Error ? error.message.slice(0, 240) : 'PEER_UNAVAILABLE' });
222
+ }
223
+ })).then(() => { }).finally(() => { this.inFlight = undefined; });
224
+ }
225
+ directory() {
226
+ const known = new Set(this.document.peers.map(peer => peer.origin));
227
+ const missing = [...new Set(this.options.store.list().flatMap(group => group.sessionIds.map(id => address(id)?.origin).filter((origin) => !!origin && !known.has(origin))))];
228
+ const services = this.document.peers.map(peer => {
229
+ const observed = this.observations.get(peer.serviceId);
230
+ return { origin: peer.origin, serviceId: peer.serviceId, label: peer.origin,
231
+ connected: !!observed && !observed.error && Date.now() - observed.checkedAt < 15_000, error: observed?.error ?? (!observed ? 'CONNECTING' : undefined) };
232
+ });
233
+ return { protocolVersion: 2, sessions: this.document.peers.flatMap(peer => (this.observations.get(peer.serviceId)?.sessions ?? []).map(session => ({ ...session,
234
+ sessionId: remoteSession(peer.origin, session.sessionId), backendSessionId: null, agentNativeSessionId: null, serviceOrigin: peer.origin, serviceLabel: peer.origin,
235
+ serviceConnected: services.find(s => s.origin === peer.origin).connected, serviceCheckedAt: this.observations.get(peer.serviceId)?.checkedAt }))),
236
+ services: [...services, ...missing.map(origin => ({ origin, label: origin, connected: false, error: '两端服务尚未配对;请使用 td collab transport invite / accept 完成一次授权' }))] };
237
+ }
238
+ async save(input) {
239
+ const existing = input.id ? this.options.store.getGroup(input.id) : null;
240
+ if (input.id && (!existing || existing.deleted))
241
+ throw new Error('GROUP_NOT_FOUND');
242
+ if (existing && input.expectedUpdatedAt !== existing.updatedAt)
243
+ throw new CollaborationError('GROUP_CHANGED', '协作组已变化,请刷新后重试', 409);
244
+ if (typeof input.name !== 'string' || !input.name.trim() || COLLAB_NAME_FORBIDDEN.test(input.name) || !Array.isArray(input.sessionIds) || input.sessionIds.some(id => typeof id !== 'string') || new Set(input.sessionIds).size < 2)
245
+ throw new Error('INVALID_GROUP');
246
+ if (!input.sessionIds.some(id => address(id)) && !existing?.federated) {
247
+ if (input.sessionIds.some(id => !this.options.sessions().some(s => s.sessionId === id) && !existing?.sessionIds.includes(id)))
248
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
249
+ return { group: this.options.store.save(input) };
250
+ }
251
+ if (!this.document.origin)
252
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
253
+ await this.refresh();
254
+ if (existing && this.options.store.getGroup(existing.id)?.updatedAt !== existing.updatedAt)
255
+ throw new Error('GROUP_CHANGED');
256
+ if (!input.sessionIds.some(id => !address(id)))
257
+ throw new Error('GROUP_MUST_INCLUDE_LOCAL_MEMBER');
258
+ const available = new Set([...this.options.sessions(), ...this.directory().sessions.filter(s => s.serviceConnected)].map(s => s.sessionId));
259
+ if (!Array.isArray(input.sessionIds) || input.sessionIds.some(id => !available.has(id) && !existing?.sessionIds.includes(id)))
260
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
261
+ const group = { ...existing, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
262
+ sessionIds: [...new Set(input.sessionIds)], createdAt: existing?.createdAt ?? Date.now(), updatedAt: Math.max(Date.now(), (existing?.updatedAt ?? 0) + 1), federated: true };
263
+ const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
264
+ if (canonical.sessionIds.some(id => !this.nodes().some(node => node.origin === address(id)?.origin)))
265
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
266
+ // Promotion transfers history atomically to the federated group id.
267
+ if (existing && !existing.federated) {
268
+ const promoted = this.options.store.promoteGroup(existing.id, { ...group, remoteSessions: [] }, existing.updatedAt);
269
+ canonical.updatedAt = promoted.updatedAt;
270
+ }
271
+ this.merge(canonical, this.nodes());
272
+ void this.refresh();
273
+ return { group: this.options.store.getGroup(group.id), synchronization: 'background' };
274
+ }
275
+ }
@@ -404,6 +404,16 @@ export class CollaborationStore {
404
404
  const messages = this.document.messages.filter((message) => ids.has(message.groupId));
405
405
  return { groups, messages, transportDiagnostics: Object.fromEntries(messages.flatMap((message) => this.document.transport?.[message.id] ? [[message.id, this.document.transport[message.id]]] : [])) };
406
406
  }
407
+ /** Drop only this replica after its last local member leaves; never broadcast
408
+ * a global deletion of the group that still exists on other services. */
409
+ dropFederatedReplica(id, updatedAt) {
410
+ const group = this.getGroup(id);
411
+ if (!group?.federated || group.updatedAt > updatedAt)
412
+ return;
413
+ this.document.groups = this.document.groups.filter(item => item.id !== id);
414
+ this.document.messages = this.document.messages.filter(item => item.groupId !== id);
415
+ this.persist();
416
+ }
407
417
  mergeFederatedGroup(group) {
408
418
  validateFederatedGroup(group);
409
419
  const existing = this.getGroup(group.id);
@@ -1,6 +1,7 @@
1
+ import { CollaborationService } from './agent/collaborationService.js';
1
2
  import { X509Certificate } from 'node:crypto';
2
3
  import { CollaborationPeerTransport, connectCollaborationRpc } from './agent/collaborationPeerTransport.js';
3
- import { collaborationStore, deliverPeerCollaboration, collaborationLocalActivity } from './routes/terminal.js';
4
+ import { collaborationStore, deliverPeerCollaboration, collaborationLocalActivity, collaborationDirectorySessions } from './routes/terminal.js';
4
5
  import { setPushTargetPeerId } from './notifications/pushService.js';
5
6
  import { desktopDirectTargets } from './federation/desktopTargets.js';
6
7
  import { createOpenAccessRouter } from './federation/openAccess.js';
@@ -407,6 +408,7 @@ export function startServer(options = {}) {
407
408
  runtimeMonitor,
408
409
  });
409
410
  const { server, scheme } = createServerForApp(app, options);
411
+ let collaborationService;
410
412
  let collaborationTransport;
411
413
  let entrySubjectAllowed = (_subjectId) => false;
412
414
  let routeInvitations;
@@ -445,6 +447,11 @@ export function startServer(options = {}) {
445
447
  const federation = createFederationRuntime(app, path.join(homedir(), '.termdock', 'federation'), {
446
448
  terminal: handleTerminalWebSocket, control: handleControlWebSocket,
447
449
  }, {
450
+ collaborationService: (subjectId, packet) => {
451
+ if (!collaborationService)
452
+ throw new Error('COLLABORATION_UNAVAILABLE');
453
+ return collaborationService.receive(subjectId, packet);
454
+ },
448
455
  collaborationExchange: (subjectId, packet) => {
449
456
  if (!collaborationTransport)
450
457
  throw new Error('COLLABORATION_UNAVAILABLE');
@@ -516,6 +523,12 @@ export function startServer(options = {}) {
516
523
  }
517
524
  catch { /* System TLS trust remains required. */ }
518
525
  app.locals.collaborationNode = { serviceId: runtime.serviceId, ...(caFingerprint256 ? { caFingerprint256 } : {}) };
526
+ collaborationService = new CollaborationService({ file: path.join(homedir(), '.termdock', 'federation', 'collaboration-services.json'),
527
+ store: collaborationStore, transport: collaborationTransport, node: () => app.locals.collaborationNode,
528
+ sessions: collaborationDirectorySessions, connect: peer => connectCollaborationRpc(runtime.identity, peer) });
529
+ app.locals.collaborationService = collaborationService;
530
+ collaborationService.start();
531
+ server.once('close', () => collaborationService?.close());
519
532
  collaborationTransport.start();
520
533
  server.once('close', () => collaborationTransport?.close());
521
534
  entrySubjectAllowed = subjectId => runtime.store.authorize({ subjectId, serviceId: runtime.serviceId, action: 'authorization.manage' }).allowed;
@@ -251,7 +251,12 @@ export async function createFederationRuntime(app, directory, handlers, options
251
251
  try {
252
252
  if (packet.id.length > 128)
253
253
  throw new Error('INVALID_ID');
254
- if (packet.type === 'collaboration-exchange') {
254
+ if (packet.type === 'collaboration-service') {
255
+ if (!options.collaborationService)
256
+ throw new Error('COLLABORATION_UPGRADE_REQUIRED');
257
+ send({ type: 'result', id: packet.id, ...options.collaborationService(subjectId, packet) });
258
+ }
259
+ else if (packet.type === 'collaboration-exchange') {
255
260
  if (!options.collaborationExchange)
256
261
  throw new Error('COLLABORATION_UNAVAILABLE');
257
262
  send({ type: 'result', id: packet.id, ...options.collaborationExchange(subjectId, packet) });
@@ -1,4 +1,3 @@
1
- import { assertPeerRegistrationAuthority } from '../agent/collaborationPeerTransport.js';
2
1
  import { progressRoutes } from '../notifications/progressRoutes.js';
3
2
  import { ensureNodePty } from '../utils/ensureNodePty.js';
4
3
  import { PtySpawnBackoff, PtySpawnDeferredError } from '../utils/ptySpawnBackoff.js';
@@ -39,7 +38,7 @@ import { isTmuxRecoveryCandidate } from '../utils/tmuxRecoveryCandidate.js';
39
38
  import { AutomationStore, normalizeAutomationSchedule } from '../agent/automationStore.js';
40
39
  import { buildBracketedSubmitBytes, canDeliverPromptToAgent } from '../agent/promptDelivery.js';
41
40
  import { collaborationRoutes } from '../agent/collaborationRoutes.js';
42
- import { COLLAB_LIMITS, extrasFromBody } from '../agent/collaborationProtocol.js';
41
+ import { extrasFromBody } from '../agent/collaborationProtocol.js';
43
42
  import { CollaborationStore } from '../agent/collaborationStore.js';
44
43
  import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
45
44
  import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
@@ -5476,24 +5475,19 @@ router.delete('/operations/automations/:automationId', (req, res) => {
5476
5475
  return res.status(404).json({ error: '自动任务不存在' });
5477
5476
  res.status(204).send();
5478
5477
  });
5479
- // Authenticated desktop connections exchange only collaboration data; no peer credentials
5480
- // or arbitrary remote URLs are accepted by the server.
5481
- router.get('/operations/collaboration-federation', (req, res) => {
5482
- res.json({ protocolVersion: 2, serverTransport: req.app.locals.collaborationNode, limits: COLLAB_LIMITS, ...collaborationStore.federationSnapshot(),
5483
- sessions: globalSessionState.sessions.map(orchestrationSessionSnapshot) });
5478
+ // The old native/browser relay may still poll after a CLI-only upgrade. It
5479
+ // receives a fast, explicit refusal and cannot duplicate the server worker.
5480
+ router.all(['/operations/collaboration-federation', '/operations/collaboration-peers'], (_req, res) => {
5481
+ res.status(410).json({ code: 'CLIENT_RELAY_REMOVED', error: '协作由服务端处理,请重新加载服务页面' });
5484
5482
  });
5485
- router.post('/operations/collaboration-peers', (req, res) => {
5486
- try {
5487
- assertPeerRegistrationAuthority(req);
5488
- if (!req.app.locals.collaborationTransport)
5489
- return res.status(503).json({ error: 'COLLABORATION_UNAVAILABLE' });
5490
- req.app.locals.collaborationTransport.configure(req.body.groupId, req.body.localOrigin, req.body.nodes);
5491
- res.json({ ok: true });
5492
- }
5493
- catch (error) {
5494
- res.status(400).json({ error: getErrorMessage(error) });
5495
- }
5483
+ router.get('/operations/collaboration-directory', (req, res) => {
5484
+ const service = req.app.locals.collaborationService;
5485
+ if (!service)
5486
+ return res.status(503).json({ error: '协作服务正在启动,请稍后重试' });
5487
+ void service.refresh();
5488
+ res.json(service.directory());
5496
5489
  });
5490
+ export function collaborationDirectorySessions() { return globalSessionState.sessions.map(orchestrationSessionSnapshot); }
5497
5491
  export function collaborationLocalActivity() {
5498
5492
  return globalSessionState.sessions.map(record => {
5499
5493
  const backendId = collaborationRouting.get(record.sessionId)?.backendSessionId ?? record.backendSessionId;
@@ -5502,45 +5496,13 @@ export function collaborationLocalActivity() {
5502
5496
  });
5503
5497
  }
5504
5498
  export function deliverPeerCollaboration(sessionId) { void tryDeliverCollaborationInbox(sessionId); }
5505
- router.post('/operations/collaboration-federation', (req, res) => {
5506
- try {
5507
- const group = req.body?.group;
5508
- if (!group || !Array.isArray(group.remoteSessions))
5509
- throw new Error('跨服务工作组无效');
5510
- const remoteIds = new Set(group.remoteSessions.map((session) => session.sessionId));
5511
- const localIds = new Set(globalSessionState.sessions.map((session) => session.sessionId));
5512
- const existing = collaborationStore.getGroup(group.id);
5513
- if (req.body.expectedUpdatedAt !== undefined && req.body.expectedUpdatedAt !== existing?.updatedAt) {
5514
- return res.status(409).json({ code: 'GROUP_CHANGED', error: '协作组已被修改,请重新打开成员管理后再保存' });
5515
- }
5516
- const retainedIds = new Set(existing?.sessionIds ?? []);
5517
- if (!group.deleted && group.sessionIds.some((id) => !localIds.has(id) && !remoteIds.has(id) && !retainedIds.has(id))) {
5518
- throw new Error('工作组包含不存在的本服务会话,请刷新成员列表');
5519
- }
5520
- collaborationStore.mergeFederatedGroup(group);
5521
- collaborationStore.mergeFederatedMessages(Array.isArray(req.body.messages) ? req.body.messages : []);
5522
- const fragments = Array.isArray(req.body.fragments) ? req.body.fragments : [];
5523
- if (fragments.length > 25 || fragments.some((fragment) => fragment.group_id !== group.id))
5524
- throw new Error('无效消息分片');
5525
- const fragmentReceipts = fragments.map((fragment) => collaborationStore.acceptFragment(fragment));
5526
- for (const entry of Array.isArray(req.body.transport) ? req.body.transport : []) {
5527
- if (collaborationStore.getMessage(entry.message_id)?.groupId !== group.id || !entry.diagnostic || !Number.isFinite(entry.diagnostic.checked_at) || !Number.isInteger(entry.diagnostic.attempt_count) || entry.diagnostic.attempt_count < 0)
5528
- continue;
5529
- collaborationStore.recordTransport(entry.message_id, entry.diagnostic);
5530
- if (entry.failure_reason)
5531
- collaborationStore.fail(entry.message_id, entry.failure_reason);
5532
- }
5533
- for (const id of group.sessionIds)
5534
- if (localIds.has(id))
5535
- tryDeliverCollaborationInbox(id);
5536
- res.json({ protocolVersion: 2, ...collaborationStore.federationSnapshot(), fragmentReceipts });
5537
- }
5538
- catch (error) {
5539
- res.status(400).json({ error: getErrorMessage(error) });
5540
- }
5541
- });
5542
5499
  router.use('/operations', collaborationGroupRoutes({ store: collaborationStore,
5543
- sessions: () => globalSessionState.sessions.map(orchestrationSessionSnapshot) }));
5500
+ sessions: () => globalSessionState.sessions.map(orchestrationSessionSnapshot),
5501
+ save: (req, input) => {
5502
+ if (!req.app.locals.collaborationService)
5503
+ throw new Error('COLLABORATION_UNAVAILABLE');
5504
+ return req.app.locals.collaborationService.save(input);
5505
+ } }));
5544
5506
  /** Group id from a URL param: the short id a terminal shows resolves to
5545
5507
  * its full id, an ambiguous prefix is refused with 409, and anything else is
5546
5508
  * simply "no such group" — the same 404 each route already answers. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdock",
3
- "version": "1.4.233",
3
+ "version": "1.4.234",
4
4
  "description": "A complete web-based terminal application with modern UI",
5
5
  "type": "module",
6
6
  "main": ".desktop-dist/main.js",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "termdock",
4
- "version": "1.4.233",
4
+ "version": "1.4.234",
5
5
  "runtimeProtocolVersion": 1,
6
6
  "minimumDesktopVersion": "1.4.46",
7
7
  "nodeMajor": 22,
8
8
  "dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
9
- "serverBundleHash": "sha256-GHB6P5NPYFEdDR/sPm0FHVSEBdpzcJ8Jghr5X2uikWU=",
10
- "clientBundleHash": "sha256-QvAwqyw8+TgNqMDTtWnsp5gbsoE5RiJyCfPonVL0aX4=",
9
+ "serverBundleHash": "sha256-nhvsmvXOZsSAZPmQTztOUo074PRH4RrLqcMhe5r4kKk=",
10
+ "clientBundleHash": "sha256-NULxnbzNlpIXlrOpTKR4KD9KY3y5ekC2/dn3VWl6AcE=",
11
11
  "entrypoint": "dist/server/cli.js",
12
12
  "clientEntrypoint": "dist/client/index.html"
13
13
  }