termdock 1.4.299 → 1.4.301
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-BziVLy1b.js +179 -0
- package/dist/client/assets/App-BziVLy1b.js.br +0 -0
- package/dist/client/assets/App-BziVLy1b.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js +12 -0
- package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-2lXfrgk4.js → ChangeWalkthroughPanel-00oa0z6w.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js +1 -0
- package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-B_mzpNuw.js +2 -0
- package/dist/client/assets/ComputerControlView-C9v1Ooc9.js +5 -0
- package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.br +0 -0
- package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-EFUb9S3u.js → DagPlayground-DfUpMALj.js} +1 -1
- package/dist/client/assets/DagPlayground-DfUpMALj.js.br +0 -0
- package/dist/client/assets/DagPlayground-DfUpMALj.js.gz +0 -0
- package/dist/client/assets/{DiffLab-CXWjSE2q.js → DiffLab-Cf_URhQt.js} +1 -1
- package/dist/client/assets/DiffLab-Cf_URhQt.js.br +0 -0
- package/dist/client/assets/DiffLab-Cf_URhQt.js.gz +0 -0
- package/dist/client/assets/{DiffReview-ByMkBaD9.js → DiffReview-DjG9vCpH.js} +1 -1
- package/dist/client/assets/DiffReview-DjG9vCpH.js.br +0 -0
- package/dist/client/assets/DiffReview-DjG9vCpH.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-DoWaKm94.js → DiffReviewLab-BKOvHdJY.js} +2 -2
- package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-B4hn9z0k.js → DiffViewer-6n3tgsdc.js} +8 -8
- package/dist/client/assets/DiffViewer-6n3tgsdc.js.br +0 -0
- package/dist/client/assets/DiffViewer-6n3tgsdc.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-C-LJZ8so.js → ModelPreview-BNs2sRct.js} +1 -1
- package/dist/client/assets/ModelPreview-BNs2sRct.js.br +0 -0
- package/dist/client/assets/ModelPreview-BNs2sRct.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CcMSU1v8.js +1677 -0
- package/dist/client/assets/RightSidebar-CcMSU1v8.js.br +0 -0
- package/dist/client/assets/RightSidebar-CcMSU1v8.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-BY-pfYKk.js → abnfDiagram-VCTEODGH-DogjvRKp.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.gz +0 -0
- package/dist/client/assets/app-D6vN8PW-.js +35 -0
- package/dist/client/assets/app-D6vN8PW-.js.br +0 -0
- package/dist/client/assets/app-D6vN8PW-.js.gz +0 -0
- package/dist/client/assets/{arc-DIENgCZn.js → arc-Cqel4Ted.js} +1 -1
- package/dist/client/assets/arc-Cqel4Ted.js.br +0 -0
- package/dist/client/assets/arc-Cqel4Ted.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-C1h4e4az.js → architectureDiagram-5GKGNRK7-DgvPtgNb.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-B_yOr54K.js → blockDiagram-I7D4REHJ-BxbWO4x4.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DRD4zxIo.js → c4Diagram-7LVT6UL2-gsA37gjL.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.gz +0 -0
- package/dist/client/assets/channel-CBc8-n7y.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-CXytnjmD.js → chunk-2Q5K7J3B-ChC9yYOd.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-Bjmajkf7.js → chunk-5VM5RSS4-Bi4Hc8yL.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-CQCL8mJG.js → chunk-F27PBJKO-DvVWvQEk.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-ruHGCADe.js → chunk-IMKFNOWR-BnwQLR5S.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-U18S7rxZ.js → chunk-JWPE2WC7-BkNnaDiR.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-DWnwssfL.js → chunk-POPQ4Y6H-BRzm6Uzi.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-DMHj3NSq.js → chunk-SVP7TREG-CDYm-dzk.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-CDYm-dzk.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-CDYm-dzk.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-Dh_ekvFW.js → chunk-TICWLB2K-BKaW9Z9i.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-Bq5wD6iJ.js → chunk-XXDRQBXY-DcHZ_FHb.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CNFc2dOc.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CNFc2dOc.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CsySWhYg.js → cose-bilkent-JH36ORCC-DCbbTNc6.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-9DKE5wV7.js → cynefin-OW5HDTMX-i23hD0Z2.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-B_8fvr9c.js → cynefinDiagram-5FMLGOSQ-CbyLYcqc.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-BOdsFNFT.js → dagre-GXQ25YYZ-iJ4VKzZm.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-6v_qvAvi.js → diagram-S7CK7UJ4-BNVZcdsO.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-c03oOGhD.js → diagram-UQ7AKVKN-1ghTvPiu.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-MMIhRYp_.js → diagram-VSXAHHWV-Ce_0nMd3.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-B_l-uebl.js → diagram-VX7I27RA-CLHEkhUt.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-DKoBC5gn.js → diagram-Z3DM3KII-iyb5DkP6.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-Bs3wu8Az.js → ebnfDiagram-PWID7BFC-Dxh2TzXm.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-mYnHYbKl.js → erDiagram-RLTQ6QDP-Cr8IkdCC.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-Cr8IkdCC.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Cr8IkdCC.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-CwCLWbib.js → flowDiagram-HODETNUW-B8ZF1bwr.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-mse8c-ni.js → ganttDiagram-EL5Y4UJY-zhdL10eM.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-BviL29ps.js → gestureArbiter-CXYHfU__.js} +1 -1
- package/dist/client/assets/gestureArbiter-CXYHfU__.js.br +0 -0
- package/dist/client/assets/gestureArbiter-CXYHfU__.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-BVUfLjAX.js → gitGraphDiagram-WWUBYQGX-BQf2fHBo.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.gz +0 -0
- package/dist/client/assets/{index-C6ftfFlG.css → index-CNY7CTVa.css} +1 -1
- package/dist/client/assets/index-CNY7CTVa.css.br +0 -0
- package/dist/client/assets/index-CNY7CTVa.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-XjoFF4BC.js → infoDiagram-27XIBGKW-BT8xey7S.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-bH299bWL.js → ishikawaDiagram-5VMMS53U-DunPdwW9.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-BG0u_X1A.js → journeyDiagram-3NMN7TZE-B0y75iIg.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-DRoc9N-i.js → kanban-definition-UXKFOSKX-BfgA5WLo.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.gz +0 -0
- package/dist/client/assets/{layout-mh0t6JpE.js → layout-BvjABLff.js} +1 -1
- package/dist/client/assets/layout-BvjABLff.js.br +0 -0
- package/dist/client/assets/layout-BvjABLff.js.gz +0 -0
- package/dist/client/assets/{linear-B9KTRuUV.js → linear-DgeFRliF.js} +1 -1
- package/dist/client/assets/linear-DgeFRliF.js.br +0 -0
- package/dist/client/assets/linear-DgeFRliF.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-Bu2G9zhK.js → mermaid.core-DM6Rlal9.js} +5 -5
- package/dist/client/assets/mermaid.core-DM6Rlal9.js.br +0 -0
- package/dist/client/assets/mermaid.core-DM6Rlal9.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-Go6Bg5xm.js → mindmap-definition-YA3MSWOX-DhEiABv6.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-C7-MUAHQ.js → passwordBootstrap-Dxv0I4wM.js} +1 -1
- package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DKUIl_U6.js → pegDiagram-XKGWAZYB-LTlVaJfm.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-aoDNlV7J.js → pieDiagram-E7YTZNPT-9bnryna3.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BKuuaF2s.js → quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BKuuaF2s.js.br → quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.br} +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BwfCW2bp.js → railroadDiagram-O6MQD6OU-DTtXvKOu.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.gz +0 -0
- package/dist/client/assets/rdpSession-Ggo3N5s5.js +1 -0
- package/dist/client/assets/rdpSession-Ggo3N5s5.js.br +0 -0
- package/dist/client/assets/rdpSession-Ggo3N5s5.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-Df8W2cyP.js +1 -0
- package/dist/client/assets/referenceLongPress-Df8W2cyP.js.br +0 -0
- package/dist/client/assets/referenceLongPress-Df8W2cyP.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-DbpU9Uig.js → requirementDiagram-BXWQKSXE-BNia9zI0.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-Bxa22XVM.js → sankeyDiagram-P5KCCOFB-uvg5npfK.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-C4GvzT9z.js → sequenceDiagram-WJ2MYXX4-DpJNf2MD.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-B9zJJwQw.js → sizeCapture-INFHLROL-CkWB2AnF.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.gz +0 -0
- package/dist/client/assets/{sparkles-BJbfT-v-.js → sparkles-DOuEtn62.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-fqbw6LZa.js → stateDiagram-D77RDMKH-CeMzMQq-.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CNYUpLxt.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-Hpc28YZY.js → swimlanes-42K2YHIH-Bo9GejWo.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-EB7zBRFR.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-qDieK7Re.js → timeline-definition-24CTP7MA-cvWXYEZ4.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-YOCAWhtT.js → triangle-alert-DzV0gQXz.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-BJpZlHao.js → vennDiagram-4TSXK5OY-CzleWcmk.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-BJpZlHao.js.br → vennDiagram-4TSXK5OY-CzleWcmk.js.br} +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.gz +0 -0
- package/dist/client/assets/{videoCompression-DI9vP0dm.js → videoCompression-DCnzjEfU.js} +1 -1
- package/dist/client/assets/videoCompression-DCnzjEfU.js.br +0 -0
- package/dist/client/assets/videoCompression-DCnzjEfU.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-CQJfj6Ns.js → wardleyDiagram-VM6X3IG4-Cir1iutR.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-Cy-lkWBX.js → xychartDiagram-S5SC5T6Z-pNPcVJ1C.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.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 +56 -10
- package/dist/server/agent/collaborationGroupRoutes.js +10 -5
- package/dist/server/agent/collaborationLaunchProfiles.js +43 -0
- package/dist/server/agent/collaborationRoutes.js +6 -2
- package/dist/server/agent/collaborationService.js +8 -2
- package/dist/server/agent/collaborationStore.js +32 -1
- package/dist/server/agent/collaborationTaskCli.js +11 -3
- package/dist/server/agent/collaborationTaskService.js +16 -10
- package/dist/server/agent/collaborationTaskStore.js +70 -25
- package/dist/server/agent/collaborationTeam.js +3 -3
- package/dist/server/agent/integrationCli.js +243 -0
- package/dist/server/agent/integrationServer.js +331 -0
- package/dist/server/agent/integrationSessions.js +344 -0
- package/dist/server/agent/integrationStore.js +222 -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 +196 -13
- package/dist/server/utils/localAccess.js +27 -7
- package/dist/server/utils/sessionDisplayName.js +53 -0
- package/docs/integrations.md +304 -0
- package/package.json +3 -2
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-DzuUkEI_.js +0 -179
- package/dist/client/assets/App-DzuUkEI_.js.br +0 -0
- package/dist/client/assets/App-DzuUkEI_.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-QodxiiCc.js +0 -7
- package/dist/client/assets/ArchitecturePanel-QodxiiCc.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-QodxiiCc.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-2lXfrgk4.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-2lXfrgk4.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-05-Py9N8.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-BJe9TUtZ.js +0 -2
- package/dist/client/assets/ComputerControlView-B5vigCoW.js +0 -5
- package/dist/client/assets/ComputerControlView-B5vigCoW.js.br +0 -0
- package/dist/client/assets/ComputerControlView-B5vigCoW.js.gz +0 -0
- package/dist/client/assets/DagPlayground-EFUb9S3u.js.br +0 -0
- package/dist/client/assets/DagPlayground-EFUb9S3u.js.gz +0 -0
- package/dist/client/assets/DiffLab-CXWjSE2q.js.br +0 -0
- package/dist/client/assets/DiffLab-CXWjSE2q.js.gz +0 -0
- package/dist/client/assets/DiffReview-ByMkBaD9.js.br +0 -0
- package/dist/client/assets/DiffReview-ByMkBaD9.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-DoWaKm94.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-DoWaKm94.js.gz +0 -0
- package/dist/client/assets/DiffViewer-B4hn9z0k.js.br +0 -0
- package/dist/client/assets/DiffViewer-B4hn9z0k.js.gz +0 -0
- package/dist/client/assets/ModelPreview-C-LJZ8so.js.br +0 -0
- package/dist/client/assets/ModelPreview-C-LJZ8so.js.gz +0 -0
- package/dist/client/assets/RightSidebar-EaMm4prW.js +0 -1678
- package/dist/client/assets/RightSidebar-EaMm4prW.js.br +0 -0
- package/dist/client/assets/RightSidebar-EaMm4prW.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BY-pfYKk.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BY-pfYKk.js.gz +0 -0
- package/dist/client/assets/app-Ch0UDtJ-.js +0 -35
- package/dist/client/assets/app-Ch0UDtJ-.js.br +0 -0
- package/dist/client/assets/app-Ch0UDtJ-.js.gz +0 -0
- package/dist/client/assets/arc-DIENgCZn.js.br +0 -0
- package/dist/client/assets/arc-DIENgCZn.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C1h4e4az.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C1h4e4az.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-B_yOr54K.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-B_yOr54K.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DRD4zxIo.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DRD4zxIo.js.gz +0 -0
- package/dist/client/assets/channel-MOXuUKM7.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-CQCL8mJG.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CQCL8mJG.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-ruHGCADe.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-ruHGCADe.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-DMHj3NSq.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-DMHj3NSq.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Dh_ekvFW.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Dh_ekvFW.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CEXMdudw.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CEXMdudw.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CsySWhYg.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CsySWhYg.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-9DKE5wV7.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-9DKE5wV7.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B_8fvr9c.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-B_8fvr9c.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BOdsFNFT.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BOdsFNFT.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-6v_qvAvi.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-6v_qvAvi.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-c03oOGhD.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-c03oOGhD.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-MMIhRYp_.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-MMIhRYp_.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-B_l-uebl.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-B_l-uebl.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-DKoBC5gn.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-DKoBC5gn.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Bs3wu8Az.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Bs3wu8Az.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-mYnHYbKl.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-mYnHYbKl.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CwCLWbib.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CwCLWbib.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-mse8c-ni.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-mse8c-ni.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-BviL29ps.js.br +0 -0
- package/dist/client/assets/gestureArbiter-BviL29ps.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVUfLjAX.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVUfLjAX.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-bH299bWL.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-bH299bWL.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BG0u_X1A.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BG0u_X1A.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-DRoc9N-i.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-DRoc9N-i.js.gz +0 -0
- package/dist/client/assets/layout-mh0t6JpE.js.br +0 -0
- package/dist/client/assets/layout-mh0t6JpE.js.gz +0 -0
- package/dist/client/assets/linear-B9KTRuUV.js.br +0 -0
- package/dist/client/assets/linear-B9KTRuUV.js.gz +0 -0
- package/dist/client/assets/mermaid.core-Bu2G9zhK.js.br +0 -0
- package/dist/client/assets/mermaid.core-Bu2G9zhK.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-Go6Bg5xm.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-Go6Bg5xm.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-C7-MUAHQ.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-C7-MUAHQ.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DKUIl_U6.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DKUIl_U6.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-aoDNlV7J.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-aoDNlV7J.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BKuuaF2s.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BwfCW2bp.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BwfCW2bp.js.gz +0 -0
- package/dist/client/assets/rdpSession--y6Q98uo.js +0 -1
- package/dist/client/assets/rdpSession--y6Q98uo.js.br +0 -0
- package/dist/client/assets/rdpSession--y6Q98uo.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-BeG1PAdI.js +0 -1
- package/dist/client/assets/referenceLongPress-BeG1PAdI.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BeG1PAdI.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DbpU9Uig.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DbpU9Uig.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bxa22XVM.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bxa22XVM.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C4GvzT9z.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C4GvzT9z.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-B9zJJwQw.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-B9zJJwQw.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-fqbw6LZa.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-fqbw6LZa.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BO23Pbrn.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-Hpc28YZY.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-Hpc28YZY.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BLS7y4Di.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-qDieK7Re.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-qDieK7Re.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BJpZlHao.js.gz +0 -0
- package/dist/client/assets/videoCompression-DI9vP0dm.js.br +0 -0
- package/dist/client/assets/videoCompression-DI9vP0dm.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CQJfj6Ns.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CQJfj6Ns.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cy-lkWBX.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cy-lkWBX.js.gz +0 -0
|
@@ -83,7 +83,7 @@ function validateSnapshot(task) {
|
|
|
83
83
|
invalid();
|
|
84
84
|
if (event.deliveryId !== undefined && !validId(event.deliveryId))
|
|
85
85
|
invalid();
|
|
86
|
-
if (event.source !== undefined && !['system', 'user'].includes(event.source))
|
|
86
|
+
if (event.source !== undefined && !['system', 'user', 'integration'].includes(event.source))
|
|
87
87
|
invalid();
|
|
88
88
|
if (event.actor !== null)
|
|
89
89
|
member(event.actor);
|
|
@@ -115,6 +115,9 @@ function validateSnapshot(task) {
|
|
|
115
115
|
/** Atomic document writes keep decisions and their outgoing messages in one transaction. */
|
|
116
116
|
export class CollaborationTaskStore {
|
|
117
117
|
file;
|
|
118
|
+
listeners = new Set();
|
|
119
|
+
subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
|
|
120
|
+
snapshot() { return structuredClone(this.document.tasks); }
|
|
118
121
|
document = { version: 1, tasks: [], outbox: [], requests: {} };
|
|
119
122
|
constructor(file) {
|
|
120
123
|
this.file = file;
|
|
@@ -129,9 +132,18 @@ export class CollaborationTaskStore {
|
|
|
129
132
|
throw error;
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
|
-
transaction(mutate) {
|
|
135
|
+
transaction(mutate, origin) {
|
|
133
136
|
const next = structuredClone(this.document);
|
|
134
137
|
const result = mutate(next);
|
|
138
|
+
if (origin) {
|
|
139
|
+
const previous = new Set(this.document.tasks.flatMap(t => t.events.map(e => e.id)));
|
|
140
|
+
for (const task of next.tasks)
|
|
141
|
+
for (const event of task.events)
|
|
142
|
+
if (!previous.has(event.id) && event.actor === null && event.source !== 'system') {
|
|
143
|
+
event.source = 'integration';
|
|
144
|
+
event.origin = structuredClone(origin);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
135
147
|
if (next.tasks.some(task => Buffer.byteLength(JSON.stringify(task)) > 512_000))
|
|
136
148
|
throw new CollaborationError('TASK_HISTORY_LIMIT', '此任务记录达到 500 KiB,请建立后续任务并保留本任务为历史', 409);
|
|
137
149
|
const serialized = JSON.stringify(next);
|
|
@@ -142,8 +154,18 @@ export class CollaborationTaskStore {
|
|
|
142
154
|
writeFileSync(temporary, serialized, { mode: 0o600 });
|
|
143
155
|
renameSync(temporary, this.file);
|
|
144
156
|
this.document = next;
|
|
157
|
+
for (const listener of this.listeners) {
|
|
158
|
+
try {
|
|
159
|
+
listener();
|
|
160
|
+
}
|
|
161
|
+
catch { /* Durable sources allow the integration journal to retry. */ }
|
|
162
|
+
}
|
|
145
163
|
return structuredClone(result);
|
|
146
164
|
}
|
|
165
|
+
integrationRequestEvent(principalId, key, taskId) {
|
|
166
|
+
const request = this.document.requests[`integration:${principalId}:${key}`];
|
|
167
|
+
return request?.taskId === taskId ? structuredClone(this.document.tasks.find(t => t.id === taskId)?.events.find(e => e.id === request.eventId)) : undefined;
|
|
168
|
+
}
|
|
147
169
|
get(taskId) { return structuredClone(this.document.tasks.find(t => t.id === taskId) ?? null); }
|
|
148
170
|
owned(owner) { return structuredClone(this.document.tasks.filter(t => t.ownerServiceId === owner)); }
|
|
149
171
|
children(taskId) {
|
|
@@ -166,7 +188,7 @@ export class CollaborationTaskStore {
|
|
|
166
188
|
attempts: current ? [{ ...current, report: current.report ? { ...current.report, summary: reportSummary, content: '', evidence: undefined } : undefined }] : [],
|
|
167
189
|
artifacts: [plan, result, ...task.artifacts.filter(a => a.kind === 'review' && a.reviewsArtifactId === result?.id)].filter((a) => !!a).map(a => ({ ...a, content: '', summary: (a.summary || (a.kind === 'result' && collaborationResultPresentation(a.content).condensed ? collaborationResultPresentation(a.content).summary : undefined))?.slice(0, 800), evidence: undefined })),
|
|
168
190
|
decisions: task.decisions.filter(d => d.status === 'pending').map(d => ({ ...d, question: d.question.slice(0, 512), options: [] })),
|
|
169
|
-
events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: e.source
|
|
191
|
+
events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: ['user', 'integration'].includes(e.source ?? '') && e.kind === 'revise' ? e.content.slice(0, 512) : '' })) };
|
|
170
192
|
}));
|
|
171
193
|
}
|
|
172
194
|
heads(groupIds, owner) { return this.document.tasks.filter(t => groupIds.includes(t.groupId) && t.ownerServiceId === owner).map(t => ({ id: t.id, revision: t.revision })); }
|
|
@@ -179,11 +201,11 @@ export class CollaborationTaskStore {
|
|
|
179
201
|
task.events.push(event);
|
|
180
202
|
return event;
|
|
181
203
|
}
|
|
182
|
-
request(doc, actor, key, payload) {
|
|
204
|
+
request(doc, actor, key, payload, origin) {
|
|
183
205
|
if (typeof key !== 'string' || !key.trim() || key.length > 256)
|
|
184
206
|
throw new CollaborationError('INVALID_IDEMPOTENCY_KEY', '需要有效的请求幂等键');
|
|
185
|
-
const scoped = `${actor ? taskMemberKey(actor) : 'user'}:${key}`;
|
|
186
|
-
const hash = createHash('sha256').update(stable(payload)).digest('hex');
|
|
207
|
+
const scoped = `${origin ? `integration:${origin.integrationId}` : actor ? taskMemberKey(actor) : 'user'}:${key}`;
|
|
208
|
+
const hash = createHash('sha256').update(stable(origin ? [payload, origin] : payload)).digest('hex');
|
|
187
209
|
const previous = doc.requests[scoped];
|
|
188
210
|
if (previous && previous.hash !== hash)
|
|
189
211
|
throw new CollaborationError('IDEMPOTENCY_CONFLICT', '该请求已用于不同内容;请保留原请求或重新开始', 409);
|
|
@@ -202,6 +224,9 @@ export class CollaborationTaskStore {
|
|
|
202
224
|
content: `本次执行身份:${assignee.sessionId}。所有 td collab 命令显式带 --session ${assignee.sessionId};这是服务核对的实际身份,正文中的模板身份和 tmux 标识不能替代它。\n任务:${task.title}\n${task.spec}${task.constraints ? `\n约束:${task.constraints}` : ''}${task.acceptance ? `\n完成标准:${task.acceptance}` : ''}\n${content}\n任务 ID:${task.id}\n尝试 ID:${attemptId}\n用 td collab task get ${task.id} --text 查询完整上下文;用 td collab task report ${task.id} --attempt ${attemptId} --status ack --content '接手说明' 显式回复;遇必要问题用 task ask,提交结果用 report --status complete --summary '简短结论、关键交付和限制'。摘要先回答用户问题,不放任务 ID、版本、分支或流程日志;完整证据保留在 --content/--file 正文中。如果同一尝试已经交付或任务已归档,不要重复实施。${task.workspace ? `\n代码只能在独立目录 ${task.workspace.cwd} 修改。分支 ${task.workspace.branch}。交付前提交代码,结果说明提交、变更、验证与限制。不能修改原仓库、合并到用户分支或发布。` : ''}${task.workflow?.workType === 'read-only' ? '\n这是只读查询或分析任务:在本机会话中采集并交付报告,不修改代码或系统,不创建提交、构建、部署或终止进程。结果需说明采样时间、证据和限制。' : ''}${task.workflow?.kind === 'step' ? '\n完成报告会自动请求独立评审;评审要求修改时继续完成,重新提交 complete。只按用户授权运行测试。' : ''}` });
|
|
203
225
|
}
|
|
204
226
|
goalInstructions(task) {
|
|
227
|
+
return `${this.baseGoalInstructions(task)}\n启动 Agent 前,读取 task get 返回的 launchProfiles、defaultLaunchProfileId 和 roster.launchProfile。根据用户填写的 notes 判断适用任务;这些是选用建议,不是能力或性能保证。需要另一种模型或参数时,用 td collab spawn ${task.groupId} <agentSlug> --launch-profile <方案id> --cwd <项目目录> --session ${task.coordinator?.sessionId} 创建对应执行成员,再把子任务分派给它。已有成员及其代码执行会话保留创建时的启动方案,不因默认方案变更而更换模型。`;
|
|
228
|
+
}
|
|
229
|
+
baseGoalInstructions(task) {
|
|
205
230
|
return `你是此目标的协调者,负责把目标推进到可验收交付,不亲自实施子任务。\n目标:${task.spec}\n约束:${task.constraints || '未补充'}\n完成标准:${task.acceptance || '满足用户目标,说明交付与限制'}\n任务 ID:${task.id}\n当前协调身份:${task.coordinator?.sessionId};所有命令显式带 --session ${task.coordinator?.sessionId},不使用 tmux 标识。\n用 td collab task get ${task.id} --json 读取 roster、子任务、完整记录和 revision。\n先报告 ack;拆分为可交付子任务,用 td collab task create --group ${task.groupId} --parent ${task.id} --title '标题' --content '完整要求' --assignee <roster里的成员sessionId> --acceptance '明确标准' --work-type read-only|code。先按目标明确分类:查询、检查、研究、分析用 read-only,复用现有执行成员会话,不创建 Git 目录;修改代码用 code,才创建独立目录。简单问题只建一个必要任务,不为了形式拆成采集、复述、汇总多份任务。子任务继承自动评审与协调者。依赖任务用 --depends-on id,id,服务在依赖评审通过后自动分派。保持分工合理,不根据终端输出推断谁有空。\n每次收到明确更新后读取最新记录,处理失败与交接。服务自动安排独立评审、返工和依赖接续;不要重复请求评审或催促正在等待回复的成员。必要的人类决策用 task ask --attempt <本轮id>;方案需要用户确认时提交 task plan,不把普通回复当授权。\n只有已交付代码任务实际包含相对 base 的提交变化时,才需创建 --work-type code --integration 的最终集成子任务,--depends-on 依赖全部代码实施任务;独立目录会包含依赖的提交。不得在原仓库自动合并或发布。\n结束本次协调用 td collab task coordinate ${task.id} --revision <最新版本> --content '已安排的工作和下一步';有新事件服务会再次唤起你。所有子任务评审通过后报告 complete --summary '直接回答用户的简短结果和限制';只读目标直接汇总已核对结果,不创建集成或重复汇总任务。正文保留证据、评审结论和限制,仅代码目标补充目录、分支和提交,等待用户最终验收。任何异常都要留下明确说明,不伪造完成或进度。`;
|
|
206
231
|
}
|
|
207
232
|
notifyCoordinator(doc, task, update) {
|
|
@@ -222,7 +247,7 @@ export class CollaborationTaskStore {
|
|
|
222
247
|
doc.outbox.push({ id: id(), taskId: task.id, attemptId: attempt?.id ?? null, target: task.coordinator, kind: 'message', threadId: attempt?.threadId ?? task.id,
|
|
223
248
|
content: `你被指定为任务「${task.title}」的协调者。\n目标:${task.spec}\n约束:${task.constraints || '未补充'}\n验收标准:${task.acceptance || '由用户决定'}\n任务 ID:${task.id}\n用 td collab task get ${task.id} --text 查看原始记录;用 task list --group ${task.groupId} 查看工作组。需要拆分时用 task create --group ${task.groupId} --parent ${task.id} --title '子任务标题' --content '任务内容'。分派前查询 revision,再用 task assign --revision;把必要的人类决策交给 task ask,不推断成员忙闲,不停止其他成员终端。` });
|
|
224
249
|
}
|
|
225
|
-
create(owner, input, actor) {
|
|
250
|
+
create(owner, input, actor, origin) {
|
|
226
251
|
const title = text(input.title, '标题', 640, true), spec = text(input.spec, '任务内容', 32_000, true);
|
|
227
252
|
const constraints = text(input.constraints, '约束'), acceptance = text(input.acceptance, '完成标准');
|
|
228
253
|
if (input.dependsOn !== undefined && (!Array.isArray(input.dependsOn) || input.dependsOn.length > 32 || input.dependsOn.some(v => typeof v !== 'string')))
|
|
@@ -242,7 +267,7 @@ export class CollaborationTaskStore {
|
|
|
242
267
|
throw new CollaborationError('INVALID_REVIEWERS', '评审成员列表无效');
|
|
243
268
|
input.reviewers?.forEach(member);
|
|
244
269
|
return this.transaction(doc => {
|
|
245
|
-
const request = this.request(doc, actor, input.idempotencyKey, input);
|
|
270
|
+
const request = this.request(doc, actor, input.idempotencyKey, input, origin);
|
|
246
271
|
if (request.previous)
|
|
247
272
|
return request.previous;
|
|
248
273
|
if (doc.tasks.length >= 2000)
|
|
@@ -285,7 +310,7 @@ export class CollaborationTaskStore {
|
|
|
285
310
|
const task = { id: id(), ownerServiceId: owner, groupId: input.groupId, title, spec, constraints, acceptance,
|
|
286
311
|
coordinator: parent?.workflow ? parent.coordinator : input.coordinator !== undefined ? input.coordinator : input.parentTaskId ? doc.tasks.find(t => t.id === input.parentTaskId)?.coordinator ?? actor : null,
|
|
287
312
|
parentTaskId: input.parentTaskId ?? null, dependsOn, createdAt: now, updatedAt: now, revision: 1,
|
|
288
|
-
status: 'open', activeAttemptId: null, attempts: [], decisions: [], artifacts: [], events: [], deliveries: [], ...(workflow ? { workflow } : {}) };
|
|
313
|
+
...(origin ? { origin: structuredClone(origin) } : {}), status: 'open', activeAttemptId: null, attempts: [], decisions: [], artifacts: [], events: [], deliveries: [], ...(workflow ? { workflow } : {}) };
|
|
289
314
|
this.event(task, 'created', actor, spec);
|
|
290
315
|
if (parent?.constraints)
|
|
291
316
|
task.constraints = text([parent.constraints, constraints].filter(Boolean).join('\n'), '继承约束');
|
|
@@ -307,9 +332,9 @@ export class CollaborationTaskStore {
|
|
|
307
332
|
this.touchParent(doc, task, '新子任务已创建');
|
|
308
333
|
doc.requests[request.key] = { hash: request.hash, taskId: task.id };
|
|
309
334
|
return task;
|
|
310
|
-
});
|
|
335
|
+
}, origin);
|
|
311
336
|
}
|
|
312
|
-
apply(taskId, input, actor) {
|
|
337
|
+
apply(taskId, input, actor, origin) {
|
|
313
338
|
const content = text(input.content, '内容');
|
|
314
339
|
const summary = text(input.summary, '结果摘要', 2400);
|
|
315
340
|
if (summary && (input.kind !== 'report' || input.status !== 'complete'))
|
|
@@ -322,23 +347,25 @@ export class CollaborationTaskStore {
|
|
|
322
347
|
const task = doc.tasks.find(t => t.id === taskId);
|
|
323
348
|
if (!task)
|
|
324
349
|
throw new CollaborationError('TASK_NOT_FOUND', '任务不存在或无权查看', 404);
|
|
325
|
-
const request = this.request(doc, actor, input.idempotencyKey, [taskId, input]);
|
|
350
|
+
const request = this.request(doc, actor, input.idempotencyKey, [taskId, input], origin);
|
|
326
351
|
if (request.previous)
|
|
327
352
|
return request.previous;
|
|
353
|
+
if (origin && ['accept', 'approve-plan', 'report', 'ask', 'submit-plan', 'review', 'respond'].includes(input.kind))
|
|
354
|
+
throw new CollaborationError('INTEGRATION_OPERATION_DENIED', '此操作需要用户或原执行成员身份', 403);
|
|
328
355
|
const coordination = actor === null || sameMember(actor, task.coordinator);
|
|
329
356
|
if (['assign', 'coordinator', 'close', 'reopen', 'revise', 'request-review', 'coordinate', 'pause', 'resume', 'retry'].includes(input.kind) && !coordination)
|
|
330
357
|
throw new CollaborationError('TASK_PERMISSION_DENIED', '此操作需要用户或当前协调者', 403);
|
|
331
358
|
if (['answer', 'accept', 'approve-plan'].includes(input.kind) && actor !== null)
|
|
332
359
|
throw new CollaborationError('USER_DECISION_REQUIRED', '回答、方案确认与验收需要用户操作', 403);
|
|
333
|
-
if (!['report', 'ask', 'submit-plan', 'review', 'comment', 'answer'].includes(input.kind) && input.expectedRevision !== task.revision)
|
|
360
|
+
if (!['report', 'ask', 'submit-plan', 'review', 'comment', 'respond', 'answer'].includes(input.kind) && input.expectedRevision !== task.revision)
|
|
334
361
|
throw new CollaborationError('TASK_CHANGED', '任务已更新,请查看最新记录后重试', 409);
|
|
335
362
|
const userFollowup = actor === null && input.kind === 'revise' && !!content;
|
|
336
|
-
if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind) && !(task.status === 'accepted' && userFollowup))
|
|
363
|
+
if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind) && !(task.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
|
|
337
364
|
throw new CollaborationError('TASK_ARCHIVED', '任务已归档;需要继续时请重新打开', 409);
|
|
338
|
-
if (task.workflow && task.status !== 'open' && input.kind !== 'reopen' && !(task.status === 'accepted' && userFollowup))
|
|
365
|
+
if (task.workflow && task.status !== 'open' && input.kind !== 'reopen' && !(task.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
|
|
339
366
|
throw new CollaborationError('TASK_ARCHIVED', '自动协作的已完成版本不能追加执行报告,请先重新打开', 409);
|
|
340
367
|
const root = task.workflow?.rootTaskId ? doc.tasks.find(t => t.id === task.workflow.rootTaskId) : undefined;
|
|
341
|
-
if (root && root.status !== 'open' && !(root.status === 'accepted' && userFollowup))
|
|
368
|
+
if (root && root.status !== 'open' && !(root.status === 'accepted' && (userFollowup || ['comment', 'respond'].includes(input.kind))))
|
|
342
369
|
throw new CollaborationError('GOAL_ARCHIVED', '请先重新打开所属目标,再继续子任务', 409);
|
|
343
370
|
if (userFollowup && task.status === 'accepted') {
|
|
344
371
|
task.status = 'open';
|
|
@@ -418,8 +445,26 @@ export class CollaborationTaskStore {
|
|
|
418
445
|
const event = this.event(task, 'report', actor, content, attempt.id);
|
|
419
446
|
event.reportStatus = input.status;
|
|
420
447
|
event.evidence = input.evidence;
|
|
421
|
-
if (input.status === 'complete')
|
|
422
|
-
task.
|
|
448
|
+
if (input.status === 'complete' && attempt.id === task.activeAttemptId && task.status === 'accepted') {
|
|
449
|
+
task.status = 'open';
|
|
450
|
+
delete task.acceptedArtifactId;
|
|
451
|
+
delete task.completionMode;
|
|
452
|
+
}
|
|
453
|
+
if (input.status === 'complete') {
|
|
454
|
+
event.artifactId = id();
|
|
455
|
+
task.artifacts.push({ id: event.artifactId, attemptId: attempt.id, kind: 'result', content, ...(summary ? { summary } : {}), evidence: input.evidence, actor: actor, createdAt: event.createdAt });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else if (input.kind === 'respond') {
|
|
459
|
+
const original = task.events.find(e => e.id === input.replyToEventId && e.kind === 'comment');
|
|
460
|
+
const originalAttempt = original && task.attempts.find(a => a.id === original.attemptId);
|
|
461
|
+
if (!original || !originalAttempt || originalAttempt.id !== input.attemptId || !sameMember(actor, originalAttempt.assignee))
|
|
462
|
+
throw new CollaborationError('RESPONSE_PERMISSION_DENIED', '请由原追问对应的执行成员回复,并指定原事件和分派', 403);
|
|
463
|
+
if (!content)
|
|
464
|
+
throw new CollaborationError('INVALID_RESPONSE', '答复不能为空');
|
|
465
|
+
const response = this.event(task, 'respond', actor, content, originalAttempt.id);
|
|
466
|
+
response.replyToEventId = original.id;
|
|
467
|
+
response.evidence = input.evidence;
|
|
423
468
|
}
|
|
424
469
|
else if (input.kind === 'ask') {
|
|
425
470
|
if (!content || input.options !== undefined && (!Array.isArray(input.options) || input.options.length > 8 || input.options.some(v => typeof v !== 'string' || !v.trim() || v.length > 500)))
|
|
@@ -439,7 +484,7 @@ export class CollaborationTaskStore {
|
|
|
439
484
|
this.event(task, 'answer', null, content, decision.attemptId);
|
|
440
485
|
const recipient = task.attempts.find(a => a.id === decision.attemptId);
|
|
441
486
|
doc.outbox.push({ id: id(), taskId: task.id, attemptId: recipient.id, target: recipient.assignee, kind: 'message', threadId: recipient.threadId,
|
|
442
|
-
content: `任务「${task.title}」问题已回答\n问题:${decision.question}\n
|
|
487
|
+
content: `任务「${task.title}」问题已回答\n问题:${decision.question}\n${origin ? '外部接入回答' : '用户回答'}:${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}` });
|
|
443
488
|
}
|
|
444
489
|
else if (input.kind === 'submit-plan' || input.kind === 'review') {
|
|
445
490
|
if (!content || !actor)
|
|
@@ -519,7 +564,7 @@ export class CollaborationTaskStore {
|
|
|
519
564
|
event.target = recipient.assignee;
|
|
520
565
|
event.deliveryId = id();
|
|
521
566
|
doc.outbox.push({ id: event.deliveryId, taskId: task.id, attemptId: recipient.id, target: recipient.assignee,
|
|
522
|
-
kind: 'message', threadId: recipient.threadId, content: `任务「${task.title}」${input.kind === 'revise' ? actor === null ? '用户要求继续跟进' : '修改要求' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}${task.workflow?.kind === 'goal' ? `\n本轮继续沿用此目标和已交付证据,只推进新增要求。\n${this.goalInstructions(task)}` : '\n按这次要求继续当前分派,重新提交 complete 和摘要;新版本会重新独立评审。'}` });
|
|
567
|
+
kind: 'message', threadId: recipient.threadId, replyToEventId: event.id, content: `任务「${task.title}」${input.kind === 'revise' ? actor === null ? origin ? '外部接入要求继续跟进' : '用户要求继续跟进' : '修改要求' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}${input.kind === 'comment' ? `\n这是纯咨询,保留当前结果与验收。请用 td collab --session ${recipient.assignee.sessionId} task respond ${task.id} --attempt ${recipient.id} --to-event ${event.id} --content '答复' 关联回复;只有明确提交新的交付物才用 complete。` : task.workflow?.kind === 'goal' ? `\n本轮继续沿用此目标和已交付证据,只推进新增要求。\n${this.goalInstructions(task)}` : '\n按这次要求继续当前分派,重新提交 complete 和摘要;新版本会重新独立评审。'}` });
|
|
523
568
|
if (task.workflow?.kind === 'goal' && task.coordination) {
|
|
524
569
|
task.coordination.notifiedSequence = event.sequence;
|
|
525
570
|
task.coordination.notifiedAt = Date.now();
|
|
@@ -584,21 +629,21 @@ export class CollaborationTaskStore {
|
|
|
584
629
|
}
|
|
585
630
|
else
|
|
586
631
|
throw new CollaborationError('INVALID_TASK_OPERATION', '不支持的任务操作');
|
|
587
|
-
if (['report', 'ask', 'answer', 'submit-plan', 'review', 'accept', 'revise', 'close', 'reopen'].includes(input.kind))
|
|
632
|
+
if (['report', 'respond', 'ask', 'answer', 'submit-plan', 'review', 'accept', 'revise', 'close', 'reopen'].includes(input.kind))
|
|
588
633
|
this.notifyCoordinator(doc, task, task.events.at(-1));
|
|
589
634
|
if (task.events.length > 1000 || task.artifacts.length > 200 || task.attempts.length > 100)
|
|
590
635
|
throw new CollaborationError('TASK_HISTORY_LIMIT', '此任务历史达到上限,请建立后续任务并保留本任务为历史', 409);
|
|
591
636
|
task.revision++;
|
|
592
637
|
task.updatedAt = Math.max(Date.now(), task.updatedAt + 1);
|
|
593
|
-
if (task.workflow &&
|
|
638
|
+
if (task.workflow && !['coordinate', 'comment', 'respond'].includes(input.kind))
|
|
594
639
|
this.touchParent(doc, task, `${input.kind}${input.status ? ` / ${input.status}` : ''}:${content.slice(0, 1500)}`);
|
|
595
640
|
if (task.workflow && ['blocked', 'failed'].includes(input.status ?? ''))
|
|
596
641
|
task.automationIssue = content.slice(0, 1000);
|
|
597
642
|
if (task.workflow && input.kind === 'report' && ['ack', 'working', 'complete'].includes(input.status ?? ''))
|
|
598
643
|
delete task.automationIssue;
|
|
599
|
-
doc.requests[request.key] = { hash: request.hash, taskId };
|
|
644
|
+
doc.requests[request.key] = { hash: request.hash, taskId, eventId: task.events.at(-1)?.id };
|
|
600
645
|
return task;
|
|
601
|
-
});
|
|
646
|
+
}, origin);
|
|
602
647
|
}
|
|
603
648
|
touchParent(doc, task, content) {
|
|
604
649
|
const root = task.workflow?.rootTaskId ? doc.tasks.find(t => t.id === task.workflow.rootTaskId) : undefined;
|
|
@@ -717,7 +762,7 @@ export class CollaborationTaskStore {
|
|
|
717
762
|
this.touchParent(doc, task, '独立评审通过,后续依赖可以接续');
|
|
718
763
|
}
|
|
719
764
|
else if (action === 'review-feedback') {
|
|
720
|
-
const boundary = task.events.filter(e => e.kind === 'revise' && e.source
|
|
765
|
+
const boundary = task.events.filter(e => e.kind === 'revise' && ['user', 'integration'].includes(e.source ?? '') && e.attemptId === task.activeAttemptId).at(-1)?.sequence ?? 0;
|
|
721
766
|
const rounds = task.events.filter(e => e.kind === 'revise' && e.actor === null && e.artifactId && e.attemptId === task.activeAttemptId && e.sequence > boundary).length;
|
|
722
767
|
const blocked = review.verdict === 'blocked' || rounds >= task.workflow.maxRevisions;
|
|
723
768
|
const event = this.event(task, blocked ? 'review-blocked' : 'revise', null, review.content);
|
|
@@ -6,10 +6,10 @@ export async function ensureTeam(options) {
|
|
|
6
6
|
for (const role of labels) {
|
|
7
7
|
const members = options.members();
|
|
8
8
|
const reserved = members.find(member => member.role?.startsWith(role.split(' (')[0]));
|
|
9
|
-
if (reserved && (reserved.role !== role || reserved.cwd !== options.cwd)) {
|
|
10
|
-
throw new Error('本组已有自动配置的成员,请使用原 Agent
|
|
9
|
+
if (reserved && (reserved.role !== role || reserved.cwd !== options.cwd || (reserved.launchProfileKey ?? '') !== (options.launchProfileKey ?? ''))) {
|
|
10
|
+
throw new Error('本组已有自动配置的成员,请使用原 Agent、启动方案和目录,或在成员设置中移出后重新配置');
|
|
11
11
|
}
|
|
12
|
-
const existing = reserved ?? members.find(member => !ids.includes(member.id) && member.agentSlug === options.agentSlug && member.cwd === options.cwd);
|
|
12
|
+
const existing = reserved ?? members.find(member => !ids.includes(member.id) && member.agentSlug === options.agentSlug && member.cwd === options.cwd && (member.launchProfileKey ?? '') === (options.launchProfileKey ?? ''));
|
|
13
13
|
ids.push(existing?.id ?? await options.spawn(role));
|
|
14
14
|
}
|
|
15
15
|
return { coordinatorSessionId: ids[0], reviewerSessionIds: [ids[1]] };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import http from 'node:http';
|
|
4
|
+
import { INTEGRATION_PROTOCOL } from './integrationStore.js';
|
|
5
|
+
import { integrationSocketPath } from './integrationServer.js';
|
|
6
|
+
import { CollaborationError } from './collaborationProtocol.js';
|
|
7
|
+
import { executeCollaborationCommand, duration } from './collaborationCli.js';
|
|
8
|
+
import { getTermdockVersion } from '../utils/version.js';
|
|
9
|
+
export const INTEGRATION_HELP = `td integration — local scoped integration administration
|
|
10
|
+
groups | list
|
|
11
|
+
create --file <policy.json> --credential-file <private-output.json>
|
|
12
|
+
update --file <policy.json> (same identity/group; preserves the credential)
|
|
13
|
+
revoke <principal-id>
|
|
14
|
+
Credentials are saved as a private file and never printed. Workers set
|
|
15
|
+
TERMDOCK_INTEGRATION_CREDENTIAL_FILE, then use td collab --principal <id> …
|
|
16
|
+
Only the local administrator can provision/revoke grants. See docs/integrations.md.`;
|
|
17
|
+
export function readIntegrationCredential(file, principalId) {
|
|
18
|
+
const info = fs.lstatSync(file);
|
|
19
|
+
if (!info.isFile() || info.size > 4096 || (info.mode & 0o077) || process.getuid && info.uid !== process.getuid())
|
|
20
|
+
throw new CollaborationError('INTEGRATION_CREDENTIAL_FILE_DENIED', 'Credential file must be owned by this user and private (0600)', 403);
|
|
21
|
+
const credential = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
22
|
+
if (credential.id !== principalId || !credential.token || credential.protocol !== INTEGRATION_PROTOCOL)
|
|
23
|
+
throw new CollaborationError('INTEGRATION_CREDENTIAL_MISMATCH', 'Credential identity/protocol does not match the requested principal', 403);
|
|
24
|
+
return credential;
|
|
25
|
+
}
|
|
26
|
+
function headers(credential, adminToken) {
|
|
27
|
+
return { 'Content-Type': 'application/json', 'x-termdock-integration-protocol': String(INTEGRATION_PROTOCOL),
|
|
28
|
+
'x-termdock-cli-version': getTermdockVersion(),
|
|
29
|
+
...(credential ? { 'x-termdock-integration-id': credential.id, authorization: `Bearer ${credential.token}` } : {}),
|
|
30
|
+
...(adminToken ? { 'x-termdock-local-token': adminToken } : {}) };
|
|
31
|
+
}
|
|
32
|
+
function request(socketPath, method, route, body, requestHeaders, timeout = 30000) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const req = http.request({ socketPath, method, path: route, headers: requestHeaders }, res => {
|
|
35
|
+
let raw = '';
|
|
36
|
+
res.setEncoding('utf8');
|
|
37
|
+
res.on('data', chunk => { raw += chunk; if (raw.length > 4 * 1024 * 1024)
|
|
38
|
+
req.destroy(new Error('Integration response exceeds limit')); });
|
|
39
|
+
res.once('end', () => resolve({ statusCode: res.statusCode ?? 500, body: raw }));
|
|
40
|
+
res.once('error', reject);
|
|
41
|
+
});
|
|
42
|
+
req.setTimeout(timeout, () => req.destroy(new Error('Integration request timed out')));
|
|
43
|
+
req.once('error', reject);
|
|
44
|
+
req.end(body === undefined ? undefined : JSON.stringify(body));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function parsed(response) {
|
|
48
|
+
const result = JSON.parse(response.body);
|
|
49
|
+
if (response.statusCode >= 300)
|
|
50
|
+
throw new CollaborationError(result.code ?? 'INTEGRATION_REQUEST_FAILED', result.error ?? 'Integration request failed', response.statusCode);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
export async function runIntegrationAdmin(argv, port, adminToken, write) {
|
|
54
|
+
let reserved;
|
|
55
|
+
try {
|
|
56
|
+
const action = argv[0] ?? 'help', options = {};
|
|
57
|
+
if (action === 'help' || action === '--help') {
|
|
58
|
+
write(INTEGRATION_HELP);
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
for (let index = 1; index < argv.length; index += 2) {
|
|
62
|
+
const flag = argv[index];
|
|
63
|
+
if (!['--file', '--credential-file'].includes(flag) || !argv[index + 1] || options[flag])
|
|
64
|
+
throw new Error('Invalid integration arguments');
|
|
65
|
+
options[flag] = argv[index + 1];
|
|
66
|
+
}
|
|
67
|
+
const socketPath = integrationSocketPath(port);
|
|
68
|
+
if (action === 'groups' || action === 'list') {
|
|
69
|
+
if (argv.length !== 1)
|
|
70
|
+
throw new Error('Unexpected arguments');
|
|
71
|
+
write(JSON.stringify(parsed(await request(socketPath, 'GET', action === 'groups' ? '/admin/groups' : '/admin/principals', undefined, headers(undefined, adminToken)))));
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
if (action === 'update') {
|
|
75
|
+
if (!options['--file'] || Object.keys(options).length !== 1)
|
|
76
|
+
throw new Error('update requires only --file');
|
|
77
|
+
const policy = JSON.parse(fs.readFileSync(options['--file'], 'utf8'));
|
|
78
|
+
const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
|
|
79
|
+
if (capabilities.integration_policy_update !== true || policy.launchProfiles?.some((profile) => profile.startupInput !== undefined) && capabilities.startup_input_conditions !== true)
|
|
80
|
+
throw new CollaborationError('STARTUP_INPUT_CONDITION_UNSUPPORTED', 'Running service does not support this policy update', 409);
|
|
81
|
+
write(JSON.stringify(parsed(await request(socketPath, 'POST', `/admin/principals/${encodeURIComponent(policy.id)}/policy`, policy, headers(undefined, adminToken)))));
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
if (action === 'create') {
|
|
85
|
+
if (!options['--file'] || !options['--credential-file'] || Object.keys(options).length !== 2)
|
|
86
|
+
throw new Error('create requires --file and --credential-file');
|
|
87
|
+
const policy = JSON.parse(fs.readFileSync(options['--file'], 'utf8'));
|
|
88
|
+
if (policy.launchProfiles?.some((profile) => profile.startupInput !== undefined)) {
|
|
89
|
+
const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
|
|
90
|
+
if (capabilities.startup_input_conditions !== true)
|
|
91
|
+
throw new CollaborationError('STARTUP_INPUT_CONDITION_UNSUPPORTED', 'Running service does not support startup input conditions', 409);
|
|
92
|
+
}
|
|
93
|
+
const destination = path.resolve(options['--credential-file']);
|
|
94
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
|
|
95
|
+
fs.closeSync(fs.openSync(destination, 'wx', 0o600));
|
|
96
|
+
reserved = destination;
|
|
97
|
+
const credential = parsed(await request(socketPath, 'POST', '/admin/principals', policy, headers(undefined, adminToken)));
|
|
98
|
+
fs.writeFileSync(reserved, JSON.stringify(credential), { mode: 0o600 });
|
|
99
|
+
write(JSON.stringify({ ok: true, id: credential.id, credential_file: reserved }));
|
|
100
|
+
reserved = undefined;
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
throw new Error('Unknown integration operation');
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (reserved)
|
|
107
|
+
fs.unlinkSync(reserved);
|
|
108
|
+
write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_ADMIN_ERROR', error: error instanceof CollaborationError ? error.message : 'Integration administration failed; inspect arguments and private file paths' }));
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export async function revokeIntegration(id, port, adminToken, write) {
|
|
113
|
+
try {
|
|
114
|
+
write(JSON.stringify(parsed(await request(integrationSocketPath(port), 'POST', `/admin/principals/${encodeURIComponent(id)}/revoke`, {}, headers(undefined, adminToken)))));
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_ADMIN_ERROR', error: error instanceof CollaborationError ? error.message : 'Integration revocation failed' }));
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export async function runIntegrationCollab(command, port, io) {
|
|
123
|
+
try {
|
|
124
|
+
const principalId = String(command.options.principal), file = process.env.TERMDOCK_INTEGRATION_CREDENTIAL_FILE;
|
|
125
|
+
if (!file)
|
|
126
|
+
throw new CollaborationError('INTEGRATION_CREDENTIAL_REQUIRED', 'Set TERMDOCK_INTEGRATION_CREDENTIAL_FILE to a private credential file', 401);
|
|
127
|
+
const credential = readIntegrationCredential(file, principalId), socketPath = integrationSocketPath(port);
|
|
128
|
+
const capability = parsed(await request(socketPath, 'GET', '/capabilities', undefined, headers(credential)));
|
|
129
|
+
if (capability.integration_protocol !== INTEGRATION_PROTOCOL)
|
|
130
|
+
throw new CollaborationError('INCOMPATIBLE_INTEGRATION_PROTOCOL', 'CLI/service protocols are incompatible', 409);
|
|
131
|
+
if (command.action === 'capabilities') {
|
|
132
|
+
io.write(JSON.stringify({ ...capability, cli_version: getTermdockVersion() }));
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
if (command.action === 'events') {
|
|
136
|
+
const consumer = String(command.options.consumer ?? 'default');
|
|
137
|
+
if (command.operation === 'ack') {
|
|
138
|
+
io.write(JSON.stringify(parsed(await request(socketPath, 'POST', '/events/ack', { consumer, cursor: command.target }, headers(credential)))));
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
const params = new URLSearchParams({ consumer });
|
|
142
|
+
if (command.options.group)
|
|
143
|
+
params.set('group', String(command.options.group));
|
|
144
|
+
return subscribe(socketPath, `/events?${params}`, credential, command.options.timeout ? duration(String(command.options.timeout)) : undefined, io.write);
|
|
145
|
+
}
|
|
146
|
+
if (command.action === 'session') {
|
|
147
|
+
const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${command.operation === 'restore' ? '/restore' : ''}`;
|
|
148
|
+
const body = command.operation === 'create' ? { group_id: command.options.group, launch_profile: command.options['launch-profile'], cwd: command.options.cwd, idempotency_key: command.options['idempotency-key'] }
|
|
149
|
+
: command.operation === 'restore' ? { idempotency_key: command.options['idempotency-key'] } : undefined;
|
|
150
|
+
io.write(JSON.stringify(parsed(await request(socketPath, command.operation === 'get' ? 'GET' : 'POST', route, body, headers(credential)))));
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
if (!['task', 'message', 'help'].includes(command.action) || command.action === 'message' && command.operation !== 'get')
|
|
154
|
+
throw new CollaborationError('INTEGRATION_OPERATION_DENIED', 'Command is not exposed to integration identities', 403);
|
|
155
|
+
return executeCollaborationCommand(command, {}, { ...io, request: async (method, endpoint, body, timeout) => {
|
|
156
|
+
const origin = { source: command.options.source, externalActor: command.options['external-actor'] ? JSON.parse(String(command.options['external-actor'])) : undefined,
|
|
157
|
+
externalMessageId: command.options['external-message-id'], metadata: command.options.metadata ? JSON.parse(String(command.options.metadata)) : undefined };
|
|
158
|
+
return request(socketPath, method, endpoint.replace('/api/terminal/operations/orchestration', ''), body === undefined ? undefined : { ...body, origin }, headers(credential), timeout);
|
|
159
|
+
} });
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
io.write(JSON.stringify({ ok: false, code: error instanceof CollaborationError ? error.code : 'INTEGRATION_CONNECTION_FAILED', error: error instanceof CollaborationError ? error.message : 'Local integration connection failed; credentials were not sent to a public service' }));
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function subscribe(socketPath, route, credential, timeout, write) {
|
|
167
|
+
const deadline = timeout ? Date.now() + timeout : Infinity;
|
|
168
|
+
let backoff = 1000;
|
|
169
|
+
while (Date.now() < deadline) {
|
|
170
|
+
const outcome = await new Promise(resolve => {
|
|
171
|
+
let timer;
|
|
172
|
+
const req = http.request({ socketPath, method: 'GET', path: route, headers: headers(credential) }, res => {
|
|
173
|
+
res.setEncoding('utf8');
|
|
174
|
+
let pending = '', ended = false;
|
|
175
|
+
res.on('data', chunk => {
|
|
176
|
+
pending += chunk;
|
|
177
|
+
if (pending.length > 2 * 1024 * 1024) {
|
|
178
|
+
ended = true;
|
|
179
|
+
req.destroy();
|
|
180
|
+
resolve('stop');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
for (;;) {
|
|
184
|
+
const newline = pending.indexOf('\n');
|
|
185
|
+
if (newline < 0)
|
|
186
|
+
break;
|
|
187
|
+
const line = pending.slice(0, newline);
|
|
188
|
+
pending = pending.slice(newline + 1);
|
|
189
|
+
if (!line.trim())
|
|
190
|
+
continue;
|
|
191
|
+
let event;
|
|
192
|
+
try {
|
|
193
|
+
event = JSON.parse(line);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
write(JSON.stringify({ type: 'error', code: 'INVALID_EVENT_RESPONSE', retryable: false }));
|
|
197
|
+
ended = true;
|
|
198
|
+
req.destroy();
|
|
199
|
+
resolve('stop');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
write(line);
|
|
203
|
+
if (event.type === 'error' && !event.retryable) {
|
|
204
|
+
ended = true;
|
|
205
|
+
req.destroy();
|
|
206
|
+
resolve('stop');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
res.once('end', () => {
|
|
211
|
+
if ((res.statusCode ?? 500) >= 300 && pending) {
|
|
212
|
+
try {
|
|
213
|
+
const error = JSON.parse(pending);
|
|
214
|
+
write(JSON.stringify(error));
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
write(JSON.stringify({ type: 'error', code: 'INVALID_EVENT_RESPONSE', retryable: false }));
|
|
218
|
+
}
|
|
219
|
+
resolve((res.statusCode ?? 500) >= 500 ? 'retry' : 'stop');
|
|
220
|
+
}
|
|
221
|
+
else if (!ended)
|
|
222
|
+
resolve('retry');
|
|
223
|
+
});
|
|
224
|
+
res.once('close', () => { if (!ended)
|
|
225
|
+
resolve('retry'); });
|
|
226
|
+
});
|
|
227
|
+
req.once('error', () => resolve('retry'));
|
|
228
|
+
req.once('close', () => { if (timer)
|
|
229
|
+
clearTimeout(timer); });
|
|
230
|
+
if (Number.isFinite(deadline))
|
|
231
|
+
timer = setTimeout(() => { resolve('timeout'); req.destroy(); }, Math.max(1, deadline - Date.now()));
|
|
232
|
+
req.end();
|
|
233
|
+
});
|
|
234
|
+
if (outcome === 'stop')
|
|
235
|
+
return 1;
|
|
236
|
+
if (outcome === 'timeout')
|
|
237
|
+
return 0;
|
|
238
|
+
write(JSON.stringify({ type: 'connection', state: 'reconnecting', retry_in_ms: backoff }));
|
|
239
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(backoff, Math.max(0, deadline - Date.now()))));
|
|
240
|
+
backoff = Math.min(backoff * 2, 30000);
|
|
241
|
+
}
|
|
242
|
+
return 0;
|
|
243
|
+
}
|