termdock 1.4.300 → 1.4.302
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/App-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-BrfM7rGb.js → ArchitecturePanel-CARNBEqQ.js} +1 -1
- 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-RoMt6giT.js → ChangeWalkthroughPanel-00oa0z6w.js} +1 -1
- 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-CK2BKKnn.js → CollaborationMainWorkspace-DuEeKQKZ.js} +1 -1
- 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-AdjmvNUa.js → ComputerControlView-C9v1Ooc9.js} +2 -2
- 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-x8-mp6wC.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-B9KLZ--H.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-IEcDVREB.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-lWra5pTJ.js → DiffReviewLab-BKOvHdJY.js} +1 -1
- 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-B2Rue9PA.js → DiffViewer-6n3tgsdc.js} +1 -1
- 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-NMQsfqAe.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-BOBbegdP.js → RightSidebar-CcMSU1v8.js} +3 -3
- 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-BIVuPQbA.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-CHoOHnjf.js → app-D6vN8PW-.js} +3 -3
- 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-CNTT1p1A.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-Cl3gg8ZE.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-Sxmfy_3_.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-DaTnrOn7.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-BoiPA32P.js → chunk-2Q5K7J3B-ChC9yYOd.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-G5G1YBLk.js → chunk-5VM5RSS4-Bi4Hc8yL.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-BNHv_EnL.js → chunk-F27PBJKO-DvVWvQEk.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-BNHv_EnL.js.br → chunk-F27PBJKO-DvVWvQEk.js.br} +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-CxgtyKdq.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-UO-0iK-G.js → chunk-JWPE2WC7-BkNnaDiR.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-Cox9z-e2.js → chunk-POPQ4Y6H-BRzm6Uzi.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-B2W8-Lxh.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-B2W8-Lxh.js.gz → chunk-SVP7TREG-CDYm-dzk.js.gz} +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-BYDr9nkD.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-atArreox.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-CvfJj195.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-ByWE_fUn.js → cynefin-OW5HDTMX-i23hD0Z2.js} +1 -1
- package/dist/client/assets/{cynefin-OW5HDTMX-ByWE_fUn.js.br → cynefin-OW5HDTMX-i23hD0Z2.js.br} +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BRT0d0MN.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-Twtyv7ud.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-Vl06hZtd.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-Cp39Mpse.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-Drchji0N.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-rp8n_lct.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-BEGDaMBm.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-kvgdD4A7.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-DcYHv9hK.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-T9FQKn1F.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-BEB_X-y5.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-Do0TIdkh.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-_0Y7kKA2.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-D2_-ypCL.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-B6y0nIx8.js → infoDiagram-27XIBGKW-BT8xey7S.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-ebGuykGn.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-DJDkcIq5.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-pTBbviRZ.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-Dw9FB4Uc.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-CyLfVGgB.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-xRkirK0l.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-BxvaDEGQ.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-CaYbIQDC.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-Cxh-Bbay.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-CgOsk0Ai.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-D1S_gKcT.js → quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BG5e87Np.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-DL1rZ49m.js → rdpSession-Ggo3N5s5.js} +1 -1
- 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-DB039WbW.js → referenceLongPress-Df8W2cyP.js} +1 -1
- 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-C37DsQfu.js → requirementDiagram-BXWQKSXE-BNia9zI0.js} +1 -1
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-C37DsQfu.js.br → requirementDiagram-BXWQKSXE-BNia9zI0.js.br} +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DLzhCYoj.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-DeVpSvB0.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-CE03E66U.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-ButxQzs4.js → sparkles-DOuEtn62.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-DuqhHT3k.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-DxkSdJ8g.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-DVpcJ6y5.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-BRy562K_.js → triangle-alert-DzV0gQXz.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-BEKXLWGa.js → vennDiagram-4TSXK5OY-CzleWcmk.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.gz +0 -0
- package/dist/client/assets/{videoCompression-CoVXZ8qw.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-Cj30GTa9.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-Cm7NEdCl.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 +11 -4
- package/dist/server/agent/collaborationGroupRoutes.js +10 -5
- package/dist/server/agent/collaborationLaunchProfiles.js +43 -0
- package/dist/server/agent/collaborationRoutes.js +2 -0
- package/dist/server/agent/collaborationService.js +8 -2
- package/dist/server/agent/collaborationStore.js +23 -1
- package/dist/server/agent/collaborationTaskService.js +2 -2
- package/dist/server/agent/collaborationTaskStore.js +4 -1
- package/dist/server/agent/collaborationTeam.js +3 -3
- package/dist/server/agent/integrationCli.js +16 -0
- package/dist/server/agent/integrationServer.js +6 -1
- package/dist/server/agent/integrationSessions.js +55 -7
- package/dist/server/agent/integrationStore.js +23 -0
- package/dist/server/agent/registry.js +31 -1
- package/dist/server/routes/terminal.js +43 -12
- package/docs/integrations.md +84 -4
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-7k2I_09f.js +0 -179
- 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.br +0 -0
- package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.gz +0 -0
- 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.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-FlTbV9e1.js +0 -2
- 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-x8-mp6wC.js.br +0 -0
- package/dist/client/assets/DagPlayground-x8-mp6wC.js.gz +0 -0
- 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-IEcDVREB.js.br +0 -0
- package/dist/client/assets/DiffReview-IEcDVREB.js.gz +0 -0
- 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-B2Rue9PA.js.br +0 -0
- package/dist/client/assets/DiffViewer-B2Rue9PA.js.gz +0 -0
- 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.br +0 -0
- package/dist/client/assets/RightSidebar-BOBbegdP.js.gz +0 -0
- 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.br +0 -0
- package/dist/client/assets/app-CHoOHnjf.js.gz +0 -0
- 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-Cl3gg8ZE.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.gz +0 -0
- 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-DaTnrOn7.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.gz +0 -0
- package/dist/client/assets/channel-CEvBVvt3.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.gz +0 -0
- 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-SVP7TREG-B2W8-Lxh.js.br +0 -0
- 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/classDiagram-ZZMXUADV-nSmGM9Fl.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-nSmGM9Fl.js +0 -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-ByWE_fUn.js.gz +0 -0
- 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-Twtyv7ud.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.gz +0 -0
- 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-Cp39Mpse.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.gz +0 -0
- 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-rp8n_lct.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.gz +0 -0
- 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-kvgdD4A7.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.gz +0 -0
- 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-T9FQKn1F.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.gz +0 -0
- 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-Do0TIdkh.js.br +0 -0
- package/dist/client/assets/gestureArbiter-Do0TIdkh.js.gz +0 -0
- 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-D2_-ypCL.css.br +0 -0
- package/dist/client/assets/index-D2_-ypCL.css.gz +0 -0
- 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-DJDkcIq5.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.gz +0 -0
- 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-Dw9FB4Uc.js.br +0 -0
- package/dist/client/assets/layout-Dw9FB4Uc.js.gz +0 -0
- 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-xRkirK0l.js.br +0 -0
- package/dist/client/assets/mermaid.core-xRkirK0l.js.gz +0 -0
- 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-CaYbIQDC.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.gz +0 -0
- 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-CgOsk0Ai.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.gz +0 -0
- 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-BG5e87Np.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.gz +0 -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.br +0 -0
- package/dist/client/assets/referenceLongPress-DB039WbW.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.gz +0 -0
- 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-DeVpSvB0.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.gz +0 -0
- 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/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 +0 -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 +0 -8
- 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/vennDiagram-4TSXK5OY-BEKXLWGa.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.gz +0 -0
- 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-Cj30GTa9.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.gz +0 -0
|
@@ -3,6 +3,7 @@ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
import { CollaborationError } from './collaborationProtocol.js';
|
|
5
5
|
import { COLLAB_NAME_FORBIDDEN } from './collaborationPrompt.js';
|
|
6
|
+
import { validateLaunchProfiles } from './collaborationLaunchProfiles.js';
|
|
6
7
|
import { remoteSession, validateCollaborationNode } from './collaborationPeerTransport.js';
|
|
7
8
|
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
8
9
|
const address = (id) => { if (!id.startsWith('remote:'))
|
|
@@ -12,6 +13,7 @@ const local = (origin, id) => { const a = address(id); return a?.origin === orig
|
|
|
12
13
|
const mapRoles = (value, map) => value && Object.fromEntries(Object.entries(value).map(([id, item]) => [map(id), item]));
|
|
13
14
|
function mapGroup(group, map) {
|
|
14
15
|
return { ...group, sessionIds: group.sessionIds.map(map), roles: mapRoles(group.roles, map), roleVersions: mapRoles(group.roleVersions, map),
|
|
16
|
+
memberLaunchProfiles: mapRoles(group.memberLaunchProfiles, map),
|
|
15
17
|
instructions: group.instructions && { ...group.instructions, updatedBy: map(group.instructions.updatedBy) } };
|
|
16
18
|
}
|
|
17
19
|
/** Sole owner of peer discovery and group replication. UI and CLI call the same
|
|
@@ -460,10 +462,14 @@ export class CollaborationService {
|
|
|
460
462
|
throw new CollaborationError('GROUP_CHANGED', `协作组已变化(当前 updatedAt=${existing.updatedAt}),请刷新后重试`, 409, { currentUpdatedAt: existing.updatedAt });
|
|
461
463
|
if (typeof input.name !== 'string' || !input.name.trim() || COLLAB_NAME_FORBIDDEN.test(input.name) || !Array.isArray(input.sessionIds) || input.sessionIds.some(id => typeof id !== 'string'))
|
|
462
464
|
throw new Error('INVALID_GROUP');
|
|
465
|
+
const launch = input.launchProfiles !== undefined || input.defaultLaunchProfileId !== undefined ? {
|
|
466
|
+
launchProfiles: validateLaunchProfiles(input.launchProfiles ?? existing?.launchProfiles ?? [], input.defaultLaunchProfileId ?? null),
|
|
467
|
+
defaultLaunchProfileId: input.defaultLaunchProfileId ?? null,
|
|
468
|
+
} : {};
|
|
463
469
|
if (!input.sessionIds.some(id => address(id)) && !existing?.federated) {
|
|
464
470
|
if (input.sessionIds.some(id => !this.options.sessions().some(s => s.sessionId === id) && !existing?.sessionIds.includes(id)))
|
|
465
471
|
throw new Error('GROUP_MEMBER_UNAVAILABLE');
|
|
466
|
-
return { group: this.options.store.save(input) };
|
|
472
|
+
return { group: this.options.store.save({ ...input, ...launch }) };
|
|
467
473
|
}
|
|
468
474
|
if (input.sessionIds.length < 2)
|
|
469
475
|
throw new CollaborationError('INVALID_GROUP', '跨服务组仍需两个成员;修改未保存', 400);
|
|
@@ -477,7 +483,7 @@ export class CollaborationService {
|
|
|
477
483
|
const available = new Set([...this.options.sessions(), ...this.directory().sessions.filter(s => s.serviceConnected)].map(s => s.sessionId));
|
|
478
484
|
if (!Array.isArray(input.sessionIds) || input.sessionIds.some(id => !available.has(id) && !existing?.sessionIds.includes(id)))
|
|
479
485
|
throw new Error('GROUP_MEMBER_UNAVAILABLE');
|
|
480
|
-
const group = { ...existing, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
|
|
486
|
+
const group = { ...existing, ...launch, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
|
|
481
487
|
sessionIds: [...new Set(input.sessionIds)], createdAt: existing?.createdAt ?? Date.now(), updatedAt: Math.max(Date.now(), (existing?.updatedAt ?? 0) + 1), federated: true };
|
|
482
488
|
const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
|
|
483
489
|
if (canonical.sessionIds.some(id => !this.nodes().some(node => node.origin === address(id)?.origin)))
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { plainCollaborationSnapshot } from './collaborationText.js';
|
|
2
|
+
import { validateLaunchProfiles } from './collaborationLaunchProfiles.js';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import crypto from 'node:crypto';
|
|
@@ -125,9 +126,14 @@ export class CollaborationStore {
|
|
|
125
126
|
const existing = input.id ? this.document.groups.find((group) => group.id === input.id) : null;
|
|
126
127
|
if (input.id && (!existing || existing.deleted))
|
|
127
128
|
throw new CollaborationError('GROUP_NOT_FOUND', '协作组已删除,请刷新列表', 404);
|
|
129
|
+
const launchProfiles = input.launchProfiles === undefined ? existing?.launchProfiles : validateLaunchProfiles(input.launchProfiles, input.defaultLaunchProfileId ?? null);
|
|
130
|
+
const defaultLaunchProfileId = input.defaultLaunchProfileId !== undefined ? input.defaultLaunchProfileId : input.launchProfiles !== undefined ? null : existing?.defaultLaunchProfileId;
|
|
131
|
+
if (launchProfiles || defaultLaunchProfileId)
|
|
132
|
+
validateLaunchProfiles(launchProfiles ?? [], defaultLaunchProfileId);
|
|
128
133
|
const sessionIds = Array.from(new Set(input.sessionIds.map((id) => id.trim()).filter(Boolean)));
|
|
129
134
|
const group = pruneRoles({
|
|
130
135
|
...existing,
|
|
136
|
+
launchProfiles, defaultLaunchProfileId,
|
|
131
137
|
...(existing?.remoteSessions ? { remoteSessions: existing.remoteSessions.filter((session) => sessionIds.includes(session.sessionId)) } : {}),
|
|
132
138
|
id: existing?.id ?? newCollaborationId(this.takenIds(), this.drawId),
|
|
133
139
|
name: input.name.trim(),
|
|
@@ -141,6 +147,15 @@ export class CollaborationStore {
|
|
|
141
147
|
this.persist();
|
|
142
148
|
return group;
|
|
143
149
|
}
|
|
150
|
+
setMemberLaunchProfile(groupId, sessionId, profile) {
|
|
151
|
+
const group = this.getGroup(groupId);
|
|
152
|
+
if (!group || group.deleted || !group.sessionIds.includes(sessionId))
|
|
153
|
+
throw new CollaborationError('NOT_A_MEMBER', '会话不在协作组内', 409);
|
|
154
|
+
const [snapshot] = validateLaunchProfiles([profile], profile.id);
|
|
155
|
+
this.document.groups = this.document.groups.map(g => g.id === groupId ? { ...g,
|
|
156
|
+
memberLaunchProfiles: { ...g.memberLaunchProfiles, [sessionId]: snapshot }, updatedAt: Math.max(Date.now(), g.updatedAt + 1) } : g);
|
|
157
|
+
this.persist();
|
|
158
|
+
}
|
|
144
159
|
/** Set or clear a member's role (定位). The target may be any group member —
|
|
145
160
|
* oneself, a peer, or a federated remote session; peers can set each other's
|
|
146
161
|
* roles. Roles ride the group document into federation and are dropped
|
|
@@ -202,7 +217,9 @@ export class CollaborationStore {
|
|
|
202
217
|
...(group.remoteSessions ? { remoteSessions: group.remoteSessions.filter((session) => sourceIds.includes(session.sessionId)) } : {}),
|
|
203
218
|
updatedAt: Math.max(now, group.updatedAt + 1) }, sourceIds)];
|
|
204
219
|
}
|
|
205
|
-
return group.id === target.id ? [{ ...group, sessionIds: [...new Set([...group.sessionIds, input.sessionId])],
|
|
220
|
+
return group.id === target.id ? [{ ...group, sessionIds: [...new Set([...group.sessionIds, input.sessionId])],
|
|
221
|
+
...(source.memberLaunchProfiles?.[input.sessionId] ? { memberLaunchProfiles: { ...group.memberLaunchProfiles, [input.sessionId]: source.memberLaunchProfiles[input.sessionId] } } : {}),
|
|
222
|
+
updatedAt: Math.max(now, group.updatedAt + 1) }] : [group];
|
|
206
223
|
}),
|
|
207
224
|
messages: this.document.messages,
|
|
208
225
|
};
|
|
@@ -748,6 +765,11 @@ function stableJson(value) {
|
|
|
748
765
|
? Object.fromEntries(Object.entries(entry).sort(([a], [b]) => a.localeCompare(b))) : entry);
|
|
749
766
|
}
|
|
750
767
|
function validateFederatedGroup(group) {
|
|
768
|
+
if (group.launchProfiles !== undefined || group.defaultLaunchProfileId !== undefined)
|
|
769
|
+
validateLaunchProfiles(group.launchProfiles ?? [], group.defaultLaunchProfileId);
|
|
770
|
+
if (group.memberLaunchProfiles)
|
|
771
|
+
for (const profile of Object.values(group.memberLaunchProfiles))
|
|
772
|
+
validateLaunchProfiles([profile], profile.id);
|
|
751
773
|
if (!group.federated || typeof group.id !== 'string' || !group.id.startsWith('cross-') || typeof group.name !== 'string' || !group.name.trim()
|
|
752
774
|
|| !Number.isFinite(group.updatedAt) || !Number.isFinite(group.createdAt)
|
|
753
775
|
|| !Array.isArray(group.sessionIds) || group.sessionIds.some((id) => typeof id !== 'string')
|
|
@@ -54,9 +54,9 @@ export class CollaborationTaskService {
|
|
|
54
54
|
const roster = summary ? undefined : group?.sessionIds.map(sessionId => {
|
|
55
55
|
const member = this.peers.taskMember(sessionId);
|
|
56
56
|
const template = task.workflow?.reviewers.some(m => taskMemberKey(m) === taskMemberKey(member));
|
|
57
|
-
return { member, sessionId, role: `${template ? '此目标的执行成员模板;' : ''}${group.roles?.[sessionId] ?? ''}` };
|
|
57
|
+
return { member, sessionId, launchProfile: group.memberLaunchProfiles?.[sessionId], role: `${template ? '此目标的执行成员模板;' : ''}${group.roles?.[sessionId] ?? ''}` };
|
|
58
58
|
});
|
|
59
|
-
return { ...task, children, roster, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
|
|
59
|
+
return { ...task, children, roster, launchProfiles: summary ? undefined : group?.launchProfiles, defaultLaunchProfileId: summary ? undefined : group?.defaultLaunchProfileId, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
|
|
60
60
|
.map(({ id, attemptId, messageId, lastError }) => ({ id, attemptId, messageId, lastError })) };
|
|
61
61
|
}
|
|
62
62
|
list(groupId, actor) {
|
|
@@ -224,6 +224,9 @@ export class CollaborationTaskStore {
|
|
|
224
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。只按用户授权运行测试。' : ''}` });
|
|
225
225
|
}
|
|
226
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) {
|
|
227
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 '直接回答用户的简短结果和限制';只读目标直接汇总已核对结果,不创建集成或重复汇总任务。正文保留证据、评审结论和限制,仅代码目标补充目录、分支和提交,等待用户最终验收。任何异常都要留下明确说明,不伪造完成或进度。`;
|
|
228
231
|
}
|
|
229
232
|
notifyCoordinator(doc, task, update) {
|
|
@@ -442,7 +445,7 @@ export class CollaborationTaskStore {
|
|
|
442
445
|
const event = this.event(task, 'report', actor, content, attempt.id);
|
|
443
446
|
event.reportStatus = input.status;
|
|
444
447
|
event.evidence = input.evidence;
|
|
445
|
-
if (input.status === 'complete' && task.status === 'accepted') {
|
|
448
|
+
if (input.status === 'complete' && attempt.id === task.activeAttemptId && task.status === 'accepted') {
|
|
446
449
|
task.status = 'open';
|
|
447
450
|
delete task.acceptedArtifactId;
|
|
448
451
|
delete task.completionMode;
|
|
@@ -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]] };
|
|
@@ -9,6 +9,7 @@ import { getTermdockVersion } from '../utils/version.js';
|
|
|
9
9
|
export const INTEGRATION_HELP = `td integration — local scoped integration administration
|
|
10
10
|
groups | list
|
|
11
11
|
create --file <policy.json> --credential-file <private-output.json>
|
|
12
|
+
update --file <policy.json> (same identity/group; preserves the credential)
|
|
12
13
|
revoke <principal-id>
|
|
13
14
|
Credentials are saved as a private file and never printed. Workers set
|
|
14
15
|
TERMDOCK_INTEGRATION_CREDENTIAL_FILE, then use td collab --principal <id> …
|
|
@@ -70,10 +71,25 @@ export async function runIntegrationAdmin(argv, port, adminToken, write) {
|
|
|
70
71
|
write(JSON.stringify(parsed(await request(socketPath, 'GET', action === 'groups' ? '/admin/groups' : '/admin/principals', undefined, headers(undefined, adminToken)))));
|
|
71
72
|
return 0;
|
|
72
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
|
+
}
|
|
73
84
|
if (action === 'create') {
|
|
74
85
|
if (!options['--file'] || !options['--credential-file'] || Object.keys(options).length !== 2)
|
|
75
86
|
throw new Error('create requires --file and --credential-file');
|
|
76
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
|
+
}
|
|
77
93
|
const destination = path.resolve(options['--credential-file']);
|
|
78
94
|
fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
|
|
79
95
|
fs.closeSync(fs.openSync(destination, 'wx', 0o600));
|
|
@@ -60,6 +60,7 @@ export class IntegrationRuntime {
|
|
|
60
60
|
integrationError('INTEGRATION_ADMIN_REQUIRED', 'Local administration credential is required', 401);
|
|
61
61
|
};
|
|
62
62
|
app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.list().filter(g => !g.federated).map(g => ({ id: g.id, name: g.name })) }; }));
|
|
63
|
+
app.get('/admin/capabilities', run(req => { admin(req); return this.capabilities(); }));
|
|
63
64
|
app.get('/admin/principals', run(req => { admin(req); return { integrations: this.store.policies() }; }));
|
|
64
65
|
app.post('/admin/principals', run(req => { admin(req); this.group(req.body?.groupId); const provisioned = this.store.provision(req.body); try {
|
|
65
66
|
this.sync();
|
|
@@ -67,8 +68,10 @@ export class IntegrationRuntime {
|
|
|
67
68
|
catch {
|
|
68
69
|
this.scheduleRetry();
|
|
69
70
|
} return { id: provisioned.principal.id, token: provisioned.token, protocol: INTEGRATION_PROTOCOL }; }));
|
|
71
|
+
app.post('/admin/principals/:id/policy', run(req => { admin(req); if (req.body?.id !== req.params.id)
|
|
72
|
+
integrationError('INVALID_INTEGRATION_POLICY', 'Policy identity must match the requested identity'); this.group(req.body?.groupId); this.store.update(req.body); return { ok: true, id: req.params.id }; }));
|
|
70
73
|
app.post('/admin/principals/:id/revoke', run(req => { admin(req); this.store.revoke(req.params.id); return { ok: true }; }));
|
|
71
|
-
app.get('/capabilities', run(req => { const p = principal(req); return { ...this.capabilities(), principal: { id: p.id, group_id: p.groupId, permissions: p.permissions, launch_profiles: p.launchProfiles.map(profile => ({ id: profile.id, agent_slug: profile.agentSlug, cwd_roots: profile.cwdRoots })) } }; }));
|
|
74
|
+
app.get('/capabilities', run(req => { const p = principal(req); return { ...this.capabilities(), principal: { id: p.id, group_id: p.groupId, permissions: p.permissions, launch_profiles: p.launchProfiles.map(profile => ({ id: profile.id, agent_slug: profile.agentSlug, cwd_roots: profile.cwdRoots, startup_input_condition: !!profile.startupInput })) } }; }));
|
|
72
75
|
app.get('/tasks', run(req => { const p = principal(req); this.authorize(p, 'task.read', req.query.group); return { tasks: options.tasks.list(p.groupId, null) }; }));
|
|
73
76
|
app.get('/tasks/:id', run(async (req) => { const p = principal(req); this.taskPermission(p, req.params.id, 'task.read'); return { task: await options.tasks.get(req.params.id, null) }; }));
|
|
74
77
|
app.post('/tasks', run(async (req) => {
|
|
@@ -140,6 +143,8 @@ export class IntegrationRuntime {
|
|
|
140
143
|
integration_principal: true, group_scoped_permissions: true, durable_event_push: true,
|
|
141
144
|
event_ack_semantics: 'durable_received', correlated_task_responses: true, accepted_task_comments: true,
|
|
142
145
|
idempotent_session_create: true, exact_session_restore: true, external_validation: false,
|
|
146
|
+
startup_input_conditions: true,
|
|
147
|
+
integration_policy_update: true,
|
|
143
148
|
permissions: ['task.read', 'task.create', 'task.assign', 'task.comment', 'task.revise', 'task.answer', 'events.read', 'session.create', 'session.read', 'session.restore'] };
|
|
144
149
|
}
|
|
145
150
|
error(error) {
|
|
@@ -10,8 +10,12 @@ export function integrationLaunchCommand(record, restore) {
|
|
|
10
10
|
const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
11
11
|
return `cd -- ${quote(record.cwd)} && ${[record.profile.executable, ...args].map(quote).join(' ')}`;
|
|
12
12
|
}
|
|
13
|
+
function startupInput(record, now) {
|
|
14
|
+
const condition = record.startup_condition ?? record.profile.startupInput;
|
|
15
|
+
return condition ? { state: 'pending', deadline: now + (condition.timeoutMs ?? 120000), matched_since: null, observed_at: null } : undefined;
|
|
16
|
+
}
|
|
13
17
|
export function publicIntegrationSession(record) {
|
|
14
|
-
const { profile: _, deadline: _deadline, launch_submitted: _submitted, ...publicRecord } = record;
|
|
18
|
+
const { profile: _, deadline: _deadline, launch_submitted: _submitted, startup_condition: _condition, ...publicRecord } = record;
|
|
15
19
|
return publicRecord;
|
|
16
20
|
}
|
|
17
21
|
export function permittedCwd(profile, cwd) {
|
|
@@ -48,6 +52,7 @@ export class IntegrationSessions {
|
|
|
48
52
|
restoring = new Set();
|
|
49
53
|
timers = new Set();
|
|
50
54
|
stopObservation;
|
|
55
|
+
stopDeliveryGuard;
|
|
51
56
|
observing = false;
|
|
52
57
|
observeAgain = false;
|
|
53
58
|
constructor(file, adapter, bindingTimeout = 120000) {
|
|
@@ -65,14 +70,21 @@ export class IntegrationSessions {
|
|
|
65
70
|
throw error;
|
|
66
71
|
}
|
|
67
72
|
for (const record of this.doc.sessions)
|
|
68
|
-
if (['starting', 'restoring', 'binding_pending'].includes(record.state))
|
|
73
|
+
if (['starting', 'restoring', 'binding_pending'].includes(record.state) || record.startup_input?.state === 'pending')
|
|
69
74
|
this.scheduleRefresh(record);
|
|
75
|
+
// A pre-restart partial match cannot prove continuity of the live screen.
|
|
76
|
+
for (const record of this.doc.sessions)
|
|
77
|
+
if (record.startup_input && record.startup_input.state !== 'observed')
|
|
78
|
+
record.startup_input.matched_since = null;
|
|
79
|
+
this.stopDeliveryGuard = adapter.registerDeliveryGuard?.(id => this.deliveryReadiness(id));
|
|
70
80
|
this.stopObservation = adapter.subscribe(() => { void this.refreshAll().catch(() => { }); });
|
|
71
81
|
}
|
|
72
82
|
scheduleRefresh(record) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
for (const deadline of new Set([record.deadline, ...(record.startup_input ? [record.startup_input.deadline] : [])])) {
|
|
84
|
+
const timer = setTimeout(() => { this.timers.delete(timer); void this.refresh(record.session_id).catch(() => { }); }, Math.max(1, deadline - Date.now() + 5));
|
|
85
|
+
timer.unref();
|
|
86
|
+
this.timers.add(timer);
|
|
87
|
+
}
|
|
76
88
|
}
|
|
77
89
|
subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
|
|
78
90
|
snapshot() { return structuredClone(this.doc.sessions); }
|
|
@@ -128,6 +140,7 @@ export class IntegrationSessions {
|
|
|
128
140
|
group_id: principal.groupId, principal_id: principal.id, agent_slug: profile.agentSlug, agent_native_session_id: null,
|
|
129
141
|
state: 'starting', error_code: null, cwd, launch_profile: profile.id, profile: structuredClone(profile),
|
|
130
142
|
created_at: now, updated_at: now, deadline: now + this.bindingTimeout, launch_submitted: false };
|
|
143
|
+
record.startup_input = startupInput(record, now);
|
|
131
144
|
this.doc.requests[request.scoped] = { hash: request.hash, sessionId: record.session_id };
|
|
132
145
|
try {
|
|
133
146
|
this.saveRecord(record);
|
|
@@ -202,6 +215,9 @@ export class IntegrationSessions {
|
|
|
202
215
|
record.operation_id = randomUUID();
|
|
203
216
|
record.deadline = Date.now() + this.bindingTimeout;
|
|
204
217
|
record.updated_at = Date.now();
|
|
218
|
+
if (!record.profile.startupInput && !record.startup_condition)
|
|
219
|
+
record.startup_condition = structuredClone(principal.launchProfiles.find(profile => profile.id === record.launch_profile)?.startupInput);
|
|
220
|
+
record.startup_input = startupInput(record, Date.now());
|
|
205
221
|
this.doc.requests[request.scoped] = { hash: request.hash, sessionId: id };
|
|
206
222
|
try {
|
|
207
223
|
this.saveRecord(record);
|
|
@@ -267,13 +283,45 @@ export class IntegrationSessions {
|
|
|
267
283
|
next.state = 'failed';
|
|
268
284
|
next.error_code = 'SESSION_BINDING_TIMEOUT';
|
|
269
285
|
}
|
|
286
|
+
if (next.startup_input && next.startup_input.state !== 'observed') {
|
|
287
|
+
const condition = next.startup_condition ?? next.profile.startupInput;
|
|
288
|
+
let matched = false;
|
|
289
|
+
if (observed.running && observed.agentSlug === record.agent_slug && (!next.error_code || next.error_code === 'SESSION_BINDING_TIMEOUT') && this.adapter.capture) {
|
|
290
|
+
try {
|
|
291
|
+
const fold = (text) => text.replace(/[^\S\r\n]+/g, ' ').trim();
|
|
292
|
+
const viewport = fold(await this.adapter.capture(next));
|
|
293
|
+
matched = condition.allOf.every(term => viewport.includes(fold(term))) && !(condition.noneOf ?? []).some(term => viewport.includes(fold(term)));
|
|
294
|
+
}
|
|
295
|
+
catch { /* Missing capture never opens the delivery guard. */ }
|
|
296
|
+
}
|
|
297
|
+
const input = { ...next.startup_input };
|
|
298
|
+
input.matched_since = matched ? input.matched_since ?? Date.now() : null;
|
|
299
|
+
if (matched && Date.now() - input.matched_since >= (condition.stableMs ?? 1000)) {
|
|
300
|
+
input.state = 'observed';
|
|
301
|
+
input.observed_at = Date.now();
|
|
302
|
+
}
|
|
303
|
+
else
|
|
304
|
+
input.state = Date.now() >= input.deadline ? 'timed_out' : 'pending';
|
|
305
|
+
next.startup_input = input;
|
|
306
|
+
}
|
|
270
307
|
if (JSON.stringify(this.doc.sessions.find(s => s.session_id === id)) !== JSON.stringify(record))
|
|
271
308
|
return;
|
|
272
|
-
if (
|
|
309
|
+
if (JSON.stringify(next) !== JSON.stringify(record)) {
|
|
273
310
|
next.updated_at = Date.now();
|
|
274
311
|
this.saveRecord(next);
|
|
275
312
|
}
|
|
276
313
|
}
|
|
314
|
+
/** Only gates configured launches. Native UUID and Agent turn state are not input prerequisites. */
|
|
315
|
+
async deliveryReadiness(id) {
|
|
316
|
+
if (!this.doc.sessions.find(record => record.session_id === id)?.startup_input)
|
|
317
|
+
return null;
|
|
318
|
+
await this.refresh(id);
|
|
319
|
+
const record = this.doc.sessions.find(record => record.session_id === id);
|
|
320
|
+
if (record.error_code && record.error_code !== 'SESSION_BINDING_TIMEOUT')
|
|
321
|
+
return { allowed: false, reason: record.error_code };
|
|
322
|
+
return record.startup_input.state === 'observed' ? { allowed: true, reason: null }
|
|
323
|
+
: { allowed: false, reason: record.startup_input.state === 'timed_out' ? 'SESSION_STARTUP_INPUT_TIMEOUT' : 'SESSION_STARTUP_INPUT_PENDING' };
|
|
324
|
+
}
|
|
277
325
|
async refreshAll() {
|
|
278
326
|
if (this.observing) {
|
|
279
327
|
this.observeAgain = true;
|
|
@@ -291,6 +339,6 @@ export class IntegrationSessions {
|
|
|
291
339
|
this.observing = false;
|
|
292
340
|
}
|
|
293
341
|
}
|
|
294
|
-
close() { this.stopObservation(); for (const timer of this.timers)
|
|
342
|
+
close() { this.stopObservation(); this.stopDeliveryGuard?.(); for (const timer of this.timers)
|
|
295
343
|
clearTimeout(timer); }
|
|
296
344
|
}
|
|
@@ -54,6 +54,16 @@ export function validatePolicy(value) {
|
|
|
54
54
|
integrationError('INVALID_INTEGRATION_POLICY', 'resumeArgv requires one {sessionId} and one {launchArgs}; --last is forbidden');
|
|
55
55
|
if (!Array.isArray(profile.cwdRoots) || !profile.cwdRoots.length || profile.cwdRoots.some(root => typeof root !== 'string' || !path.isAbsolute(root) || /[\x00-\x1f\x7f]/.test(root)))
|
|
56
56
|
integrationError('INVALID_INTEGRATION_POLICY', 'cwdRoots must be absolute paths');
|
|
57
|
+
if (profile.startupInput !== undefined) {
|
|
58
|
+
const condition = profile.startupInput;
|
|
59
|
+
const validTerms = (terms) => Array.isArray(terms) && terms.length <= 16
|
|
60
|
+
&& terms.every(term => typeof term === 'string' && !!term.trim() && term.length <= 256 && !/[\x00-\x1f\x7f]/.test(term));
|
|
61
|
+
if (!condition || !validTerms(condition.allOf) || !condition.allOf.length || condition.noneOf !== undefined && !validTerms(condition.noneOf)
|
|
62
|
+
|| condition.stableMs !== undefined && (!Number.isInteger(condition.stableMs) || condition.stableMs < 500 || condition.stableMs > 5000)
|
|
63
|
+
|| condition.timeoutMs !== undefined && (!Number.isInteger(condition.timeoutMs) || condition.timeoutMs < 1000 || condition.timeoutMs > 120000)) {
|
|
64
|
+
integrationError('INVALID_INTEGRATION_POLICY', 'Invalid startupInput literal conditions or timing bounds');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
57
67
|
}
|
|
58
68
|
// Policies contain launcher configuration, never authentication material.
|
|
59
69
|
return structuredClone({ id: value.id, groupId: value.groupId, permissions: value.permissions, launchProfiles: value.launchProfiles });
|
|
@@ -101,6 +111,19 @@ export class IntegrationStore {
|
|
|
101
111
|
this.write(next);
|
|
102
112
|
return { principal: checked, token };
|
|
103
113
|
}
|
|
114
|
+
update(policy) {
|
|
115
|
+
const checked = validatePolicy(policy), next = structuredClone(this.doc);
|
|
116
|
+
const index = next.principals.findIndex(principal => principal.id === checked.id);
|
|
117
|
+
if (index < 0)
|
|
118
|
+
integrationError('INTEGRATION_NOT_FOUND', 'Integration does not exist', 404);
|
|
119
|
+
const existing = next.principals[index];
|
|
120
|
+
if (existing.revoked)
|
|
121
|
+
integrationError('INTEGRATION_REVOKED', 'Revoked identities cannot be reactivated', 403);
|
|
122
|
+
if (existing.groupId !== checked.groupId)
|
|
123
|
+
integrationError('INTEGRATION_GROUP_IMMUTABLE', 'An existing identity cannot change its group', 409);
|
|
124
|
+
next.principals[index] = { ...checked, tokenHash: existing.tokenHash, revoked: false };
|
|
125
|
+
this.write(next);
|
|
126
|
+
}
|
|
104
127
|
revoke(id) {
|
|
105
128
|
const next = structuredClone(this.doc), principal = next.principals.find(p => p.id === id);
|
|
106
129
|
if (!principal)
|
|
@@ -220,7 +220,7 @@ function replayFlags(agent, argv) {
|
|
|
220
220
|
* the only durable source available for a future recovery.
|
|
221
221
|
*/
|
|
222
222
|
export function inferResumeSessionId(agent, argv) {
|
|
223
|
-
const safeId = (value) => value && /^[A-Za-z0-9._-]+$/.test(value) ? value : null;
|
|
223
|
+
const safeId = (value) => value && !value.startsWith('-') && /^[A-Za-z0-9._-]+$/.test(value) ? value : null;
|
|
224
224
|
let named = -1;
|
|
225
225
|
for (let index = 0; index < argv.length; index += 1) {
|
|
226
226
|
if (tokenNamesAgent(argv[index], agent)) {
|
|
@@ -231,6 +231,36 @@ export function inferResumeSessionId(agent, argv) {
|
|
|
231
231
|
if (named < 0)
|
|
232
232
|
return null;
|
|
233
233
|
const tail = argv.slice(named + 1);
|
|
234
|
+
if (agent.isPlugin) {
|
|
235
|
+
const template = getPluginResumeConfig(agent.slug)?.command.trim().split(/\s+/);
|
|
236
|
+
if (!template || !tokenNamesAgent(template[0], agent))
|
|
237
|
+
return null;
|
|
238
|
+
const pattern = template.slice(1);
|
|
239
|
+
if (pattern.join(' ').split('{sessionId}').length !== 2)
|
|
240
|
+
return null;
|
|
241
|
+
const matches = new Set();
|
|
242
|
+
// A positional resume command must start the argument tail. Flag-based
|
|
243
|
+
// templates may follow other launch flags, as for built-in --resume.
|
|
244
|
+
const starts = pattern[0]?.startsWith('-') ? tail.map((_, index) => index) : [0];
|
|
245
|
+
for (const start of starts) {
|
|
246
|
+
let id = null;
|
|
247
|
+
const matched = pattern.every((token, index) => {
|
|
248
|
+
const value = tail[start + index];
|
|
249
|
+
if (value === undefined)
|
|
250
|
+
return false;
|
|
251
|
+
if (!token.includes('{sessionId}'))
|
|
252
|
+
return token === value;
|
|
253
|
+
const [prefix, suffix] = token.split('{sessionId}');
|
|
254
|
+
if (!value.startsWith(prefix) || !value.endsWith(suffix))
|
|
255
|
+
return false;
|
|
256
|
+
id = safeId(value.slice(prefix.length, suffix ? -suffix.length : undefined));
|
|
257
|
+
return !!id;
|
|
258
|
+
});
|
|
259
|
+
if (matched && id)
|
|
260
|
+
matches.add(id);
|
|
261
|
+
}
|
|
262
|
+
return matches.size === 1 ? [...matches][0] : null;
|
|
263
|
+
}
|
|
234
264
|
if (agent.slug === 'codex') {
|
|
235
265
|
const resume = tail.indexOf('resume');
|
|
236
266
|
return resume >= 0 ? safeId(tail[resume + 1]) : null;
|
|
@@ -54,6 +54,7 @@ import { CollaborationStore } from '../agent/collaborationStore.js';
|
|
|
54
54
|
import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
|
|
55
55
|
import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
|
|
56
56
|
import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
57
|
+
import { resolveCollaborationLaunch, launchProfileKey } from '../agent/collaborationLaunchProfiles.js';
|
|
57
58
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
58
59
|
import { NativeSessionSearch } from '../agent/nativeSessionSearch.js';
|
|
59
60
|
import { CollaborationError } from '../agent/collaborationProtocol.js';
|
|
@@ -439,6 +440,7 @@ async function flushClientStateBroadcast() {
|
|
|
439
440
|
}
|
|
440
441
|
}
|
|
441
442
|
const integrationSessionObservers = new Set();
|
|
443
|
+
let integrationDeliveryGuard;
|
|
442
444
|
function notifyIntegrationSessions() { for (const listener of integrationSessionObservers) {
|
|
443
445
|
try {
|
|
444
446
|
listener();
|
|
@@ -1594,7 +1596,9 @@ async function inspectCollaborationTmux(binding, requestedPane, allowPlainPane =
|
|
|
1594
1596
|
if (!Number.isInteger(serverPid) || serverPid <= 0)
|
|
1595
1597
|
throw new Error('TMUX_SERVER_IDENTITY_UNAVAILABLE');
|
|
1596
1598
|
const panes = await Promise.all(layout.windows.flatMap((window) => window.panes).map(async (pane) => {
|
|
1597
|
-
|
|
1599
|
+
// Native identity needs the foreground argv even for a named binary;
|
|
1600
|
+
// pane_current_command alone cannot prove an exact resumed conversation.
|
|
1601
|
+
const program = await resolveTmuxPaneProgram(pane, true);
|
|
1598
1602
|
const agent = detectAgentFromCommand(program?.rawArgs ?? program?.command ?? '', agentCustomCommands());
|
|
1599
1603
|
const nativeSessionId = agent && program?.rawArgs ? inferResumeSessionId(agent, splitCommandToArgv(program.rawArgs)) : null;
|
|
1600
1604
|
return { serverPid, sessionId: layout.sessionId, paneId: pane.id, panePid: pane.pid,
|
|
@@ -1658,6 +1662,11 @@ async function resolveCollaborationRoute(frontendSessionId) {
|
|
|
1658
1662
|
let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1659
1663
|
if (!record)
|
|
1660
1664
|
return { state: 'offline', reason: 'SESSION_REMOVED' };
|
|
1665
|
+
if (record.tmuxSessionName?.startsWith('wt-integration-') && !integrationDeliveryGuard)
|
|
1666
|
+
return { state: 'unavailable', reason: 'INTEGRATION_RUNTIME_UNAVAILABLE' };
|
|
1667
|
+
const startup = await integrationDeliveryGuard?.(frontendSessionId);
|
|
1668
|
+
if (startup && !startup.allowed)
|
|
1669
|
+
return { state: 'recovering', reason: startup.reason ?? 'SESSION_STARTUP_INPUT_PENDING' };
|
|
1661
1670
|
let backend = resolveOrchestrationBackend(record);
|
|
1662
1671
|
let binding = collaborationRouting.get(frontendSessionId) ?? {
|
|
1663
1672
|
sessionId: record.sessionId, backendSessionId: null, mode: record.mode, tmuxSessionName: record.tmuxSessionName,
|
|
@@ -1745,7 +1754,7 @@ function deliverCollaborationInboxWhenAgentReady(frontendSessionId) {
|
|
|
1745
1754
|
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1746
1755
|
}
|
|
1747
1756
|
async function spawnCollaborationAgentSession(req, group, sourceSessionId, input, bootstrapRole) {
|
|
1748
|
-
const agentSlug
|
|
1757
|
+
const { agentSlug, profile } = resolveCollaborationLaunch(group, input);
|
|
1749
1758
|
const launchers = await listDetectedAgentLaunchers();
|
|
1750
1759
|
const launcher = launchers.find((candidate) => candidate.slug === agentSlug);
|
|
1751
1760
|
if (!launcher)
|
|
@@ -1767,7 +1776,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1767
1776
|
}
|
|
1768
1777
|
const requestedCwd = typeof input.cwd === 'string' ? input.cwd.trim() : '';
|
|
1769
1778
|
const opened = await openInventorySession(req, {
|
|
1770
|
-
name: requestedName || `${launcher.displayName} · ${group.name}`,
|
|
1779
|
+
name: requestedName || `${profile?.name ?? launcher.displayName} · ${group.name}`,
|
|
1771
1780
|
customName: true,
|
|
1772
1781
|
mode,
|
|
1773
1782
|
cwd: requestedCwd || fallbackRecord?.cwd || undefined,
|
|
@@ -1796,7 +1805,9 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1796
1805
|
});
|
|
1797
1806
|
if (bootstrapRole)
|
|
1798
1807
|
collaborationStore.setRole({ groupId: group.id, sessionId: frontendSessionId, role: bootstrapRole });
|
|
1799
|
-
|
|
1808
|
+
if (profile)
|
|
1809
|
+
collaborationStore.setMemberLaunchProfile(group.id, frontendSessionId, profile);
|
|
1810
|
+
writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: profile?.command ?? launcher.command })}\r`);
|
|
1800
1811
|
const requestedTask = typeof input.task === 'string' ? input.task.trim().slice(0, 20_000) : '';
|
|
1801
1812
|
collaborationStore.send({
|
|
1802
1813
|
groupId: updatedGroup.id,
|
|
@@ -1808,7 +1819,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1808
1819
|
setTimeout(() => {
|
|
1809
1820
|
deliverCollaborationInboxWhenAgentReady(frontendSessionId);
|
|
1810
1821
|
}, 300).unref?.();
|
|
1811
|
-
return { group:
|
|
1822
|
+
return { group: collaborationStore.getGroup(group.id), session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
|
|
1812
1823
|
}
|
|
1813
1824
|
/** Restricted session adapter: only preconfigured argv and the exact owned
|
|
1814
1825
|
* terminal can be launched; no remote drive or arbitrary shell commands. */
|
|
@@ -1836,6 +1847,16 @@ function integrationTerminalRecord(record) {
|
|
|
1836
1847
|
}
|
|
1837
1848
|
export const integrationSessionAdapter = {
|
|
1838
1849
|
subscribe(listener) { integrationSessionObservers.add(listener); return () => { integrationSessionObservers.delete(listener); }; },
|
|
1850
|
+
registerDeliveryGuard(guard) { integrationDeliveryGuard = guard; return () => { if (integrationDeliveryGuard === guard)
|
|
1851
|
+
integrationDeliveryGuard = undefined; }; },
|
|
1852
|
+
async capture(record) {
|
|
1853
|
+
const observed = await integrationSessionAdapter.inspect(record);
|
|
1854
|
+
if (!observed.exists || !observed.running || observed.agentSlug !== record.agent_slug || !record.terminal_binding
|
|
1855
|
+
|| record.agent_native_session_id && observed.nativeId && record.agent_native_session_id !== observed.nativeId) {
|
|
1856
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Startup input observation requires the original Agent pane', 409);
|
|
1857
|
+
}
|
|
1858
|
+
return captureTmuxPaneText(runTmux, record.terminal_binding);
|
|
1859
|
+
},
|
|
1839
1860
|
async inspect(record) {
|
|
1840
1861
|
const terminal = integrationTerminalRecord(record);
|
|
1841
1862
|
if (!terminal)
|
|
@@ -1959,10 +1980,16 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
|
|
|
1959
1980
|
if (!source || template.serviceId !== service.descriptor().serviceId)
|
|
1960
1981
|
throw new Error('执行成员当前不可用,请协调者重新分派');
|
|
1961
1982
|
const snapshot = orchestrationSessionSnapshot(source);
|
|
1983
|
+
const inheritedProfile = collaborationStore.getGroup(task.groupId)?.memberLaunchProfiles?.[template.sessionId];
|
|
1962
1984
|
const slug = snapshot.agent?.slug ?? source.agentResume?.slug;
|
|
1963
1985
|
const launcher = (await listDetectedAgentLaunchers()).find(a => a.slug === slug);
|
|
1964
1986
|
if (!launcher || !source.cwd)
|
|
1965
1987
|
throw new Error('独立代码任务需要有项目目录的 Agent 成员,请选择可用成员');
|
|
1988
|
+
if (inheritedProfile) {
|
|
1989
|
+
if (inheritedProfile.agentSlug !== launcher.slug)
|
|
1990
|
+
throw new Error('执行成员的启动方案与当前 Agent 不一致,请重新分派');
|
|
1991
|
+
resolveCollaborationLaunch({ launchProfiles: [inheritedProfile] }, { launchProfileId: inheritedProfile.id, agentSlug: launcher.slug });
|
|
1992
|
+
}
|
|
1966
1993
|
const workspace = await prepareTaskWorkspace(task, source.cwd, dependencies, (dependency, repository) => importTaskDependency(task, dependency, repository, service, readDependency));
|
|
1967
1994
|
await pathValidator.allowSessionCwd(workspace.cwd);
|
|
1968
1995
|
const existing = globalSessionState.sessions.find(s => s.cwd === workspace.cwd);
|
|
@@ -1988,7 +2015,7 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
|
|
|
1988
2015
|
const temporary = `${launchFile}.${process.pid}.tmp`;
|
|
1989
2016
|
await fs.promises.writeFile(temporary, JSON.stringify({ sessionId: record.sessionId, backendId: opened.terminalSession.sessionId }), { mode: 0o600 });
|
|
1990
2017
|
await fs.promises.rename(temporary, launchFile);
|
|
1991
|
-
writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
|
|
2018
|
+
writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: inheritedProfile?.command ?? launcher.command })}\r`);
|
|
1992
2019
|
}
|
|
1993
2020
|
const group = collaborationStore.getGroup(task.groupId);
|
|
1994
2021
|
if (!group || group.deleted)
|
|
@@ -1996,6 +2023,8 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
|
|
|
1996
2023
|
if (!group.sessionIds.includes(record.sessionId))
|
|
1997
2024
|
await service.save({ id: group.id, name: group.name,
|
|
1998
2025
|
sessionIds: [...group.sessionIds, record.sessionId], expectedUpdatedAt: group.updatedAt });
|
|
2026
|
+
if (inheritedProfile)
|
|
2027
|
+
collaborationStore.setMemberLaunchProfile(group.id, record.sessionId, inheritedProfile);
|
|
1999
2028
|
return { sessionId: record.sessionId, workspace };
|
|
2000
2029
|
})().finally(() => preparingTaskWorkers.delete(key));
|
|
2001
2030
|
preparingTaskWorkers.set(key, operation);
|
|
@@ -3817,14 +3846,14 @@ async function getProcessSnapshot() {
|
|
|
3817
3846
|
});
|
|
3818
3847
|
return processSnapshotPromise;
|
|
3819
3848
|
}
|
|
3820
|
-
async function resolveTmuxPaneProgram(pane) {
|
|
3849
|
+
async function resolveTmuxPaneProgram(pane, requireArgs = false) {
|
|
3821
3850
|
// If pane command is a known shell, try to find a child foreground process
|
|
3822
3851
|
const command = normalizeProgramName(pane.command);
|
|
3823
3852
|
const commandKey = command?.toLowerCase() ?? null;
|
|
3824
3853
|
const isShell = commandKey ? shellNamesBackend.has(commandKey) : false;
|
|
3825
3854
|
// If pane command is NOT a shell but also too generic (e.g. "node"), also try
|
|
3826
3855
|
const isGeneric = commandKey ? genericProgramNames.has(commandKey) : false;
|
|
3827
|
-
if (!isShell && !isGeneric) {
|
|
3856
|
+
if (!isShell && !isGeneric && !requireArgs) {
|
|
3828
3857
|
// Non-shell, non-generic command — pane_current_command is good enough
|
|
3829
3858
|
return { command, source: 'tmux-pane', rawArgs: null };
|
|
3830
3859
|
}
|
|
@@ -5956,26 +5985,28 @@ router.post('/operations/collaboration-groups/:groupId/team', async (req, res) =
|
|
|
5956
5985
|
return res.status(404).json({ error: '协作组不存在' });
|
|
5957
5986
|
if (group.federated)
|
|
5958
5987
|
throw new Error('跨服务组请先从成员设置添加 Agent');
|
|
5959
|
-
const agentSlug
|
|
5988
|
+
const { agentSlug, profile } = resolveCollaborationLaunch(group, req.body ?? {});
|
|
5960
5989
|
if (!(await listDetectedAgentLaunchers()).some(agent => agent.slug === agentSlug))
|
|
5961
5990
|
throw new Error('所选 Agent 当前不可用');
|
|
5962
5991
|
if (typeof req.body?.cwd !== 'string' || !req.body.cwd.trim())
|
|
5963
5992
|
throw new Error('请选择工作目录');
|
|
5964
5993
|
const cwd = await resolveWorkingDirectory(req, req.body.cwd);
|
|
5965
5994
|
const previous = preparingTeams.get(group.id) ?? Promise.resolve();
|
|
5966
|
-
const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd,
|
|
5995
|
+
const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd, launchProfileKey: launchProfileKey(profile),
|
|
5967
5996
|
members: () => {
|
|
5968
5997
|
const current = collaborationStore.getGroup(group.id);
|
|
5969
5998
|
if (!current || current.deleted)
|
|
5970
5999
|
throw new Error('协作组已删除');
|
|
6000
|
+
if (profile && launchProfileKey(current.launchProfiles?.find(p => p.id === profile.id)) !== launchProfileKey(profile))
|
|
6001
|
+
throw new Error('启动方案已变更,请重新开始协作');
|
|
5971
6002
|
return globalSessionState.sessions.filter(record => current.sessionIds.includes(record.sessionId))
|
|
5972
|
-
.map(record => ({ id: record.sessionId, cwd: record.cwd, role: current.roles?.[record.sessionId], agentSlug: orchestrationSessionSnapshot(record).agent?.slug }));
|
|
6003
|
+
.map(record => ({ id: record.sessionId, cwd: record.cwd, role: current.roles?.[record.sessionId], agentSlug: orchestrationSessionSnapshot(record).agent?.slug, launchProfileKey: launchProfileKey(current.memberLaunchProfiles?.[record.sessionId]) }));
|
|
5973
6004
|
},
|
|
5974
6005
|
spawn: async (role) => {
|
|
5975
6006
|
const current = collaborationStore.getGroup(group.id);
|
|
5976
6007
|
if (!current || current.deleted)
|
|
5977
6008
|
throw new Error('协作组已删除');
|
|
5978
|
-
const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
|
|
6009
|
+
const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, launchProfileId: profile?.id ?? '', cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
|
|
5979
6010
|
return result.session.sessionId;
|
|
5980
6011
|
},
|
|
5981
6012
|
}));
|