termdock 1.4.298 → 1.4.300
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +2 -1
- package/dist/client/assets/App-7k2I_09f.js +179 -0
- package/dist/client/assets/App-7k2I_09f.js.br +0 -0
- package/dist/client/assets/App-7k2I_09f.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js +12 -0
- package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BrAwRa20.js → ChangeWalkthroughPanel-RoMt6giT.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js +1 -0
- package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-FlTbV9e1.js +2 -0
- package/dist/client/assets/ComputerControlView-AdjmvNUa.js +5 -0
- package/dist/client/assets/ComputerControlView-AdjmvNUa.js.br +0 -0
- package/dist/client/assets/ComputerControlView-AdjmvNUa.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-DiAEIEvj.js → DagPlayground-x8-mp6wC.js} +1 -1
- package/dist/client/assets/DagPlayground-x8-mp6wC.js.br +0 -0
- package/dist/client/assets/DagPlayground-x8-mp6wC.js.gz +0 -0
- package/dist/client/assets/{DiffLab-BOXARpIG.js → DiffLab-B9KLZ--H.js} +1 -1
- package/dist/client/assets/DiffLab-B9KLZ--H.js.br +0 -0
- package/dist/client/assets/DiffLab-B9KLZ--H.js.gz +0 -0
- package/dist/client/assets/{DiffReview-C90Qt0Gx.js → DiffReview-IEcDVREB.js} +1 -1
- package/dist/client/assets/DiffReview-IEcDVREB.js.br +0 -0
- package/dist/client/assets/DiffReview-IEcDVREB.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-CrVXGE1A.js → DiffReviewLab-lWra5pTJ.js} +2 -2
- package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-BA_9bZxs.js → DiffViewer-B2Rue9PA.js} +8 -8
- package/dist/client/assets/DiffViewer-B2Rue9PA.js.br +0 -0
- package/dist/client/assets/DiffViewer-B2Rue9PA.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-BX75IksJ.js → ModelPreview-NMQsfqAe.js} +1 -1
- package/dist/client/assets/ModelPreview-NMQsfqAe.js.br +0 -0
- package/dist/client/assets/ModelPreview-NMQsfqAe.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BOBbegdP.js +1677 -0
- package/dist/client/assets/RightSidebar-BOBbegdP.js.br +0 -0
- package/dist/client/assets/RightSidebar-BOBbegdP.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-BGF7DKjR.js → abnfDiagram-VCTEODGH-BIVuPQbA.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.gz +0 -0
- package/dist/client/assets/app-CHoOHnjf.js +35 -0
- package/dist/client/assets/app-CHoOHnjf.js.br +0 -0
- package/dist/client/assets/app-CHoOHnjf.js.gz +0 -0
- package/dist/client/assets/{arc-DZUR8G8M.js → arc-CNTT1p1A.js} +1 -1
- package/dist/client/assets/arc-CNTT1p1A.js.br +0 -0
- package/dist/client/assets/arc-CNTT1p1A.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-CWXE4hoZ.js → architectureDiagram-5GKGNRK7-Cl3gg8ZE.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-BZdUQzE2.js → blockDiagram-I7D4REHJ-Sxmfy_3_.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DA7m-AbK.js → c4Diagram-7LVT6UL2-DaTnrOn7.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.gz +0 -0
- package/dist/client/assets/channel-CEvBVvt3.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-Dg-zs5cw.js → chunk-2Q5K7J3B-BoiPA32P.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-DKMA2xz_.js → chunk-5VM5RSS4-G5G1YBLk.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-ByyS2RgO.js → chunk-F27PBJKO-BNHv_EnL.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-DTTmE4mJ.js → chunk-IMKFNOWR-CxgtyKdq.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-D9nr-n4D.js → chunk-JWPE2WC7-UO-0iK-G.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-BPVZ3XrQ.js → chunk-POPQ4Y6H-Cox9z-e2.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-Ctn5SKJf.js → chunk-SVP7TREG-B2W8-Lxh.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-BCNSq9V4.js → chunk-TICWLB2K-BYDr9nkD.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-BlJ1FL7I.js → chunk-XXDRQBXY-atArreox.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-nSmGM9Fl.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-nSmGM9Fl.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-Cv4gLm7L.js → cose-bilkent-JH36ORCC-CvfJj195.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-BNbPniZY.js → cynefin-OW5HDTMX-ByWE_fUn.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DFR2fyKM.js → cynefinDiagram-5FMLGOSQ-BRT0d0MN.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-DM9QOAp3.js → dagre-GXQ25YYZ-Twtyv7ud.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-DqeCdIIK.js → diagram-S7CK7UJ4-Vl06hZtd.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-Cc1KiE_N.js → diagram-UQ7AKVKN-Cp39Mpse.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-I_0NpClr.js → diagram-VSXAHHWV-Drchji0N.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-DLjDAEEa.js → diagram-VX7I27RA-rp8n_lct.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-DHo74-rP.js → diagram-Z3DM3KII-BEGDaMBm.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-S9dCP2Z9.js → ebnfDiagram-PWID7BFC-kvgdD4A7.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-CR99Mjq9.js → erDiagram-RLTQ6QDP-DcYHv9hK.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-CIqCbTFC.js → flowDiagram-HODETNUW-T9FQKn1F.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CSpUeAKu.js → ganttDiagram-EL5Y4UJY-BEB_X-y5.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-C3GqyqzH.js → gestureArbiter-Do0TIdkh.js} +1 -1
- package/dist/client/assets/gestureArbiter-Do0TIdkh.js.br +0 -0
- package/dist/client/assets/gestureArbiter-Do0TIdkh.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DBICHCGp.js → gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.gz +0 -0
- package/dist/client/assets/{index-C6ftfFlG.css → index-D2_-ypCL.css} +1 -1
- package/dist/client/assets/index-D2_-ypCL.css.br +0 -0
- package/dist/client/assets/index-D2_-ypCL.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-CjPrG37z.js → infoDiagram-27XIBGKW-B6y0nIx8.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-C718heAs.js → ishikawaDiagram-5VMMS53U-ebGuykGn.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-fu76FPSY.js → journeyDiagram-3NMN7TZE-DJDkcIq5.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-BVj6v9-z.js → kanban-definition-UXKFOSKX-pTBbviRZ.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.gz +0 -0
- package/dist/client/assets/{layout-Dspjm0zn.js → layout-Dw9FB4Uc.js} +1 -1
- package/dist/client/assets/layout-Dw9FB4Uc.js.br +0 -0
- package/dist/client/assets/layout-Dw9FB4Uc.js.gz +0 -0
- package/dist/client/assets/{linear-BktGv6ei.js → linear-CyLfVGgB.js} +1 -1
- package/dist/client/assets/linear-CyLfVGgB.js.br +0 -0
- package/dist/client/assets/linear-CyLfVGgB.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-Dt2TVLfQ.js → mermaid.core-xRkirK0l.js} +5 -5
- package/dist/client/assets/mermaid.core-xRkirK0l.js.br +0 -0
- package/dist/client/assets/mermaid.core-xRkirK0l.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-DwxtrCyv.js → mindmap-definition-YA3MSWOX-BxvaDEGQ.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-B5Inu7kn.js → passwordBootstrap-CaYbIQDC.js} +1 -1
- package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-eDDhkygS.js → pegDiagram-XKGWAZYB-Cxh-Bbay.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-C-A_a53h.js → pieDiagram-E7YTZNPT-CgOsk0Ai.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BRmQ1U56.js → quadrantDiagram-AXDQQJYC-D1S_gKcT.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BRmkfIUn.js → railroadDiagram-O6MQD6OU-BG5e87Np.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.gz +0 -0
- package/dist/client/assets/rdpSession-DL1rZ49m.js +1 -0
- package/dist/client/assets/rdpSession-DL1rZ49m.js.br +0 -0
- package/dist/client/assets/rdpSession-DL1rZ49m.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-DB039WbW.js +1 -0
- package/dist/client/assets/referenceLongPress-DB039WbW.js.br +0 -0
- package/dist/client/assets/referenceLongPress-DB039WbW.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-C95pjpXU.js → requirementDiagram-BXWQKSXE-C37DsQfu.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-BD7SJZ8y.js → sankeyDiagram-P5KCCOFB-DLzhCYoj.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-D0egOILU.js → sequenceDiagram-WJ2MYXX4-DeVpSvB0.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-BaJGHebj.js → sizeCapture-INFHLROL-CE03E66U.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.gz +0 -0
- package/dist/client/assets/{sparkles-D4t6BWgT.js → sparkles-ButxQzs4.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-DEZo4cxr.js → stateDiagram-D77RDMKH-DuqhHT3k.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CxfzEwAH.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-DSZuZ910.js → swimlanes-42K2YHIH-DxkSdJ8g.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-B_muKJLi.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-Bw0gUCrJ.js → timeline-definition-24CTP7MA-DVpcJ6y5.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-Cf9mn5Ug.js → triangle-alert-BRy562K_.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-B0gzw-En.js → vennDiagram-4TSXK5OY-BEKXLWGa.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.gz +0 -0
- package/dist/client/assets/{videoCompression-CyTyZG2b.js → videoCompression-CoVXZ8qw.js} +1 -1
- package/dist/client/assets/videoCompression-CoVXZ8qw.js.br +0 -0
- package/dist/client/assets/videoCompression-CoVXZ8qw.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-vaiRXtUT.js → wardleyDiagram-VM6X3IG4-Cj30GTa9.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-Dosv-6JB.js → xychartDiagram-S5SC5T6Z-Cm7NEdCl.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.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 +45 -6
- package/dist/server/agent/collaborationRoutes.js +4 -2
- package/dist/server/agent/collaborationStore.js +9 -0
- package/dist/server/agent/collaborationTaskCli.js +11 -3
- package/dist/server/agent/collaborationTaskService.js +14 -8
- package/dist/server/agent/collaborationTaskStore.js +67 -25
- package/dist/server/agent/integrationCli.js +227 -0
- package/dist/server/agent/integrationServer.js +326 -0
- package/dist/server/agent/integrationSessions.js +296 -0
- package/dist/server/agent/integrationStore.js +199 -0
- package/dist/server/cli.js +29 -0
- package/dist/server/entry.js +26 -7
- package/dist/server/routes/filesystem.js +120 -87
- package/dist/server/routes/terminal.js +158 -4
- package/dist/server/utils/localAccess.js +27 -7
- package/dist/server/utils/sessionDisplayName.js +53 -0
- package/docs/integrations.md +239 -0
- package/package.json +3 -2
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-D7FKaW3e.js +0 -179
- package/dist/client/assets/App-D7FKaW3e.js.br +0 -0
- package/dist/client/assets/App-D7FKaW3e.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js +0 -7
- package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CU5vv8Fm.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BrAwRa20.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BrAwRa20.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CH6mfIDe.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-DkhB6WXO.js +0 -2
- package/dist/client/assets/ComputerControlView-CI-TMTRg.js +0 -5
- package/dist/client/assets/ComputerControlView-CI-TMTRg.js.br +0 -0
- package/dist/client/assets/ComputerControlView-CI-TMTRg.js.gz +0 -0
- package/dist/client/assets/DagPlayground-DiAEIEvj.js.br +0 -0
- package/dist/client/assets/DagPlayground-DiAEIEvj.js.gz +0 -0
- package/dist/client/assets/DiffLab-BOXARpIG.js.br +0 -0
- package/dist/client/assets/DiffLab-BOXARpIG.js.gz +0 -0
- package/dist/client/assets/DiffReview-C90Qt0Gx.js.br +0 -0
- package/dist/client/assets/DiffReview-C90Qt0Gx.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CrVXGE1A.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CrVXGE1A.js.gz +0 -0
- package/dist/client/assets/DiffViewer-BA_9bZxs.js.br +0 -0
- package/dist/client/assets/DiffViewer-BA_9bZxs.js.gz +0 -0
- package/dist/client/assets/ModelPreview-BX75IksJ.js.br +0 -0
- package/dist/client/assets/ModelPreview-BX75IksJ.js.gz +0 -0
- package/dist/client/assets/RightSidebar-B_dO0nU5.js +0 -1678
- package/dist/client/assets/RightSidebar-B_dO0nU5.js.br +0 -0
- package/dist/client/assets/RightSidebar-B_dO0nU5.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BGF7DKjR.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BGF7DKjR.js.gz +0 -0
- package/dist/client/assets/app-BJP8Xgmi.js +0 -35
- package/dist/client/assets/app-BJP8Xgmi.js.br +0 -0
- package/dist/client/assets/app-BJP8Xgmi.js.gz +0 -0
- package/dist/client/assets/arc-DZUR8G8M.js.br +0 -0
- package/dist/client/assets/arc-DZUR8G8M.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CWXE4hoZ.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CWXE4hoZ.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BZdUQzE2.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BZdUQzE2.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DA7m-AbK.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DA7m-AbK.js.gz +0 -0
- package/dist/client/assets/channel-DD06PSxm.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-ByyS2RgO.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-ByyS2RgO.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DTTmE4mJ.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DTTmE4mJ.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-Ctn5SKJf.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-Ctn5SKJf.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BCNSq9V4.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BCNSq9V4.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-DkVrFG7y.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DkVrFG7y.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Cv4gLm7L.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Cv4gLm7L.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BNbPniZY.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BNbPniZY.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DFR2fyKM.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DFR2fyKM.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-DM9QOAp3.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-DM9QOAp3.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DqeCdIIK.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DqeCdIIK.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cc1KiE_N.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cc1KiE_N.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-I_0NpClr.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-I_0NpClr.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DLjDAEEa.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DLjDAEEa.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-DHo74-rP.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-DHo74-rP.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-S9dCP2Z9.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-S9dCP2Z9.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-CR99Mjq9.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-CR99Mjq9.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CIqCbTFC.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CIqCbTFC.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CSpUeAKu.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CSpUeAKu.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-C3GqyqzH.js.br +0 -0
- package/dist/client/assets/gestureArbiter-C3GqyqzH.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DBICHCGp.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DBICHCGp.js.gz +0 -0
- package/dist/client/assets/index-C6ftfFlG.css.br +0 -0
- package/dist/client/assets/index-C6ftfFlG.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C718heAs.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C718heAs.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-fu76FPSY.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-fu76FPSY.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BVj6v9-z.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BVj6v9-z.js.gz +0 -0
- package/dist/client/assets/layout-Dspjm0zn.js.br +0 -0
- package/dist/client/assets/layout-Dspjm0zn.js.gz +0 -0
- package/dist/client/assets/linear-BktGv6ei.js.br +0 -0
- package/dist/client/assets/linear-BktGv6ei.js.gz +0 -0
- package/dist/client/assets/mermaid.core-Dt2TVLfQ.js.br +0 -0
- package/dist/client/assets/mermaid.core-Dt2TVLfQ.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DwxtrCyv.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DwxtrCyv.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-B5Inu7kn.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-B5Inu7kn.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-eDDhkygS.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-eDDhkygS.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-C-A_a53h.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-C-A_a53h.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BRmQ1U56.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BRmQ1U56.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BRmkfIUn.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BRmkfIUn.js.gz +0 -0
- package/dist/client/assets/rdpSession-DknTu7rD.js +0 -1
- package/dist/client/assets/rdpSession-DknTu7rD.js.br +0 -0
- package/dist/client/assets/rdpSession-DknTu7rD.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-CnGp4Gdi.js +0 -1
- package/dist/client/assets/referenceLongPress-CnGp4Gdi.js.br +0 -0
- package/dist/client/assets/referenceLongPress-CnGp4Gdi.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C95pjpXU.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C95pjpXU.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BD7SJZ8y.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BD7SJZ8y.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D0egOILU.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D0egOILU.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BaJGHebj.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BaJGHebj.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DEZo4cxr.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DEZo4cxr.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CJg7AUmk.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-DSZuZ910.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-DSZuZ910.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CW58IimW.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-Bw0gUCrJ.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-Bw0gUCrJ.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-B0gzw-En.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-B0gzw-En.js.gz +0 -0
- package/dist/client/assets/videoCompression-CyTyZG2b.js.br +0 -0
- package/dist/client/assets/videoCompression-CyTyZG2b.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-vaiRXtUT.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-vaiRXtUT.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Dosv-6JB.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Dosv-6JB.js.gz +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { collaborationSessionDisplayName } from '../utils/sessionDisplayName.js';
|
|
2
|
+
import { integrationError } from '../agent/integrationStore.js';
|
|
3
|
+
import { integrationLaunchCommand } from '../agent/integrationSessions.js';
|
|
1
4
|
import { ensureTeam } from '../agent/collaborationTeam.js';
|
|
2
5
|
import { androidRecordings } from '../android/recording.js';
|
|
3
6
|
import { assertPeerRegistrationAuthority } from '../agent/collaborationPeerTransport.js';
|
|
@@ -26,7 +29,7 @@ import { gitStatusCache } from '../utils/gitStatus.js';
|
|
|
26
29
|
import { getPtyHostManager } from '../ptyhost/manager.js';
|
|
27
30
|
import { pathValidator } from '../utils/pathValidator.js';
|
|
28
31
|
import { TERMINAL, TMUX } from '../config.js';
|
|
29
|
-
import { localAccessManager } from '../utils/localAccess.js';
|
|
32
|
+
import { localAccessManager, localAccessInterfaceUrl } from '../utils/localAccess.js';
|
|
30
33
|
import { normalizeLocalAccessName, getLocaleSetting, setLocaleSetting, getContextDraftHeightSetting, setContextDraftHeightSetting, getAutoRenameAgentsSetting, setAutoRenameAgentsSetting, getAutoRenameNamerSetting, setAutoRenameNamerSetting, getAutoRenameModelsSetting, setAutoRenameModelsSetting, getAutoRenameIntervalMinutesSetting, setAutoRenameIntervalMinutesSetting, getAutoRenamePromptPreferenceSetting, setAutoRenamePromptPreferenceSetting, getAutoRenamePromptPayloadCharsSetting, setAutoRenamePromptPayloadCharsSetting, getNewSessionAgentSlugSetting, setNewSessionAgentSlugSetting, getRunningSessionButtonEnabledSetting, getAttentionButtonEnabledSetting, getCollaborationPanelsSetting, setCollaborationPanelSetting, getAndroidPanelSetting, setAndroidPanelSetting, normalizeAndroidPanel, getCollaborationFloatingGroupIdSetting, setCollaborationFloatingGroupIdSetting, getServiceSwitcherExpandedSetting, setServiceSwitcherExpandedSetting, setRunningSessionButtonEnabledSetting, setAttentionButtonEnabledSetting, getFileSortModesSetting, getHideGitIgnoredRootsSetting, getGitIgnoreExceptionsSetting, setGitIgnoreExceptionsSetting, setHideGitIgnoredRootSetting, setFileSortModesSetting, setFileSortModeSetting, getNestedGitScanRootsSetting, setNestedGitScanRootSetting, getPinnedExplorerRootsSetting, setPinnedExplorerRootsSetting, setPinnedExplorerRootSetting, watchPinnedExplorerRootsSetting, getActiveGitReposSetting, setActiveGitRepoSetting, } from '../utils/settings.js';
|
|
31
34
|
import { loadContextDraft, saveContextDraft } from '../utils/contextDraft.js';
|
|
32
35
|
import { getOnboardingServerUrl } from '../onboardingServer.js';
|
|
@@ -435,7 +438,15 @@ async function flushClientStateBroadcast() {
|
|
|
435
438
|
}
|
|
436
439
|
}
|
|
437
440
|
}
|
|
441
|
+
const integrationSessionObservers = new Set();
|
|
442
|
+
function notifyIntegrationSessions() { for (const listener of integrationSessionObservers) {
|
|
443
|
+
try {
|
|
444
|
+
listener();
|
|
445
|
+
}
|
|
446
|
+
catch { /* Observation failure does not affect terminal clients. */ }
|
|
447
|
+
} }
|
|
438
448
|
function broadcastClientState() {
|
|
449
|
+
notifyIntegrationSessions();
|
|
439
450
|
if (broadcastClientStateTimer) {
|
|
440
451
|
return;
|
|
441
452
|
}
|
|
@@ -1401,7 +1412,12 @@ function orchestrationSessionSnapshot(record) {
|
|
|
1401
1412
|
sessionId: record.sessionId,
|
|
1402
1413
|
agentNativeSessionId: binding?.pane ? binding.pane.nativeSessionId : binding?.nativeSessionId ?? backend?.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
|
|
1403
1414
|
backendSessionId: backendId ?? null,
|
|
1404
|
-
name: record
|
|
1415
|
+
name: collaborationSessionDisplayName({ ...record,
|
|
1416
|
+
activeProgram: backend?.activeProgram?.command ?? record.activeProgram,
|
|
1417
|
+
shellTitle: backend?.lastOscTitle ?? record.shellTitle,
|
|
1418
|
+
cwd: backend?.cwd ?? record.cwd,
|
|
1419
|
+
agent: agent ? { displayName: agent.displayName } : null,
|
|
1420
|
+
}),
|
|
1405
1421
|
cwd: backend?.cwd ?? record.cwd ?? '',
|
|
1406
1422
|
agent: agent ? { slug: agent.slug, displayName: agent.displayName } : null,
|
|
1407
1423
|
status: route.state === 'unchecked' && terminalConnected ? 'terminal-connected' : route.state,
|
|
@@ -1794,6 +1810,143 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1794
1810
|
}, 300).unref?.();
|
|
1795
1811
|
return { group: updatedGroup, session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
|
|
1796
1812
|
}
|
|
1813
|
+
/** Restricted session adapter: only preconfigured argv and the exact owned
|
|
1814
|
+
* terminal can be launched; no remote drive or arbitrary shell commands. */
|
|
1815
|
+
async function integrationPaneIsIdleShell(pane) {
|
|
1816
|
+
if (!pane.isShell)
|
|
1817
|
+
return false;
|
|
1818
|
+
try {
|
|
1819
|
+
const { stdout } = await execFileAsync('ps', ['-p', String(pane.panePid), '-o', 'pgid=,tpgid=,args='], { timeout: 3000, maxBuffer: 16384 });
|
|
1820
|
+
const row = stdout.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
1821
|
+
if (!row || row[1] !== row[2])
|
|
1822
|
+
return false;
|
|
1823
|
+
const argv = splitCommandToArgv(row[3]);
|
|
1824
|
+
const command = normalizeProgramName(argv[0]?.replace(/^-/, ''))?.toLowerCase();
|
|
1825
|
+
return !!command && shellNamesBackend.has(command) && argv.slice(1).every(arg => ['-l', '-i', '-il', '-li', '--login', '--interactive'].includes(arg));
|
|
1826
|
+
}
|
|
1827
|
+
catch {
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
function integrationTerminalRecord(record) {
|
|
1832
|
+
const terminal = globalSessionState.sessions.find(s => s.sessionId === record.session_id);
|
|
1833
|
+
if (terminal && (terminal.mode !== 'tmux' || terminal.tmuxSessionName !== `wt-integration-${record.session_id}`))
|
|
1834
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Owned TD terminal binding changed', 409);
|
|
1835
|
+
return terminal;
|
|
1836
|
+
}
|
|
1837
|
+
export const integrationSessionAdapter = {
|
|
1838
|
+
subscribe(listener) { integrationSessionObservers.add(listener); return () => { integrationSessionObservers.delete(listener); }; },
|
|
1839
|
+
async inspect(record) {
|
|
1840
|
+
const terminal = integrationTerminalRecord(record);
|
|
1841
|
+
if (!terminal)
|
|
1842
|
+
return { exists: false, running: false, shell: false, agentSlug: null, nativeId: null };
|
|
1843
|
+
const binding = { sessionId: record.session_id, backendSessionId: terminal.backendSessionId,
|
|
1844
|
+
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: record.agent_slug,
|
|
1845
|
+
nativeSessionId: record.agent_native_session_id, pane: record.terminal_binding ?? null };
|
|
1846
|
+
const inspected = await inspectCollaborationTmux(binding);
|
|
1847
|
+
if (inspected.state === 'offline')
|
|
1848
|
+
return { exists: false, running: false, shell: false, agentSlug: null, nativeId: null };
|
|
1849
|
+
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1850
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Owned tmux pane cannot be uniquely verified', 409);
|
|
1851
|
+
const pane = inspected.pane;
|
|
1852
|
+
const layout = await getTmuxLayout(terminal.tmuxSessionName);
|
|
1853
|
+
if (layout.windows.flatMap(window => window.panes).length !== 1 || layout.activePaneId !== pane.paneId)
|
|
1854
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Integration native binding requires the single owned pane', 409);
|
|
1855
|
+
const backend = terminal.backendSessionId ? terminalSessions.get(terminal.backendSessionId) : null;
|
|
1856
|
+
// Hook evidence is fresh process evidence only on the single owned pane.
|
|
1857
|
+
// A recovered last-known UUID must not prove a new launch succeeded.
|
|
1858
|
+
const nativeId = pane.agentSlug ? pane.nativeSessionId ?? (backend?.agent?.slug === pane.agentSlug && backend.agentSession?.rich && !backend.agentResumeRecovered ? backend.agentSession.sessionId : null) ?? null : null;
|
|
1859
|
+
if (nativeId && (nativeId.length > 256 || /[\x00-\x1f\x7f]/.test(nativeId)))
|
|
1860
|
+
integrationError('NATIVE_SESSION_ID_INVALID', 'Observed native session id is invalid', 409);
|
|
1861
|
+
const route = collaborationRouting.get(record.session_id);
|
|
1862
|
+
if (nativeId && (!record.agent_native_session_id || record.agent_native_session_id === nativeId) && route?.pane
|
|
1863
|
+
&& route.pane.paneId === pane.paneId && route.pane.panePid === pane.panePid && route.pane.serverPid === pane.serverPid && route.pane.sessionId === pane.sessionId
|
|
1864
|
+
&& route.pane.nativeSessionId !== nativeId)
|
|
1865
|
+
collaborationRouting.bind({ ...route, nativeSessionId: nativeId, pane: { ...route.pane, nativeSessionId: nativeId } });
|
|
1866
|
+
return { exists: true, running: Boolean(pane.agentSlug), shell: await integrationPaneIsIdleShell(pane), agentSlug: pane.agentSlug || null, nativeId };
|
|
1867
|
+
},
|
|
1868
|
+
async create(record, prepared) {
|
|
1869
|
+
await listDetectedAgentLaunchers();
|
|
1870
|
+
if (!agentBySlug(record.agent_slug))
|
|
1871
|
+
integrationError('COLLAB_AGENT_UNAVAILABLE', 'Configured agent plugin is not installed');
|
|
1872
|
+
if (integrationTerminalRecord(record))
|
|
1873
|
+
integrationError('SESSION_CREATE_OUTCOME_UNKNOWN', 'Creation intent already has a terminal; inspect it instead of relaunching', 409);
|
|
1874
|
+
await launchIntegrationTerminal(record, false, prepared);
|
|
1875
|
+
},
|
|
1876
|
+
async restore(record, prepared) {
|
|
1877
|
+
await listDetectedAgentLaunchers();
|
|
1878
|
+
const agent = agentBySlug(record.agent_slug);
|
|
1879
|
+
if (!record.agent_native_session_id || !agent || !buildResumeCommand(agent, record.agent_native_session_id, null))
|
|
1880
|
+
integrationError('EXACT_RESUME_UNSUPPORTED', 'Agent plugin must support exact native resume');
|
|
1881
|
+
if (findActiveAgentResumeOwner('', { slug: record.agent_slug, nativeSessionId: record.agent_native_session_id, command: '' }))
|
|
1882
|
+
integrationError('NATIVE_SESSION_ALREADY_RUNNING', 'Native Agent conversation is already running', 409);
|
|
1883
|
+
await launchIntegrationTerminal(record, true, prepared);
|
|
1884
|
+
},
|
|
1885
|
+
};
|
|
1886
|
+
async function launchIntegrationTerminal(record, restoring, prepared) {
|
|
1887
|
+
await collaborationDeliveryWorker.reconfigure(record.session_id, async () => {
|
|
1888
|
+
const group = collaborationStore.getGroup(record.group_id);
|
|
1889
|
+
if (!group || group.deleted)
|
|
1890
|
+
integrationError('TASK_GROUP_NOT_FOUND', 'Integration group no longer exists', 404);
|
|
1891
|
+
const original = await integrationSessionAdapter.inspect(record);
|
|
1892
|
+
if (!original.exists) {
|
|
1893
|
+
let tmuxExists = false;
|
|
1894
|
+
try {
|
|
1895
|
+
await runTmux(['has-session', '-t', `=wt-integration-${record.session_id}`]);
|
|
1896
|
+
tmuxExists = true;
|
|
1897
|
+
}
|
|
1898
|
+
catch (error) {
|
|
1899
|
+
if (!/can't find session|no server running|no sessions|error connecting.*No such file/i.test(getErrorMessage(error)))
|
|
1900
|
+
throw error;
|
|
1901
|
+
}
|
|
1902
|
+
if (tmuxExists)
|
|
1903
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'A terminal with this name exists without the owned binding', 409);
|
|
1904
|
+
}
|
|
1905
|
+
const opened = await openInventorySession({}, { preferredFrontendSessionId: record.session_id,
|
|
1906
|
+
mode: 'tmux', tmuxSessionName: `wt-integration-${record.session_id}`, cwd: record.cwd,
|
|
1907
|
+
name: `${record.agent_slug} · ${group.name}`, customName: true });
|
|
1908
|
+
if (opened.session.sessionId !== record.session_id)
|
|
1909
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Terminal id changed', 409);
|
|
1910
|
+
const terminal = integrationTerminalRecord(record);
|
|
1911
|
+
const backend = terminalSessions.get(opened.terminalSession.sessionId);
|
|
1912
|
+
if (!backend)
|
|
1913
|
+
integrationError('SESSION_BACKEND_UNAVAILABLE', 'Owned terminal backend is unavailable', 503);
|
|
1914
|
+
const inspected = await inspectCollaborationTmux({ sessionId: record.session_id, backendSessionId: opened.terminalSession.sessionId,
|
|
1915
|
+
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: null, nativeSessionId: null,
|
|
1916
|
+
// A recreated tmux may get a new pane only during explicit exact resume.
|
|
1917
|
+
pane: original.exists ? record.terminal_binding ?? null : null }, null, true);
|
|
1918
|
+
if (inspected.state !== 'ready' || !inspected.pane || !await integrationPaneIsIdleShell(inspected.pane))
|
|
1919
|
+
integrationError('SESSION_TARGET_NOT_SHELL', 'Refusing to launch into an unverified shell', 409);
|
|
1920
|
+
const pane = inspected.pane;
|
|
1921
|
+
const pinned = { serverPid: pane.serverPid, sessionId: pane.sessionId,
|
|
1922
|
+
paneId: pane.paneId, panePid: pane.panePid, agentSlug: record.agent_slug, nativeSessionId: record.agent_native_session_id };
|
|
1923
|
+
prepared(pinned); // Must persist before the command enters the terminal.
|
|
1924
|
+
collaborationRouting.bind({ sessionId: record.session_id, backendSessionId: opened.terminalSession.sessionId,
|
|
1925
|
+
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: record.agent_slug, nativeSessionId: record.agent_native_session_id, pane: pinned });
|
|
1926
|
+
const latest = collaborationStore.getGroup(group.id);
|
|
1927
|
+
if (!latest || latest.deleted)
|
|
1928
|
+
integrationError('TASK_GROUP_NOT_FOUND', 'Integration group changed', 409);
|
|
1929
|
+
collaborationStore.save({ id: latest.id, name: latest.name, sessionIds: [...new Set([...latest.sessionIds, record.session_id])] });
|
|
1930
|
+
if (restoring)
|
|
1931
|
+
upsertGlobalSessionRecord({ ...terminal, agentResume: { slug: record.agent_slug, sessionId: record.agent_native_session_id,
|
|
1932
|
+
launchArgv: [record.profile.executable, ...record.profile.argv], updatedAt: Date.now() } });
|
|
1933
|
+
await persistGlobalStateNow();
|
|
1934
|
+
// Recheck the actual pinned shell immediately before writing. This cannot
|
|
1935
|
+
// use an active-pane target which might have changed during preparation.
|
|
1936
|
+
const shell = await inspectCollaborationTmux({ sessionId: record.session_id, backendSessionId: opened.terminalSession.sessionId,
|
|
1937
|
+
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: null, nativeSessionId: null, pane: pinned });
|
|
1938
|
+
if (shell.state !== 'ready' || !shell.pane || !await integrationPaneIsIdleShell(shell.pane))
|
|
1939
|
+
integrationError('SESSION_TARGET_NOT_SHELL', 'Owned shell changed before launch', 409);
|
|
1940
|
+
// This owned pane is a verified shell; clear stale rich state before a new
|
|
1941
|
+
// process starts, so its new SessionStart must supply the native binding.
|
|
1942
|
+
backend.agentSession = null;
|
|
1943
|
+
backend.agent = null;
|
|
1944
|
+
backend.agentResumeRecovered = false;
|
|
1945
|
+
await runTmux(['send-keys', '-t', pinned.paneId, '-l', integrationLaunchCommand(record, restoring)]);
|
|
1946
|
+
await runTmux(['send-keys', '-t', pinned.paneId, 'Enter']);
|
|
1947
|
+
broadcastClientState();
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1797
1950
|
const preparingTaskWorkers = new Map();
|
|
1798
1951
|
/** Dedicated persistent terminals leave existing member sessions and the user's checkout untouched. */
|
|
1799
1952
|
export function prepareCollaborationTaskWorker(task, template, dependencies, service, readDependency) {
|
|
@@ -3192,6 +3345,7 @@ function broadcastAgentStatus(sessionId, session, force = false) {
|
|
|
3192
3345
|
if (!force && previousSnapshot === snapshot)
|
|
3193
3346
|
return;
|
|
3194
3347
|
lastAgentStatusSnapshots.set(sessionId, snapshot);
|
|
3348
|
+
notifyIntegrationSessions();
|
|
3195
3349
|
broadcastEvent(sessionId, payload);
|
|
3196
3350
|
const previous = previousSnapshot
|
|
3197
3351
|
? JSON.parse(previousSnapshot)
|
|
@@ -6633,7 +6787,7 @@ router.put('/toolbar-presets', (req, res) => {
|
|
|
6633
6787
|
async function getSettingsPayload() {
|
|
6634
6788
|
const localAccess = localAccessManager.getState();
|
|
6635
6789
|
const interfaces = await Promise.all(localAccess.interfaces.map(async (entry) => {
|
|
6636
|
-
const url =
|
|
6790
|
+
const url = localAccessInterfaceUrl(localAccess.url, entry.address);
|
|
6637
6791
|
const qrDataUrl = await QRCode.toDataURL(url, {
|
|
6638
6792
|
margin: 1,
|
|
6639
6793
|
width: 132,
|
|
@@ -6669,7 +6823,7 @@ async function getSettingsPayload() {
|
|
|
6669
6823
|
localAccess: {
|
|
6670
6824
|
...localAccess,
|
|
6671
6825
|
interfaces,
|
|
6672
|
-
onboardingUrl: getOnboardingServerUrl() ?? null,
|
|
6826
|
+
onboardingUrl: localAccess.status === 'loopback-only' ? null : getOnboardingServerUrl() ?? null,
|
|
6673
6827
|
},
|
|
6674
6828
|
};
|
|
6675
6829
|
}
|
|
@@ -11,7 +11,26 @@ function normalizeQuestionName(name) {
|
|
|
11
11
|
return name.endsWith('.') ? name.slice(0, -1).toLowerCase() : name.toLowerCase();
|
|
12
12
|
}
|
|
13
13
|
export function isLoopbackHost(host) {
|
|
14
|
-
|
|
14
|
+
const normalized = host.replace(/^\[|\]$/g, '').toLowerCase();
|
|
15
|
+
return LOCALHOST_NAMES.has(normalized) || normalized.startsWith('127.') || normalized.startsWith('::ffff:127.');
|
|
16
|
+
}
|
|
17
|
+
export function localAccessOrigin(host, port, scheme) {
|
|
18
|
+
const hostname = host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
|
|
19
|
+
return new URL(`${scheme}://${hostname}:${port}`).origin;
|
|
20
|
+
}
|
|
21
|
+
export function localAccessInterfaceUrl(serviceUrl, address) {
|
|
22
|
+
const origin = new URL(serviceUrl);
|
|
23
|
+
origin.hostname = address.includes(':') && !address.startsWith('[') ? `[${address}]` : address;
|
|
24
|
+
return origin.origin;
|
|
25
|
+
}
|
|
26
|
+
function getBoundLanIPv4Interfaces(host) {
|
|
27
|
+
if (isLoopbackHost(host))
|
|
28
|
+
return [];
|
|
29
|
+
const interfaces = getLanIPv4Interfaces();
|
|
30
|
+
if (host === '0.0.0.0' || host === '::' || host === '[::]')
|
|
31
|
+
return interfaces;
|
|
32
|
+
const address = host.replace(/^::ffff:/, '');
|
|
33
|
+
return interfaces.filter((entry) => entry.address === address);
|
|
15
34
|
}
|
|
16
35
|
function interfaceLabel(name) {
|
|
17
36
|
if (name === 'en0')
|
|
@@ -47,18 +66,19 @@ export function getLanIPv4Addresses() {
|
|
|
47
66
|
}
|
|
48
67
|
function buildState(setting, options, status, reason) {
|
|
49
68
|
const hostname = hostnameForName(setting.name);
|
|
50
|
-
const interfaces =
|
|
69
|
+
const interfaces = getBoundLanIPv4Interfaces(options.host);
|
|
51
70
|
const lanAddresses = interfaces.map((entry) => entry.address);
|
|
52
71
|
const onboardingHost = lanAddresses[0] ?? null;
|
|
53
|
-
const
|
|
72
|
+
const wildcardHost = options.host === '0.0.0.0' || options.host === '::' || options.host === '[::]';
|
|
73
|
+
const fallbackHostname = onboardingHost ?? (wildcardHost ? hostname : options.host);
|
|
54
74
|
return {
|
|
55
75
|
name: setting.name,
|
|
56
76
|
source: setting.source,
|
|
57
77
|
hostname,
|
|
58
78
|
fallbackHostname,
|
|
59
|
-
url:
|
|
60
|
-
fallbackUrl:
|
|
61
|
-
onboardingUrl: onboardingHost ?
|
|
79
|
+
url: localAccessOrigin(hostname, options.port, options.scheme),
|
|
80
|
+
fallbackUrl: localAccessOrigin(fallbackHostname, options.port, options.scheme),
|
|
81
|
+
onboardingUrl: onboardingHost ? `${localAccessOrigin(onboardingHost, options.onboardingPort ?? options.port, 'http')}/ca` : null,
|
|
62
82
|
status,
|
|
63
83
|
reason,
|
|
64
84
|
httpsEnabled: options.scheme === 'https',
|
|
@@ -84,7 +104,7 @@ export class LocalAccessManager {
|
|
|
84
104
|
this.options = options;
|
|
85
105
|
await this.stop();
|
|
86
106
|
let setting = await getLocalAccessSettingAsync();
|
|
87
|
-
const lanAddresses =
|
|
107
|
+
const lanAddresses = getBoundLanIPv4Interfaces(options.host).map((entry) => entry.address);
|
|
88
108
|
if (isLoopbackHost(options.host)) {
|
|
89
109
|
this.state = buildState(setting, options, 'loopback-only', 'Server is bound to loopback only.');
|
|
90
110
|
return this.state;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const DEFAULT_SESSION_DISPLAY_SHELL_NAMES = new Set([
|
|
2
|
+
'bash',
|
|
3
|
+
'zsh',
|
|
4
|
+
'fish',
|
|
5
|
+
'sh',
|
|
6
|
+
'dash',
|
|
7
|
+
'ksh',
|
|
8
|
+
'tcsh',
|
|
9
|
+
'csh',
|
|
10
|
+
'nu',
|
|
11
|
+
]);
|
|
12
|
+
export function getCwdLeafName(cwd) {
|
|
13
|
+
if (!cwd)
|
|
14
|
+
return null;
|
|
15
|
+
if (cwd === '/')
|
|
16
|
+
return '/';
|
|
17
|
+
const segments = cwd.replace(/\/+$/, '').split('/');
|
|
18
|
+
return segments[segments.length - 1] || cwd;
|
|
19
|
+
}
|
|
20
|
+
export function getSessionDisplayLines(session, activeProgram, cwd, shellNames = DEFAULT_SESSION_DISPLAY_SHELL_NAMES, shellTitle = null, promptState = null) {
|
|
21
|
+
if (session.customName)
|
|
22
|
+
return { primary: session.name, secondary: getCwdLeafName(cwd) };
|
|
23
|
+
// Shell integration (OSC 2) provides real-time title: command name when running,
|
|
24
|
+
// cwd when idle. This is faster and more accurate than server-side process polling.
|
|
25
|
+
if (shellTitle) {
|
|
26
|
+
const cwdLeaf = getCwdLeafName(cwd);
|
|
27
|
+
const titleLooksLikeCwd = shellTitle === cwd || shellTitle === cwdLeaf;
|
|
28
|
+
// If the shell reports a running state, the title is the command name.
|
|
29
|
+
if (promptState !== 'idle' && !shellNames.has(shellTitle) && !titleLooksLikeCwd) {
|
|
30
|
+
return { primary: shellTitle, secondary: cwdLeaf };
|
|
31
|
+
}
|
|
32
|
+
// If idle, the title is typically the cwd — fall through to show cwd leaf.
|
|
33
|
+
}
|
|
34
|
+
if (activeProgram && !shellNames.has(activeProgram)) {
|
|
35
|
+
return { primary: activeProgram, secondary: getCwdLeafName(cwd) };
|
|
36
|
+
}
|
|
37
|
+
const dir = getCwdLeafName(cwd);
|
|
38
|
+
if (dir)
|
|
39
|
+
return { primary: dir, secondary: null };
|
|
40
|
+
return { primary: session.name, secondary: null };
|
|
41
|
+
}
|
|
42
|
+
export function getSessionDisplayName(session, activeProgram, cwd, shellNames = DEFAULT_SESSION_DISPLAY_SHELL_NAMES, shellTitle = null, promptState = null) {
|
|
43
|
+
return getSessionDisplayLines(session, activeProgram, cwd, shellNames, shellTitle, promptState).primary;
|
|
44
|
+
}
|
|
45
|
+
/** Resolve the same title hints the session sidebar uses, without exposing route IDs. */
|
|
46
|
+
export function collaborationSessionDisplayName(session) {
|
|
47
|
+
const cwdLeaf = getCwdLeafName(session.cwd ?? null);
|
|
48
|
+
const title = session.shellTitle === session.cwd || session.shellTitle === cwdLeaf ? null : session.shellTitle ?? null;
|
|
49
|
+
const name = getSessionDisplayName(session, session.activeProgram ?? null, null, DEFAULT_SESSION_DISPLAY_SHELL_NAMES, title);
|
|
50
|
+
return !name.trim() || /^(?:tmux:|remote:|wt-)/i.test(name) || name === session.sessionId
|
|
51
|
+
? session.agent?.displayName || cwdLeaf || '终端会话'
|
|
52
|
+
: name;
|
|
53
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# TD 通用外部接入(协议 1)
|
|
2
|
+
|
|
3
|
+
最低 CLI / 运行服务版本:**termdock 1.4.300**,两者都必须升级并重启服务。
|
|
4
|
+
现有 `--session`、消息投递、任务报告与自动协作入口继续兼容;旧的
|
|
5
|
+
`task create --integration` 仍表示代码集成子任务,新身份使用 `--principal`。
|
|
6
|
+
|
|
7
|
+
TD 提供受限身份、持久事件推送、关联咨询回复,以及会话创建/精确恢复。
|
|
8
|
+
机器人、定时器、外部用户授权、业务幂等键、外部 inbox/outbox、业务校验由接入方实现。
|
|
9
|
+
`complete` 是执行成员的明确交付,`accepted` 是用户明确验收;投递成功、终端快照、
|
|
10
|
+
事件 ACK 都不表示已读、任务完成或业务校验通过。
|
|
11
|
+
|
|
12
|
+
## 管理员配置身份
|
|
13
|
+
|
|
14
|
+
接口只监听本机 Unix socket `~/.termdock/integration-<port>.sock`(0600),
|
|
15
|
+
不暴露 HTTP 业务接口,不使用网页转发。首版身份绑定**一个本机协作组**,
|
|
16
|
+
不支持跨服务组。先在 TD 建立本机组,再查询其 ID:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
td integration groups
|
|
20
|
+
td integration list
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
管理员保存策略文件;实际可执行文件、插件 slug 和目录由本机管理员填写:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"id": "report-bridge",
|
|
28
|
+
"groupId": "实际本机协作组ID",
|
|
29
|
+
"permissions": [
|
|
30
|
+
"task.read", "task.create", "task.assign", "task.comment", "task.revise",
|
|
31
|
+
"task.answer", "events.read", "session.create", "session.read", "session.restore"
|
|
32
|
+
],
|
|
33
|
+
"launchProfiles": [{
|
|
34
|
+
"id": "report-agent",
|
|
35
|
+
"agentSlug": "实际已安装的插件slug",
|
|
36
|
+
"executable": "/absolute/path/to/private-launch-wrapper",
|
|
37
|
+
"argv": ["-m", "model-name", "-C", "{cwd}"],
|
|
38
|
+
"cwdRoots": ["/absolute/path/to/runs"],
|
|
39
|
+
"resumeArgv": ["resume", "{sessionId}", "{launchArgs}"]
|
|
40
|
+
}]
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`launchProfiles` 可为空(只操作已有任务)。创建会话时只能选择预配 profile 和
|
|
45
|
+
目录,不能传任意命令/参数。`cwd` 必须已存在,真实路径须位于 `cwdRoots` 内;
|
|
46
|
+
符号链接不能逃出目录边界。参数按独立 token 引号转义,控制字符禁止。
|
|
47
|
+
`{cwd}`、`{sessionId}`、`{launchArgs}` 必须是独立参数;恢复模板必须各包含一次
|
|
48
|
+
`{sessionId}` 与 `{launchArgs}`,禁止 `--last`。示例模板需按真实 Agent 的
|
|
49
|
+
命令语法调整;启动与恢复仍需安装并启用对应 Agent 插件,插件必须支持精确 ID 恢复。
|
|
50
|
+
|
|
51
|
+
包装器须原样透传参数,安静加载 Agent 登录环境,不打印密钥。TD 保存原始
|
|
52
|
+
profile(可执行文件/包装器路径、argv、模型参数、cwd、恢复模板);恢复使用该
|
|
53
|
+
快照,并切回原 cwd 后启动。只有精确绑定且经进程前台组核验的空闲 shell 才能启动;
|
|
54
|
+
前台包装器或其他进程尚在运行时拒绝重复启动。包装器文件本身由管理员维护,TD 不备份其代码或密钥。
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
td integration create --file /private/policy.json --credential-file /private/bridge-credential.json
|
|
58
|
+
export TERMDOCK_INTEGRATION_CREDENTIAL_FILE=/private/bridge-credential.json
|
|
59
|
+
td collab --principal report-bridge capabilities
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
输出文件必须不存在;TD 创建 0600 文件,只打印身份及文件路径,不打印 token。
|
|
63
|
+
运行方只能通过 `TERMDOCK_INTEGRATION_CREDENTIAL_FILE` 注入同用户、私有、非符号链接
|
|
64
|
+
的文件。不要把 token 放入 argv、消息、日志或 profile。策略与凭据不入库。
|
|
65
|
+
管理员 `td integration revoke report-bridge` 会拒绝后续请求并关闭已有订阅;
|
|
66
|
+
恢复授权需新建身份,不会静默替换原凭据。
|
|
67
|
+
|
|
68
|
+
CLI 使用当前运行服务的端口,默认 9834。每个端口的身份、游标、事件、会话记录
|
|
69
|
+
分别保存在 `~/.termdock/integration-state/<port>/`,通过原子写入及 fsync 持久化。
|
|
70
|
+
这是 TD 接口的权限边界,不是同一操作系统用户下的进程沙箱。
|
|
71
|
+
|
|
72
|
+
## 版本与能力核验
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
td --version
|
|
76
|
+
td collab --principal report-bridge capabilities
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
第二条回读实际服务 `server_version`、本机 `cli_version`、`integration_protocol`、
|
|
80
|
+
`local_only`、支持的权限名称与功能布尔值;`principal` 回读本身份的组、实际授予权限
|
|
81
|
+
及允许的 profile ID / Agent slug / cwd roots,不返回完整启动配置。要求协议 1;缺 socket、鉴权失败或协议不兼容
|
|
82
|
+
会直接失败,不回退到旧会话身份/公开 HTTP。协议兼容是运行判断依据,不能只比较
|
|
83
|
+
已安装 npm 版本。普通 `td collab --session <id> capabilities` 同样返回 CLI/服务版本。
|
|
84
|
+
|
|
85
|
+
## 任务与权限
|
|
86
|
+
|
|
87
|
+
| 操作 | 外部身份 | 会话成员 / 用户规则 |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| list/get | `task.read`,只读授权组 | 保留现有组权限 |
|
|
90
|
+
| create | `task.create`,普通独立任务 | 指定负责人另需 `task.assign`,必须在组内 |
|
|
91
|
+
| assign | `task.assign` | 必须带当前 revision;新建 attempt,不隐式恢复会话 |
|
|
92
|
+
| comment | `task.comment` | open/accepted 可咨询,保留原结果、验收和 attempt |
|
|
93
|
+
| revise | `task.revise` | 带 revision,重开并使旧结果失去验收资格,沿用当前 attempt |
|
|
94
|
+
| answer | `task.answer` | 指定尚未回答且属于当前 attempt 的 decision ID |
|
|
95
|
+
| respond | 禁止 | 原咨询 attempt 的负责人,必须带 attempt 和 comment event ID |
|
|
96
|
+
| report | 禁止 | 只有该 attempt 的负责人;自动协作只接受当前 attempt |
|
|
97
|
+
| accept / approve-plan | 禁止 | 保留明确用户操作 |
|
|
98
|
+
| drive、成员/组管理、托管目标、任意启动参数 | 禁止 | 保留原有入口,不向外部身份开放 |
|
|
99
|
+
|
|
100
|
+
所有写入都有幂等键,重试必须使用同键同参数;换参数返回 `IDEMPOTENCY_CONFLICT`。
|
|
101
|
+
现有会话 CLI 可自动生成键;外部身份的任务/会话写入必须显式提供稳定键才能跨进程重试。
|
|
102
|
+
幂等键按身份隔离。`assign/revise` 的过期 revision 返回 `TASK_CHANGED`;
|
|
103
|
+
`comment` 追加到提交时的当前 attempt,并返回真实原事件和投递关联,不覆盖结果。
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
td collab --principal report-bridge task create --group <group-id> --title '报告' \
|
|
107
|
+
--content '完整要求' --assignee <TD-session-id> --idempotency-key <business-key> \
|
|
108
|
+
--source external --external-actor '{"id":"external-user-id"}' --external-message-id <external-message-id>
|
|
109
|
+
td collab --principal report-bridge task comment <task-id> --content '请解释结果' --idempotency-key <comment-key>
|
|
110
|
+
td collab --principal report-bridge task get <task-id>
|
|
111
|
+
td collab --principal report-bridge task revise <task-id> --revision <revision> --content '变更要求' --idempotency-key <revision-key>
|
|
112
|
+
td collab --principal report-bridge task answer <task-id> --decision <decision-id> --content '答复条件' --idempotency-key <answer-key>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
来源同时记录 `source=integration`、`origin.integrationId` 和外部 actor / 消息 ID / metadata,
|
|
116
|
+
不会伪装为终端成员或用户验收。CLI `--metadata` 接收 JSON 对象,来源总大小限 16 KiB。
|
|
117
|
+
咨询消息关联 `replyToEventId`,执行成员收到命令提示:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
td collab --session <original-assignee-id> task respond <task-id> \
|
|
121
|
+
--attempt <original-attempt-id> --to-event <comment-event-id> --content '解释' --idempotency-key <response-key>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`respond` 只新增回复事件,不改 report、artifact 或 accepted。对咨询投递消息使用
|
|
125
|
+
普通 `td collab reply <message-id> ...`(不带任务 status)同样存为关联 `respond`;
|
|
126
|
+
明确带 status 才是任务报告。修改要求后,负责人需明确提交新的 `complete`,产生新
|
|
127
|
+
`artifact_id` 并重新验收。历史版本始终保留,外部业务校验按 artifact ID 存储。
|
|
128
|
+
|
|
129
|
+
负责人离线时,咨询仍进入原会话的持久消息队列,返回原 `attempt_id` / `delivery_id`;
|
|
130
|
+
用 task get 的 outbox、deliveries 或 message get 查询实际投递状态与失败原因。
|
|
131
|
+
不悄悄创建新 attempt/会话;会话恢复只能显式调用下节接口。已删除/脱组的负责人会
|
|
132
|
+
留下明确失败记录,接入方需交给用户处理,不能根据终端无输出猜测任务状态。
|
|
133
|
+
|
|
134
|
+
## 主动事件与 ACK
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
td collab --principal report-bridge events subscribe --consumer bridge-main --jsonl
|
|
138
|
+
td collab --principal report-bridge events ack <cursor> --consumer bridge-main
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
这是持续连接的主动推送,不是轮询收件箱。业务方读取 JSONL 调用自己的处理器。
|
|
142
|
+
TD 不提供任意 URL 的 HTTP webhook;首版使用本机 socket,进程重连时自动退避。
|
|
143
|
+
无 `--timeout` 时订阅持续运行;可显式加 `--timeout 60s` 结束本次订阅。
|
|
144
|
+
|
|
145
|
+
事件外层固定字段:`type=event,event_id,group_id,sequence,cursor,kind,created_at,payload`。
|
|
146
|
+
按情况包含 `task_id,attempt_id,artifact_id,message_id,reply_to_event_id,source,actor,external_actor`。
|
|
147
|
+
`sequence` 是组内持久顺序;`created_at` 是原事实时间,不用于顺序/去重。
|
|
148
|
+
task 事件 `event_id` 与任务内事件 ID 一致,`respond.reply_to_event_id` 指向原 comment。
|
|
149
|
+
|
|
150
|
+
| kind | payload |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| `task.created/assigned/scheduled/coordinator` | `{event}`:原事件、actor、content、attemptId、sequence、createdAt、origin 等 |
|
|
153
|
+
| `task.report` | `{event}`:另含明确 reportStatus(ack/working/blocked/complete/failed)、evidence;新 complete 含 artifactId |
|
|
154
|
+
| `task.comment/respond/revise` | `{event}`:咨询含 target/deliveryId,答复含 replyToEventId |
|
|
155
|
+
| `task.question/answer` | `{event}`;decision 的完整状态从 task get 回读 |
|
|
156
|
+
| `task.submit-plan/review/request-review/approve-plan/accepted` | `{event}`:相关 artifactId、target 或 evidence(依实际事件) |
|
|
157
|
+
| `task.close/reopen/coordinate/pause/resume/retry/child-update/automation-blocked/review-passed/review-blocked` | `{event}`:保留服务事件的 source 与原文 |
|
|
158
|
+
| `task.result` | `{artifact}`:结果 id、attemptId、actor、content、summary、evidence、createdAt |
|
|
159
|
+
| `task.delivery` | `{delivery}`:任务投递 id、attemptId、messageId、status、deliveredAt、error |
|
|
160
|
+
| `message.queued/message.reply` | `{message_id,thread_id,reply_to,from_session_id,to_session_id,content,metadata}` |
|
|
161
|
+
| `message.delivery` | `{receipt}`:实际入队/远端接收/终端写入阶段、失败、重试计数与时间,及明确回复 ID;不含快照或已读字段 |
|
|
162
|
+
| `session.starting/binding_pending/ready/restoring/failed` | `{session}`:下节公开会话记录,失败包含 error_code |
|
|
163
|
+
|
|
164
|
+
TD 在源记录提交后触发 journal 及订阅,journal 中的同一事件 ID 在重连重放时保持不变。
|
|
165
|
+
新 consumer 从最早保留事件开始;接入方按 `event_id` 去重,**先持久写入 inbox,
|
|
166
|
+
再 ACK 连续已持久接收前缀的 cursor**,之后自行按任务顺序处理和重试业务 outbox。
|
|
167
|
+
只读取/打印事件不会 ACK;ACK 也不意味着处理完成、机器人已发送或用户验收。
|
|
168
|
+
TD 检查 cursor 属于授权组、没有回退、已经发送,但无法替业务方证明其数据库已经落盘。
|
|
169
|
+
服务重启后先重新订阅再 ACK,发送窗口会重建。每个 consumer 保持一个逻辑接收者。
|
|
170
|
+
|
|
171
|
+
每组保留最近 10,000 个事件,journal 总大小上限 64 MiB。已存在 consumer 的确认
|
|
172
|
+
位置早于保留窗口会报错,不能静默跳过;接入方回读任务/会话快照,完成业务补偿后
|
|
173
|
+
启用新 consumer。保留期不是永久归档。源记录与 journal 分文件提交,重启会从仍保留
|
|
174
|
+
的源事实补齐事件;源历史也被管理性删除的事实无法重建。
|
|
175
|
+
|
|
176
|
+
错误格式(HTTP 请求直接返回 JSON;流内错误也以一行 JSON 返回):
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{"type":"error","ok":false,"code":"EVENT_RETENTION_GAP","error":"...","retryable":false}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`heartbeat` 每 15 秒保持连接;`connection/state=reconnecting` 是 CLI 重连提示。
|
|
183
|
+
连接断开重连后从服务持久 ACK 位置重放,未 ACK 的事件可以重复。终止错误包括
|
|
184
|
+
`INTEGRATION_UNAUTHORIZED/REVOKED/PERMISSION_DENIED`、`EVENT_RETENTION_GAP`、
|
|
185
|
+
`INVALID_EVENT_CURSOR`、`INCOMPATIBLE_INTEGRATION_PROTOCOL`;修正配置或完成补偿再连。
|
|
186
|
+
`EVENT_CONSUMER_SLOW`、存储/连接暂时错误可重连;未发送的 cursor ACK 返回
|
|
187
|
+
`EVENT_ACK_NOT_SENT`。不会为了连通而降低鉴权或绕过权限。
|
|
188
|
+
|
|
189
|
+
## 会话创建、绑定与恢复
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
td collab --principal report-bridge session create --launch-profile report-agent \
|
|
193
|
+
--cwd /absolute/path/to/runs/current --idempotency-key <session-create-key>
|
|
194
|
+
td collab --principal report-bridge session get <session-id>
|
|
195
|
+
td collab --principal report-bridge session restore <session-id> --idempotency-key <restore-key>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
公开记录包含 `operation_id,session_id,group_id,principal_id,agent_slug,
|
|
199
|
+
agent_native_session_id,state,error_code,cwd,launch_profile,created_at,updated_at,terminal_binding`。
|
|
200
|
+
`terminal_binding` 是实际 tmux server/session/pane/PID 的固定身份。集成终端须保持单一 pane;
|
|
201
|
+
新增窗口/pane 会使原生身份核验失败,避免误用其它 pane 的 hook。完整原 profile
|
|
202
|
+
仅在服务私有持久记录中保存,不通过事件发送。
|
|
203
|
+
|
|
204
|
+
| state | 精确含义 |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| starting | 创建意图已持久化,命令提交尚未证明 Agent / 原生绑定就绪 |
|
|
207
|
+
| binding_pending | 实际观察到该 Agent,尚未捕获确切原生 ID |
|
|
208
|
+
| ready | 实际观察到该 Agent 和确切原生 ID;不表示任务空闲/完成 |
|
|
209
|
+
| restoring | 显式恢复意图持久化,等待原 ID 的运行证据 |
|
|
210
|
+
| failed | 启动、绑定、身份核验或运行观察失败,详见 error_code |
|
|
211
|
+
|
|
212
|
+
同键同请求返回原 TD ID;已记录但结果不明的启动不会自动重新执行。通常等
|
|
213
|
+
session 事件;get 也会现场核验。120 秒内未形成绑定返回 `SESSION_BINDING_TIMEOUT`。
|
|
214
|
+
服务重启会继续核验待绑定记录。未捕获真实原生 ID,restore 返回
|
|
215
|
+
`NATIVE_SESSION_ID_MISSING`,不能伪造 UUID 或选择“最近会话”。
|
|
216
|
+
|
|
217
|
+
恢复保留原 TD ID、原生 ID、argv/model/cwd/包装器配置。原会话已经精确运行时直接
|
|
218
|
+
返回现状;发现其它 ID、未知进程或 pane 被复用则拒绝。原 tmux 已退出时,只有
|
|
219
|
+
显式精确恢复可以重建承载终端;这本身不证明 Agent 原生会话恢复成功。新参数/profile
|
|
220
|
+
不能覆盖原启动快照,尚未确定结果的恢复拒绝另一次恢复,防止重复 Agent。
|
|
221
|
+
|
|
222
|
+
常见失败码(每项都是完整字符串):
|
|
223
|
+
|
|
224
|
+
- 配置/权限:`LAUNCH_PROFILE_DENIED`, `LAUNCHER_UNAVAILABLE`, `COLLAB_AGENT_UNAVAILABLE`, `SESSION_CWD_MISSING`, `SESSION_CWD_DENIED`, `INTEGRATION_SESSION_NOT_FOUND`, `INTEGRATION_SESSION_LIMIT`。
|
|
225
|
+
- 终端/身份:`SESSION_BACKEND_UNAVAILABLE`, `SESSION_TARGET_NOT_SHELL`, `SESSION_IDENTITY_MISMATCH`, `NATIVE_SESSION_ID_MISMATCH`, `NATIVE_SESSION_ID_INVALID`, `NATIVE_SESSION_ID_MISSING`。
|
|
226
|
+
- 启动/恢复:`SESSION_CREATE_OUTCOME_UNKNOWN`, `SESSION_OPERATION_IN_PROGRESS`, `SESSION_BINDING_TIMEOUT`, `EXACT_RESUME_UNSUPPORTED`, `NATIVE_SESSION_ALREADY_RUNNING`, `AGENT_NOT_RUNNING`。
|
|
227
|
+
- 未分类异常:`SESSION_LAUNCH_FAILED`, `SESSION_OBSERVATION_FAILED`;错误不包含包装器输出、密钥或原始堆栈。
|
|
228
|
+
|
|
229
|
+
## 验收边界
|
|
230
|
+
|
|
231
|
+
维护机验证:真实 Unix socket 主动推送/断线重放/撤销、journal 重载与源记录补偿、
|
|
232
|
+
权限与幂等隔离、accepted 上咨询/关联答复、修订后的新结果、原 profile 恢复和
|
|
233
|
+
实际 shell 参数透传。适配器测试不冒充真实 Agent 的原生会话验收。
|
|
234
|
+
|
|
235
|
+
维护机未安装 TraeX;**真实 TraeX UUID 捕获及恢复尚待使用方开发机联调**。
|
|
236
|
+
使用方升级 CLI 和实际运行服务、回读 capabilities 后,应核对原 TD ID、真实
|
|
237
|
+
TraeX UUID 与原 argv/cwd/包装器快照,并验证 Agent 真正加载原对话。
|
|
238
|
+
先完成测试环境的创建→追问→关联答复,再验重复键、追加咨询、断线重投和精确恢复。
|
|
239
|
+
业务定时器或正式群接入由使用方明确启用,TD 发版不会启用这些业务流程。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termdock",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.300",
|
|
4
4
|
"description": "A complete web-based terminal application with modern UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".desktop-dist/main.js",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"runtime-manifest.json",
|
|
17
17
|
"README.md",
|
|
18
18
|
"LICENSE",
|
|
19
|
-
"scripts"
|
|
19
|
+
"scripts",
|
|
20
|
+
"docs/integrations.md"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"clean": "rimraf dist",
|
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.300",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-E9GDa4nrCXADRXdyPKufusZt6IcCr4m2mGdx5h34KMQ=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-SExYhaWtbBl5XCKB2eHSjEG0s9tkdn4YXABrBUGt14U=",
|
|
10
|
+
"clientBundleHash": "sha256-bGTHf29nHBxsGrjxP9qshn4lEoTuZ5EyGDcsAvT1Bq8=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|