termdock 1.4.230 → 1.4.232
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/.desktop-dist/main.js +9 -0
- package/dist/client/assets/App-Di12Ej1O.js +155 -0
- package/dist/client/assets/App-Di12Ej1O.js.br +0 -0
- package/dist/client/assets/App-Di12Ej1O.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BoJ_fimI.js → ChangeWalkthroughPanel-pOBFJTxi.js} +1 -1
- package/dist/client/assets/ChangeWalkthroughPanel-pOBFJTxi.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-pOBFJTxi.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-Ds7ArhFS.js → DagPlayground-DWaLn2pa.js} +1 -1
- package/dist/client/assets/DagPlayground-DWaLn2pa.js.br +0 -0
- package/dist/client/assets/DagPlayground-DWaLn2pa.js.gz +0 -0
- package/dist/client/assets/{DiffLab-Bbse8Ju0.js → DiffLab-DMvlScEP.js} +1 -1
- package/dist/client/assets/DiffLab-DMvlScEP.js.br +0 -0
- package/dist/client/assets/DiffLab-DMvlScEP.js.gz +0 -0
- package/dist/client/assets/{DiffReview-83Xj6PCu.js → DiffReview-D1M9kVJy.js} +1 -1
- package/dist/client/assets/DiffReview-D1M9kVJy.js.br +0 -0
- package/dist/client/assets/DiffReview-D1M9kVJy.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-DFn8TDlG.js → DiffReviewLab-BVkhfOhb.js} +1 -1
- package/dist/client/assets/DiffReviewLab-BVkhfOhb.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-BVkhfOhb.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-DbOviqxz.js → DiffViewer-DN5Akyco.js} +7 -7
- package/dist/client/assets/DiffViewer-DN5Akyco.js.br +0 -0
- package/dist/client/assets/DiffViewer-DN5Akyco.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-DTbtvbXf.js → ModelPreview-D5Vl9PdY.js} +1 -1
- package/dist/client/assets/ModelPreview-D5Vl9PdY.js.br +0 -0
- package/dist/client/assets/ModelPreview-D5Vl9PdY.js.gz +0 -0
- package/dist/client/assets/{RightSidebar-CkBjWyP3.js → RightSidebar-CNICBrOL.js} +22 -22
- package/dist/client/assets/RightSidebar-CNICBrOL.js.br +0 -0
- package/dist/client/assets/RightSidebar-CNICBrOL.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-DNI-fpHF.js → abnfDiagram-VCTEODGH-Coq8shqQ.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-Coq8shqQ.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-Coq8shqQ.js.gz +0 -0
- package/dist/client/assets/app-BNPa5Xzm.js +35 -0
- package/dist/client/assets/app-BNPa5Xzm.js.br +0 -0
- package/dist/client/assets/app-BNPa5Xzm.js.gz +0 -0
- package/dist/client/assets/{arc-BTzsKyvV.js → arc-D9F88oM8.js} +1 -1
- package/dist/client/assets/arc-D9F88oM8.js.br +0 -0
- package/dist/client/assets/arc-D9F88oM8.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-zSPsJaBw.js → architectureDiagram-5GKGNRK7-Ci04e9H2.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Ci04e9H2.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Ci04e9H2.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-DrKjCBZz.js → blockDiagram-I7D4REHJ-B0V-2mCO.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-B0V-2mCO.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-B0V-2mCO.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-TcGgIvXV.js → c4Diagram-7LVT6UL2-CgHFeNkR.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-CgHFeNkR.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CgHFeNkR.js.gz +0 -0
- package/dist/client/assets/channel-Cf-_AhxJ.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BtkT5EO8.js → chunk-2Q5K7J3B-C1R3c0d8.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-Plu2bVNV.js → chunk-5VM5RSS4-USD4Kst0.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-D7sV_zVS.js → chunk-F27PBJKO-D-1deMoU.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-D-1deMoU.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-D-1deMoU.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-8EiaBwNr.js → chunk-IMKFNOWR-DyunJ4J2.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-DyunJ4J2.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DyunJ4J2.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-MSPgHaTm.js → chunk-JWPE2WC7-ClM1w-Bz.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-D0-gOtAK.js → chunk-POPQ4Y6H-Dmz_lI83.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-BNSQ1QfI.js → chunk-SVP7TREG-JSkpe0Ru.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-JSkpe0Ru.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-JSkpe0Ru.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-DDkkYDMp.js → chunk-TICWLB2K-DNRVmHct.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-DNRVmHct.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DNRVmHct.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-DPtyGiHW.js → chunk-XXDRQBXY-C_nQBcA8.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CCnYK6Cj.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CCnYK6Cj.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-D1SSHh8-.js → cose-bilkent-JH36ORCC-D2_1Vg5L.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-D2_1Vg5L.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-D2_1Vg5L.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-BwyEKhRj.js → cynefin-OW5HDTMX-C_zLtwow.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-C_zLtwow.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-C_zLtwow.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-C-37gprr.js → cynefinDiagram-5FMLGOSQ-B0Wvfl0v.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B0Wvfl0v.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B0Wvfl0v.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-D-CrfEPO.js → dagre-GXQ25YYZ-D4dD95T3.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-D4dD95T3.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D4dD95T3.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-CWcwLpnZ.js → diagram-S7CK7UJ4-2nnj1lep.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-2nnj1lep.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-2nnj1lep.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-DYBHXnFg.js → diagram-UQ7AKVKN-C9N4zQ8r.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-C9N4zQ8r.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-C9N4zQ8r.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-ChHGrhQk.js → diagram-VSXAHHWV-Cv7_lYe4.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-Cv7_lYe4.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Cv7_lYe4.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-BLRGz0tZ.js → diagram-VX7I27RA-BcgskUTT.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-BcgskUTT.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-BcgskUTT.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-1y-77z0j.js → diagram-Z3DM3KII-v37kk7HI.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-v37kk7HI.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-v37kk7HI.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-CkWYT96W.js → ebnfDiagram-PWID7BFC-CWRegGRp.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CWRegGRp.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CWRegGRp.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-5UOgSbxO.js → erDiagram-RLTQ6QDP-C-a8Ptae.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-C-a8Ptae.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-C-a8Ptae.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-B34ih9F5.js → flowDiagram-HODETNUW-CZQBLCvO.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-CZQBLCvO.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CZQBLCvO.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-Df8xOtuv.js → ganttDiagram-EL5Y4UJY-CDJSVMvA.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CDJSVMvA.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CDJSVMvA.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-BMlW6Vkl.js → gestureArbiter-3yU_eQSh.js} +1 -1
- package/dist/client/assets/gestureArbiter-3yU_eQSh.js.br +0 -0
- package/dist/client/assets/gestureArbiter-3yU_eQSh.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DCQxgM1e.js → gitGraphDiagram-WWUBYQGX-BpKnzvx5.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BpKnzvx5.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BpKnzvx5.js.gz +0 -0
- package/dist/client/assets/{index-ovIMtffC.css → index-BzglvLj5.css} +1 -1
- package/dist/client/assets/{index-ovIMtffC.css.br → index-BzglvLj5.css.br} +0 -0
- package/dist/client/assets/{index-ovIMtffC.css.gz → index-BzglvLj5.css.gz} +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-BReB6pyw.js → infoDiagram-27XIBGKW-CvIbo5Yc.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-Dxm41GIR.js → ishikawaDiagram-5VMMS53U-iTcusz9p.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-iTcusz9p.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-iTcusz9p.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-6EeNJsvk.js → journeyDiagram-3NMN7TZE-AnHrPTVQ.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-AnHrPTVQ.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-AnHrPTVQ.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-Cqy7b348.js → kanban-definition-UXKFOSKX-BSGQKNkn.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-BSGQKNkn.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BSGQKNkn.js.gz +0 -0
- package/dist/client/assets/{layout-2zQZggOo.js → layout-DWfvuKxK.js} +1 -1
- package/dist/client/assets/layout-DWfvuKxK.js.br +0 -0
- package/dist/client/assets/layout-DWfvuKxK.js.gz +0 -0
- package/dist/client/assets/{linear-B_n7SwkQ.js → linear-YGUC9oFd.js} +1 -1
- package/dist/client/assets/linear-YGUC9oFd.js.br +0 -0
- package/dist/client/assets/linear-YGUC9oFd.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-BbAe0O0I.js → mermaid.core-C2ofdbDu.js} +5 -5
- package/dist/client/assets/mermaid.core-C2ofdbDu.js.br +0 -0
- package/dist/client/assets/mermaid.core-C2ofdbDu.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-9cQsoIK-.js → mindmap-definition-YA3MSWOX-DhpmPHTj.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DhpmPHTj.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DhpmPHTj.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-D20oiv4Y.js → passwordBootstrap-VsBMR4A9.js} +2 -2
- package/dist/client/assets/passwordBootstrap-VsBMR4A9.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-VsBMR4A9.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DAJtEqBo.js → pegDiagram-XKGWAZYB-BRFYfMc1.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-BRFYfMc1.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BRFYfMc1.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-BfTrVCVw.js → pieDiagram-E7YTZNPT-B3r-kmkG.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-B3r-kmkG.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-B3r-kmkG.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-O0-rsLC6.js → quadrantDiagram-AXDQQJYC-DR1I_zXC.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DR1I_zXC.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DR1I_zXC.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BmnxmIkC.js → railroadDiagram-O6MQD6OU-Bvfl-CvM.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Bvfl-CvM.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Bvfl-CvM.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-CCa3ab0P.js → referenceLongPress-X-OUHgyO.js} +1 -1
- package/dist/client/assets/referenceLongPress-X-OUHgyO.js.br +0 -0
- package/dist/client/assets/referenceLongPress-X-OUHgyO.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-DDkFvkBr.js → requirementDiagram-BXWQKSXE-C77Ip0mm.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C77Ip0mm.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C77Ip0mm.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-D9FHbxr4.js → sankeyDiagram-P5KCCOFB-C1irBgQp.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-C1irBgQp.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-C1irBgQp.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CNiMBOrr.js → sequenceDiagram-WJ2MYXX4-ziivSu_M.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-ziivSu_M.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-ziivSu_M.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-aQkXiDbr.js → sizeCapture-INFHLROL-Bd6W2W84.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-Bd6W2W84.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Bd6W2W84.js.gz +0 -0
- package/dist/client/assets/{sparkles-DOMB8YK5.js → sparkles-CnZrFoGY.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-Cy-ouYt1.js → stateDiagram-D77RDMKH-Dbp0hGDY.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-Dbp0hGDY.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Dbp0hGDY.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-DC2wSMDB.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-CzalwHK_.js → swimlanes-42K2YHIH-C2Dr-53Y.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-C2Dr-53Y.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-C2Dr-53Y.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-HYd7_s0l.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-6mx1eXdq.js → timeline-definition-24CTP7MA-D-bKj_yT.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-D-bKj_yT.js.br +0 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-6mx1eXdq.js.gz → timeline-definition-24CTP7MA-D-bKj_yT.js.gz} +0 -0
- package/dist/client/assets/{triangle-alert-Ddcj0Ymu.js → triangle-alert-D1EDVJ4z.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-lYrJr0Ul.js → vennDiagram-4TSXK5OY-Dxo8DU3z.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-Dxo8DU3z.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-Dxo8DU3z.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-n0hxf-l2.js → wardleyDiagram-VM6X3IG4-BZMkLNpR.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BZMkLNpR.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BZMkLNpR.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-CEAOqXIs.js → xychartDiagram-S5SC5T6Z-CbxLQ8Sa.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CbxLQ8Sa.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CbxLQ8Sa.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.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 +113 -33
- package/dist/server/agent/collaborationPeerTransport.js +441 -0
- package/dist/server/agent/collaborationPrompt.js +10 -4
- package/dist/server/agent/collaborationRoutes.js +75 -15
- package/dist/server/agent/collaborationStore.js +108 -23
- package/dist/server/agent/collaborationText.js +5 -0
- package/dist/server/agent/collaborationTmuxDelivery.js +9 -6
- package/dist/server/entry.js +42 -2
- package/dist/server/federation/runtime.js +7 -2
- package/dist/server/routes/terminal.js +48 -20
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-C2R-hBtg.js +0 -155
- package/dist/client/assets/App-C2R-hBtg.js.br +0 -0
- package/dist/client/assets/App-C2R-hBtg.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BoJ_fimI.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BoJ_fimI.js.gz +0 -0
- package/dist/client/assets/DagPlayground-Ds7ArhFS.js.br +0 -0
- package/dist/client/assets/DagPlayground-Ds7ArhFS.js.gz +0 -0
- package/dist/client/assets/DiffLab-Bbse8Ju0.js.br +0 -0
- package/dist/client/assets/DiffLab-Bbse8Ju0.js.gz +0 -0
- package/dist/client/assets/DiffReview-83Xj6PCu.js.br +0 -0
- package/dist/client/assets/DiffReview-83Xj6PCu.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-DFn8TDlG.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-DFn8TDlG.js.gz +0 -0
- package/dist/client/assets/DiffViewer-DbOviqxz.js.br +0 -0
- package/dist/client/assets/DiffViewer-DbOviqxz.js.gz +0 -0
- package/dist/client/assets/ModelPreview-DTbtvbXf.js.br +0 -0
- package/dist/client/assets/ModelPreview-DTbtvbXf.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CkBjWyP3.js.br +0 -0
- package/dist/client/assets/RightSidebar-CkBjWyP3.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DNI-fpHF.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DNI-fpHF.js.gz +0 -0
- package/dist/client/assets/app-pwlY5amH.js +0 -35
- package/dist/client/assets/app-pwlY5amH.js.br +0 -0
- package/dist/client/assets/app-pwlY5amH.js.gz +0 -0
- package/dist/client/assets/arc-BTzsKyvV.js.br +0 -0
- package/dist/client/assets/arc-BTzsKyvV.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-zSPsJaBw.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-zSPsJaBw.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-DrKjCBZz.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-DrKjCBZz.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-TcGgIvXV.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-TcGgIvXV.js.gz +0 -0
- package/dist/client/assets/channel-DXA5J9pS.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-D7sV_zVS.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-D7sV_zVS.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-8EiaBwNr.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-8EiaBwNr.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BNSQ1QfI.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BNSQ1QfI.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DDkkYDMp.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DDkkYDMp.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CRTloq-v.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CRTloq-v.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-D1SSHh8-.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-D1SSHh8-.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BwyEKhRj.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BwyEKhRj.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-C-37gprr.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-C-37gprr.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D-CrfEPO.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D-CrfEPO.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CWcwLpnZ.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CWcwLpnZ.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DYBHXnFg.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DYBHXnFg.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-ChHGrhQk.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-ChHGrhQk.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-BLRGz0tZ.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-BLRGz0tZ.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-1y-77z0j.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-1y-77z0j.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CkWYT96W.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CkWYT96W.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-5UOgSbxO.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-5UOgSbxO.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B34ih9F5.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B34ih9F5.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-Df8xOtuv.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-Df8xOtuv.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-BMlW6Vkl.js.br +0 -0
- package/dist/client/assets/gestureArbiter-BMlW6Vkl.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DCQxgM1e.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DCQxgM1e.js.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Dxm41GIR.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Dxm41GIR.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-6EeNJsvk.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-6EeNJsvk.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Cqy7b348.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Cqy7b348.js.gz +0 -0
- package/dist/client/assets/layout-2zQZggOo.js.br +0 -0
- package/dist/client/assets/layout-2zQZggOo.js.gz +0 -0
- package/dist/client/assets/linear-B_n7SwkQ.js.br +0 -0
- package/dist/client/assets/linear-B_n7SwkQ.js.gz +0 -0
- package/dist/client/assets/mermaid.core-BbAe0O0I.js.br +0 -0
- package/dist/client/assets/mermaid.core-BbAe0O0I.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-9cQsoIK-.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-9cQsoIK-.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-D20oiv4Y.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-D20oiv4Y.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DAJtEqBo.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DAJtEqBo.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BfTrVCVw.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BfTrVCVw.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-O0-rsLC6.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-O0-rsLC6.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BmnxmIkC.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BmnxmIkC.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-CCa3ab0P.js.br +0 -0
- package/dist/client/assets/referenceLongPress-CCa3ab0P.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DDkFvkBr.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DDkFvkBr.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-D9FHbxr4.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-D9FHbxr4.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CNiMBOrr.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CNiMBOrr.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-aQkXiDbr.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-aQkXiDbr.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Cy-ouYt1.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Cy-ouYt1.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BBcvA2eS.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-CzalwHK_.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-CzalwHK_.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DG4zIqD3.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-6mx1eXdq.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-lYrJr0Ul.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-lYrJr0Ul.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-n0hxf-l2.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-n0hxf-l2.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CEAOqXIs.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CEAOqXIs.js.gz +0 -0
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { plainCollaborationSnapshot } from './collaborationText.js';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import crypto from 'node:crypto';
|
|
4
5
|
import { COLLAB_LIMITS, CollaborationError, STATUS_RANK, canonicalShortId, drawCollaborationId, newCollaborationId, resolveIdPrefix, validateExtras } from './collaborationProtocol.js';
|
|
6
|
+
function newerRevision(a, b) {
|
|
7
|
+
return !b || a.updatedAt > b.updatedAt || (a.updatedAt === b.updatedAt && a.version > b.version);
|
|
8
|
+
}
|
|
9
|
+
function validRevision(value) {
|
|
10
|
+
return !!value && Number.isSafeInteger(value.updatedAt) && value.updatedAt >= 0 && typeof value.version === 'string' && /^[a-zA-Z0-9-]{1,80}$/.test(value.version);
|
|
11
|
+
}
|
|
5
12
|
/** Characters that would corrupt a role line injected into the delivery shell
|
|
6
13
|
* (`你的定位:...`) or smuggle terminal output. Unlike names, roles are plain
|
|
7
14
|
* prose — 「」 and · are harmless there — so only control sequences are barred. */
|
|
@@ -21,11 +28,29 @@ function pruneRoles(group, sessionIds) {
|
|
|
21
28
|
const roles = Object.fromEntries(Object.entries(group.roles).filter(([id]) => sessionIds.includes(id)));
|
|
22
29
|
return Object.keys(roles).length ? { ...group, roles } : { ...group, roles: undefined };
|
|
23
30
|
}
|
|
31
|
+
/** Public collaboration facts never expose legacy read guesses. */
|
|
32
|
+
export function terminalMessage(message, raw = false) {
|
|
33
|
+
const { readAt: _readAt, readSource: _readSource, ...body } = message;
|
|
34
|
+
const legacyConsumption = message.deliverySource === 'consumer_read';
|
|
35
|
+
return { ...body, status: legacyConsumption ? 'pending' : message.status === 'read' ? 'delivered' : message.status,
|
|
36
|
+
deliveredAt: legacyConsumption ? null : message.deliveredAt,
|
|
37
|
+
deliverySource: legacyConsumption ? undefined : message.deliverySource,
|
|
38
|
+
snapshot: message.snapshot ? (raw ? message.snapshot : plainCollaborationSnapshot(message.snapshot)) : null };
|
|
39
|
+
}
|
|
24
40
|
const MAX_MESSAGES = 2_000;
|
|
25
41
|
const MESSAGE_KINDS = new Set(['message', 'ask', 'reply', 'task', 'handoff', 'done']);
|
|
26
42
|
export class CollaborationStore {
|
|
27
43
|
filePath;
|
|
28
44
|
drawId;
|
|
45
|
+
onMessageQueued;
|
|
46
|
+
peerActivity = new Map();
|
|
47
|
+
recordPeerActivity(sessionId, outputAt, observedAt) {
|
|
48
|
+
if (this.peerActivity.size >= 2000 && !this.peerActivity.has(sessionId))
|
|
49
|
+
this.peerActivity.delete(this.peerActivity.keys().next().value);
|
|
50
|
+
this.peerActivity.set(sessionId, { last_terminal_output_at: typeof outputAt === 'number' && Number.isFinite(outputAt) && outputAt > 0 ? outputAt : null,
|
|
51
|
+
activity_observed_at: typeof observedAt === 'number' && Number.isFinite(observedAt) ? observedAt : null,
|
|
52
|
+
last_peer_sync_at: Date.now(), activity_source: 'remote_terminal_output' });
|
|
53
|
+
}
|
|
29
54
|
document = { version: 2, groups: [], messages: [] };
|
|
30
55
|
persistedDocument = JSON.stringify(this.document);
|
|
31
56
|
/** `drawId` is the random source behind newCollaborationId. It is a
|
|
@@ -128,9 +153,11 @@ export class CollaborationStore {
|
|
|
128
153
|
else
|
|
129
154
|
delete roles[input.sessionId];
|
|
130
155
|
const updated = { ...group, roles: Object.keys(roles).length ? roles : undefined,
|
|
156
|
+
roleVersions: { ...group.roleVersions, [input.sessionId]: { version: crypto.randomUUID(), updatedAt: Math.max(Date.now(), (group.roleVersions?.[input.sessionId]?.updatedAt ?? 0) + 1) } },
|
|
131
157
|
updatedAt: Math.max(Date.now(), group.updatedAt + 1) };
|
|
132
158
|
this.document.groups = this.document.groups.map((candidate) => candidate.id === group.id ? updated : candidate);
|
|
133
159
|
this.persist();
|
|
160
|
+
queueMicrotask(() => this.onMessageQueued?.());
|
|
134
161
|
return updated;
|
|
135
162
|
}
|
|
136
163
|
remove(id) {
|
|
@@ -175,6 +202,55 @@ export class CollaborationStore {
|
|
|
175
202
|
};
|
|
176
203
|
this.persist();
|
|
177
204
|
}
|
|
205
|
+
setRules(groupId, text, updatedBy, expectedVersion) {
|
|
206
|
+
const group = this.getGroup(groupId);
|
|
207
|
+
if (!group || group.deleted || !group.sessionIds.includes(updatedBy))
|
|
208
|
+
throw new CollaborationError('NOT_A_MEMBER', '只能修改当前协作组的约定', 403);
|
|
209
|
+
if (expectedVersion !== undefined && expectedVersion !== (group.instructions?.version ?? ''))
|
|
210
|
+
throw new CollaborationError('RULES_CHANGED', '群规已更新,请重新读取后再修改', 409);
|
|
211
|
+
if (typeof text === 'string')
|
|
212
|
+
text = text.replace(/\r\n?/g, '\n');
|
|
213
|
+
if (typeof text !== 'string' || Buffer.byteLength(text) > 8192 || /[\x00-\x08\x0b-\x1f\x7f]/.test(text))
|
|
214
|
+
throw new CollaborationError('INVALID_RULES', '群规最多 8192 UTF-8 字节,不允许终端控制字符');
|
|
215
|
+
const instructions = { text, updatedBy, version: crypto.randomUUID(), updatedAt: Math.max(Date.now(), (group.instructions?.updatedAt ?? 0) + 1) };
|
|
216
|
+
group.instructions = instructions;
|
|
217
|
+
group.updatedAt = Math.max(Date.now(), group.updatedAt + 1);
|
|
218
|
+
this.persist();
|
|
219
|
+
queueMicrotask(() => this.onMessageQueued?.());
|
|
220
|
+
return instructions;
|
|
221
|
+
}
|
|
222
|
+
context(groupId) {
|
|
223
|
+
const group = this.getGroup(groupId);
|
|
224
|
+
return group ? { instructions: group.instructions, roles: group.roles, roleVersions: group.roleVersions } : {};
|
|
225
|
+
}
|
|
226
|
+
mergeContext(groupId, context) {
|
|
227
|
+
const group = this.getGroup(groupId);
|
|
228
|
+
if (!group || group.deleted || !context || typeof context !== 'object')
|
|
229
|
+
return;
|
|
230
|
+
let changed = false;
|
|
231
|
+
const rules = context.instructions;
|
|
232
|
+
if (rules && validRevision(rules) && typeof rules.text === 'string' && Buffer.byteLength(rules.text) <= 8192
|
|
233
|
+
&& !/[\x00-\x08\x0b-\x1f\x7f]/.test(rules.text) && typeof rules.updatedBy === 'string'
|
|
234
|
+
&& group.sessionIds.includes(rules.updatedBy) && newerRevision(rules, group.instructions)) {
|
|
235
|
+
group.instructions = { ...rules };
|
|
236
|
+
changed = true;
|
|
237
|
+
}
|
|
238
|
+
for (const [id, revision] of Object.entries(context.roleVersions ?? {})) {
|
|
239
|
+
if (!group.sessionIds.includes(id) || !validRevision(revision) || !newerRevision(revision, group.roleVersions?.[id]))
|
|
240
|
+
continue;
|
|
241
|
+
const role = context.roles?.[id];
|
|
242
|
+
if (role !== undefined && typeof role !== 'string')
|
|
243
|
+
continue;
|
|
244
|
+
(group.roleVersions ??= {})[id] = revision;
|
|
245
|
+
if (role)
|
|
246
|
+
(group.roles ??= {})[id] = sanitizeCollaborationRole(role);
|
|
247
|
+
else if (group.roles)
|
|
248
|
+
delete group.roles[id];
|
|
249
|
+
changed = true;
|
|
250
|
+
}
|
|
251
|
+
if (changed)
|
|
252
|
+
this.persist();
|
|
253
|
+
}
|
|
178
254
|
removeSession(sessionId) {
|
|
179
255
|
const affected = this.document.groups.filter((group) => group.sessionIds.includes(sessionId));
|
|
180
256
|
if (affected.length === 0)
|
|
@@ -241,7 +317,7 @@ export class CollaborationStore {
|
|
|
241
317
|
toSessionId, kind: input.kind, content, threadId,
|
|
242
318
|
fanOutIds: recipients.length > 1 ? recipients.filter((id) => id !== toSessionId) : undefined,
|
|
243
319
|
replyTo: input.replyTo?.trim() || null, status: 'pending', createdAt: now,
|
|
244
|
-
deliveredAt: null, readAt: null,
|
|
320
|
+
deliveredAt: null, readAt: null, instructions: group.instructions ? { ...group.instructions } : undefined,
|
|
245
321
|
}));
|
|
246
322
|
if (messages.some((message) => Buffer.byteLength(JSON.stringify(message)) > COLLAB_LIMITS.wire_bytes))
|
|
247
323
|
throw new CollaborationError('MESSAGE_WIRE_TOO_LARGE', `Encoded message exceeds ${COLLAB_LIMITS.wire_bytes} JSON bytes`, 413);
|
|
@@ -249,6 +325,7 @@ export class CollaborationStore {
|
|
|
249
325
|
if (key)
|
|
250
326
|
(this.document.idempotency ??= {})[key] = { hash, ids: messages.map((message) => message.id), expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
|
|
251
327
|
this.persist();
|
|
328
|
+
queueMicrotask(() => this.onMessageQueued?.());
|
|
252
329
|
return messages;
|
|
253
330
|
}
|
|
254
331
|
listMessages(groupId, limit = 200) {
|
|
@@ -332,7 +409,10 @@ export class CollaborationStore {
|
|
|
332
409
|
const existing = this.getGroup(group.id);
|
|
333
410
|
if (existing && (!existing.federated || existing.updatedAt > group.updatedAt))
|
|
334
411
|
return;
|
|
335
|
-
|
|
412
|
+
const priorContext = existing ? this.context(group.id) : undefined;
|
|
413
|
+
this.document.groups = [...this.document.groups.filter((item) => item.id !== group.id), { ...group }];
|
|
414
|
+
if (priorContext)
|
|
415
|
+
this.mergeContext(group.id, priorContext);
|
|
336
416
|
if (group.deleted)
|
|
337
417
|
this.document.messages = this.document.messages.filter((item) => item.groupId !== group.id);
|
|
338
418
|
this.persist();
|
|
@@ -402,6 +482,11 @@ export class CollaborationStore {
|
|
|
402
482
|
changed = true;
|
|
403
483
|
}
|
|
404
484
|
}
|
|
485
|
+
if (index >= 0 && rank[message.status] >= rank[this.document.messages[index].status]
|
|
486
|
+
&& typeof message.snapshot === 'string' && message.snapshot && this.document.messages[index].snapshot !== message.snapshot) {
|
|
487
|
+
this.document.messages[index].snapshot = message.snapshot.slice(0, 16000);
|
|
488
|
+
changed = true;
|
|
489
|
+
}
|
|
405
490
|
if (index >= 0 && rank[message.status] > rank[this.document.messages[index].status]) {
|
|
406
491
|
const existing = this.document.messages[index];
|
|
407
492
|
// A receipt may advance status, but never rewrite an existing message.
|
|
@@ -443,21 +528,26 @@ export class CollaborationStore {
|
|
|
443
528
|
}
|
|
444
529
|
diagnostic(id) { return this.document.transport?.[id] ?? null; }
|
|
445
530
|
recordTransport(id, diagnostic) {
|
|
446
|
-
if (!this.getMessage(id))
|
|
531
|
+
if (!this.getMessage(id) || (this.document.transport?.[id]?.checked_at ?? 0) > diagnostic.checked_at)
|
|
447
532
|
return;
|
|
448
533
|
(this.document.transport ??= {})[id] = diagnostic;
|
|
449
534
|
this.persist();
|
|
450
535
|
}
|
|
451
|
-
receipt(id) {
|
|
536
|
+
receipt(id, raw = false) {
|
|
452
537
|
const message = this.getMessage(id);
|
|
453
538
|
if (!message)
|
|
454
539
|
throw new CollaborationError('MESSAGE_NOT_FOUND', 'Message not found or no longer retained', 404);
|
|
455
540
|
const replies = this.document.messages.filter((reply) => reply.replyTo === id && reply.fromSessionId === message.toSessionId && reply.toSessionId === message.fromSessionId);
|
|
456
541
|
const diagnostic = this.diagnostic(id);
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
542
|
+
const facts = terminalMessage(message, raw);
|
|
543
|
+
return { message_id: id, thread_id: message.threadId, status: facts.status, queued_at: message.createdAt,
|
|
544
|
+
delivered_at: facts.deliveredAt, expires_at: message.expiresAt ?? null,
|
|
545
|
+
failure_reason: message.failureReason ?? null, delivery_semantics: facts.deliverySource ?? (facts.deliveredAt === null ? 'not_delivered' : 'legacy_or_unspecified'),
|
|
546
|
+
snapshot: message.snapshot ? (raw ? message.snapshot : plainCollaborationSnapshot(message.snapshot)) : null,
|
|
547
|
+
delivery: { status: facts.status,
|
|
548
|
+
stage: facts.deliveredAt !== null ? 'terminal_written' : diagnostic?.remote_received_at ? 'remote_received' : 'queued',
|
|
549
|
+
remote_received_at: diagnostic?.remote_received_at ?? null, delivered_at: facts.deliveredAt, error: message.failureReason ?? diagnostic?.last_error ?? null },
|
|
550
|
+
reply: { status: replies.length ? 'received' : 'pending', ack_at: replies.find(reply => reply.responseKind === 'ack')?.createdAt ?? null, reply_ids: replies.map(reply => reply.id) },
|
|
461
551
|
idempotency_key: message.idempotencyKey ?? null,
|
|
462
552
|
ack_at: replies.find((reply) => reply.responseKind === 'ack')?.createdAt ?? null,
|
|
463
553
|
reply_ids: replies.map((reply) => reply.id), result_ids: replies.filter((reply) => reply.responseKind === 'result').map((reply) => reply.id),
|
|
@@ -469,26 +559,21 @@ export class CollaborationStore {
|
|
|
469
559
|
last_error: diagnostic?.last_error ?? null, transport_checked_at: diagnostic?.checked_at ?? null,
|
|
470
560
|
fragments_sent: diagnostic?.fragments_sent ?? 0, fragments_total: diagnostic?.fragments_total ?? 0 };
|
|
471
561
|
}
|
|
472
|
-
sessionFacts(sessionId, online,
|
|
562
|
+
sessionFacts(sessionId, online, _legacyTurnState) {
|
|
473
563
|
const reports = this.document.messages.filter((message) => message.fromSessionId === sessionId);
|
|
474
564
|
const tasks = new Map();
|
|
475
565
|
for (const message of reports)
|
|
476
566
|
if (message.task)
|
|
477
567
|
tasks.set(message.task.task_id, { task_id: message.task.task_id, status: message.task.status, reported_at: message.createdAt, message_id: message.id });
|
|
478
|
-
|
|
479
|
-
// agent handed a task that has not yet replied is simply idle here —
|
|
480
|
-
// dispatch in flight is the sender-side receipt's concern (pending), not a
|
|
481
|
-
// fact about this session. blocked counts as active: the agent holds the
|
|
482
|
-
// task open. A failed terminal state only surfaces once no task is still
|
|
483
|
-
// in flight, since another in-progress task means the agent is busy.
|
|
484
|
-
const latest = [...tasks.values()];
|
|
485
|
-
const taskState = latest.some((task) => task.status === 'ack' || task.status === 'working' || task.status === 'blocked') ? 'active'
|
|
486
|
-
: latest.some((task) => task.status === 'failed') ? 'failed'
|
|
487
|
-
: 'idle';
|
|
488
|
-
return { session_state: online ? 'online' : 'offline', turn_state: turnState === 'done' ? 'ended' : turnState ?? 'unknown',
|
|
489
|
-
task_state: taskState, tasks: [...tasks.values()], last_heartbeat: null, last_tool_activity_at: null,
|
|
568
|
+
return { session_state: online ? 'online' : 'offline', tasks: [...tasks.values()],
|
|
490
569
|
last_message_at: reports.length ? Math.max(...reports.map((message) => message.createdAt)) : null,
|
|
491
|
-
|
|
570
|
+
last_terminal_output_at: this.peerActivity.get(sessionId)?.last_terminal_output_at ?? null,
|
|
571
|
+
activity_observed_at: this.peerActivity.get(sessionId)?.activity_observed_at ?? null,
|
|
572
|
+
last_peer_sync_at: this.peerActivity.get(sessionId)?.last_peer_sync_at ?? null,
|
|
573
|
+
activity_source: this.peerActivity.get(sessionId)?.activity_source ?? 'unavailable',
|
|
574
|
+
output_idle_seconds: this.peerActivity.get(sessionId)?.last_terminal_output_at && this.peerActivity.get(sessionId)?.activity_observed_at
|
|
575
|
+
? Math.max(0, Math.floor((this.peerActivity.get(sessionId).activity_observed_at - this.peerActivity.get(sessionId).last_terminal_output_at) / 1000)) : null,
|
|
576
|
+
task_source: 'explicit_message' };
|
|
492
577
|
}
|
|
493
578
|
/** Timeline of one task as reported by a session, oldest first. The lineage
|
|
494
579
|
* starts at the recipient's first reply carrying that task_id — a bare
|
|
@@ -538,7 +623,7 @@ export class CollaborationStore {
|
|
|
538
623
|
&& (!options.thread || message.threadId === options.thread) && (!options.kind || message.kind === options.kind)
|
|
539
624
|
&& (!options.responseKind || message.responseKind === options.responseKind))
|
|
540
625
|
.sort((a, b) => (a.sequence ?? 0) - (b.sequence ?? 0));
|
|
541
|
-
const messages = incremental || options.order === 'oldest' ? candidates.slice(0, limit) : candidates.slice().sort((a, b) =>
|
|
626
|
+
const messages = incremental || options.order === 'oldest' ? candidates.slice(0, limit) : candidates.slice().sort((a, b) => (b.sequence ?? 0) - (a.sequence ?? 0)).slice(0, limit);
|
|
542
627
|
const sequence = messages.length ? Math.max(...messages.map((message) => message.sequence ?? 0)) : after;
|
|
543
628
|
const next_cursor = Buffer.from(JSON.stringify({ scope, sequence })).toString('base64url');
|
|
544
629
|
return { messages, next_cursor, retention_gap: incremental && after < (this.document.prunedThrough?.[sessionId] ?? 0), has_more: incremental ? candidates.length > messages.length : false, consumer: options.consumer ?? null };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { plainCollaborationSnapshot } from './collaborationText.js';
|
|
1
2
|
import { normalizePromptForPaste } from './promptDelivery.js';
|
|
2
3
|
const TMUX_KEY_NAMES = {
|
|
3
4
|
enter: 'Enter', escape: 'Escape', space: 'Space', left: 'Left', right: 'Right', up: 'Up', down: 'Down',
|
|
@@ -120,10 +121,10 @@ export async function sendTmuxPaneKey(run, pane, key) {
|
|
|
120
121
|
await run(['send-keys', '-t', pane.paneId, TMUX_KEY_NAMES[key]]);
|
|
121
122
|
}
|
|
122
123
|
/** Current pane screen text (viewport), identity-checked. */
|
|
123
|
-
export async function captureTmuxPaneText(run, pane) {
|
|
124
|
+
export async function captureTmuxPaneText(run, pane, rawOutput = false) {
|
|
124
125
|
await assertSamePane(run, pane);
|
|
125
|
-
const raw = await run(['capture-pane', '-p', '-J', '-t', pane.paneId]);
|
|
126
|
-
return raw
|
|
126
|
+
const raw = await run(['capture-pane', '-p', '-J', ...(rawOutput ? ['-e'] : []), '-t', pane.paneId]);
|
|
127
|
+
return rawOutput ? raw : plainCollaborationSnapshot(raw);
|
|
127
128
|
}
|
|
128
129
|
/** Dismiss an interactive approval dialog on the member pane by pressing
|
|
129
130
|
* Enter (the highlighted option). Refuses unless the screen actually shows
|
|
@@ -153,8 +154,10 @@ export async function recoverStuckPaste(run, pane, baseline) {
|
|
|
153
154
|
}
|
|
154
155
|
/** History-inclusive pane text (scrollback + viewport) used to confirm a
|
|
155
156
|
* delivery was actually rendered by the agent, not wiped by a boot clear. */
|
|
156
|
-
export async function captureTmuxPaneHistory(run, pane, lines = 800) {
|
|
157
|
+
export async function captureTmuxPaneHistory(run, pane, lines = 800, rawOutput = false) {
|
|
157
158
|
await assertSamePane(run, pane);
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
if (!Number.isInteger(lines) || lines < 1 || lines > 10000)
|
|
160
|
+
throw new Error('INVALID_HISTORY_LINES');
|
|
161
|
+
const raw = await run(['capture-pane', '-p', '-J', ...(rawOutput ? ['-e'] : []), '-S', `-${lines}`, '-t', pane.paneId]);
|
|
162
|
+
return rawOutput ? raw : plainCollaborationSnapshot(raw);
|
|
160
163
|
}
|
package/dist/server/entry.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { X509Certificate } from 'node:crypto';
|
|
2
|
+
import { CollaborationPeerTransport, connectCollaborationRpc } from './agent/collaborationPeerTransport.js';
|
|
3
|
+
import { collaborationStore, deliverPeerCollaboration, collaborationLocalActivity } from './routes/terminal.js';
|
|
1
4
|
import { setPushTargetPeerId } from './notifications/pushService.js';
|
|
2
5
|
import { desktopDirectTargets } from './federation/desktopTargets.js';
|
|
3
6
|
import { createOpenAccessRouter } from './federation/openAccess.js';
|
|
@@ -404,11 +407,12 @@ export function startServer(options = {}) {
|
|
|
404
407
|
runtimeMonitor,
|
|
405
408
|
});
|
|
406
409
|
const { server, scheme } = createServerForApp(app, options);
|
|
410
|
+
let collaborationTransport;
|
|
407
411
|
let entrySubjectAllowed = (_subjectId) => false;
|
|
408
412
|
let routeInvitations;
|
|
409
413
|
let federationServiceId = '';
|
|
410
414
|
const desktopTargets = () => desktopDirectTargets(path.join(homedir(), '.termdock', 'desktop.json'), federationServiceId);
|
|
411
|
-
const routeAccess = new RouteAccess(path.join(homedir(), '.termdock', 'federation', 'routes.json'), Date.now, (subjectId, targetServiceId) => routeInvitations?.allows(subjectId, targetServiceId) === true, desktopTargets);
|
|
415
|
+
const routeAccess = new RouteAccess(path.join(homedir(), '.termdock', 'federation', 'routes.json'), Date.now, (subjectId, targetServiceId) => routeInvitations?.allows(subjectId, targetServiceId) === true || collaborationTransport?.canRoute(subjectId, targetServiceId) === true, desktopTargets);
|
|
412
416
|
const relayRouter = new RelayRouter({
|
|
413
417
|
authenticate: context => context,
|
|
414
418
|
allowRegister: (principal, serviceId) => routeAccess.allowRegister(principal, serviceId),
|
|
@@ -441,6 +445,11 @@ export function startServer(options = {}) {
|
|
|
441
445
|
const federation = createFederationRuntime(app, path.join(homedir(), '.termdock', 'federation'), {
|
|
442
446
|
terminal: handleTerminalWebSocket, control: handleControlWebSocket,
|
|
443
447
|
}, {
|
|
448
|
+
collaborationExchange: (subjectId, packet) => {
|
|
449
|
+
if (!collaborationTransport)
|
|
450
|
+
throw new Error('COLLABORATION_UNAVAILABLE');
|
|
451
|
+
return collaborationTransport.receive(subjectId, packet);
|
|
452
|
+
},
|
|
444
453
|
listRouteTargets: () => { refreshDirectTargets(); return routeAccess.configuredTargets().map(target => ({ ...target, available: relayRouter.hasRoute(target.serviceId) })); },
|
|
445
454
|
listRouteAccess: () => routeInvitations?.list() || [],
|
|
446
455
|
grantRouteAccess: async (issuerId, targetServiceId, subjectId, url) => {
|
|
@@ -461,7 +470,7 @@ export function startServer(options = {}) {
|
|
|
461
470
|
},
|
|
462
471
|
revokeRouteAccess: id => routeInvitations?.revoke(id) || false,
|
|
463
472
|
issueRouteTicket: (subjectId, serviceId) => routeAccess.issueRouteTicket(subjectId, serviceId),
|
|
464
|
-
hasRouteGrant: (subjectId, serviceId) => routeInvitations?.allows(subjectId, serviceId) === true,
|
|
473
|
+
hasRouteGrant: (subjectId, serviceId) => routeInvitations?.allows(subjectId, serviceId) === true || collaborationTransport?.canRoute(subjectId, serviceId) === true,
|
|
465
474
|
createRouteInvitation: (issuerId, serviceId) => {
|
|
466
475
|
if (!routeInvitations)
|
|
467
476
|
throw new Error('ROUTE_NOT_AVAILABLE');
|
|
@@ -478,6 +487,37 @@ export function startServer(options = {}) {
|
|
|
478
487
|
setPushTargetPeerId(runtime.serviceId);
|
|
479
488
|
refreshDirectTargets();
|
|
480
489
|
app.locals.passwordRuntime = runtime;
|
|
490
|
+
collaborationTransport = new CollaborationPeerTransport({
|
|
491
|
+
file: path.join(homedir(), '.termdock', 'federation', 'collaboration-peers.json'), serviceId: runtime.serviceId,
|
|
492
|
+
store: collaborationStore, deliver: deliverPeerCollaboration, activity: collaborationLocalActivity,
|
|
493
|
+
connect: async (peer, via) => {
|
|
494
|
+
if (!via)
|
|
495
|
+
return connectCollaborationRpc(runtime.identity, peer);
|
|
496
|
+
const entry = await connectCollaborationRpc(runtime.identity, via);
|
|
497
|
+
try {
|
|
498
|
+
const ticket = await entry.request({ type: 'route-ticket', serviceId: peer.serviceId });
|
|
499
|
+
if (typeof ticket.routeToken !== 'string')
|
|
500
|
+
throw new Error('ROUTE_UNAVAILABLE');
|
|
501
|
+
const url = new URL('/api/federation/relay', via.origin.replace(/^https:/, 'wss:'));
|
|
502
|
+
url.searchParams.set('routeToken', ticket.routeToken);
|
|
503
|
+
// TLS authenticates the entry; Noise pins the final destination.
|
|
504
|
+
return await connectCollaborationRpc(runtime.identity, { ...via, serviceId: peer.serviceId }, url.href);
|
|
505
|
+
}
|
|
506
|
+
finally {
|
|
507
|
+
entry.close();
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
app.locals.collaborationTransport = collaborationTransport;
|
|
512
|
+
let caFingerprint256;
|
|
513
|
+
if (options.httpsCaPath)
|
|
514
|
+
try {
|
|
515
|
+
caFingerprint256 = new X509Certificate(fs.readFileSync(options.httpsCaPath)).fingerprint256;
|
|
516
|
+
}
|
|
517
|
+
catch { /* System TLS trust remains required. */ }
|
|
518
|
+
app.locals.collaborationNode = { serviceId: runtime.serviceId, ...(caFingerprint256 ? { caFingerprint256 } : {}) };
|
|
519
|
+
collaborationTransport.start();
|
|
520
|
+
server.once('close', () => collaborationTransport?.close());
|
|
481
521
|
entrySubjectAllowed = subjectId => runtime.store.authorize({ subjectId, serviceId: runtime.serviceId, action: 'authorization.manage' }).allowed;
|
|
482
522
|
routeInvitations = new RouteInvitationStore({
|
|
483
523
|
filePath: path.join(homedir(), '.termdock', 'federation', 'route-invitations.json'), serviceId: runtime.serviceId,
|
|
@@ -251,7 +251,12 @@ export async function createFederationRuntime(app, directory, handlers, options
|
|
|
251
251
|
try {
|
|
252
252
|
if (packet.id.length > 128)
|
|
253
253
|
throw new Error('INVALID_ID');
|
|
254
|
-
if (packet.type === '
|
|
254
|
+
if (packet.type === 'collaboration-exchange') {
|
|
255
|
+
if (!options.collaborationExchange)
|
|
256
|
+
throw new Error('COLLABORATION_UNAVAILABLE');
|
|
257
|
+
send({ type: 'result', id: packet.id, ...options.collaborationExchange(subjectId, packet) });
|
|
258
|
+
}
|
|
259
|
+
else if (packet.type === 'password-parameters') {
|
|
255
260
|
send({ type: 'result', id: packet.id, ...passwordBootstrap.parameters() });
|
|
256
261
|
}
|
|
257
262
|
else if (packet.type === 'password-start') {
|
|
@@ -544,6 +549,6 @@ export async function createFederationRuntime(app, directory, handlers, options
|
|
|
544
549
|
}
|
|
545
550
|
}
|
|
546
551
|
}
|
|
547
|
-
return { serviceId, store, accept, close() { for (const channel of channels)
|
|
552
|
+
return { serviceId, identity, store, accept, close() { for (const channel of channels)
|
|
548
553
|
channel.close(); internal.close(); } };
|
|
549
554
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertPeerRegistrationAuthority } from '../agent/collaborationPeerTransport.js';
|
|
1
2
|
import { progressRoutes } from '../notifications/progressRoutes.js';
|
|
2
3
|
import { ensureNodePty } from '../utils/ensureNodePty.js';
|
|
3
4
|
import { PtySpawnBackoff, PtySpawnDeferredError } from '../utils/ptySpawnBackoff.js';
|
|
@@ -68,7 +69,7 @@ const router = express.Router();
|
|
|
68
69
|
const execFileAsync = promisify(execFile);
|
|
69
70
|
const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
70
71
|
const automationStore = new AutomationStore(`${TERMDOCK_DIR}/automations.json`);
|
|
71
|
-
const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
|
|
72
|
+
export const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
|
|
72
73
|
const collaborationRouting = new CollaborationRoutingStore(`${TERMDOCK_DIR}/collaboration-routing.json`);
|
|
73
74
|
const collaborationDeliveryWorker = new CollaborationDeliveryWorker({
|
|
74
75
|
store: collaborationStore,
|
|
@@ -1338,12 +1339,12 @@ function orchestrationSessionSnapshot(record) {
|
|
|
1338
1339
|
const route = collaborationDeliveryWorker.state(record.sessionId);
|
|
1339
1340
|
const slug = binding?.pane?.agentSlug ?? binding?.agentSlug ?? record.agentResume?.slug;
|
|
1340
1341
|
const agent = slug ? agentBySlug(slug) : backend?.agent;
|
|
1341
|
-
const sameNativeSession = !binding?.pane || Boolean(binding.pane.nativeSessionId
|
|
1342
|
-
&& binding.pane.nativeSessionId === backend?.agentSession?.sessionId && binding.pane.agentSlug === backend?.agent?.slug);
|
|
1343
|
-
const turnState = sameNativeSession ? backend?.agentSession?.status : undefined;
|
|
1344
|
-
const latestPrompt = sameNativeSession ? backend?.autoTitlePromptPayloads.at(-1)?.trim() ?? '' : '';
|
|
1345
1342
|
return {
|
|
1346
|
-
...collaborationStore.sessionFacts(record.sessionId, route.state === 'ready'
|
|
1343
|
+
...collaborationStore.sessionFacts(record.sessionId, route.state === 'ready'),
|
|
1344
|
+
last_terminal_output_at: backend?.lastOutputAt || null,
|
|
1345
|
+
activity_observed_at: backend ? Date.now() : null,
|
|
1346
|
+
activity_source: backend ? 'terminal_output' : 'unavailable',
|
|
1347
|
+
output_idle_seconds: backend?.lastOutputAt ? Math.max(0, Math.floor((Date.now() - backend.lastOutputAt) / 1000)) : null,
|
|
1347
1348
|
route_state: route.state,
|
|
1348
1349
|
route_error: route.reason,
|
|
1349
1350
|
route_checked_at: route.checkedAt,
|
|
@@ -1353,11 +1354,10 @@ function orchestrationSessionSnapshot(record) {
|
|
|
1353
1354
|
name: record.name,
|
|
1354
1355
|
cwd: backend?.cwd ?? record.cwd ?? '',
|
|
1355
1356
|
agent: agent ? { slug: agent.slug, displayName: agent.displayName } : null,
|
|
1356
|
-
status: route.state
|
|
1357
|
+
status: route.state,
|
|
1357
1358
|
capability: agent
|
|
1358
1359
|
? [agent.displayName, ...(agent.capabilities ?? []), backend?.activeProgram?.command || record.activeProgram || 'Agent 会话'].join(' · ')
|
|
1359
1360
|
: (backend?.activeProgram?.command || record.activeProgram || 'Shell 终端'),
|
|
1360
|
-
currentTask: latestPrompt || record.name,
|
|
1361
1361
|
updatedAt: backend?.lastActivity ?? record.lastActivity,
|
|
1362
1362
|
};
|
|
1363
1363
|
}
|
|
@@ -1438,16 +1438,20 @@ function resolveFrontendSessionId(input) {
|
|
|
1438
1438
|
}
|
|
1439
1439
|
function collaborationRemoteSessions() {
|
|
1440
1440
|
return Array.from(new Map(collaborationStore.list().flatMap((group) => group.remoteSessions ?? [])
|
|
1441
|
-
.map((session) =>
|
|
1441
|
+
.map((session) => {
|
|
1442
|
+
const facts = collaborationStore.sessionFacts(session.sessionId, false);
|
|
1443
|
+
const { currentTask: _currentTask, ...terminalSession } = session;
|
|
1444
|
+
const checkedAt = Math.max(session.serviceCheckedAt ?? 0, facts.last_peer_sync_at ?? 0);
|
|
1445
|
+
const online = Date.now() - checkedAt <= 15_000 && (session.serviceConnected === true || !!facts.last_peer_sync_at && Date.now() - facts.last_peer_sync_at <= 15_000);
|
|
1446
|
+
return [session.sessionId, { ...terminalSession, ...facts, session_state: online ? 'online' : 'offline',
|
|
1447
|
+
serviceConnected: online, serviceCheckedAt: checkedAt, status: online ? 'service-reachable' : 'service-unreachable', name: `${session.name} · ${session.serviceLabel}` }];
|
|
1448
|
+
})).values());
|
|
1442
1449
|
}
|
|
1443
1450
|
function tryDeliverCollaborationInbox(frontendSessionId) {
|
|
1444
1451
|
const remote = collaborationRemoteSessions().find((session) => session.sessionId === frontendSessionId);
|
|
1445
1452
|
if (remote) {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
serviceUnavailable: unavailable, reason: unavailable
|
|
1449
|
-
? `服务 ${remote.serviceLabel} 不可达:消息尚未送达,仅保存在待发送队列;请勿等待对方已收到的回复,服务重连且转发客户端恢复运行后重试投递。`
|
|
1450
|
-
: '跨服务消息尚未确认送达,正在等待在线客户端转发。' };
|
|
1453
|
+
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId), waitable: true,
|
|
1454
|
+
reason: '跨服务消息已入队,服务后台正在投递;使用 message get 查看送达回执或注册、重试原因。' };
|
|
1451
1455
|
}
|
|
1452
1456
|
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1453
1457
|
const route = collaborationDeliveryWorker.state(frontendSessionId);
|
|
@@ -1608,7 +1612,7 @@ async function resolveCollaborationRoute(frontendSessionId) {
|
|
|
1608
1612
|
}
|
|
1609
1613
|
if (!backend)
|
|
1610
1614
|
return { state: 'detached', reason: 'TMUX_BACKEND_UNAVAILABLE' };
|
|
1611
|
-
return { state: 'ready', isShell: pane.isShell, capture: async () => (
|
|
1615
|
+
return { state: 'ready', isShell: pane.isShell, capture: async () => captureTmuxPaneText(runTmux, pinned), write: async (messages) => {
|
|
1612
1616
|
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1613
1617
|
throw new Error('SESSION_REMOVED');
|
|
1614
1618
|
await writeCollaborationTmuxPane(runTmux, pinned, formatLocalCollaborationMessages(frontendSessionId, messages), runTmuxStdin);
|
|
@@ -4805,7 +4809,7 @@ async function spawnTerminalSessionUnlocked(req, input) {
|
|
|
4805
4809
|
cols,
|
|
4806
4810
|
rows,
|
|
4807
4811
|
lastActivity: Date.now(),
|
|
4808
|
-
lastOutputAt:
|
|
4812
|
+
lastOutputAt: 0,
|
|
4809
4813
|
clients: new Map(),
|
|
4810
4814
|
createdAt: Date.now(),
|
|
4811
4815
|
hasWrittenData: false,
|
|
@@ -4894,7 +4898,7 @@ async function adoptPtyHostSessions() {
|
|
|
4894
4898
|
cols: meta.cols,
|
|
4895
4899
|
rows: meta.rows,
|
|
4896
4900
|
lastActivity: Date.now(),
|
|
4897
|
-
lastOutputAt:
|
|
4901
|
+
lastOutputAt: 0,
|
|
4898
4902
|
clients: new Map(),
|
|
4899
4903
|
createdAt: meta.startedAt,
|
|
4900
4904
|
hasWrittenData: true,
|
|
@@ -5474,10 +5478,30 @@ router.delete('/operations/automations/:automationId', (req, res) => {
|
|
|
5474
5478
|
});
|
|
5475
5479
|
// Authenticated desktop connections exchange only collaboration data; no peer credentials
|
|
5476
5480
|
// or arbitrary remote URLs are accepted by the server.
|
|
5477
|
-
router.get('/operations/collaboration-federation', (
|
|
5478
|
-
res.json({ protocolVersion: 2, limits: COLLAB_LIMITS, ...collaborationStore.federationSnapshot(),
|
|
5481
|
+
router.get('/operations/collaboration-federation', (req, res) => {
|
|
5482
|
+
res.json({ protocolVersion: 2, serverTransport: req.app.locals.collaborationNode, limits: COLLAB_LIMITS, ...collaborationStore.federationSnapshot(),
|
|
5479
5483
|
sessions: globalSessionState.sessions.map(orchestrationSessionSnapshot) });
|
|
5480
5484
|
});
|
|
5485
|
+
router.post('/operations/collaboration-peers', (req, res) => {
|
|
5486
|
+
try {
|
|
5487
|
+
assertPeerRegistrationAuthority(req);
|
|
5488
|
+
if (!req.app.locals.collaborationTransport)
|
|
5489
|
+
return res.status(503).json({ error: 'COLLABORATION_UNAVAILABLE' });
|
|
5490
|
+
req.app.locals.collaborationTransport.configure(req.body.groupId, req.body.localOrigin, req.body.nodes);
|
|
5491
|
+
res.json({ ok: true });
|
|
5492
|
+
}
|
|
5493
|
+
catch (error) {
|
|
5494
|
+
res.status(400).json({ error: getErrorMessage(error) });
|
|
5495
|
+
}
|
|
5496
|
+
});
|
|
5497
|
+
export function collaborationLocalActivity() {
|
|
5498
|
+
return globalSessionState.sessions.map(record => {
|
|
5499
|
+
const backendId = collaborationRouting.get(record.sessionId)?.backendSessionId ?? record.backendSessionId;
|
|
5500
|
+
const backend = backendId ? terminalSessions.get(backendId) : undefined;
|
|
5501
|
+
return { sessionId: record.sessionId, last_terminal_output_at: backend?.lastOutputAt || null, activity_observed_at: backend ? Date.now() : null };
|
|
5502
|
+
});
|
|
5503
|
+
}
|
|
5504
|
+
export function deliverPeerCollaboration(sessionId) { void tryDeliverCollaborationInbox(sessionId); }
|
|
5481
5505
|
router.post('/operations/collaboration-federation', (req, res) => {
|
|
5482
5506
|
try {
|
|
5483
5507
|
const group = req.body?.group;
|
|
@@ -5832,7 +5856,11 @@ router.post('/operations/orchestration/drive', async (req, res) => {
|
|
|
5832
5856
|
return res.json({ ok: true, action, sessionId: target, approved: true });
|
|
5833
5857
|
}
|
|
5834
5858
|
if (action === 'capture') {
|
|
5835
|
-
const
|
|
5859
|
+
const lines = req.body.lines;
|
|
5860
|
+
if (lines !== undefined && (!Number.isInteger(lines) || lines < 1 || lines > 10000))
|
|
5861
|
+
return res.status(400).json({ error: 'lines 必须为 1..10000 的历史行数' });
|
|
5862
|
+
const snapshot = lines === undefined ? await captureTmuxPaneText(runTmux, pane, req.body.raw === true)
|
|
5863
|
+
: await captureTmuxPaneHistory(runTmux, pane, lines, req.body.raw === true);
|
|
5836
5864
|
return res.json({ ok: true, action, sessionId: target, snapshot });
|
|
5837
5865
|
}
|
|
5838
5866
|
if (action === 'run') {
|
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.232",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-GHB6P5NPYFEdDR/sPm0FHVSEBdpzcJ8Jghr5X2uikWU=",
|
|
10
|
+
"clientBundleHash": "sha256-AWAHyBvSOOJ9eViAjt+iC2NSH0Uex5K8eEGrkeFVEFI=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|