termdock 1.4.299 → 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 (367) 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-2lXfrgk4.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-EFUb9S3u.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-CXWjSE2q.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-ByMkBaD9.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-DoWaKm94.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-B4hn9z0k.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-C-LJZ8so.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-BY-pfYKk.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-DIENgCZn.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-C1h4e4az.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-B_yOr54K.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-DRD4zxIo.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-CXytnjmD.js → chunk-2Q5K7J3B-BoiPA32P.js} +1 -1
  60. package/dist/client/assets/{chunk-5VM5RSS4-Bjmajkf7.js → chunk-5VM5RSS4-G5G1YBLk.js} +1 -1
  61. package/dist/client/assets/{chunk-F27PBJKO-CQCL8mJG.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-ruHGCADe.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-U18S7rxZ.js → chunk-JWPE2WC7-UO-0iK-G.js} +1 -1
  68. package/dist/client/assets/{chunk-POPQ4Y6H-DWnwssfL.js → chunk-POPQ4Y6H-Cox9z-e2.js} +1 -1
  69. package/dist/client/assets/{chunk-SVP7TREG-DMHj3NSq.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-Dh_ekvFW.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-Bq5wD6iJ.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-CsySWhYg.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-9DKE5wV7.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-9DKE5wV7.js.gz → cynefin-OW5HDTMX-ByWE_fUn.js.gz} +0 -0
  84. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-B_8fvr9c.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-BOdsFNFT.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-6v_qvAvi.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-c03oOGhD.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-MMIhRYp_.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-B_l-uebl.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-DKoBC5gn.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-Bs3wu8Az.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-mYnHYbKl.js → erDiagram-RLTQ6QDP-DcYHv9hK.js} +1 -1
  109. package/dist/client/assets/{erDiagram-RLTQ6QDP-mYnHYbKl.js.br → 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-CwCLWbib.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-mse8c-ni.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-BviL29ps.js → gestureArbiter-Do0TIdkh.js} +1 -1
  118. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.br +0 -0
  119. package/dist/client/assets/{gestureArbiter-BviL29ps.js.gz → gestureArbiter-Do0TIdkh.js.gz} +0 -0
  120. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-BVUfLjAX.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-XjoFF4BC.js → infoDiagram-27XIBGKW-B6y0nIx8.js} +1 -1
  127. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-bH299bWL.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-BG0u_X1A.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-DRoc9N-i.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-mh0t6JpE.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-B9KTRuUV.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-Bu2G9zhK.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-Go6Bg5xm.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-C7-MUAHQ.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-DKUIl_U6.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-aoDNlV7J.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-BKuuaF2s.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-BwfCW2bp.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-DbpU9Uig.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-Bxa22XVM.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-C4GvzT9z.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-B9zJJwQw.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-BJbfT-v-.js → sparkles-ButxQzs4.js} +1 -1
  182. package/dist/client/assets/{stateDiagram-D77RDMKH-fqbw6LZa.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-Hpc28YZY.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-qDieK7Re.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-YOCAWhtT.js → triangle-alert-BRy562K_.js} +1 -1
  194. package/dist/client/assets/{vennDiagram-4TSXK5OY-BJpZlHao.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-DI9vP0dm.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-CQJfj6Ns.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-Cy-lkWBX.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-DzuUkEI_.js +0 -179
  232. package/dist/client/assets/App-DzuUkEI_.js.br +0 -0
  233. package/dist/client/assets/App-DzuUkEI_.js.gz +0 -0
  234. package/dist/client/assets/ArchitecturePanel-QodxiiCc.js +0 -7
  235. package/dist/client/assets/ArchitecturePanel-QodxiiCc.js.br +0 -0
  236. package/dist/client/assets/ArchitecturePanel-QodxiiCc.js.gz +0 -0
  237. package/dist/client/assets/ChangeWalkthroughPanel-2lXfrgk4.js.br +0 -0
  238. package/dist/client/assets/ChangeWalkthroughPanel-2lXfrgk4.js.gz +0 -0
  239. package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js +0 -1
  240. package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js.br +0 -0
  241. package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js.gz +0 -0
  242. package/dist/client/assets/CollaborationTaskContent-BJe9TUtZ.js +0 -2
  243. package/dist/client/assets/ComputerControlView-B5vigCoW.js +0 -5
  244. package/dist/client/assets/ComputerControlView-B5vigCoW.js.br +0 -0
  245. package/dist/client/assets/ComputerControlView-B5vigCoW.js.gz +0 -0
  246. package/dist/client/assets/DagPlayground-EFUb9S3u.js.br +0 -0
  247. package/dist/client/assets/DagPlayground-EFUb9S3u.js.gz +0 -0
  248. package/dist/client/assets/DiffLab-CXWjSE2q.js.br +0 -0
  249. package/dist/client/assets/DiffLab-CXWjSE2q.js.gz +0 -0
  250. package/dist/client/assets/DiffReview-ByMkBaD9.js.br +0 -0
  251. package/dist/client/assets/DiffReview-ByMkBaD9.js.gz +0 -0
  252. package/dist/client/assets/DiffReviewLab-DoWaKm94.js.br +0 -0
  253. package/dist/client/assets/DiffReviewLab-DoWaKm94.js.gz +0 -0
  254. package/dist/client/assets/DiffViewer-B4hn9z0k.js.br +0 -0
  255. package/dist/client/assets/DiffViewer-B4hn9z0k.js.gz +0 -0
  256. package/dist/client/assets/ModelPreview-C-LJZ8so.js.br +0 -0
  257. package/dist/client/assets/ModelPreview-C-LJZ8so.js.gz +0 -0
  258. package/dist/client/assets/RightSidebar-EaMm4prW.js +0 -1678
  259. package/dist/client/assets/RightSidebar-EaMm4prW.js.br +0 -0
  260. package/dist/client/assets/RightSidebar-EaMm4prW.js.gz +0 -0
  261. package/dist/client/assets/abnfDiagram-VCTEODGH-BY-pfYKk.js.br +0 -0
  262. package/dist/client/assets/abnfDiagram-VCTEODGH-BY-pfYKk.js.gz +0 -0
  263. package/dist/client/assets/app-Ch0UDtJ-.js +0 -35
  264. package/dist/client/assets/app-Ch0UDtJ-.js.br +0 -0
  265. package/dist/client/assets/app-Ch0UDtJ-.js.gz +0 -0
  266. package/dist/client/assets/arc-DIENgCZn.js.br +0 -0
  267. package/dist/client/assets/arc-DIENgCZn.js.gz +0 -0
  268. package/dist/client/assets/architectureDiagram-5GKGNRK7-C1h4e4az.js.br +0 -0
  269. package/dist/client/assets/architectureDiagram-5GKGNRK7-C1h4e4az.js.gz +0 -0
  270. package/dist/client/assets/blockDiagram-I7D4REHJ-B_yOr54K.js.br +0 -0
  271. package/dist/client/assets/blockDiagram-I7D4REHJ-B_yOr54K.js.gz +0 -0
  272. package/dist/client/assets/c4Diagram-7LVT6UL2-DRD4zxIo.js.br +0 -0
  273. package/dist/client/assets/c4Diagram-7LVT6UL2-DRD4zxIo.js.gz +0 -0
  274. package/dist/client/assets/channel-MOXuUKM7.js +0 -1
  275. package/dist/client/assets/chunk-F27PBJKO-CQCL8mJG.js.br +0 -0
  276. package/dist/client/assets/chunk-F27PBJKO-CQCL8mJG.js.gz +0 -0
  277. package/dist/client/assets/chunk-IMKFNOWR-ruHGCADe.js.br +0 -0
  278. package/dist/client/assets/chunk-IMKFNOWR-ruHGCADe.js.gz +0 -0
  279. package/dist/client/assets/chunk-SVP7TREG-DMHj3NSq.js.br +0 -0
  280. package/dist/client/assets/chunk-SVP7TREG-DMHj3NSq.js.gz +0 -0
  281. package/dist/client/assets/chunk-TICWLB2K-Dh_ekvFW.js.br +0 -0
  282. package/dist/client/assets/chunk-TICWLB2K-Dh_ekvFW.js.gz +0 -0
  283. package/dist/client/assets/classDiagram-ZZMXUADV-CEXMdudw.js +0 -1
  284. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CEXMdudw.js +0 -1
  285. package/dist/client/assets/cose-bilkent-JH36ORCC-CsySWhYg.js.br +0 -0
  286. package/dist/client/assets/cose-bilkent-JH36ORCC-CsySWhYg.js.gz +0 -0
  287. package/dist/client/assets/cynefin-OW5HDTMX-9DKE5wV7.js.br +0 -0
  288. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B_8fvr9c.js.br +0 -0
  289. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B_8fvr9c.js.gz +0 -0
  290. package/dist/client/assets/dagre-GXQ25YYZ-BOdsFNFT.js.br +0 -0
  291. package/dist/client/assets/dagre-GXQ25YYZ-BOdsFNFT.js.gz +0 -0
  292. package/dist/client/assets/diagram-S7CK7UJ4-6v_qvAvi.js.br +0 -0
  293. package/dist/client/assets/diagram-S7CK7UJ4-6v_qvAvi.js.gz +0 -0
  294. package/dist/client/assets/diagram-UQ7AKVKN-c03oOGhD.js.br +0 -0
  295. package/dist/client/assets/diagram-UQ7AKVKN-c03oOGhD.js.gz +0 -0
  296. package/dist/client/assets/diagram-VSXAHHWV-MMIhRYp_.js.br +0 -0
  297. package/dist/client/assets/diagram-VSXAHHWV-MMIhRYp_.js.gz +0 -0
  298. package/dist/client/assets/diagram-VX7I27RA-B_l-uebl.js.br +0 -0
  299. package/dist/client/assets/diagram-VX7I27RA-B_l-uebl.js.gz +0 -0
  300. package/dist/client/assets/diagram-Z3DM3KII-DKoBC5gn.js.br +0 -0
  301. package/dist/client/assets/diagram-Z3DM3KII-DKoBC5gn.js.gz +0 -0
  302. package/dist/client/assets/ebnfDiagram-PWID7BFC-Bs3wu8Az.js.br +0 -0
  303. package/dist/client/assets/ebnfDiagram-PWID7BFC-Bs3wu8Az.js.gz +0 -0
  304. package/dist/client/assets/erDiagram-RLTQ6QDP-mYnHYbKl.js.gz +0 -0
  305. package/dist/client/assets/flowDiagram-HODETNUW-CwCLWbib.js.br +0 -0
  306. package/dist/client/assets/flowDiagram-HODETNUW-CwCLWbib.js.gz +0 -0
  307. package/dist/client/assets/ganttDiagram-EL5Y4UJY-mse8c-ni.js.br +0 -0
  308. package/dist/client/assets/ganttDiagram-EL5Y4UJY-mse8c-ni.js.gz +0 -0
  309. package/dist/client/assets/gestureArbiter-BviL29ps.js.br +0 -0
  310. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVUfLjAX.js.br +0 -0
  311. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVUfLjAX.js.gz +0 -0
  312. package/dist/client/assets/index-C6ftfFlG.css.br +0 -0
  313. package/dist/client/assets/index-C6ftfFlG.css.gz +0 -0
  314. package/dist/client/assets/ishikawaDiagram-5VMMS53U-bH299bWL.js.br +0 -0
  315. package/dist/client/assets/ishikawaDiagram-5VMMS53U-bH299bWL.js.gz +0 -0
  316. package/dist/client/assets/journeyDiagram-3NMN7TZE-BG0u_X1A.js.br +0 -0
  317. package/dist/client/assets/journeyDiagram-3NMN7TZE-BG0u_X1A.js.gz +0 -0
  318. package/dist/client/assets/kanban-definition-UXKFOSKX-DRoc9N-i.js.br +0 -0
  319. package/dist/client/assets/kanban-definition-UXKFOSKX-DRoc9N-i.js.gz +0 -0
  320. package/dist/client/assets/layout-mh0t6JpE.js.br +0 -0
  321. package/dist/client/assets/layout-mh0t6JpE.js.gz +0 -0
  322. package/dist/client/assets/linear-B9KTRuUV.js.br +0 -0
  323. package/dist/client/assets/linear-B9KTRuUV.js.gz +0 -0
  324. package/dist/client/assets/mermaid.core-Bu2G9zhK.js.br +0 -0
  325. package/dist/client/assets/mermaid.core-Bu2G9zhK.js.gz +0 -0
  326. package/dist/client/assets/mindmap-definition-YA3MSWOX-Go6Bg5xm.js.br +0 -0
  327. package/dist/client/assets/mindmap-definition-YA3MSWOX-Go6Bg5xm.js.gz +0 -0
  328. package/dist/client/assets/passwordBootstrap-C7-MUAHQ.js.br +0 -0
  329. package/dist/client/assets/passwordBootstrap-C7-MUAHQ.js.gz +0 -0
  330. package/dist/client/assets/pegDiagram-XKGWAZYB-DKUIl_U6.js.br +0 -0
  331. package/dist/client/assets/pegDiagram-XKGWAZYB-DKUIl_U6.js.gz +0 -0
  332. package/dist/client/assets/pieDiagram-E7YTZNPT-aoDNlV7J.js.br +0 -0
  333. package/dist/client/assets/pieDiagram-E7YTZNPT-aoDNlV7J.js.gz +0 -0
  334. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BKuuaF2s.js.br +0 -0
  335. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BKuuaF2s.js.gz +0 -0
  336. package/dist/client/assets/railroadDiagram-O6MQD6OU-BwfCW2bp.js.br +0 -0
  337. package/dist/client/assets/railroadDiagram-O6MQD6OU-BwfCW2bp.js.gz +0 -0
  338. package/dist/client/assets/rdpSession--y6Q98uo.js +0 -1
  339. package/dist/client/assets/rdpSession--y6Q98uo.js.br +0 -0
  340. package/dist/client/assets/rdpSession--y6Q98uo.js.gz +0 -0
  341. package/dist/client/assets/referenceLongPress-BeG1PAdI.js +0 -1
  342. package/dist/client/assets/referenceLongPress-BeG1PAdI.js.br +0 -0
  343. package/dist/client/assets/referenceLongPress-BeG1PAdI.js.gz +0 -0
  344. package/dist/client/assets/requirementDiagram-BXWQKSXE-DbpU9Uig.js.br +0 -0
  345. package/dist/client/assets/requirementDiagram-BXWQKSXE-DbpU9Uig.js.gz +0 -0
  346. package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bxa22XVM.js.br +0 -0
  347. package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bxa22XVM.js.gz +0 -0
  348. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C4GvzT9z.js.br +0 -0
  349. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C4GvzT9z.js.gz +0 -0
  350. package/dist/client/assets/sizeCapture-INFHLROL-B9zJJwQw.js.br +0 -0
  351. package/dist/client/assets/sizeCapture-INFHLROL-B9zJJwQw.js.gz +0 -0
  352. package/dist/client/assets/stateDiagram-D77RDMKH-fqbw6LZa.js.br +0 -0
  353. package/dist/client/assets/stateDiagram-D77RDMKH-fqbw6LZa.js.gz +0 -0
  354. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BO23Pbrn.js +0 -1
  355. package/dist/client/assets/swimlanes-42K2YHIH-Hpc28YZY.js.br +0 -0
  356. package/dist/client/assets/swimlanes-42K2YHIH-Hpc28YZY.js.gz +0 -0
  357. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BLS7y4Di.js +0 -8
  358. package/dist/client/assets/timeline-definition-24CTP7MA-qDieK7Re.js.br +0 -0
  359. package/dist/client/assets/timeline-definition-24CTP7MA-qDieK7Re.js.gz +0 -0
  360. package/dist/client/assets/vennDiagram-4TSXK5OY-BJpZlHao.js.br +0 -0
  361. package/dist/client/assets/vennDiagram-4TSXK5OY-BJpZlHao.js.gz +0 -0
  362. package/dist/client/assets/videoCompression-DI9vP0dm.js.br +0 -0
  363. package/dist/client/assets/videoCompression-DI9vP0dm.js.gz +0 -0
  364. package/dist/client/assets/wardleyDiagram-VM6X3IG4-CQJfj6Ns.js.br +0 -0
  365. package/dist/client/assets/wardleyDiagram-VM6X3IG4-CQJfj6Ns.js.gz +0 -0
  366. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cy-lkWBX.js.br +0 -0
  367. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cy-lkWBX.js.gz +0 -0
@@ -3,8 +3,12 @@ import { randomUUID } from 'node:crypto';
3
3
  export async function executeTaskCommand(command, request, io) {
4
4
  const o = command.options, operation = command.operation;
5
5
  const bodyOptions = ['content', 'file', 'stdin', 'idempotency-key'];
6
+ const originOptions = ['source', 'external-actor', 'external-message-id', 'metadata'];
7
+ if (originOptions.some(key => o[key]) && !o.principal)
8
+ throw new Error('External source metadata requires --principal');
6
9
  const allowed = {
7
10
  list: ['group'], get: [], create: [...bodyOptions, 'group', 'title', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'managed', 'reviewers', 'shared-directory', 'integration', 'work-type'],
11
+ answer: [...bodyOptions, 'decision'], respond: [...bodyOptions, 'attempt', 'to-event', 'evidence'],
8
12
  assign: [...bodyOptions, 'assignee', 'revision'], report: [...bodyOptions, 'attempt', 'status', 'evidence', 'summary'], ask: [...bodyOptions, 'attempt', 'options'],
9
13
  plan: [...bodyOptions, 'attempt', 'evidence'], review: [...bodyOptions, 'artifact', 'evidence', 'verdict'], comment: bodyOptions,
10
14
  'request-review': [...bodyOptions, 'artifact', 'assignee', 'revision'],
@@ -12,7 +16,7 @@ export async function executeTaskCommand(command, request, io) {
12
16
  coordinate: [...bodyOptions, 'revision'], pause: [...bodyOptions, 'revision'], resume: [...bodyOptions, 'revision'], retry: [...bodyOptions, 'revision'],
13
17
  };
14
18
  for (const flag of Object.keys(o))
15
- if (![...allowed[operation], 'session', 'json', 'jsonl', 'text', 'help'].includes(flag))
19
+ if (![...allowed[operation], ...(o.principal ? originOptions : []), 'principal', 'session', 'json', 'jsonl', 'text', 'help'].includes(flag))
16
20
  throw new Error(`--${flag} is not supported by task ${operation}`);
17
21
  if (operation === 'list')
18
22
  return request('GET', `/tasks${o.group ? `?group=${encodeURIComponent(String(o.group))}` : ''}`);
@@ -32,8 +36,12 @@ export async function executeTaskCommand(command, request, io) {
32
36
  throw new Error('Read task get first, then supply --revision to avoid overwriting another decision');
33
37
  if (['assign', 'request-review'].includes(operation) && !o.assignee)
34
38
  throw new Error('This task operation requires --assignee');
35
- if (['report', 'ask', 'plan'].includes(operation) && !o.attempt)
39
+ if (['report', 'respond', 'ask', 'plan'].includes(operation) && !o.attempt)
36
40
  throw new Error('Use --attempt from the dispatch message; never guess the current attempt');
41
+ if (operation === 'respond' && !o['to-event'])
42
+ throw new Error('respond requires --to-event <comment-event-id>');
43
+ if (operation === 'answer' && !o.decision)
44
+ throw new Error('answer requires --decision <question-id>');
37
45
  if (operation === 'create') {
38
46
  if (!o.title || !o.group)
39
47
  throw new Error('task create requires --title and --group');
@@ -47,7 +55,7 @@ export async function executeTaskCommand(command, request, io) {
47
55
  if (options !== undefined && (!Array.isArray(options) || options.some(v => typeof v !== 'string')))
48
56
  throw new Error('--options must be a JSON array of strings');
49
57
  return request('POST', `/tasks/${encodeURIComponent(command.target)}`, { input: {
50
- kind: operation === 'plan' ? 'submit-plan' : operation, idempotencyKey, expectedRevision, content,
58
+ replyToEventId: o['to-event'], decisionId: o.decision, kind: operation === 'plan' ? 'submit-plan' : operation, idempotencyKey, expectedRevision, content,
51
59
  assigneeSessionId: o.assignee, attemptId: o.attempt, status: o.status, artifactId: o.artifact, options, verdict: o.verdict,
52
60
  evidence: o.evidence ? JSON.parse(String(o.evidence)) : undefined, summary: o.summary,
53
61
  } });
@@ -70,7 +70,7 @@ export class CollaborationTaskService {
70
70
  this.group(task.groupId, actor);
71
71
  return this.view(task);
72
72
  }
73
- async create(input, actor) {
73
+ async create(input, actor, origin) {
74
74
  this.group(input.groupId, actor);
75
75
  this.validateMembers(input.groupId, input);
76
76
  const dependencies = [...(input.dependsOn ?? []), ...(input.parentTaskId ? [input.parentTaskId] : [])];
@@ -81,22 +81,26 @@ export class CollaborationTaskService {
81
81
  if (this.tasks.get(dependency)?.ownerServiceId !== authority)
82
82
  throw new CollaborationError('DEPENDENCY_AUTHORITY_REQUIRED', '依赖与父任务需在同一来源服务创建', 409);
83
83
  }
84
+ if (origin && authority !== this.self)
85
+ throw new CollaborationError('INTEGRATION_LOCAL_ONLY', '首版集成任务须由本机服务保存', 409);
84
86
  if (authority !== this.self) {
85
87
  const result = await this.peers.requestTasks(authority, { op: 'create', input, actorSessionId: actor?.sessionId ?? null });
86
88
  const task = result.task;
87
89
  this.tasks.merge(task, authority);
88
90
  return this.view(task);
89
91
  }
90
- const task = this.tasks.create(this.self, input, actor);
92
+ const task = this.tasks.create(this.self, input, actor, origin);
91
93
  void this.flush();
92
94
  return this.view(task);
93
95
  }
94
- async apply(taskId, input, actor) {
96
+ async apply(taskId, input, actor, origin) {
95
97
  const task = this.tasks.get(taskId);
96
98
  if (!task)
97
99
  throw new CollaborationError('TASK_NOT_FOUND', '任务不存在', 404);
98
100
  this.group(task.groupId, actor);
99
101
  this.validateMembers(task.groupId, input);
102
+ if (origin && task.ownerServiceId !== this.self)
103
+ throw new CollaborationError('INTEGRATION_LOCAL_ONLY', '首版集成任务须由本机服务保存', 409);
100
104
  if (task.ownerServiceId !== this.self) {
101
105
  const result = await this.peers.requestTasks(task.ownerServiceId, { op: 'apply', taskId, input, actorSessionId: actor?.sessionId ?? null });
102
106
  this.tasks.merge(result.task, task.ownerServiceId);
@@ -126,7 +130,7 @@ export class CollaborationTaskService {
126
130
  artifactId: child.acceptedArtifactId, integration: child.workflow?.integration === true,
127
131
  commit: child.artifacts.find(a => a.id === child.acceptedArtifactId)?.evidence?.commit })) } };
128
132
  }
129
- const updated = this.tasks.apply(taskId, input, actor);
133
+ const updated = this.tasks.apply(taskId, input, actor, origin);
130
134
  void this.flush();
131
135
  return this.view(updated);
132
136
  }
@@ -196,7 +200,7 @@ export class CollaborationTaskService {
196
200
  const queued = this.messages.send({ groupId: group.id, fromSessionId: null, toSessionIds: [recipient.sessionId],
197
201
  kind: packet.kind === 'task' ? 'task' : 'message', content: String(packet.content ?? ''), threadId: attempt?.threadId ?? task.id,
198
202
  idempotencyKey: `task:${peer.serviceId}:${String(packet.outboxId)}`,
199
- metadata: { taskOutboxId: packet.outboxId, termdockTask: { taskId: task.id, attemptId: attempt?.id ?? null, ownerServiceId: peer.serviceId } } });
203
+ metadata: { taskOutboxId: packet.outboxId, termdockTask: { taskId: task.id, attemptId: attempt?.id ?? null, ownerServiceId: peer.serviceId, replyToEventId: task.events.find(e => e.deliveryId === packet.outboxId && e.attemptId === attempt?.id && e.kind === 'comment')?.id } } });
200
204
  this.deliver(recipient.sessionId);
201
205
  return { receipt: this.receipt(queued[0].id) };
202
206
  }
@@ -313,14 +317,16 @@ export class CollaborationTaskService {
313
317
  if (!task?.workflow)
314
318
  return true;
315
319
  const root = task.workflow.rootTaskId ? this.tasks.get(task.workflow.rootTaskId) : task;
316
- return !task.workflow.paused && !root?.workflow?.paused && root?.status === 'open';
320
+ const consultation = outbox.replyToEventId && task.events.some(e => e.id === outbox.replyToEventId && e.kind === 'comment');
321
+ return consultation || !task.workflow.paused && !root?.workflow?.paused && root?.status === 'open';
317
322
  }).slice(0, 16);
318
323
  for (const outbox of ready) {
319
324
  try {
320
325
  const task = this.tasks.get(outbox.taskId);
321
326
  this.group(task.groupId, outbox.target);
322
327
  const root = task.workflow?.rootTaskId ? this.tasks.get(task.workflow.rootTaskId) : task;
323
- if (task.workflow && (task.workflow.paused || root?.workflow?.paused || root?.status !== 'open'))
328
+ const consultation = outbox.replyToEventId && task.events.some(e => e.id === outbox.replyToEventId && e.kind === 'comment');
329
+ if (task.workflow && !consultation && (task.workflow.paused || root?.workflow?.paused || root?.status !== 'open'))
324
330
  continue;
325
331
  if (outbox.target.serviceId === this.self) {
326
332
  if (outbox.messageId && this.messages.getMessage(outbox.messageId)) {
@@ -330,7 +336,7 @@ export class CollaborationTaskService {
330
336
  }
331
337
  const queued = this.messages.send({ groupId: task.groupId, fromSessionId: null, toSessionIds: [outbox.target.sessionId],
332
338
  kind: outbox.kind, content: outbox.content, threadId: outbox.threadId, idempotencyKey: `task:${this.self}:${outbox.id}`,
333
- metadata: { termdockTask: { taskId: task.id, attemptId: outbox.attemptId, ownerServiceId: this.self } } });
339
+ metadata: { termdockTask: { taskId: task.id, attemptId: outbox.attemptId, ownerServiceId: this.self, replyToEventId: outbox.replyToEventId } } });
334
340
  this.deliver(outbox.target.sessionId);
335
341
  this.tasks.delivered(outbox.id, this.receipt(queued[0].id));
336
342
  }
@@ -83,7 +83,7 @@ function validateSnapshot(task) {
83
83
  invalid();
84
84
  if (event.deliveryId !== undefined && !validId(event.deliveryId))
85
85
  invalid();
86
- if (event.source !== undefined && !['system', 'user'].includes(event.source))
86
+ if (event.source !== undefined && !['system', 'user', 'integration'].includes(event.source))
87
87
  invalid();
88
88
  if (event.actor !== null)
89
89
  member(event.actor);
@@ -115,6 +115,9 @@ function validateSnapshot(task) {
115
115
  /** Atomic document writes keep decisions and their outgoing messages in one transaction. */
116
116
  export class CollaborationTaskStore {
117
117
  file;
118
+ listeners = new Set();
119
+ subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
120
+ snapshot() { return structuredClone(this.document.tasks); }
118
121
  document = { version: 1, tasks: [], outbox: [], requests: {} };
119
122
  constructor(file) {
120
123
  this.file = file;
@@ -129,9 +132,18 @@ export class CollaborationTaskStore {
129
132
  throw error;
130
133
  }
131
134
  }
132
- transaction(mutate) {
135
+ transaction(mutate, origin) {
133
136
  const next = structuredClone(this.document);
134
137
  const result = mutate(next);
138
+ if (origin) {
139
+ const previous = new Set(this.document.tasks.flatMap(t => t.events.map(e => e.id)));
140
+ for (const task of next.tasks)
141
+ for (const event of task.events)
142
+ if (!previous.has(event.id) && event.actor === null && event.source !== 'system') {
143
+ event.source = 'integration';
144
+ event.origin = structuredClone(origin);
145
+ }
146
+ }
135
147
  if (next.tasks.some(task => Buffer.byteLength(JSON.stringify(task)) > 512_000))
136
148
  throw new CollaborationError('TASK_HISTORY_LIMIT', '此任务记录达到 500 KiB,请建立后续任务并保留本任务为历史', 409);
137
149
  const serialized = JSON.stringify(next);
@@ -142,8 +154,18 @@ export class CollaborationTaskStore {
142
154
  writeFileSync(temporary, serialized, { mode: 0o600 });
143
155
  renameSync(temporary, this.file);
144
156
  this.document = next;
157
+ for (const listener of this.listeners) {
158
+ try {
159
+ listener();
160
+ }
161
+ catch { /* Durable sources allow the integration journal to retry. */ }
162
+ }
145
163
  return structuredClone(result);
146
164
  }
165
+ integrationRequestEvent(principalId, key, taskId) {
166
+ const request = this.document.requests[`integration:${principalId}:${key}`];
167
+ return request?.taskId === taskId ? structuredClone(this.document.tasks.find(t => t.id === taskId)?.events.find(e => e.id === request.eventId)) : undefined;
168
+ }
147
169
  get(taskId) { return structuredClone(this.document.tasks.find(t => t.id === taskId) ?? null); }
148
170
  owned(owner) { return structuredClone(this.document.tasks.filter(t => t.ownerServiceId === owner)); }
149
171
  children(taskId) {
@@ -166,7 +188,7 @@ export class CollaborationTaskStore {
166
188
  attempts: current ? [{ ...current, report: current.report ? { ...current.report, summary: reportSummary, content: '', evidence: undefined } : undefined }] : [],
167
189
  artifacts: [plan, result, ...task.artifacts.filter(a => a.kind === 'review' && a.reviewsArtifactId === result?.id)].filter((a) => !!a).map(a => ({ ...a, content: '', summary: (a.summary || (a.kind === 'result' && collaborationResultPresentation(a.content).condensed ? collaborationResultPresentation(a.content).summary : undefined))?.slice(0, 800), evidence: undefined })),
168
190
  decisions: task.decisions.filter(d => d.status === 'pending').map(d => ({ ...d, question: d.question.slice(0, 512), options: [] })),
169
- events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: e.source === 'user' && e.kind === 'revise' ? e.content.slice(0, 512) : '' })) };
191
+ events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: ['user', 'integration'].includes(e.source ?? '') && e.kind === 'revise' ? e.content.slice(0, 512) : '' })) };
170
192
  }));
171
193
  }
172
194
  heads(groupIds, owner) { return this.document.tasks.filter(t => groupIds.includes(t.groupId) && t.ownerServiceId === owner).map(t => ({ id: t.id, revision: t.revision })); }
@@ -179,11 +201,11 @@ export class CollaborationTaskStore {
179
201
  task.events.push(event);
180
202
  return event;
181
203
  }
182
- request(doc, actor, key, payload) {
204
+ request(doc, actor, key, payload, origin) {
183
205
  if (typeof key !== 'string' || !key.trim() || key.length > 256)
184
206
  throw new CollaborationError('INVALID_IDEMPOTENCY_KEY', '需要有效的请求幂等键');
185
- const scoped = `${actor ? taskMemberKey(actor) : 'user'}:${key}`;
186
- const hash = createHash('sha256').update(stable(payload)).digest('hex');
207
+ const scoped = `${origin ? `integration:${origin.integrationId}` : actor ? taskMemberKey(actor) : 'user'}:${key}`;
208
+ const hash = createHash('sha256').update(stable(origin ? [payload, origin] : payload)).digest('hex');
187
209
  const previous = doc.requests[scoped];
188
210
  if (previous && previous.hash !== hash)
189
211
  throw new CollaborationError('IDEMPOTENCY_CONFLICT', '该请求已用于不同内容;请保留原请求或重新开始', 409);
@@ -222,7 +244,7 @@ export class CollaborationTaskStore {
222
244
  doc.outbox.push({ id: id(), taskId: task.id, attemptId: attempt?.id ?? null, target: task.coordinator, kind: 'message', threadId: attempt?.threadId ?? task.id,
223
245
  content: `你被指定为任务「${task.title}」的协调者。\n目标:${task.spec}\n约束:${task.constraints || '未补充'}\n验收标准:${task.acceptance || '由用户决定'}\n任务 ID:${task.id}\n用 td collab task get ${task.id} --text 查看原始记录;用 task list --group ${task.groupId} 查看工作组。需要拆分时用 task create --group ${task.groupId} --parent ${task.id} --title '子任务标题' --content '任务内容'。分派前查询 revision,再用 task assign --revision;把必要的人类决策交给 task ask,不推断成员忙闲,不停止其他成员终端。` });
224
246
  }
225
- create(owner, input, actor) {
247
+ create(owner, input, actor, origin) {
226
248
  const title = text(input.title, '标题', 640, true), spec = text(input.spec, '任务内容', 32_000, true);
227
249
  const constraints = text(input.constraints, '约束'), acceptance = text(input.acceptance, '完成标准');
228
250
  if (input.dependsOn !== undefined && (!Array.isArray(input.dependsOn) || input.dependsOn.length > 32 || input.dependsOn.some(v => typeof v !== 'string')))
@@ -242,7 +264,7 @@ export class CollaborationTaskStore {
242
264
  throw new CollaborationError('INVALID_REVIEWERS', '评审成员列表无效');
243
265
  input.reviewers?.forEach(member);
244
266
  return this.transaction(doc => {
245
- const request = this.request(doc, actor, input.idempotencyKey, input);
267
+ const request = this.request(doc, actor, input.idempotencyKey, input, origin);
246
268
  if (request.previous)
247
269
  return request.previous;
248
270
  if (doc.tasks.length >= 2000)
@@ -285,7 +307,7 @@ export class CollaborationTaskStore {
285
307
  const task = { id: id(), ownerServiceId: owner, groupId: input.groupId, title, spec, constraints, acceptance,
286
308
  coordinator: parent?.workflow ? parent.coordinator : input.coordinator !== undefined ? input.coordinator : input.parentTaskId ? doc.tasks.find(t => t.id === input.parentTaskId)?.coordinator ?? actor : null,
287
309
  parentTaskId: input.parentTaskId ?? null, dependsOn, createdAt: now, updatedAt: now, revision: 1,
288
- status: 'open', activeAttemptId: null, attempts: [], decisions: [], artifacts: [], events: [], deliveries: [], ...(workflow ? { workflow } : {}) };
310
+ ...(origin ? { origin: structuredClone(origin) } : {}), status: 'open', activeAttemptId: null, attempts: [], decisions: [], artifacts: [], events: [], deliveries: [], ...(workflow ? { workflow } : {}) };
289
311
  this.event(task, 'created', actor, spec);
290
312
  if (parent?.constraints)
291
313
  task.constraints = text([parent.constraints, constraints].filter(Boolean).join('\n'), '继承约束');
@@ -307,9 +329,9 @@ export class CollaborationTaskStore {
307
329
  this.touchParent(doc, task, '新子任务已创建');
308
330
  doc.requests[request.key] = { hash: request.hash, taskId: task.id };
309
331
  return task;
310
- });
332
+ }, origin);
311
333
  }
312
- apply(taskId, input, actor) {
334
+ apply(taskId, input, actor, origin) {
313
335
  const content = text(input.content, '内容');
314
336
  const summary = text(input.summary, '结果摘要', 2400);
315
337
  if (summary && (input.kind !== 'report' || input.status !== 'complete'))
@@ -322,23 +344,25 @@ export class CollaborationTaskStore {
322
344
  const task = doc.tasks.find(t => t.id === taskId);
323
345
  if (!task)
324
346
  throw new CollaborationError('TASK_NOT_FOUND', '任务不存在或无权查看', 404);
325
- const request = this.request(doc, actor, input.idempotencyKey, [taskId, input]);
347
+ const request = this.request(doc, actor, input.idempotencyKey, [taskId, input], origin);
326
348
  if (request.previous)
327
349
  return request.previous;
350
+ if (origin && ['accept', 'approve-plan', 'report', 'ask', 'submit-plan', 'review', 'respond'].includes(input.kind))
351
+ throw new CollaborationError('INTEGRATION_OPERATION_DENIED', '此操作需要用户或原执行成员身份', 403);
328
352
  const coordination = actor === null || sameMember(actor, task.coordinator);
329
353
  if (['assign', 'coordinator', 'close', 'reopen', 'revise', 'request-review', 'coordinate', 'pause', 'resume', 'retry'].includes(input.kind) && !coordination)
330
354
  throw new CollaborationError('TASK_PERMISSION_DENIED', '此操作需要用户或当前协调者', 403);
331
355
  if (['answer', 'accept', 'approve-plan'].includes(input.kind) && actor !== null)
332
356
  throw new CollaborationError('USER_DECISION_REQUIRED', '回答、方案确认与验收需要用户操作', 403);
333
- if (!['report', 'ask', 'submit-plan', 'review', 'comment', 'answer'].includes(input.kind) && input.expectedRevision !== task.revision)
357
+ if (!['report', 'ask', 'submit-plan', 'review', 'comment', 'respond', 'answer'].includes(input.kind) && input.expectedRevision !== task.revision)
334
358
  throw new CollaborationError('TASK_CHANGED', '任务已更新,请查看最新记录后重试', 409);
335
359
  const userFollowup = actor === null && input.kind === 'revise' && !!content;
336
- if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind) && !(task.status === 'accepted' && userFollowup))
360
+ if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind) && !(task.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
337
361
  throw new CollaborationError('TASK_ARCHIVED', '任务已归档;需要继续时请重新打开', 409);
338
- if (task.workflow && task.status !== 'open' && input.kind !== 'reopen' && !(task.status === 'accepted' && userFollowup))
362
+ if (task.workflow && task.status !== 'open' && input.kind !== 'reopen' && !(task.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
339
363
  throw new CollaborationError('TASK_ARCHIVED', '自动协作的已完成版本不能追加执行报告,请先重新打开', 409);
340
364
  const root = task.workflow?.rootTaskId ? doc.tasks.find(t => t.id === task.workflow.rootTaskId) : undefined;
341
- if (root && root.status !== 'open' && !(root.status === 'accepted' && userFollowup))
365
+ if (root && root.status !== 'open' && !(root.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
342
366
  throw new CollaborationError('GOAL_ARCHIVED', '请先重新打开所属目标,再继续子任务', 409);
343
367
  if (userFollowup && task.status === 'accepted') {
344
368
  task.status = 'open';
@@ -418,8 +442,26 @@ export class CollaborationTaskStore {
418
442
  const event = this.event(task, 'report', actor, content, attempt.id);
419
443
  event.reportStatus = input.status;
420
444
  event.evidence = input.evidence;
421
- if (input.status === 'complete')
422
- task.artifacts.push({ id: id(), attemptId: attempt.id, kind: 'result', content, ...(summary ? { summary } : {}), evidence: input.evidence, actor: actor, createdAt: event.createdAt });
445
+ if (input.status === 'complete' && task.status === 'accepted') {
446
+ task.status = 'open';
447
+ delete task.acceptedArtifactId;
448
+ delete task.completionMode;
449
+ }
450
+ if (input.status === 'complete') {
451
+ event.artifactId = id();
452
+ task.artifacts.push({ id: event.artifactId, attemptId: attempt.id, kind: 'result', content, ...(summary ? { summary } : {}), evidence: input.evidence, actor: actor, createdAt: event.createdAt });
453
+ }
454
+ }
455
+ else if (input.kind === 'respond') {
456
+ const original = task.events.find(e => e.id === input.replyToEventId && e.kind === 'comment');
457
+ const originalAttempt = original && task.attempts.find(a => a.id === original.attemptId);
458
+ if (!original || !originalAttempt || originalAttempt.id !== input.attemptId || !sameMember(actor, originalAttempt.assignee))
459
+ throw new CollaborationError('RESPONSE_PERMISSION_DENIED', '请由原追问对应的执行成员回复,并指定原事件和分派', 403);
460
+ if (!content)
461
+ throw new CollaborationError('INVALID_RESPONSE', '答复不能为空');
462
+ const response = this.event(task, 'respond', actor, content, originalAttempt.id);
463
+ response.replyToEventId = original.id;
464
+ response.evidence = input.evidence;
423
465
  }
424
466
  else if (input.kind === 'ask') {
425
467
  if (!content || input.options !== undefined && (!Array.isArray(input.options) || input.options.length > 8 || input.options.some(v => typeof v !== 'string' || !v.trim() || v.length > 500)))
@@ -439,7 +481,7 @@ export class CollaborationTaskStore {
439
481
  this.event(task, 'answer', null, content, decision.attemptId);
440
482
  const recipient = task.attempts.find(a => a.id === decision.attemptId);
441
483
  doc.outbox.push({ id: id(), taskId: task.id, attemptId: recipient.id, target: recipient.assignee, kind: 'message', threadId: recipient.threadId,
442
- content: `任务「${task.title}」问题已回答\n问题:${decision.question}\n用户回答:${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}` });
484
+ content: `任务「${task.title}」问题已回答\n问题:${decision.question}\n${origin ? '外部接入回答' : '用户回答'}:${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}` });
443
485
  }
444
486
  else if (input.kind === 'submit-plan' || input.kind === 'review') {
445
487
  if (!content || !actor)
@@ -519,7 +561,7 @@ export class CollaborationTaskStore {
519
561
  event.target = recipient.assignee;
520
562
  event.deliveryId = id();
521
563
  doc.outbox.push({ id: event.deliveryId, taskId: task.id, attemptId: recipient.id, target: recipient.assignee,
522
- kind: 'message', threadId: recipient.threadId, content: `任务「${task.title}」${input.kind === 'revise' ? actor === null ? '用户要求继续跟进' : '修改要求' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}${task.workflow?.kind === 'goal' ? `\n本轮继续沿用此目标和已交付证据,只推进新增要求。\n${this.goalInstructions(task)}` : '\n按这次要求继续当前分派,重新提交 complete 和摘要;新版本会重新独立评审。'}` });
564
+ kind: 'message', threadId: recipient.threadId, replyToEventId: event.id, content: `任务「${task.title}」${input.kind === 'revise' ? actor === null ? origin ? '外部接入要求继续跟进' : '用户要求继续跟进' : '修改要求' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}${input.kind === 'comment' ? `\n这是纯咨询,保留当前结果与验收。请用 td collab --session ${recipient.assignee.sessionId} task respond ${task.id} --attempt ${recipient.id} --to-event ${event.id} --content '答复' 关联回复;只有明确提交新的交付物才用 complete。` : task.workflow?.kind === 'goal' ? `\n本轮继续沿用此目标和已交付证据,只推进新增要求。\n${this.goalInstructions(task)}` : '\n按这次要求继续当前分派,重新提交 complete 和摘要;新版本会重新独立评审。'}` });
523
565
  if (task.workflow?.kind === 'goal' && task.coordination) {
524
566
  task.coordination.notifiedSequence = event.sequence;
525
567
  task.coordination.notifiedAt = Date.now();
@@ -584,21 +626,21 @@ export class CollaborationTaskStore {
584
626
  }
585
627
  else
586
628
  throw new CollaborationError('INVALID_TASK_OPERATION', '不支持的任务操作');
587
- if (['report', 'ask', 'answer', 'submit-plan', 'review', 'accept', 'revise', 'close', 'reopen'].includes(input.kind))
629
+ if (['report', 'respond', 'ask', 'answer', 'submit-plan', 'review', 'accept', 'revise', 'close', 'reopen'].includes(input.kind))
588
630
  this.notifyCoordinator(doc, task, task.events.at(-1));
589
631
  if (task.events.length > 1000 || task.artifacts.length > 200 || task.attempts.length > 100)
590
632
  throw new CollaborationError('TASK_HISTORY_LIMIT', '此任务历史达到上限,请建立后续任务并保留本任务为历史', 409);
591
633
  task.revision++;
592
634
  task.updatedAt = Math.max(Date.now(), task.updatedAt + 1);
593
- if (task.workflow && input.kind !== 'coordinate')
635
+ if (task.workflow && !['coordinate', 'comment', 'respond'].includes(input.kind))
594
636
  this.touchParent(doc, task, `${input.kind}${input.status ? ` / ${input.status}` : ''}:${content.slice(0, 1500)}`);
595
637
  if (task.workflow && ['blocked', 'failed'].includes(input.status ?? ''))
596
638
  task.automationIssue = content.slice(0, 1000);
597
639
  if (task.workflow && input.kind === 'report' && ['ack', 'working', 'complete'].includes(input.status ?? ''))
598
640
  delete task.automationIssue;
599
- doc.requests[request.key] = { hash: request.hash, taskId };
641
+ doc.requests[request.key] = { hash: request.hash, taskId, eventId: task.events.at(-1)?.id };
600
642
  return task;
601
- });
643
+ }, origin);
602
644
  }
603
645
  touchParent(doc, task, content) {
604
646
  const root = task.workflow?.rootTaskId ? doc.tasks.find(t => t.id === task.workflow.rootTaskId) : undefined;
@@ -717,7 +759,7 @@ export class CollaborationTaskStore {
717
759
  this.touchParent(doc, task, '独立评审通过,后续依赖可以接续');
718
760
  }
719
761
  else if (action === 'review-feedback') {
720
- const boundary = task.events.filter(e => e.kind === 'revise' && e.source === 'user' && e.attemptId === task.activeAttemptId).at(-1)?.sequence ?? 0;
762
+ const boundary = task.events.filter(e => e.kind === 'revise' && ['user', 'integration'].includes(e.source ?? '') && e.attemptId === task.activeAttemptId).at(-1)?.sequence ?? 0;
721
763
  const rounds = task.events.filter(e => e.kind === 'revise' && e.actor === null && e.artifactId && e.attemptId === task.activeAttemptId && e.sequence > boundary).length;
722
764
  const blocked = review.verdict === 'blocked' || rounds >= task.workflow.maxRevisions;
723
765
  const event = this.event(task, blocked ? 'review-blocked' : 'revise', null, review.content);
@@ -0,0 +1,227 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import http from 'node:http';
4
+ import { INTEGRATION_PROTOCOL } from './integrationStore.js';
5
+ import { integrationSocketPath } from './integrationServer.js';
6
+ import { CollaborationError } from './collaborationProtocol.js';
7
+ import { executeCollaborationCommand, duration } from './collaborationCli.js';
8
+ import { getTermdockVersion } from '../utils/version.js';
9
+ export const INTEGRATION_HELP = `td integration — local scoped integration administration
10
+ groups | list
11
+ create --file <policy.json> --credential-file <private-output.json>
12
+ revoke <principal-id>
13
+ Credentials are saved as a private file and never printed. Workers set
14
+ TERMDOCK_INTEGRATION_CREDENTIAL_FILE, then use td collab --principal <id> …
15
+ Only the local administrator can provision/revoke grants. See docs/integrations.md.`;
16
+ export function readIntegrationCredential(file, principalId) {
17
+ const info = fs.lstatSync(file);
18
+ if (!info.isFile() || info.size > 4096 || (info.mode & 0o077) || process.getuid && info.uid !== process.getuid())
19
+ throw new CollaborationError('INTEGRATION_CREDENTIAL_FILE_DENIED', 'Credential file must be owned by this user and private (0600)', 403);
20
+ const credential = JSON.parse(fs.readFileSync(file, 'utf8'));
21
+ if (credential.id !== principalId || !credential.token || credential.protocol !== INTEGRATION_PROTOCOL)
22
+ throw new CollaborationError('INTEGRATION_CREDENTIAL_MISMATCH', 'Credential identity/protocol does not match the requested principal', 403);
23
+ return credential;
24
+ }
25
+ function headers(credential, adminToken) {
26
+ return { 'Content-Type': 'application/json', 'x-termdock-integration-protocol': String(INTEGRATION_PROTOCOL),
27
+ 'x-termdock-cli-version': getTermdockVersion(),
28
+ ...(credential ? { 'x-termdock-integration-id': credential.id, authorization: `Bearer ${credential.token}` } : {}),
29
+ ...(adminToken ? { 'x-termdock-local-token': adminToken } : {}) };
30
+ }
31
+ function request(socketPath, method, route, body, requestHeaders, timeout = 30000) {
32
+ return new Promise((resolve, reject) => {
33
+ const req = http.request({ socketPath, method, path: route, headers: requestHeaders }, res => {
34
+ let raw = '';
35
+ res.setEncoding('utf8');
36
+ res.on('data', chunk => { raw += chunk; if (raw.length > 4 * 1024 * 1024)
37
+ req.destroy(new Error('Integration response exceeds limit')); });
38
+ res.once('end', () => resolve({ statusCode: res.statusCode ?? 500, body: raw }));
39
+ res.once('error', reject);
40
+ });
41
+ req.setTimeout(timeout, () => req.destroy(new Error('Integration request timed out')));
42
+ req.once('error', reject);
43
+ req.end(body === undefined ? undefined : JSON.stringify(body));
44
+ });
45
+ }
46
+ function parsed(response) {
47
+ const result = JSON.parse(response.body);
48
+ if (response.statusCode >= 300)
49
+ throw new CollaborationError(result.code ?? 'INTEGRATION_REQUEST_FAILED', result.error ?? 'Integration request failed', response.statusCode);
50
+ return result;
51
+ }
52
+ export async function runIntegrationAdmin(argv, port, adminToken, write) {
53
+ let reserved;
54
+ try {
55
+ const action = argv[0] ?? 'help', options = {};
56
+ if (action === 'help' || action === '--help') {
57
+ write(INTEGRATION_HELP);
58
+ return 0;
59
+ }
60
+ for (let index = 1; index < argv.length; index += 2) {
61
+ const flag = argv[index];
62
+ if (!['--file', '--credential-file'].includes(flag) || !argv[index + 1] || options[flag])
63
+ throw new Error('Invalid integration arguments');
64
+ options[flag] = argv[index + 1];
65
+ }
66
+ const socketPath = integrationSocketPath(port);
67
+ if (action === 'groups' || action === 'list') {
68
+ if (argv.length !== 1)
69
+ throw new Error('Unexpected arguments');
70
+ write(JSON.stringify(parsed(await request(socketPath, 'GET', action === 'groups' ? '/admin/groups' : '/admin/principals', undefined, headers(undefined, adminToken)))));
71
+ return 0;
72
+ }
73
+ if (action === 'create') {
74
+ if (!options['--file'] || !options['--credential-file'] || Object.keys(options).length !== 2)
75
+ throw new Error('create requires --file and --credential-file');
76
+ const policy = JSON.parse(fs.readFileSync(options['--file'], 'utf8'));
77
+ const destination = path.resolve(options['--credential-file']);
78
+ fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
79
+ fs.closeSync(fs.openSync(destination, 'wx', 0o600));
80
+ reserved = destination;
81
+ const credential = parsed(await request(socketPath, 'POST', '/admin/principals', policy, headers(undefined, adminToken)));
82
+ fs.writeFileSync(reserved, JSON.stringify(credential), { mode: 0o600 });
83
+ write(JSON.stringify({ ok: true, id: credential.id, credential_file: reserved }));
84
+ reserved = undefined;
85
+ return 0;
86
+ }
87
+ throw new Error('Unknown integration operation');
88
+ }
89
+ catch (error) {
90
+ if (reserved)
91
+ fs.unlinkSync(reserved);
92
+ write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_ADMIN_ERROR', error: error instanceof CollaborationError ? error.message : 'Integration administration failed; inspect arguments and private file paths' }));
93
+ return 1;
94
+ }
95
+ }
96
+ export async function revokeIntegration(id, port, adminToken, write) {
97
+ try {
98
+ write(JSON.stringify(parsed(await request(integrationSocketPath(port), 'POST', `/admin/principals/${encodeURIComponent(id)}/revoke`, {}, headers(undefined, adminToken)))));
99
+ return 0;
100
+ }
101
+ catch (error) {
102
+ write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_ADMIN_ERROR', error: error instanceof CollaborationError ? error.message : 'Integration revocation failed' }));
103
+ return 1;
104
+ }
105
+ }
106
+ export async function runIntegrationCollab(command, port, io) {
107
+ try {
108
+ const principalId = String(command.options.principal), file = process.env.TERMDOCK_INTEGRATION_CREDENTIAL_FILE;
109
+ if (!file)
110
+ throw new CollaborationError('INTEGRATION_CREDENTIAL_REQUIRED', 'Set TERMDOCK_INTEGRATION_CREDENTIAL_FILE to a private credential file', 401);
111
+ const credential = readIntegrationCredential(file, principalId), socketPath = integrationSocketPath(port);
112
+ const capability = parsed(await request(socketPath, 'GET', '/capabilities', undefined, headers(credential)));
113
+ if (capability.integration_protocol !== INTEGRATION_PROTOCOL)
114
+ throw new CollaborationError('INCOMPATIBLE_INTEGRATION_PROTOCOL', 'CLI/service protocols are incompatible', 409);
115
+ if (command.action === 'capabilities') {
116
+ io.write(JSON.stringify({ ...capability, cli_version: getTermdockVersion() }));
117
+ return 0;
118
+ }
119
+ if (command.action === 'events') {
120
+ const consumer = String(command.options.consumer ?? 'default');
121
+ if (command.operation === 'ack') {
122
+ io.write(JSON.stringify(parsed(await request(socketPath, 'POST', '/events/ack', { consumer, cursor: command.target }, headers(credential)))));
123
+ return 0;
124
+ }
125
+ const params = new URLSearchParams({ consumer });
126
+ if (command.options.group)
127
+ params.set('group', String(command.options.group));
128
+ return subscribe(socketPath, `/events?${params}`, credential, command.options.timeout ? duration(String(command.options.timeout)) : undefined, io.write);
129
+ }
130
+ if (command.action === 'session') {
131
+ const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${command.operation === 'restore' ? '/restore' : ''}`;
132
+ const body = command.operation === 'create' ? { group_id: command.options.group, launch_profile: command.options['launch-profile'], cwd: command.options.cwd, idempotency_key: command.options['idempotency-key'] }
133
+ : command.operation === 'restore' ? { idempotency_key: command.options['idempotency-key'] } : undefined;
134
+ io.write(JSON.stringify(parsed(await request(socketPath, command.operation === 'get' ? 'GET' : 'POST', route, body, headers(credential)))));
135
+ return 0;
136
+ }
137
+ if (!['task', 'message', 'help'].includes(command.action) || command.action === 'message' && command.operation !== 'get')
138
+ throw new CollaborationError('INTEGRATION_OPERATION_DENIED', 'Command is not exposed to integration identities', 403);
139
+ return executeCollaborationCommand(command, {}, { ...io, request: async (method, endpoint, body, timeout) => {
140
+ const origin = { source: command.options.source, externalActor: command.options['external-actor'] ? JSON.parse(String(command.options['external-actor'])) : undefined,
141
+ externalMessageId: command.options['external-message-id'], metadata: command.options.metadata ? JSON.parse(String(command.options.metadata)) : undefined };
142
+ return request(socketPath, method, endpoint.replace('/api/terminal/operations/orchestration', ''), body === undefined ? undefined : { ...body, origin }, headers(credential), timeout);
143
+ } });
144
+ }
145
+ catch (error) {
146
+ io.write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_CONNECTION_FAILED', error: error instanceof CollaborationError ? error.message : 'Local integration connection failed; credentials were not sent to a public service' }));
147
+ return 1;
148
+ }
149
+ }
150
+ async function subscribe(socketPath, route, credential, timeout, write) {
151
+ const deadline = timeout ? Date.now() + timeout : Infinity;
152
+ let backoff = 1000;
153
+ while (Date.now() < deadline) {
154
+ const outcome = await new Promise(resolve => {
155
+ let timer;
156
+ const req = http.request({ socketPath, method: 'GET', path: route, headers: headers(credential) }, res => {
157
+ res.setEncoding('utf8');
158
+ let pending = '', ended = false;
159
+ res.on('data', chunk => {
160
+ pending += chunk;
161
+ if (pending.length > 2 * 1024 * 1024) {
162
+ ended = true;
163
+ req.destroy();
164
+ resolve('stop');
165
+ return;
166
+ }
167
+ for (;;) {
168
+ const newline = pending.indexOf('\n');
169
+ if (newline < 0)
170
+ break;
171
+ const line = pending.slice(0, newline);
172
+ pending = pending.slice(newline + 1);
173
+ if (!line.trim())
174
+ continue;
175
+ let event;
176
+ try {
177
+ event = JSON.parse(line);
178
+ }
179
+ catch {
180
+ write(JSON.stringify({ type: 'error', code: 'INVALID_EVENT_RESPONSE', retryable: false }));
181
+ ended = true;
182
+ req.destroy();
183
+ resolve('stop');
184
+ return;
185
+ }
186
+ write(line);
187
+ if (event.type === 'error' && !event.retryable) {
188
+ ended = true;
189
+ req.destroy();
190
+ resolve('stop');
191
+ }
192
+ }
193
+ });
194
+ res.once('end', () => {
195
+ if ((res.statusCode ?? 500) >= 300 && pending) {
196
+ try {
197
+ const error = JSON.parse(pending);
198
+ write(JSON.stringify(error));
199
+ }
200
+ catch {
201
+ write(JSON.stringify({ type: 'error', code: 'INVALID_EVENT_RESPONSE', retryable: false }));
202
+ }
203
+ resolve((res.statusCode ?? 500) >= 500 ? 'retry' : 'stop');
204
+ }
205
+ else if (!ended)
206
+ resolve('retry');
207
+ });
208
+ res.once('close', () => { if (!ended)
209
+ resolve('retry'); });
210
+ });
211
+ req.once('error', () => resolve('retry'));
212
+ req.once('close', () => { if (timer)
213
+ clearTimeout(timer); });
214
+ if (Number.isFinite(deadline))
215
+ timer = setTimeout(() => { resolve('timeout'); req.destroy(); }, Math.max(1, deadline - Date.now()));
216
+ req.end();
217
+ });
218
+ if (outcome === 'stop')
219
+ return 1;
220
+ if (outcome === 'timeout')
221
+ return 0;
222
+ write(JSON.stringify({ type: 'connection', state: 'reconnecting', retry_in_ms: backoff }));
223
+ await new Promise(resolve => setTimeout(resolve, Math.min(backoff, Math.max(0, deadline - Date.now()))));
224
+ backoff = Math.min(backoff * 2, 30000);
225
+ }
226
+ return 0;
227
+ }