termdock 1.4.190 → 1.4.192

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 (326) hide show
  1. package/dist/client/assets/{App-BkcaQXGe.js → App-BbIl0DAa.js} +37 -37
  2. package/dist/client/assets/App-BbIl0DAa.js.br +0 -0
  3. package/dist/client/assets/App-BbIl0DAa.js.gz +0 -0
  4. package/dist/client/assets/{ChangeWalkthroughPanel-BkTM7zQ0.js → ChangeWalkthroughPanel-CT7mLy_e.js} +1 -1
  5. package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.br +0 -0
  6. package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.gz +0 -0
  7. package/dist/client/assets/{DagPlayground-7UxLIhH5.js → DagPlayground-Bsf4bv7e.js} +1 -1
  8. package/dist/client/assets/DagPlayground-Bsf4bv7e.js.br +0 -0
  9. package/dist/client/assets/DagPlayground-Bsf4bv7e.js.gz +0 -0
  10. package/dist/client/assets/{DiffLab-Cn2bPbbB.js → DiffLab-BjiZcUSn.js} +1 -1
  11. package/dist/client/assets/DiffLab-BjiZcUSn.js.br +0 -0
  12. package/dist/client/assets/DiffLab-BjiZcUSn.js.gz +0 -0
  13. package/dist/client/assets/{DiffReview-Bax7DQmN.js → DiffReview-DmkAyOh4.js} +1 -1
  14. package/dist/client/assets/DiffReview-DmkAyOh4.js.br +0 -0
  15. package/dist/client/assets/DiffReview-DmkAyOh4.js.gz +0 -0
  16. package/dist/client/assets/{DiffReviewLab-TG4kjagX.js → DiffReviewLab-wXYUmdna.js} +1 -1
  17. package/dist/client/assets/DiffReviewLab-wXYUmdna.js.br +0 -0
  18. package/dist/client/assets/DiffReviewLab-wXYUmdna.js.gz +0 -0
  19. package/dist/client/assets/{DiffViewer-3MezBn_a.js → DiffViewer-BlwwMnsB.js} +1 -1
  20. package/dist/client/assets/DiffViewer-BlwwMnsB.js.br +0 -0
  21. package/dist/client/assets/DiffViewer-BlwwMnsB.js.gz +0 -0
  22. package/dist/client/assets/{ModelPreview-BV_0H-Ls.js → ModelPreview-DGIaQAcq.js} +1 -1
  23. package/dist/client/assets/ModelPreview-DGIaQAcq.js.br +0 -0
  24. package/dist/client/assets/ModelPreview-DGIaQAcq.js.gz +0 -0
  25. package/dist/client/assets/{RightSidebar-BHlwv3gW.js → RightSidebar-PgJ0XzEh.js} +47 -47
  26. package/dist/client/assets/RightSidebar-PgJ0XzEh.js.br +0 -0
  27. package/dist/client/assets/RightSidebar-PgJ0XzEh.js.gz +0 -0
  28. package/dist/client/assets/{abnfDiagram-VCTEODGH-DOk0otQq.js → abnfDiagram-VCTEODGH-B_sCIKms.js} +1 -1
  29. package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.br +0 -0
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.gz +0 -0
  31. package/dist/client/assets/app-5zC_J9mq.js +34 -0
  32. package/dist/client/assets/app-5zC_J9mq.js.br +0 -0
  33. package/dist/client/assets/app-5zC_J9mq.js.gz +0 -0
  34. package/dist/client/assets/{arc-CfeOYtSi.js → arc-Ps2GTfJ3.js} +1 -1
  35. package/dist/client/assets/arc-Ps2GTfJ3.js.br +0 -0
  36. package/dist/client/assets/arc-Ps2GTfJ3.js.gz +0 -0
  37. package/dist/client/assets/{architectureDiagram-5GKGNRK7-Dys1Wr-I.js → architectureDiagram-5GKGNRK7-DxKfqPAR.js} +1 -1
  38. package/dist/client/assets/architectureDiagram-5GKGNRK7-DxKfqPAR.js.br +0 -0
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-DxKfqPAR.js.gz +0 -0
  40. package/dist/client/assets/{blockDiagram-I7D4REHJ-D8DbjMtS.js → blockDiagram-I7D4REHJ-BNL8tkC0.js} +1 -1
  41. package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.br +0 -0
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.gz +0 -0
  43. package/dist/client/assets/{c4Diagram-7LVT6UL2-DJcMUQXb.js → c4Diagram-7LVT6UL2-hyEavwo6.js} +1 -1
  44. package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.br +0 -0
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.gz +0 -0
  46. package/dist/client/assets/channel-Bkfq4dnz.js +1 -0
  47. package/dist/client/assets/{chunk-2Q5K7J3B-BPmsHIzM.js → chunk-2Q5K7J3B-DN_HrxPz.js} +1 -1
  48. package/dist/client/assets/{chunk-5VM5RSS4-BTxB35XY.js → chunk-5VM5RSS4-BofAFEcC.js} +1 -1
  49. package/dist/client/assets/{chunk-F27PBJKO-BiUpuqFN.js → chunk-F27PBJKO-CLNw1JDM.js} +1 -1
  50. package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.br +0 -0
  51. package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.gz +0 -0
  52. package/dist/client/assets/{chunk-IMKFNOWR-Bvca4yZA.js → chunk-IMKFNOWR-DsVDl8p7.js} +1 -1
  53. package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.br +0 -0
  54. package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.gz +0 -0
  55. package/dist/client/assets/{chunk-JWPE2WC7-CxCNQSVG.js → chunk-JWPE2WC7-CdfzOrPC.js} +1 -1
  56. package/dist/client/assets/{chunk-POPQ4Y6H-C9E5B9o7.js → chunk-POPQ4Y6H-MXmTC-sG.js} +1 -1
  57. package/dist/client/assets/{chunk-SVP7TREG-D4Qm28xz.js → chunk-SVP7TREG-DfwfQAY6.js} +1 -1
  58. package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.br +0 -0
  59. package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.gz +0 -0
  60. package/dist/client/assets/{chunk-TICWLB2K-CS0AERD9.js → chunk-TICWLB2K-N3OPIvmU.js} +1 -1
  61. package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.br +0 -0
  62. package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.gz +0 -0
  63. package/dist/client/assets/{chunk-XXDRQBXY-C1vCDgbp.js → chunk-XXDRQBXY-IurIojt5.js} +1 -1
  64. package/dist/client/assets/classDiagram-ZZMXUADV-Dahb2o7K.js +1 -0
  65. package/dist/client/assets/classDiagram-v2-VYDZK3BY-Dahb2o7K.js +1 -0
  66. package/dist/client/assets/{cose-bilkent-JH36ORCC-D0-yuoIn.js → cose-bilkent-JH36ORCC-DHbDHTvp.js} +1 -1
  67. package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.br +0 -0
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.gz +0 -0
  69. package/dist/client/assets/{cynefin-OW5HDTMX-C9ZqMdi9.js → cynefin-OW5HDTMX-DyTW2Iyi.js} +1 -1
  70. package/dist/client/assets/cynefin-OW5HDTMX-DyTW2Iyi.js.br +0 -0
  71. package/dist/client/assets/cynefin-OW5HDTMX-DyTW2Iyi.js.gz +0 -0
  72. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-Drvx9Dke.js → cynefinDiagram-5FMLGOSQ-DEan3pcN.js} +1 -1
  73. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.br +0 -0
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.gz +0 -0
  75. package/dist/client/assets/{dagre-GXQ25YYZ-mUXNFR_H.js → dagre-GXQ25YYZ-Rnw2v1uV.js} +1 -1
  76. package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.br +0 -0
  77. package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.gz +0 -0
  78. package/dist/client/assets/{diagram-S7CK7UJ4-CKJgF0VS.js → diagram-S7CK7UJ4-CmBa29S9.js} +1 -1
  79. package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.br +0 -0
  80. package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.gz +0 -0
  81. package/dist/client/assets/{diagram-UQ7AKVKN-Cp9NE2Gc.js → diagram-UQ7AKVKN-Cqzmpane.js} +1 -1
  82. package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.br +0 -0
  83. package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.gz +0 -0
  84. package/dist/client/assets/{diagram-VSXAHHWV-mnyDXn4l.js → diagram-VSXAHHWV-CrHTQozH.js} +1 -1
  85. package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.br +0 -0
  86. package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.gz +0 -0
  87. package/dist/client/assets/{diagram-VX7I27RA-6lyjUw6O.js → diagram-VX7I27RA-C7nQgan9.js} +1 -1
  88. package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.br +0 -0
  89. package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.gz +0 -0
  90. package/dist/client/assets/{diagram-Z3DM3KII-3K_Dhn1t.js → diagram-Z3DM3KII-93X66n3v.js} +1 -1
  91. package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.br +0 -0
  92. package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.gz +0 -0
  93. package/dist/client/assets/{ebnfDiagram-PWID7BFC-B2mHkMsE.js → ebnfDiagram-PWID7BFC-LfScrrH8.js} +1 -1
  94. package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.br +0 -0
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.gz +0 -0
  96. package/dist/client/assets/{erDiagram-RLTQ6QDP-Brfc7YEf.js → erDiagram-RLTQ6QDP-DAWKsrIB.js} +1 -1
  97. package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.br +0 -0
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.gz +0 -0
  99. package/dist/client/assets/{flowDiagram-HODETNUW-UGHsvQMc.js → flowDiagram-HODETNUW-CcdXfpU7.js} +1 -1
  100. package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.br +0 -0
  101. package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.gz +0 -0
  102. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CC30lZuL.js → ganttDiagram-EL5Y4UJY-DPLNbHSY.js} +1 -1
  103. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DPLNbHSY.js.br +0 -0
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DPLNbHSY.js.gz +0 -0
  105. package/dist/client/assets/{gestureArbiter-DIYXjBYY.js → gestureArbiter-B5PxTluh.js} +1 -1
  106. package/dist/client/assets/gestureArbiter-B5PxTluh.js.br +0 -0
  107. package/dist/client/assets/gestureArbiter-B5PxTluh.js.gz +0 -0
  108. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-CiwHG61R.js → gitGraphDiagram-WWUBYQGX-P741cwUf.js} +1 -1
  109. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.br +0 -0
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.gz +0 -0
  111. package/dist/client/assets/{index-TEmneMvF.css → index-oKp6kSMR.css} +1 -1
  112. package/dist/client/assets/index-oKp6kSMR.css.br +0 -0
  113. package/dist/client/assets/{index-TEmneMvF.css.gz → index-oKp6kSMR.css.gz} +0 -0
  114. package/dist/client/assets/{infoDiagram-27XIBGKW-CcAeYa6X.js → infoDiagram-27XIBGKW-xrl-VW74.js} +1 -1
  115. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-COFtLTBs.js → ishikawaDiagram-5VMMS53U-BJmiVWrB.js} +1 -1
  116. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.br +0 -0
  117. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.gz +0 -0
  118. package/dist/client/assets/{journeyDiagram-3NMN7TZE-D0iXlEDw.js → journeyDiagram-3NMN7TZE-DZX8uYK2.js} +1 -1
  119. package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.br +0 -0
  120. package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.gz +0 -0
  121. package/dist/client/assets/{kanban-definition-UXKFOSKX-Dr2EVP3G.js → kanban-definition-UXKFOSKX-1SmdXf0S.js} +1 -1
  122. package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.br +0 -0
  123. package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.gz +0 -0
  124. package/dist/client/assets/{layout-CCzGp_3m.js → layout-De4QxmMN.js} +1 -1
  125. package/dist/client/assets/layout-De4QxmMN.js.br +0 -0
  126. package/dist/client/assets/layout-De4QxmMN.js.gz +0 -0
  127. package/dist/client/assets/{linear-B-AlDkuD.js → linear-Dc93ZnhP.js} +1 -1
  128. package/dist/client/assets/linear-Dc93ZnhP.js.br +0 -0
  129. package/dist/client/assets/linear-Dc93ZnhP.js.gz +0 -0
  130. package/dist/client/assets/{mermaid.core-C8GTgQvN.js → mermaid.core-Ny4ifEul.js} +5 -5
  131. package/dist/client/assets/mermaid.core-Ny4ifEul.js.br +0 -0
  132. package/dist/client/assets/mermaid.core-Ny4ifEul.js.gz +0 -0
  133. package/dist/client/assets/{mindmap-definition-YA3MSWOX-B1_AIM7r.js → mindmap-definition-YA3MSWOX-CE75rUrK.js} +1 -1
  134. package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.br +0 -0
  135. package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.gz +0 -0
  136. package/dist/client/assets/{passwordBootstrap-DUJCjLgA.js → passwordBootstrap-BuXV9qsA.js} +1 -1
  137. package/dist/client/assets/passwordBootstrap-BuXV9qsA.js.br +0 -0
  138. package/dist/client/assets/passwordBootstrap-BuXV9qsA.js.gz +0 -0
  139. package/dist/client/assets/{pegDiagram-XKGWAZYB-BGri_6xk.js → pegDiagram-XKGWAZYB-CR8E9wWF.js} +1 -1
  140. package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.br +0 -0
  141. package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.gz +0 -0
  142. package/dist/client/assets/{pieDiagram-E7YTZNPT-DyJUIUtO.js → pieDiagram-E7YTZNPT-CKZz2-Iz.js} +1 -1
  143. package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.br +0 -0
  144. package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.gz +0 -0
  145. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-ChYx832r.js → quadrantDiagram-AXDQQJYC-w0wqPGg1.js} +1 -1
  146. package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.br +0 -0
  147. package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.gz +0 -0
  148. package/dist/client/assets/{railroadDiagram-O6MQD6OU-Ll7CEGCv.js → railroadDiagram-O6MQD6OU-CWYxj4Iu.js} +1 -1
  149. package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.br +0 -0
  150. package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.gz +0 -0
  151. package/dist/client/assets/{referenceLongPress-DhODL_ZR.js → referenceLongPress-xQTJmyUv.js} +1 -1
  152. package/dist/client/assets/referenceLongPress-xQTJmyUv.js.br +0 -0
  153. package/dist/client/assets/referenceLongPress-xQTJmyUv.js.gz +0 -0
  154. package/dist/client/assets/{requirementDiagram-BXWQKSXE-WAFfi6wC.js → requirementDiagram-BXWQKSXE-DzcUlIZo.js} +1 -1
  155. package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.br +0 -0
  156. package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.gz +0 -0
  157. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-Bmk9JzLH.js → sankeyDiagram-P5KCCOFB-B1KFzqqb.js} +1 -1
  158. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.br +0 -0
  159. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.gz +0 -0
  160. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CQLp4wz7.js → sequenceDiagram-WJ2MYXX4-Bph5Zy95.js} +1 -1
  161. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.br +0 -0
  162. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.gz +0 -0
  163. package/dist/client/assets/{sizeCapture-INFHLROL-7biER85h.js → sizeCapture-INFHLROL-B22qdSe2.js} +1 -1
  164. package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.br +0 -0
  165. package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.gz +0 -0
  166. package/dist/client/assets/{sparkles-Bdif6oju.js → sparkles-iFGazcNw.js} +1 -1
  167. package/dist/client/assets/{stateDiagram-D77RDMKH-Cy9r_YvI.js → stateDiagram-D77RDMKH-qt4ZIeIU.js} +1 -1
  168. package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.br +0 -0
  169. package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.gz +0 -0
  170. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-xKezo5Hj.js +1 -0
  171. package/dist/client/assets/{swimlanes-42K2YHIH-DeAGlPA-.js → swimlanes-42K2YHIH-BLCfdR2d.js} +1 -1
  172. package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.br +0 -0
  173. package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.gz +0 -0
  174. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CNA032xr.js +8 -0
  175. package/dist/client/assets/{timeline-definition-24CTP7MA-BRAobv6g.js → timeline-definition-24CTP7MA-B1KrZ6Jd.js} +1 -1
  176. package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.br +0 -0
  177. package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.gz +0 -0
  178. package/dist/client/assets/{triangle-alert-Bllwcw-n.js → triangle-alert-BNMREFNU.js} +1 -1
  179. package/dist/client/assets/{vennDiagram-4TSXK5OY-B4uz7tHn.js → vennDiagram-4TSXK5OY-DWNDoCmv.js} +1 -1
  180. package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.br +0 -0
  181. package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.gz +0 -0
  182. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-uRNjWdqL.js → wardleyDiagram-VM6X3IG4-kdVZqb5Z.js} +1 -1
  183. package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.br +0 -0
  184. package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.gz +0 -0
  185. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DCT4HGtL.js → xychartDiagram-S5SC5T6Z-C1U9Cr8F.js} +1 -1
  186. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.js.br +0 -0
  187. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.js.gz +0 -0
  188. package/dist/client/index.html +2 -2
  189. package/dist/client/index.html.br +0 -0
  190. package/dist/client/index.html.gz +0 -0
  191. package/dist/client/sw.js +1 -1
  192. package/dist/client/sw.js.br +0 -0
  193. package/dist/client/sw.js.gz +0 -0
  194. package/dist/server/agent/collaborationCli.js +224 -11
  195. package/dist/server/agent/collaborationDeliveryWorker.js +94 -1
  196. package/dist/server/agent/collaborationGroupRoutes.js +25 -0
  197. package/dist/server/agent/collaborationPrompt.js +97 -34
  198. package/dist/server/agent/collaborationRoutes.js +119 -9
  199. package/dist/server/agent/collaborationSpawn.js +16 -0
  200. package/dist/server/agent/collaborationStore.js +86 -12
  201. package/dist/server/agent/collaborationTmuxDelivery.js +86 -1
  202. package/dist/server/cli.js +11 -4
  203. package/dist/server/routes/terminal.js +271 -13
  204. package/package.json +1 -1
  205. package/runtime-manifest.json +3 -3
  206. package/dist/client/assets/App-BkcaQXGe.js.br +0 -0
  207. package/dist/client/assets/App-BkcaQXGe.js.gz +0 -0
  208. package/dist/client/assets/ChangeWalkthroughPanel-BkTM7zQ0.js.br +0 -0
  209. package/dist/client/assets/ChangeWalkthroughPanel-BkTM7zQ0.js.gz +0 -0
  210. package/dist/client/assets/DagPlayground-7UxLIhH5.js.br +0 -0
  211. package/dist/client/assets/DagPlayground-7UxLIhH5.js.gz +0 -0
  212. package/dist/client/assets/DiffLab-Cn2bPbbB.js.br +0 -0
  213. package/dist/client/assets/DiffLab-Cn2bPbbB.js.gz +0 -0
  214. package/dist/client/assets/DiffReview-Bax7DQmN.js.br +0 -0
  215. package/dist/client/assets/DiffReview-Bax7DQmN.js.gz +0 -0
  216. package/dist/client/assets/DiffReviewLab-TG4kjagX.js.br +0 -0
  217. package/dist/client/assets/DiffReviewLab-TG4kjagX.js.gz +0 -0
  218. package/dist/client/assets/DiffViewer-3MezBn_a.js.br +0 -0
  219. package/dist/client/assets/DiffViewer-3MezBn_a.js.gz +0 -0
  220. package/dist/client/assets/ModelPreview-BV_0H-Ls.js.br +0 -0
  221. package/dist/client/assets/ModelPreview-BV_0H-Ls.js.gz +0 -0
  222. package/dist/client/assets/RightSidebar-BHlwv3gW.js.br +0 -0
  223. package/dist/client/assets/RightSidebar-BHlwv3gW.js.gz +0 -0
  224. package/dist/client/assets/abnfDiagram-VCTEODGH-DOk0otQq.js.br +0 -0
  225. package/dist/client/assets/abnfDiagram-VCTEODGH-DOk0otQq.js.gz +0 -0
  226. package/dist/client/assets/app-B0az5Yz6.js +0 -34
  227. package/dist/client/assets/app-B0az5Yz6.js.br +0 -0
  228. package/dist/client/assets/app-B0az5Yz6.js.gz +0 -0
  229. package/dist/client/assets/arc-CfeOYtSi.js.br +0 -0
  230. package/dist/client/assets/arc-CfeOYtSi.js.gz +0 -0
  231. package/dist/client/assets/architectureDiagram-5GKGNRK7-Dys1Wr-I.js.br +0 -0
  232. package/dist/client/assets/architectureDiagram-5GKGNRK7-Dys1Wr-I.js.gz +0 -0
  233. package/dist/client/assets/blockDiagram-I7D4REHJ-D8DbjMtS.js.br +0 -0
  234. package/dist/client/assets/blockDiagram-I7D4REHJ-D8DbjMtS.js.gz +0 -0
  235. package/dist/client/assets/c4Diagram-7LVT6UL2-DJcMUQXb.js.br +0 -0
  236. package/dist/client/assets/c4Diagram-7LVT6UL2-DJcMUQXb.js.gz +0 -0
  237. package/dist/client/assets/channel-SwcBL8jW.js +0 -1
  238. package/dist/client/assets/chunk-F27PBJKO-BiUpuqFN.js.br +0 -0
  239. package/dist/client/assets/chunk-F27PBJKO-BiUpuqFN.js.gz +0 -0
  240. package/dist/client/assets/chunk-IMKFNOWR-Bvca4yZA.js.br +0 -0
  241. package/dist/client/assets/chunk-IMKFNOWR-Bvca4yZA.js.gz +0 -0
  242. package/dist/client/assets/chunk-SVP7TREG-D4Qm28xz.js.br +0 -0
  243. package/dist/client/assets/chunk-SVP7TREG-D4Qm28xz.js.gz +0 -0
  244. package/dist/client/assets/chunk-TICWLB2K-CS0AERD9.js.br +0 -0
  245. package/dist/client/assets/chunk-TICWLB2K-CS0AERD9.js.gz +0 -0
  246. package/dist/client/assets/classDiagram-ZZMXUADV-BOSTHjwM.js +0 -1
  247. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BOSTHjwM.js +0 -1
  248. package/dist/client/assets/cose-bilkent-JH36ORCC-D0-yuoIn.js.br +0 -0
  249. package/dist/client/assets/cose-bilkent-JH36ORCC-D0-yuoIn.js.gz +0 -0
  250. package/dist/client/assets/cynefin-OW5HDTMX-C9ZqMdi9.js.br +0 -0
  251. package/dist/client/assets/cynefin-OW5HDTMX-C9ZqMdi9.js.gz +0 -0
  252. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Drvx9Dke.js.br +0 -0
  253. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Drvx9Dke.js.gz +0 -0
  254. package/dist/client/assets/dagre-GXQ25YYZ-mUXNFR_H.js.br +0 -0
  255. package/dist/client/assets/dagre-GXQ25YYZ-mUXNFR_H.js.gz +0 -0
  256. package/dist/client/assets/diagram-S7CK7UJ4-CKJgF0VS.js.br +0 -0
  257. package/dist/client/assets/diagram-S7CK7UJ4-CKJgF0VS.js.gz +0 -0
  258. package/dist/client/assets/diagram-UQ7AKVKN-Cp9NE2Gc.js.br +0 -0
  259. package/dist/client/assets/diagram-UQ7AKVKN-Cp9NE2Gc.js.gz +0 -0
  260. package/dist/client/assets/diagram-VSXAHHWV-mnyDXn4l.js.br +0 -0
  261. package/dist/client/assets/diagram-VSXAHHWV-mnyDXn4l.js.gz +0 -0
  262. package/dist/client/assets/diagram-VX7I27RA-6lyjUw6O.js.br +0 -0
  263. package/dist/client/assets/diagram-VX7I27RA-6lyjUw6O.js.gz +0 -0
  264. package/dist/client/assets/diagram-Z3DM3KII-3K_Dhn1t.js.br +0 -0
  265. package/dist/client/assets/diagram-Z3DM3KII-3K_Dhn1t.js.gz +0 -0
  266. package/dist/client/assets/ebnfDiagram-PWID7BFC-B2mHkMsE.js.br +0 -0
  267. package/dist/client/assets/ebnfDiagram-PWID7BFC-B2mHkMsE.js.gz +0 -0
  268. package/dist/client/assets/erDiagram-RLTQ6QDP-Brfc7YEf.js.br +0 -0
  269. package/dist/client/assets/erDiagram-RLTQ6QDP-Brfc7YEf.js.gz +0 -0
  270. package/dist/client/assets/flowDiagram-HODETNUW-UGHsvQMc.js.br +0 -0
  271. package/dist/client/assets/flowDiagram-HODETNUW-UGHsvQMc.js.gz +0 -0
  272. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CC30lZuL.js.br +0 -0
  273. package/dist/client/assets/ganttDiagram-EL5Y4UJY-CC30lZuL.js.gz +0 -0
  274. package/dist/client/assets/gestureArbiter-DIYXjBYY.js.br +0 -0
  275. package/dist/client/assets/gestureArbiter-DIYXjBYY.js.gz +0 -0
  276. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CiwHG61R.js.br +0 -0
  277. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CiwHG61R.js.gz +0 -0
  278. package/dist/client/assets/index-TEmneMvF.css.br +0 -0
  279. package/dist/client/assets/ishikawaDiagram-5VMMS53U-COFtLTBs.js.br +0 -0
  280. package/dist/client/assets/ishikawaDiagram-5VMMS53U-COFtLTBs.js.gz +0 -0
  281. package/dist/client/assets/journeyDiagram-3NMN7TZE-D0iXlEDw.js.br +0 -0
  282. package/dist/client/assets/journeyDiagram-3NMN7TZE-D0iXlEDw.js.gz +0 -0
  283. package/dist/client/assets/kanban-definition-UXKFOSKX-Dr2EVP3G.js.br +0 -0
  284. package/dist/client/assets/kanban-definition-UXKFOSKX-Dr2EVP3G.js.gz +0 -0
  285. package/dist/client/assets/layout-CCzGp_3m.js.br +0 -0
  286. package/dist/client/assets/layout-CCzGp_3m.js.gz +0 -0
  287. package/dist/client/assets/linear-B-AlDkuD.js.br +0 -0
  288. package/dist/client/assets/linear-B-AlDkuD.js.gz +0 -0
  289. package/dist/client/assets/mermaid.core-C8GTgQvN.js.br +0 -0
  290. package/dist/client/assets/mermaid.core-C8GTgQvN.js.gz +0 -0
  291. package/dist/client/assets/mindmap-definition-YA3MSWOX-B1_AIM7r.js.br +0 -0
  292. package/dist/client/assets/mindmap-definition-YA3MSWOX-B1_AIM7r.js.gz +0 -0
  293. package/dist/client/assets/passwordBootstrap-DUJCjLgA.js.br +0 -0
  294. package/dist/client/assets/passwordBootstrap-DUJCjLgA.js.gz +0 -0
  295. package/dist/client/assets/pegDiagram-XKGWAZYB-BGri_6xk.js.br +0 -0
  296. package/dist/client/assets/pegDiagram-XKGWAZYB-BGri_6xk.js.gz +0 -0
  297. package/dist/client/assets/pieDiagram-E7YTZNPT-DyJUIUtO.js.br +0 -0
  298. package/dist/client/assets/pieDiagram-E7YTZNPT-DyJUIUtO.js.gz +0 -0
  299. package/dist/client/assets/quadrantDiagram-AXDQQJYC-ChYx832r.js.br +0 -0
  300. package/dist/client/assets/quadrantDiagram-AXDQQJYC-ChYx832r.js.gz +0 -0
  301. package/dist/client/assets/railroadDiagram-O6MQD6OU-Ll7CEGCv.js.br +0 -0
  302. package/dist/client/assets/railroadDiagram-O6MQD6OU-Ll7CEGCv.js.gz +0 -0
  303. package/dist/client/assets/referenceLongPress-DhODL_ZR.js.br +0 -0
  304. package/dist/client/assets/referenceLongPress-DhODL_ZR.js.gz +0 -0
  305. package/dist/client/assets/requirementDiagram-BXWQKSXE-WAFfi6wC.js.br +0 -0
  306. package/dist/client/assets/requirementDiagram-BXWQKSXE-WAFfi6wC.js.gz +0 -0
  307. package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bmk9JzLH.js.br +0 -0
  308. package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bmk9JzLH.js.gz +0 -0
  309. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CQLp4wz7.js.br +0 -0
  310. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CQLp4wz7.js.gz +0 -0
  311. package/dist/client/assets/sizeCapture-INFHLROL-7biER85h.js.br +0 -0
  312. package/dist/client/assets/sizeCapture-INFHLROL-7biER85h.js.gz +0 -0
  313. package/dist/client/assets/stateDiagram-D77RDMKH-Cy9r_YvI.js.br +0 -0
  314. package/dist/client/assets/stateDiagram-D77RDMKH-Cy9r_YvI.js.gz +0 -0
  315. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-DOly_YL3.js +0 -1
  316. package/dist/client/assets/swimlanes-42K2YHIH-DeAGlPA-.js.br +0 -0
  317. package/dist/client/assets/swimlanes-42K2YHIH-DeAGlPA-.js.gz +0 -0
  318. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DxYgGhLH.js +0 -8
  319. package/dist/client/assets/timeline-definition-24CTP7MA-BRAobv6g.js.br +0 -0
  320. package/dist/client/assets/timeline-definition-24CTP7MA-BRAobv6g.js.gz +0 -0
  321. package/dist/client/assets/vennDiagram-4TSXK5OY-B4uz7tHn.js.br +0 -0
  322. package/dist/client/assets/vennDiagram-4TSXK5OY-B4uz7tHn.js.gz +0 -0
  323. package/dist/client/assets/wardleyDiagram-VM6X3IG4-uRNjWdqL.js.br +0 -0
  324. package/dist/client/assets/wardleyDiagram-VM6X3IG4-uRNjWdqL.js.gz +0 -0
  325. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DCT4HGtL.js.br +0 -0
  326. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DCT4HGtL.js.gz +0 -0
@@ -13,7 +13,7 @@ import { createRequire } from 'module';
13
13
  import { fileURLToPath } from 'url';
14
14
  import QRCode from 'qrcode';
15
15
  import { execFile, spawn } from 'child_process';
16
- import { createHash, randomUUID } from 'crypto';
16
+ import { createHash, randomBytes, randomUUID } from 'crypto';
17
17
  import { promisify } from 'util';
18
18
  import { caffeinateManager } from '../utils/caffeinate.js';
19
19
  import { DesktopRuntimeOwnerClient } from '../utils/desktopRuntimeOwner.js';
@@ -39,13 +39,13 @@ import { buildBracketedSubmitBytes, canDeliverPromptToAgent } from '../agent/pro
39
39
  import { collaborationRoutes } from '../agent/collaborationRoutes.js';
40
40
  import { COLLAB_LIMITS, extrasFromBody } from '../agent/collaborationProtocol.js';
41
41
  import { CollaborationStore } from '../agent/collaborationStore.js';
42
- import { formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
43
- import { resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
42
+ import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
43
+ import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
44
44
  import { SessionSearchStore } from '../agent/sessionSearchStore.js';
45
45
  import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
46
46
  import { CollaborationRoutingStore, selectCollaborationPane } from '../agent/collaborationRouting.js';
47
47
  import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
48
- import { writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
48
+ import { approveCollaborationDialog, captureTmuxPaneHistory, captureTmuxPaneText, recoverStuckPaste, sendTmuxPaneKey, writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
49
49
  import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, uninstallHooksForSlug, } from '../agent/installers.js';
50
50
  import { loadPlugins, savePlugin, removePlugin, readPluginIcon, validateManifest, } from '../agent/plugins.js';
51
51
  import { TmuxLifecycleCoordinator } from '../utils/tmuxLifecycle.js';
@@ -152,6 +152,18 @@ class HttpStatusError extends Error {
152
152
  }
153
153
  const terminalSessions = new Map();
154
154
  let globalSessionState = { sessions: [], updatedAt: Date.now() };
155
+ /** 8-char base36 frontend session ids. Dozens of sessions live at once, so a
156
+ * short collision-checked id beats a 36-char UUID to type in CLI operations;
157
+ * the random draw is retried when it hits an existing session. Each of the 8
158
+ * bytes maps to one base36 character — toString(36) turns the 0-35 residue
159
+ * into a single 0-9a-z char, never a multi-digit number string. */
160
+ function generateFrontendSessionId() {
161
+ for (;;) {
162
+ const id = [...randomBytes(8)].map((byte) => (byte % 36).toString(36)).join('');
163
+ if (!globalSessionState.sessions.some((session) => session.sessionId === id))
164
+ return id;
165
+ }
166
+ }
155
167
  // ── 持久化 globalSessionState 到磁盘,防止服务重启后丢失 ──
156
168
  const GLOBAL_SESSION_STATE_FILE = `${TERMDOCK_DIR}/global-session-state.json`;
157
169
  const CLIENT_STATES_FILE = `${TERMDOCK_DIR}/client-states.json`; // 保留用于迁移
@@ -1043,6 +1055,10 @@ function removeGlobalSessionRecord(frontendSessionId) {
1043
1055
  if (changed) {
1044
1056
  collaborationStore.removeSession(frontendSessionId);
1045
1057
  collaborationRouting.remove(frontendSessionId);
1058
+ for (const key of [...collaborationScaffoldState.keys()]) {
1059
+ if (key.startsWith(`${frontendSessionId}|`))
1060
+ collaborationScaffoldState.delete(key);
1061
+ }
1046
1062
  }
1047
1063
  return changed;
1048
1064
  }
@@ -1243,7 +1259,7 @@ async function openInventorySession(req, input) {
1243
1259
  throw new HttpStatusError(404, 'session not found');
1244
1260
  }
1245
1261
  if (!record) {
1246
- const sessionId = preferredFrontendSessionId || randomUUID();
1262
+ const sessionId = preferredFrontendSessionId || generateFrontendSessionId();
1247
1263
  const defaultName = normalizedMode === 'tmux' && normalizedTmuxName
1248
1264
  ? `tmux:${normalizedTmuxName}`
1249
1265
  : `terminal-${now.toString(36)}`;
@@ -1433,13 +1449,33 @@ function tryDeliverCollaborationInbox(frontendSessionId) {
1433
1449
  }
1434
1450
  collaborationDeliveryWorker.wake(frontendSessionId);
1435
1451
  const route = collaborationDeliveryWorker.state(frontendSessionId);
1436
- return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId),
1452
+ return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId), waitable: true,
1437
1453
  reason: route.reason ?? '消息已入队,后台正在恢复路由并投递' };
1438
1454
  }
1455
+ // Per-session routing-help education state: static help (peer roster + help
1456
+ // pointer) rides only the first delivery, then returns when the roster
1457
+ // changes (a new member joined). In-memory only — a restart simply re-educates
1458
+ // once, which is harmless. Dynamic notices are never gated by this.
1459
+ const collaborationScaffoldState = new Map();
1460
+ function collaborationRosterSignature(frontendSessionId) {
1461
+ const peerIds = new Set(collaborationStore.groupsForSession(frontendSessionId)
1462
+ .flatMap((group) => group.sessionIds ?? []).filter((id) => id !== frontendSessionId));
1463
+ return [...peerIds].sort().join('|');
1464
+ }
1439
1465
  function formatLocalCollaborationMessages(frontendSessionId, messages) {
1466
+ const signature = collaborationRosterSignature(frontendSessionId);
1467
+ // Education follows the bound agent identity, not just the pane: a rebind to
1468
+ // a different agent session must re-educate the newcomer.
1469
+ const binding = collaborationRouting.get(frontendSessionId);
1470
+ const educationKey = `${frontendSessionId}|${binding?.agentSlug ?? ''}|${binding?.nativeSessionId ?? ''}`;
1471
+ const educated = collaborationScaffoldState.get(educationKey);
1472
+ const showRoutingHelp = !educated || educated.rosterSignature !== signature;
1473
+ if (showRoutingHelp)
1474
+ collaborationScaffoldState.set(educationKey, { rosterSignature: signature });
1440
1475
  return formatCollaborationDelivery({ targetSessionId: frontendSessionId, messages,
1441
1476
  groups: collaborationStore.groupsForSession(frontendSessionId),
1442
1477
  sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(orchestrationSessionSnapshot)],
1478
+ showRoutingHelp,
1443
1479
  });
1444
1480
  }
1445
1481
  async function inspectCollaborationTmux(binding, requestedPane) {
@@ -1511,6 +1547,12 @@ async function rebindCollaborationRoute(frontendSessionId, paneId) {
1511
1547
  collaborationDeliveryWorker.wake(frontendSessionId);
1512
1548
  return { ...collaborationRouting.get(frontendSessionId), state: 'recovering' };
1513
1549
  }
1550
+ /**
1551
+ * Delivery is deliberately un-gated: any reachable recipient receives the
1552
+ * message immediately, whatever its agent is doing. Busy guessing (hook state,
1553
+ * turn phase) is the sender's job — the send receipt carries a screen
1554
+ * snapshot of the recipient after the write, and the sender judges from that.
1555
+ */
1514
1556
  async function resolveCollaborationRoute(frontendSessionId) {
1515
1557
  let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
1516
1558
  if (!record)
@@ -1554,12 +1596,16 @@ async function resolveCollaborationRoute(frontendSessionId) {
1554
1596
  }
1555
1597
  if (!backend)
1556
1598
  return { state: 'detached', reason: 'TMUX_BACKEND_UNAVAILABLE' };
1557
- return { state: 'ready', write: async (messages) => {
1599
+ return { state: 'ready', capture: async () => (await captureTmuxPane(pinned.paneId)).content, write: async (messages) => {
1558
1600
  if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
1559
1601
  throw new Error('SESSION_REMOVED');
1560
1602
  await writeCollaborationTmuxPane(runTmux, pinned, formatLocalCollaborationMessages(frontendSessionId, messages));
1561
1603
  backend.lastActivity = Date.now();
1562
- } };
1604
+ // First-delivery confirm: history proves the agent rendered our
1605
+ // message (boot sequences clear only the screen, never the history a
1606
+ // live TUI writes into). Terminal-state only — no agent hooks.
1607
+ }, confirm: async () => captureTmuxPaneHistory(runTmux, pinned), approve: async () => approveCollaborationDialog(runTmux, pinned),
1608
+ recoverStuck: async (baseline) => recoverStuckPaste(runTmux, pinned, baseline) };
1563
1609
  }
1564
1610
  if (!backend || !binding.backendSessionId)
1565
1611
  return { state: 'offline', reason: 'SHELL_BACKEND_NOT_RUNNING' };
@@ -1597,7 +1643,7 @@ async function refreshCollaborationAgentIdentity(backendSessionId, session) {
1597
1643
  persistActiveProgramBinding(backendSessionId, activeProgram?.command);
1598
1644
  syncAgentIdentity(backendSessionId, session);
1599
1645
  }
1600
- function deliverCollaborationInboxWhenAgentReady(frontendSessionId, backendSessionId, attempt = 0) {
1646
+ function deliverCollaborationInboxWhenAgentReady(frontendSessionId) {
1601
1647
  collaborationDeliveryWorker.wake(frontendSessionId);
1602
1648
  }
1603
1649
  async function spawnCollaborationAgentSession(req, group, sourceSessionId, input) {
@@ -1618,6 +1664,9 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1618
1664
  fallbackMode: fallbackRecord?.mode,
1619
1665
  });
1620
1666
  const requestedName = typeof input.name === 'string' ? input.name.trim().slice(0, 120) : '';
1667
+ if (requestedName && COLLAB_NAME_FORBIDDEN.test(requestedName)) {
1668
+ throw new HttpStatusError(400, '会话名称不能包含「【】」、间隔号「·」、换行或控制字符', 'COLLAB_NAME_INVALID');
1669
+ }
1621
1670
  const requestedCwd = typeof input.cwd === 'string' ? input.cwd.trim() : '';
1622
1671
  const opened = await openInventorySession(req, {
1623
1672
  name: requestedName || `${launcher.displayName} · ${group.name}`,
@@ -1634,7 +1683,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1634
1683
  name: group.name,
1635
1684
  sessionIds: [...group.sessionIds, frontendSessionId],
1636
1685
  });
1637
- writeTerminalInput(backend, `${launcher.command}\r`);
1686
+ writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
1638
1687
  const requestedTask = typeof input.task === 'string' ? input.task.trim().slice(0, 20_000) : '';
1639
1688
  collaborationStore.send({
1640
1689
  groupId: updatedGroup.id,
@@ -1644,7 +1693,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1644
1693
  content: requestedTask || `你已创建并加入协作组“${updatedGroup.name}”。请运行 td collab status 查看成员,并准备参与协作。`,
1645
1694
  });
1646
1695
  setTimeout(() => {
1647
- deliverCollaborationInboxWhenAgentReady(frontendSessionId, opened.terminalSession.sessionId);
1696
+ deliverCollaborationInboxWhenAgentReady(frontendSessionId);
1648
1697
  }, 300).unref?.();
1649
1698
  return { group: updatedGroup, session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
1650
1699
  }
@@ -4858,7 +4907,7 @@ async function adoptPtyHostSessions() {
4858
4907
  if (!existing) {
4859
4908
  const now = Date.now();
4860
4909
  upsertGlobalSessionRecord({
4861
- sessionId: randomUUID(),
4910
+ sessionId: generateFrontendSessionId(),
4862
4911
  name: `terminal-${now.toString(36)}`,
4863
4912
  backendSessionId: meta.id,
4864
4913
  mode: 'shell',
@@ -5541,8 +5590,217 @@ router.post('/operations/orchestration/spawn', async (req, res) => {
5541
5590
  res.status(400).json({ error: getErrorMessage(error) });
5542
5591
  }
5543
5592
  });
5593
+ /**
5594
+ * Session cleanup for the collaboration CLI. Two semantic guards make the
5595
+ * destructive surface safe from inside a group:
5596
+ * - a target must share a collaboration group with the caller (personal
5597
+ * sessions stay reachable only from the web UI), and the caller can never
5598
+ * clean its own session;
5599
+ * - cleanup refuses when removing a target would dissolve a group the caller
5600
+ * still belongs to (groups under 2 members are dropped with their whole
5601
+ * message history — dissolving your own group needs a deliberate group op,
5602
+ * not a member cleanup).
5603
+ * Every execution is a two-step human-granted act: without `confirmed: true`
5604
+ * the route only reports the plan (records, groups, tmux sessions that would
5605
+ * be destroyed) and refuses; an agent must relay that plan to the user and
5606
+ * re-run with explicit confirmation before anything is deleted.
5607
+ */
5608
+ router.post('/operations/orchestration/cleanup', async (req, res) => {
5609
+ const sourceSessionId = resolveFrontendSessionId(req.body ?? {});
5610
+ if (!sourceSessionId)
5611
+ return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
5612
+ const requested = Array.isArray(req.body?.sessionIds)
5613
+ ? [...new Set(req.body.sessionIds.filter((id) => typeof id === 'string' && id.trim().length > 0))]
5614
+ : [];
5615
+ if (requested.length === 0)
5616
+ return res.status(400).json({ error: '至少需要一个待清理的会话 id' });
5617
+ if (requested.includes(sourceSessionId))
5618
+ return res.status(400).json({ error: '不能清理当前会话自身;如需退出协作请使用组操作或网页端' });
5619
+ const recordsById = new Map(globalSessionState.sessions.map((session) => [session.sessionId, session]));
5620
+ const missing = requested.filter((id) => !recordsById.has(id));
5621
+ if (missing.length > 0)
5622
+ return res.status(404).json({ error: `以下会话不存在(可能已清理或已离线):${missing.join('、')}` });
5623
+ const label = (sessionId) => recordsById.get(sessionId)?.name.trim() || sessionId;
5624
+ // Targets must share a collaboration group with the caller: CLI cleanup only
5625
+ // covers orchestration peers, never unrelated personal terminals.
5626
+ const callerGroupIds = new Set(collaborationStore.groupsForSession(sourceSessionId).map((group) => group.id));
5627
+ const outsiders = requested.filter((id) => !collaborationStore.groupsForSession(id).some((group) => callerGroupIds.has(group.id)));
5628
+ if (outsiders.length > 0) {
5629
+ return res.status(403).json({ error: `目标会话不在你的协作组中,CLI 无法清理:${outsiders.map(label).join('、')}(个人会话请在网页端删除)` });
5630
+ }
5631
+ const requestedSet = new Set(requested);
5632
+ const groupEffects = [];
5633
+ for (const id of requested) {
5634
+ for (const group of collaborationStore.groupsForSession(id)) {
5635
+ if (groupEffects.some((effect) => effect.id === group.id))
5636
+ continue;
5637
+ const remaining = group.sessionIds.filter((member) => !requestedSet.has(member)).length;
5638
+ groupEffects.push({
5639
+ id: group.id, name: group.name ?? null, sizeBefore: group.sessionIds.length,
5640
+ sizeAfter: remaining < 2 ? 0 : remaining, dissolves: remaining < 2,
5641
+ includesCaller: group.sessionIds.includes(sourceSessionId),
5642
+ });
5643
+ }
5644
+ }
5645
+ const dissolvingOwn = groupEffects.find((effect) => effect.dissolves && effect.includesCaller);
5646
+ if (dissolvingOwn) {
5647
+ return res.status(403).json({ error: `清理会使你所在的协作组「${dissolvingOwn.name ?? dissolvingOwn.id}」解散(剩余成员不足 2,组内消息会被一并清除);如需解散协作组请在网页端操作,组保留时再清理成员会话` });
5648
+ }
5649
+ const targets = requested.map((sessionId) => {
5650
+ const record = recordsById.get(sessionId);
5651
+ return { sessionId: record.sessionId, name: record.name.trim() || null, mode: record.mode,
5652
+ tmuxSessionName: record.mode === 'tmux' ? (record.tmuxSessionName ?? null) : null,
5653
+ backendSessionId: record.backendSessionId ?? null };
5654
+ });
5655
+ const plan = { targets, groups: groupEffects };
5656
+ if (req.body?.confirmed !== true) {
5657
+ return res.json({ ok: false, code: 'CLEANUP_CONFIRM_REQUIRED',
5658
+ error: '不可恢复的风险操作:执行前必须先获得用户(人类)的明确同意',
5659
+ instruction: `将清理计划告知用户并取得明确同意后重跑:td collab cleanup ${requested.join(' ')} --confirm`,
5660
+ plan });
5661
+ }
5662
+ const removed = [];
5663
+ for (const sessionId of requested) {
5664
+ const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === sessionId);
5665
+ if (!record) {
5666
+ removed.push({ sessionId, name: null });
5667
+ continue;
5668
+ }
5669
+ archiveAgentResumeRecord(record, 'closed');
5670
+ removeGlobalSessionRecord(sessionId);
5671
+ if (record.mode === 'tmux' && record.tmuxSessionName) {
5672
+ try {
5673
+ const outcome = await destroyTmuxSessionSafely(record.tmuxSessionName);
5674
+ removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: record.tmuxSessionName, alreadyGone: outcome.alreadyGone === true });
5675
+ }
5676
+ catch (error) {
5677
+ console.error(`[cleanup] tmux teardown failed for ${record.tmuxSessionName}:`, getErrorMessage(error));
5678
+ removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: record.tmuxSessionName, failure: getErrorMessage(error) });
5679
+ }
5680
+ }
5681
+ else {
5682
+ let killed = false;
5683
+ if (record.backendSessionId && terminalSessions.has(record.backendSessionId)) {
5684
+ try {
5685
+ cleanupSession(record.backendSessionId, { killProcess: true });
5686
+ killed = true;
5687
+ }
5688
+ catch (error) {
5689
+ console.error(`[cleanup] pty teardown failed for ${sessionId}:`, getErrorMessage(error));
5690
+ }
5691
+ }
5692
+ removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: null, killed });
5693
+ }
5694
+ }
5695
+ await persistGlobalStateNow();
5696
+ broadcastClientState();
5697
+ res.json({ ok: true, removed, plan });
5698
+ });
5699
+ // Drive a collaboration member's terminal: inject one named key, or dismiss
5700
+ // an interactive approval dialog. Wrapping key injection in our own CLI keeps
5701
+ // every manipulation of another session explicit and auditable — no ad-hoc
5702
+ // tmux calls. Scope mirrors cleanup: only members sharing a group with the
5703
+ // caller, tmux-local only, and `approve` refuses unless an approval dialog is
5704
+ // actually showing (Enter on the highlighted option). The dialogs may be
5705
+ // approving any command the member agent asked for; the caller is presumed to
5706
+ // own that decision (the delivery confirm gate uses the same primitive).
5707
+ const DRIVE_KEYS = new Set(['enter', 'escape', 'space', 'left', 'right', 'up', 'down']);
5708
+ router.post('/operations/orchestration/drive', async (req, res) => {
5709
+ const sourceSessionId = resolveFrontendSessionId(req.body ?? {});
5710
+ if (!sourceSessionId)
5711
+ return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
5712
+ const target = typeof req.body?.session === 'string' ? req.body.session.trim() : '';
5713
+ const action = typeof req.body?.action === 'string' ? req.body.action.trim() : '';
5714
+ if (!target || !action)
5715
+ return res.status(400).json({ error: 'drive 需要目标会话 id 与动作(approve|enter|escape|space|left|right|up|down|capture|run)' });
5716
+ if (target === sourceSessionId)
5717
+ return res.status(400).json({ error: '不能驱动当前会话自身' });
5718
+ const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === target);
5719
+ if (!record)
5720
+ return res.status(404).json({ error: '目标会话不存在(可能已清理或已离线)' });
5721
+ const callerGroups = collaborationStore.groupsForSession(sourceSessionId).map((group) => group.id);
5722
+ if (!collaborationStore.groupsForSession(target).some((group) => callerGroups.includes(group.id))) {
5723
+ return res.status(403).json({ error: '目标会话不在你的协作组中,CLI 无法驱动;如需操作个人会话请在网页端进行' });
5724
+ }
5725
+ if (record.mode !== 'tmux' || !record.tmuxSessionName) {
5726
+ return res.status(409).json({ error: '目标会话不是 tmux 会话,无法从 CLI 驱动(模式:' + (record.mode ?? 'unknown') + ')' });
5727
+ }
5728
+ const binding = collaborationRouting.get(target);
5729
+ const pane = binding?.pane ?? null;
5730
+ if (!pane)
5731
+ return res.status(409).json({ error: '目标会话还没有可驱动的已固定终端面板(agent 可能尚未启动或已离线)' });
5732
+ try {
5733
+ if (action === 'approve') {
5734
+ const approved = await approveCollaborationDialog(runTmux, pane);
5735
+ if (!approved)
5736
+ return res.status(409).json({ ok: false, code: 'NO_APPROVAL_DIALOG', error: '目标面板当前没有显示审批对话框;未发送任何按键' });
5737
+ return res.json({ ok: true, action, sessionId: target, approved: true });
5738
+ }
5739
+ if (action === 'capture') {
5740
+ const snapshot = await captureTmuxPaneText(runTmux, pane);
5741
+ return res.json({ ok: true, action, sessionId: target, snapshot });
5742
+ }
5743
+ if (action === 'run') {
5744
+ const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';
5745
+ if (!text)
5746
+ return res.status(400).json({ error: 'run 需要一行文本(放在请求的 text 字段)' });
5747
+ if (text.length > 10_000)
5748
+ return res.status(400).json({ error: 'run 文本过长(上限 10000 字符);需要更多内容请走 td collab send 投递' });
5749
+ // Same bracketed-paste write path deliveries use: one line, one submit,
5750
+ // embedded newlines stay inside the recipient's editor.
5751
+ await writeCollaborationTmuxPane(runTmux, pane, text);
5752
+ const snapshot = await captureTmuxPaneText(runTmux, pane);
5753
+ return res.json({ ok: true, action, sessionId: target, text, snapshot });
5754
+ }
5755
+ if (!DRIVE_KEYS.has(action)) {
5756
+ return res.status(400).json({ error: `未知动作 ${action};可用:approve|enter|escape|space|left|right|up|down|capture|run` });
5757
+ }
5758
+ await sendTmuxPaneKey(runTmux, pane, action);
5759
+ res.json({ ok: true, action, sessionId: target });
5760
+ }
5761
+ catch (error) {
5762
+ res.status(409).json({ ok: false, code: 'DRIVE_FAILED', error: getErrorMessage(error) });
5763
+ }
5764
+ });
5544
5765
  router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
5545
- deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute }));
5766
+ deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute,
5767
+ resolveNames: (ids) => {
5768
+ const names = new Map([...collaborationRemoteSessions(),
5769
+ ...globalSessionState.sessions.map(orchestrationSessionSnapshot)].map((session) => [session.sessionId, session.name]));
5770
+ return Object.fromEntries(ids.map((id) => [id, names.get(id) ?? null]));
5771
+ },
5772
+ // Same semantics as the inventory PATCH rename: a display name that wins
5773
+ // over auto-titles (customName=true clears autoTitle) and is pushed into
5774
+ // the tmux @termdock-friendly-name option. Names arrive pre-cleaned from
5775
+ // the collaboration route.
5776
+ renameSession: async (frontendSessionId, name) => {
5777
+ const index = globalSessionState.sessions.findIndex((session) => session.sessionId === frontendSessionId);
5778
+ if (index < 0)
5779
+ return { ok: false, code: 'SESSION_NOT_FOUND', error: 'session not found' };
5780
+ const next = { ...globalSessionState.sessions[index], lastActivity: Date.now(),
5781
+ name, customName: true, autoTitle: null };
5782
+ if (next.mode === 'tmux' && next.tmuxSessionName) {
5783
+ try {
5784
+ await setTmuxOption(next.tmuxSessionName, '@termdock-friendly-name', name);
5785
+ }
5786
+ catch (error) {
5787
+ console.warn(`[collab] failed to set friendly name on ${next.tmuxSessionName}: ${getErrorMessage(error)}`);
5788
+ }
5789
+ }
5790
+ upsertGlobalSessionRecord(next);
5791
+ await persistGlobalStateNow();
5792
+ // The broadcast inventory must not be a stale (≤1.5s TTL) cache build:
5793
+ // browsers apply the inventory over the session list, so an old cached
5794
+ // name would keep the sidebar showing the previous title.
5795
+ try {
5796
+ await getSessionInventorySnapshot({ refresh: true });
5797
+ }
5798
+ catch (error) {
5799
+ console.warn('[collab] failed to refresh inventory after rename:', getErrorMessage(error));
5800
+ }
5801
+ broadcastClientState();
5802
+ return { ok: true };
5803
+ } }));
5546
5804
  router.get('/operations/session-search', (req, res) => {
5547
5805
  const query = typeof req.query.q === 'string' ? req.query.q : '';
5548
5806
  const results = sessionSearchStore.search(query, Number(req.query.limit) || 30).map((result) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdock",
3
- "version": "1.4.190",
3
+ "version": "1.4.192",
4
4
  "description": "A complete web-based terminal application with modern UI",
5
5
  "type": "module",
6
6
  "main": ".desktop-dist/main.js",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "termdock",
4
- "version": "1.4.190",
4
+ "version": "1.4.192",
5
5
  "runtimeProtocolVersion": 1,
6
6
  "minimumDesktopVersion": "1.4.46",
7
7
  "nodeMajor": 22,
8
8
  "dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
9
- "serverBundleHash": "sha256-oNkzCv5hSYAirgwQMVMQLGGEhu1GuhpHYUODkc7g0Eg=",
10
- "clientBundleHash": "sha256-YR2GZIYxr5gypWl/+mLDzxUCC12obrg4Gp1mWhDKV8k=",
9
+ "serverBundleHash": "sha256-jIRqCSStnF2/E81Q9GtAEeXAsshmnR4UUDtExjgLex0=",
10
+ "clientBundleHash": "sha256-wpGsT/oJup/G4MyJSKqpHBJlzc2Ha3wFIWmtp4vc/Wc=",
11
11
  "entrypoint": "dist/server/cli.js",
12
12
  "clientEntrypoint": "dist/client/index.html"
13
13
  }