termdock 1.4.298 → 1.4.300

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 (370) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +2 -1
  3. package/dist/client/assets/App-7k2I_09f.js +179 -0
  4. package/dist/client/assets/App-7k2I_09f.js.br +0 -0
  5. package/dist/client/assets/App-7k2I_09f.js.gz +0 -0
  6. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js +12 -0
  7. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.br +0 -0
  8. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.gz +0 -0
  9. package/dist/client/assets/{ChangeWalkthroughPanel-BrAwRa20.js → ChangeWalkthroughPanel-RoMt6giT.js} +2 -2
  10. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.br +0 -0
  11. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.gz +0 -0
  12. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js +1 -0
  13. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.br +0 -0
  14. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.gz +0 -0
  15. package/dist/client/assets/CollaborationTaskContent-FlTbV9e1.js +2 -0
  16. package/dist/client/assets/ComputerControlView-AdjmvNUa.js +5 -0
  17. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.br +0 -0
  18. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.gz +0 -0
  19. package/dist/client/assets/{DagPlayground-DiAEIEvj.js → DagPlayground-x8-mp6wC.js} +1 -1
  20. package/dist/client/assets/DagPlayground-x8-mp6wC.js.br +0 -0
  21. package/dist/client/assets/DagPlayground-x8-mp6wC.js.gz +0 -0
  22. package/dist/client/assets/{DiffLab-BOXARpIG.js → DiffLab-B9KLZ--H.js} +1 -1
  23. package/dist/client/assets/DiffLab-B9KLZ--H.js.br +0 -0
  24. package/dist/client/assets/DiffLab-B9KLZ--H.js.gz +0 -0
  25. package/dist/client/assets/{DiffReview-C90Qt0Gx.js → DiffReview-IEcDVREB.js} +1 -1
  26. package/dist/client/assets/DiffReview-IEcDVREB.js.br +0 -0
  27. package/dist/client/assets/DiffReview-IEcDVREB.js.gz +0 -0
  28. package/dist/client/assets/{DiffReviewLab-CrVXGE1A.js → DiffReviewLab-lWra5pTJ.js} +2 -2
  29. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.br +0 -0
  30. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.gz +0 -0
  31. package/dist/client/assets/{DiffViewer-BA_9bZxs.js → DiffViewer-B2Rue9PA.js} +8 -8
  32. package/dist/client/assets/DiffViewer-B2Rue9PA.js.br +0 -0
  33. package/dist/client/assets/DiffViewer-B2Rue9PA.js.gz +0 -0
  34. package/dist/client/assets/{ModelPreview-BX75IksJ.js → ModelPreview-NMQsfqAe.js} +1 -1
  35. package/dist/client/assets/ModelPreview-NMQsfqAe.js.br +0 -0
  36. package/dist/client/assets/ModelPreview-NMQsfqAe.js.gz +0 -0
  37. package/dist/client/assets/RightSidebar-BOBbegdP.js +1677 -0
  38. package/dist/client/assets/RightSidebar-BOBbegdP.js.br +0 -0
  39. package/dist/client/assets/RightSidebar-BOBbegdP.js.gz +0 -0
  40. package/dist/client/assets/{abnfDiagram-VCTEODGH-BGF7DKjR.js → abnfDiagram-VCTEODGH-BIVuPQbA.js} +1 -1
  41. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.br +0 -0
  42. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.gz +0 -0
  43. package/dist/client/assets/app-CHoOHnjf.js +35 -0
  44. package/dist/client/assets/app-CHoOHnjf.js.br +0 -0
  45. package/dist/client/assets/app-CHoOHnjf.js.gz +0 -0
  46. package/dist/client/assets/{arc-DZUR8G8M.js → arc-CNTT1p1A.js} +1 -1
  47. package/dist/client/assets/arc-CNTT1p1A.js.br +0 -0
  48. package/dist/client/assets/arc-CNTT1p1A.js.gz +0 -0
  49. package/dist/client/assets/{architectureDiagram-5GKGNRK7-CWXE4hoZ.js → architectureDiagram-5GKGNRK7-Cl3gg8ZE.js} +1 -1
  50. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.br +0 -0
  51. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.gz +0 -0
  52. package/dist/client/assets/{blockDiagram-I7D4REHJ-BZdUQzE2.js → blockDiagram-I7D4REHJ-Sxmfy_3_.js} +1 -1
  53. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.br +0 -0
  54. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.gz +0 -0
  55. package/dist/client/assets/{c4Diagram-7LVT6UL2-DA7m-AbK.js → c4Diagram-7LVT6UL2-DaTnrOn7.js} +1 -1
  56. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.br +0 -0
  57. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.gz +0 -0
  58. package/dist/client/assets/channel-CEvBVvt3.js +1 -0
  59. package/dist/client/assets/{chunk-2Q5K7J3B-Dg-zs5cw.js → chunk-2Q5K7J3B-BoiPA32P.js} +1 -1
  60. package/dist/client/assets/{chunk-5VM5RSS4-DKMA2xz_.js → chunk-5VM5RSS4-G5G1YBLk.js} +1 -1
  61. package/dist/client/assets/{chunk-F27PBJKO-ByyS2RgO.js → chunk-F27PBJKO-BNHv_EnL.js} +1 -1
  62. package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.br +0 -0
  63. package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.gz +0 -0
  64. package/dist/client/assets/{chunk-IMKFNOWR-DTTmE4mJ.js → chunk-IMKFNOWR-CxgtyKdq.js} +1 -1
  65. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.br +0 -0
  66. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.gz +0 -0
  67. package/dist/client/assets/{chunk-JWPE2WC7-D9nr-n4D.js → chunk-JWPE2WC7-UO-0iK-G.js} +1 -1
  68. package/dist/client/assets/{chunk-POPQ4Y6H-BPVZ3XrQ.js → chunk-POPQ4Y6H-Cox9z-e2.js} +1 -1
  69. package/dist/client/assets/{chunk-SVP7TREG-Ctn5SKJf.js → chunk-SVP7TREG-B2W8-Lxh.js} +1 -1
  70. package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.br +0 -0
  71. package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.gz +0 -0
  72. package/dist/client/assets/{chunk-TICWLB2K-BCNSq9V4.js → chunk-TICWLB2K-BYDr9nkD.js} +1 -1
  73. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.br +0 -0
  74. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.gz +0 -0
  75. package/dist/client/assets/{chunk-XXDRQBXY-BlJ1FL7I.js → chunk-XXDRQBXY-atArreox.js} +1 -1
  76. package/dist/client/assets/classDiagram-ZZMXUADV-nSmGM9Fl.js +1 -0
  77. package/dist/client/assets/classDiagram-v2-VYDZK3BY-nSmGM9Fl.js +1 -0
  78. package/dist/client/assets/{cose-bilkent-JH36ORCC-Cv4gLm7L.js → cose-bilkent-JH36ORCC-CvfJj195.js} +1 -1
  79. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.br +0 -0
  80. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.gz +0 -0
  81. package/dist/client/assets/{cynefin-OW5HDTMX-BNbPniZY.js → cynefin-OW5HDTMX-ByWE_fUn.js} +1 -1
  82. package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.br +0 -0
  83. package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.gz +0 -0
  84. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DFR2fyKM.js → cynefinDiagram-5FMLGOSQ-BRT0d0MN.js} +1 -1
  85. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.br +0 -0
  86. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.gz +0 -0
  87. package/dist/client/assets/{dagre-GXQ25YYZ-DM9QOAp3.js → dagre-GXQ25YYZ-Twtyv7ud.js} +1 -1
  88. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.br +0 -0
  89. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.gz +0 -0
  90. package/dist/client/assets/{diagram-S7CK7UJ4-DqeCdIIK.js → diagram-S7CK7UJ4-Vl06hZtd.js} +1 -1
  91. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.br +0 -0
  92. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.gz +0 -0
  93. package/dist/client/assets/{diagram-UQ7AKVKN-Cc1KiE_N.js → diagram-UQ7AKVKN-Cp39Mpse.js} +1 -1
  94. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.br +0 -0
  95. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.gz +0 -0
  96. package/dist/client/assets/{diagram-VSXAHHWV-I_0NpClr.js → diagram-VSXAHHWV-Drchji0N.js} +1 -1
  97. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.br +0 -0
  98. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.gz +0 -0
  99. package/dist/client/assets/{diagram-VX7I27RA-DLjDAEEa.js → diagram-VX7I27RA-rp8n_lct.js} +1 -1
  100. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.br +0 -0
  101. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.gz +0 -0
  102. package/dist/client/assets/{diagram-Z3DM3KII-DHo74-rP.js → diagram-Z3DM3KII-BEGDaMBm.js} +1 -1
  103. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.br +0 -0
  104. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.gz +0 -0
  105. package/dist/client/assets/{ebnfDiagram-PWID7BFC-S9dCP2Z9.js → ebnfDiagram-PWID7BFC-kvgdD4A7.js} +1 -1
  106. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.br +0 -0
  107. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.gz +0 -0
  108. package/dist/client/assets/{erDiagram-RLTQ6QDP-CR99Mjq9.js → erDiagram-RLTQ6QDP-DcYHv9hK.js} +1 -1
  109. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.br +0 -0
  110. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.gz +0 -0
  111. package/dist/client/assets/{flowDiagram-HODETNUW-CIqCbTFC.js → flowDiagram-HODETNUW-T9FQKn1F.js} +1 -1
  112. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.br +0 -0
  113. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.gz +0 -0
  114. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CSpUeAKu.js → ganttDiagram-EL5Y4UJY-BEB_X-y5.js} +1 -1
  115. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.br +0 -0
  116. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.gz +0 -0
  117. package/dist/client/assets/{gestureArbiter-C3GqyqzH.js → gestureArbiter-Do0TIdkh.js} +1 -1
  118. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.br +0 -0
  119. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.gz +0 -0
  120. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DBICHCGp.js → gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js} +1 -1
  121. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.br +0 -0
  122. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.gz +0 -0
  123. package/dist/client/assets/{index-C6ftfFlG.css → index-D2_-ypCL.css} +1 -1
  124. package/dist/client/assets/index-D2_-ypCL.css.br +0 -0
  125. package/dist/client/assets/index-D2_-ypCL.css.gz +0 -0
  126. package/dist/client/assets/{infoDiagram-27XIBGKW-CjPrG37z.js → infoDiagram-27XIBGKW-B6y0nIx8.js} +1 -1
  127. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-C718heAs.js → ishikawaDiagram-5VMMS53U-ebGuykGn.js} +1 -1
  128. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.br +0 -0
  129. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.gz +0 -0
  130. package/dist/client/assets/{journeyDiagram-3NMN7TZE-fu76FPSY.js → journeyDiagram-3NMN7TZE-DJDkcIq5.js} +1 -1
  131. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.br +0 -0
  132. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.gz +0 -0
  133. package/dist/client/assets/{kanban-definition-UXKFOSKX-BVj6v9-z.js → kanban-definition-UXKFOSKX-pTBbviRZ.js} +1 -1
  134. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.br +0 -0
  135. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.gz +0 -0
  136. package/dist/client/assets/{layout-Dspjm0zn.js → layout-Dw9FB4Uc.js} +1 -1
  137. package/dist/client/assets/layout-Dw9FB4Uc.js.br +0 -0
  138. package/dist/client/assets/layout-Dw9FB4Uc.js.gz +0 -0
  139. package/dist/client/assets/{linear-BktGv6ei.js → linear-CyLfVGgB.js} +1 -1
  140. package/dist/client/assets/linear-CyLfVGgB.js.br +0 -0
  141. package/dist/client/assets/linear-CyLfVGgB.js.gz +0 -0
  142. package/dist/client/assets/{mermaid.core-Dt2TVLfQ.js → mermaid.core-xRkirK0l.js} +5 -5
  143. package/dist/client/assets/mermaid.core-xRkirK0l.js.br +0 -0
  144. package/dist/client/assets/mermaid.core-xRkirK0l.js.gz +0 -0
  145. package/dist/client/assets/{mindmap-definition-YA3MSWOX-DwxtrCyv.js → mindmap-definition-YA3MSWOX-BxvaDEGQ.js} +1 -1
  146. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.br +0 -0
  147. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.gz +0 -0
  148. package/dist/client/assets/{passwordBootstrap-B5Inu7kn.js → passwordBootstrap-CaYbIQDC.js} +1 -1
  149. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.br +0 -0
  150. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.gz +0 -0
  151. package/dist/client/assets/{pegDiagram-XKGWAZYB-eDDhkygS.js → pegDiagram-XKGWAZYB-Cxh-Bbay.js} +1 -1
  152. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.br +0 -0
  153. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.gz +0 -0
  154. package/dist/client/assets/{pieDiagram-E7YTZNPT-C-A_a53h.js → pieDiagram-E7YTZNPT-CgOsk0Ai.js} +1 -1
  155. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.br +0 -0
  156. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.gz +0 -0
  157. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BRmQ1U56.js → quadrantDiagram-AXDQQJYC-D1S_gKcT.js} +1 -1
  158. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.br +0 -0
  159. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.gz +0 -0
  160. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BRmkfIUn.js → railroadDiagram-O6MQD6OU-BG5e87Np.js} +1 -1
  161. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.br +0 -0
  162. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.gz +0 -0
  163. package/dist/client/assets/rdpSession-DL1rZ49m.js +1 -0
  164. package/dist/client/assets/rdpSession-DL1rZ49m.js.br +0 -0
  165. package/dist/client/assets/rdpSession-DL1rZ49m.js.gz +0 -0
  166. package/dist/client/assets/referenceLongPress-DB039WbW.js +1 -0
  167. package/dist/client/assets/referenceLongPress-DB039WbW.js.br +0 -0
  168. package/dist/client/assets/referenceLongPress-DB039WbW.js.gz +0 -0
  169. package/dist/client/assets/{requirementDiagram-BXWQKSXE-C95pjpXU.js → requirementDiagram-BXWQKSXE-C37DsQfu.js} +1 -1
  170. package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.br +0 -0
  171. package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.gz +0 -0
  172. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-BD7SJZ8y.js → sankeyDiagram-P5KCCOFB-DLzhCYoj.js} +1 -1
  173. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.br +0 -0
  174. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.gz +0 -0
  175. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-D0egOILU.js → sequenceDiagram-WJ2MYXX4-DeVpSvB0.js} +1 -1
  176. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.br +0 -0
  177. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.gz +0 -0
  178. package/dist/client/assets/{sizeCapture-INFHLROL-BaJGHebj.js → sizeCapture-INFHLROL-CE03E66U.js} +1 -1
  179. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.br +0 -0
  180. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.gz +0 -0
  181. package/dist/client/assets/{sparkles-D4t6BWgT.js → sparkles-ButxQzs4.js} +1 -1
  182. package/dist/client/assets/{stateDiagram-D77RDMKH-DEZo4cxr.js → stateDiagram-D77RDMKH-DuqhHT3k.js} +1 -1
  183. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.br +0 -0
  184. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.gz +0 -0
  185. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CxfzEwAH.js +1 -0
  186. package/dist/client/assets/{swimlanes-42K2YHIH-DSZuZ910.js → swimlanes-42K2YHIH-DxkSdJ8g.js} +1 -1
  187. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.br +0 -0
  188. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.gz +0 -0
  189. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-B_muKJLi.js +8 -0
  190. package/dist/client/assets/{timeline-definition-24CTP7MA-Bw0gUCrJ.js → timeline-definition-24CTP7MA-DVpcJ6y5.js} +1 -1
  191. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.br +0 -0
  192. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.gz +0 -0
  193. package/dist/client/assets/{triangle-alert-Cf9mn5Ug.js → triangle-alert-BRy562K_.js} +1 -1
  194. package/dist/client/assets/{vennDiagram-4TSXK5OY-B0gzw-En.js → vennDiagram-4TSXK5OY-BEKXLWGa.js} +1 -1
  195. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.br +0 -0
  196. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.gz +0 -0
  197. package/dist/client/assets/{videoCompression-CyTyZG2b.js → videoCompression-CoVXZ8qw.js} +1 -1
  198. package/dist/client/assets/videoCompression-CoVXZ8qw.js.br +0 -0
  199. package/dist/client/assets/videoCompression-CoVXZ8qw.js.gz +0 -0
  200. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-vaiRXtUT.js → wardleyDiagram-VM6X3IG4-Cj30GTa9.js} +1 -1
  201. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.br +0 -0
  202. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.gz +0 -0
  203. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-Dosv-6JB.js → xychartDiagram-S5SC5T6Z-Cm7NEdCl.js} +1 -1
  204. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.br +0 -0
  205. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.gz +0 -0
  206. package/dist/client/index.html +2 -2
  207. package/dist/client/index.html.br +0 -0
  208. package/dist/client/index.html.gz +0 -0
  209. package/dist/client/sw.js +1 -1
  210. package/dist/client/sw.js.br +0 -0
  211. package/dist/client/sw.js.gz +0 -0
  212. package/dist/server/agent/collaborationCli.js +45 -6
  213. package/dist/server/agent/collaborationRoutes.js +4 -2
  214. package/dist/server/agent/collaborationStore.js +9 -0
  215. package/dist/server/agent/collaborationTaskCli.js +11 -3
  216. package/dist/server/agent/collaborationTaskService.js +14 -8
  217. package/dist/server/agent/collaborationTaskStore.js +67 -25
  218. package/dist/server/agent/integrationCli.js +227 -0
  219. package/dist/server/agent/integrationServer.js +326 -0
  220. package/dist/server/agent/integrationSessions.js +296 -0
  221. package/dist/server/agent/integrationStore.js +199 -0
  222. package/dist/server/cli.js +29 -0
  223. package/dist/server/entry.js +26 -7
  224. package/dist/server/routes/filesystem.js +120 -87
  225. package/dist/server/routes/terminal.js +158 -4
  226. package/dist/server/utils/localAccess.js +27 -7
  227. package/dist/server/utils/sessionDisplayName.js +53 -0
  228. package/docs/integrations.md +239 -0
  229. package/package.json +3 -2
  230. package/runtime-manifest.json +3 -3
  231. package/dist/client/assets/App-D7FKaW3e.js +0 -179
  232. package/dist/client/assets/App-D7FKaW3e.js.br +0 -0
  233. package/dist/client/assets/App-D7FKaW3e.js.gz +0 -0
  234. package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js +0 -7
  235. package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js.br +0 -0
  236. package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js.gz +0 -0
  237. package/dist/client/assets/ChangeWalkthroughPanel-BrAwRa20.js.br +0 -0
  238. package/dist/client/assets/ChangeWalkthroughPanel-BrAwRa20.js.gz +0 -0
  239. package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js +0 -1
  240. package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js.br +0 -0
  241. package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js.gz +0 -0
  242. package/dist/client/assets/CollaborationTaskContent-DkhB6WXO.js +0 -2
  243. package/dist/client/assets/ComputerControlView-CI-TMTRg.js +0 -5
  244. package/dist/client/assets/ComputerControlView-CI-TMTRg.js.br +0 -0
  245. package/dist/client/assets/ComputerControlView-CI-TMTRg.js.gz +0 -0
  246. package/dist/client/assets/DagPlayground-DiAEIEvj.js.br +0 -0
  247. package/dist/client/assets/DagPlayground-DiAEIEvj.js.gz +0 -0
  248. package/dist/client/assets/DiffLab-BOXARpIG.js.br +0 -0
  249. package/dist/client/assets/DiffLab-BOXARpIG.js.gz +0 -0
  250. package/dist/client/assets/DiffReview-C90Qt0Gx.js.br +0 -0
  251. package/dist/client/assets/DiffReview-C90Qt0Gx.js.gz +0 -0
  252. package/dist/client/assets/DiffReviewLab-CrVXGE1A.js.br +0 -0
  253. package/dist/client/assets/DiffReviewLab-CrVXGE1A.js.gz +0 -0
  254. package/dist/client/assets/DiffViewer-BA_9bZxs.js.br +0 -0
  255. package/dist/client/assets/DiffViewer-BA_9bZxs.js.gz +0 -0
  256. package/dist/client/assets/ModelPreview-BX75IksJ.js.br +0 -0
  257. package/dist/client/assets/ModelPreview-BX75IksJ.js.gz +0 -0
  258. package/dist/client/assets/RightSidebar-B_dO0nU5.js +0 -1678
  259. package/dist/client/assets/RightSidebar-B_dO0nU5.js.br +0 -0
  260. package/dist/client/assets/RightSidebar-B_dO0nU5.js.gz +0 -0
  261. package/dist/client/assets/abnfDiagram-VCTEODGH-BGF7DKjR.js.br +0 -0
  262. package/dist/client/assets/abnfDiagram-VCTEODGH-BGF7DKjR.js.gz +0 -0
  263. package/dist/client/assets/app-BJP8Xgmi.js +0 -35
  264. package/dist/client/assets/app-BJP8Xgmi.js.br +0 -0
  265. package/dist/client/assets/app-BJP8Xgmi.js.gz +0 -0
  266. package/dist/client/assets/arc-DZUR8G8M.js.br +0 -0
  267. package/dist/client/assets/arc-DZUR8G8M.js.gz +0 -0
  268. package/dist/client/assets/architectureDiagram-5GKGNRK7-CWXE4hoZ.js.br +0 -0
  269. package/dist/client/assets/architectureDiagram-5GKGNRK7-CWXE4hoZ.js.gz +0 -0
  270. package/dist/client/assets/blockDiagram-I7D4REHJ-BZdUQzE2.js.br +0 -0
  271. package/dist/client/assets/blockDiagram-I7D4REHJ-BZdUQzE2.js.gz +0 -0
  272. package/dist/client/assets/c4Diagram-7LVT6UL2-DA7m-AbK.js.br +0 -0
  273. package/dist/client/assets/c4Diagram-7LVT6UL2-DA7m-AbK.js.gz +0 -0
  274. package/dist/client/assets/channel-DD06PSxm.js +0 -1
  275. package/dist/client/assets/chunk-F27PBJKO-ByyS2RgO.js.br +0 -0
  276. package/dist/client/assets/chunk-F27PBJKO-ByyS2RgO.js.gz +0 -0
  277. package/dist/client/assets/chunk-IMKFNOWR-DTTmE4mJ.js.br +0 -0
  278. package/dist/client/assets/chunk-IMKFNOWR-DTTmE4mJ.js.gz +0 -0
  279. package/dist/client/assets/chunk-SVP7TREG-Ctn5SKJf.js.br +0 -0
  280. package/dist/client/assets/chunk-SVP7TREG-Ctn5SKJf.js.gz +0 -0
  281. package/dist/client/assets/chunk-TICWLB2K-BCNSq9V4.js.br +0 -0
  282. package/dist/client/assets/chunk-TICWLB2K-BCNSq9V4.js.gz +0 -0
  283. package/dist/client/assets/classDiagram-ZZMXUADV-DkVrFG7y.js +0 -1
  284. package/dist/client/assets/classDiagram-v2-VYDZK3BY-DkVrFG7y.js +0 -1
  285. package/dist/client/assets/cose-bilkent-JH36ORCC-Cv4gLm7L.js.br +0 -0
  286. package/dist/client/assets/cose-bilkent-JH36ORCC-Cv4gLm7L.js.gz +0 -0
  287. package/dist/client/assets/cynefin-OW5HDTMX-BNbPniZY.js.br +0 -0
  288. package/dist/client/assets/cynefin-OW5HDTMX-BNbPniZY.js.gz +0 -0
  289. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DFR2fyKM.js.br +0 -0
  290. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DFR2fyKM.js.gz +0 -0
  291. package/dist/client/assets/dagre-GXQ25YYZ-DM9QOAp3.js.br +0 -0
  292. package/dist/client/assets/dagre-GXQ25YYZ-DM9QOAp3.js.gz +0 -0
  293. package/dist/client/assets/diagram-S7CK7UJ4-DqeCdIIK.js.br +0 -0
  294. package/dist/client/assets/diagram-S7CK7UJ4-DqeCdIIK.js.gz +0 -0
  295. package/dist/client/assets/diagram-UQ7AKVKN-Cc1KiE_N.js.br +0 -0
  296. package/dist/client/assets/diagram-UQ7AKVKN-Cc1KiE_N.js.gz +0 -0
  297. package/dist/client/assets/diagram-VSXAHHWV-I_0NpClr.js.br +0 -0
  298. package/dist/client/assets/diagram-VSXAHHWV-I_0NpClr.js.gz +0 -0
  299. package/dist/client/assets/diagram-VX7I27RA-DLjDAEEa.js.br +0 -0
  300. package/dist/client/assets/diagram-VX7I27RA-DLjDAEEa.js.gz +0 -0
  301. package/dist/client/assets/diagram-Z3DM3KII-DHo74-rP.js.br +0 -0
  302. package/dist/client/assets/diagram-Z3DM3KII-DHo74-rP.js.gz +0 -0
  303. package/dist/client/assets/ebnfDiagram-PWID7BFC-S9dCP2Z9.js.br +0 -0
  304. package/dist/client/assets/ebnfDiagram-PWID7BFC-S9dCP2Z9.js.gz +0 -0
  305. package/dist/client/assets/erDiagram-RLTQ6QDP-CR99Mjq9.js.br +0 -0
  306. package/dist/client/assets/erDiagram-RLTQ6QDP-CR99Mjq9.js.gz +0 -0
  307. package/dist/client/assets/flowDiagram-HODETNUW-CIqCbTFC.js.br +0 -0
  308. package/dist/client/assets/flowDiagram-HODETNUW-CIqCbTFC.js.gz +0 -0
  309. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CSpUeAKu.js.br +0 -0
  310. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CSpUeAKu.js.gz +0 -0
  311. package/dist/client/assets/gestureArbiter-C3GqyqzH.js.br +0 -0
  312. package/dist/client/assets/gestureArbiter-C3GqyqzH.js.gz +0 -0
  313. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DBICHCGp.js.br +0 -0
  314. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DBICHCGp.js.gz +0 -0
  315. package/dist/client/assets/index-C6ftfFlG.css.br +0 -0
  316. package/dist/client/assets/index-C6ftfFlG.css.gz +0 -0
  317. package/dist/client/assets/ishikawaDiagram-5VMMS53U-C718heAs.js.br +0 -0
  318. package/dist/client/assets/ishikawaDiagram-5VMMS53U-C718heAs.js.gz +0 -0
  319. package/dist/client/assets/journeyDiagram-3NMN7TZE-fu76FPSY.js.br +0 -0
  320. package/dist/client/assets/journeyDiagram-3NMN7TZE-fu76FPSY.js.gz +0 -0
  321. package/dist/client/assets/kanban-definition-UXKFOSKX-BVj6v9-z.js.br +0 -0
  322. package/dist/client/assets/kanban-definition-UXKFOSKX-BVj6v9-z.js.gz +0 -0
  323. package/dist/client/assets/layout-Dspjm0zn.js.br +0 -0
  324. package/dist/client/assets/layout-Dspjm0zn.js.gz +0 -0
  325. package/dist/client/assets/linear-BktGv6ei.js.br +0 -0
  326. package/dist/client/assets/linear-BktGv6ei.js.gz +0 -0
  327. package/dist/client/assets/mermaid.core-Dt2TVLfQ.js.br +0 -0
  328. package/dist/client/assets/mermaid.core-Dt2TVLfQ.js.gz +0 -0
  329. package/dist/client/assets/mindmap-definition-YA3MSWOX-DwxtrCyv.js.br +0 -0
  330. package/dist/client/assets/mindmap-definition-YA3MSWOX-DwxtrCyv.js.gz +0 -0
  331. package/dist/client/assets/passwordBootstrap-B5Inu7kn.js.br +0 -0
  332. package/dist/client/assets/passwordBootstrap-B5Inu7kn.js.gz +0 -0
  333. package/dist/client/assets/pegDiagram-XKGWAZYB-eDDhkygS.js.br +0 -0
  334. package/dist/client/assets/pegDiagram-XKGWAZYB-eDDhkygS.js.gz +0 -0
  335. package/dist/client/assets/pieDiagram-E7YTZNPT-C-A_a53h.js.br +0 -0
  336. package/dist/client/assets/pieDiagram-E7YTZNPT-C-A_a53h.js.gz +0 -0
  337. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BRmQ1U56.js.br +0 -0
  338. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BRmQ1U56.js.gz +0 -0
  339. package/dist/client/assets/railroadDiagram-O6MQD6OU-BRmkfIUn.js.br +0 -0
  340. package/dist/client/assets/railroadDiagram-O6MQD6OU-BRmkfIUn.js.gz +0 -0
  341. package/dist/client/assets/rdpSession-DknTu7rD.js +0 -1
  342. package/dist/client/assets/rdpSession-DknTu7rD.js.br +0 -0
  343. package/dist/client/assets/rdpSession-DknTu7rD.js.gz +0 -0
  344. package/dist/client/assets/referenceLongPress-CnGp4Gdi.js +0 -1
  345. package/dist/client/assets/referenceLongPress-CnGp4Gdi.js.br +0 -0
  346. package/dist/client/assets/referenceLongPress-CnGp4Gdi.js.gz +0 -0
  347. package/dist/client/assets/requirementDiagram-BXWQKSXE-C95pjpXU.js.br +0 -0
  348. package/dist/client/assets/requirementDiagram-BXWQKSXE-C95pjpXU.js.gz +0 -0
  349. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BD7SJZ8y.js.br +0 -0
  350. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BD7SJZ8y.js.gz +0 -0
  351. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D0egOILU.js.br +0 -0
  352. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D0egOILU.js.gz +0 -0
  353. package/dist/client/assets/sizeCapture-INFHLROL-BaJGHebj.js.br +0 -0
  354. package/dist/client/assets/sizeCapture-INFHLROL-BaJGHebj.js.gz +0 -0
  355. package/dist/client/assets/stateDiagram-D77RDMKH-DEZo4cxr.js.br +0 -0
  356. package/dist/client/assets/stateDiagram-D77RDMKH-DEZo4cxr.js.gz +0 -0
  357. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CJg7AUmk.js +0 -1
  358. package/dist/client/assets/swimlanes-42K2YHIH-DSZuZ910.js.br +0 -0
  359. package/dist/client/assets/swimlanes-42K2YHIH-DSZuZ910.js.gz +0 -0
  360. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CW58IimW.js +0 -8
  361. package/dist/client/assets/timeline-definition-24CTP7MA-Bw0gUCrJ.js.br +0 -0
  362. package/dist/client/assets/timeline-definition-24CTP7MA-Bw0gUCrJ.js.gz +0 -0
  363. package/dist/client/assets/vennDiagram-4TSXK5OY-B0gzw-En.js.br +0 -0
  364. package/dist/client/assets/vennDiagram-4TSXK5OY-B0gzw-En.js.gz +0 -0
  365. package/dist/client/assets/videoCompression-CyTyZG2b.js.br +0 -0
  366. package/dist/client/assets/videoCompression-CyTyZG2b.js.gz +0 -0
  367. package/dist/client/assets/wardleyDiagram-VM6X3IG4-vaiRXtUT.js.br +0 -0
  368. package/dist/client/assets/wardleyDiagram-VM6X3IG4-vaiRXtUT.js.gz +0 -0
  369. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Dosv-6JB.js.br +0 -0
  370. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Dosv-6JB.js.gz +0 -0
@@ -0,0 +1,199 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
4
+ import { CollaborationError } from './collaborationProtocol.js';
5
+ export const INTEGRATION_PROTOCOL = 1;
6
+ export const INTEGRATION_OPERATIONS = ['task.read', 'task.create', 'task.assign', 'task.comment', 'task.revise', 'task.answer', 'events.read', 'session.create', 'session.read', 'session.restore'];
7
+ export function integrationError(code, message, status = 400) { throw new CollaborationError(code, message, status); }
8
+ export function atomicJson(file, value) {
9
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
10
+ const temporary = `${file}.${process.pid}.tmp`;
11
+ const descriptor = fs.openSync(temporary, 'w', 0o600);
12
+ try {
13
+ fs.fchmodSync(descriptor, 0o600);
14
+ fs.writeFileSync(descriptor, JSON.stringify(value));
15
+ fs.fsyncSync(descriptor);
16
+ }
17
+ finally {
18
+ fs.closeSync(descriptor);
19
+ }
20
+ fs.renameSync(temporary, file);
21
+ const directory = fs.openSync(path.dirname(file), 'r');
22
+ try {
23
+ fs.fsyncSync(directory);
24
+ }
25
+ finally {
26
+ fs.closeSync(directory);
27
+ }
28
+ }
29
+ const hash = (value) => createHash('sha256').update(value).digest('hex');
30
+ function identifier(value, label) {
31
+ if (typeof value !== 'string' || !/^[a-zA-Z0-9._-]{1,80}$/.test(value))
32
+ integrationError('INVALID_INTEGRATION_POLICY', `Invalid ${label}`);
33
+ }
34
+ export function validatePolicy(value) {
35
+ identifier(value?.id, 'principal id');
36
+ if (!value.groupId || typeof value.groupId !== 'string' || value.groupId.length > 160)
37
+ integrationError('INVALID_INTEGRATION_POLICY', 'A single groupId is required');
38
+ if (!Array.isArray(value.permissions) || value.permissions.some(p => !INTEGRATION_OPERATIONS.includes(p)))
39
+ integrationError('INVALID_INTEGRATION_POLICY', 'Unknown permission');
40
+ if (!Array.isArray(value.launchProfiles) || value.launchProfiles.length > 16)
41
+ integrationError('INVALID_INTEGRATION_POLICY', 'launchProfiles must be an array');
42
+ const ids = new Set();
43
+ for (const profile of value.launchProfiles) {
44
+ identifier(profile?.id, 'profile id');
45
+ identifier(profile.agentSlug, 'agent slug');
46
+ if (ids.has(profile.id))
47
+ integrationError('INVALID_INTEGRATION_POLICY', 'Duplicate launch profile');
48
+ ids.add(profile.id);
49
+ if (typeof profile.executable !== 'string' || !path.isAbsolute(profile.executable) || /[\x00-\x1f\x7f]/.test(profile.executable))
50
+ integrationError('INVALID_INTEGRATION_POLICY', 'Launcher must be an absolute path');
51
+ if (!Array.isArray(profile.argv) || !Array.isArray(profile.resumeArgv) || [...profile.argv, ...profile.resumeArgv].some(a => typeof a !== 'string' || /[\x00-\x1f\x7f]/.test(a) || a.length > 8192) || profile.argv.length > 128 || profile.resumeArgv.length > 128)
52
+ integrationError('INVALID_INTEGRATION_POLICY', 'Invalid structured argv');
53
+ if (profile.resumeArgv.filter(a => a === '{sessionId}').length !== 1 || profile.resumeArgv.filter(a => a === '{launchArgs}').length !== 1 || profile.resumeArgv.some(a => a.includes('{sessionId}') && a !== '{sessionId}') || profile.resumeArgv.includes('--last'))
54
+ integrationError('INVALID_INTEGRATION_POLICY', 'resumeArgv requires one {sessionId} and one {launchArgs}; --last is forbidden');
55
+ if (!Array.isArray(profile.cwdRoots) || !profile.cwdRoots.length || profile.cwdRoots.some(root => typeof root !== 'string' || !path.isAbsolute(root) || /[\x00-\x1f\x7f]/.test(root)))
56
+ integrationError('INVALID_INTEGRATION_POLICY', 'cwdRoots must be absolute paths');
57
+ }
58
+ // Policies contain launcher configuration, never authentication material.
59
+ return structuredClone({ id: value.id, groupId: value.groupId, permissions: value.permissions, launchProfiles: value.launchProfiles });
60
+ }
61
+ export class IntegrationStore {
62
+ file;
63
+ retention;
64
+ doc = { version: 1, principals: [], events: [], sequences: {}, prunedThrough: {}, sources: {}, consumers: {} };
65
+ listeners = new Set();
66
+ constructor(file, retention = 10000) {
67
+ this.file = file;
68
+ this.retention = retention;
69
+ try {
70
+ const document = JSON.parse(fs.readFileSync(file, 'utf8'));
71
+ if (document.version !== 1 || !Array.isArray(document.events) || !Array.isArray(document.principals) || !document.sources || !document.consumers || !document.sequences || !document.prunedThrough)
72
+ throw new Error('Invalid integration journal');
73
+ this.doc = document;
74
+ }
75
+ catch (error) {
76
+ if (error.code !== 'ENOENT')
77
+ throw error;
78
+ }
79
+ }
80
+ write(next) {
81
+ if (Buffer.byteLength(JSON.stringify(next)) > 64 * 1024 * 1024)
82
+ integrationError('EVENT_STORAGE_FULL', 'Integration journal is full; no event was acknowledged', 503);
83
+ atomicJson(this.file, next);
84
+ this.doc = next;
85
+ for (const listener of this.listeners) {
86
+ try {
87
+ listener();
88
+ }
89
+ catch { /* Subscription reports its own failure. */ }
90
+ }
91
+ }
92
+ subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
93
+ policies() { return this.doc.principals.map(({ tokenHash: _, ...principal }) => structuredClone(principal)); }
94
+ provision(policy) {
95
+ const checked = validatePolicy(policy);
96
+ if (this.doc.principals.some(p => p.id === checked.id))
97
+ integrationError('INTEGRATION_EXISTS', 'Use a new id; credentials cannot be silently replaced', 409);
98
+ const token = randomBytes(32).toString('base64url');
99
+ const next = structuredClone(this.doc);
100
+ next.principals.push({ ...checked, tokenHash: hash(token), revoked: false });
101
+ this.write(next);
102
+ return { principal: checked, token };
103
+ }
104
+ revoke(id) {
105
+ const next = structuredClone(this.doc), principal = next.principals.find(p => p.id === id);
106
+ if (!principal)
107
+ integrationError('INTEGRATION_NOT_FOUND', 'Integration does not exist', 404);
108
+ principal.revoked = true;
109
+ this.write(next);
110
+ }
111
+ authenticate(id, token) {
112
+ const principal = this.doc.principals.find(p => p.id === id);
113
+ const given = Buffer.from(hash(token)), expected = Buffer.from(principal?.tokenHash ?? hash('invalid'));
114
+ if (!principal || !timingSafeEqual(given, expected))
115
+ integrationError('INTEGRATION_UNAUTHORIZED', 'Invalid integration credential', 401);
116
+ if (principal.revoked)
117
+ integrationError('INTEGRATION_REVOKED', 'Integration authorization was revoked', 403);
118
+ return structuredClone(principal);
119
+ }
120
+ authorize(principal, permission, groupId = principal.groupId) {
121
+ const active = this.doc.principals.find(p => p.id === principal.id);
122
+ if (!active || active.revoked)
123
+ integrationError('INTEGRATION_REVOKED', 'Integration authorization was revoked', 403);
124
+ if (groupId !== active.groupId || !active.permissions.includes(permission))
125
+ integrationError('INTEGRATION_PERMISSION_DENIED', 'Operation is outside the integration grant', 403);
126
+ }
127
+ append(updates) {
128
+ const next = structuredClone(this.doc);
129
+ let changed = false;
130
+ for (const update of updates) {
131
+ if (next.sources[update.sourceKey] === update.sourceVersion)
132
+ continue;
133
+ const group = update.event.group_id, sequence = (next.sequences[group] ?? 0) + 1;
134
+ next.events.push({ ...update.event, event_id: update.eventId ?? randomUUID(), sequence, cursor: this.cursor(group, sequence) });
135
+ next.sequences[group] = sequence;
136
+ next.sources[update.sourceKey] = update.sourceVersion;
137
+ changed = true;
138
+ }
139
+ if (!changed)
140
+ return;
141
+ const counts = new Map();
142
+ next.events = next.events.reverse().filter(event => {
143
+ const count = (counts.get(event.group_id) ?? 0) + 1;
144
+ counts.set(event.group_id, count);
145
+ if (count <= this.retention)
146
+ return true;
147
+ next.prunedThrough[event.group_id] = Math.max(next.prunedThrough[event.group_id] ?? 0, event.sequence);
148
+ return false;
149
+ }).reverse();
150
+ this.write(next);
151
+ }
152
+ cursor(group, sequence) { return Buffer.from(JSON.stringify({ v: 1, group, sequence })).toString('base64url'); }
153
+ decode(group, cursor) {
154
+ let value;
155
+ try {
156
+ value = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
157
+ }
158
+ catch {
159
+ return integrationError('INVALID_EVENT_CURSOR', 'Invalid cursor');
160
+ }
161
+ if (value.v !== 1 || value.group !== group || !Number.isSafeInteger(value.sequence) || value.sequence < 0 || value.sequence > (this.doc.sequences[group] ?? 0))
162
+ integrationError('INVALID_EVENT_CURSOR', 'Cursor does not belong to this group');
163
+ return value.sequence;
164
+ }
165
+ key(principal, consumer) { identifier(consumer, 'consumer'); return `${principal.id}:${principal.groupId}:${consumer}`; }
166
+ page(principal, consumer, cursor) {
167
+ this.authorize(principal, 'events.read');
168
+ const key = this.key(principal, consumer), group = principal.groupId;
169
+ const floor = this.doc.prunedThrough[group] ?? 0;
170
+ // A new consumer explicitly starts at the earliest retained fact. Existing
171
+ // consumers never silently skip a retention gap.
172
+ if (this.doc.consumers[key] === undefined) {
173
+ const next = structuredClone(this.doc);
174
+ next.consumers[key] = floor;
175
+ this.write(next);
176
+ }
177
+ const after = cursor ? this.decode(group, cursor) : this.doc.consumers[key];
178
+ if (after < floor)
179
+ integrationError('EVENT_RETENTION_GAP', 'Confirmed cursor is older than retained history; reconcile task/session snapshots and use a new consumer', 409);
180
+ return { events: structuredClone(this.doc.events.filter(e => e.group_id === group && e.sequence > after).slice(0, 100)), cursor: this.cursor(group, after) };
181
+ }
182
+ ack(principal, consumer, cursor) {
183
+ this.authorize(principal, 'events.read');
184
+ const key = this.key(principal, consumer), sequence = this.decode(principal.groupId, cursor), confirmed = this.doc.consumers[key];
185
+ if (confirmed === undefined)
186
+ integrationError('EVENT_CONSUMER_NOT_STARTED', 'Subscribe before acknowledging');
187
+ if (sequence < confirmed)
188
+ integrationError('EVENT_ACK_REWIND', 'Acknowledgement cannot rewind');
189
+ if (confirmed < (this.doc.prunedThrough[principal.groupId] ?? 0))
190
+ integrationError('EVENT_RETENTION_GAP', 'Consumer has a retention gap', 409);
191
+ // Cursor is a cumulative declaration that every preceding event is safely
192
+ // in the bridge inbox. The subscription server separately checks it was sent.
193
+ const next = structuredClone(this.doc);
194
+ next.consumers[key] = sequence;
195
+ this.write(next);
196
+ return { cursor, ack_semantics: 'durable_received' };
197
+ }
198
+ cursorSequence(group, cursor) { return this.decode(group, cursor); }
199
+ }
@@ -15,6 +15,7 @@ if (process.argv.slice(2).some(arg => arg === '--federation-pairing' || arg ===
15
15
  const { runFederationCli } = await import('./federation/cli.js');
16
16
  process.exit(await runFederationCli(process.argv.slice(2)));
17
17
  }
18
+ import { INTEGRATION_HELP, runIntegrationAdmin, revokeIntegration, runIntegrationCollab } from './agent/integrationCli.js';
18
19
  import { parseNotifyCommand, NOTIFY_HELP, NOTIFY_PROMPT } from './agent/notifyCli.js';
19
20
  import { detectLocalSessionContext, resolveLocalCollaborationContext } from './agent/localSessionContext.js';
20
21
  import { parseAutomationCommand, executeAutomationCommand, AUTOMATION_HELP } from './agent/automationCli.js';
@@ -207,6 +208,7 @@ Short commands:
207
208
  n [message] Progress reminder; run td n for help and Agent instructions
208
209
  notify [message] Same as n (see td n --prompt)
209
210
  automation Manage scheduled tasks (see td automation --help)
211
+ integration Manage local scoped integrations (see integration help)
210
212
  collab status Show this Session's collaboration groups and peers
211
213
  collab inbox Read messages without acknowledging (see collab --help)
212
214
  collab send <session-id> <message>
@@ -658,6 +660,7 @@ function parseArgs(argv) {
658
660
  let pluginHooks;
659
661
  let pluginRemove;
660
662
  let agentEvent;
663
+ let integration;
661
664
  let collab;
662
665
  let automation;
663
666
  let notify;
@@ -797,6 +800,10 @@ function parseArgs(argv) {
797
800
  }
798
801
  argv = [];
799
802
  }
803
+ else if (command === 'integration') {
804
+ integration = argv.slice(1);
805
+ argv = [];
806
+ }
800
807
  else if (command === 'collab') {
801
808
  try {
802
809
  collab = parseCollaborationCommand(argv.slice(1));
@@ -1183,6 +1190,7 @@ function parseArgs(argv) {
1183
1190
  pluginHooks,
1184
1191
  pluginRemove,
1185
1192
  agentEvent,
1193
+ integration,
1186
1194
  collab,
1187
1195
  automation,
1188
1196
  notify,
@@ -1434,6 +1442,10 @@ async function runAutomation(command) {
1434
1442
  }
1435
1443
  async function runCollab(command) {
1436
1444
  const runningState = getRunningState();
1445
+ if (command.options.principal) {
1446
+ process.exitCode = await runIntegrationCollab(command, runningState?.port ?? 9834, { write: line => console.log(line), stdin: readStdinText });
1447
+ return;
1448
+ }
1437
1449
  if (!runningState?.localApiToken) {
1438
1450
  // Help is a plain read: it must stay available without a running server.
1439
1451
  if (command.action === 'help') {
@@ -3475,6 +3487,23 @@ async function main() {
3475
3487
  await runAutomation(options.automation);
3476
3488
  return;
3477
3489
  }
3490
+ if (options.integration) {
3491
+ const argv = options.integration;
3492
+ if (!argv.length || ['help', '--help'].includes(argv[0])) {
3493
+ console.log(INTEGRATION_HELP);
3494
+ return;
3495
+ }
3496
+ const state = getRunningState();
3497
+ if (!state?.localApiToken) {
3498
+ console.error(JSON.stringify({ ok: false, code: 'SERVICE_UNAVAILABLE', error: 'Termdock is not running' }));
3499
+ process.exitCode = 1;
3500
+ return;
3501
+ }
3502
+ process.exitCode = argv[0] === 'revoke' && argv.length === 2
3503
+ ? await revokeIntegration(argv[1], state.port, state.localApiToken, line => console.log(line))
3504
+ : await runIntegrationAdmin(argv, state.port, state.localApiToken, line => console.log(line));
3505
+ return;
3506
+ }
3478
3507
  if (options.collab) {
3479
3508
  await runCollab(options.collab);
3480
3509
  return; // Preserve the receipt exit code and allow piped JSON output to drain.
@@ -1,7 +1,8 @@
1
+ import { IntegrationRuntime, integrationSocketPath } from './agent/integrationServer.js';
1
2
  import { CollaborationService } from './agent/collaborationService.js';
2
3
  import { X509Certificate } from 'node:crypto';
3
4
  import { CollaborationPeerTransport, connectCollaborationRpc } from './agent/collaborationPeerTransport.js';
4
- import { collaborationStore, collaborationTaskStore, deliverPeerCollaboration, collaborationLocalActivity, collaborationDirectorySessions, prepareCollaborationTaskWorker } from './routes/terminal.js';
5
+ import { collaborationStore, collaborationTaskStore, deliverPeerCollaboration, collaborationLocalActivity, collaborationDirectorySessions, prepareCollaborationTaskWorker, integrationSessionAdapter } from './routes/terminal.js';
5
6
  import { CollaborationTaskService } from './agent/collaborationTaskService.js';
6
7
  import { setPushTargetPeerId } from './notifications/pushService.js';
7
8
  import { desktopDirectTargets } from './federation/desktopTargets.js';
@@ -571,7 +572,7 @@ export function startServer(options = {}) {
571
572
  return routeInvitations.consume(code, subjectId);
572
573
  },
573
574
  });
574
- void federation.then(runtime => {
575
+ void federation.then(async (runtime) => {
575
576
  federationServiceId = runtime.serviceId;
576
577
  setPushTargetPeerId(runtime.serviceId);
577
578
  refreshDirectTargets();
@@ -634,6 +635,21 @@ export function startServer(options = {}) {
634
635
  app.locals.collaborationService = collaborationService;
635
636
  const collaborationTasks = new CollaborationTaskService(collaborationTaskStore, collaborationStore, collaborationService, deliverPeerCollaboration, (task, template, dependencies) => prepareCollaborationTaskWorker(task, template, dependencies, collaborationService, (taskId, dependencyId, offset) => collaborationTasks.dependencyChunk(taskId, dependencyId, offset)));
636
637
  app.locals.collaborationTasks = collaborationTasks;
638
+ if (options.port !== 0) {
639
+ let integration;
640
+ try {
641
+ const port = options.port ?? DEFAULT_PORT;
642
+ integration = new IntegrationRuntime({ directory: path.join(homedir(), '.termdock', 'integration-state', String(port)), socketPath: integrationSocketPath(port), adminToken: options.localApiToken,
643
+ messages: collaborationStore, taskStore: collaborationTaskStore, tasks: collaborationTasks, peers: collaborationService, sessions: integrationSessionAdapter });
644
+ await integration.listen();
645
+ app.locals.integrationRuntime = integration;
646
+ server.once('close', () => { void integration?.close(); });
647
+ }
648
+ catch {
649
+ await integration?.close();
650
+ console.error('[integrations] Local integration endpoint could not start; existing terminal service remains available');
651
+ }
652
+ }
637
653
  collaborationTasks.start();
638
654
  server.once('close', () => collaborationTasks.close());
639
655
  collaborationService.start();
@@ -786,15 +802,18 @@ export function startServer(options = {}) {
786
802
  return;
787
803
  });
788
804
  server.listen(port, host, () => {
805
+ const listeningAddress = server.address();
806
+ const listeningPort = listeningAddress && typeof listeningAddress !== 'string' ? listeningAddress.port : port;
807
+ const listeningHost = listeningAddress && typeof listeningAddress !== 'string' ? listeningAddress.address : host;
789
808
  const displayHost = host === '0.0.0.0' ? 'localhost' : host;
790
- console.log(`Termdock server running at ${scheme}://${displayHost}:${port}`);
791
- console.log(`Health check: ${scheme}://${displayHost}:${port}/health`);
809
+ console.log(`Termdock server running at ${scheme}://${displayHost}:${listeningPort}`);
810
+ console.log(`Health check: ${scheme}://${displayHost}:${listeningPort}/health`);
792
811
  const onboardingServerState = scheme === 'https'
793
- ? startOnboardingServer({ httpsPort: port, caCertPath: options.httpsCaPath })
812
+ ? startOnboardingServer({ httpsPort: listeningPort, caCertPath: options.httpsCaPath })
794
813
  : { server: null, url: null };
795
- void localAccessManager.start({ host, port, scheme, caCertPath: options.httpsCaPath, onboardingPort: options.onboardingPort ?? port }).then((state) => {
814
+ void localAccessManager.start({ host: listeningHost, port: listeningPort, scheme, caCertPath: options.httpsCaPath, onboardingPort: options.onboardingPort ?? listeningPort }).then((state) => {
796
815
  const publishState = () => {
797
- const onboardingUrl = onboardingServerState.url ?? state.onboardingUrl;
816
+ const onboardingUrl = state.status === 'loopback-only' ? null : onboardingServerState.url ?? state.onboardingUrl;
798
817
  latestLocalAccessState = state;
799
818
  latestOnboardingUrl = onboardingUrl;
800
819
  if (state.status === 'active') {
@@ -5064,89 +5064,136 @@ router.post('/upload', async (req, res) => {
5064
5064
  res.status(400).json({ error: 'Missing dir query parameter', code: 'MISSING_DIR' });
5065
5065
  return;
5066
5066
  }
5067
+ // New clients can reject an oversized selection before sending any files.
5068
+ // Older clients are still checked against the actual multipart contents.
5069
+ const declaredCount = req.query.fileCount;
5070
+ if (declaredCount !== undefined && (typeof declaredCount !== 'string' || !/^\d+$/.test(declaredCount))) {
5071
+ res.status(400).json({ error: 'Invalid fileCount query parameter', code: 'INVALID_FILE_COUNT' });
5072
+ return;
5073
+ }
5074
+ if (typeof declaredCount === 'string' && Number(declaredCount) > MAX_UPLOAD_FILES) {
5075
+ res.status(413).json({ error: 'Upload limit exceeded', code: 'UPLOAD_LIMIT', files: [], results: [], maxFiles: MAX_UPLOAD_FILES });
5076
+ return;
5077
+ }
5067
5078
  const resolvedDir = await pathValidator.validatePathAsync(dir);
5068
5079
  const stat = await fs.promises.stat(resolvedDir);
5069
5080
  if (!stat.isDirectory()) {
5070
5081
  res.status(400).json({ error: 'Target is not a directory', code: 'NOT_A_DIRECTORY' });
5071
5082
  return;
5072
5083
  }
5073
- const files = [];
5074
- let fileCount = 0;
5075
- let aborted = false;
5084
+ const results = [];
5085
+ const createdPaths = [];
5086
+ let failure;
5076
5087
  let totalSize = 0;
5088
+ const fail = (error, code = 'UPLOAD_FAILED', status = 500) => {
5089
+ failure ??= { error, code, status };
5090
+ };
5077
5091
  const bb = busboy({
5078
5092
  headers: req.headers,
5079
5093
  // Browser FormData sends UTF-8 filenames, while busboy defaults bare
5080
5094
  // multipart filename parameters to latin1. Without this, dropped files
5081
5095
  // named with CJK characters turn into mojibake like "查询".
5082
5096
  defParamCharset: 'utf8',
5083
- limits: { fileSize: MAX_UPLOAD_SIZE, files: MAX_UPLOAD_FILES },
5097
+ // Enforce the count in the file handler rather than Busboy's files limit:
5098
+ // the latter silently omits the names of files beyond the limit.
5099
+ // Busboy marks a stream truncated as soon as this boundary is reached;
5100
+ // one extra byte lets an exactly-100MB file remain a valid upload.
5101
+ limits: { fileSize: MAX_UPLOAD_SIZE + 1 },
5084
5102
  });
5085
5103
  const writePromises = [];
5086
5104
  bb.on('file', (_fieldname, fileStream, info) => {
5087
- if (aborted) {
5088
- fileStream.resume();
5089
- return;
5090
- }
5091
- fileCount++;
5092
- if (fileCount > MAX_UPLOAD_FILES) {
5093
- aborted = true;
5094
- fileStream.resume();
5095
- return;
5096
- }
5097
- const { filename } = info;
5098
- const destName = sanitizeUploadFilename(filename, `file_${fileCount}`);
5105
+ const index = results.length;
5106
+ const destName = sanitizeUploadFilename(info.filename, `file_${index + 1}`);
5107
+ const result = { index, name: destName, status: 'failed' };
5108
+ results.push(result);
5109
+ if (results.length > MAX_UPLOAD_FILES)
5110
+ fail('Upload limit exceeded', 'UPLOAD_LIMIT', 413);
5099
5111
  const baseDestPath = path.join(resolvedDir, destName);
5100
- // Resolve unique path asynchronously inside the write promise
5112
+ fileStream.on('limit', () => fail('Upload limit exceeded', 'UPLOAD_LIMIT', 413));
5113
+ // Parsing/cancellation can destroy this stream while exclusive open is
5114
+ // pending. The async iterator below will still observe the same error.
5115
+ fileStream.on('error', () => { });
5101
5116
  const writePromise = (async () => {
5102
- const destPath = await uniquePath(baseDestPath);
5103
- return new Promise((resolve, reject) => {
5104
- const writeStream = fs.createWriteStream(destPath);
5105
- let fileSize = 0;
5106
- fileStream.on('data', (chunk) => {
5107
- fileSize += chunk.length;
5108
- totalSize += chunk.length;
5109
- if (totalSize > MAX_UPLOAD_SIZE) {
5110
- aborted = true;
5111
- fileStream.destroy?.(new Error('File too large'));
5112
- writeStream.destroy();
5113
- return;
5117
+ let destination;
5118
+ let fileSize = 0;
5119
+ try {
5120
+ if (!failure) {
5121
+ destination = await createUniqueUploadFile(baseDestPath);
5122
+ createdPaths.push(destination.path);
5123
+ }
5124
+ // Keep draining rejected files so every multipart item can be
5125
+ // reported, and Busboy can finish without a dangling write stream.
5126
+ for await (const chunk of fileStream) {
5127
+ const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
5128
+ fileSize += data.length;
5129
+ totalSize += data.length;
5130
+ if (totalSize > MAX_UPLOAD_SIZE)
5131
+ fail('Upload limit exceeded', 'UPLOAD_LIMIT', 413);
5132
+ if (destination && !failure) {
5133
+ try {
5134
+ await destination.handle.writeFile(data);
5135
+ }
5136
+ catch (error) {
5137
+ // Do not throw from the iterator body: that would destroy the
5138
+ // multipart source before Busboy can drain the rest of it.
5139
+ fail(error instanceof Error ? error.message : 'Upload failed');
5140
+ }
5114
5141
  }
5115
- });
5116
- fileStream.pipe(writeStream);
5117
- writeStream.on('finish', () => {
5118
- files.push({ name: destName, path: destPath, size: fileSize });
5119
- resolve();
5120
- });
5121
- writeStream.on('error', (err) => {
5122
- fs.promises.unlink(destPath).catch(() => { });
5123
- reject(err);
5124
- });
5125
- });
5142
+ }
5143
+ if (destination && !failure) {
5144
+ result.status = 'uploaded';
5145
+ result.path = destination.path;
5146
+ result.size = fileSize;
5147
+ }
5148
+ }
5149
+ catch (error) {
5150
+ fail(error instanceof Error ? error.message : 'Upload failed');
5151
+ fileStream.resume();
5152
+ }
5153
+ finally {
5154
+ try {
5155
+ await destination?.handle.close();
5156
+ }
5157
+ catch (error) {
5158
+ fail(error instanceof Error ? error.message : 'Upload failed');
5159
+ }
5160
+ }
5126
5161
  })();
5127
5162
  writePromises.push(writePromise);
5128
5163
  });
5129
- bb.on('error', (_err) => {
5130
- // Will be handled by the promise rejection below
5131
- });
5132
- bb.on('filesLimit', () => {
5133
- aborted = true;
5134
- });
5135
- bb.on('finish', async () => {
5136
- try {
5137
- await Promise.all(writePromises);
5138
- if (aborted && files.length === 0) {
5139
- res.status(413).json({ error: 'Upload limit exceeded', code: 'UPLOAD_LIMIT' });
5140
- return;
5141
- }
5142
- res.status(200).json({ files });
5143
- }
5144
- catch (err) {
5145
- const message = err instanceof Error ? err.message : 'Upload failed';
5146
- res.status(500).json({ error: message, code: 'UPLOAD_FAILED' });
5147
- }
5148
- });
5149
- req.pipe(bb);
5164
+ const onAborted = () => bb.destroy(new Error('Upload canceled'));
5165
+ req.once('aborted', onAborted);
5166
+ try {
5167
+ await new Promise((resolve, reject) => {
5168
+ bb.once('finish', resolve);
5169
+ bb.once('error', reject);
5170
+ req.pipe(bb);
5171
+ });
5172
+ }
5173
+ catch (error) {
5174
+ fail(error instanceof Error ? error.message : 'Upload failed');
5175
+ }
5176
+ finally {
5177
+ req.off('aborted', onAborted);
5178
+ }
5179
+ await Promise.all(writePromises);
5180
+ if (res.destroyed)
5181
+ fail('Upload canceled');
5182
+ if (failure) {
5183
+ // Paths enter this list only after an exclusive create succeeded. Never
5184
+ // remove an existing file or another concurrent request's destination.
5185
+ await Promise.all(createdPaths.map(filePath => fs.promises.unlink(filePath).catch(error => {
5186
+ if (error.code !== 'ENOENT')
5187
+ throw error;
5188
+ })));
5189
+ const { error, code, status } = failure;
5190
+ if (!res.destroyed)
5191
+ res.status(status).json({ error, code, files: [], maxFiles: MAX_UPLOAD_FILES,
5192
+ results: results.map(result => ({ index: result.index, name: result.name, status: 'failed', error, code })) });
5193
+ return;
5194
+ }
5195
+ const files = results.map(result => ({ name: result.name, path: result.path, size: result.size }));
5196
+ res.status(200).json({ files, results });
5150
5197
  }
5151
5198
  catch (error) {
5152
5199
  if (error instanceof Error && error.code === 'PATH_NOT_ALLOWED') {
@@ -5157,32 +5204,18 @@ router.post('/upload', async (req, res) => {
5157
5204
  res.status(500).json({ error: message, code: 'UPLOAD_FAILED' });
5158
5205
  }
5159
5206
  });
5160
- async function uniquePath(filePath) {
5161
- try {
5162
- await fs.promises.access(filePath);
5163
- // File exists — find a unique name
5164
- const ext = path.extname(filePath);
5165
- const base = filePath.slice(0, filePath.length - ext.length);
5166
- let counter = 1;
5167
- let candidate;
5168
- do {
5169
- candidate = `${base}_${counter}${ext}`;
5170
- counter++;
5171
- } while (await exists(candidate));
5172
- return candidate;
5173
- }
5174
- catch {
5175
- // File doesn't exist — use as-is
5176
- return filePath;
5177
- }
5178
- }
5179
- async function exists(p) {
5180
- try {
5181
- await fs.promises.access(p);
5182
- return true;
5183
- }
5184
- catch {
5185
- return false;
5207
+ async function createUniqueUploadFile(filePath) {
5208
+ const ext = path.extname(filePath);
5209
+ const base = filePath.slice(0, filePath.length - ext.length);
5210
+ for (let counter = 0;; counter++) {
5211
+ const candidate = counter === 0 ? filePath : `${base}_${counter}${ext}`;
5212
+ try {
5213
+ return { path: candidate, handle: await fs.promises.open(candidate, 'wx') };
5214
+ }
5215
+ catch (error) {
5216
+ if (error.code !== 'EEXIST')
5217
+ throw error;
5218
+ }
5186
5219
  }
5187
5220
  }
5188
5221
  export default router;