termdock 1.4.233 → 1.4.235

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 (320) hide show
  1. package/.desktop-dist/main.js +1 -39
  2. package/dist/client/assets/{App-CBhXXSRZ.js → App-zSykqDrK.js} +3 -3
  3. package/dist/client/assets/App-zSykqDrK.js.br +0 -0
  4. package/dist/client/assets/App-zSykqDrK.js.gz +0 -0
  5. package/dist/client/assets/{ChangeWalkthroughPanel-CeOq4iWF.js → ChangeWalkthroughPanel-Bhk5u4ei.js} +1 -1
  6. package/dist/client/assets/ChangeWalkthroughPanel-Bhk5u4ei.js.br +0 -0
  7. package/dist/client/assets/ChangeWalkthroughPanel-Bhk5u4ei.js.gz +0 -0
  8. package/dist/client/assets/{DagPlayground-nzwoofiL.js → DagPlayground-9DE-yoRV.js} +1 -1
  9. package/dist/client/assets/DagPlayground-9DE-yoRV.js.br +0 -0
  10. package/dist/client/assets/DagPlayground-9DE-yoRV.js.gz +0 -0
  11. package/dist/client/assets/{DiffLab-BA5JJf_P.js → DiffLab-CjyMoL77.js} +1 -1
  12. package/dist/client/assets/DiffLab-CjyMoL77.js.br +0 -0
  13. package/dist/client/assets/DiffLab-CjyMoL77.js.gz +0 -0
  14. package/dist/client/assets/{DiffReview-CNnJIPtm.js → DiffReview-kSV-dlGf.js} +1 -1
  15. package/dist/client/assets/DiffReview-kSV-dlGf.js.br +0 -0
  16. package/dist/client/assets/DiffReview-kSV-dlGf.js.gz +0 -0
  17. package/dist/client/assets/{DiffReviewLab-CH9k1x7c.js → DiffReviewLab-DL8o3FzO.js} +1 -1
  18. package/dist/client/assets/DiffReviewLab-DL8o3FzO.js.br +0 -0
  19. package/dist/client/assets/DiffReviewLab-DL8o3FzO.js.gz +0 -0
  20. package/dist/client/assets/{DiffViewer-D17tBMWn.js → DiffViewer-8lX1Yp21.js} +1 -1
  21. package/dist/client/assets/DiffViewer-8lX1Yp21.js.br +0 -0
  22. package/dist/client/assets/DiffViewer-8lX1Yp21.js.gz +0 -0
  23. package/dist/client/assets/{ModelPreview-eUZEjzgg.js → ModelPreview-Di9v8eC8.js} +1 -1
  24. package/dist/client/assets/ModelPreview-Di9v8eC8.js.br +0 -0
  25. package/dist/client/assets/ModelPreview-Di9v8eC8.js.gz +0 -0
  26. package/dist/client/assets/{RightSidebar-DQSjBvAA.js → RightSidebar-C4BW2Wwm.js} +4 -4
  27. package/dist/client/assets/RightSidebar-C4BW2Wwm.js.br +0 -0
  28. package/dist/client/assets/RightSidebar-C4BW2Wwm.js.gz +0 -0
  29. package/dist/client/assets/{abnfDiagram-VCTEODGH-CeIsieUL.js → abnfDiagram-VCTEODGH-hlnPn1XS.js} +1 -1
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-hlnPn1XS.js.br +0 -0
  31. package/dist/client/assets/abnfDiagram-VCTEODGH-hlnPn1XS.js.gz +0 -0
  32. package/dist/client/assets/app-CSIciOPl.js +35 -0
  33. package/dist/client/assets/app-CSIciOPl.js.br +0 -0
  34. package/dist/client/assets/app-CSIciOPl.js.gz +0 -0
  35. package/dist/client/assets/{arc-B0iWWfl8.js → arc-DRWaGXMR.js} +1 -1
  36. package/dist/client/assets/arc-DRWaGXMR.js.br +0 -0
  37. package/dist/client/assets/arc-DRWaGXMR.js.gz +0 -0
  38. package/dist/client/assets/{architectureDiagram-5GKGNRK7-IV8_iAgW.js → architectureDiagram-5GKGNRK7-CGgjTR71.js} +1 -1
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-CGgjTR71.js.br +0 -0
  40. package/dist/client/assets/architectureDiagram-5GKGNRK7-CGgjTR71.js.gz +0 -0
  41. package/dist/client/assets/{blockDiagram-I7D4REHJ-DWdbwev4.js → blockDiagram-I7D4REHJ-CoSXp3bo.js} +1 -1
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-CoSXp3bo.js.br +0 -0
  43. package/dist/client/assets/blockDiagram-I7D4REHJ-CoSXp3bo.js.gz +0 -0
  44. package/dist/client/assets/{c4Diagram-7LVT6UL2-DL6qM9qX.js → c4Diagram-7LVT6UL2-T24R2Qbf.js} +1 -1
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-T24R2Qbf.js.br +0 -0
  46. package/dist/client/assets/c4Diagram-7LVT6UL2-T24R2Qbf.js.gz +0 -0
  47. package/dist/client/assets/channel-3gIaLXRE.js +1 -0
  48. package/dist/client/assets/{chunk-2Q5K7J3B-CADcnv26.js → chunk-2Q5K7J3B--LXR3vbd.js} +1 -1
  49. package/dist/client/assets/{chunk-5VM5RSS4-Dy92AQjN.js → chunk-5VM5RSS4-CCATC1Bu.js} +1 -1
  50. package/dist/client/assets/{chunk-F27PBJKO-CXMkCSL3.js → chunk-F27PBJKO-BZcc7Ic4.js} +1 -1
  51. package/dist/client/assets/chunk-F27PBJKO-BZcc7Ic4.js.br +0 -0
  52. package/dist/client/assets/chunk-F27PBJKO-BZcc7Ic4.js.gz +0 -0
  53. package/dist/client/assets/{chunk-IMKFNOWR-BHb_PEkZ.js → chunk-IMKFNOWR-CwEcJlSi.js} +1 -1
  54. package/dist/client/assets/chunk-IMKFNOWR-CwEcJlSi.js.br +0 -0
  55. package/dist/client/assets/chunk-IMKFNOWR-CwEcJlSi.js.gz +0 -0
  56. package/dist/client/assets/{chunk-JWPE2WC7-B4h9BTig.js → chunk-JWPE2WC7-BgzA4uHj.js} +1 -1
  57. package/dist/client/assets/{chunk-POPQ4Y6H-DMc2ZxT_.js → chunk-POPQ4Y6H-BS7G7nLt.js} +1 -1
  58. package/dist/client/assets/{chunk-SVP7TREG-D2bHA6KO.js → chunk-SVP7TREG-MyWxRYxW.js} +1 -1
  59. package/dist/client/assets/chunk-SVP7TREG-MyWxRYxW.js.br +0 -0
  60. package/dist/client/assets/chunk-SVP7TREG-MyWxRYxW.js.gz +0 -0
  61. package/dist/client/assets/{chunk-TICWLB2K-CSV5x8Yz.js → chunk-TICWLB2K-By-otV7X.js} +1 -1
  62. package/dist/client/assets/chunk-TICWLB2K-By-otV7X.js.br +0 -0
  63. package/dist/client/assets/chunk-TICWLB2K-By-otV7X.js.gz +0 -0
  64. package/dist/client/assets/{chunk-XXDRQBXY-CEkyVIOo.js → chunk-XXDRQBXY-dFHhSKLr.js} +1 -1
  65. package/dist/client/assets/classDiagram-ZZMXUADV-BZRR_eIG.js +1 -0
  66. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BZRR_eIG.js +1 -0
  67. package/dist/client/assets/{cose-bilkent-JH36ORCC-CUiXAbSC.js → cose-bilkent-JH36ORCC-S4CHO_jm.js} +1 -1
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-S4CHO_jm.js.br +0 -0
  69. package/dist/client/assets/cose-bilkent-JH36ORCC-S4CHO_jm.js.gz +0 -0
  70. package/dist/client/assets/{cynefin-OW5HDTMX-BgqqNrml.js → cynefin-OW5HDTMX-D-Tdm--u.js} +1 -1
  71. package/dist/client/assets/cynefin-OW5HDTMX-D-Tdm--u.js.br +0 -0
  72. package/dist/client/assets/cynefin-OW5HDTMX-D-Tdm--u.js.gz +0 -0
  73. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CHJg73sm.js → cynefinDiagram-5FMLGOSQ-FSHeEj_r.js} +1 -1
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-FSHeEj_r.js.br +0 -0
  75. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-FSHeEj_r.js.gz +0 -0
  76. package/dist/client/assets/{dagre-GXQ25YYZ-Du6cYIDp.js → dagre-GXQ25YYZ-BdeaoJ-E.js} +1 -1
  77. package/dist/client/assets/dagre-GXQ25YYZ-BdeaoJ-E.js.br +0 -0
  78. package/dist/client/assets/dagre-GXQ25YYZ-BdeaoJ-E.js.gz +0 -0
  79. package/dist/client/assets/{diagram-S7CK7UJ4-CNHI3hSO.js → diagram-S7CK7UJ4-BmneUVZf.js} +1 -1
  80. package/dist/client/assets/diagram-S7CK7UJ4-BmneUVZf.js.br +0 -0
  81. package/dist/client/assets/diagram-S7CK7UJ4-BmneUVZf.js.gz +0 -0
  82. package/dist/client/assets/{diagram-UQ7AKVKN-BP2TAlt5.js → diagram-UQ7AKVKN-SsDlu1y-.js} +1 -1
  83. package/dist/client/assets/diagram-UQ7AKVKN-SsDlu1y-.js.br +0 -0
  84. package/dist/client/assets/diagram-UQ7AKVKN-SsDlu1y-.js.gz +0 -0
  85. package/dist/client/assets/{diagram-VSXAHHWV-BiNbCPl8.js → diagram-VSXAHHWV-D8x3I59I.js} +1 -1
  86. package/dist/client/assets/diagram-VSXAHHWV-D8x3I59I.js.br +0 -0
  87. package/dist/client/assets/diagram-VSXAHHWV-D8x3I59I.js.gz +0 -0
  88. package/dist/client/assets/{diagram-VX7I27RA-Doe3bOA3.js → diagram-VX7I27RA-Cxdgnfbh.js} +1 -1
  89. package/dist/client/assets/diagram-VX7I27RA-Cxdgnfbh.js.br +0 -0
  90. package/dist/client/assets/diagram-VX7I27RA-Cxdgnfbh.js.gz +0 -0
  91. package/dist/client/assets/{diagram-Z3DM3KII-CRWmdH0s.js → diagram-Z3DM3KII-Bn-Md8Cr.js} +1 -1
  92. package/dist/client/assets/diagram-Z3DM3KII-Bn-Md8Cr.js.br +0 -0
  93. package/dist/client/assets/diagram-Z3DM3KII-Bn-Md8Cr.js.gz +0 -0
  94. package/dist/client/assets/{ebnfDiagram-PWID7BFC-CwItyAmD.js → ebnfDiagram-PWID7BFC-C3FeaeR-.js} +1 -1
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-C3FeaeR-.js.br +0 -0
  96. package/dist/client/assets/ebnfDiagram-PWID7BFC-C3FeaeR-.js.gz +0 -0
  97. package/dist/client/assets/{erDiagram-RLTQ6QDP-BWF0eELE.js → erDiagram-RLTQ6QDP-Dkl931on.js} +1 -1
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-Dkl931on.js.br +0 -0
  99. package/dist/client/assets/erDiagram-RLTQ6QDP-Dkl931on.js.gz +0 -0
  100. package/dist/client/assets/{flowDiagram-HODETNUW-Dz_4CTgZ.js → flowDiagram-HODETNUW-DJHNp3k9.js} +1 -1
  101. package/dist/client/assets/flowDiagram-HODETNUW-DJHNp3k9.js.br +0 -0
  102. package/dist/client/assets/{flowDiagram-HODETNUW-Dz_4CTgZ.js.gz → flowDiagram-HODETNUW-DJHNp3k9.js.gz} +0 -0
  103. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-V27-kSI4.js → ganttDiagram-EL5Y4UJY-DaY1g-nz.js} +1 -1
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DaY1g-nz.js.br +0 -0
  105. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DaY1g-nz.js.gz +0 -0
  106. package/dist/client/assets/{gestureArbiter-RzRq7gG2.js → gestureArbiter-4U4PoLbc.js} +1 -1
  107. package/dist/client/assets/gestureArbiter-4U4PoLbc.js.br +0 -0
  108. package/dist/client/assets/gestureArbiter-4U4PoLbc.js.gz +0 -0
  109. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-C9WTgqiD.js → gitGraphDiagram-WWUBYQGX--yhLbQDf.js} +1 -1
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX--yhLbQDf.js.br +0 -0
  111. package/dist/client/assets/gitGraphDiagram-WWUBYQGX--yhLbQDf.js.gz +0 -0
  112. package/dist/client/assets/{infoDiagram-27XIBGKW-DKp6kmDi.js → infoDiagram-27XIBGKW-Cx-8WBsL.js} +1 -1
  113. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-B0UG9H7W.js → ishikawaDiagram-5VMMS53U-BXGub2Wj.js} +1 -1
  114. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BXGub2Wj.js.br +0 -0
  115. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BXGub2Wj.js.gz +0 -0
  116. package/dist/client/assets/{journeyDiagram-3NMN7TZE-Cgj9wfeA.js → journeyDiagram-3NMN7TZE-DXFRBC7a.js} +1 -1
  117. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXFRBC7a.js.br +0 -0
  118. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXFRBC7a.js.gz +0 -0
  119. package/dist/client/assets/{kanban-definition-UXKFOSKX-B0Su1S1B.js → kanban-definition-UXKFOSKX-D5DbjYN0.js} +1 -1
  120. package/dist/client/assets/kanban-definition-UXKFOSKX-D5DbjYN0.js.br +0 -0
  121. package/dist/client/assets/kanban-definition-UXKFOSKX-D5DbjYN0.js.gz +0 -0
  122. package/dist/client/assets/{layout-Cte2rCfs.js → layout-v1om_wqK.js} +1 -1
  123. package/dist/client/assets/layout-v1om_wqK.js.br +0 -0
  124. package/dist/client/assets/layout-v1om_wqK.js.gz +0 -0
  125. package/dist/client/assets/{linear-CC59HR-0.js → linear-B0oeh-Ux.js} +1 -1
  126. package/dist/client/assets/linear-B0oeh-Ux.js.br +0 -0
  127. package/dist/client/assets/linear-B0oeh-Ux.js.gz +0 -0
  128. package/dist/client/assets/{mermaid.core-DNvgq7UV.js → mermaid.core-Dd5NfkiJ.js} +5 -5
  129. package/dist/client/assets/mermaid.core-Dd5NfkiJ.js.br +0 -0
  130. package/dist/client/assets/mermaid.core-Dd5NfkiJ.js.gz +0 -0
  131. package/dist/client/assets/{mindmap-definition-YA3MSWOX-CL-Too_c.js → mindmap-definition-YA3MSWOX-aItop2_3.js} +1 -1
  132. package/dist/client/assets/mindmap-definition-YA3MSWOX-aItop2_3.js.br +0 -0
  133. package/dist/client/assets/mindmap-definition-YA3MSWOX-aItop2_3.js.gz +0 -0
  134. package/dist/client/assets/{passwordBootstrap-DHkPEJte.js → passwordBootstrap-CkZxpps_.js} +1 -1
  135. package/dist/client/assets/passwordBootstrap-CkZxpps_.js.br +0 -0
  136. package/dist/client/assets/{passwordBootstrap-DHkPEJte.js.gz → passwordBootstrap-CkZxpps_.js.gz} +0 -0
  137. package/dist/client/assets/{pegDiagram-XKGWAZYB-DqHkIEjl.js → pegDiagram-XKGWAZYB-io_H-_O0.js} +1 -1
  138. package/dist/client/assets/pegDiagram-XKGWAZYB-io_H-_O0.js.br +0 -0
  139. package/dist/client/assets/pegDiagram-XKGWAZYB-io_H-_O0.js.gz +0 -0
  140. package/dist/client/assets/{pieDiagram-E7YTZNPT-6G1ciW1i.js → pieDiagram-E7YTZNPT-DW51U3Vc.js} +1 -1
  141. package/dist/client/assets/pieDiagram-E7YTZNPT-DW51U3Vc.js.br +0 -0
  142. package/dist/client/assets/pieDiagram-E7YTZNPT-DW51U3Vc.js.gz +0 -0
  143. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-C08ycYeq.js → quadrantDiagram-AXDQQJYC-Bb7_7BbB.js} +1 -1
  144. package/dist/client/assets/quadrantDiagram-AXDQQJYC-Bb7_7BbB.js.br +0 -0
  145. package/dist/client/assets/quadrantDiagram-AXDQQJYC-Bb7_7BbB.js.gz +0 -0
  146. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BsmprHau.js → railroadDiagram-O6MQD6OU-Cch7xSel.js} +1 -1
  147. package/dist/client/assets/railroadDiagram-O6MQD6OU-Cch7xSel.js.br +0 -0
  148. package/dist/client/assets/railroadDiagram-O6MQD6OU-Cch7xSel.js.gz +0 -0
  149. package/dist/client/assets/{referenceLongPress-D5zkcISp.js → referenceLongPress-EA0xcUM0.js} +1 -1
  150. package/dist/client/assets/referenceLongPress-EA0xcUM0.js.br +0 -0
  151. package/dist/client/assets/referenceLongPress-EA0xcUM0.js.gz +0 -0
  152. package/dist/client/assets/{requirementDiagram-BXWQKSXE-Ct7OZ0sX.js → requirementDiagram-BXWQKSXE-VF-s2PGq.js} +1 -1
  153. package/dist/client/assets/requirementDiagram-BXWQKSXE-VF-s2PGq.js.br +0 -0
  154. package/dist/client/assets/requirementDiagram-BXWQKSXE-VF-s2PGq.js.gz +0 -0
  155. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-BcGS-zY5.js → sankeyDiagram-P5KCCOFB-DgyDYPKU.js} +1 -1
  156. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DgyDYPKU.js.br +0 -0
  157. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DgyDYPKU.js.gz +0 -0
  158. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CzGOOMf7.js → sequenceDiagram-WJ2MYXX4-tcxB1wCN.js} +1 -1
  159. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-tcxB1wCN.js.br +0 -0
  160. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-tcxB1wCN.js.gz +0 -0
  161. package/dist/client/assets/{sizeCapture-INFHLROL-B4nglf-i.js → sizeCapture-INFHLROL-4D8RAV4f.js} +1 -1
  162. package/dist/client/assets/sizeCapture-INFHLROL-4D8RAV4f.js.br +0 -0
  163. package/dist/client/assets/sizeCapture-INFHLROL-4D8RAV4f.js.gz +0 -0
  164. package/dist/client/assets/{sparkles-D2dbUVEe.js → sparkles-C_kAT_YI.js} +1 -1
  165. package/dist/client/assets/{stateDiagram-D77RDMKH-DTid36j9.js → stateDiagram-D77RDMKH-C5AtNVsT.js} +1 -1
  166. package/dist/client/assets/stateDiagram-D77RDMKH-C5AtNVsT.js.br +0 -0
  167. package/dist/client/assets/stateDiagram-D77RDMKH-C5AtNVsT.js.gz +0 -0
  168. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BTojZbKH.js +1 -0
  169. package/dist/client/assets/{swimlanes-42K2YHIH-CfBzltgL.js → swimlanes-42K2YHIH-jeWNfuDW.js} +1 -1
  170. package/dist/client/assets/swimlanes-42K2YHIH-jeWNfuDW.js.br +0 -0
  171. package/dist/client/assets/swimlanes-42K2YHIH-jeWNfuDW.js.gz +0 -0
  172. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DAkPyl3g.js +8 -0
  173. package/dist/client/assets/{timeline-definition-24CTP7MA-ChGIsqiN.js → timeline-definition-24CTP7MA--h_HSNFo.js} +1 -1
  174. package/dist/client/assets/timeline-definition-24CTP7MA--h_HSNFo.js.br +0 -0
  175. package/dist/client/assets/timeline-definition-24CTP7MA--h_HSNFo.js.gz +0 -0
  176. package/dist/client/assets/{triangle-alert-BVxZtbSu.js → triangle-alert-BoF7CWrV.js} +1 -1
  177. package/dist/client/assets/{vennDiagram-4TSXK5OY-KovgHKV7.js → vennDiagram-4TSXK5OY-DtCIS7M_.js} +1 -1
  178. package/dist/client/assets/vennDiagram-4TSXK5OY-DtCIS7M_.js.br +0 -0
  179. package/dist/client/assets/vennDiagram-4TSXK5OY-DtCIS7M_.js.gz +0 -0
  180. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Br0xO1Ao.js → wardleyDiagram-VM6X3IG4-wWbcTCgW.js} +1 -1
  181. package/dist/client/assets/wardleyDiagram-VM6X3IG4-wWbcTCgW.js.br +0 -0
  182. package/dist/client/assets/wardleyDiagram-VM6X3IG4-wWbcTCgW.js.gz +0 -0
  183. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BzbDfcqJ.js → xychartDiagram-S5SC5T6Z-CiqNySOJ.js} +1 -1
  184. package/dist/client/assets/xychartDiagram-S5SC5T6Z-CiqNySOJ.js.br +0 -0
  185. package/dist/client/assets/xychartDiagram-S5SC5T6Z-CiqNySOJ.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 +304 -0
  197. package/dist/server/agent/collaborationStore.js +10 -0
  198. package/dist/server/entry.js +19 -1
  199. package/dist/server/federation/runtime.js +11 -1
  200. package/dist/server/routes/terminal.js +25 -50
  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/ganttDiagram-EL5Y4UJY-V27-kSI4.js.br +0 -0
  269. package/dist/client/assets/ganttDiagram-EL5Y4UJY-V27-kSI4.js.gz +0 -0
  270. package/dist/client/assets/gestureArbiter-RzRq7gG2.js.br +0 -0
  271. package/dist/client/assets/gestureArbiter-RzRq7gG2.js.gz +0 -0
  272. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C9WTgqiD.js.br +0 -0
  273. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C9WTgqiD.js.gz +0 -0
  274. package/dist/client/assets/ishikawaDiagram-5VMMS53U-B0UG9H7W.js.br +0 -0
  275. package/dist/client/assets/ishikawaDiagram-5VMMS53U-B0UG9H7W.js.gz +0 -0
  276. package/dist/client/assets/journeyDiagram-3NMN7TZE-Cgj9wfeA.js.br +0 -0
  277. package/dist/client/assets/journeyDiagram-3NMN7TZE-Cgj9wfeA.js.gz +0 -0
  278. package/dist/client/assets/kanban-definition-UXKFOSKX-B0Su1S1B.js.br +0 -0
  279. package/dist/client/assets/kanban-definition-UXKFOSKX-B0Su1S1B.js.gz +0 -0
  280. package/dist/client/assets/layout-Cte2rCfs.js.br +0 -0
  281. package/dist/client/assets/layout-Cte2rCfs.js.gz +0 -0
  282. package/dist/client/assets/linear-CC59HR-0.js.br +0 -0
  283. package/dist/client/assets/linear-CC59HR-0.js.gz +0 -0
  284. package/dist/client/assets/mermaid.core-DNvgq7UV.js.br +0 -0
  285. package/dist/client/assets/mermaid.core-DNvgq7UV.js.gz +0 -0
  286. package/dist/client/assets/mindmap-definition-YA3MSWOX-CL-Too_c.js.br +0 -0
  287. package/dist/client/assets/mindmap-definition-YA3MSWOX-CL-Too_c.js.gz +0 -0
  288. package/dist/client/assets/passwordBootstrap-DHkPEJte.js.br +0 -0
  289. package/dist/client/assets/pegDiagram-XKGWAZYB-DqHkIEjl.js.br +0 -0
  290. package/dist/client/assets/pegDiagram-XKGWAZYB-DqHkIEjl.js.gz +0 -0
  291. package/dist/client/assets/pieDiagram-E7YTZNPT-6G1ciW1i.js.br +0 -0
  292. package/dist/client/assets/pieDiagram-E7YTZNPT-6G1ciW1i.js.gz +0 -0
  293. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C08ycYeq.js.br +0 -0
  294. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C08ycYeq.js.gz +0 -0
  295. package/dist/client/assets/railroadDiagram-O6MQD6OU-BsmprHau.js.br +0 -0
  296. package/dist/client/assets/railroadDiagram-O6MQD6OU-BsmprHau.js.gz +0 -0
  297. package/dist/client/assets/referenceLongPress-D5zkcISp.js.br +0 -0
  298. package/dist/client/assets/referenceLongPress-D5zkcISp.js.gz +0 -0
  299. package/dist/client/assets/requirementDiagram-BXWQKSXE-Ct7OZ0sX.js.br +0 -0
  300. package/dist/client/assets/requirementDiagram-BXWQKSXE-Ct7OZ0sX.js.gz +0 -0
  301. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BcGS-zY5.js.br +0 -0
  302. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BcGS-zY5.js.gz +0 -0
  303. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CzGOOMf7.js.br +0 -0
  304. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CzGOOMf7.js.gz +0 -0
  305. package/dist/client/assets/sizeCapture-INFHLROL-B4nglf-i.js.br +0 -0
  306. package/dist/client/assets/sizeCapture-INFHLROL-B4nglf-i.js.gz +0 -0
  307. package/dist/client/assets/stateDiagram-D77RDMKH-DTid36j9.js.br +0 -0
  308. package/dist/client/assets/stateDiagram-D77RDMKH-DTid36j9.js.gz +0 -0
  309. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CivC-WBT.js +0 -1
  310. package/dist/client/assets/swimlanes-42K2YHIH-CfBzltgL.js.br +0 -0
  311. package/dist/client/assets/swimlanes-42K2YHIH-CfBzltgL.js.gz +0 -0
  312. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DNedYJ8P.js +0 -8
  313. package/dist/client/assets/timeline-definition-24CTP7MA-ChGIsqiN.js.br +0 -0
  314. package/dist/client/assets/timeline-definition-24CTP7MA-ChGIsqiN.js.gz +0 -0
  315. package/dist/client/assets/vennDiagram-4TSXK5OY-KovgHKV7.js.br +0 -0
  316. package/dist/client/assets/vennDiagram-4TSXK5OY-KovgHKV7.js.gz +0 -0
  317. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Br0xO1Ao.js.br +0 -0
  318. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Br0xO1Ao.js.gz +0 -0
  319. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BzbDfcqJ.js.br +0 -0
  320. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BzbDfcqJ.js.gz +0 -0
@@ -0,0 +1,304 @@
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
+ descriptor() { return { ...this.options.node(), ...(this.document.origin ? { origin: this.document.origin } : {}) }; }
70
+ /** Called through the existing authenticated administrator connection. No
71
+ * client key is copied; only pinned public service descriptors are persisted. */
72
+ connectKnown(origin, nodes) {
73
+ if (!Array.isArray(nodes) || nodes.length > 64)
74
+ throw new Error('INVALID_SERVICE_DIRECTORY');
75
+ const self = this.node(origin);
76
+ const own = nodes.find(node => node?.serviceId === self.serviceId);
77
+ if (!own || own.origin !== origin || own.caFingerprint256 !== self.caFingerprint256)
78
+ throw new Error('LOCAL_NODE_MISMATCH');
79
+ const ids = new Set(), origins = new Set();
80
+ for (const node of nodes) {
81
+ validateCollaborationNode(node);
82
+ if (ids.has(node.serviceId) || origins.has(node.origin))
83
+ throw new Error('DUPLICATE_SERVICE_DIRECTORY');
84
+ ids.add(node.serviceId);
85
+ origins.add(node.origin);
86
+ const old = this.document.peers.find(peer => peer.serviceId === node.serviceId || peer.origin === node.origin);
87
+ if (old && (old.serviceId !== node.serviceId || old.origin !== node.origin || old.caFingerprint256 !== node.caFingerprint256))
88
+ throw new Error('PEER_IDENTITY_CHANGED');
89
+ }
90
+ if (new Set([...this.document.peers.map(peer => peer.serviceId), ...nodes.filter(node => node.serviceId !== self.serviceId).map(node => node.serviceId)]).size > 63)
91
+ throw new Error('PEER_LIMIT');
92
+ this.setOrigin(origin);
93
+ this.document.peers = [...new Map([...this.document.peers, ...nodes.filter(node => node.serviceId !== self.serviceId)].map(node => [node.serviceId, node])).values()];
94
+ this.persist();
95
+ void this.refresh();
96
+ return { ok: true, registered: this.document.peers.map(peer => peer.serviceId) };
97
+ }
98
+ invite(origin) {
99
+ this.setOrigin(origin);
100
+ const code = randomBytes(32).toString('base64url'), expiresAt = Date.now() + 10 * 60_000;
101
+ this.document.offers = this.document.offers.filter(offer => offer.expiresAt > Date.now()).slice(-15);
102
+ this.document.offers.push({ hash: hash(code), expiresAt });
103
+ this.persist();
104
+ return { version: 1, node: this.node(), code, expiresAt, scope: 'collaboration-directory-and-groups' };
105
+ }
106
+ async accept(origin, invitation) {
107
+ if (invitation?.version !== 1 || !/^[A-Za-z0-9_-]{43}$/.test(invitation.code) || !Number.isFinite(invitation.expiresAt) || invitation.expiresAt <= Date.now())
108
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
109
+ validateCollaborationNode(invitation.node);
110
+ this.setOrigin(origin);
111
+ const rpc = await this.options.connect(invitation.node);
112
+ try {
113
+ await rpc.request({ type: 'collaboration-service', action: 'pair', code: invitation.code, node: this.node() });
114
+ this.remember(invitation.node);
115
+ void this.refresh();
116
+ return { ok: true, peer: invitation.node };
117
+ }
118
+ finally {
119
+ rpc.close();
120
+ }
121
+ }
122
+ receive(subject, packet) {
123
+ if (packet.action === 'pair') {
124
+ if (typeof packet.code !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(packet.code))
125
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
126
+ const codeHash = hash(packet.code);
127
+ const offer = this.document.offers.find(item => item.hash === codeHash && item.expiresAt > Date.now());
128
+ const node = packet.node;
129
+ validateCollaborationNode(node);
130
+ if (!offer || node.serviceId !== subject || offer.acceptedBy && offer.acceptedBy !== subject)
131
+ throw new Error('PAIRING_EXPIRED_OR_INVALID');
132
+ if (offer.acceptedBy && !this.document.peers.some(peer => peer.serviceId === subject))
133
+ throw new Error('PAIRING_REVOKED');
134
+ offer.acceptedBy = subject;
135
+ this.remember(node);
136
+ void this.refresh();
137
+ return { ok: true };
138
+ }
139
+ const peer = this.document.peers.find(node => node.serviceId === subject);
140
+ if (!peer)
141
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
142
+ if (packet.action === 'directory')
143
+ return this.snapshot(peer.origin);
144
+ if (packet.action === 'group') {
145
+ const canonical = packet.group;
146
+ if (!canonical?.sessionIds?.some(id => address(id)?.origin === peer.origin) && !this.document.replicas?.[canonical?.id]?.includes(peer.origin))
147
+ throw new Error('GROUP_SENDER_NOT_MEMBER');
148
+ this.merge(canonical, packet.nodes);
149
+ return { ok: true };
150
+ }
151
+ throw new Error('COLLABORATION_OPERATION_UNSUPPORTED');
152
+ }
153
+ nodes() { return [...new Map([...this.options.transport.registeredNodes(), ...this.document.peers, this.node()].map(node => [node.origin, node])).values()]; }
154
+ snapshot(peerOrigin) {
155
+ const origin = this.node().origin;
156
+ return { sessions: this.options.sessions(), groups: this.options.store.federationSnapshot().groups
157
+ .filter(group => group.federated && (group.sessionIds.some(id => address(id)?.origin === peerOrigin) || this.document.replicas?.[group.id]?.includes(peerOrigin)))
158
+ .map(group => mapGroup(group, id => address(id) ? id : remoteSession(origin, id))), nodes: this.nodes() };
159
+ }
160
+ merge(canonical, nodes) {
161
+ if (!canonical || typeof canonical.id !== 'string' || !canonical.id.startsWith('cross-') || !Array.isArray(canonical.sessionIds)
162
+ || canonical.sessionIds.length < 2 || canonical.sessionIds.length > 500 || typeof canonical.name !== 'string' || !canonical.name.trim() || COLLAB_NAME_FORBIDDEN.test(canonical.name)
163
+ || !Number.isFinite(canonical.updatedAt) || canonical.updatedAt > Date.now() + 60_000 || !Array.isArray(nodes) || nodes.length > 65)
164
+ throw new Error('INVALID_GROUP');
165
+ for (const node of nodes) {
166
+ validateCollaborationNode(node);
167
+ const trusted = this.document.peers.find(peer => peer.serviceId === node.serviceId || peer.origin === node.origin);
168
+ if (trusted && (trusted.serviceId !== node.serviceId || trusted.origin !== node.origin || trusted.caFingerprint256 !== node.caFingerprint256))
169
+ throw new Error('PEER_IDENTITY_CHANGED');
170
+ }
171
+ const self = this.node(), own = nodes.find(node => node.serviceId === self.serviceId);
172
+ if (!own || own.origin !== self.origin || own.caFingerprint256 !== self.caFingerprint256)
173
+ throw new Error('LOCAL_NODE_MISMATCH');
174
+ const existing = this.options.store.getGroup(canonical.id);
175
+ if ((this.document.departed?.[canonical.id] ?? -1) >= canonical.updatedAt)
176
+ return;
177
+ if (!canonical.sessionIds.some(id => address(id)?.origin === self.origin)) {
178
+ (this.document.departed ??= {})[canonical.id] = canonical.updatedAt;
179
+ this.options.store.dropFederatedReplica(canonical.id, canonical.updatedAt);
180
+ this.persist();
181
+ return;
182
+ }
183
+ const origins = [...new Set([...(this.document.replicas?.[canonical.id] ?? []), ...canonical.sessionIds.map(id => address(id)?.origin).filter((origin) => !!origin)])];
184
+ if (JSON.stringify(origins) !== JSON.stringify(this.document.replicas?.[canonical.id])) {
185
+ (this.document.replicas ??= {})[canonical.id] = origins;
186
+ this.persist();
187
+ }
188
+ const localIds = new Set(this.options.sessions().map(s => s.sessionId));
189
+ const incoming = { ...mapGroup(canonical, id => local(self.origin, id)), federated: true };
190
+ for (const id of incoming.sessionIds) {
191
+ const remote = address(id);
192
+ if (remote ? !nodes.some(node => node.origin === remote.origin) : !localIds.has(id) && !existing?.sessionIds.includes(id))
193
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
194
+ }
195
+ incoming.remoteSessions = incoming.sessionIds.filter(id => address(id)).map(id => {
196
+ const a = address(id);
197
+ const observed = this.observations.get(nodes.find(node => node.origin === a.origin).serviceId)?.sessions.find(s => s.sessionId === a.id);
198
+ return { ...observed, sessionId: id, name: observed?.name ?? a.id, cwd: observed?.cwd ?? '', status: 'offline', currentTask: '', capability: observed?.capability ?? '', updatedAt: observed?.updatedAt ?? 0,
199
+ serviceOrigin: a.origin, serviceLabel: a.origin, backendSessionId: null, agentNativeSessionId: null, agent: observed?.agent ?? null };
200
+ });
201
+ this.options.store.mergeFederatedGroup(incoming);
202
+ const saved = this.options.store.getGroup(incoming.id);
203
+ if (saved && !saved.deleted && saved.updatedAt === incoming.updatedAt)
204
+ this.options.transport.configure(saved.id, self.origin, nodes);
205
+ }
206
+ start() { this.timer = setInterval(() => { void this.refresh(); }, 2000); this.timer.unref(); void this.refresh(); }
207
+ close() { this.stopped = true; clearInterval(this.timer); for (const rpc of this.clients.values())
208
+ void rpc.then(client => client.close()).catch(() => { }); this.clients.clear(); }
209
+ async client(peer) {
210
+ let pending = this.clients.get(peer.serviceId);
211
+ if (pending && (await pending).closed) {
212
+ this.clients.delete(peer.serviceId);
213
+ pending = undefined;
214
+ }
215
+ if (!pending) {
216
+ pending = this.options.connect(peer);
217
+ this.clients.set(peer.serviceId, pending);
218
+ }
219
+ try {
220
+ return await pending;
221
+ }
222
+ catch (error) {
223
+ this.clients.delete(peer.serviceId);
224
+ throw error;
225
+ }
226
+ }
227
+ refresh() {
228
+ if (this.stopped || !this.document.origin)
229
+ return Promise.resolve();
230
+ return this.inFlight ??= Promise.all(this.document.peers.map(async (peer) => {
231
+ try {
232
+ const client = await this.client(peer);
233
+ const data = await client.request({ type: 'collaboration-service', action: 'directory' });
234
+ if (!Array.isArray(data.sessions) || data.sessions.length > 5000 || !Array.isArray(data.groups))
235
+ throw new Error('INVALID_DIRECTORY');
236
+ const sessions = data.sessions.filter((s) => s && typeof s.sessionId === 'string' && !s.sessionId.startsWith('remote:'));
237
+ this.observations.set(peer.serviceId, { sessions, checkedAt: Date.now() });
238
+ for (const group of data.groups)
239
+ this.merge(group, data.nodes);
240
+ 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)))) {
241
+ const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
242
+ const nodes = this.nodes();
243
+ if (canonical.sessionIds.some(id => !nodes.some(node => node.origin === address(id)?.origin)))
244
+ continue;
245
+ this.merge(canonical, nodes);
246
+ await client.request({ type: 'collaboration-service', action: 'group', group: canonical, nodes });
247
+ }
248
+ }
249
+ catch (error) {
250
+ 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' });
251
+ }
252
+ })).then(() => { }).finally(() => { this.inFlight = undefined; });
253
+ }
254
+ directory() {
255
+ const known = new Set(this.document.peers.map(peer => peer.origin));
256
+ const missing = [...new Set(this.options.store.list().flatMap(group => group.sessionIds.map(id => address(id)?.origin).filter((origin) => !!origin && !known.has(origin))))];
257
+ const services = this.document.peers.map(peer => {
258
+ const observed = this.observations.get(peer.serviceId);
259
+ return { origin: peer.origin, serviceId: peer.serviceId, label: peer.origin,
260
+ connected: !!observed && !observed.error && Date.now() - observed.checkedAt < 15_000, error: observed?.error ?? (!observed ? 'CONNECTING' : undefined) };
261
+ });
262
+ return { protocolVersion: 2, sessions: this.document.peers.flatMap(peer => (this.observations.get(peer.serviceId)?.sessions ?? []).map(session => ({ ...session,
263
+ sessionId: remoteSession(peer.origin, session.sessionId), backendSessionId: null, agentNativeSessionId: null, serviceOrigin: peer.origin, serviceLabel: peer.origin,
264
+ serviceConnected: services.find(s => s.origin === peer.origin).connected, serviceCheckedAt: this.observations.get(peer.serviceId)?.checkedAt }))),
265
+ services: [...services, ...missing.map(origin => ({ origin, label: origin, connected: false, error: '服务连接授权尚未同步;在已授权服务页面连接后会自动完成' }))] };
266
+ }
267
+ async save(input) {
268
+ const existing = input.id ? this.options.store.getGroup(input.id) : null;
269
+ if (input.id && (!existing || existing.deleted))
270
+ throw new Error('GROUP_NOT_FOUND');
271
+ if (existing && input.expectedUpdatedAt !== existing.updatedAt)
272
+ throw new CollaborationError('GROUP_CHANGED', '协作组已变化,请刷新后重试', 409);
273
+ 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)
274
+ throw new Error('INVALID_GROUP');
275
+ if (!input.sessionIds.some(id => address(id)) && !existing?.federated) {
276
+ if (input.sessionIds.some(id => !this.options.sessions().some(s => s.sessionId === id) && !existing?.sessionIds.includes(id)))
277
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
278
+ return { group: this.options.store.save(input) };
279
+ }
280
+ if (!this.document.origin)
281
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
282
+ await this.refresh();
283
+ if (existing && this.options.store.getGroup(existing.id)?.updatedAt !== existing.updatedAt)
284
+ throw new Error('GROUP_CHANGED');
285
+ if (!input.sessionIds.some(id => !address(id)))
286
+ throw new Error('GROUP_MUST_INCLUDE_LOCAL_MEMBER');
287
+ const available = new Set([...this.options.sessions(), ...this.directory().sessions.filter(s => s.serviceConnected)].map(s => s.sessionId));
288
+ if (!Array.isArray(input.sessionIds) || input.sessionIds.some(id => !available.has(id) && !existing?.sessionIds.includes(id)))
289
+ throw new Error('GROUP_MEMBER_UNAVAILABLE');
290
+ const group = { ...existing, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
291
+ sessionIds: [...new Set(input.sessionIds)], createdAt: existing?.createdAt ?? Date.now(), updatedAt: Math.max(Date.now(), (existing?.updatedAt ?? 0) + 1), federated: true };
292
+ const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
293
+ if (canonical.sessionIds.some(id => !this.nodes().some(node => node.origin === address(id)?.origin)))
294
+ throw new Error('COLLABORATION_PAIRING_REQUIRED');
295
+ // Promotion transfers history atomically to the federated group id.
296
+ if (existing && !existing.federated) {
297
+ const promoted = this.options.store.promoteGroup(existing.id, { ...group, remoteSessions: [] }, existing.updatedAt);
298
+ canonical.updatedAt = promoted.updatedAt;
299
+ }
300
+ this.merge(canonical, this.nodes());
301
+ void this.refresh();
302
+ return { group: this.options.store.getGroup(group.id), synchronization: 'background' };
303
+ }
304
+ }
@@ -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,16 @@ export function startServer(options = {}) {
445
447
  const federation = createFederationRuntime(app, path.join(homedir(), '.termdock', 'federation'), {
446
448
  terminal: handleTerminalWebSocket, control: handleControlWebSocket,
447
449
  }, {
450
+ collaborationDescriptor: () => {
451
+ if (!collaborationService)
452
+ throw new Error('COLLABORATION_UNAVAILABLE');
453
+ return collaborationService.descriptor();
454
+ },
455
+ collaborationService: (subjectId, packet) => {
456
+ if (!collaborationService)
457
+ throw new Error('COLLABORATION_UNAVAILABLE');
458
+ return collaborationService.receive(subjectId, packet);
459
+ },
448
460
  collaborationExchange: (subjectId, packet) => {
449
461
  if (!collaborationTransport)
450
462
  throw new Error('COLLABORATION_UNAVAILABLE');
@@ -516,6 +528,12 @@ export function startServer(options = {}) {
516
528
  }
517
529
  catch { /* System TLS trust remains required. */ }
518
530
  app.locals.collaborationNode = { serviceId: runtime.serviceId, ...(caFingerprint256 ? { caFingerprint256 } : {}) };
531
+ collaborationService = new CollaborationService({ file: path.join(homedir(), '.termdock', 'federation', 'collaboration-services.json'),
532
+ store: collaborationStore, transport: collaborationTransport, node: () => app.locals.collaborationNode,
533
+ sessions: collaborationDirectorySessions, connect: peer => connectCollaborationRpc(runtime.identity, peer) });
534
+ app.locals.collaborationService = collaborationService;
535
+ collaborationService.start();
536
+ server.once('close', () => collaborationService?.close());
519
537
  collaborationTransport.start();
520
538
  server.once('close', () => collaborationTransport?.close());
521
539
  entrySubjectAllowed = subjectId => runtime.store.authorize({ subjectId, serviceId: runtime.serviceId, action: 'authorization.manage' }).allowed;
@@ -251,7 +251,17 @@ 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-descriptor') {
255
+ if (!options.collaborationDescriptor)
256
+ throw new Error('COLLABORATION_UPGRADE_REQUIRED');
257
+ send({ type: 'result', id: packet.id, node: options.collaborationDescriptor() });
258
+ }
259
+ else if (packet.type === 'collaboration-service') {
260
+ if (!options.collaborationService)
261
+ throw new Error('COLLABORATION_UPGRADE_REQUIRED');
262
+ send({ type: 'result', id: packet.id, ...options.collaborationService(subjectId, packet) });
263
+ }
264
+ else if (packet.type === 'collaboration-exchange') {
255
265
  if (!options.collaborationExchange)
256
266
  throw new Error('COLLABORATION_UNAVAILABLE');
257
267
  send({ type: 'result', id: packet.id, ...options.collaborationExchange(subjectId, packet) });
@@ -39,7 +39,7 @@ import { isTmuxRecoveryCandidate } from '../utils/tmuxRecoveryCandidate.js';
39
39
  import { AutomationStore, normalizeAutomationSchedule } from '../agent/automationStore.js';
40
40
  import { buildBracketedSubmitBytes, canDeliverPromptToAgent } from '../agent/promptDelivery.js';
41
41
  import { collaborationRoutes } from '../agent/collaborationRoutes.js';
42
- import { COLLAB_LIMITS, extrasFromBody } from '../agent/collaborationProtocol.js';
42
+ import { extrasFromBody } from '../agent/collaborationProtocol.js';
43
43
  import { CollaborationStore } from '../agent/collaborationStore.js';
44
44
  import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
45
45
  import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
@@ -5476,24 +5476,31 @@ router.delete('/operations/automations/:automationId', (req, res) => {
5476
5476
  return res.status(404).json({ error: '自动任务不存在' });
5477
5477
  res.status(204).send();
5478
5478
  });
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) });
5479
+ // The old native/browser relay may still poll after a CLI-only upgrade. It
5480
+ // receives a fast, explicit refusal and cannot duplicate the server worker.
5481
+ router.all(['/operations/collaboration-federation', '/operations/collaboration-peers'], (_req, res) => {
5482
+ res.status(410).json({ code: 'CLIENT_RELAY_REMOVED', error: '协作由服务端处理,请重新加载服务页面' });
5484
5483
  });
5485
- router.post('/operations/collaboration-peers', (req, res) => {
5484
+ router.post('/operations/collaboration-connections', (req, res) => {
5486
5485
  try {
5487
5486
  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 });
5487
+ const service = req.app.locals.collaborationService;
5488
+ if (!service)
5489
+ return res.status(503).json({ error: '协作服务正在启动' });
5490
+ res.json(service.connectKnown(req.body?.origin, req.body?.nodes));
5492
5491
  }
5493
5492
  catch (error) {
5494
- res.status(400).json({ error: getErrorMessage(error) });
5493
+ res.status(getErrorMessage(error) === 'AUTHORIZATION_DENIED' ? 403 : 400).json({ error: getErrorMessage(error) });
5495
5494
  }
5496
5495
  });
5496
+ router.get('/operations/collaboration-directory', (req, res) => {
5497
+ const service = req.app.locals.collaborationService;
5498
+ if (!service)
5499
+ return res.status(503).json({ error: '协作服务正在启动,请稍后重试' });
5500
+ void service.refresh();
5501
+ res.json(service.directory());
5502
+ });
5503
+ export function collaborationDirectorySessions() { return globalSessionState.sessions.map(orchestrationSessionSnapshot); }
5497
5504
  export function collaborationLocalActivity() {
5498
5505
  return globalSessionState.sessions.map(record => {
5499
5506
  const backendId = collaborationRouting.get(record.sessionId)?.backendSessionId ?? record.backendSessionId;
@@ -5502,45 +5509,13 @@ export function collaborationLocalActivity() {
5502
5509
  });
5503
5510
  }
5504
5511
  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
5512
  router.use('/operations', collaborationGroupRoutes({ store: collaborationStore,
5543
- sessions: () => globalSessionState.sessions.map(orchestrationSessionSnapshot) }));
5513
+ sessions: () => globalSessionState.sessions.map(orchestrationSessionSnapshot),
5514
+ save: (req, input) => {
5515
+ if (!req.app.locals.collaborationService)
5516
+ throw new Error('COLLABORATION_UNAVAILABLE');
5517
+ return req.app.locals.collaborationService.save(input);
5518
+ } }));
5544
5519
  /** Group id from a URL param: the short id a terminal shows resolves to
5545
5520
  * its full id, an ambiguous prefix is refused with 409, and anything else is
5546
5521
  * 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.235",
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.235",
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-N6VdNhxaWAlCuV8LabMbD3Ro9U9s54H5leeRD5KpGwc=",
10
+ "clientBundleHash": "sha256-trS+ETMzb9TMz5LAjLxWuftR8zROhre6jdKGsnMbg60=",
11
11
  "entrypoint": "dist/server/cli.js",
12
12
  "clientEntrypoint": "dist/client/index.html"
13
13
  }