termdock 1.4.178 → 1.4.180

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 (345) hide show
  1. package/.desktop-dist/main.js +1 -1
  2. package/dist/client/assets/App-DBds0RoW.js +150 -0
  3. package/dist/client/assets/App-DBds0RoW.js.br +0 -0
  4. package/dist/client/assets/App-DBds0RoW.js.gz +0 -0
  5. package/dist/client/assets/{ChangeWalkthroughPanel-BNWqAnqc.js → ChangeWalkthroughPanel-DonkfMV2.js} +2 -2
  6. package/dist/client/assets/ChangeWalkthroughPanel-DonkfMV2.js.br +0 -0
  7. package/dist/client/assets/ChangeWalkthroughPanel-DonkfMV2.js.gz +0 -0
  8. package/dist/client/assets/{DagPlayground-IlTGwr-I.js → DagPlayground-CYL8vhaU.js} +1 -1
  9. package/dist/client/assets/DagPlayground-CYL8vhaU.js.br +0 -0
  10. package/dist/client/assets/DagPlayground-CYL8vhaU.js.gz +0 -0
  11. package/dist/client/assets/{DiffLab-CwhNpJvo.js → DiffLab-COUjebir.js} +1 -1
  12. package/dist/client/assets/DiffLab-COUjebir.js.br +0 -0
  13. package/dist/client/assets/DiffLab-COUjebir.js.gz +0 -0
  14. package/dist/client/assets/{DiffReview-DyhINv0F.js → DiffReview-DqiRXomQ.js} +1 -1
  15. package/dist/client/assets/DiffReview-DqiRXomQ.js.br +0 -0
  16. package/dist/client/assets/DiffReview-DqiRXomQ.js.gz +0 -0
  17. package/dist/client/assets/{DiffReviewLab-D9Kt5VwB.js → DiffReviewLab-Aqib8Qr6.js} +1 -1
  18. package/dist/client/assets/DiffReviewLab-Aqib8Qr6.js.br +0 -0
  19. package/dist/client/assets/DiffReviewLab-Aqib8Qr6.js.gz +0 -0
  20. package/dist/client/assets/{DiffViewer-3zz2PI-8.js → DiffViewer-KR2GhX72.js} +1 -1
  21. package/dist/client/assets/DiffViewer-KR2GhX72.js.br +0 -0
  22. package/dist/client/assets/DiffViewer-KR2GhX72.js.gz +0 -0
  23. package/dist/client/assets/{ModelPreview-CN8xsiGC.js → ModelPreview-Bo6giTPm.js} +1 -1
  24. package/dist/client/assets/ModelPreview-Bo6giTPm.js.br +0 -0
  25. package/dist/client/assets/ModelPreview-Bo6giTPm.js.gz +0 -0
  26. package/dist/client/assets/RightSidebar-rROjRuPY.js +1678 -0
  27. package/dist/client/assets/RightSidebar-rROjRuPY.js.br +0 -0
  28. package/dist/client/assets/RightSidebar-rROjRuPY.js.gz +0 -0
  29. package/dist/client/assets/{abnfDiagram-VCTEODGH-DkdoJ2ZH.js → abnfDiagram-VCTEODGH-m85vHRkF.js} +1 -1
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-m85vHRkF.js.br +0 -0
  31. package/dist/client/assets/abnfDiagram-VCTEODGH-m85vHRkF.js.gz +0 -0
  32. package/dist/client/assets/app-Djvvt1Mi.js +34 -0
  33. package/dist/client/assets/app-Djvvt1Mi.js.br +0 -0
  34. package/dist/client/assets/app-Djvvt1Mi.js.gz +0 -0
  35. package/dist/client/assets/{arc-ctzz5PcK.js → arc-DmZ3EVhj.js} +1 -1
  36. package/dist/client/assets/arc-DmZ3EVhj.js.br +0 -0
  37. package/dist/client/assets/arc-DmZ3EVhj.js.gz +0 -0
  38. package/dist/client/assets/{architectureDiagram-5GKGNRK7-DNLcYfGQ.js → architectureDiagram-5GKGNRK7-C8yGaNGM.js} +1 -1
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-C8yGaNGM.js.br +0 -0
  40. package/dist/client/assets/architectureDiagram-5GKGNRK7-C8yGaNGM.js.gz +0 -0
  41. package/dist/client/assets/{blockDiagram-I7D4REHJ-Coye1ou5.js → blockDiagram-I7D4REHJ-CZhh__cR.js} +1 -1
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-CZhh__cR.js.br +0 -0
  43. package/dist/client/assets/blockDiagram-I7D4REHJ-CZhh__cR.js.gz +0 -0
  44. package/dist/client/assets/{c4Diagram-7LVT6UL2-DTa8jyPp.js → c4Diagram-7LVT6UL2-py7O2OOO.js} +1 -1
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-py7O2OOO.js.br +0 -0
  46. package/dist/client/assets/c4Diagram-7LVT6UL2-py7O2OOO.js.gz +0 -0
  47. package/dist/client/assets/channel-BnKOV34C.js +1 -0
  48. package/dist/client/assets/{chunk-2Q5K7J3B-BpDyjNfs.js → chunk-2Q5K7J3B-SXGoQcYW.js} +1 -1
  49. package/dist/client/assets/{chunk-5VM5RSS4-DgwdqXKl.js → chunk-5VM5RSS4-1ZVyPSGv.js} +1 -1
  50. package/dist/client/assets/{chunk-F27PBJKO-DWf2gZ96.js → chunk-F27PBJKO-DkIqnjYj.js} +1 -1
  51. package/dist/client/assets/chunk-F27PBJKO-DkIqnjYj.js.br +0 -0
  52. package/dist/client/assets/chunk-F27PBJKO-DkIqnjYj.js.gz +0 -0
  53. package/dist/client/assets/{chunk-IMKFNOWR-l42o_E2c.js → chunk-IMKFNOWR-CLS_EByq.js} +1 -1
  54. package/dist/client/assets/chunk-IMKFNOWR-CLS_EByq.js.br +0 -0
  55. package/dist/client/assets/chunk-IMKFNOWR-CLS_EByq.js.gz +0 -0
  56. package/dist/client/assets/{chunk-JWPE2WC7-8CkEf05l.js → chunk-JWPE2WC7-8LtK4UYW.js} +1 -1
  57. package/dist/client/assets/{chunk-POPQ4Y6H-BKKOfbdg.js → chunk-POPQ4Y6H-D1PhOQEv.js} +1 -1
  58. package/dist/client/assets/{chunk-SVP7TREG-RORrf2aM.js → chunk-SVP7TREG-cVWIfR_4.js} +1 -1
  59. package/dist/client/assets/chunk-SVP7TREG-cVWIfR_4.js.br +0 -0
  60. package/dist/client/assets/chunk-SVP7TREG-cVWIfR_4.js.gz +0 -0
  61. package/dist/client/assets/{chunk-TICWLB2K-hPqafEtC.js → chunk-TICWLB2K-BiO8AIZ7.js} +1 -1
  62. package/dist/client/assets/chunk-TICWLB2K-BiO8AIZ7.js.br +0 -0
  63. package/dist/client/assets/chunk-TICWLB2K-BiO8AIZ7.js.gz +0 -0
  64. package/dist/client/assets/{chunk-XXDRQBXY-CrvjJwms.js → chunk-XXDRQBXY-CwP0rAj8.js} +1 -1
  65. package/dist/client/assets/classDiagram-ZZMXUADV-CZ5ElqWa.js +1 -0
  66. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CZ5ElqWa.js +1 -0
  67. package/dist/client/assets/{cose-bilkent-JH36ORCC-CwRSiQLa.js → cose-bilkent-JH36ORCC-Dv9GK27H.js} +1 -1
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-Dv9GK27H.js.br +0 -0
  69. package/dist/client/assets/{cose-bilkent-JH36ORCC-CwRSiQLa.js.gz → cose-bilkent-JH36ORCC-Dv9GK27H.js.gz} +0 -0
  70. package/dist/client/assets/{cynefin-OW5HDTMX-CXnppZ8I.js → cynefin-OW5HDTMX-DDI6luT6.js} +1 -1
  71. package/dist/client/assets/cynefin-OW5HDTMX-DDI6luT6.js.br +0 -0
  72. package/dist/client/assets/cynefin-OW5HDTMX-DDI6luT6.js.gz +0 -0
  73. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BSDrHSIA.js → cynefinDiagram-5FMLGOSQ-DQpaBQcE.js} +1 -1
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DQpaBQcE.js.br +0 -0
  75. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DQpaBQcE.js.gz +0 -0
  76. package/dist/client/assets/{dagre-GXQ25YYZ-d-5j4vkq.js → dagre-GXQ25YYZ-CZ-1rzmK.js} +1 -1
  77. package/dist/client/assets/dagre-GXQ25YYZ-CZ-1rzmK.js.br +0 -0
  78. package/dist/client/assets/dagre-GXQ25YYZ-CZ-1rzmK.js.gz +0 -0
  79. package/dist/client/assets/{diagram-S7CK7UJ4-DizMBpGW.js → diagram-S7CK7UJ4-DeYvGv1c.js} +1 -1
  80. package/dist/client/assets/diagram-S7CK7UJ4-DeYvGv1c.js.br +0 -0
  81. package/dist/client/assets/diagram-S7CK7UJ4-DeYvGv1c.js.gz +0 -0
  82. package/dist/client/assets/{diagram-UQ7AKVKN-CNQK26rH.js → diagram-UQ7AKVKN-BaYQC1t6.js} +1 -1
  83. package/dist/client/assets/diagram-UQ7AKVKN-BaYQC1t6.js.br +0 -0
  84. package/dist/client/assets/diagram-UQ7AKVKN-BaYQC1t6.js.gz +0 -0
  85. package/dist/client/assets/{diagram-VSXAHHWV-Dua-2_46.js → diagram-VSXAHHWV-B517BnK0.js} +1 -1
  86. package/dist/client/assets/diagram-VSXAHHWV-B517BnK0.js.br +0 -0
  87. package/dist/client/assets/diagram-VSXAHHWV-B517BnK0.js.gz +0 -0
  88. package/dist/client/assets/{diagram-VX7I27RA-CIq-4729.js → diagram-VX7I27RA-DyjVlDcv.js} +1 -1
  89. package/dist/client/assets/diagram-VX7I27RA-DyjVlDcv.js.br +0 -0
  90. package/dist/client/assets/diagram-VX7I27RA-DyjVlDcv.js.gz +0 -0
  91. package/dist/client/assets/{diagram-Z3DM3KII-BukQ0u-x.js → diagram-Z3DM3KII-Cy9JXHOz.js} +1 -1
  92. package/dist/client/assets/diagram-Z3DM3KII-Cy9JXHOz.js.br +0 -0
  93. package/dist/client/assets/diagram-Z3DM3KII-Cy9JXHOz.js.gz +0 -0
  94. package/dist/client/assets/{ebnfDiagram-PWID7BFC-D2zRkRBI.js → ebnfDiagram-PWID7BFC-xvEH045b.js} +1 -1
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-xvEH045b.js.br +0 -0
  96. package/dist/client/assets/ebnfDiagram-PWID7BFC-xvEH045b.js.gz +0 -0
  97. package/dist/client/assets/{erDiagram-RLTQ6QDP-DbX6A3ls.js → erDiagram-RLTQ6QDP-BTJDgwKq.js} +1 -1
  98. package/dist/client/assets/{erDiagram-RLTQ6QDP-DbX6A3ls.js.br → erDiagram-RLTQ6QDP-BTJDgwKq.js.br} +0 -0
  99. package/dist/client/assets/erDiagram-RLTQ6QDP-BTJDgwKq.js.gz +0 -0
  100. package/dist/client/assets/{flowDiagram-HODETNUW-YECDWA2G.js → flowDiagram-HODETNUW-6pq1SF-S.js} +1 -1
  101. package/dist/client/assets/flowDiagram-HODETNUW-6pq1SF-S.js.br +0 -0
  102. package/dist/client/assets/flowDiagram-HODETNUW-6pq1SF-S.js.gz +0 -0
  103. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-D3EJdUov.js → ganttDiagram-EL5Y4UJY-DS_l5v9m.js} +1 -1
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DS_l5v9m.js.br +0 -0
  105. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DS_l5v9m.js.gz +0 -0
  106. package/dist/client/assets/{gestureArbiter-D3IZpZS7.js → gestureArbiter-FtwbRpdp.js} +1 -1
  107. package/dist/client/assets/gestureArbiter-FtwbRpdp.js.br +0 -0
  108. package/dist/client/assets/gestureArbiter-FtwbRpdp.js.gz +0 -0
  109. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-tr_HKoQK.js → gitGraphDiagram-WWUBYQGX-DEGFo3aa.js} +1 -1
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DEGFo3aa.js.br +0 -0
  111. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DEGFo3aa.js.gz +0 -0
  112. package/dist/client/assets/{index-C4Ny0NDQ.css → index-PWu1VEN_.css} +1 -1
  113. package/dist/client/assets/index-PWu1VEN_.css.br +0 -0
  114. package/dist/client/assets/index-PWu1VEN_.css.gz +0 -0
  115. package/dist/client/assets/{infoDiagram-27XIBGKW-xBV63YYg.js → infoDiagram-27XIBGKW-CFVSyJdD.js} +1 -1
  116. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BrYSZu1R.js → ishikawaDiagram-5VMMS53U-_YX0U_wu.js} +1 -1
  117. package/dist/client/assets/ishikawaDiagram-5VMMS53U-_YX0U_wu.js.br +0 -0
  118. package/dist/client/assets/ishikawaDiagram-5VMMS53U-_YX0U_wu.js.gz +0 -0
  119. package/dist/client/assets/{journeyDiagram-3NMN7TZE-C9Ft--aA.js → journeyDiagram-3NMN7TZE-DCujswss.js} +1 -1
  120. package/dist/client/assets/journeyDiagram-3NMN7TZE-DCujswss.js.br +0 -0
  121. package/dist/client/assets/journeyDiagram-3NMN7TZE-DCujswss.js.gz +0 -0
  122. package/dist/client/assets/{kanban-definition-UXKFOSKX-FCxqSu11.js → kanban-definition-UXKFOSKX-DC6lqIBi.js} +1 -1
  123. package/dist/client/assets/kanban-definition-UXKFOSKX-DC6lqIBi.js.br +0 -0
  124. package/dist/client/assets/kanban-definition-UXKFOSKX-DC6lqIBi.js.gz +0 -0
  125. package/dist/client/assets/{layout-BqjIyPqO.js → layout-D_hlL0MC.js} +1 -1
  126. package/dist/client/assets/layout-D_hlL0MC.js.br +0 -0
  127. package/dist/client/assets/layout-D_hlL0MC.js.gz +0 -0
  128. package/dist/client/assets/{linear-CEAGQX5N.js → linear-BxpjJ2yt.js} +1 -1
  129. package/dist/client/assets/linear-BxpjJ2yt.js.br +0 -0
  130. package/dist/client/assets/linear-BxpjJ2yt.js.gz +0 -0
  131. package/dist/client/assets/{mermaid.core-DvAY78t_.js → mermaid.core-BRLYB1Uj.js} +5 -5
  132. package/dist/client/assets/mermaid.core-BRLYB1Uj.js.br +0 -0
  133. package/dist/client/assets/mermaid.core-BRLYB1Uj.js.gz +0 -0
  134. package/dist/client/assets/{mindmap-definition-YA3MSWOX-D7_qIQ9H.js → mindmap-definition-YA3MSWOX-BF2f-Z54.js} +1 -1
  135. package/dist/client/assets/mindmap-definition-YA3MSWOX-BF2f-Z54.js.br +0 -0
  136. package/dist/client/assets/mindmap-definition-YA3MSWOX-BF2f-Z54.js.gz +0 -0
  137. package/dist/client/assets/{passwordBootstrap-Dksfdvk3.js → passwordBootstrap-hj2Lcgoj.js} +1 -1
  138. package/dist/client/assets/passwordBootstrap-hj2Lcgoj.js.br +0 -0
  139. package/dist/client/assets/passwordBootstrap-hj2Lcgoj.js.gz +0 -0
  140. package/dist/client/assets/{pegDiagram-XKGWAZYB-DeTIBKGC.js → pegDiagram-XKGWAZYB-FxHPBhCy.js} +1 -1
  141. package/dist/client/assets/pegDiagram-XKGWAZYB-FxHPBhCy.js.br +0 -0
  142. package/dist/client/assets/pegDiagram-XKGWAZYB-FxHPBhCy.js.gz +0 -0
  143. package/dist/client/assets/{pieDiagram-E7YTZNPT-z5IcoVwx.js → pieDiagram-E7YTZNPT-DqAcArGH.js} +1 -1
  144. package/dist/client/assets/pieDiagram-E7YTZNPT-DqAcArGH.js.br +0 -0
  145. package/dist/client/assets/pieDiagram-E7YTZNPT-DqAcArGH.js.gz +0 -0
  146. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B-zCZvES.js → quadrantDiagram-AXDQQJYC-31lk30-Y.js} +1 -1
  147. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B-zCZvES.js.br → quadrantDiagram-AXDQQJYC-31lk30-Y.js.br} +0 -0
  148. package/dist/client/assets/quadrantDiagram-AXDQQJYC-31lk30-Y.js.gz +0 -0
  149. package/dist/client/assets/{railroadDiagram-O6MQD6OU-DbpeB7P2.js → railroadDiagram-O6MQD6OU-C-Jt17Wl.js} +1 -1
  150. package/dist/client/assets/railroadDiagram-O6MQD6OU-C-Jt17Wl.js.br +0 -0
  151. package/dist/client/assets/railroadDiagram-O6MQD6OU-C-Jt17Wl.js.gz +0 -0
  152. package/dist/client/assets/{referenceLongPress-p56g9xFb.js → referenceLongPress-BPFSnCDQ.js} +1 -1
  153. package/dist/client/assets/referenceLongPress-BPFSnCDQ.js.br +0 -0
  154. package/dist/client/assets/referenceLongPress-BPFSnCDQ.js.gz +0 -0
  155. package/dist/client/assets/{requirementDiagram-BXWQKSXE-UhJYMJ_U.js → requirementDiagram-BXWQKSXE-DdrbsuCJ.js} +1 -1
  156. package/dist/client/assets/requirementDiagram-BXWQKSXE-DdrbsuCJ.js.br +0 -0
  157. package/dist/client/assets/requirementDiagram-BXWQKSXE-DdrbsuCJ.js.gz +0 -0
  158. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-xKsuFR17.js → sankeyDiagram-P5KCCOFB-CZjs9KFo.js} +1 -1
  159. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZjs9KFo.js.br +0 -0
  160. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZjs9KFo.js.gz +0 -0
  161. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DrjBl65C.js → sequenceDiagram-WJ2MYXX4-CkD1f6v4.js} +1 -1
  162. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CkD1f6v4.js.br +0 -0
  163. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CkD1f6v4.js.gz +0 -0
  164. package/dist/client/assets/{sizeCapture-INFHLROL-BIlGJ9tm.js → sizeCapture-INFHLROL-BMuKV0gL.js} +1 -1
  165. package/dist/client/assets/sizeCapture-INFHLROL-BMuKV0gL.js.br +0 -0
  166. package/dist/client/assets/sizeCapture-INFHLROL-BMuKV0gL.js.gz +0 -0
  167. package/dist/client/assets/{sparkles-B-uEi0Pa.js → sparkles-DuRvUCWG.js} +1 -1
  168. package/dist/client/assets/{stateDiagram-D77RDMKH-C3k9XvA6.js → stateDiagram-D77RDMKH-B1lLo2WA.js} +1 -1
  169. package/dist/client/assets/stateDiagram-D77RDMKH-B1lLo2WA.js.br +0 -0
  170. package/dist/client/assets/stateDiagram-D77RDMKH-B1lLo2WA.js.gz +0 -0
  171. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-F5MbngH8.js +1 -0
  172. package/dist/client/assets/{swimlanes-42K2YHIH-BN-x1rXp.js → swimlanes-42K2YHIH-CfC5CNjA.js} +1 -1
  173. package/dist/client/assets/swimlanes-42K2YHIH-CfC5CNjA.js.br +0 -0
  174. package/dist/client/assets/swimlanes-42K2YHIH-CfC5CNjA.js.gz +0 -0
  175. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BqbZwNvL.js +8 -0
  176. package/dist/client/assets/{timeline-definition-24CTP7MA-Dcedjtic.js → timeline-definition-24CTP7MA-0mM-Skdo.js} +1 -1
  177. package/dist/client/assets/timeline-definition-24CTP7MA-0mM-Skdo.js.br +0 -0
  178. package/dist/client/assets/timeline-definition-24CTP7MA-0mM-Skdo.js.gz +0 -0
  179. package/dist/client/assets/{triangle-alert-BSNAwyll.js → triangle-alert-wyGdnPOg.js} +1 -1
  180. package/dist/client/assets/{vennDiagram-4TSXK5OY-DAIk-5Rt.js → vennDiagram-4TSXK5OY-BYsJo_hU.js} +1 -1
  181. package/dist/client/assets/vennDiagram-4TSXK5OY-BYsJo_hU.js.br +0 -0
  182. package/dist/client/assets/vennDiagram-4TSXK5OY-BYsJo_hU.js.gz +0 -0
  183. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-3iXOTpAg.js → wardleyDiagram-VM6X3IG4-ree3u-pI.js} +1 -1
  184. package/dist/client/assets/wardleyDiagram-VM6X3IG4-ree3u-pI.js.br +0 -0
  185. package/dist/client/assets/wardleyDiagram-VM6X3IG4-ree3u-pI.js.gz +0 -0
  186. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-OD1a_9Qg.js → xychartDiagram-S5SC5T6Z-DDono4Wc.js} +1 -1
  187. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DDono4Wc.js.br +0 -0
  188. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DDono4Wc.js.gz +0 -0
  189. package/dist/client/index.html +2 -2
  190. package/dist/client/index.html.br +0 -0
  191. package/dist/client/index.html.gz +0 -0
  192. package/dist/client/sw-downloads.js +40 -0
  193. package/dist/client/sw-downloads.js.br +0 -0
  194. package/dist/client/sw-downloads.js.gz +0 -0
  195. package/dist/client/sw-encrypted-background.js +25 -0
  196. package/dist/client/sw-encrypted-background.js.br +0 -0
  197. package/dist/client/sw-encrypted-background.js.gz +0 -0
  198. package/dist/client/sw-notifications.js +11 -43
  199. package/dist/client/sw-notifications.js.br +0 -0
  200. package/dist/client/sw-notifications.js.gz +0 -0
  201. package/dist/client/sw.js +1 -1
  202. package/dist/client/sw.js.br +0 -0
  203. package/dist/client/sw.js.gz +0 -0
  204. package/dist/server/agent/collaborationCli.js +10 -3
  205. package/dist/server/agent/collaborationDeliveryWorker.js +154 -0
  206. package/dist/server/agent/collaborationRoutes.js +15 -4
  207. package/dist/server/agent/collaborationRouting.js +94 -0
  208. package/dist/server/agent/collaborationStore.js +6 -1
  209. package/dist/server/agent/collaborationTmuxDelivery.js +10 -0
  210. package/dist/server/agent/sessionBindingRecovery.js +80 -0
  211. package/dist/server/cli.js +9 -5
  212. package/dist/server/entry.js +32 -13
  213. package/dist/server/federation/desktopTargets.js +64 -0
  214. package/dist/server/federation/directRoutes.js +27 -19
  215. package/dist/server/federation/requestContext.js +7 -1
  216. package/dist/server/federation/routeAccess.js +14 -7
  217. package/dist/server/federation/runtime.js +5 -3
  218. package/dist/server/federation/trustedCa.js +55 -0
  219. package/dist/server/notifications/pushService.js +3 -0
  220. package/dist/server/routes/notifications.js +4 -0
  221. package/dist/server/routes/terminal.js +235 -78
  222. package/dist/server/utils/publicSecurity.js +1 -1
  223. package/package.json +2 -1
  224. package/runtime-manifest.json +3 -3
  225. package/dist/client/assets/App-CAEtVL8Z.js +0 -150
  226. package/dist/client/assets/App-CAEtVL8Z.js.br +0 -0
  227. package/dist/client/assets/App-CAEtVL8Z.js.gz +0 -0
  228. package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.br +0 -0
  229. package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.gz +0 -0
  230. package/dist/client/assets/DagPlayground-IlTGwr-I.js.br +0 -0
  231. package/dist/client/assets/DagPlayground-IlTGwr-I.js.gz +0 -0
  232. package/dist/client/assets/DiffLab-CwhNpJvo.js.br +0 -0
  233. package/dist/client/assets/DiffLab-CwhNpJvo.js.gz +0 -0
  234. package/dist/client/assets/DiffReview-DyhINv0F.js.br +0 -0
  235. package/dist/client/assets/DiffReview-DyhINv0F.js.gz +0 -0
  236. package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.br +0 -0
  237. package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.gz +0 -0
  238. package/dist/client/assets/DiffViewer-3zz2PI-8.js.br +0 -0
  239. package/dist/client/assets/DiffViewer-3zz2PI-8.js.gz +0 -0
  240. package/dist/client/assets/ModelPreview-CN8xsiGC.js.br +0 -0
  241. package/dist/client/assets/ModelPreview-CN8xsiGC.js.gz +0 -0
  242. package/dist/client/assets/RightSidebar-DQk1cwKH.js +0 -79
  243. package/dist/client/assets/RightSidebar-DQk1cwKH.js.br +0 -0
  244. package/dist/client/assets/RightSidebar-DQk1cwKH.js.gz +0 -0
  245. package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.br +0 -0
  246. package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.gz +0 -0
  247. package/dist/client/assets/app-B42CoF1r.js +0 -34
  248. package/dist/client/assets/app-B42CoF1r.js.br +0 -0
  249. package/dist/client/assets/app-B42CoF1r.js.gz +0 -0
  250. package/dist/client/assets/arc-ctzz5PcK.js.br +0 -0
  251. package/dist/client/assets/arc-ctzz5PcK.js.gz +0 -0
  252. package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.br +0 -0
  253. package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.gz +0 -0
  254. package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.br +0 -0
  255. package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.gz +0 -0
  256. package/dist/client/assets/c4Diagram-7LVT6UL2-DTa8jyPp.js.br +0 -0
  257. package/dist/client/assets/c4Diagram-7LVT6UL2-DTa8jyPp.js.gz +0 -0
  258. package/dist/client/assets/channel-CzvSDv06.js +0 -1
  259. package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.br +0 -0
  260. package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.gz +0 -0
  261. package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.br +0 -0
  262. package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.gz +0 -0
  263. package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.br +0 -0
  264. package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.gz +0 -0
  265. package/dist/client/assets/chunk-TICWLB2K-hPqafEtC.js.br +0 -0
  266. package/dist/client/assets/chunk-TICWLB2K-hPqafEtC.js.gz +0 -0
  267. package/dist/client/assets/classDiagram-ZZMXUADV-CDknJfE0.js +0 -1
  268. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CDknJfE0.js +0 -1
  269. package/dist/client/assets/cose-bilkent-JH36ORCC-CwRSiQLa.js.br +0 -0
  270. package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.br +0 -0
  271. package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.gz +0 -0
  272. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.br +0 -0
  273. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.gz +0 -0
  274. package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.br +0 -0
  275. package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.gz +0 -0
  276. package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.br +0 -0
  277. package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.gz +0 -0
  278. package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.br +0 -0
  279. package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.gz +0 -0
  280. package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.br +0 -0
  281. package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.gz +0 -0
  282. package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.br +0 -0
  283. package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.gz +0 -0
  284. package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.br +0 -0
  285. package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.gz +0 -0
  286. package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.br +0 -0
  287. package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.gz +0 -0
  288. package/dist/client/assets/erDiagram-RLTQ6QDP-DbX6A3ls.js.gz +0 -0
  289. package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.br +0 -0
  290. package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.gz +0 -0
  291. package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.br +0 -0
  292. package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.gz +0 -0
  293. package/dist/client/assets/gestureArbiter-D3IZpZS7.js.br +0 -0
  294. package/dist/client/assets/gestureArbiter-D3IZpZS7.js.gz +0 -0
  295. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.br +0 -0
  296. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.gz +0 -0
  297. package/dist/client/assets/index-C4Ny0NDQ.css.br +0 -0
  298. package/dist/client/assets/index-C4Ny0NDQ.css.gz +0 -0
  299. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.br +0 -0
  300. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.gz +0 -0
  301. package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.br +0 -0
  302. package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.gz +0 -0
  303. package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.br +0 -0
  304. package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.gz +0 -0
  305. package/dist/client/assets/layout-BqjIyPqO.js.br +0 -0
  306. package/dist/client/assets/layout-BqjIyPqO.js.gz +0 -0
  307. package/dist/client/assets/linear-CEAGQX5N.js.br +0 -0
  308. package/dist/client/assets/linear-CEAGQX5N.js.gz +0 -0
  309. package/dist/client/assets/mermaid.core-DvAY78t_.js.br +0 -0
  310. package/dist/client/assets/mermaid.core-DvAY78t_.js.gz +0 -0
  311. package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.br +0 -0
  312. package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.gz +0 -0
  313. package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.br +0 -0
  314. package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.gz +0 -0
  315. package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.br +0 -0
  316. package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.gz +0 -0
  317. package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.br +0 -0
  318. package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.gz +0 -0
  319. package/dist/client/assets/quadrantDiagram-AXDQQJYC-B-zCZvES.js.gz +0 -0
  320. package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.br +0 -0
  321. package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.gz +0 -0
  322. package/dist/client/assets/referenceLongPress-p56g9xFb.js.br +0 -0
  323. package/dist/client/assets/referenceLongPress-p56g9xFb.js.gz +0 -0
  324. package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.br +0 -0
  325. package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.gz +0 -0
  326. package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.br +0 -0
  327. package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.gz +0 -0
  328. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.br +0 -0
  329. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.gz +0 -0
  330. package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.br +0 -0
  331. package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.gz +0 -0
  332. package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.br +0 -0
  333. package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.gz +0 -0
  334. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-B7GnCu2o.js +0 -1
  335. package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.br +0 -0
  336. package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.gz +0 -0
  337. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BH_gm34Y.js +0 -8
  338. package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.br +0 -0
  339. package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.gz +0 -0
  340. package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.br +0 -0
  341. package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.gz +0 -0
  342. package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.br +0 -0
  343. package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.gz +0 -0
  344. package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.br +0 -0
  345. package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.gz +0 -0
@@ -17,6 +17,7 @@ self.addEventListener('push', (event) => {
17
17
  data: {
18
18
  url: payload.url || '/',
19
19
  sessionId: payload.sessionId,
20
+ targetPeerId: payload.targetPeerId,
20
21
  },
21
22
  };
22
23
  event.waitUntil((async () => {
@@ -60,35 +61,13 @@ self.addEventListener('push', (event) => {
60
61
  self.addEventListener('pushsubscriptionchange', (event) => {
61
62
  event.waitUntil((async () => {
62
63
  try {
63
- const statusResponse = await fetch('/api/notifications/status', { credentials: 'same-origin' });
64
- if (statusResponse.ok) {
65
- const status = await statusResponse.json();
66
- const subscription = event.newSubscription || await self.registration.pushManager.getSubscription();
67
- if (subscription) {
68
- const tokenResponse = await fetch('/api/csrf-token', { credentials: 'same-origin' });
69
- if (tokenResponse.ok) {
70
- const { csrfToken } = await tokenResponse.json();
71
- await fetch('/api/notifications/subscribe', {
72
- method: 'POST',
73
- credentials: 'same-origin',
74
- headers: {
75
- 'Content-Type': 'application/json',
76
- 'X-XSRF-TOKEN': csrfToken,
77
- },
78
- body: JSON.stringify({
79
- subscription: subscription.toJSON(),
80
- aiEnabled: status.subscription?.aiEnabled !== false,
81
- exitEnabled: status.subscription?.exitEnabled === true,
82
- alertStyle: status.subscription?.alertStyle || 'normal',
83
- locale: status.subscription?.locale || 'zh-CN',
84
- }),
85
- });
86
- }
87
- }
64
+ let subscription = event.newSubscription || await self.registration.pushManager.getSubscription();
65
+ if (!subscription && event.oldSubscription?.options) {
66
+ subscription = await self.registration.pushManager.subscribe(event.oldSubscription.options);
88
67
  }
89
- } catch {
90
- // App foreground reconciliation is the fallback for browsers that do not
91
- // permit re-subscription inside pushsubscriptionchange.
68
+ if (subscription) await self.termdockBackground?.renewSubscriptions(subscription.toJSON());
69
+ } catch (error) {
70
+ console.warn('Background encrypted subscription sync failed', error);
92
71
  }
93
72
  const windows = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
94
73
  for (const client of windows) {
@@ -98,20 +77,8 @@ self.addEventListener('pushsubscriptionchange', (event) => {
98
77
  });
99
78
 
100
79
  async function reportNotificationClick(stage, traceId, data = {}) {
101
- try {
102
- await fetch('/api/client-log', {
103
- method: 'POST',
104
- credentials: 'same-origin',
105
- headers: { 'Content-Type': 'application/json' },
106
- body: JSON.stringify({
107
- level: 'info',
108
- message: `PWA_NOTIFICATION_CLICK ${stage}`,
109
- data: { traceId, ts: Date.now(), ...data },
110
- }),
111
- });
112
- } catch {
113
- // Diagnostics must never interfere with notification navigation.
114
- }
80
+ try { await self.termdockBackground?.reportClick(stage, traceId, data); }
81
+ catch { console.debug(`PWA_NOTIFICATION_CLICK ${stage}`, { traceId, ...data }); }
115
82
  }
116
83
 
117
84
  const NOTIFICATION_TARGET_CACHE = 'termdock-notification-target-v1';
@@ -176,6 +143,7 @@ self.addEventListener('notificationclick', (event) => {
176
143
 
177
144
  event.waitUntil((async () => {
178
145
  await reportNotificationClick('sw-click-start', traceId, {
146
+ targetPeerId: data.targetPeerId,
179
147
  sessionId: data.sessionId || null,
180
148
  url: data.url || null,
181
149
  tag: event.notification.tag || null,
@@ -254,5 +222,5 @@ self.addEventListener('notificationclick', (event) => {
254
222
  resultUrl: openedClient?.url || null,
255
223
  });
256
224
  }
257
- })());
225
+ })().finally(() => self.termdockBackground?.flushLogs().catch(() => {})));
258
226
  });
Binary file
Binary file
package/dist/client/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,a)=>{const c=e||("document"in self?document.currentScript.src:"")||location.href;if(s[c])return;let o={};const r=e=>i(e,c),t={module:{uri:c},exports:o,require:r};s[c]=Promise.all(n.map(e=>t[e]||r(e))).then(e=>(a(...e),o))}}define(["./workbox-9845bf53"],function(e){"use strict";importScripts("sw-notifications.js","sw-secure-api.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"ca66dd41431972ae5897439615539f92"},{url:"assets/app-B42CoF1r.js",revision:null},{url:"assets/react-XDCjal2T.js",revision:null},{url:"assets/dnd-BCFAPC0L.js",revision:null},{url:"assets/terminal-C7X98Amf.js",revision:null},{url:"assets/index-C4Ny0NDQ.css",revision:null},{url:"icons/agents/opencode.svg",revision:"851c2931d95821ba2b39b68587b48976"},{url:"icons/agents/kimi.svg",revision:"5366c710f4f763022fe159a69cf17409"},{url:"icons/agents/grok.svg",revision:"cd745b0b964545bc7a6b4f36021c3298"},{url:"icons/agents/goose.svg",revision:"c79b927278983c19098d77b4b0c11ab3"},{url:"icons/agents/gemini.svg",revision:"7a64f6d6f80a908255e9c0256903882e"},{url:"icons/agents/droid.svg",revision:"f28723b8c5c115e0908ba3c72613e256"},{url:"icons/agents/cursor.svg",revision:"a1528f3d3cda497f75a5db28defb19de"},{url:"icons/agents/copilot.svg",revision:"dd52e96c1eac0b6ec4bedcd0ddd3b52d"},{url:"icons/agents/codex.svg",revision:"608e55babb0b217e1f2da287342b46d3"},{url:"icons/agents/claude.svg",revision:"08af0225b7acdd8e8f573f3247e5e1c3"},{url:"icons/agents/amp.svg",revision:"af12f8b33faa81e4d46ea7b93c7e43f3"},{url:"apple-touch-icon-180x180.png",revision:"628c2e2f28a9b545aa57be1352025643"},{url:"favicon.ico",revision:"2994debfa6f59a0898e24eb0c54d64b5"},{url:"maskable-icon-512x512.png",revision:"4d489331fd81f0713c400b26426442b9"},{url:"pwa-192x192.png",revision:"4d6f899888cfb8ccd7a1fd3c9e583ad2"},{url:"pwa-512x512.png",revision:"0885dd7ec8932e7f878b013d51a380a4"},{url:"robots.txt",revision:"d9339907142917dee4aac78494ac9cfe"},{url:"sw-notifications.js",revision:"051f4ad0b9931c67d8094c2db255ac34"},{url:"sw-secure-api.js",revision:"739c54d3fc8f4cbb87bab12b6b8a8160"},{url:"manifest.webmanifest",revision:"83f9a43b74c236012adafb077fb6d774"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"),{denylist:[/^\/api\//,/^\/health$/]})),e.registerRoute(/\/assets\/.*\.(?:js|css)$/,new e.CacheFirst({cacheName:"termdock-lazy-assets-v1",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:96,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/terminal\/[^/]+-[a-f0-9]{12}\.woff2$/,new e.CacheFirst({cacheName:"termdock-fonts-v2",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:384,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/.*\.(?:woff2|ttf)$/,new e.StaleWhileRevalidate({cacheName:"termdock-fonts-v1",plugins:[new e.ExpirationPlugin({maxEntries:24,maxAgeSeconds:2592e3})]}),"GET")});
1
+ if(!self.define){let e,s={};const n=(n,i)=>(n=new URL(n+".js",i).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(i,a)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let c={};const r=e=>n(e,o),t={module:{uri:o},exports:c,require:r};s[o]=Promise.all(i.map(e=>t[e]||r(e))).then(e=>(a(...e),c))}}define(["./workbox-9845bf53"],function(e){"use strict";importScripts("sw-encrypted-background.js","sw-notifications.js","sw-secure-api.js","sw-downloads.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"1d87402f5f09e34eb3f563b011b5205f"},{url:"assets/app-Djvvt1Mi.js",revision:null},{url:"assets/react-XDCjal2T.js",revision:null},{url:"assets/dnd-BCFAPC0L.js",revision:null},{url:"assets/terminal-C7X98Amf.js",revision:null},{url:"assets/index-PWu1VEN_.css",revision:null},{url:"icons/agents/opencode.svg",revision:"851c2931d95821ba2b39b68587b48976"},{url:"icons/agents/kimi.svg",revision:"5366c710f4f763022fe159a69cf17409"},{url:"icons/agents/grok.svg",revision:"cd745b0b964545bc7a6b4f36021c3298"},{url:"icons/agents/goose.svg",revision:"c79b927278983c19098d77b4b0c11ab3"},{url:"icons/agents/gemini.svg",revision:"7a64f6d6f80a908255e9c0256903882e"},{url:"icons/agents/droid.svg",revision:"f28723b8c5c115e0908ba3c72613e256"},{url:"icons/agents/cursor.svg",revision:"a1528f3d3cda497f75a5db28defb19de"},{url:"icons/agents/copilot.svg",revision:"dd52e96c1eac0b6ec4bedcd0ddd3b52d"},{url:"icons/agents/codex.svg",revision:"608e55babb0b217e1f2da287342b46d3"},{url:"icons/agents/claude.svg",revision:"08af0225b7acdd8e8f573f3247e5e1c3"},{url:"icons/agents/amp.svg",revision:"af12f8b33faa81e4d46ea7b93c7e43f3"},{url:"apple-touch-icon-180x180.png",revision:"628c2e2f28a9b545aa57be1352025643"},{url:"favicon.ico",revision:"2994debfa6f59a0898e24eb0c54d64b5"},{url:"maskable-icon-512x512.png",revision:"4d489331fd81f0713c400b26426442b9"},{url:"pwa-192x192.png",revision:"4d6f899888cfb8ccd7a1fd3c9e583ad2"},{url:"pwa-512x512.png",revision:"0885dd7ec8932e7f878b013d51a380a4"},{url:"robots.txt",revision:"d9339907142917dee4aac78494ac9cfe"},{url:"sw-downloads.js",revision:"29b4d23945d109a334e1c3902a8cf02a"},{url:"sw-notifications.js",revision:"a5a2b585a4336d66519195e5e63aee2d"},{url:"sw-secure-api.js",revision:"739c54d3fc8f4cbb87bab12b6b8a8160"},{url:"manifest.webmanifest",revision:"83f9a43b74c236012adafb077fb6d774"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"),{denylist:[/^\/api\//,/^\/__termdock-download\//,/^\/health$/]})),e.registerRoute(/\/assets\/.*\.(?:js|css)$/,new e.CacheFirst({cacheName:"termdock-lazy-assets-v1",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:96,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/terminal\/[^/]+-[a-f0-9]{12}\.woff2$/,new e.CacheFirst({cacheName:"termdock-fonts-v2",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:384,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/.*\.(?:woff2|ttf)$/,new e.StaleWhileRevalidate({cacheName:"termdock-fonts-v1",plugins:[new e.ExpirationPlugin({maxEntries:24,maxAgeSeconds:2592e3})]}),"GET")});
Binary file
Binary file
@@ -3,6 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  import { COLLAB_LIMITS, CollaborationError } from './collaborationProtocol.js';
4
4
  export const COLLAB_HELP = `td collab — durable messages; no agent-specific hooks required
5
5
  status | capabilities
6
+ rebind [--pane %3] (explicitly bind this peer to its current Agent; resumes queued delivery)
6
7
  send <session-id> <message> | reply <message-id> <message> | handoff <session-id> <message>
7
8
  --group <id> --thread <id> --idempotency-key <key>
8
9
  --file <path> | --stdin (instead of inline body; -- ends option parsing)
@@ -29,7 +30,7 @@ Idempotency retention: 7 days. read/ACK/result never imply each other.
29
30
  Inbox defaults to unread first, newest 50; cursor/consumer mode reads oldest unseen first.
30
31
  Reading never advances a consumer or marks messages read automatically.`;
31
32
  const BOOLEAN_OPTIONS = new Set(['json', 'jsonl', 'text', 'unread', 'follow', 'stdin', 'receipt-only', 'help']);
32
- const VALUE_OPTIONS = new Set(['group', 'thread', 'idempotency-key', 'file', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'kind', 'name', 'cwd', 'task']);
33
+ const VALUE_OPTIONS = new Set(['group', 'thread', 'idempotency-key', 'file', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'kind', 'name', 'cwd', 'task', 'pane']);
33
34
  export function parseCollaborationCommand(argv) {
34
35
  const options = {};
35
36
  const positional = [];
@@ -60,8 +61,10 @@ export function parseCollaborationCommand(argv) {
60
61
  positional.push(value);
61
62
  }
62
63
  const action = (options.help ? 'help' : positional.shift() ?? 'status');
63
- if (!['status', 'inbox', 'send', 'handoff', 'reply', 'add', 'remove', 'spawn', 'message', 'cursor', 'capabilities', 'help'].includes(action))
64
+ if (!['status', 'inbox', 'send', 'handoff', 'reply', 'add', 'remove', 'spawn', 'message', 'cursor', 'rebind', 'capabilities', 'help'].includes(action))
64
65
  throw new Error('Unknown collaboration command; see td collab --help');
66
+ if (options.pane && !/^%\d+$/.test(String(options.pane)))
67
+ throw new Error('pane must be a tmux pane id such as %3');
65
68
  if (['json', 'jsonl', 'text'].filter((key) => options[key]).length > 1)
66
69
  throw new Error('Choose one output format');
67
70
  if (options['wait-until'] && !['queued', 'delivered', 'read'].includes(String(options['wait-until'])))
@@ -107,7 +110,7 @@ export function parseCollaborationCommand(argv) {
107
110
  throw new Error(`Unexpected arguments for ${action}`);
108
111
  const allowed = new Set(['json', 'jsonl', 'text', 'help']);
109
112
  const byAction = {
110
- status: [], capabilities: [], help: [...BOOLEAN_OPTIONS, ...VALUE_OPTIONS],
113
+ status: [], capabilities: [], rebind: ['pane'], help: [...BOOLEAN_OPTIONS, ...VALUE_OPTIONS],
111
114
  send: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at', 'kind'],
112
115
  handoff: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at'],
113
116
  reply: ['idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at'],
@@ -187,6 +190,10 @@ export async function executeCollaborationCommand(command, context, io) {
187
190
  output(await request('GET', command.action === 'status' ? '/peers' : '/capabilities'));
188
191
  return 0;
189
192
  }
193
+ if (command.action === 'rebind') {
194
+ output(await request('POST', '/route/rebind', { pane: o.pane ?? null }));
195
+ return 0;
196
+ }
190
197
  if (command.action === 'inbox') {
191
198
  let cursor = o.cursor;
192
199
  do {
@@ -0,0 +1,154 @@
1
+ export class CollaborationDeliveryWorker {
2
+ options;
3
+ running = new Map();
4
+ states = new Map();
5
+ failures = new Map();
6
+ submitted = new Set();
7
+ timer = null;
8
+ ticking = null;
9
+ constructor(options) {
10
+ this.options = options;
11
+ }
12
+ start() {
13
+ if (this.timer)
14
+ return;
15
+ this.timer = setInterval(() => { void this.tick(); }, 2_000);
16
+ this.timer.unref?.();
17
+ void this.tick();
18
+ }
19
+ stop() {
20
+ if (this.timer)
21
+ clearInterval(this.timer);
22
+ this.timer = null;
23
+ }
24
+ state(id) {
25
+ const state = this.states.get(id);
26
+ return state && Date.now() - state.checkedAt < 15_000 ? state
27
+ : { state: 'recovering', reason: 'ROUTE_NOT_CHECKED', checkedAt: state?.checkedAt ?? null };
28
+ }
29
+ wake(id) {
30
+ void this.run(id).catch(this.options.onError);
31
+ }
32
+ tick() {
33
+ if (this.ticking)
34
+ return this.ticking;
35
+ this.ticking = this.tickOnce().finally(() => { this.ticking = null; });
36
+ return this.ticking;
37
+ }
38
+ async tickOnce() {
39
+ try {
40
+ const ids = [...new Set([...this.options.peers(), ...this.options.store.pendingRecipients()])].filter(this.options.isLocal);
41
+ // Bound process probes/attachments while keeping a broken peer from
42
+ // serially blocking the entire queue.
43
+ let cursor = 0;
44
+ await Promise.all(Array.from({ length: Math.min(4, ids.length) }, async () => {
45
+ while (cursor < ids.length)
46
+ await this.run(ids[cursor++]).catch(this.options.onError);
47
+ }));
48
+ }
49
+ catch (error) {
50
+ this.options.onError(error);
51
+ }
52
+ }
53
+ run(id) {
54
+ const running = this.running.get(id);
55
+ if (running)
56
+ return running;
57
+ if (!this.options.isLocal(id))
58
+ return Promise.resolve();
59
+ const operation = Promise.resolve().then(() => this.deliver(id)).finally(() => {
60
+ if (this.running.get(id) === operation)
61
+ this.running.delete(id);
62
+ });
63
+ this.running.set(id, operation);
64
+ return operation;
65
+ }
66
+ reconfigure(id, configure) {
67
+ const previous = this.running.get(id) ?? Promise.resolve();
68
+ const operation = previous.catch(() => undefined).then(async () => {
69
+ await configure();
70
+ this.failures.delete(id);
71
+ this.states.delete(id);
72
+ for (const message of this.options.store.inbox(id, { pendingOnly: true, limit: 200 })) {
73
+ const diagnostic = this.options.store.diagnostic(message.id);
74
+ if (diagnostic)
75
+ this.options.store.recordTransport(message.id, { ...diagnostic, next_retry_at: null, last_error: null });
76
+ }
77
+ }).finally(() => {
78
+ if (this.running.get(id) === operation)
79
+ this.running.delete(id);
80
+ });
81
+ this.running.set(id, operation);
82
+ return operation;
83
+ }
84
+ async deliver(id) {
85
+ const { store } = this.options;
86
+ const first = store.inbox(id, { pendingOnly: true, limit: 1 })[0];
87
+ if (!first && Date.now() - (this.states.get(id)?.checkedAt ?? 0) < 5_000)
88
+ return;
89
+ if (first && this.submitted.has(first.id)) {
90
+ this.complete(id, first);
91
+ return;
92
+ }
93
+ if (first && (store.diagnostic(first.id)?.next_retry_at ?? 0) > Date.now())
94
+ return;
95
+ this.states.set(id, { state: 'recovering', reason: null, checkedAt: Date.now() });
96
+ let route;
97
+ try {
98
+ route = await this.options.resolve(id);
99
+ }
100
+ catch (error) {
101
+ route = { state: 'unavailable', reason: `ROUTE_RECOVERY_FAILED: ${error instanceof Error ? error.message : String(error)}` };
102
+ }
103
+ this.states.set(id, { state: route.state, reason: route.reason ?? null, checkedAt: Date.now() });
104
+ // Re-read after asynchronous recovery: a consumer may have read the
105
+ // message, or its TTL may have elapsed while tmux was being inspected.
106
+ const pending = store.inbox(id, { pendingOnly: true, limit: 1 });
107
+ if (!pending.length)
108
+ return;
109
+ if (route.state !== 'ready' || !route.write) {
110
+ this.retry(id, pending, route.reason ?? route.state, false);
111
+ return;
112
+ }
113
+ const message = pending[0];
114
+ if (!this.submitted.has(message.id)) {
115
+ store.recordTransport(message.id, {
116
+ relay_online: null, peer_reachable: true,
117
+ attempt_count: (store.diagnostic(message.id)?.attempt_count ?? 0) + 1,
118
+ next_retry_at: Date.now() + 2_000, last_error: 'DELIVERY_IN_PROGRESS', checked_at: Date.now(),
119
+ });
120
+ try {
121
+ await route.write(pending);
122
+ }
123
+ catch (error) {
124
+ this.retry(id, pending, `TERMINAL_WRITE_FAILED: ${error instanceof Error ? error.message : String(error)}`, true);
125
+ return;
126
+ }
127
+ this.submitted.add(message.id);
128
+ }
129
+ // If persistence fails after writing, the in-process guard avoids a
130
+ // second write on retry. Across a crash, transport is at-least-once.
131
+ this.complete(id, message);
132
+ }
133
+ complete(id, message) {
134
+ const { store } = this.options;
135
+ store.markDelivered([message.id]);
136
+ this.submitted.delete(message.id);
137
+ store.recordTransport(message.id, {
138
+ relay_online: null, peer_reachable: true, attempt_count: store.diagnostic(message.id)?.attempt_count ?? 1,
139
+ next_retry_at: null, last_error: null, checked_at: Date.now(),
140
+ });
141
+ this.failures.delete(id);
142
+ }
143
+ retry(id, messages, reason, reachable) {
144
+ const failures = (this.failures.get(id) ?? 0) + 1;
145
+ this.failures.set(id, failures);
146
+ const next = Date.now() + Math.min(30_000, 2_000 * 2 ** Math.min(failures - 1, 4));
147
+ for (const message of messages)
148
+ this.options.store.recordTransport(message.id, {
149
+ relay_online: null, peer_reachable: reachable,
150
+ attempt_count: this.options.store.diagnostic(message.id)?.attempt_count ?? 0,
151
+ next_retry_at: next, last_error: reason, checked_at: Date.now(),
152
+ });
153
+ }
154
+ }
@@ -1,19 +1,28 @@
1
1
  import { Router } from 'express';
2
2
  import { COLLAB_LIMITS, CollaborationError, extrasFromBody } from './collaborationProtocol.js';
3
- export function collaborationRoutes({ store, resolveSession, deliver }) {
3
+ export function collaborationRoutes({ store, resolveSession, deliver, rebind }) {
4
4
  const router = Router();
5
- const run = (handler) => (req, res) => {
5
+ const run = (handler) => async (req, res) => {
6
6
  try {
7
7
  const sessionId = resolveSession((req.method === 'GET' ? req.query : req.body) ?? {});
8
8
  if (!sessionId)
9
9
  throw new CollaborationError('SESSION_NOT_FOUND', 'Run inside a Termdock managed session', 404);
10
- handler(req, res, sessionId);
10
+ await handler(req, res, sessionId);
11
11
  }
12
12
  catch (error) {
13
13
  res.status(error instanceof CollaborationError ? error.httpStatus : 400).json({ ok: false,
14
14
  code: error instanceof CollaborationError ? error.code : 'COLLABORATION_ERROR', error: error instanceof Error ? error.message : String(error) });
15
15
  }
16
16
  };
17
+ router.post('/route/rebind', run(async (req, res, sessionId) => {
18
+ if (!rebind)
19
+ throw new CollaborationError('ROUTE_REBIND_UNAVAILABLE', 'Route rebinding is not available');
20
+ const pane = req.body.pane;
21
+ if (pane !== undefined && pane !== null && (typeof pane !== 'string' || !/^%\d+$/.test(pane))) {
22
+ throw new CollaborationError('INVALID_PANE', 'pane must be a tmux pane id such as %3');
23
+ }
24
+ res.json({ ok: true, route: await rebind(sessionId, pane ?? null) });
25
+ }));
17
26
  const ownMessage = (id, sessionId, recipientOnly = false) => {
18
27
  const message = store.getMessage(id);
19
28
  if (!message || (message.toSessionId !== sessionId && (recipientOnly || message.fromSessionId !== sessionId)))
@@ -27,11 +36,13 @@ export function collaborationRoutes({ store, resolveSession, deliver }) {
27
36
  };
28
37
  router.get('/capabilities', run((_req, res) => {
29
38
  res.json({ protocol_version: 2, limits: COLLAB_LIMITS,
39
+ routing: { background_recovery: true, explicit_rebind: Boolean(rebind), fixed_tmux_pane: true },
30
40
  statuses: ['pending', 'delivered', 'read', 'failed', 'expired'], queued_status: 'pending',
31
41
  semantics: { delivered: 'written to terminal; no application acknowledgement implied', read: 'explicit consumer acknowledgement',
32
42
  ack: 'explicit response_kind=ack', result: 'explicit response_kind=result; inspect task.status and evidence',
33
43
  done: 'adapter reports turn ended, never proof of task completion', heartbeat: 'null unless explicitly observed',
34
- timeout: 'stops waiting, does not cancel delivery' } });
44
+ timeout: 'stops waiting, does not cancel delivery',
45
+ retry: 'same message id may be submitted again after a crash or uncertain transport result; consumers deduplicate by message id' } });
35
46
  }));
36
47
  router.post('/send', run((req, res, sessionId) => {
37
48
  const target = typeof req.body.targetSessionId === 'string' ? req.body.targetSessionId.trim() : '';
@@ -0,0 +1,94 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export class CollaborationRoutingStore {
4
+ file;
5
+ bindings = new Map();
6
+ loadError = null;
7
+ constructor(file) {
8
+ this.file = file;
9
+ try {
10
+ const data = JSON.parse(fs.readFileSync(file, 'utf8'));
11
+ if (data.version !== 1 || !Array.isArray(data.bindings))
12
+ throw new Error('Invalid collaboration routing store');
13
+ for (const binding of data.bindings) {
14
+ if (!binding || typeof binding.sessionId !== 'string' || !['shell', 'tmux'].includes(binding.mode)
15
+ || ![binding.backendSessionId, binding.tmuxSessionName, binding.agentSlug, binding.nativeSessionId].every((value) => value === null || typeof value === 'string')
16
+ || (binding.pane !== null && (!binding.pane || !Number.isInteger(binding.pane.serverPid) || !Number.isInteger(binding.pane.panePid)
17
+ || !/^%\d+$/.test(binding.pane.paneId) || !/^\$\d+$/.test(binding.pane.sessionId) || typeof binding.pane.agentSlug !== 'string'))) {
18
+ throw new Error('Invalid collaboration binding');
19
+ }
20
+ this.bindings.set(binding.sessionId, binding);
21
+ }
22
+ }
23
+ catch (error) {
24
+ if (error.code !== 'ENOENT') {
25
+ this.bindings.clear();
26
+ this.loadError = error;
27
+ }
28
+ }
29
+ }
30
+ get(id) {
31
+ const binding = this.bindings.get(id);
32
+ return binding ? structuredClone(binding) : null;
33
+ }
34
+ assertReadable() {
35
+ if (this.loadError)
36
+ throw new Error('COLLABORATION_ROUTING_STORE_UNREADABLE', { cause: this.loadError });
37
+ }
38
+ ownerOfBackend(id) {
39
+ return [...this.bindings.values()].find((binding) => binding.backendSessionId === id)?.sessionId ?? null;
40
+ }
41
+ bind(binding) {
42
+ this.assertReadable();
43
+ const owner = binding.backendSessionId ? this.ownerOfBackend(binding.backendSessionId) : null;
44
+ if (owner && owner !== binding.sessionId)
45
+ throw new Error('BACKEND_ALREADY_BOUND');
46
+ if (JSON.stringify(this.bindings.get(binding.sessionId)) === JSON.stringify(binding))
47
+ return;
48
+ const next = new Map(this.bindings);
49
+ next.set(binding.sessionId, structuredClone(binding));
50
+ this.persist(next);
51
+ }
52
+ remove(id) {
53
+ if (!this.bindings.has(id))
54
+ return;
55
+ const next = new Map(this.bindings);
56
+ next.delete(id);
57
+ this.persist(next);
58
+ }
59
+ persist(next) {
60
+ this.assertReadable();
61
+ fs.mkdirSync(path.dirname(this.file), { recursive: true });
62
+ const temporary = `${this.file}.${process.pid}.tmp`;
63
+ fs.writeFileSync(temporary, JSON.stringify({ version: 1, bindings: [...next.values()] }), { mode: 0o600 });
64
+ fs.renameSync(temporary, this.file);
65
+ this.bindings = next;
66
+ }
67
+ }
68
+ // Once pinned, changing the active pane cannot change the recipient. A reused
69
+ // tmux name/pane after a server restart must not inherit an old peer's inbox.
70
+ export function selectCollaborationPane(binding, panes) {
71
+ if (binding.pane) {
72
+ const previous = binding.pane;
73
+ const pane = panes.find((candidate) => candidate.serverPid === previous.serverPid
74
+ && candidate.sessionId === previous.sessionId && candidate.paneId === previous.paneId && candidate.panePid === previous.panePid);
75
+ if (!pane)
76
+ return { state: 'identity-mismatch', reason: 'TMUX_PANE_CHANGED' };
77
+ if (!pane.agentSlug)
78
+ return { state: 'agent-exited', reason: 'AGENT_NOT_RUNNING' };
79
+ if (pane.agentSlug !== previous.agentSlug || (previous.nativeSessionId && pane.nativeSessionId && previous.nativeSessionId !== pane.nativeSessionId)) {
80
+ return { state: 'identity-mismatch', reason: 'AGENT_IDENTITY_CHANGED' };
81
+ }
82
+ return { state: 'ready', pane };
83
+ }
84
+ const candidates = panes.filter((pane) => pane.agentSlug && (!binding.agentSlug || binding.agentSlug === pane.agentSlug)
85
+ && (!binding.nativeSessionId || !pane.nativeSessionId || binding.nativeSessionId === pane.nativeSessionId));
86
+ const exact = binding.nativeSessionId ? candidates.filter((pane) => pane.nativeSessionId === binding.nativeSessionId) : [];
87
+ const matches = exact.length ? exact : candidates;
88
+ if (matches.length === 1)
89
+ return { state: 'ready', pane: matches[0] };
90
+ if (!matches.length && panes.some((pane) => pane.agentSlug))
91
+ return { state: 'identity-mismatch', reason: 'AGENT_IDENTITY_CHANGED' };
92
+ return matches.length > 1 ? { state: 'ambiguous', reason: 'MULTIPLE_AGENT_PANES' }
93
+ : { state: 'agent-exited', reason: 'AGENT_NOT_RUNNING_OR_IDENTITY_CHANGED' };
94
+ }
@@ -160,6 +160,10 @@ export class CollaborationStore {
160
160
  this.expire();
161
161
  return this.document.messages.filter((message) => message.toSessionId === sessionId && message.status === 'pending').length;
162
162
  }
163
+ pendingRecipients() {
164
+ this.expire();
165
+ return [...new Set(this.document.messages.filter((message) => message.status === 'pending').map((message) => message.toSessionId))];
166
+ }
163
167
  markDelivered(messageIds) {
164
168
  return this.updateStatus(messageIds, 'delivered');
165
169
  }
@@ -249,7 +253,8 @@ export class CollaborationStore {
249
253
  expire() {
250
254
  let changed = false;
251
255
  for (const message of this.document.messages)
252
- if (message.status === 'pending' && message.expiresAt && message.expiresAt <= Date.now()) {
256
+ if (message.status === 'pending' && message.expiresAt && message.expiresAt <= Date.now()
257
+ && this.document.transport?.[message.id]?.last_error !== 'DELIVERY_IN_PROGRESS') {
253
258
  message.status = 'expired';
254
259
  message.failureReason = 'MESSAGE_EXPIRED';
255
260
  changed = true;
@@ -0,0 +1,10 @@
1
+ import { buildBracketedSubmitBytes } from './promptDelivery.js';
2
+ export async function writeCollaborationTmuxPane(run, pane, prompt) {
3
+ const identity = (await run(['display-message', '-p', '-t', pane.paneId,
4
+ '#{pid}:#{session_id}:#{pane_id}:#{pane_pid}'])).trim();
5
+ if (identity !== `${pane.serverPid}:${pane.sessionId}:${pane.paneId}:${pane.panePid}`)
6
+ throw new Error('TMUX_PANE_CHANGED');
7
+ // A fixed pane target is independent of the current window, keyboard focus
8
+ // and browser presence. One invocation preserves the bracketed submit bytes.
9
+ await run(['send-keys', '-t', pane.paneId, '-l', '--', buildBracketedSubmitBytes(prompt)]);
10
+ }
@@ -0,0 +1,80 @@
1
+ export function resolveCollaborationSessionId(input, records, routing) {
2
+ const sessionId = typeof input.sessionId === 'string' ? input.sessionId.trim() : '';
3
+ if (sessionId && records.some((record) => record.sessionId === sessionId))
4
+ return sessionId;
5
+ const backendSessionId = typeof input.backendSessionId === 'string' ? input.backendSessionId.trim() : '';
6
+ if (backendSessionId) {
7
+ const owner = routing.ownerOfBackend(backendSessionId);
8
+ if (owner && records.some((record) => record.sessionId === owner))
9
+ return owner;
10
+ const resolved = records.find((record) => record.backendSessionId === backendSessionId)?.sessionId;
11
+ if (resolved)
12
+ return resolved;
13
+ }
14
+ const tmuxSessionName = typeof input.tmuxSessionName === 'string' ? input.tmuxSessionName.trim() : '';
15
+ if (!tmuxSessionName)
16
+ return null;
17
+ const bound = records.find((record) => routing.get(record.sessionId)?.tmuxSessionName === tmuxSessionName);
18
+ if (bound)
19
+ return bound.sessionId;
20
+ return records.find((record) => record.tmuxSessionName === tmuxSessionName)?.sessionId ?? null;
21
+ }
22
+ export function resolveCollaborationBackend(record, records, backends, routing) {
23
+ routing.assertReadable();
24
+ const binding = routing.get(record.sessionId);
25
+ const target = binding ? { ...record, mode: binding.mode, tmuxSessionName: binding.tmuxSessionName,
26
+ backendSessionId: binding.backendSessionId,
27
+ agentResume: binding.agentSlug ? { slug: binding.agentSlug, sessionId: binding.nativeSessionId } : record.agentResume } : record;
28
+ const existing = target.backendSessionId ? backends.get(target.backendSessionId) : null;
29
+ const recovered = existing && existing.mode === target.mode && existing.tmuxSessionName === target.tmuxSessionName
30
+ ? [target.backendSessionId, existing]
31
+ : recoverSessionBinding({ ...target, backendSessionId: null }, records, backends, binding);
32
+ if (!recovered)
33
+ return null;
34
+ const [id, backend] = recovered;
35
+ if (backend.mode !== 'shell' && backend.mode !== 'tmux')
36
+ return null;
37
+ const owner = routing.ownerOfBackend(id);
38
+ if (owner && owner !== record.sessionId)
39
+ return null;
40
+ if (records.some((other) => other.sessionId !== record.sessionId && other.backendSessionId === id))
41
+ return null;
42
+ routing.bind({ sessionId: record.sessionId, backendSessionId: id, mode: backend.mode,
43
+ tmuxSessionName: backend.tmuxSessionName,
44
+ agentSlug: binding?.agentSlug ?? backend.agent?.slug ?? record.agentResume?.slug ?? null,
45
+ nativeSessionId: binding?.nativeSessionId ?? backend.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
46
+ pane: binding?.pane ?? null });
47
+ return recovered;
48
+ }
49
+ // Match live runtime evidence, never the search index or a title/cwd heuristic.
50
+ export function recoverSessionBinding(record, records, backends, binding) {
51
+ if (binding && binding.sessionId !== record.sessionId)
52
+ return null;
53
+ if (record.backendSessionId && backends.has(record.backendSessionId))
54
+ return null;
55
+ const nativeSessionId = binding?.nativeSessionId ?? record.agentResume?.sessionId;
56
+ const agentSlug = binding?.agentSlug ?? record.agentResume?.slug;
57
+ const candidates = [...backends].filter(([id, backend]) => {
58
+ if (records.some((other) => other.sessionId !== record.sessionId && other.backendSessionId === id))
59
+ return false;
60
+ if (backend.mode !== record.mode)
61
+ return false;
62
+ if (record.tmuxSessionName && backend.tmuxSessionName !== record.tmuxSessionName)
63
+ return false;
64
+ const nativeId = backend.agentSession?.sessionId;
65
+ const slug = backend.agent?.slug;
66
+ if (agentSlug && slug && agentSlug !== slug)
67
+ return false;
68
+ if (nativeSessionId && nativeId && nativeSessionId !== nativeId)
69
+ return false;
70
+ if (record.agentResume && slug && record.agentResume.slug !== slug)
71
+ return false;
72
+ if (record.agentResume?.sessionId && nativeId && record.agentResume.sessionId !== nativeId)
73
+ return false;
74
+ const tmuxMatch = record.mode === 'tmux' && Boolean(record.tmuxSessionName)
75
+ && backend.tmuxSessionName === record.tmuxSessionName;
76
+ const nativeMatch = Boolean(nativeId) && nativeSessionId === nativeId && agentSlug === slug;
77
+ return tmuxMatch || nativeMatch;
78
+ });
79
+ return candidates.length === 1 ? candidates[0] : null;
80
+ }
@@ -1255,9 +1255,12 @@ async function runCollab(command) {
1255
1255
  }
1256
1256
  const backendSessionId = process.env.TERMDOCK_BACKEND_SESSION_ID?.trim() || null;
1257
1257
  let tmuxSessionName = null;
1258
- if (!backendSessionId && process.env.TMUX) {
1258
+ if (process.env.TMUX) {
1259
1259
  try {
1260
- const { stdout } = await execFileAsync(process.env.TMUX_BIN || 'tmux', ['display-message', '-p', '#S'], {
1260
+ const paneId = process.env.TMUX_PANE?.trim();
1261
+ const { stdout } = await execFileAsync(process.env.TMUX_BIN || 'tmux', [
1262
+ 'display-message', '-p', ...(/^%\d+$/.test(paneId ?? '') ? ['-t', paneId] : []), '#S',
1263
+ ], {
1261
1264
  timeout: 2_000,
1262
1265
  maxBuffer: 16 * 1024,
1263
1266
  });
@@ -1266,15 +1269,16 @@ async function runCollab(command) {
1266
1269
  tmuxSessionName = detected;
1267
1270
  }
1268
1271
  catch {
1269
- // The explicit backend id remains the primary path; tmux is a fallback
1270
- // for managed panes whose inner shell predates the attach wrapper env.
1272
+ // Keep a working backend context if tmux inspection fails. A surviving
1273
+ // pane may retain an obsolete backend id after TD reattaches it, so
1274
+ // send both identities whenever tmux inspection succeeds.
1271
1275
  }
1272
1276
  }
1273
1277
  if (!backendSessionId && !tmuxSessionName) {
1274
1278
  console.error(JSON.stringify({ ok: false, code: 'SESSION_NOT_FOUND', error: 'td collab must run inside a Termdock-managed Session.' }));
1275
1279
  process.exit(1);
1276
1280
  }
1277
- const context = backendSessionId ? { backendSessionId } : { tmuxSessionName };
1281
+ const context = { backendSessionId, tmuxSessionName };
1278
1282
  const baseUrl = runningState.localUrl
1279
1283
  ?? `${runningState.scheme ?? 'http'}://${runningState.host === '0.0.0.0' ? 'localhost' : runningState.host}:${runningState.port}`;
1280
1284
  process.exitCode = await executeCollaborationCommand(command, Object.fromEntries(Object.entries(context).filter((entry) => typeof entry[1] === 'string')), { request: (method, endpoint, body, timeout) => method === 'GET'