termdock 1.4.190 → 1.4.192
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-BkcaQXGe.js → App-BbIl0DAa.js} +37 -37
- package/dist/client/assets/App-BbIl0DAa.js.br +0 -0
- package/dist/client/assets/App-BbIl0DAa.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BkTM7zQ0.js → ChangeWalkthroughPanel-CT7mLy_e.js} +1 -1
- package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-7UxLIhH5.js → DagPlayground-Bsf4bv7e.js} +1 -1
- package/dist/client/assets/DagPlayground-Bsf4bv7e.js.br +0 -0
- package/dist/client/assets/DagPlayground-Bsf4bv7e.js.gz +0 -0
- package/dist/client/assets/{DiffLab-Cn2bPbbB.js → DiffLab-BjiZcUSn.js} +1 -1
- package/dist/client/assets/DiffLab-BjiZcUSn.js.br +0 -0
- package/dist/client/assets/DiffLab-BjiZcUSn.js.gz +0 -0
- package/dist/client/assets/{DiffReview-Bax7DQmN.js → DiffReview-DmkAyOh4.js} +1 -1
- package/dist/client/assets/DiffReview-DmkAyOh4.js.br +0 -0
- package/dist/client/assets/DiffReview-DmkAyOh4.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-TG4kjagX.js → DiffReviewLab-wXYUmdna.js} +1 -1
- package/dist/client/assets/DiffReviewLab-wXYUmdna.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-wXYUmdna.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-3MezBn_a.js → DiffViewer-BlwwMnsB.js} +1 -1
- package/dist/client/assets/DiffViewer-BlwwMnsB.js.br +0 -0
- package/dist/client/assets/DiffViewer-BlwwMnsB.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-BV_0H-Ls.js → ModelPreview-DGIaQAcq.js} +1 -1
- package/dist/client/assets/ModelPreview-DGIaQAcq.js.br +0 -0
- package/dist/client/assets/ModelPreview-DGIaQAcq.js.gz +0 -0
- package/dist/client/assets/{RightSidebar-BHlwv3gW.js → RightSidebar-PgJ0XzEh.js} +47 -47
- package/dist/client/assets/RightSidebar-PgJ0XzEh.js.br +0 -0
- package/dist/client/assets/RightSidebar-PgJ0XzEh.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-DOk0otQq.js → abnfDiagram-VCTEODGH-B_sCIKms.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.gz +0 -0
- package/dist/client/assets/app-5zC_J9mq.js +34 -0
- package/dist/client/assets/app-5zC_J9mq.js.br +0 -0
- package/dist/client/assets/app-5zC_J9mq.js.gz +0 -0
- package/dist/client/assets/{arc-CfeOYtSi.js → arc-Ps2GTfJ3.js} +1 -1
- package/dist/client/assets/arc-Ps2GTfJ3.js.br +0 -0
- package/dist/client/assets/arc-Ps2GTfJ3.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-Dys1Wr-I.js → architectureDiagram-5GKGNRK7-DxKfqPAR.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DxKfqPAR.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DxKfqPAR.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-D8DbjMtS.js → blockDiagram-I7D4REHJ-BNL8tkC0.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DJcMUQXb.js → c4Diagram-7LVT6UL2-hyEavwo6.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.gz +0 -0
- package/dist/client/assets/channel-Bkfq4dnz.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BPmsHIzM.js → chunk-2Q5K7J3B-DN_HrxPz.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-BTxB35XY.js → chunk-5VM5RSS4-BofAFEcC.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-BiUpuqFN.js → chunk-F27PBJKO-CLNw1JDM.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-Bvca4yZA.js → chunk-IMKFNOWR-DsVDl8p7.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-CxCNQSVG.js → chunk-JWPE2WC7-CdfzOrPC.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-C9E5B9o7.js → chunk-POPQ4Y6H-MXmTC-sG.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-D4Qm28xz.js → chunk-SVP7TREG-DfwfQAY6.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-CS0AERD9.js → chunk-TICWLB2K-N3OPIvmU.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-C1vCDgbp.js → chunk-XXDRQBXY-IurIojt5.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-Dahb2o7K.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-Dahb2o7K.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-D0-yuoIn.js → cose-bilkent-JH36ORCC-DHbDHTvp.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-C9ZqMdi9.js → cynefin-OW5HDTMX-DyTW2Iyi.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-DyTW2Iyi.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DyTW2Iyi.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-Drvx9Dke.js → cynefinDiagram-5FMLGOSQ-DEan3pcN.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-mUXNFR_H.js → dagre-GXQ25YYZ-Rnw2v1uV.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-CKJgF0VS.js → diagram-S7CK7UJ4-CmBa29S9.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-Cp9NE2Gc.js → diagram-UQ7AKVKN-Cqzmpane.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-mnyDXn4l.js → diagram-VSXAHHWV-CrHTQozH.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-6lyjUw6O.js → diagram-VX7I27RA-C7nQgan9.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-3K_Dhn1t.js → diagram-Z3DM3KII-93X66n3v.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-B2mHkMsE.js → ebnfDiagram-PWID7BFC-LfScrrH8.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Brfc7YEf.js → erDiagram-RLTQ6QDP-DAWKsrIB.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-UGHsvQMc.js → flowDiagram-HODETNUW-CcdXfpU7.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CC30lZuL.js → ganttDiagram-EL5Y4UJY-DPLNbHSY.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DPLNbHSY.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DPLNbHSY.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-DIYXjBYY.js → gestureArbiter-B5PxTluh.js} +1 -1
- package/dist/client/assets/gestureArbiter-B5PxTluh.js.br +0 -0
- package/dist/client/assets/gestureArbiter-B5PxTluh.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-CiwHG61R.js → gitGraphDiagram-WWUBYQGX-P741cwUf.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.gz +0 -0
- package/dist/client/assets/{index-TEmneMvF.css → index-oKp6kSMR.css} +1 -1
- package/dist/client/assets/index-oKp6kSMR.css.br +0 -0
- package/dist/client/assets/{index-TEmneMvF.css.gz → index-oKp6kSMR.css.gz} +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-CcAeYa6X.js → infoDiagram-27XIBGKW-xrl-VW74.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-COFtLTBs.js → ishikawaDiagram-5VMMS53U-BJmiVWrB.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-D0iXlEDw.js → journeyDiagram-3NMN7TZE-DZX8uYK2.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-Dr2EVP3G.js → kanban-definition-UXKFOSKX-1SmdXf0S.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.gz +0 -0
- package/dist/client/assets/{layout-CCzGp_3m.js → layout-De4QxmMN.js} +1 -1
- package/dist/client/assets/layout-De4QxmMN.js.br +0 -0
- package/dist/client/assets/layout-De4QxmMN.js.gz +0 -0
- package/dist/client/assets/{linear-B-AlDkuD.js → linear-Dc93ZnhP.js} +1 -1
- package/dist/client/assets/linear-Dc93ZnhP.js.br +0 -0
- package/dist/client/assets/linear-Dc93ZnhP.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-C8GTgQvN.js → mermaid.core-Ny4ifEul.js} +5 -5
- package/dist/client/assets/mermaid.core-Ny4ifEul.js.br +0 -0
- package/dist/client/assets/mermaid.core-Ny4ifEul.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-B1_AIM7r.js → mindmap-definition-YA3MSWOX-CE75rUrK.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-DUJCjLgA.js → passwordBootstrap-BuXV9qsA.js} +1 -1
- package/dist/client/assets/passwordBootstrap-BuXV9qsA.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-BuXV9qsA.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-BGri_6xk.js → pegDiagram-XKGWAZYB-CR8E9wWF.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-DyJUIUtO.js → pieDiagram-E7YTZNPT-CKZz2-Iz.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-ChYx832r.js → quadrantDiagram-AXDQQJYC-w0wqPGg1.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-Ll7CEGCv.js → railroadDiagram-O6MQD6OU-CWYxj4Iu.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-DhODL_ZR.js → referenceLongPress-xQTJmyUv.js} +1 -1
- package/dist/client/assets/referenceLongPress-xQTJmyUv.js.br +0 -0
- package/dist/client/assets/referenceLongPress-xQTJmyUv.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-WAFfi6wC.js → requirementDiagram-BXWQKSXE-DzcUlIZo.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-Bmk9JzLH.js → sankeyDiagram-P5KCCOFB-B1KFzqqb.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CQLp4wz7.js → sequenceDiagram-WJ2MYXX4-Bph5Zy95.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-7biER85h.js → sizeCapture-INFHLROL-B22qdSe2.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.gz +0 -0
- package/dist/client/assets/{sparkles-Bdif6oju.js → sparkles-iFGazcNw.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-Cy9r_YvI.js → stateDiagram-D77RDMKH-qt4ZIeIU.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-xKezo5Hj.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-DeAGlPA-.js → swimlanes-42K2YHIH-BLCfdR2d.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CNA032xr.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-BRAobv6g.js → timeline-definition-24CTP7MA-B1KrZ6Jd.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-Bllwcw-n.js → triangle-alert-BNMREFNU.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-B4uz7tHn.js → vennDiagram-4TSXK5OY-DWNDoCmv.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-uRNjWdqL.js → wardleyDiagram-VM6X3IG4-kdVZqb5Z.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DCT4HGtL.js → xychartDiagram-S5SC5T6Z-C1U9Cr8F.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.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 +224 -11
- package/dist/server/agent/collaborationDeliveryWorker.js +94 -1
- package/dist/server/agent/collaborationGroupRoutes.js +25 -0
- package/dist/server/agent/collaborationPrompt.js +97 -34
- package/dist/server/agent/collaborationRoutes.js +119 -9
- package/dist/server/agent/collaborationSpawn.js +16 -0
- package/dist/server/agent/collaborationStore.js +86 -12
- package/dist/server/agent/collaborationTmuxDelivery.js +86 -1
- package/dist/server/cli.js +11 -4
- package/dist/server/routes/terminal.js +271 -13
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-BkcaQXGe.js.br +0 -0
- package/dist/client/assets/App-BkcaQXGe.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BkTM7zQ0.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BkTM7zQ0.js.gz +0 -0
- package/dist/client/assets/DagPlayground-7UxLIhH5.js.br +0 -0
- package/dist/client/assets/DagPlayground-7UxLIhH5.js.gz +0 -0
- package/dist/client/assets/DiffLab-Cn2bPbbB.js.br +0 -0
- package/dist/client/assets/DiffLab-Cn2bPbbB.js.gz +0 -0
- package/dist/client/assets/DiffReview-Bax7DQmN.js.br +0 -0
- package/dist/client/assets/DiffReview-Bax7DQmN.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-TG4kjagX.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-TG4kjagX.js.gz +0 -0
- package/dist/client/assets/DiffViewer-3MezBn_a.js.br +0 -0
- package/dist/client/assets/DiffViewer-3MezBn_a.js.gz +0 -0
- package/dist/client/assets/ModelPreview-BV_0H-Ls.js.br +0 -0
- package/dist/client/assets/ModelPreview-BV_0H-Ls.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BHlwv3gW.js.br +0 -0
- package/dist/client/assets/RightSidebar-BHlwv3gW.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DOk0otQq.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DOk0otQq.js.gz +0 -0
- package/dist/client/assets/app-B0az5Yz6.js +0 -34
- package/dist/client/assets/app-B0az5Yz6.js.br +0 -0
- package/dist/client/assets/app-B0az5Yz6.js.gz +0 -0
- package/dist/client/assets/arc-CfeOYtSi.js.br +0 -0
- package/dist/client/assets/arc-CfeOYtSi.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Dys1Wr-I.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-Dys1Wr-I.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-D8DbjMtS.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-D8DbjMtS.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DJcMUQXb.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DJcMUQXb.js.gz +0 -0
- package/dist/client/assets/channel-SwcBL8jW.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-BiUpuqFN.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-BiUpuqFN.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Bvca4yZA.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Bvca4yZA.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-D4Qm28xz.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-D4Qm28xz.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-CS0AERD9.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-CS0AERD9.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-BOSTHjwM.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BOSTHjwM.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-D0-yuoIn.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-D0-yuoIn.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-C9ZqMdi9.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-C9ZqMdi9.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Drvx9Dke.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Drvx9Dke.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-mUXNFR_H.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-mUXNFR_H.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CKJgF0VS.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CKJgF0VS.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cp9NE2Gc.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-Cp9NE2Gc.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-mnyDXn4l.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-mnyDXn4l.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-6lyjUw6O.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-6lyjUw6O.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-3K_Dhn1t.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-3K_Dhn1t.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-B2mHkMsE.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-B2mHkMsE.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Brfc7YEf.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Brfc7YEf.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-UGHsvQMc.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-UGHsvQMc.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CC30lZuL.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CC30lZuL.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-DIYXjBYY.js.br +0 -0
- package/dist/client/assets/gestureArbiter-DIYXjBYY.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CiwHG61R.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CiwHG61R.js.gz +0 -0
- package/dist/client/assets/index-TEmneMvF.css.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-COFtLTBs.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-COFtLTBs.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-D0iXlEDw.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-D0iXlEDw.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Dr2EVP3G.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Dr2EVP3G.js.gz +0 -0
- package/dist/client/assets/layout-CCzGp_3m.js.br +0 -0
- package/dist/client/assets/layout-CCzGp_3m.js.gz +0 -0
- package/dist/client/assets/linear-B-AlDkuD.js.br +0 -0
- package/dist/client/assets/linear-B-AlDkuD.js.gz +0 -0
- package/dist/client/assets/mermaid.core-C8GTgQvN.js.br +0 -0
- package/dist/client/assets/mermaid.core-C8GTgQvN.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-B1_AIM7r.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-B1_AIM7r.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-DUJCjLgA.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-DUJCjLgA.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BGri_6xk.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BGri_6xk.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DyJUIUtO.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DyJUIUtO.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-ChYx832r.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-ChYx832r.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Ll7CEGCv.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Ll7CEGCv.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-DhODL_ZR.js.br +0 -0
- package/dist/client/assets/referenceLongPress-DhODL_ZR.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-WAFfi6wC.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-WAFfi6wC.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bmk9JzLH.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Bmk9JzLH.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CQLp4wz7.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CQLp4wz7.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-7biER85h.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-7biER85h.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Cy9r_YvI.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Cy9r_YvI.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-DOly_YL3.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-DeAGlPA-.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-DeAGlPA-.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DxYgGhLH.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-BRAobv6g.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-BRAobv6g.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-B4uz7tHn.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-B4uz7tHn.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-uRNjWdqL.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-uRNjWdqL.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DCT4HGtL.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DCT4HGtL.js.gz +0 -0
|
@@ -13,7 +13,7 @@ import { createRequire } from 'module';
|
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import QRCode from 'qrcode';
|
|
15
15
|
import { execFile, spawn } from 'child_process';
|
|
16
|
-
import { createHash, randomUUID } from 'crypto';
|
|
16
|
+
import { createHash, randomBytes, randomUUID } from 'crypto';
|
|
17
17
|
import { promisify } from 'util';
|
|
18
18
|
import { caffeinateManager } from '../utils/caffeinate.js';
|
|
19
19
|
import { DesktopRuntimeOwnerClient } from '../utils/desktopRuntimeOwner.js';
|
|
@@ -39,13 +39,13 @@ import { buildBracketedSubmitBytes, canDeliverPromptToAgent } from '../agent/pro
|
|
|
39
39
|
import { collaborationRoutes } from '../agent/collaborationRoutes.js';
|
|
40
40
|
import { COLLAB_LIMITS, extrasFromBody } from '../agent/collaborationProtocol.js';
|
|
41
41
|
import { CollaborationStore } from '../agent/collaborationStore.js';
|
|
42
|
-
import { formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
|
|
43
|
-
import { resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
42
|
+
import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
|
|
43
|
+
import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
44
44
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
45
45
|
import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
|
|
46
46
|
import { CollaborationRoutingStore, selectCollaborationPane } from '../agent/collaborationRouting.js';
|
|
47
47
|
import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
|
|
48
|
-
import { writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
|
|
48
|
+
import { approveCollaborationDialog, captureTmuxPaneHistory, captureTmuxPaneText, recoverStuckPaste, sendTmuxPaneKey, writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
|
|
49
49
|
import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, uninstallHooksForSlug, } from '../agent/installers.js';
|
|
50
50
|
import { loadPlugins, savePlugin, removePlugin, readPluginIcon, validateManifest, } from '../agent/plugins.js';
|
|
51
51
|
import { TmuxLifecycleCoordinator } from '../utils/tmuxLifecycle.js';
|
|
@@ -152,6 +152,18 @@ class HttpStatusError extends Error {
|
|
|
152
152
|
}
|
|
153
153
|
const terminalSessions = new Map();
|
|
154
154
|
let globalSessionState = { sessions: [], updatedAt: Date.now() };
|
|
155
|
+
/** 8-char base36 frontend session ids. Dozens of sessions live at once, so a
|
|
156
|
+
* short collision-checked id beats a 36-char UUID to type in CLI operations;
|
|
157
|
+
* the random draw is retried when it hits an existing session. Each of the 8
|
|
158
|
+
* bytes maps to one base36 character — toString(36) turns the 0-35 residue
|
|
159
|
+
* into a single 0-9a-z char, never a multi-digit number string. */
|
|
160
|
+
function generateFrontendSessionId() {
|
|
161
|
+
for (;;) {
|
|
162
|
+
const id = [...randomBytes(8)].map((byte) => (byte % 36).toString(36)).join('');
|
|
163
|
+
if (!globalSessionState.sessions.some((session) => session.sessionId === id))
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
155
167
|
// ── 持久化 globalSessionState 到磁盘,防止服务重启后丢失 ──
|
|
156
168
|
const GLOBAL_SESSION_STATE_FILE = `${TERMDOCK_DIR}/global-session-state.json`;
|
|
157
169
|
const CLIENT_STATES_FILE = `${TERMDOCK_DIR}/client-states.json`; // 保留用于迁移
|
|
@@ -1043,6 +1055,10 @@ function removeGlobalSessionRecord(frontendSessionId) {
|
|
|
1043
1055
|
if (changed) {
|
|
1044
1056
|
collaborationStore.removeSession(frontendSessionId);
|
|
1045
1057
|
collaborationRouting.remove(frontendSessionId);
|
|
1058
|
+
for (const key of [...collaborationScaffoldState.keys()]) {
|
|
1059
|
+
if (key.startsWith(`${frontendSessionId}|`))
|
|
1060
|
+
collaborationScaffoldState.delete(key);
|
|
1061
|
+
}
|
|
1046
1062
|
}
|
|
1047
1063
|
return changed;
|
|
1048
1064
|
}
|
|
@@ -1243,7 +1259,7 @@ async function openInventorySession(req, input) {
|
|
|
1243
1259
|
throw new HttpStatusError(404, 'session not found');
|
|
1244
1260
|
}
|
|
1245
1261
|
if (!record) {
|
|
1246
|
-
const sessionId = preferredFrontendSessionId ||
|
|
1262
|
+
const sessionId = preferredFrontendSessionId || generateFrontendSessionId();
|
|
1247
1263
|
const defaultName = normalizedMode === 'tmux' && normalizedTmuxName
|
|
1248
1264
|
? `tmux:${normalizedTmuxName}`
|
|
1249
1265
|
: `terminal-${now.toString(36)}`;
|
|
@@ -1433,13 +1449,33 @@ function tryDeliverCollaborationInbox(frontendSessionId) {
|
|
|
1433
1449
|
}
|
|
1434
1450
|
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1435
1451
|
const route = collaborationDeliveryWorker.state(frontendSessionId);
|
|
1436
|
-
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId),
|
|
1452
|
+
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId), waitable: true,
|
|
1437
1453
|
reason: route.reason ?? '消息已入队,后台正在恢复路由并投递' };
|
|
1438
1454
|
}
|
|
1455
|
+
// Per-session routing-help education state: static help (peer roster + help
|
|
1456
|
+
// pointer) rides only the first delivery, then returns when the roster
|
|
1457
|
+
// changes (a new member joined). In-memory only — a restart simply re-educates
|
|
1458
|
+
// once, which is harmless. Dynamic notices are never gated by this.
|
|
1459
|
+
const collaborationScaffoldState = new Map();
|
|
1460
|
+
function collaborationRosterSignature(frontendSessionId) {
|
|
1461
|
+
const peerIds = new Set(collaborationStore.groupsForSession(frontendSessionId)
|
|
1462
|
+
.flatMap((group) => group.sessionIds ?? []).filter((id) => id !== frontendSessionId));
|
|
1463
|
+
return [...peerIds].sort().join('|');
|
|
1464
|
+
}
|
|
1439
1465
|
function formatLocalCollaborationMessages(frontendSessionId, messages) {
|
|
1466
|
+
const signature = collaborationRosterSignature(frontendSessionId);
|
|
1467
|
+
// Education follows the bound agent identity, not just the pane: a rebind to
|
|
1468
|
+
// a different agent session must re-educate the newcomer.
|
|
1469
|
+
const binding = collaborationRouting.get(frontendSessionId);
|
|
1470
|
+
const educationKey = `${frontendSessionId}|${binding?.agentSlug ?? ''}|${binding?.nativeSessionId ?? ''}`;
|
|
1471
|
+
const educated = collaborationScaffoldState.get(educationKey);
|
|
1472
|
+
const showRoutingHelp = !educated || educated.rosterSignature !== signature;
|
|
1473
|
+
if (showRoutingHelp)
|
|
1474
|
+
collaborationScaffoldState.set(educationKey, { rosterSignature: signature });
|
|
1440
1475
|
return formatCollaborationDelivery({ targetSessionId: frontendSessionId, messages,
|
|
1441
1476
|
groups: collaborationStore.groupsForSession(frontendSessionId),
|
|
1442
1477
|
sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(orchestrationSessionSnapshot)],
|
|
1478
|
+
showRoutingHelp,
|
|
1443
1479
|
});
|
|
1444
1480
|
}
|
|
1445
1481
|
async function inspectCollaborationTmux(binding, requestedPane) {
|
|
@@ -1511,6 +1547,12 @@ async function rebindCollaborationRoute(frontendSessionId, paneId) {
|
|
|
1511
1547
|
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1512
1548
|
return { ...collaborationRouting.get(frontendSessionId), state: 'recovering' };
|
|
1513
1549
|
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Delivery is deliberately un-gated: any reachable recipient receives the
|
|
1552
|
+
* message immediately, whatever its agent is doing. Busy guessing (hook state,
|
|
1553
|
+
* turn phase) is the sender's job — the send receipt carries a screen
|
|
1554
|
+
* snapshot of the recipient after the write, and the sender judges from that.
|
|
1555
|
+
*/
|
|
1514
1556
|
async function resolveCollaborationRoute(frontendSessionId) {
|
|
1515
1557
|
let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1516
1558
|
if (!record)
|
|
@@ -1554,12 +1596,16 @@ async function resolveCollaborationRoute(frontendSessionId) {
|
|
|
1554
1596
|
}
|
|
1555
1597
|
if (!backend)
|
|
1556
1598
|
return { state: 'detached', reason: 'TMUX_BACKEND_UNAVAILABLE' };
|
|
1557
|
-
return { state: 'ready', write: async (messages) => {
|
|
1599
|
+
return { state: 'ready', capture: async () => (await captureTmuxPane(pinned.paneId)).content, write: async (messages) => {
|
|
1558
1600
|
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1559
1601
|
throw new Error('SESSION_REMOVED');
|
|
1560
1602
|
await writeCollaborationTmuxPane(runTmux, pinned, formatLocalCollaborationMessages(frontendSessionId, messages));
|
|
1561
1603
|
backend.lastActivity = Date.now();
|
|
1562
|
-
|
|
1604
|
+
// First-delivery confirm: history proves the agent rendered our
|
|
1605
|
+
// message (boot sequences clear only the screen, never the history a
|
|
1606
|
+
// live TUI writes into). Terminal-state only — no agent hooks.
|
|
1607
|
+
}, confirm: async () => captureTmuxPaneHistory(runTmux, pinned), approve: async () => approveCollaborationDialog(runTmux, pinned),
|
|
1608
|
+
recoverStuck: async (baseline) => recoverStuckPaste(runTmux, pinned, baseline) };
|
|
1563
1609
|
}
|
|
1564
1610
|
if (!backend || !binding.backendSessionId)
|
|
1565
1611
|
return { state: 'offline', reason: 'SHELL_BACKEND_NOT_RUNNING' };
|
|
@@ -1597,7 +1643,7 @@ async function refreshCollaborationAgentIdentity(backendSessionId, session) {
|
|
|
1597
1643
|
persistActiveProgramBinding(backendSessionId, activeProgram?.command);
|
|
1598
1644
|
syncAgentIdentity(backendSessionId, session);
|
|
1599
1645
|
}
|
|
1600
|
-
function deliverCollaborationInboxWhenAgentReady(frontendSessionId
|
|
1646
|
+
function deliverCollaborationInboxWhenAgentReady(frontendSessionId) {
|
|
1601
1647
|
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1602
1648
|
}
|
|
1603
1649
|
async function spawnCollaborationAgentSession(req, group, sourceSessionId, input) {
|
|
@@ -1618,6 +1664,9 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1618
1664
|
fallbackMode: fallbackRecord?.mode,
|
|
1619
1665
|
});
|
|
1620
1666
|
const requestedName = typeof input.name === 'string' ? input.name.trim().slice(0, 120) : '';
|
|
1667
|
+
if (requestedName && COLLAB_NAME_FORBIDDEN.test(requestedName)) {
|
|
1668
|
+
throw new HttpStatusError(400, '会话名称不能包含「【】」、间隔号「·」、换行或控制字符', 'COLLAB_NAME_INVALID');
|
|
1669
|
+
}
|
|
1621
1670
|
const requestedCwd = typeof input.cwd === 'string' ? input.cwd.trim() : '';
|
|
1622
1671
|
const opened = await openInventorySession(req, {
|
|
1623
1672
|
name: requestedName || `${launcher.displayName} · ${group.name}`,
|
|
@@ -1634,7 +1683,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1634
1683
|
name: group.name,
|
|
1635
1684
|
sessionIds: [...group.sessionIds, frontendSessionId],
|
|
1636
1685
|
});
|
|
1637
|
-
writeTerminalInput(backend, `${launcher.command}\r`);
|
|
1686
|
+
writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
|
|
1638
1687
|
const requestedTask = typeof input.task === 'string' ? input.task.trim().slice(0, 20_000) : '';
|
|
1639
1688
|
collaborationStore.send({
|
|
1640
1689
|
groupId: updatedGroup.id,
|
|
@@ -1644,7 +1693,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
|
|
|
1644
1693
|
content: requestedTask || `你已创建并加入协作组“${updatedGroup.name}”。请运行 td collab status 查看成员,并准备参与协作。`,
|
|
1645
1694
|
});
|
|
1646
1695
|
setTimeout(() => {
|
|
1647
|
-
deliverCollaborationInboxWhenAgentReady(frontendSessionId
|
|
1696
|
+
deliverCollaborationInboxWhenAgentReady(frontendSessionId);
|
|
1648
1697
|
}, 300).unref?.();
|
|
1649
1698
|
return { group: updatedGroup, session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
|
|
1650
1699
|
}
|
|
@@ -4858,7 +4907,7 @@ async function adoptPtyHostSessions() {
|
|
|
4858
4907
|
if (!existing) {
|
|
4859
4908
|
const now = Date.now();
|
|
4860
4909
|
upsertGlobalSessionRecord({
|
|
4861
|
-
sessionId:
|
|
4910
|
+
sessionId: generateFrontendSessionId(),
|
|
4862
4911
|
name: `terminal-${now.toString(36)}`,
|
|
4863
4912
|
backendSessionId: meta.id,
|
|
4864
4913
|
mode: 'shell',
|
|
@@ -5541,8 +5590,217 @@ router.post('/operations/orchestration/spawn', async (req, res) => {
|
|
|
5541
5590
|
res.status(400).json({ error: getErrorMessage(error) });
|
|
5542
5591
|
}
|
|
5543
5592
|
});
|
|
5593
|
+
/**
|
|
5594
|
+
* Session cleanup for the collaboration CLI. Two semantic guards make the
|
|
5595
|
+
* destructive surface safe from inside a group:
|
|
5596
|
+
* - a target must share a collaboration group with the caller (personal
|
|
5597
|
+
* sessions stay reachable only from the web UI), and the caller can never
|
|
5598
|
+
* clean its own session;
|
|
5599
|
+
* - cleanup refuses when removing a target would dissolve a group the caller
|
|
5600
|
+
* still belongs to (groups under 2 members are dropped with their whole
|
|
5601
|
+
* message history — dissolving your own group needs a deliberate group op,
|
|
5602
|
+
* not a member cleanup).
|
|
5603
|
+
* Every execution is a two-step human-granted act: without `confirmed: true`
|
|
5604
|
+
* the route only reports the plan (records, groups, tmux sessions that would
|
|
5605
|
+
* be destroyed) and refuses; an agent must relay that plan to the user and
|
|
5606
|
+
* re-run with explicit confirmation before anything is deleted.
|
|
5607
|
+
*/
|
|
5608
|
+
router.post('/operations/orchestration/cleanup', async (req, res) => {
|
|
5609
|
+
const sourceSessionId = resolveFrontendSessionId(req.body ?? {});
|
|
5610
|
+
if (!sourceSessionId)
|
|
5611
|
+
return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
|
|
5612
|
+
const requested = Array.isArray(req.body?.sessionIds)
|
|
5613
|
+
? [...new Set(req.body.sessionIds.filter((id) => typeof id === 'string' && id.trim().length > 0))]
|
|
5614
|
+
: [];
|
|
5615
|
+
if (requested.length === 0)
|
|
5616
|
+
return res.status(400).json({ error: '至少需要一个待清理的会话 id' });
|
|
5617
|
+
if (requested.includes(sourceSessionId))
|
|
5618
|
+
return res.status(400).json({ error: '不能清理当前会话自身;如需退出协作请使用组操作或网页端' });
|
|
5619
|
+
const recordsById = new Map(globalSessionState.sessions.map((session) => [session.sessionId, session]));
|
|
5620
|
+
const missing = requested.filter((id) => !recordsById.has(id));
|
|
5621
|
+
if (missing.length > 0)
|
|
5622
|
+
return res.status(404).json({ error: `以下会话不存在(可能已清理或已离线):${missing.join('、')}` });
|
|
5623
|
+
const label = (sessionId) => recordsById.get(sessionId)?.name.trim() || sessionId;
|
|
5624
|
+
// Targets must share a collaboration group with the caller: CLI cleanup only
|
|
5625
|
+
// covers orchestration peers, never unrelated personal terminals.
|
|
5626
|
+
const callerGroupIds = new Set(collaborationStore.groupsForSession(sourceSessionId).map((group) => group.id));
|
|
5627
|
+
const outsiders = requested.filter((id) => !collaborationStore.groupsForSession(id).some((group) => callerGroupIds.has(group.id)));
|
|
5628
|
+
if (outsiders.length > 0) {
|
|
5629
|
+
return res.status(403).json({ error: `目标会话不在你的协作组中,CLI 无法清理:${outsiders.map(label).join('、')}(个人会话请在网页端删除)` });
|
|
5630
|
+
}
|
|
5631
|
+
const requestedSet = new Set(requested);
|
|
5632
|
+
const groupEffects = [];
|
|
5633
|
+
for (const id of requested) {
|
|
5634
|
+
for (const group of collaborationStore.groupsForSession(id)) {
|
|
5635
|
+
if (groupEffects.some((effect) => effect.id === group.id))
|
|
5636
|
+
continue;
|
|
5637
|
+
const remaining = group.sessionIds.filter((member) => !requestedSet.has(member)).length;
|
|
5638
|
+
groupEffects.push({
|
|
5639
|
+
id: group.id, name: group.name ?? null, sizeBefore: group.sessionIds.length,
|
|
5640
|
+
sizeAfter: remaining < 2 ? 0 : remaining, dissolves: remaining < 2,
|
|
5641
|
+
includesCaller: group.sessionIds.includes(sourceSessionId),
|
|
5642
|
+
});
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5645
|
+
const dissolvingOwn = groupEffects.find((effect) => effect.dissolves && effect.includesCaller);
|
|
5646
|
+
if (dissolvingOwn) {
|
|
5647
|
+
return res.status(403).json({ error: `清理会使你所在的协作组「${dissolvingOwn.name ?? dissolvingOwn.id}」解散(剩余成员不足 2,组内消息会被一并清除);如需解散协作组请在网页端操作,组保留时再清理成员会话` });
|
|
5648
|
+
}
|
|
5649
|
+
const targets = requested.map((sessionId) => {
|
|
5650
|
+
const record = recordsById.get(sessionId);
|
|
5651
|
+
return { sessionId: record.sessionId, name: record.name.trim() || null, mode: record.mode,
|
|
5652
|
+
tmuxSessionName: record.mode === 'tmux' ? (record.tmuxSessionName ?? null) : null,
|
|
5653
|
+
backendSessionId: record.backendSessionId ?? null };
|
|
5654
|
+
});
|
|
5655
|
+
const plan = { targets, groups: groupEffects };
|
|
5656
|
+
if (req.body?.confirmed !== true) {
|
|
5657
|
+
return res.json({ ok: false, code: 'CLEANUP_CONFIRM_REQUIRED',
|
|
5658
|
+
error: '不可恢复的风险操作:执行前必须先获得用户(人类)的明确同意',
|
|
5659
|
+
instruction: `将清理计划告知用户并取得明确同意后重跑:td collab cleanup ${requested.join(' ')} --confirm`,
|
|
5660
|
+
plan });
|
|
5661
|
+
}
|
|
5662
|
+
const removed = [];
|
|
5663
|
+
for (const sessionId of requested) {
|
|
5664
|
+
const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === sessionId);
|
|
5665
|
+
if (!record) {
|
|
5666
|
+
removed.push({ sessionId, name: null });
|
|
5667
|
+
continue;
|
|
5668
|
+
}
|
|
5669
|
+
archiveAgentResumeRecord(record, 'closed');
|
|
5670
|
+
removeGlobalSessionRecord(sessionId);
|
|
5671
|
+
if (record.mode === 'tmux' && record.tmuxSessionName) {
|
|
5672
|
+
try {
|
|
5673
|
+
const outcome = await destroyTmuxSessionSafely(record.tmuxSessionName);
|
|
5674
|
+
removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: record.tmuxSessionName, alreadyGone: outcome.alreadyGone === true });
|
|
5675
|
+
}
|
|
5676
|
+
catch (error) {
|
|
5677
|
+
console.error(`[cleanup] tmux teardown failed for ${record.tmuxSessionName}:`, getErrorMessage(error));
|
|
5678
|
+
removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: record.tmuxSessionName, failure: getErrorMessage(error) });
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
else {
|
|
5682
|
+
let killed = false;
|
|
5683
|
+
if (record.backendSessionId && terminalSessions.has(record.backendSessionId)) {
|
|
5684
|
+
try {
|
|
5685
|
+
cleanupSession(record.backendSessionId, { killProcess: true });
|
|
5686
|
+
killed = true;
|
|
5687
|
+
}
|
|
5688
|
+
catch (error) {
|
|
5689
|
+
console.error(`[cleanup] pty teardown failed for ${sessionId}:`, getErrorMessage(error));
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
removed.push({ sessionId, name: record.name.trim() || null, mode: record.mode, tmuxSessionName: null, killed });
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
await persistGlobalStateNow();
|
|
5696
|
+
broadcastClientState();
|
|
5697
|
+
res.json({ ok: true, removed, plan });
|
|
5698
|
+
});
|
|
5699
|
+
// Drive a collaboration member's terminal: inject one named key, or dismiss
|
|
5700
|
+
// an interactive approval dialog. Wrapping key injection in our own CLI keeps
|
|
5701
|
+
// every manipulation of another session explicit and auditable — no ad-hoc
|
|
5702
|
+
// tmux calls. Scope mirrors cleanup: only members sharing a group with the
|
|
5703
|
+
// caller, tmux-local only, and `approve` refuses unless an approval dialog is
|
|
5704
|
+
// actually showing (Enter on the highlighted option). The dialogs may be
|
|
5705
|
+
// approving any command the member agent asked for; the caller is presumed to
|
|
5706
|
+
// own that decision (the delivery confirm gate uses the same primitive).
|
|
5707
|
+
const DRIVE_KEYS = new Set(['enter', 'escape', 'space', 'left', 'right', 'up', 'down']);
|
|
5708
|
+
router.post('/operations/orchestration/drive', async (req, res) => {
|
|
5709
|
+
const sourceSessionId = resolveFrontendSessionId(req.body ?? {});
|
|
5710
|
+
if (!sourceSessionId)
|
|
5711
|
+
return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
|
|
5712
|
+
const target = typeof req.body?.session === 'string' ? req.body.session.trim() : '';
|
|
5713
|
+
const action = typeof req.body?.action === 'string' ? req.body.action.trim() : '';
|
|
5714
|
+
if (!target || !action)
|
|
5715
|
+
return res.status(400).json({ error: 'drive 需要目标会话 id 与动作(approve|enter|escape|space|left|right|up|down|capture|run)' });
|
|
5716
|
+
if (target === sourceSessionId)
|
|
5717
|
+
return res.status(400).json({ error: '不能驱动当前会话自身' });
|
|
5718
|
+
const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === target);
|
|
5719
|
+
if (!record)
|
|
5720
|
+
return res.status(404).json({ error: '目标会话不存在(可能已清理或已离线)' });
|
|
5721
|
+
const callerGroups = collaborationStore.groupsForSession(sourceSessionId).map((group) => group.id);
|
|
5722
|
+
if (!collaborationStore.groupsForSession(target).some((group) => callerGroups.includes(group.id))) {
|
|
5723
|
+
return res.status(403).json({ error: '目标会话不在你的协作组中,CLI 无法驱动;如需操作个人会话请在网页端进行' });
|
|
5724
|
+
}
|
|
5725
|
+
if (record.mode !== 'tmux' || !record.tmuxSessionName) {
|
|
5726
|
+
return res.status(409).json({ error: '目标会话不是 tmux 会话,无法从 CLI 驱动(模式:' + (record.mode ?? 'unknown') + ')' });
|
|
5727
|
+
}
|
|
5728
|
+
const binding = collaborationRouting.get(target);
|
|
5729
|
+
const pane = binding?.pane ?? null;
|
|
5730
|
+
if (!pane)
|
|
5731
|
+
return res.status(409).json({ error: '目标会话还没有可驱动的已固定终端面板(agent 可能尚未启动或已离线)' });
|
|
5732
|
+
try {
|
|
5733
|
+
if (action === 'approve') {
|
|
5734
|
+
const approved = await approveCollaborationDialog(runTmux, pane);
|
|
5735
|
+
if (!approved)
|
|
5736
|
+
return res.status(409).json({ ok: false, code: 'NO_APPROVAL_DIALOG', error: '目标面板当前没有显示审批对话框;未发送任何按键' });
|
|
5737
|
+
return res.json({ ok: true, action, sessionId: target, approved: true });
|
|
5738
|
+
}
|
|
5739
|
+
if (action === 'capture') {
|
|
5740
|
+
const snapshot = await captureTmuxPaneText(runTmux, pane);
|
|
5741
|
+
return res.json({ ok: true, action, sessionId: target, snapshot });
|
|
5742
|
+
}
|
|
5743
|
+
if (action === 'run') {
|
|
5744
|
+
const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';
|
|
5745
|
+
if (!text)
|
|
5746
|
+
return res.status(400).json({ error: 'run 需要一行文本(放在请求的 text 字段)' });
|
|
5747
|
+
if (text.length > 10_000)
|
|
5748
|
+
return res.status(400).json({ error: 'run 文本过长(上限 10000 字符);需要更多内容请走 td collab send 投递' });
|
|
5749
|
+
// Same bracketed-paste write path deliveries use: one line, one submit,
|
|
5750
|
+
// embedded newlines stay inside the recipient's editor.
|
|
5751
|
+
await writeCollaborationTmuxPane(runTmux, pane, text);
|
|
5752
|
+
const snapshot = await captureTmuxPaneText(runTmux, pane);
|
|
5753
|
+
return res.json({ ok: true, action, sessionId: target, text, snapshot });
|
|
5754
|
+
}
|
|
5755
|
+
if (!DRIVE_KEYS.has(action)) {
|
|
5756
|
+
return res.status(400).json({ error: `未知动作 ${action};可用:approve|enter|escape|space|left|right|up|down|capture|run` });
|
|
5757
|
+
}
|
|
5758
|
+
await sendTmuxPaneKey(runTmux, pane, action);
|
|
5759
|
+
res.json({ ok: true, action, sessionId: target });
|
|
5760
|
+
}
|
|
5761
|
+
catch (error) {
|
|
5762
|
+
res.status(409).json({ ok: false, code: 'DRIVE_FAILED', error: getErrorMessage(error) });
|
|
5763
|
+
}
|
|
5764
|
+
});
|
|
5544
5765
|
router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
|
|
5545
|
-
deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute
|
|
5766
|
+
deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute,
|
|
5767
|
+
resolveNames: (ids) => {
|
|
5768
|
+
const names = new Map([...collaborationRemoteSessions(),
|
|
5769
|
+
...globalSessionState.sessions.map(orchestrationSessionSnapshot)].map((session) => [session.sessionId, session.name]));
|
|
5770
|
+
return Object.fromEntries(ids.map((id) => [id, names.get(id) ?? null]));
|
|
5771
|
+
},
|
|
5772
|
+
// Same semantics as the inventory PATCH rename: a display name that wins
|
|
5773
|
+
// over auto-titles (customName=true clears autoTitle) and is pushed into
|
|
5774
|
+
// the tmux @termdock-friendly-name option. Names arrive pre-cleaned from
|
|
5775
|
+
// the collaboration route.
|
|
5776
|
+
renameSession: async (frontendSessionId, name) => {
|
|
5777
|
+
const index = globalSessionState.sessions.findIndex((session) => session.sessionId === frontendSessionId);
|
|
5778
|
+
if (index < 0)
|
|
5779
|
+
return { ok: false, code: 'SESSION_NOT_FOUND', error: 'session not found' };
|
|
5780
|
+
const next = { ...globalSessionState.sessions[index], lastActivity: Date.now(),
|
|
5781
|
+
name, customName: true, autoTitle: null };
|
|
5782
|
+
if (next.mode === 'tmux' && next.tmuxSessionName) {
|
|
5783
|
+
try {
|
|
5784
|
+
await setTmuxOption(next.tmuxSessionName, '@termdock-friendly-name', name);
|
|
5785
|
+
}
|
|
5786
|
+
catch (error) {
|
|
5787
|
+
console.warn(`[collab] failed to set friendly name on ${next.tmuxSessionName}: ${getErrorMessage(error)}`);
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
upsertGlobalSessionRecord(next);
|
|
5791
|
+
await persistGlobalStateNow();
|
|
5792
|
+
// The broadcast inventory must not be a stale (≤1.5s TTL) cache build:
|
|
5793
|
+
// browsers apply the inventory over the session list, so an old cached
|
|
5794
|
+
// name would keep the sidebar showing the previous title.
|
|
5795
|
+
try {
|
|
5796
|
+
await getSessionInventorySnapshot({ refresh: true });
|
|
5797
|
+
}
|
|
5798
|
+
catch (error) {
|
|
5799
|
+
console.warn('[collab] failed to refresh inventory after rename:', getErrorMessage(error));
|
|
5800
|
+
}
|
|
5801
|
+
broadcastClientState();
|
|
5802
|
+
return { ok: true };
|
|
5803
|
+
} }));
|
|
5546
5804
|
router.get('/operations/session-search', (req, res) => {
|
|
5547
5805
|
const query = typeof req.query.q === 'string' ? req.query.q : '';
|
|
5548
5806
|
const results = sessionSearchStore.search(query, Number(req.query.limit) || 30).map((result) => {
|
package/package.json
CHANGED
package/runtime-manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.192",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-jIRqCSStnF2/E81Q9GtAEeXAsshmnR4UUDtExjgLex0=",
|
|
10
|
+
"clientBundleHash": "sha256-wpGsT/oJup/G4MyJSKqpHBJlzc2Ha3wFIWmtp4vc/Wc=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|