termdock 1.4.178 → 1.4.179
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.
- package/dist/client/assets/App-DXq9kwY5.js +150 -0
- package/dist/client/assets/App-DXq9kwY5.js.br +0 -0
- package/dist/client/assets/App-DXq9kwY5.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BNWqAnqc.js → ChangeWalkthroughPanel-DWUBYx6Q.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-DWUBYx6Q.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DWUBYx6Q.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-IlTGwr-I.js → DagPlayground-DyczrcHM.js} +1 -1
- package/dist/client/assets/DagPlayground-DyczrcHM.js.br +0 -0
- package/dist/client/assets/DagPlayground-DyczrcHM.js.gz +0 -0
- package/dist/client/assets/{DiffLab-CwhNpJvo.js → DiffLab-D5wb-H7y.js} +1 -1
- package/dist/client/assets/DiffLab-D5wb-H7y.js.br +0 -0
- package/dist/client/assets/DiffLab-D5wb-H7y.js.gz +0 -0
- package/dist/client/assets/{DiffReview-DyhINv0F.js → DiffReview-CVIz-B5T.js} +1 -1
- package/dist/client/assets/DiffReview-CVIz-B5T.js.br +0 -0
- package/dist/client/assets/DiffReview-CVIz-B5T.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-D9Kt5VwB.js → DiffReviewLab-99hTNRaj.js} +1 -1
- package/dist/client/assets/DiffReviewLab-99hTNRaj.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-99hTNRaj.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-3zz2PI-8.js → DiffViewer-BPor2_7o.js} +1 -1
- package/dist/client/assets/DiffViewer-BPor2_7o.js.br +0 -0
- package/dist/client/assets/DiffViewer-BPor2_7o.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-CN8xsiGC.js → ModelPreview-DpImj4vY.js} +1 -1
- package/dist/client/assets/ModelPreview-DpImj4vY.js.br +0 -0
- package/dist/client/assets/ModelPreview-DpImj4vY.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BW8SSsoA.js +79 -0
- package/dist/client/assets/RightSidebar-BW8SSsoA.js.br +0 -0
- package/dist/client/assets/RightSidebar-BW8SSsoA.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-DkdoJ2ZH.js → abnfDiagram-VCTEODGH-CNWmFVMh.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-CNWmFVMh.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CNWmFVMh.js.gz +0 -0
- package/dist/client/assets/app-Pl7tV8Be.js +34 -0
- package/dist/client/assets/app-Pl7tV8Be.js.br +0 -0
- package/dist/client/assets/app-Pl7tV8Be.js.gz +0 -0
- package/dist/client/assets/{arc-ctzz5PcK.js → arc-Z8mcHjh7.js} +1 -1
- package/dist/client/assets/arc-Z8mcHjh7.js.br +0 -0
- package/dist/client/assets/arc-Z8mcHjh7.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-DNLcYfGQ.js → architectureDiagram-5GKGNRK7-DuxbAeFP.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DuxbAeFP.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DuxbAeFP.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-Coye1ou5.js → blockDiagram-I7D4REHJ-SxAFw0An.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-SxAFw0An.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-SxAFw0An.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DTa8jyPp.js → c4Diagram-7LVT6UL2-CcRdiWo7.js} +1 -1
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DTa8jyPp.js.br → c4Diagram-7LVT6UL2-CcRdiWo7.js.br} +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CcRdiWo7.js.gz +0 -0
- package/dist/client/assets/channel-nXn0ntBk.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BpDyjNfs.js → chunk-2Q5K7J3B-DJ_qzvZZ.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-DgwdqXKl.js → chunk-5VM5RSS4-l3H0C2ci.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DWf2gZ96.js → chunk-F27PBJKO-B7xASv8n.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-B7xASv8n.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-B7xASv8n.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-l42o_E2c.js → chunk-IMKFNOWR-Wg5gR7sZ.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-Wg5gR7sZ.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Wg5gR7sZ.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-8CkEf05l.js → chunk-JWPE2WC7-BlXUS79m.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-BKKOfbdg.js → chunk-POPQ4Y6H-CU38G8hb.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-RORrf2aM.js → chunk-SVP7TREG-D-yZlXqX.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-D-yZlXqX.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-D-yZlXqX.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-hPqafEtC.js → chunk-TICWLB2K-DrOYmcBB.js} +1 -1
- package/dist/client/assets/{chunk-TICWLB2K-hPqafEtC.js.br → chunk-TICWLB2K-DrOYmcBB.js.br} +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DrOYmcBB.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-CrvjJwms.js → chunk-XXDRQBXY-BbsHtVFa.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-DxqN38m7.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DxqN38m7.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CwRSiQLa.js → cose-bilkent-JH36ORCC-_wKUNL8T.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-_wKUNL8T.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-_wKUNL8T.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CXnppZ8I.js → cynefin-OW5HDTMX-CPr--Wnc.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-CPr--Wnc.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CPr--Wnc.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BSDrHSIA.js → cynefinDiagram-5FMLGOSQ-5ygpPOWT.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-5ygpPOWT.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-5ygpPOWT.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-d-5j4vkq.js → dagre-GXQ25YYZ-Cvx65nHO.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-Cvx65nHO.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-Cvx65nHO.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-DizMBpGW.js → diagram-S7CK7UJ4-Dw5cp_BA.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-Dw5cp_BA.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-Dw5cp_BA.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-CNQK26rH.js → diagram-UQ7AKVKN-D8BZ5Tp3.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-D8BZ5Tp3.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-D8BZ5Tp3.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-Dua-2_46.js → diagram-VSXAHHWV-BxJD7ZWH.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-BxJD7ZWH.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BxJD7ZWH.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-CIq-4729.js → diagram-VX7I27RA-CvzlU0bI.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-CvzlU0bI.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CvzlU0bI.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-BukQ0u-x.js → diagram-Z3DM3KII-Be4HdFlL.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-Be4HdFlL.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-Be4HdFlL.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-D2zRkRBI.js → ebnfDiagram-PWID7BFC-C_wrrPjF.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-C_wrrPjF.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-C_wrrPjF.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DbX6A3ls.js → erDiagram-RLTQ6QDP-IHyLgV6M.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-IHyLgV6M.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-IHyLgV6M.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-YECDWA2G.js → flowDiagram-HODETNUW-CpfW88Uc.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-CpfW88Uc.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CpfW88Uc.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-D3EJdUov.js → ganttDiagram-EL5Y4UJY-CsbQ6KLb.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CsbQ6KLb.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CsbQ6KLb.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-D3IZpZS7.js → gestureArbiter-CJQ8K48V.js} +1 -1
- package/dist/client/assets/gestureArbiter-CJQ8K48V.js.br +0 -0
- package/dist/client/assets/gestureArbiter-CJQ8K48V.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-tr_HKoQK.js → gitGraphDiagram-WWUBYQGX-DJZqSGtH.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DJZqSGtH.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DJZqSGtH.js.gz +0 -0
- package/dist/client/assets/{index-C4Ny0NDQ.css → index-PWu1VEN_.css} +1 -1
- package/dist/client/assets/index-PWu1VEN_.css.br +0 -0
- package/dist/client/assets/index-PWu1VEN_.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-xBV63YYg.js → infoDiagram-27XIBGKW-D6sSgR2Y.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BrYSZu1R.js → ishikawaDiagram-5VMMS53U-BJJSUXep.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJJSUXep.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJJSUXep.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-C9Ft--aA.js → journeyDiagram-3NMN7TZE-ou1XS2DP.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-ou1XS2DP.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-ou1XS2DP.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-FCxqSu11.js → kanban-definition-UXKFOSKX-BFQGYQzd.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-BFQGYQzd.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BFQGYQzd.js.gz +0 -0
- package/dist/client/assets/{layout-BqjIyPqO.js → layout-zUU3xcam.js} +1 -1
- package/dist/client/assets/layout-zUU3xcam.js.br +0 -0
- package/dist/client/assets/layout-zUU3xcam.js.gz +0 -0
- package/dist/client/assets/{linear-CEAGQX5N.js → linear-CYwYQI2I.js} +1 -1
- package/dist/client/assets/linear-CYwYQI2I.js.br +0 -0
- package/dist/client/assets/linear-CYwYQI2I.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-DvAY78t_.js → mermaid.core-91Llsw5j.js} +5 -5
- package/dist/client/assets/mermaid.core-91Llsw5j.js.br +0 -0
- package/dist/client/assets/mermaid.core-91Llsw5j.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-D7_qIQ9H.js → mindmap-definition-YA3MSWOX-RWG074lb.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-RWG074lb.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-RWG074lb.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-Dksfdvk3.js → passwordBootstrap-DRknuNUN.js} +1 -1
- package/dist/client/assets/passwordBootstrap-DRknuNUN.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-DRknuNUN.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DeTIBKGC.js → pegDiagram-XKGWAZYB-Dyh2rzGS.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-Dyh2rzGS.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-Dyh2rzGS.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-z5IcoVwx.js → pieDiagram-E7YTZNPT-CL3UcUfh.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-CL3UcUfh.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CL3UcUfh.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B-zCZvES.js → quadrantDiagram-AXDQQJYC-DOkejMel.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DOkejMel.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DOkejMel.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-DbpeB7P2.js → railroadDiagram-O6MQD6OU-BC780Uh3.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BC780Uh3.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BC780Uh3.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-p56g9xFb.js → referenceLongPress-Dlc0425_.js} +1 -1
- package/dist/client/assets/referenceLongPress-Dlc0425_.js.br +0 -0
- package/dist/client/assets/referenceLongPress-Dlc0425_.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-UhJYMJ_U.js → requirementDiagram-BXWQKSXE-CwCGr0qR.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CwCGr0qR.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CwCGr0qR.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-xKsuFR17.js → sankeyDiagram-P5KCCOFB-CYDYBwJQ.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CYDYBwJQ.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CYDYBwJQ.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DrjBl65C.js → sequenceDiagram-WJ2MYXX4-DhHsZi3K.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DhHsZi3K.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DhHsZi3K.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-BIlGJ9tm.js → sizeCapture-INFHLROL-4oUXWvYA.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-4oUXWvYA.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-4oUXWvYA.js.gz +0 -0
- package/dist/client/assets/{sparkles-B-uEi0Pa.js → sparkles-gqiuBkfP.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-C3k9XvA6.js → stateDiagram-D77RDMKH-7hUELZGo.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-7hUELZGo.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-7hUELZGo.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-MO84Jqt6.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-BN-x1rXp.js → swimlanes-42K2YHIH-E1qoordP.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-E1qoordP.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-E1qoordP.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DnSXMnZQ.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-Dcedjtic.js → timeline-definition-24CTP7MA-CTYyCnUW.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-CTYyCnUW.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-CTYyCnUW.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-BSNAwyll.js → triangle-alert-DrBuMUPw.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-DAIk-5Rt.js → vennDiagram-4TSXK5OY-DVWbhWsS.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-DVWbhWsS.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DVWbhWsS.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-3iXOTpAg.js → wardleyDiagram-VM6X3IG4-BFAwOxhc.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BFAwOxhc.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BFAwOxhc.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-OD1a_9Qg.js → xychartDiagram-S5SC5T6Z-DYXOiBiX.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DYXOiBiX.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DYXOiBiX.js.gz +0 -0
- package/dist/client/index.html +2 -2
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/sw-notifications.js +5 -45
- package/dist/client/sw-notifications.js.br +0 -0
- package/dist/client/sw-notifications.js.gz +0 -0
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.br +0 -0
- package/dist/client/sw.js.gz +0 -0
- package/dist/server/agent/collaborationCli.js +10 -3
- package/dist/server/agent/collaborationDeliveryWorker.js +154 -0
- package/dist/server/agent/collaborationRoutes.js +15 -4
- package/dist/server/agent/collaborationRouting.js +94 -0
- package/dist/server/agent/collaborationStore.js +6 -1
- package/dist/server/agent/collaborationTmuxDelivery.js +10 -0
- package/dist/server/agent/sessionBindingRecovery.js +59 -0
- package/dist/server/entry.js +30 -13
- package/dist/server/federation/desktopTargets.js +64 -0
- package/dist/server/federation/directRoutes.js +27 -19
- package/dist/server/federation/routeAccess.js +14 -7
- package/dist/server/federation/trustedCa.js +55 -0
- package/dist/server/routes/terminal.js +242 -67
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-CAEtVL8Z.js +0 -150
- package/dist/client/assets/App-CAEtVL8Z.js.br +0 -0
- package/dist/client/assets/App-CAEtVL8Z.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.gz +0 -0
- package/dist/client/assets/DagPlayground-IlTGwr-I.js.br +0 -0
- package/dist/client/assets/DagPlayground-IlTGwr-I.js.gz +0 -0
- package/dist/client/assets/DiffLab-CwhNpJvo.js.br +0 -0
- package/dist/client/assets/DiffLab-CwhNpJvo.js.gz +0 -0
- package/dist/client/assets/DiffReview-DyhINv0F.js.br +0 -0
- package/dist/client/assets/DiffReview-DyhINv0F.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.gz +0 -0
- package/dist/client/assets/DiffViewer-3zz2PI-8.js.br +0 -0
- package/dist/client/assets/DiffViewer-3zz2PI-8.js.gz +0 -0
- package/dist/client/assets/ModelPreview-CN8xsiGC.js.br +0 -0
- package/dist/client/assets/ModelPreview-CN8xsiGC.js.gz +0 -0
- package/dist/client/assets/RightSidebar-DQk1cwKH.js +0 -79
- package/dist/client/assets/RightSidebar-DQk1cwKH.js.br +0 -0
- package/dist/client/assets/RightSidebar-DQk1cwKH.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.gz +0 -0
- package/dist/client/assets/app-B42CoF1r.js +0 -34
- package/dist/client/assets/app-B42CoF1r.js.br +0 -0
- package/dist/client/assets/app-B42CoF1r.js.gz +0 -0
- package/dist/client/assets/arc-ctzz5PcK.js.br +0 -0
- package/dist/client/assets/arc-ctzz5PcK.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DTa8jyPp.js.gz +0 -0
- package/dist/client/assets/channel-CzvSDv06.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-hPqafEtC.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CDknJfE0.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CDknJfE0.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CwRSiQLa.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CwRSiQLa.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbX6A3ls.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbX6A3ls.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-D3IZpZS7.js.br +0 -0
- package/dist/client/assets/gestureArbiter-D3IZpZS7.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.gz +0 -0
- package/dist/client/assets/index-C4Ny0NDQ.css.br +0 -0
- package/dist/client/assets/index-C4Ny0NDQ.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.gz +0 -0
- package/dist/client/assets/layout-BqjIyPqO.js.br +0 -0
- package/dist/client/assets/layout-BqjIyPqO.js.gz +0 -0
- package/dist/client/assets/linear-CEAGQX5N.js.br +0 -0
- package/dist/client/assets/linear-CEAGQX5N.js.gz +0 -0
- package/dist/client/assets/mermaid.core-DvAY78t_.js.br +0 -0
- package/dist/client/assets/mermaid.core-DvAY78t_.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B-zCZvES.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B-zCZvES.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-p56g9xFb.js.br +0 -0
- package/dist/client/assets/referenceLongPress-p56g9xFb.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-B7GnCu2o.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BH_gm34Y.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.gz +0 -0
|
@@ -8,11 +8,13 @@ export class RouteAccess {
|
|
|
8
8
|
filePath;
|
|
9
9
|
now;
|
|
10
10
|
canRouteSubject;
|
|
11
|
+
additionalTargets;
|
|
11
12
|
tickets = new Map();
|
|
12
|
-
constructor(filePath, now = Date.now, canRouteSubject = () => false) {
|
|
13
|
+
constructor(filePath, now = Date.now, canRouteSubject = () => false, additionalTargets = () => []) {
|
|
13
14
|
this.filePath = filePath;
|
|
14
15
|
this.now = now;
|
|
15
16
|
this.canRouteSubject = canRouteSubject;
|
|
17
|
+
this.additionalTargets = additionalTargets;
|
|
16
18
|
}
|
|
17
19
|
registry() {
|
|
18
20
|
const empty = { relays: [], directTargets: [] };
|
|
@@ -32,8 +34,8 @@ export class RouteAccess {
|
|
|
32
34
|
}
|
|
33
35
|
const targetIds = new Set();
|
|
34
36
|
for (const target of directTargets) {
|
|
35
|
-
if (!target || typeof target !== 'object' || Array.isArray(target) || Object.keys(target).some(key => !['serviceId', 'url', 'caPath'].includes(key))
|
|
36
|
-
|| !validId(target.serviceId) || targetIds.has(target.serviceId) || typeof target.url !== 'string' || (target.caPath !== undefined && typeof target.caPath !== 'string'))
|
|
37
|
+
if (!target || typeof target !== 'object' || Array.isArray(target) || Object.keys(target).some(key => !['serviceId', 'url', 'caPath', 'label'].includes(key))
|
|
38
|
+
|| (target.label !== undefined && (typeof target.label !== 'string' || target.label.length > 120)) || !validId(target.serviceId) || targetIds.has(target.serviceId) || typeof target.url !== 'string' || (target.caPath !== undefined && typeof target.caPath !== 'string'))
|
|
37
39
|
return empty;
|
|
38
40
|
const url = new URL(target.url);
|
|
39
41
|
if (!['https:', 'wss:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || !['/', '/api/federation/secure'].includes(url.pathname))
|
|
@@ -90,7 +92,7 @@ export class RouteAccess {
|
|
|
90
92
|
if (!validId(serviceId))
|
|
91
93
|
return false;
|
|
92
94
|
const registry = this.registry();
|
|
93
|
-
return registry.relays.some(relay => relay.targets.includes(serviceId)) || registry.directTargets.some(target => target.serviceId === serviceId);
|
|
95
|
+
return registry.relays.some(relay => relay.targets.includes(serviceId)) || registry.directTargets.some(target => target.serviceId === serviceId) || this.additionalTargets().some(target => target.serviceId === serviceId);
|
|
94
96
|
}
|
|
95
97
|
addDirectTarget(target) {
|
|
96
98
|
const url = new URL(target.url);
|
|
@@ -120,17 +122,22 @@ export class RouteAccess {
|
|
|
120
122
|
writeFileSync(temporary, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
121
123
|
renameSync(temporary, this.filePath);
|
|
122
124
|
}
|
|
123
|
-
configuredDirectTargets() {
|
|
125
|
+
configuredDirectTargets() {
|
|
126
|
+
const targets = new Map(this.additionalTargets().map(target => [target.serviceId, target]));
|
|
127
|
+
for (const target of this.registry().directTargets)
|
|
128
|
+
targets.set(target.serviceId, target);
|
|
129
|
+
return [...targets.values()];
|
|
130
|
+
}
|
|
124
131
|
configuredTargets() {
|
|
125
132
|
const registry = this.registry();
|
|
126
133
|
const targets = new Map();
|
|
127
134
|
for (const relay of registry.relays)
|
|
128
135
|
for (const serviceId of relay.targets)
|
|
129
136
|
targets.set(serviceId, { serviceId });
|
|
130
|
-
for (const target of
|
|
137
|
+
for (const target of this.configuredDirectTargets()) {
|
|
131
138
|
const url = new URL(target.url);
|
|
132
139
|
url.protocol = 'https:';
|
|
133
|
-
targets.set(target.serviceId, { serviceId: target.serviceId, url: url.origin });
|
|
140
|
+
targets.set(target.serviceId, { serviceId: target.serviceId, url: url.origin, ...(target.label ? { label: target.label } : {}) });
|
|
134
141
|
}
|
|
135
142
|
return [...targets.values()];
|
|
136
143
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { X509Certificate } from 'node:crypto';
|
|
2
|
+
import { get } from 'node:https';
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
/** Only the CA download is unauthenticated. Its exact SHA-256 fingerprint was
|
|
5
|
+
* already approved locally for this origin; the subsequent WSS connection must
|
|
6
|
+
* still pass normal TLS chain, hostname and expiry checks using this CA.
|
|
7
|
+
*/
|
|
8
|
+
export function readPinnedCertificateAuthority(origin, fingerprint) {
|
|
9
|
+
const url = new URL('/onboarding/ca.crt', origin);
|
|
10
|
+
if (url.protocol !== 'https:' || url.username || url.password || !/^(?:[\da-f]{2}:){31}[\da-f]{2}$/i.test(fingerprint))
|
|
11
|
+
return Promise.reject(new Error('Invalid pinned certificate authority'));
|
|
12
|
+
const key = `${url.origin}:${fingerprint.toUpperCase()}`;
|
|
13
|
+
const cached = cache.get(key);
|
|
14
|
+
if (cached && cached.expires > Date.now())
|
|
15
|
+
return cached.value;
|
|
16
|
+
const value = new Promise((resolve, reject) => {
|
|
17
|
+
const request = get(url, { agent: false, rejectUnauthorized: false, signal: AbortSignal.timeout(5000) }, response => {
|
|
18
|
+
if (response.statusCode !== 200) {
|
|
19
|
+
response.resume();
|
|
20
|
+
reject(new Error('Pinned certificate authority unavailable'));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const chunks = [];
|
|
24
|
+
let size = 0;
|
|
25
|
+
response.on('data', (chunk) => {
|
|
26
|
+
size += chunk.length;
|
|
27
|
+
if (size > 256 * 1024) {
|
|
28
|
+
request.destroy(new Error('Certificate authority is too large'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
chunks.push(chunk);
|
|
32
|
+
});
|
|
33
|
+
response.on('error', reject);
|
|
34
|
+
response.on('end', () => {
|
|
35
|
+
try {
|
|
36
|
+
const pem = Buffer.concat(chunks), certificate = new X509Certificate(pem);
|
|
37
|
+
if (!certificate.ca || certificate.fingerprint256 !== fingerprint.toUpperCase()
|
|
38
|
+
|| Date.now() < Date.parse(certificate.validFrom) || Date.now() > Date.parse(certificate.validTo))
|
|
39
|
+
throw new Error('Certificate authority no longer matches Desktop trust');
|
|
40
|
+
resolve(pem);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
reject(error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
request.on('error', reject);
|
|
48
|
+
});
|
|
49
|
+
if (cache.size >= 64)
|
|
50
|
+
cache.delete(cache.keys().next().value);
|
|
51
|
+
cache.set(key, { expires: Date.now() + 120_000, value });
|
|
52
|
+
void value.catch(() => { if (cache.get(key)?.value === value)
|
|
53
|
+
cache.delete(key); });
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
@@ -39,6 +39,10 @@ import { CollaborationStore } from '../agent/collaborationStore.js';
|
|
|
39
39
|
import { formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
|
|
40
40
|
import { resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
41
41
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
42
|
+
import { resolveCollaborationBackend } from '../agent/sessionBindingRecovery.js';
|
|
43
|
+
import { CollaborationRoutingStore, selectCollaborationPane } from '../agent/collaborationRouting.js';
|
|
44
|
+
import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
|
|
45
|
+
import { writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
|
|
42
46
|
import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, uninstallHooksForSlug, } from '../agent/installers.js';
|
|
43
47
|
import { loadPlugins, savePlugin, removePlugin, readPluginIcon, validateManifest, } from '../agent/plugins.js';
|
|
44
48
|
import { TmuxLifecycleCoordinator } from '../utils/tmuxLifecycle.js';
|
|
@@ -60,6 +64,14 @@ const execFileAsync = promisify(execFile);
|
|
|
60
64
|
const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
61
65
|
const automationStore = new AutomationStore(`${TERMDOCK_DIR}/automations.json`);
|
|
62
66
|
const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
|
|
67
|
+
const collaborationRouting = new CollaborationRoutingStore(`${TERMDOCK_DIR}/collaboration-routing.json`);
|
|
68
|
+
const collaborationDeliveryWorker = new CollaborationDeliveryWorker({
|
|
69
|
+
store: collaborationStore,
|
|
70
|
+
peers: () => collaborationStore.list().filter((group) => !group.deleted).flatMap((group) => group.sessionIds),
|
|
71
|
+
isLocal: (id) => !id.startsWith('remote:'),
|
|
72
|
+
resolve: resolveCollaborationRoute,
|
|
73
|
+
onError: (error) => console.warn('[collaboration] background delivery failed:', getErrorMessage(error)),
|
|
74
|
+
});
|
|
63
75
|
const sessionSearchStore = new SessionSearchStore(`${TERMDOCK_DIR}/session-search`);
|
|
64
76
|
let atomicJsonWriteSequence = 0;
|
|
65
77
|
async function readJsonFileIfExists(filePath) {
|
|
@@ -572,6 +584,7 @@ void (async () => {
|
|
|
572
584
|
});
|
|
573
585
|
pruneOrphanSessions();
|
|
574
586
|
await backfillPersistedTmuxMetadata();
|
|
587
|
+
collaborationDeliveryWorker.start();
|
|
575
588
|
})();
|
|
576
589
|
caffeinateManager.startNetworkMonitor();
|
|
577
590
|
// 清理磁盘恢复后后端已不存在的 session 引用。
|
|
@@ -1002,6 +1015,16 @@ function upsertGlobalSessionRecord(record) {
|
|
|
1002
1015
|
sessions: deduplicateGlobalSessions(next),
|
|
1003
1016
|
updatedAt: Date.now(),
|
|
1004
1017
|
};
|
|
1018
|
+
// Capture server-observed bindings when sessions are created/adopted. Client
|
|
1019
|
+
// projections may later lose their backend id; the routing registry must not.
|
|
1020
|
+
if (normalized.backendSessionId && terminalSessions.has(normalized.backendSessionId)) {
|
|
1021
|
+
try {
|
|
1022
|
+
resolveOrchestrationBackend(normalized);
|
|
1023
|
+
}
|
|
1024
|
+
catch (error) {
|
|
1025
|
+
console.warn('[collaboration] binding persistence failed:', getErrorMessage(error));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1005
1028
|
return normalized;
|
|
1006
1029
|
}
|
|
1007
1030
|
function removeGlobalSessionRecord(frontendSessionId) {
|
|
@@ -1011,8 +1034,10 @@ function removeGlobalSessionRecord(frontendSessionId) {
|
|
|
1011
1034
|
updatedAt: Date.now(),
|
|
1012
1035
|
};
|
|
1013
1036
|
const changed = globalSessionState.sessions.length !== before;
|
|
1014
|
-
if (changed)
|
|
1037
|
+
if (changed) {
|
|
1015
1038
|
collaborationStore.removeSession(frontendSessionId);
|
|
1039
|
+
collaborationRouting.remove(frontendSessionId);
|
|
1040
|
+
}
|
|
1016
1041
|
return changed;
|
|
1017
1042
|
}
|
|
1018
1043
|
function archiveAgentResumeRecord(record, reason) {
|
|
@@ -1263,19 +1288,48 @@ async function openInventorySession(req, input) {
|
|
|
1263
1288
|
reused,
|
|
1264
1289
|
};
|
|
1265
1290
|
}
|
|
1291
|
+
function resolveOrchestrationBackend(record) {
|
|
1292
|
+
const recovered = resolveCollaborationBackend(record, globalSessionState.sessions, terminalSessions, collaborationRouting);
|
|
1293
|
+
if (!recovered)
|
|
1294
|
+
return null;
|
|
1295
|
+
const [backendSessionId, backend] = recovered;
|
|
1296
|
+
const changed = record.backendSessionId !== backendSessionId || record.tmuxSessionName !== backend.tmuxSessionName || record.mode !== backend.mode;
|
|
1297
|
+
record.backendSessionId = backendSessionId;
|
|
1298
|
+
record.mode = backend.mode;
|
|
1299
|
+
record.tmuxSessionName = backend.tmuxSessionName;
|
|
1300
|
+
record.cwd = backend.cwd ?? record.cwd;
|
|
1301
|
+
if (changed) {
|
|
1302
|
+
globalSessionState.updatedAt = Date.now();
|
|
1303
|
+
schedulePersistGlobalState();
|
|
1304
|
+
const metadata = searchMetadataForBackend(backendSessionId, backend);
|
|
1305
|
+
if (metadata)
|
|
1306
|
+
sessionSearchStore.update(metadata);
|
|
1307
|
+
}
|
|
1308
|
+
return backend;
|
|
1309
|
+
}
|
|
1266
1310
|
function orchestrationSessionSnapshot(record) {
|
|
1267
|
-
const
|
|
1268
|
-
const
|
|
1269
|
-
const
|
|
1311
|
+
const binding = collaborationRouting.get(record.sessionId);
|
|
1312
|
+
const backendId = binding?.backendSessionId ?? record.backendSessionId;
|
|
1313
|
+
const backend = backendId ? terminalSessions.get(backendId) : null;
|
|
1314
|
+
const route = collaborationDeliveryWorker.state(record.sessionId);
|
|
1315
|
+
const slug = binding?.pane?.agentSlug ?? binding?.agentSlug ?? record.agentResume?.slug;
|
|
1316
|
+
const agent = slug ? agentBySlug(slug) : backend?.agent;
|
|
1317
|
+
const sameNativeSession = !binding?.pane || Boolean(binding.pane.nativeSessionId
|
|
1318
|
+
&& binding.pane.nativeSessionId === backend?.agentSession?.sessionId && binding.pane.agentSlug === backend?.agent?.slug);
|
|
1319
|
+
const turnState = sameNativeSession ? backend?.agentSession?.status : undefined;
|
|
1320
|
+
const latestPrompt = sameNativeSession ? backend?.autoTitlePromptPayloads.at(-1)?.trim() ?? '' : '';
|
|
1270
1321
|
return {
|
|
1271
|
-
...collaborationStore.sessionFacts(record.sessionId,
|
|
1322
|
+
...collaborationStore.sessionFacts(record.sessionId, route.state === 'ready', turnState),
|
|
1323
|
+
route_state: route.state,
|
|
1324
|
+
route_error: route.reason,
|
|
1325
|
+
route_checked_at: route.checkedAt,
|
|
1272
1326
|
sessionId: record.sessionId,
|
|
1273
|
-
agentNativeSessionId: backend?.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
|
|
1274
|
-
backendSessionId:
|
|
1327
|
+
agentNativeSessionId: binding?.pane ? binding.pane.nativeSessionId : binding?.nativeSessionId ?? backend?.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
|
|
1328
|
+
backendSessionId: backendId ?? null,
|
|
1275
1329
|
name: record.name,
|
|
1276
1330
|
cwd: backend?.cwd ?? record.cwd ?? '',
|
|
1277
1331
|
agent: agent ? { slug: agent.slug, displayName: agent.displayName } : null,
|
|
1278
|
-
status:
|
|
1332
|
+
status: route.state === 'ready' ? turnState ?? 'ready' : route.state,
|
|
1279
1333
|
capability: agent
|
|
1280
1334
|
? [agent.displayName, ...(agent.capabilities ?? []), backend?.activeProgram?.command || record.activeProgram || 'Agent 会话'].join(' · ')
|
|
1281
1335
|
: (backend?.activeProgram?.command || record.activeProgram || 'Shell 终端'),
|
|
@@ -1361,11 +1415,19 @@ function resolveFrontendSessionId(input) {
|
|
|
1361
1415
|
return sessionId;
|
|
1362
1416
|
const backendSessionId = typeof input.backendSessionId === 'string' ? input.backendSessionId.trim() : '';
|
|
1363
1417
|
if (backendSessionId) {
|
|
1418
|
+
const owner = collaborationRouting.ownerOfBackend(backendSessionId);
|
|
1419
|
+
if (owner && globalSessionState.sessions.some((record) => record.sessionId === owner))
|
|
1420
|
+
return owner;
|
|
1364
1421
|
const resolved = globalSessionState.sessions.find((record) => record.backendSessionId === backendSessionId)?.sessionId;
|
|
1365
1422
|
if (resolved)
|
|
1366
1423
|
return resolved;
|
|
1367
1424
|
}
|
|
1368
1425
|
const tmuxSessionName = typeof input.tmuxSessionName === 'string' ? input.tmuxSessionName.trim() : '';
|
|
1426
|
+
if (!tmuxSessionName)
|
|
1427
|
+
return null;
|
|
1428
|
+
const bound = globalSessionState.sessions.find((record) => collaborationRouting.get(record.sessionId)?.tmuxSessionName === tmuxSessionName);
|
|
1429
|
+
if (bound)
|
|
1430
|
+
return bound.sessionId;
|
|
1369
1431
|
return globalSessionState.sessions.find((record) => record.tmuxSessionName === tmuxSessionName)?.sessionId ?? null;
|
|
1370
1432
|
}
|
|
1371
1433
|
function collaborationRemoteSessions() {
|
|
@@ -1381,45 +1443,154 @@ function tryDeliverCollaborationInbox(frontendSessionId) {
|
|
|
1381
1443
|
? `服务 ${remote.serviceLabel} 不可达:消息尚未送达,仅保存在待发送队列;请勿等待对方已收到的回复,服务重连且 Mac 客户端运行后重试投递。`
|
|
1382
1444
|
: '跨服务消息尚未确认送达,正在等待 Mac 客户端转发。' };
|
|
1383
1445
|
}
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
}
|
|
1392
|
-
const prompt = formatCollaborationDelivery({
|
|
1393
|
-
targetSessionId: frontendSessionId,
|
|
1394
|
-
messages: pending,
|
|
1446
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1447
|
+
const route = collaborationDeliveryWorker.state(frontendSessionId);
|
|
1448
|
+
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId),
|
|
1449
|
+
reason: route.reason ?? '消息已入队,后台正在恢复路由并投递' };
|
|
1450
|
+
}
|
|
1451
|
+
function formatLocalCollaborationMessages(frontendSessionId, messages) {
|
|
1452
|
+
return formatCollaborationDelivery({ targetSessionId: frontendSessionId, messages,
|
|
1395
1453
|
groups: collaborationStore.groupsForSession(frontendSessionId),
|
|
1396
|
-
sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(
|
|
1397
|
-
const snapshot = orchestrationSessionSnapshot(candidate);
|
|
1398
|
-
return {
|
|
1399
|
-
sessionId: snapshot.sessionId,
|
|
1400
|
-
agentNativeSessionId: snapshot.agentNativeSessionId,
|
|
1401
|
-
name: snapshot.name,
|
|
1402
|
-
status: snapshot.status,
|
|
1403
|
-
};
|
|
1404
|
-
})],
|
|
1454
|
+
sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(orchestrationSessionSnapshot)],
|
|
1405
1455
|
});
|
|
1456
|
+
}
|
|
1457
|
+
async function inspectCollaborationTmux(binding, requestedPane) {
|
|
1458
|
+
if (!binding.tmuxSessionName)
|
|
1459
|
+
return { state: 'offline', reason: 'TMUX_BINDING_MISSING' };
|
|
1460
|
+
const name = `=${binding.tmuxSessionName}`;
|
|
1406
1461
|
try {
|
|
1407
|
-
session
|
|
1462
|
+
await runTmux(['has-session', '-t', name]);
|
|
1408
1463
|
}
|
|
1409
1464
|
catch (error) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
});
|
|
1415
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 2_000).unref?.();
|
|
1416
|
-
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId), reason: '终端写入失败,消息已保留并等待重试' };
|
|
1465
|
+
if (/can't find session|no server running|no sessions|error connecting.*No such file/i.test(getErrorMessage(error))) {
|
|
1466
|
+
return { state: 'offline', reason: 'TMUX_SESSION_NOT_RUNNING' };
|
|
1467
|
+
}
|
|
1468
|
+
throw error;
|
|
1417
1469
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1470
|
+
const layout = await getTmuxLayout(binding.tmuxSessionName);
|
|
1471
|
+
if (layout.sessionName !== binding.tmuxSessionName)
|
|
1472
|
+
throw new Error('TMUX_SESSION_IDENTITY_CHANGED');
|
|
1473
|
+
const serverPid = Number((await runTmux(['display-message', '-p', '#{pid}'])).trim());
|
|
1474
|
+
if (!Number.isInteger(serverPid) || serverPid <= 0)
|
|
1475
|
+
throw new Error('TMUX_SERVER_IDENTITY_UNAVAILABLE');
|
|
1476
|
+
const panes = await Promise.all(layout.windows.flatMap((window) => window.panes).map(async (pane) => {
|
|
1477
|
+
const program = await resolveTmuxPaneProgram(pane);
|
|
1478
|
+
const agent = detectAgentFromCommand(program?.rawArgs ?? program?.command ?? '', agentCustomCommands());
|
|
1479
|
+
const nativeSessionId = agent && program?.rawArgs ? inferResumeSessionId(agent, splitCommandToArgv(program.rawArgs)) : null;
|
|
1480
|
+
return { serverPid, sessionId: layout.sessionId, paneId: pane.id, panePid: pane.pid,
|
|
1481
|
+
agentSlug: agent?.slug ?? '', nativeSessionId, cwd: pane.currentPath };
|
|
1482
|
+
}));
|
|
1483
|
+
return selectCollaborationPane(binding, requestedPane ? panes.filter((pane) => pane.paneId === requestedPane) : panes);
|
|
1484
|
+
}
|
|
1485
|
+
async function rebindCollaborationRoute(frontendSessionId, paneId) {
|
|
1486
|
+
await collaborationDeliveryWorker.reconfigure(frontendSessionId, async () => {
|
|
1487
|
+
const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1488
|
+
if (!record)
|
|
1489
|
+
throw new Error('SESSION_REMOVED');
|
|
1490
|
+
const previous = collaborationRouting.get(frontendSessionId);
|
|
1491
|
+
const binding = {
|
|
1492
|
+
sessionId: frontendSessionId, backendSessionId: previous?.backendSessionId ?? record.backendSessionId,
|
|
1493
|
+
mode: record.mode, tmuxSessionName: record.tmuxSessionName ?? previous?.tmuxSessionName ?? null,
|
|
1494
|
+
agentSlug: null, nativeSessionId: null, pane: null,
|
|
1495
|
+
};
|
|
1496
|
+
if (binding.mode === 'tmux') {
|
|
1497
|
+
const inspected = await inspectCollaborationTmux(binding, paneId);
|
|
1498
|
+
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1499
|
+
throw new Error(inspected.reason ?? 'AGENT_NOT_RUNNING');
|
|
1500
|
+
const pane = inspected.pane;
|
|
1501
|
+
binding.pane = { serverPid: pane.serverPid, sessionId: pane.sessionId, paneId: pane.paneId, panePid: pane.panePid,
|
|
1502
|
+
agentSlug: pane.agentSlug, nativeSessionId: pane.nativeSessionId };
|
|
1503
|
+
binding.agentSlug = pane.agentSlug;
|
|
1504
|
+
binding.nativeSessionId = pane.nativeSessionId;
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
if (paneId)
|
|
1508
|
+
throw new Error('PANE_REQUIRES_TMUX');
|
|
1509
|
+
const backend = binding.backendSessionId ? terminalSessions.get(binding.backendSessionId) : null;
|
|
1510
|
+
if (!backend)
|
|
1511
|
+
throw new Error('SHELL_BACKEND_NOT_RUNNING');
|
|
1512
|
+
await refreshCollaborationAgentIdentity(binding.backendSessionId, backend);
|
|
1513
|
+
const agent = detectSessionAgent(backend);
|
|
1514
|
+
if (!agent)
|
|
1515
|
+
throw new Error('AGENT_NOT_RUNNING');
|
|
1516
|
+
binding.agentSlug = agent.slug;
|
|
1517
|
+
binding.nativeSessionId = backend.agentSession?.sessionId ?? null;
|
|
1518
|
+
}
|
|
1519
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1520
|
+
throw new Error('SESSION_REMOVED');
|
|
1521
|
+
collaborationRouting.bind(binding);
|
|
1522
|
+
});
|
|
1523
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1524
|
+
return { ...collaborationRouting.get(frontendSessionId), state: 'recovering' };
|
|
1525
|
+
}
|
|
1526
|
+
async function resolveCollaborationRoute(frontendSessionId) {
|
|
1527
|
+
let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1528
|
+
if (!record)
|
|
1529
|
+
return { state: 'offline', reason: 'SESSION_REMOVED' };
|
|
1530
|
+
let backend = resolveOrchestrationBackend(record);
|
|
1531
|
+
let binding = collaborationRouting.get(frontendSessionId) ?? {
|
|
1532
|
+
sessionId: record.sessionId, backendSessionId: null, mode: record.mode, tmuxSessionName: record.tmuxSessionName,
|
|
1533
|
+
agentSlug: record.agentResume?.slug ?? null, nativeSessionId: record.agentResume?.sessionId ?? null, pane: null,
|
|
1534
|
+
};
|
|
1535
|
+
if (binding.mode === 'tmux') {
|
|
1536
|
+
const inspected = await inspectCollaborationTmux(binding);
|
|
1537
|
+
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1538
|
+
return inspected;
|
|
1539
|
+
record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1540
|
+
if (!record)
|
|
1541
|
+
return { state: 'offline', reason: 'SESSION_REMOVED_DURING_RECOVERY' };
|
|
1542
|
+
const pane = inspected.pane;
|
|
1543
|
+
// Remember the target before attachment. A failed attachment must not let
|
|
1544
|
+
// a subsequent active-pane change silently choose another recipient.
|
|
1545
|
+
const pinned = { serverPid: pane.serverPid, sessionId: pane.sessionId, paneId: pane.paneId, panePid: pane.panePid,
|
|
1546
|
+
agentSlug: pane.agentSlug, nativeSessionId: pane.nativeSessionId ?? binding.nativeSessionId };
|
|
1547
|
+
binding = { ...binding, pane: pinned, agentSlug: pinned.agentSlug, nativeSessionId: pinned.nativeSessionId };
|
|
1548
|
+
collaborationRouting.bind(binding);
|
|
1549
|
+
if (!backend) {
|
|
1550
|
+
const cwd = pane.cwd || record.cwd;
|
|
1551
|
+
if (!cwd)
|
|
1552
|
+
return { state: 'detached', reason: 'TMUX_ATTACH_CWD_UNAVAILABLE' };
|
|
1553
|
+
await pathValidator.allowSessionCwd(cwd);
|
|
1554
|
+
// Attach only. Never recreate a vanished session or launch/resume an
|
|
1555
|
+
// Agent in response to a queued collaboration message.
|
|
1556
|
+
const spawned = await spawnTerminalSession({}, { cwd, mode: 'tmux',
|
|
1557
|
+
tmuxSessionName: binding.tmuxSessionName, attachExistingOnly: true, tmuxAttachTarget: pane.sessionId });
|
|
1558
|
+
record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1559
|
+
if (!record)
|
|
1560
|
+
return { state: 'offline', reason: 'SESSION_REMOVED_DURING_RECOVERY' };
|
|
1561
|
+
if (!terminalSessions.has(spawned.sessionId))
|
|
1562
|
+
return { state: 'detached', reason: 'TMUX_ATTACH_EXITED' };
|
|
1563
|
+
binding = { ...binding, backendSessionId: spawned.sessionId };
|
|
1564
|
+
collaborationRouting.bind(binding);
|
|
1565
|
+
backend = resolveOrchestrationBackend(record);
|
|
1566
|
+
}
|
|
1567
|
+
if (!backend)
|
|
1568
|
+
return { state: 'detached', reason: 'TMUX_BACKEND_UNAVAILABLE' };
|
|
1569
|
+
return { state: 'ready', write: async (messages) => {
|
|
1570
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1571
|
+
throw new Error('SESSION_REMOVED');
|
|
1572
|
+
await writeCollaborationTmuxPane(runTmux, pinned, formatLocalCollaborationMessages(frontendSessionId, messages));
|
|
1573
|
+
backend.lastActivity = Date.now();
|
|
1574
|
+
} };
|
|
1575
|
+
}
|
|
1576
|
+
if (!backend || !binding.backendSessionId)
|
|
1577
|
+
return { state: 'offline', reason: 'SHELL_BACKEND_NOT_RUNNING' };
|
|
1578
|
+
await refreshCollaborationAgentIdentity(binding.backendSessionId, backend);
|
|
1579
|
+
if (!detectSessionAgent(backend))
|
|
1580
|
+
return { state: 'agent-exited', reason: 'AGENT_NOT_RUNNING' };
|
|
1581
|
+
if ((binding.agentSlug && backend.agent?.slug !== binding.agentSlug)
|
|
1582
|
+
|| (binding.nativeSessionId && backend.agentSession?.sessionId && backend.agentSession.sessionId !== binding.nativeSessionId)) {
|
|
1583
|
+
return { state: 'identity-mismatch', reason: 'AGENT_IDENTITY_CHANGED' };
|
|
1584
|
+
}
|
|
1585
|
+
const target = backend;
|
|
1586
|
+
return { state: 'ready', write: async (messages) => {
|
|
1587
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1588
|
+
throw new Error('SESSION_REMOVED');
|
|
1589
|
+
if (terminalSessions.get(binding.backendSessionId) !== target)
|
|
1590
|
+
throw new Error('SHELL_BACKEND_CHANGED');
|
|
1591
|
+
target.ptyProcess.write(buildBracketedSubmitBytes(formatLocalCollaborationMessages(frontendSessionId, messages)));
|
|
1592
|
+
target.lastActivity = Date.now();
|
|
1593
|
+
} };
|
|
1423
1594
|
}
|
|
1424
1595
|
async function refreshCollaborationAgentIdentity(backendSessionId, session) {
|
|
1425
1596
|
let activeProgram = null;
|
|
@@ -1434,33 +1605,12 @@ async function refreshCollaborationAgentIdentity(backendSessionId, session) {
|
|
|
1434
1605
|
? { ...resolved, updatedAt: Date.now() }
|
|
1435
1606
|
: getActiveProgramFromTmuxLayout(layout);
|
|
1436
1607
|
}
|
|
1437
|
-
if (!activeProgram)
|
|
1438
|
-
return;
|
|
1439
1608
|
session.activeProgram = activeProgram;
|
|
1440
|
-
persistActiveProgramBinding(backendSessionId, activeProgram
|
|
1609
|
+
persistActiveProgramBinding(backendSessionId, activeProgram?.command);
|
|
1441
1610
|
syncAgentIdentity(backendSessionId, session);
|
|
1442
1611
|
}
|
|
1443
1612
|
function deliverCollaborationInboxWhenAgentReady(frontendSessionId, backendSessionId, attempt = 0) {
|
|
1444
|
-
|
|
1445
|
-
if (!session)
|
|
1446
|
-
return;
|
|
1447
|
-
if (canDeliverPromptToAgent(session)) {
|
|
1448
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 300).unref?.();
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
if (attempt >= 60)
|
|
1452
|
-
return;
|
|
1453
|
-
void refreshCollaborationAgentIdentity(backendSessionId, session)
|
|
1454
|
-
.catch(() => undefined)
|
|
1455
|
-
.finally(() => {
|
|
1456
|
-
if (canDeliverPromptToAgent(session)) {
|
|
1457
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 300).unref?.();
|
|
1458
|
-
return;
|
|
1459
|
-
}
|
|
1460
|
-
setTimeout(() => {
|
|
1461
|
-
deliverCollaborationInboxWhenAgentReady(frontendSessionId, backendSessionId, attempt + 1);
|
|
1462
|
-
}, 250).unref?.();
|
|
1463
|
-
});
|
|
1613
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1464
1614
|
}
|
|
1465
1615
|
async function spawnCollaborationAgentSession(req, group, sourceSessionId, input) {
|
|
1466
1616
|
const agentSlug = typeof input.agentSlug === 'string' ? input.agentSlug.trim().toLowerCase() : '';
|
|
@@ -4435,7 +4585,22 @@ function ptyHostShellModeEnabled() {
|
|
|
4435
4585
|
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
4436
4586
|
return process.platform !== 'win32' && !isBun && process.env.TERMDOCK_PTY_HOST !== 'off';
|
|
4437
4587
|
}
|
|
4588
|
+
const tmuxBackendSpawns = new Map();
|
|
4438
4589
|
async function spawnTerminalSession(req, input) {
|
|
4590
|
+
const name = input.mode === 'tmux' && input.tmuxSessionName ? normalizeTmuxSessionName(input.tmuxSessionName) : null;
|
|
4591
|
+
if (!name)
|
|
4592
|
+
return spawnTerminalSessionUnlocked(req, input);
|
|
4593
|
+
const pending = tmuxBackendSpawns.get(name);
|
|
4594
|
+
if (pending)
|
|
4595
|
+
return pending;
|
|
4596
|
+
const existing = findBackendSessionForTmux(name);
|
|
4597
|
+
if (existing)
|
|
4598
|
+
return { sessionId: existing[0], session: existing[1], cols: existing[1].cols, rows: existing[1].rows };
|
|
4599
|
+
const operation = spawnTerminalSessionUnlocked(req, input).finally(() => tmuxBackendSpawns.delete(name));
|
|
4600
|
+
tmuxBackendSpawns.set(name, operation);
|
|
4601
|
+
return operation;
|
|
4602
|
+
}
|
|
4603
|
+
async function spawnTerminalSessionUnlocked(req, input) {
|
|
4439
4604
|
const cwd = await resolveWorkingDirectory(req, input.cwd);
|
|
4440
4605
|
const cols = input.cols || 80;
|
|
4441
4606
|
const rows = input.rows || 24;
|
|
@@ -4446,19 +4611,28 @@ async function spawnTerminalSession(req, input) {
|
|
|
4446
4611
|
if (mode === 'tmux') {
|
|
4447
4612
|
const tmuxStatus = await getTmuxStatus();
|
|
4448
4613
|
if (!tmuxStatus.available) {
|
|
4614
|
+
if (input.attachExistingOnly)
|
|
4615
|
+
throw new Error('TMUX_UNAVAILABLE');
|
|
4449
4616
|
console.warn('[termdock] tmux not available, falling back to shell mode:', tmuxStatus.reason);
|
|
4450
4617
|
mode = 'shell';
|
|
4451
4618
|
}
|
|
4452
4619
|
}
|
|
4453
4620
|
const tmuxSessionName = mode === 'tmux' ? normalizeTmuxSessionName(input.tmuxSessionName) : null;
|
|
4454
4621
|
if (mode === 'tmux' && tmuxSessionName) {
|
|
4455
|
-
|
|
4622
|
+
if (input.attachExistingOnly) {
|
|
4623
|
+
if (!await tmuxSessionExists(input.tmuxAttachTarget ?? `=${tmuxSessionName}`))
|
|
4624
|
+
throw new Error('TMUX_SESSION_NOT_RUNNING');
|
|
4625
|
+
}
|
|
4626
|
+
else
|
|
4627
|
+
await prepareManagedTmuxSession(tmuxSessionName, cwd);
|
|
4456
4628
|
}
|
|
4457
4629
|
const command = mode === 'tmux'
|
|
4458
4630
|
? getTmuxBinary()
|
|
4459
4631
|
: (process.platform === 'win32' ? 'powershell.exe' : resolveShellCandidates()[0]);
|
|
4632
|
+
const supportsFeatures = mode === 'tmux' && await supportsTmuxClientFeatures(command);
|
|
4460
4633
|
const args = mode === 'tmux' && tmuxSessionName
|
|
4461
|
-
? buildTmuxAttachArgs(tmuxSessionName,
|
|
4634
|
+
? [...buildTmuxAttachArgs(input.tmuxAttachTarget ?? tmuxSessionName, supportsFeatures),
|
|
4635
|
+
...(input.attachExistingOnly && supportsFeatures ? ['-f', 'ignore-size'] : [])]
|
|
4462
4636
|
: (process.platform === 'win32' ? buildPowerShellCwdHookArgs() : []);
|
|
4463
4637
|
const envPath = buildAugmentedPath();
|
|
4464
4638
|
const resolvedEnv = buildInteractiveColorEnvironment({
|
|
@@ -5393,7 +5567,8 @@ router.post('/operations/orchestration/spawn', async (req, res) => {
|
|
|
5393
5567
|
res.status(400).json({ error: getErrorMessage(error) });
|
|
5394
5568
|
}
|
|
5395
5569
|
});
|
|
5396
|
-
router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
|
|
5570
|
+
router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
|
|
5571
|
+
deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute }));
|
|
5397
5572
|
router.get('/operations/session-search', (req, res) => {
|
|
5398
5573
|
const query = typeof req.query.q === 'string' ? req.query.q : '';
|
|
5399
5574
|
const results = sessionSearchStore.search(query, Number(req.query.limit) || 30).map((result) => {
|
package/package.json
CHANGED
package/runtime-manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.179",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-kSujny2SLOFnZ1ffxm1FYtli7g1vEN8d3vj1oQeLLdc=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-AQUq3XFy+cNp7XbeVQmCnE6m5odhlqCbuU3LbvxFjt0=",
|
|
10
|
+
"clientBundleHash": "sha256-jofF5lZQ8B7W5pmfclW2tlMMp+FlTHRi8am3AscU2CE=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|