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
@@ -0,0 +1,326 @@
1
+ import express from 'express';
2
+ import { createServer } from 'node:http';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ import { timingSafeEqual } from 'node:crypto';
7
+ import { CollaborationError } from './collaborationProtocol.js';
8
+ import { terminalMessage } from './collaborationStore.js';
9
+ import { IntegrationStore, INTEGRATION_PROTOCOL, integrationError } from './integrationStore.js';
10
+ import { IntegrationSessions, publicIntegrationSession } from './integrationSessions.js';
11
+ import { getTermdockVersion } from '../utils/version.js';
12
+ export function integrationSocketPath(port) { return path.join(homedir(), '.termdock', `integration-${port}.sock`); }
13
+ /** A local authenticated API and push stream. It is never mounted on the
14
+ * public HTTP application and does not reuse an integration's admin token. */
15
+ export class IntegrationRuntime {
16
+ options;
17
+ store;
18
+ sessions;
19
+ server;
20
+ stops = [];
21
+ streams = new Set();
22
+ sent = new Map();
23
+ syncing = false;
24
+ retry;
25
+ constructor(options) {
26
+ this.options = options;
27
+ this.store = new IntegrationStore(path.join(options.directory, 'integrations.json'));
28
+ this.sessions = new IntegrationSessions(path.join(options.directory, 'integration-sessions.json'), options.sessions);
29
+ const changed = () => { try {
30
+ this.sync();
31
+ }
32
+ catch (error) {
33
+ this.failStreams(error);
34
+ this.scheduleRetry();
35
+ } };
36
+ this.stops.push(options.messages.subscribe(changed), options.taskStore.subscribe(changed), this.sessions.subscribe(changed));
37
+ const app = express();
38
+ app.use(express.json({ limit: '1mb' }));
39
+ app.use((req, res, next) => {
40
+ if (req.headers['x-termdock-integration-protocol'] !== String(INTEGRATION_PROTOCOL)) {
41
+ res.status(409).json({ ...this.error(new CollaborationError('INCOMPATIBLE_INTEGRATION_PROTOCOL', 'Unsupported integration protocol', 409)), protocol: INTEGRATION_PROTOCOL });
42
+ return;
43
+ }
44
+ next();
45
+ });
46
+ const run = (handler) => async (req, res) => {
47
+ try {
48
+ const value = await handler(req, res);
49
+ if (!res.headersSent)
50
+ res.json(value);
51
+ }
52
+ catch (error) {
53
+ res.status(error instanceof CollaborationError ? error.httpStatus : 500).json(this.error(error));
54
+ }
55
+ };
56
+ const principal = (req) => this.store.authenticate(String(req.headers['x-termdock-integration-id'] ?? ''), String(req.headers.authorization ?? '').replace(/^Bearer /, ''));
57
+ const admin = (req) => {
58
+ const given = Buffer.from(String(req.headers['x-termdock-local-token'] ?? '')), expected = Buffer.from(options.adminToken ?? '');
59
+ if (!expected.length || given.length !== expected.length || !timingSafeEqual(given, expected))
60
+ integrationError('INTEGRATION_ADMIN_REQUIRED', 'Local administration credential is required', 401);
61
+ };
62
+ app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.list().filter(g => !g.federated).map(g => ({ id: g.id, name: g.name })) }; }));
63
+ app.get('/admin/principals', run(req => { admin(req); return { integrations: this.store.policies() }; }));
64
+ app.post('/admin/principals', run(req => { admin(req); this.group(req.body?.groupId); const provisioned = this.store.provision(req.body); try {
65
+ this.sync();
66
+ }
67
+ catch {
68
+ this.scheduleRetry();
69
+ } return { id: provisioned.principal.id, token: provisioned.token, protocol: INTEGRATION_PROTOCOL }; }));
70
+ app.post('/admin/principals/:id/revoke', run(req => { admin(req); this.store.revoke(req.params.id); return { ok: true }; }));
71
+ app.get('/capabilities', run(req => { const p = principal(req); return { ...this.capabilities(), principal: { id: p.id, group_id: p.groupId, permissions: p.permissions, launch_profiles: p.launchProfiles.map(profile => ({ id: profile.id, agent_slug: profile.agentSlug, cwd_roots: profile.cwdRoots })) } }; }));
72
+ app.get('/tasks', run(req => { const p = principal(req); this.authorize(p, 'task.read', req.query.group); return { tasks: options.tasks.list(p.groupId, null) }; }));
73
+ app.get('/tasks/:id', run(async (req) => { const p = principal(req); this.taskPermission(p, req.params.id, 'task.read'); return { task: await options.tasks.get(req.params.id, null) }; }));
74
+ app.post('/tasks', run(async (req) => {
75
+ const p = principal(req), raw = req.body?.input ?? {};
76
+ this.authorize(p, 'task.create', raw.groupId);
77
+ if (raw.managed || raw.parentTaskId || raw.dependsOn?.length || raw.coordinatorSessionId || raw.reviewerSessionIds?.length || raw.integration)
78
+ integrationError('INTEGRATION_OPERATION_DENIED', 'Integration creates ordinary standalone tasks only', 403);
79
+ const input = { idempotencyKey: raw.idempotencyKey, groupId: p.groupId, title: raw.title, spec: raw.spec,
80
+ constraints: raw.constraints, acceptance: raw.acceptance, assignee: raw.assigneeSessionId ? options.peers.taskMember(String(raw.assigneeSessionId)) : undefined };
81
+ if (input.assignee)
82
+ this.authorize(p, 'task.assign');
83
+ const task = await options.tasks.create(input, null, this.origin(p, req.body?.origin));
84
+ this.sync();
85
+ return { task };
86
+ }));
87
+ app.post('/tasks/:id', run(async (req) => {
88
+ const p = principal(req), raw = req.body?.input ?? {};
89
+ const permissions = { assign: 'task.assign', comment: 'task.comment', revise: 'task.revise', answer: 'task.answer' };
90
+ const permission = permissions[raw.kind];
91
+ if (!permission)
92
+ integrationError('INTEGRATION_OPERATION_DENIED', 'Operation requires a user or terminal member', 403);
93
+ this.taskPermission(p, req.params.id, permission);
94
+ const input = { kind: raw.kind, idempotencyKey: raw.idempotencyKey, content: raw.content,
95
+ expectedRevision: raw.expectedRevision, decisionId: raw.decisionId,
96
+ assignee: raw.assigneeSessionId ? options.peers.taskMember(String(raw.assigneeSessionId)) : undefined };
97
+ const task = await options.tasks.apply(req.params.id, input, null, this.origin(p, req.body?.origin));
98
+ this.sync();
99
+ const event = options.taskStore.integrationRequestEvent(p.id, input.idempotencyKey, task.id);
100
+ return { task, event_id: event?.id ?? null, attempt_id: event?.attemptId ?? null, delivery_id: event?.deliveryId ?? null };
101
+ }));
102
+ app.get('/message/:id', run(req => {
103
+ const p = principal(req), message = options.messages.getMessage(req.params.id);
104
+ if (!message)
105
+ integrationError('MESSAGE_NOT_FOUND', 'Message does not exist', 404);
106
+ this.authorize(p, 'task.read', message.groupId);
107
+ const receipt = options.messages.receipt(message.id);
108
+ const { snapshot: _, ...diagnostic } = receipt;
109
+ return { ok: true, ...diagnostic, ...(req.query.receipt_only === 'true' ? {} : { message: terminalMessage(message) }) };
110
+ }));
111
+ app.post('/sessions', run(async (req) => {
112
+ const p = principal(req);
113
+ this.authorize(p, 'session.create', req.body?.group_id);
114
+ const session = await this.sessions.create(p, { profile: req.body?.launch_profile, cwd: req.body?.cwd, idempotencyKey: req.body?.idempotency_key });
115
+ this.sync();
116
+ return { session: publicIntegrationSession(session) };
117
+ }));
118
+ app.get('/sessions/:id', run(async (req) => { const p = principal(req); this.authorize(p, 'session.read'); return { session: publicIntegrationSession(await this.sessions.get(p, req.params.id)) }; }));
119
+ app.post('/sessions/:id/restore', run(async (req) => { const p = principal(req); this.authorize(p, 'session.restore'); const session = await this.sessions.restore(p, req.params.id, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
120
+ app.get('/events', run((req, res) => {
121
+ const p = principal(req);
122
+ this.authorize(p, 'events.read', req.query.group);
123
+ this.sync();
124
+ this.stream(p, String(req.query.consumer ?? 'default'), req, res);
125
+ }));
126
+ app.post('/events/ack', run(req => {
127
+ const p = principal(req);
128
+ this.authorize(p, 'events.read');
129
+ const cursor = String(req.body?.cursor ?? ''), consumer = String(req.body?.consumer ?? 'default');
130
+ const sequence = this.store.cursorSequence(p.groupId, cursor);
131
+ if (sequence > (this.sent.get(`${p.id}:${p.groupId}:${consumer}`) ?? -1))
132
+ integrationError('EVENT_ACK_NOT_SENT', 'Subscribe and durably receive the continuous prefix before acknowledging', 409);
133
+ return this.store.ack(p, consumer, cursor);
134
+ }));
135
+ app.use((_error, _req, res, _next) => { res.status(400).json(this.error(new CollaborationError('INVALID_REQUEST', 'Invalid integration request', 400))); });
136
+ this.server = createServer(app);
137
+ }
138
+ capabilities() {
139
+ return { integration_protocol: INTEGRATION_PROTOCOL, server_version: getTermdockVersion(), local_only: true,
140
+ integration_principal: true, group_scoped_permissions: true, durable_event_push: true,
141
+ event_ack_semantics: 'durable_received', correlated_task_responses: true, accepted_task_comments: true,
142
+ idempotent_session_create: true, exact_session_restore: true, external_validation: false,
143
+ permissions: ['task.read', 'task.create', 'task.assign', 'task.comment', 'task.revise', 'task.answer', 'events.read', 'session.create', 'session.read', 'session.restore'] };
144
+ }
145
+ error(error) {
146
+ return { type: 'error', ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_INTERNAL_ERROR',
147
+ error: error instanceof CollaborationError ? error.message : 'Integration operation failed', retryable: !(error instanceof CollaborationError) || error.httpStatus >= 500 };
148
+ }
149
+ group(id) {
150
+ const group = typeof id === 'string' ? this.options.messages.getGroup(id) : null;
151
+ if (!group || group.deleted)
152
+ integrationError('TASK_GROUP_NOT_FOUND', 'A live group is required', 404);
153
+ if (group.federated)
154
+ integrationError('INTEGRATION_LOCAL_ONLY', 'First integration protocol uses a local collaboration group', 409);
155
+ return group;
156
+ }
157
+ authorize(p, operation, group) { this.store.authorize(p, operation, group === undefined ? p.groupId : String(group)); this.group(p.groupId); }
158
+ taskPermission(p, id, operation) {
159
+ const task = this.options.taskStore.get(id);
160
+ if (!task || task.groupId !== p.groupId)
161
+ integrationError('TASK_NOT_FOUND', 'Task does not exist within this integration grant', 404);
162
+ this.authorize(p, operation, task.groupId);
163
+ }
164
+ origin(p, input) {
165
+ const value = input && typeof input === 'object' ? input : {};
166
+ if (Buffer.byteLength(JSON.stringify(value)) > 16384)
167
+ integrationError('INVALID_SOURCE_METADATA', 'Source metadata exceeds 16 KiB');
168
+ if (value.externalActor !== undefined && (!value.externalActor || typeof value.externalActor !== 'object' || Array.isArray(value.externalActor)))
169
+ integrationError('INVALID_SOURCE_METADATA', 'externalActor must be an object');
170
+ if (value.metadata !== undefined && (!value.metadata || typeof value.metadata !== 'object' || Array.isArray(value.metadata)))
171
+ integrationError('INVALID_SOURCE_METADATA', 'metadata must be an object');
172
+ return { integrationId: p.id, source: typeof value.source === 'string' ? value.source.slice(0, 128) : 'integration',
173
+ ...(value.externalActor ? { externalActor: value.externalActor } : {}),
174
+ ...(typeof value.externalMessageId === 'string' ? { externalMessageId: value.externalMessageId.slice(0, 512) } : {}),
175
+ ...(value.metadata ? { metadata: value.metadata } : {}) };
176
+ }
177
+ sync() {
178
+ if (this.syncing)
179
+ return;
180
+ this.syncing = true;
181
+ try {
182
+ const groups = new Set(this.store.policies().filter(p => !p.revoked).map(p => p.groupId)), updates = [];
183
+ if (!groups.size)
184
+ return;
185
+ for (const task of this.options.taskStore.snapshot())
186
+ if (groups.has(task.groupId)) {
187
+ for (const event of task.events)
188
+ updates.push({ sourceKey: `task:${task.id}:${event.id}`, sourceVersion: '1', eventId: event.id, event: {
189
+ group_id: task.groupId, kind: `task.${event.kind}`, created_at: event.createdAt, task_id: task.id, attempt_id: event.attemptId,
190
+ artifact_id: event.artifactId ?? null, reply_to_event_id: event.replyToEventId ?? null,
191
+ source: event.source ?? (event.actor ? 'member' : 'user'), actor: event.actor, external_actor: event.origin?.externalActor ?? null, payload: { event }
192
+ } });
193
+ for (const artifact of task.artifacts)
194
+ if (artifact.kind === 'result')
195
+ updates.push({ sourceKey: `artifact:${artifact.id}`, sourceVersion: '1', event: {
196
+ group_id: task.groupId, kind: 'task.result', created_at: artifact.createdAt, task_id: task.id, attempt_id: artifact.attemptId,
197
+ artifact_id: artifact.id, source: 'member', actor: artifact.actor, payload: { artifact }
198
+ } });
199
+ for (const delivery of task.deliveries)
200
+ updates.push({ sourceKey: `task-delivery:${delivery.id}`, sourceVersion: JSON.stringify(delivery), event: {
201
+ group_id: task.groupId, kind: 'task.delivery', created_at: delivery.deliveredAt ?? task.updatedAt,
202
+ task_id: task.id, attempt_id: delivery.attemptId, message_id: delivery.messageId, source: 'system', payload: { delivery }
203
+ } });
204
+ }
205
+ for (const message of this.options.messages.snapshotMessages())
206
+ if (groups.has(message.groupId)) {
207
+ const link = message.metadata?.termdockTask;
208
+ const context = { group_id: message.groupId, task_id: link?.taskId ?? null, attempt_id: link?.attemptId ?? null,
209
+ reply_to_event_id: link?.replyToEventId ?? null, message_id: message.id, source: message.fromSessionId ? 'member' : 'system' };
210
+ updates.push({ sourceKey: `message:${message.id}`, sourceVersion: '1', event: { ...context,
211
+ kind: message.kind === 'reply' ? 'message.reply' : 'message.queued', created_at: message.createdAt,
212
+ payload: { message_id: message.id, thread_id: message.threadId, reply_to: message.replyTo ?? null,
213
+ from_session_id: message.fromSessionId, to_session_id: message.toSessionId, content: message.content, metadata: message.metadata ?? null } } });
214
+ const { snapshot: _, ...receipt } = this.options.messages.receipt(message.id);
215
+ updates.push({ sourceKey: `message-status:${message.id}`, sourceVersion: JSON.stringify(receipt), event: {
216
+ ...context, kind: 'message.delivery', created_at: message.deliveredAt ?? message.createdAt, payload: { receipt }
217
+ } });
218
+ }
219
+ for (const session of this.sessions.snapshot())
220
+ if (groups.has(session.group_id))
221
+ updates.push({ sourceKey: `session:${session.session_id}`, sourceVersion: JSON.stringify(publicIntegrationSession(session)), event: {
222
+ group_id: session.group_id, kind: `session.${session.state}`, created_at: session.updated_at, source: 'system', actor: { integration_id: session.principal_id }, payload: { session: publicIntegrationSession(session) }
223
+ } });
224
+ updates.sort((a, b) => a.event.created_at - b.event.created_at);
225
+ this.store.append(updates);
226
+ }
227
+ finally {
228
+ this.syncing = false;
229
+ }
230
+ }
231
+ failStreams(error) {
232
+ for (const stream of this.streams)
233
+ stream.end(`${JSON.stringify(this.error(error))}\n`);
234
+ }
235
+ scheduleRetry() {
236
+ if (this.retry)
237
+ return;
238
+ this.retry = setTimeout(() => { this.retry = undefined; try {
239
+ this.sync();
240
+ }
241
+ catch {
242
+ this.scheduleRetry();
243
+ } }, 1000);
244
+ this.retry.unref();
245
+ }
246
+ stream(p, consumer, req, res) {
247
+ let cursor, pumping = false, closed = false;
248
+ const key = `${p.id}:${p.groupId}:${consumer}`;
249
+ // Resolve the initial cursor before sending headers so retention gaps
250
+ // and bad consumer names are ordinary machine-readable HTTP errors.
251
+ const initial = this.store.page(p, consumer);
252
+ cursor = initial.cursor;
253
+ res.setHeader('Content-Type', 'application/x-ndjson');
254
+ res.setHeader('Cache-Control', 'no-store');
255
+ res.flushHeaders();
256
+ this.streams.add(res);
257
+ const stop = this.store.subscribe(() => pump());
258
+ const cleanup = () => { closed = true; stop(); clearInterval(heartbeat); this.streams.delete(res); };
259
+ const pump = () => {
260
+ if (pumping || closed)
261
+ return;
262
+ pumping = true;
263
+ try {
264
+ this.authorize(p, 'events.read');
265
+ for (;;) {
266
+ const page = this.store.page(p, consumer, cursor);
267
+ if (!page.events.length)
268
+ break;
269
+ for (const event of page.events) {
270
+ if (res.writableLength > 1024 * 1024)
271
+ integrationError('EVENT_CONSUMER_SLOW', 'Reconnect from the last acknowledged cursor', 503);
272
+ res.write(`${JSON.stringify({ type: 'event', ...event })}\n`);
273
+ cursor = event.cursor;
274
+ this.sent.set(key, Math.max(this.sent.get(key) ?? 0, event.sequence));
275
+ }
276
+ }
277
+ }
278
+ catch (error) {
279
+ res.end(`${JSON.stringify(this.error(error))}\n`);
280
+ cleanup();
281
+ }
282
+ finally {
283
+ pumping = false;
284
+ }
285
+ };
286
+ const heartbeat = setInterval(() => { pump(); if (!closed)
287
+ res.write(`${JSON.stringify({ type: 'heartbeat', ack_semantics: 'durable_received' })}\n`); }, 15000);
288
+ heartbeat.unref();
289
+ res.once('close', cleanup);
290
+ req.once('aborted', cleanup);
291
+ pump();
292
+ }
293
+ async listen() {
294
+ fs.mkdirSync(path.dirname(this.options.socketPath), { recursive: true, mode: 0o700 });
295
+ // A stale socket may be removed only after proving no listener exists.
296
+ if (fs.existsSync(this.options.socketPath)) {
297
+ const { createConnection } = await import('node:net');
298
+ const active = await new Promise((resolve, reject) => {
299
+ const socket = createConnection(this.options.socketPath);
300
+ socket.once('connect', () => { socket.destroy(); resolve(true); });
301
+ socket.once('error', (error) => { socket.destroy(); if (['ECONNREFUSED', 'ENOENT'].includes(error.code ?? ''))
302
+ resolve(false);
303
+ else
304
+ reject(error); });
305
+ });
306
+ if (active)
307
+ integrationError('INTEGRATION_SOCKET_IN_USE', 'Another integration service owns this socket', 409);
308
+ if (!fs.lstatSync(this.options.socketPath).isSocket())
309
+ integrationError('INTEGRATION_SOCKET_INVALID', 'Refusing to replace a non-socket file');
310
+ fs.unlinkSync(this.options.socketPath);
311
+ }
312
+ await new Promise((resolve, reject) => { this.server.once('error', reject); this.server.listen(this.options.socketPath, () => { this.server.removeListener('error', reject); fs.chmodSync(this.options.socketPath, 0o600); resolve(); }); });
313
+ this.sync();
314
+ await this.sessions.refreshAll();
315
+ }
316
+ async close() {
317
+ for (const stop of this.stops)
318
+ stop();
319
+ if (this.retry)
320
+ clearTimeout(this.retry);
321
+ this.sessions.close();
322
+ for (const stream of this.streams)
323
+ stream.destroy();
324
+ await new Promise(resolve => this.server.close(() => resolve()));
325
+ }
326
+ }
@@ -0,0 +1,296 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
4
+ import { atomicJson, integrationError } from './integrationStore.js';
5
+ /** Quote each configured argument as literal shell data, including paths. */
6
+ export function integrationLaunchCommand(record, restore) {
7
+ const launchArgs = record.profile.argv.map(arg => arg === '{cwd}' ? record.cwd : arg);
8
+ const args = restore ? record.profile.resumeArgv.flatMap(arg => arg === '{launchArgs}' ? launchArgs
9
+ : [arg === '{sessionId}' ? record.agent_native_session_id : arg === '{cwd}' ? record.cwd : arg]) : launchArgs;
10
+ const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
11
+ return `cd -- ${quote(record.cwd)} && ${[record.profile.executable, ...args].map(quote).join(' ')}`;
12
+ }
13
+ export function publicIntegrationSession(record) {
14
+ const { profile: _, deadline: _deadline, launch_submitted: _submitted, ...publicRecord } = record;
15
+ return publicRecord;
16
+ }
17
+ export function permittedCwd(profile, cwd) {
18
+ if (typeof cwd !== 'string' || !path.isAbsolute(cwd) || /[\x00-\x1f\x7f]/.test(cwd))
19
+ integrationError('SESSION_CWD_DENIED', 'cwd must be absolute', 403);
20
+ let real;
21
+ try {
22
+ real = fs.realpathSync(cwd);
23
+ if (/[\x00-\x1f\x7f]/.test(real) || !fs.statSync(real).isDirectory())
24
+ throw new Error();
25
+ }
26
+ catch {
27
+ return integrationError('SESSION_CWD_MISSING', 'Task working directory must exist');
28
+ }
29
+ if (!profile.cwdRoots.some(root => { try {
30
+ const relative = path.relative(fs.realpathSync(root), real);
31
+ return !relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative);
32
+ }
33
+ catch {
34
+ return false;
35
+ } }))
36
+ integrationError('SESSION_CWD_DENIED', 'Working directory is outside the configured roots', 403);
37
+ return real;
38
+ }
39
+ /** Creation intent and exact launch configuration are durable before any input
40
+ * enters a PTY. An ambiguous crash never starts another Agent automatically. */
41
+ export class IntegrationSessions {
42
+ file;
43
+ adapter;
44
+ bindingTimeout;
45
+ doc = { version: 1, sessions: [], requests: {} };
46
+ pending = new Map();
47
+ listeners = new Set();
48
+ restoring = new Set();
49
+ timers = new Set();
50
+ stopObservation;
51
+ observing = false;
52
+ observeAgain = false;
53
+ constructor(file, adapter, bindingTimeout = 120000) {
54
+ this.file = file;
55
+ this.adapter = adapter;
56
+ this.bindingTimeout = bindingTimeout;
57
+ try {
58
+ const doc = JSON.parse(fs.readFileSync(file, 'utf8'));
59
+ if (doc.version !== 1 || !Array.isArray(doc.sessions) || !doc.requests)
60
+ throw new Error('Invalid integration session store');
61
+ this.doc = doc;
62
+ }
63
+ catch (error) {
64
+ if (error.code !== 'ENOENT')
65
+ throw error;
66
+ }
67
+ for (const record of this.doc.sessions)
68
+ if (['starting', 'restoring', 'binding_pending'].includes(record.state))
69
+ this.scheduleRefresh(record);
70
+ this.stopObservation = adapter.subscribe(() => { void this.refreshAll().catch(() => { }); });
71
+ }
72
+ scheduleRefresh(record) {
73
+ const timer = setTimeout(() => { this.timers.delete(timer); void this.refresh(record.session_id).catch(() => { }); }, Math.max(1, record.deadline - Date.now() + 5));
74
+ timer.unref();
75
+ this.timers.add(timer);
76
+ }
77
+ subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
78
+ snapshot() { return structuredClone(this.doc.sessions); }
79
+ save() { atomicJson(this.file, this.doc); for (const listener of this.listeners) {
80
+ try {
81
+ listener();
82
+ }
83
+ catch { /* Durable session state is reconciled on restart. */ }
84
+ } }
85
+ saveRecord(record) {
86
+ const before = this.doc.sessions;
87
+ this.doc.sessions = [...before.filter(s => s.session_id !== record.session_id), structuredClone(record)];
88
+ try {
89
+ this.save();
90
+ }
91
+ catch (error) {
92
+ this.doc.sessions = before;
93
+ throw error;
94
+ }
95
+ }
96
+ lookup(principal, id) {
97
+ const record = this.doc.sessions.find(s => s.session_id === id && s.group_id === principal.groupId && s.principal_id === principal.id);
98
+ if (!record)
99
+ integrationError('INTEGRATION_SESSION_NOT_FOUND', 'Session is not owned by this integration', 404);
100
+ return structuredClone(record);
101
+ }
102
+ request(principal, key, input) {
103
+ if (!key || typeof key !== 'string' || key.length > 256)
104
+ integrationError('INVALID_IDEMPOTENCY_KEY', 'An idempotency key is required');
105
+ const scoped = `${principal.id}:${key}`, hash = createHash('sha256').update(JSON.stringify(input)).digest('hex');
106
+ const previous = this.doc.requests[scoped];
107
+ if (previous && previous.hash !== hash)
108
+ integrationError('IDEMPOTENCY_CONFLICT', 'Key was already used for different session parameters', 409);
109
+ return { scoped, hash, previous };
110
+ }
111
+ async create(principal, input) {
112
+ const request = this.request(principal, input.idempotencyKey, ['create', input.profile, input.cwd]);
113
+ if (request.previous)
114
+ return this.pending.get(request.previous.sessionId) ?? this.get(principal, request.previous.sessionId);
115
+ const profile = principal.launchProfiles.find(p => p.id === input.profile);
116
+ if (!profile)
117
+ integrationError('LAUNCH_PROFILE_DENIED', 'Profile is not allowed by this integration', 403);
118
+ const cwd = permittedCwd(profile, input.cwd);
119
+ try {
120
+ fs.accessSync(profile.executable, fs.constants.X_OK);
121
+ }
122
+ catch {
123
+ return integrationError('LAUNCHER_UNAVAILABLE', 'Configured executable is not available');
124
+ }
125
+ if (this.doc.sessions.length >= 2000)
126
+ integrationError('INTEGRATION_SESSION_LIMIT', 'Integration session storage is full', 409);
127
+ const now = Date.now(), record = { operation_id: randomUUID(), session_id: randomBytes(6).toString('hex'),
128
+ group_id: principal.groupId, principal_id: principal.id, agent_slug: profile.agentSlug, agent_native_session_id: null,
129
+ state: 'starting', error_code: null, cwd, launch_profile: profile.id, profile: structuredClone(profile),
130
+ created_at: now, updated_at: now, deadline: now + this.bindingTimeout, launch_submitted: false };
131
+ this.doc.requests[request.scoped] = { hash: request.hash, sessionId: record.session_id };
132
+ try {
133
+ this.saveRecord(record);
134
+ }
135
+ catch (error) {
136
+ delete this.doc.requests[request.scoped];
137
+ throw error;
138
+ }
139
+ return this.launch(record, false);
140
+ }
141
+ launch(record, restoring) {
142
+ const promise = (async () => {
143
+ try {
144
+ // Store intent before launching. A failed persistence cannot execute.
145
+ record.launch_submitted = true;
146
+ this.saveRecord(record);
147
+ const prepared = (binding) => { record.terminal_binding = binding; this.saveRecord(record); };
148
+ if (restoring)
149
+ await this.adapter.restore(record, prepared);
150
+ else
151
+ await this.adapter.create(record, prepared);
152
+ await this.refresh(record.session_id);
153
+ this.scheduleRefresh(record);
154
+ }
155
+ catch (error) {
156
+ const current = this.doc.sessions.find(s => s.session_id === record.session_id);
157
+ const code = error.code;
158
+ this.saveRecord({ ...current, state: 'failed', error_code: typeof code === 'string' ? code : 'SESSION_LAUNCH_FAILED', updated_at: Date.now() });
159
+ }
160
+ return structuredClone(this.doc.sessions.find(s => s.session_id === record.session_id));
161
+ })().finally(() => this.pending.delete(record.session_id));
162
+ this.pending.set(record.session_id, promise);
163
+ return promise;
164
+ }
165
+ async get(principal, id) { this.lookup(principal, id); await this.refresh(id); return this.lookup(principal, id); }
166
+ async restore(principal, id, idempotencyKey) {
167
+ const request = this.request(principal, idempotencyKey, ['restore', id]);
168
+ if (request.previous)
169
+ return this.pending.get(id) ?? this.get(principal, id);
170
+ if (this.pending.has(id) || this.restoring.has(id))
171
+ integrationError('SESSION_OPERATION_IN_PROGRESS', 'Another session operation is in progress', 409);
172
+ this.restoring.add(id);
173
+ try {
174
+ const record = await this.get(principal, id);
175
+ if (!record.agent_native_session_id)
176
+ integrationError('NATIVE_SESSION_ID_MISSING', 'An exact native session binding is required', 409);
177
+ // Recheck the current grant and actual directories; restore uses the
178
+ // original profile snapshot, never changed model or wrapper defaults.
179
+ if (!principal.launchProfiles.some(p => p.id === record.launch_profile))
180
+ integrationError('LAUNCH_PROFILE_DENIED', 'Original launch profile is no longer authorized', 403);
181
+ permittedCwd(record.profile, record.cwd);
182
+ const observed = await this.adapter.inspect(record);
183
+ if (observed.running) {
184
+ if (observed.agentSlug !== record.agent_slug || observed.nativeId !== record.agent_native_session_id)
185
+ integrationError('SESSION_IDENTITY_MISMATCH', 'Existing process does not prove the exact native binding', 409);
186
+ this.doc.requests[request.scoped] = { hash: request.hash, sessionId: id };
187
+ try {
188
+ this.save();
189
+ }
190
+ catch (error) {
191
+ delete this.doc.requests[request.scoped];
192
+ throw error;
193
+ }
194
+ return record;
195
+ }
196
+ if (['starting', 'restoring', 'binding_pending'].includes(record.state))
197
+ integrationError('SESSION_OPERATION_IN_PROGRESS', 'Prior launch outcome is not yet resolved; inspect the original session', 409);
198
+ if (observed.exists && !observed.shell)
199
+ integrationError('SESSION_TARGET_NOT_SHELL', 'Original pane is not a verified shell', 409);
200
+ record.state = 'restoring';
201
+ record.error_code = null;
202
+ record.operation_id = randomUUID();
203
+ record.deadline = Date.now() + this.bindingTimeout;
204
+ record.updated_at = Date.now();
205
+ this.doc.requests[request.scoped] = { hash: request.hash, sessionId: id };
206
+ try {
207
+ this.saveRecord(record);
208
+ }
209
+ catch (error) {
210
+ delete this.doc.requests[request.scoped];
211
+ throw error;
212
+ }
213
+ return await this.launch(record, true);
214
+ }
215
+ finally {
216
+ this.restoring.delete(id);
217
+ }
218
+ }
219
+ async refresh(id) {
220
+ const record = structuredClone(this.doc.sessions.find(s => s.session_id === id));
221
+ if (!record)
222
+ return;
223
+ let observed;
224
+ try {
225
+ observed = await this.adapter.inspect(record);
226
+ }
227
+ catch (error) {
228
+ const current = this.doc.sessions.find(s => s.session_id === id);
229
+ if (JSON.stringify(current) !== JSON.stringify(record))
230
+ return;
231
+ const code = error.code;
232
+ if (record.state !== 'failed' || record.error_code !== code)
233
+ this.saveRecord({ ...record, state: 'failed', error_code: typeof code === 'string' ? code : 'SESSION_OBSERVATION_FAILED', updated_at: Date.now() });
234
+ return;
235
+ }
236
+ const next = { ...record };
237
+ if (observed.nativeId && (typeof observed.nativeId !== 'string' || observed.nativeId.length > 256 || /[\x00-\x1f\x7f]/.test(observed.nativeId))) {
238
+ next.state = 'failed';
239
+ next.error_code = 'NATIVE_SESSION_ID_INVALID';
240
+ }
241
+ else if (observed.running && observed.agentSlug !== record.agent_slug) {
242
+ next.state = 'failed';
243
+ next.error_code = 'SESSION_IDENTITY_MISMATCH';
244
+ }
245
+ else if (observed.running && observed.nativeId) {
246
+ if (record.agent_native_session_id && record.agent_native_session_id !== observed.nativeId) {
247
+ next.state = 'failed';
248
+ next.error_code = 'NATIVE_SESSION_ID_MISMATCH';
249
+ }
250
+ else {
251
+ next.agent_native_session_id = observed.nativeId;
252
+ if (next.terminal_binding)
253
+ next.terminal_binding = { ...next.terminal_binding, nativeSessionId: observed.nativeId };
254
+ next.state = 'ready';
255
+ next.error_code = null;
256
+ }
257
+ }
258
+ else if (observed.running) {
259
+ next.state = Date.now() > record.deadline ? 'failed' : 'binding_pending';
260
+ next.error_code = next.state === 'failed' ? 'SESSION_BINDING_TIMEOUT' : null;
261
+ }
262
+ else if (record.state === 'ready') {
263
+ next.state = 'failed';
264
+ next.error_code = 'AGENT_NOT_RUNNING';
265
+ }
266
+ else if (Date.now() > record.deadline && record.state !== 'failed') {
267
+ next.state = 'failed';
268
+ next.error_code = 'SESSION_BINDING_TIMEOUT';
269
+ }
270
+ if (JSON.stringify(this.doc.sessions.find(s => s.session_id === id)) !== JSON.stringify(record))
271
+ return;
272
+ if (next.state !== record.state || next.error_code !== record.error_code || next.agent_native_session_id !== record.agent_native_session_id) {
273
+ next.updated_at = Date.now();
274
+ this.saveRecord(next);
275
+ }
276
+ }
277
+ async refreshAll() {
278
+ if (this.observing) {
279
+ this.observeAgain = true;
280
+ return;
281
+ }
282
+ this.observing = true;
283
+ try {
284
+ do {
285
+ this.observeAgain = false;
286
+ for (const record of this.snapshot())
287
+ await this.refresh(record.session_id);
288
+ } while (this.observeAgain);
289
+ }
290
+ finally {
291
+ this.observing = false;
292
+ }
293
+ }
294
+ close() { this.stopObservation(); for (const timer of this.timers)
295
+ clearTimeout(timer); }
296
+ }