termdock 1.4.284 → 1.4.286
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.desktop-dist/main.js +10 -553
- package/README.md +2 -4
- package/dist/client/assets/App-qs0jye8B.js +158 -0
- package/dist/client/assets/App-qs0jye8B.js.br +0 -0
- package/dist/client/assets/App-qs0jye8B.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-Bvg7jZNw.js → ChangeWalkthroughPanel-X-M6nxKJ.js} +1 -1
- package/dist/client/assets/ChangeWalkthroughPanel-X-M6nxKJ.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-X-M6nxKJ.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-DSM5oYq3.js → DagPlayground-DgiUaUkH.js} +1 -1
- package/dist/client/assets/DagPlayground-DgiUaUkH.js.br +0 -0
- package/dist/client/assets/DagPlayground-DgiUaUkH.js.gz +0 -0
- package/dist/client/assets/{DiffLab-CgI11xY5.js → DiffLab-u8njwj0d.js} +1 -1
- package/dist/client/assets/DiffLab-u8njwj0d.js.br +0 -0
- package/dist/client/assets/DiffLab-u8njwj0d.js.gz +0 -0
- package/dist/client/assets/{DiffReview-DOmoMEgC.js → DiffReview-DoQY-klj.js} +1 -1
- package/dist/client/assets/DiffReview-DoQY-klj.js.br +0 -0
- package/dist/client/assets/DiffReview-DoQY-klj.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-CEdM0rim.js → DiffReviewLab-DlKbMbfq.js} +1 -1
- package/dist/client/assets/DiffReviewLab-DlKbMbfq.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-DlKbMbfq.js.gz +0 -0
- package/dist/client/assets/DiffViewer-nlvDqGPJ.js +37 -0
- package/dist/client/assets/DiffViewer-nlvDqGPJ.js.br +0 -0
- package/dist/client/assets/DiffViewer-nlvDqGPJ.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-DNiQACGL.js → ModelPreview-C3beTH3N.js} +1 -1
- package/dist/client/assets/ModelPreview-C3beTH3N.js.br +0 -0
- package/dist/client/assets/ModelPreview-C3beTH3N.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BtktcUdh.js +1678 -0
- package/dist/client/assets/RightSidebar-BtktcUdh.js.br +0 -0
- package/dist/client/assets/RightSidebar-BtktcUdh.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-ByazGh0D.js → abnfDiagram-VCTEODGH-7J_hDjG2.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-7J_hDjG2.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-7J_hDjG2.js.gz +0 -0
- package/dist/client/assets/app-CHKqaWIE.js +35 -0
- package/dist/client/assets/app-CHKqaWIE.js.br +0 -0
- package/dist/client/assets/app-CHKqaWIE.js.gz +0 -0
- package/dist/client/assets/{arc-DMO5fEDY.js → arc-Bg_CuCZm.js} +1 -1
- package/dist/client/assets/arc-Bg_CuCZm.js.br +0 -0
- package/dist/client/assets/arc-Bg_CuCZm.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-c6WktFDt.js → architectureDiagram-5GKGNRK7-CsSHwySz.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CsSHwySz.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CsSHwySz.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CN0lQ7zu.js → blockDiagram-I7D4REHJ-DePM_hkV.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-DePM_hkV.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-DePM_hkV.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-CALO2K48.js → c4Diagram-7LVT6UL2-BQ8B9h53.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-BQ8B9h53.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BQ8B9h53.js.gz +0 -0
- package/dist/client/assets/channel-8p7Zocq1.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-CcQPyBbp.js → chunk-2Q5K7J3B-Bo9Nc-d4.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CEp2aHuN.js → chunk-5VM5RSS4-D_qQp7an.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-gRP8AiSl.js → chunk-F27PBJKO-CJvGLWuF.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-CJvGLWuF.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CJvGLWuF.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-BZewY7aF.js → chunk-IMKFNOWR-e4fa23mF.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-e4fa23mF.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-e4fa23mF.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-C4S_lg0B.js → chunk-JWPE2WC7-D9qVRvQs.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-BgYxXL0V.js → chunk-POPQ4Y6H-7eOizLll.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-rjbdtq2C.js → chunk-SVP7TREG-Co1wDBYI.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-Co1wDBYI.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-Co1wDBYI.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-CxU1tNuL.js → chunk-TICWLB2K-yKjTtLNf.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-yKjTtLNf.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-yKjTtLNf.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-CSSO11_4.js → chunk-XXDRQBXY-DDFq5Eoi.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-DM8VyUpP.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DM8VyUpP.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-BiO-E-0q.js → cose-bilkent-JH36ORCC-Ng2b-szu.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Ng2b-szu.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Ng2b-szu.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-DUC3zzns.js → cynefin-OW5HDTMX-BUzoxsoE.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-BUzoxsoE.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BUzoxsoE.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CR1RnhbU.js → cynefinDiagram-5FMLGOSQ-9sKimYPk.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-9sKimYPk.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-9sKimYPk.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-d1joyB5v.js → dagre-GXQ25YYZ-C6GZ4pYU.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-C6GZ4pYU.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-C6GZ4pYU.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-izL3GdCu.js → diagram-S7CK7UJ4-DHewWm_2.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-DHewWm_2.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DHewWm_2.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-CjoFV2sD.js → diagram-UQ7AKVKN-BU-Qm_iS.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-BU-Qm_iS.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BU-Qm_iS.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-BgHmsnTZ.js → diagram-VSXAHHWV-DEvxj92u.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-DEvxj92u.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-DEvxj92u.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-DMzRLNbg.js → diagram-VX7I27RA-DwwnVInb.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-DwwnVInb.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DwwnVInb.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-kgWacykR.js → diagram-Z3DM3KII-DDhqqeca.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-DDhqqeca.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-DDhqqeca.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-XzSXtFGS.js → ebnfDiagram-PWID7BFC-CQz2BRgT.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CQz2BRgT.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-CQz2BRgT.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Bu67_rUZ.js → erDiagram-RLTQ6QDP-D2eLjKEA.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-D2eLjKEA.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-D2eLjKEA.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-BsK_b_G_.js → flowDiagram-HODETNUW-B7oJq5P3.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-B7oJq5P3.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-B7oJq5P3.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-gCSBItav.js → ganttDiagram-EL5Y4UJY-BUA_LsMw.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BUA_LsMw.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BUA_LsMw.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-BOikQWZ8.js → gestureArbiter-8VtxF9dg.js} +1 -1
- package/dist/client/assets/gestureArbiter-8VtxF9dg.js.br +0 -0
- package/dist/client/assets/gestureArbiter-8VtxF9dg.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DL0NSPPn.js → gitGraphDiagram-WWUBYQGX-BlbsPdki.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BlbsPdki.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BlbsPdki.js.gz +0 -0
- package/dist/client/assets/{index-Bv-r5mB7.css → index-DUCUcxTy.css} +1 -1
- package/dist/client/assets/index-DUCUcxTy.css.br +0 -0
- package/dist/client/assets/index-DUCUcxTy.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-Cwevdubr.js → infoDiagram-27XIBGKW-DdYvRIDS.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-pz1lzDd4.js → ishikawaDiagram-5VMMS53U-CZbBbDjz.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CZbBbDjz.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CZbBbDjz.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-BrUnjTGd.js → journeyDiagram-3NMN7TZE-DtxlqtaG.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DtxlqtaG.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DtxlqtaG.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-BKf4UYyB.js → kanban-definition-UXKFOSKX-UMDV9UCG.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-UMDV9UCG.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-UMDV9UCG.js.gz +0 -0
- package/dist/client/assets/{layout-Dd_yzBVh.js → layout-CAAXci79.js} +1 -1
- package/dist/client/assets/layout-CAAXci79.js.br +0 -0
- package/dist/client/assets/layout-CAAXci79.js.gz +0 -0
- package/dist/client/assets/{linear-iY7daKXS.js → linear-BGBP_FE_.js} +1 -1
- package/dist/client/assets/linear-BGBP_FE_.js.br +0 -0
- package/dist/client/assets/linear-BGBP_FE_.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-CYNb258G.js → mermaid.core-BH_WDKpZ.js} +5 -5
- package/dist/client/assets/mermaid.core-BH_WDKpZ.js.br +0 -0
- package/dist/client/assets/mermaid.core-BH_WDKpZ.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-ctnc7JBK.js → mindmap-definition-YA3MSWOX-7yyx6kg5.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-7yyx6kg5.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-7yyx6kg5.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-Bth6QB9C.js → passwordBootstrap-Dt3dN800.js} +1 -1
- package/dist/client/assets/passwordBootstrap-Dt3dN800.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Dt3dN800.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DVKAZXxP.js → pegDiagram-XKGWAZYB-D254I9Yl.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-D254I9Yl.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-D254I9Yl.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-C0u6lfoS.js → pieDiagram-E7YTZNPT-DTy-4KXi.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DTy-4KXi.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DTy-4KXi.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-DXFDUoYJ.js → quadrantDiagram-AXDQQJYC-BMsaHxl0.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-DXFDUoYJ.js.br → quadrantDiagram-AXDQQJYC-BMsaHxl0.js.br} +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-BMsaHxl0.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-Cz-wrQ4E.js → railroadDiagram-O6MQD6OU-D4c39AAQ.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-D4c39AAQ.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-D4c39AAQ.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-BMcpaynk.js → referenceLongPress-TTIbwCKV.js} +1 -1
- package/dist/client/assets/referenceLongPress-TTIbwCKV.js.br +0 -0
- package/dist/client/assets/referenceLongPress-TTIbwCKV.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-DdFhwbqd.js → requirementDiagram-BXWQKSXE-CwJjBKnR.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CwJjBKnR.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CwJjBKnR.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DIEO7iin.js → sankeyDiagram-P5KCCOFB-DVlRBDMK.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DVlRBDMK.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DVlRBDMK.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-0Jq8ZpjE.js → sequenceDiagram-WJ2MYXX4-BsWqKeoD.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BsWqKeoD.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BsWqKeoD.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-CzkB3_L-.js → sizeCapture-INFHLROL-1FEOsRFT.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-1FEOsRFT.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-1FEOsRFT.js.gz +0 -0
- package/dist/client/assets/{sparkles-OssC7CZI.js → sparkles-hN4VSTDx.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-B1UZvmmJ.js → stateDiagram-D77RDMKH-DWNE48Sc.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DWNE48Sc.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DWNE48Sc.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BMprFuVB.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-CwBFrD7Q.js → swimlanes-42K2YHIH-ghhxa8QM.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-ghhxa8QM.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-ghhxa8QM.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-Dg4tYXGL.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-Cgrwm9y7.js → timeline-definition-24CTP7MA-B0eZfCze.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-B0eZfCze.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-B0eZfCze.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-gfowObfL.js → triangle-alert-zb36qQ-j.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-JVQ9D_hy.js → vennDiagram-4TSXK5OY-DCdV0A4l.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-DCdV0A4l.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DCdV0A4l.js.gz +0 -0
- package/dist/client/assets/{videoCompression-D1SrM0Eo.js → videoCompression-BzT-9eWg.js} +1 -1
- package/dist/client/assets/videoCompression-BzT-9eWg.js.br +0 -0
- package/dist/client/assets/videoCompression-BzT-9eWg.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-BsmpGt9d.js → wardleyDiagram-VM6X3IG4-BQvk-8b6.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BQvk-8b6.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BQvk-8b6.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-C33-nORD.js → xychartDiagram-S5SC5T6Z-D1VdK6g5.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-D1VdK6g5.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-D1VdK6g5.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 -3
- package/dist/server/agent/collaborationGroupRoutes.js +18 -0
- package/dist/server/agent/collaborationPeerTransport.js +19 -1
- package/dist/server/agent/collaborationRoutes.js +12 -0
- package/dist/server/agent/collaborationService.js +83 -8
- package/dist/server/agent/nativeSessionSearch.js +84 -0
- package/dist/server/agent/sessionSearchStore.js +2 -1
- package/dist/server/entry.js +2 -6
- package/dist/server/federation/trustedCa.js +9 -2
- package/dist/server/routes/filesystem.js +43 -1
- package/dist/server/routes/terminal.js +122 -32
- package/dist/server/utils/enableSupervision.js +2 -3
- package/dist/server/utils/runtimeClient.js +0 -32
- package/dist/server/utils/settings.js +14 -0
- package/dist/server/workers/nativeSessionSearchWorker.js +416 -0
- package/package.json +3 -3
- package/runtime-manifest.json +3 -3
- package/scripts/classify-release.mjs +0 -1
- package/dist/client/assets/App-BgEYOR8Z.js +0 -157
- package/dist/client/assets/App-BgEYOR8Z.js.br +0 -0
- package/dist/client/assets/App-BgEYOR8Z.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-Bvg7jZNw.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-Bvg7jZNw.js.gz +0 -0
- package/dist/client/assets/DagPlayground-DSM5oYq3.js.br +0 -0
- package/dist/client/assets/DagPlayground-DSM5oYq3.js.gz +0 -0
- package/dist/client/assets/DiffLab-CgI11xY5.js.br +0 -0
- package/dist/client/assets/DiffLab-CgI11xY5.js.gz +0 -0
- package/dist/client/assets/DiffReview-DOmoMEgC.js.br +0 -0
- package/dist/client/assets/DiffReview-DOmoMEgC.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CEdM0rim.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CEdM0rim.js.gz +0 -0
- package/dist/client/assets/DiffViewer-CI3JmNyq.js +0 -37
- package/dist/client/assets/DiffViewer-CI3JmNyq.js.br +0 -0
- package/dist/client/assets/DiffViewer-CI3JmNyq.js.gz +0 -0
- package/dist/client/assets/ModelPreview-DNiQACGL.js.br +0 -0
- package/dist/client/assets/ModelPreview-DNiQACGL.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CpPgHZEU.js +0 -1678
- package/dist/client/assets/RightSidebar-CpPgHZEU.js.br +0 -0
- package/dist/client/assets/RightSidebar-CpPgHZEU.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-ByazGh0D.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-ByazGh0D.js.gz +0 -0
- package/dist/client/assets/app-UpvbJ3fT.js +0 -35
- package/dist/client/assets/app-UpvbJ3fT.js.br +0 -0
- package/dist/client/assets/app-UpvbJ3fT.js.gz +0 -0
- package/dist/client/assets/arc-DMO5fEDY.js.br +0 -0
- package/dist/client/assets/arc-DMO5fEDY.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-c6WktFDt.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-c6WktFDt.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CN0lQ7zu.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CN0lQ7zu.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CALO2K48.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CALO2K48.js.gz +0 -0
- package/dist/client/assets/channel-DcmoMLL-.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-gRP8AiSl.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-gRP8AiSl.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BZewY7aF.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BZewY7aF.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-rjbdtq2C.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-rjbdtq2C.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-CxU1tNuL.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-CxU1tNuL.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-VSirHVcP.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-VSirHVcP.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-BiO-E-0q.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-BiO-E-0q.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DUC3zzns.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DUC3zzns.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CR1RnhbU.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CR1RnhbU.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d1joyB5v.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d1joyB5v.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-izL3GdCu.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-izL3GdCu.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CjoFV2sD.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CjoFV2sD.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BgHmsnTZ.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-BgHmsnTZ.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DMzRLNbg.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DMzRLNbg.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-kgWacykR.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-kgWacykR.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-XzSXtFGS.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-XzSXtFGS.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Bu67_rUZ.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Bu67_rUZ.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BsK_b_G_.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BsK_b_G_.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-gCSBItav.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-gCSBItav.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-BOikQWZ8.js.br +0 -0
- package/dist/client/assets/gestureArbiter-BOikQWZ8.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DL0NSPPn.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DL0NSPPn.js.gz +0 -0
- package/dist/client/assets/index-Bv-r5mB7.css.br +0 -0
- package/dist/client/assets/index-Bv-r5mB7.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-pz1lzDd4.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-pz1lzDd4.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BrUnjTGd.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BrUnjTGd.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BKf4UYyB.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-BKf4UYyB.js.gz +0 -0
- package/dist/client/assets/layout-Dd_yzBVh.js.br +0 -0
- package/dist/client/assets/layout-Dd_yzBVh.js.gz +0 -0
- package/dist/client/assets/linear-iY7daKXS.js.br +0 -0
- package/dist/client/assets/linear-iY7daKXS.js.gz +0 -0
- package/dist/client/assets/mermaid.core-CYNb258G.js.br +0 -0
- package/dist/client/assets/mermaid.core-CYNb258G.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-ctnc7JBK.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-ctnc7JBK.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-Bth6QB9C.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Bth6QB9C.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DVKAZXxP.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DVKAZXxP.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-C0u6lfoS.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-C0u6lfoS.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-DXFDUoYJ.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Cz-wrQ4E.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Cz-wrQ4E.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-BMcpaynk.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BMcpaynk.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DdFhwbqd.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DdFhwbqd.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DIEO7iin.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-DIEO7iin.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-0Jq8ZpjE.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-0Jq8ZpjE.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CzkB3_L-.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CzkB3_L-.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-B1UZvmmJ.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-B1UZvmmJ.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-Cmg877fp.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-CwBFrD7Q.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-CwBFrD7Q.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CjR47hdK.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-Cgrwm9y7.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-Cgrwm9y7.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-JVQ9D_hy.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-JVQ9D_hy.js.gz +0 -0
- package/dist/client/assets/videoCompression-D1SrM0Eo.js.br +0 -0
- package/dist/client/assets/videoCompression-D1SrM0Eo.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BsmpGt9d.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BsmpGt9d.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C33-nORD.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C33-nORD.js.gz +0 -0
- package/dist/server/utils/desktopRuntimeOwner.js +0 -105
- package/scripts/prepare-desktop-runtime.mjs +0 -228
|
@@ -33,6 +33,15 @@ export class CollaborationService {
|
|
|
33
33
|
throw new Error('INVALID_DIRECTORY');
|
|
34
34
|
for (const peer of data.peers)
|
|
35
35
|
validateCollaborationNode(peer);
|
|
36
|
+
if (data.removedPeers !== undefined) {
|
|
37
|
+
if (!Array.isArray(data.removedPeers) || data.removedPeers.length > 768)
|
|
38
|
+
throw new Error('INVALID_DIRECTORY');
|
|
39
|
+
for (const peer of data.removedPeers) {
|
|
40
|
+
validateCollaborationNode(peer);
|
|
41
|
+
if (peer.scope && !/^12D3KooW[a-zA-Z0-9]{30,60}$/.test(peer.scope.device))
|
|
42
|
+
throw new Error('INVALID_DIRECTORY');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
36
45
|
if (data.scoped !== undefined) {
|
|
37
46
|
if (!data.scoped || typeof data.scoped !== 'object' || Array.isArray(data.scoped))
|
|
38
47
|
throw new Error('INVALID_DIRECTORY');
|
|
@@ -61,10 +70,12 @@ export class CollaborationService {
|
|
|
61
70
|
throw new Error('COLLABORATION_ORIGIN_REQUIRED'); const node = { ...this.options.node(), origin }; validateCollaborationNode(node); return node; }
|
|
62
71
|
setOrigin(origin) { this.node(origin); if (this.document.origin && this.document.origin !== origin)
|
|
63
72
|
throw new Error('COLLABORATION_ORIGIN_MISMATCH'); this.document.origin = origin; }
|
|
64
|
-
remember(node) {
|
|
73
|
+
remember(node, explicitPair = false) {
|
|
65
74
|
validateCollaborationNode(node);
|
|
66
75
|
if (node.serviceId === this.options.node().serviceId || node.origin === this.document.origin)
|
|
67
76
|
throw new Error('CANNOT_PAIR_SELF');
|
|
77
|
+
if (this.document.removedPeers?.some(peer => peer.serviceId === node.serviceId) && !explicitPair)
|
|
78
|
+
throw new Error('COLLABORATION_PEER_REMOVED');
|
|
68
79
|
const old = this.document.peers.find(peer => peer.serviceId === node.serviceId || peer.origin === node.origin);
|
|
69
80
|
if (old && (old.serviceId !== node.serviceId || old.origin !== node.origin || old.caFingerprint256 !== node.caFingerprint256))
|
|
70
81
|
throw new Error('PEER_IDENTITY_CHANGED');
|
|
@@ -73,6 +84,8 @@ export class CollaborationService {
|
|
|
73
84
|
throw new Error('PEER_LIMIT');
|
|
74
85
|
this.document.peers.push(node);
|
|
75
86
|
}
|
|
87
|
+
if (explicitPair)
|
|
88
|
+
this.document.removedPeers = this.document.removedPeers?.filter(peer => peer.serviceId !== node.serviceId);
|
|
76
89
|
this.persist();
|
|
77
90
|
}
|
|
78
91
|
descriptor() { return { ...this.options.node(), ...(this.document.origin ? { origin: this.document.origin } : {}) }; }
|
|
@@ -96,14 +109,72 @@ export class CollaborationService {
|
|
|
96
109
|
if (old && (old.serviceId !== node.serviceId || old.origin !== node.origin || old.caFingerprint256 !== node.caFingerprint256))
|
|
97
110
|
throw new Error('PEER_IDENTITY_CHANGED');
|
|
98
111
|
}
|
|
99
|
-
|
|
112
|
+
const removed = new Set(this.document.removedPeers?.map(peer => peer.serviceId));
|
|
113
|
+
const eligible = nodes.filter(node => node.serviceId !== self.serviceId && !removed.has(node.serviceId));
|
|
114
|
+
if (new Set([...this.document.peers.map(peer => peer.serviceId), ...eligible.map(node => node.serviceId)]).size > 63)
|
|
100
115
|
throw new Error('PEER_LIMIT');
|
|
101
116
|
this.setOrigin(origin);
|
|
102
|
-
this.document.peers = [...new Map([...this.document.peers, ...
|
|
117
|
+
this.document.peers = [...new Map([...this.document.peers, ...eligible].map(node => [node.serviceId, node])).values()];
|
|
103
118
|
this.persist();
|
|
104
119
|
void this.refresh();
|
|
105
120
|
return { ok: true, registered: this.document.peers.map(peer => peer.serviceId) };
|
|
106
121
|
}
|
|
122
|
+
groupsUsing(peer) {
|
|
123
|
+
return this.options.store.list().filter(group => group.sessionIds.some(id => address(id)?.origin === peer.origin)
|
|
124
|
+
|| group.federated && this.document.replicas?.[group.id]?.includes(peer.origin)).map(group => ({ id: group.id, name: group.name }));
|
|
125
|
+
}
|
|
126
|
+
/** Explicit removal is durable: other open clients may still enroll their
|
|
127
|
+
* stale saved catalogs, but only a deliberate restore or pairing re-adds it. */
|
|
128
|
+
removePeer(serviceId) {
|
|
129
|
+
const peer = this.document.peers.find(item => item.serviceId === serviceId);
|
|
130
|
+
if (!peer)
|
|
131
|
+
throw new CollaborationError('PEER_NOT_FOUND', '该协作服务已移除,请刷新列表', 404);
|
|
132
|
+
const groups = this.groupsUsing(peer);
|
|
133
|
+
if (groups.length)
|
|
134
|
+
throw new CollaborationError('PEER_IN_USE', `该服务仍用于协作组「${groups.map(group => group.name).join('」「')}」,请先移出对应成员或删除协作组`, 409, { groups });
|
|
135
|
+
this.options.transport.forgetUnusedPeer(serviceId);
|
|
136
|
+
const previous = this.document;
|
|
137
|
+
const scope = previous.scoped?.[serviceId];
|
|
138
|
+
this.document = { ...previous, peers: previous.peers.filter(item => item.serviceId !== serviceId),
|
|
139
|
+
scoped: Object.fromEntries(Object.entries(previous.scoped ?? {}).filter(([id]) => id !== serviceId)),
|
|
140
|
+
removedPeers: [...(previous.removedPeers ?? []).filter(item => item.serviceId !== serviceId), { ...peer, ...(scope ? { scope } : {}) }].slice(-768) };
|
|
141
|
+
try {
|
|
142
|
+
this.persist();
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
this.document = previous;
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
const client = this.clients.get(serviceId);
|
|
149
|
+
this.clients.delete(serviceId);
|
|
150
|
+
this.observations.delete(serviceId);
|
|
151
|
+
if (client)
|
|
152
|
+
void client.then(rpc => rpc.close()).catch(() => { });
|
|
153
|
+
return { ok: true, origin: peer.origin };
|
|
154
|
+
}
|
|
155
|
+
restorePeer(serviceId) {
|
|
156
|
+
const removed = this.document.removedPeers?.find(item => item.serviceId === serviceId);
|
|
157
|
+
if (!removed)
|
|
158
|
+
throw new CollaborationError('PEER_NOT_FOUND', '找不到这条已移除的协作连接', 404);
|
|
159
|
+
const { scope, ...node } = removed;
|
|
160
|
+
validateCollaborationNode(node);
|
|
161
|
+
if (this.document.peers.some(peer => peer.serviceId === node.serviceId || peer.origin === node.origin))
|
|
162
|
+
throw new CollaborationError('PEER_IDENTITY_CHANGED', '该地址已属于另一条服务连接,请重新配对', 409);
|
|
163
|
+
if (this.document.peers.length >= 63)
|
|
164
|
+
throw new CollaborationError('PEER_LIMIT', '协作服务数量已达上限', 400);
|
|
165
|
+
const previous = this.document;
|
|
166
|
+
this.document = { ...previous, peers: [...previous.peers, node], removedPeers: previous.removedPeers?.filter(item => item.serviceId !== serviceId),
|
|
167
|
+
scoped: { ...previous.scoped, ...(scope ? { [serviceId]: scope } : {}) } };
|
|
168
|
+
try {
|
|
169
|
+
this.persist();
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
this.document = previous;
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
void this.refresh();
|
|
176
|
+
return { ok: true, origin: node.origin };
|
|
177
|
+
}
|
|
107
178
|
/** Effective local sessions a peer may touch. `undefined` means a fully
|
|
108
179
|
* authorized directory peer; an array (possibly empty) means a peer that
|
|
109
180
|
* exists only because a device shared a write invitation for those sessions. */
|
|
@@ -157,7 +228,7 @@ export class CollaborationService {
|
|
|
157
228
|
const rpc = await (this.options.pairConnect ?? this.options.connect)(invitation.node);
|
|
158
229
|
try {
|
|
159
230
|
await rpc.request({ type: 'collaboration-service', action: 'pair', code: invitation.code, node: this.node() });
|
|
160
|
-
this.remember(invitation.node);
|
|
231
|
+
this.remember(invitation.node, true);
|
|
161
232
|
void this.refresh();
|
|
162
233
|
return { ok: true, peer: invitation.node };
|
|
163
234
|
}
|
|
@@ -178,7 +249,7 @@ export class CollaborationService {
|
|
|
178
249
|
if (offer.acceptedBy && !this.document.peers.some(peer => peer.serviceId === subject))
|
|
179
250
|
throw new Error('PAIRING_REVOKED');
|
|
180
251
|
offer.acceptedBy = subject;
|
|
181
|
-
this.remember(node);
|
|
252
|
+
this.remember(node, true);
|
|
182
253
|
void this.refresh();
|
|
183
254
|
return { ok: true };
|
|
184
255
|
}
|
|
@@ -298,6 +369,8 @@ export class CollaborationService {
|
|
|
298
369
|
try {
|
|
299
370
|
const client = await this.client(peer);
|
|
300
371
|
const data = await client.request({ type: 'collaboration-service', action: 'directory' });
|
|
372
|
+
if (!this.document.peers.some(item => item.serviceId === peer.serviceId))
|
|
373
|
+
return;
|
|
301
374
|
if (!Array.isArray(data.sessions) || data.sessions.length > 5000 || !Array.isArray(data.groups))
|
|
302
375
|
throw new Error('INVALID_DIRECTORY');
|
|
303
376
|
const sessions = data.sessions.filter((s) => s && typeof s.sessionId === 'string' && !s.sessionId.startsWith('remote:'));
|
|
@@ -318,7 +391,8 @@ export class CollaborationService {
|
|
|
318
391
|
}
|
|
319
392
|
}
|
|
320
393
|
catch (error) {
|
|
321
|
-
this.
|
|
394
|
+
if (this.document.peers.some(item => item.serviceId === peer.serviceId))
|
|
395
|
+
this.observations.set(peer.serviceId, { sessions: this.observations.get(peer.serviceId)?.sessions ?? [], checkedAt: Date.now(), error: error instanceof Error ? error.message.slice(0, 240) : 'PEER_UNAVAILABLE' });
|
|
322
396
|
}
|
|
323
397
|
})).then(() => { }).finally(() => { this.inFlight = undefined; });
|
|
324
398
|
}
|
|
@@ -328,12 +402,13 @@ export class CollaborationService {
|
|
|
328
402
|
const services = this.document.peers.map(peer => {
|
|
329
403
|
const observed = this.observations.get(peer.serviceId);
|
|
330
404
|
return { origin: peer.origin, serviceId: peer.serviceId, label: peer.origin,
|
|
331
|
-
connected: !!observed && !observed.error && Date.now() - observed.checkedAt < 15_000, error: observed?.error ?? (!observed ? 'CONNECTING' : undefined) };
|
|
405
|
+
groups: this.groupsUsing(peer), connected: !!observed && !observed.error && Date.now() - observed.checkedAt < 15_000, error: observed?.error ?? (!observed ? 'CONNECTING' : undefined) };
|
|
332
406
|
});
|
|
333
407
|
return { protocolVersion: 2, sessions: this.document.peers.flatMap(peer => this.document.scoped?.[peer.serviceId] ? [] : (this.observations.get(peer.serviceId)?.sessions ?? []).map(session => ({ ...session,
|
|
334
408
|
sessionId: remoteSession(peer.origin, session.sessionId), backendSessionId: null, agentNativeSessionId: null, serviceOrigin: peer.origin, serviceLabel: peer.origin,
|
|
335
409
|
serviceConnected: services.find(s => s.origin === peer.origin).connected, serviceCheckedAt: this.observations.get(peer.serviceId)?.checkedAt }))),
|
|
336
|
-
services: [...services, ...missing.map(origin => ({ origin, label: origin, connected: false, error: '服务连接授权尚未同步;在已授权服务页面连接后会自动完成' }))]
|
|
410
|
+
services: [...services, ...missing.map(origin => ({ origin, label: origin, connected: false, error: '服务连接授权尚未同步;在已授权服务页面连接后会自动完成' }))],
|
|
411
|
+
removedServices: this.document.removedPeers?.map(peer => ({ serviceId: peer.serviceId, origin: peer.origin, label: peer.origin, connected: false })) ?? [] };
|
|
337
412
|
}
|
|
338
413
|
async save(input) {
|
|
339
414
|
const existing = input.id ? this.options.store.getGroup(input.id) : null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { Worker } from 'node:worker_threads';
|
|
5
|
+
/** Keep transcript parsing and text scans off the terminal server's event loop.
|
|
6
|
+
* The worker and its private, incremental cache start only when search is used.
|
|
7
|
+
*/
|
|
8
|
+
export class NativeSessionSearch {
|
|
9
|
+
directory;
|
|
10
|
+
worker = null;
|
|
11
|
+
sequence = 0;
|
|
12
|
+
pending = new Map();
|
|
13
|
+
constructor(directory) {
|
|
14
|
+
this.directory = directory;
|
|
15
|
+
}
|
|
16
|
+
search(query, limit, titles, signal) {
|
|
17
|
+
return this.request({ type: 'search', query, limit, titles }, signal);
|
|
18
|
+
}
|
|
19
|
+
get(key) {
|
|
20
|
+
return this.request({ type: 'get', key });
|
|
21
|
+
}
|
|
22
|
+
request(input, signal) {
|
|
23
|
+
if (signal?.aborted)
|
|
24
|
+
return Promise.reject(new Error('搜索已取消'));
|
|
25
|
+
if (!this.worker) {
|
|
26
|
+
const compiled = new URL('../workers/nativeSessionSearchWorker.js', import.meta.url);
|
|
27
|
+
// tsx development runs source files; production always uses compiled JS.
|
|
28
|
+
const url = fs.existsSync(compiled) ? compiled : new URL('../workers/nativeSessionSearchWorker.ts', import.meta.url);
|
|
29
|
+
this.worker = new Worker(url, { workerData: {
|
|
30
|
+
directory: this.directory,
|
|
31
|
+
codexHome: process.env.CODEX_HOME || path.join(os.homedir(), '.codex'),
|
|
32
|
+
claudeHome: process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'),
|
|
33
|
+
} });
|
|
34
|
+
const worker = this.worker;
|
|
35
|
+
worker.unref();
|
|
36
|
+
worker.on('message', (message) => {
|
|
37
|
+
const entry = this.pending.get(message.id);
|
|
38
|
+
if (!entry)
|
|
39
|
+
return;
|
|
40
|
+
clearTimeout(entry.timer);
|
|
41
|
+
entry.cleanup();
|
|
42
|
+
this.pending.delete(message.id);
|
|
43
|
+
if (message.error)
|
|
44
|
+
entry.reject(new Error(message.error));
|
|
45
|
+
else
|
|
46
|
+
entry.resolve(message.value);
|
|
47
|
+
});
|
|
48
|
+
const fail = (error) => {
|
|
49
|
+
if (this.worker !== worker)
|
|
50
|
+
return;
|
|
51
|
+
this.worker = null;
|
|
52
|
+
for (const entry of this.pending.values()) {
|
|
53
|
+
clearTimeout(entry.timer);
|
|
54
|
+
entry.cleanup();
|
|
55
|
+
entry.reject(error);
|
|
56
|
+
}
|
|
57
|
+
this.pending.clear();
|
|
58
|
+
void worker.terminate();
|
|
59
|
+
};
|
|
60
|
+
worker.on('error', fail);
|
|
61
|
+
worker.on('exit', () => fail(new Error('历史会话索引服务已退出,请重新搜索')));
|
|
62
|
+
}
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const id = ++this.sequence;
|
|
65
|
+
const worker = this.worker;
|
|
66
|
+
const cancel = () => {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
this.pending.delete(id);
|
|
69
|
+
signal?.removeEventListener('abort', cancel);
|
|
70
|
+
worker.postMessage({ id, type: 'cancel' });
|
|
71
|
+
reject(new Error('搜索已取消'));
|
|
72
|
+
};
|
|
73
|
+
const timer = setTimeout(() => {
|
|
74
|
+
this.pending.delete(id);
|
|
75
|
+
signal?.removeEventListener('abort', cancel);
|
|
76
|
+
worker.postMessage({ id, type: 'cancel' });
|
|
77
|
+
reject(new Error('历史会话搜索超时,请缩短关键词后重试'));
|
|
78
|
+
}, 60_000);
|
|
79
|
+
this.pending.set(id, { resolve, reject, timer, cleanup: () => signal?.removeEventListener('abort', cancel) });
|
|
80
|
+
signal?.addEventListener('abort', cancel, { once: true });
|
|
81
|
+
worker.postMessage({ id, ...input });
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -53,7 +53,8 @@ export class SessionSearchStore {
|
|
|
53
53
|
this.flush();
|
|
54
54
|
const needle = query.trim().toLocaleLowerCase();
|
|
55
55
|
if (!needle)
|
|
56
|
-
return []
|
|
56
|
+
return [...this.metadata.values()].sort((a, b) => b.updatedAt - a.updatedAt)
|
|
57
|
+
.slice(0, Math.max(1, Math.min(limit, 100))).map(metadata => ({ ...metadata, snippet: '', matchCount: 0 }));
|
|
57
58
|
const results = [];
|
|
58
59
|
for (const metadata of this.metadata.values()) {
|
|
59
60
|
const text = this.readLog(metadata.sessionId);
|
package/dist/server/entry.js
CHANGED
|
@@ -50,7 +50,7 @@ import { RuntimeMonitor } from './utils/runtimeMonitor.js';
|
|
|
50
50
|
import { startOnboardingServer, stopOnboardingServer, getOnboardingServerUrl } from './onboardingServer.js';
|
|
51
51
|
import { CertificateWatcher } from './certificateWatcher.js';
|
|
52
52
|
import { startTermdockLogMaintenance, writeDiffTraceLog, writeErrorLog, writeJsonLog, writeTextLog, } from './utils/serverLogger.js';
|
|
53
|
-
import { indexRetainedClientModules, pinBundledRuntimeClientDist
|
|
53
|
+
import { indexRetainedClientModules, pinBundledRuntimeClientDist } from './utils/runtimeClient.js';
|
|
54
54
|
import { installCrashForensics } from './utils/crashForensics.js';
|
|
55
55
|
import { getTermdockVersion, TERMDOCK_CAPABILITIES, TERMDOCK_PROTOCOL_VERSION, } from './utils/version.js';
|
|
56
56
|
import { PORT, DEFAULT_HOST } from './config.js';
|
|
@@ -265,7 +265,6 @@ export function createApp(options = {}) {
|
|
|
265
265
|
version: getTermdockVersion(),
|
|
266
266
|
protocolVersion: TERMDOCK_PROTOCOL_VERSION,
|
|
267
267
|
capabilities: TERMDOCK_CAPABILITIES,
|
|
268
|
-
desktopManaged: process.env.TERMDOCK_DESKTOP === '1',
|
|
269
268
|
});
|
|
270
269
|
});
|
|
271
270
|
// Client-side log relay — enables collecting browser/device logs
|
|
@@ -350,10 +349,7 @@ export function createApp(options = {}) {
|
|
|
350
349
|
app.use('/api/android', csrfProtection.verifyMiddleware({ bypass: isTrustedLocalCliRequest }));
|
|
351
350
|
app.use('/api/android', androidRoutes);
|
|
352
351
|
if (fs.existsSync(bundledClientIndexPath)) {
|
|
353
|
-
const
|
|
354
|
-
const clientPath = selectedClientPath === bundledClientDistPath
|
|
355
|
-
? pinBundledRuntimeClientDist(bundledClientDistPath)
|
|
356
|
-
: selectedClientPath;
|
|
352
|
+
const clientPath = pinBundledRuntimeClientDist(bundledClientDistPath);
|
|
357
353
|
const compression = createStaticCompressionMiddleware(clientPath);
|
|
358
354
|
const staticFiles = express.static(clientPath, {
|
|
359
355
|
setHeaders: (res, filePath, stat) => {
|
|
@@ -14,7 +14,8 @@ export function readPinnedCertificateAuthority(origin, fingerprint) {
|
|
|
14
14
|
if (cached && cached.expires > Date.now())
|
|
15
15
|
return cached.value;
|
|
16
16
|
const value = new Promise((resolve, reject) => {
|
|
17
|
-
const
|
|
17
|
+
const signal = AbortSignal.timeout(5000);
|
|
18
|
+
const request = get(url, { agent: false, rejectUnauthorized: false, signal }, response => {
|
|
18
19
|
if (response.statusCode !== 200) {
|
|
19
20
|
response.resume();
|
|
20
21
|
reject(new Error('Pinned certificate authority unavailable'));
|
|
@@ -44,7 +45,13 @@ export function readPinnedCertificateAuthority(origin, fingerprint) {
|
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
});
|
|
47
|
-
request.on('error',
|
|
48
|
+
request.on('error', error => {
|
|
49
|
+
if (signal.aborted && signal.reason?.name === 'TimeoutError') {
|
|
50
|
+
reject(new Error(`连接 ${url.origin} 超时(5 秒):未能读取远端服务证书,请确认远端服务已启动且网络可达`, { cause: error }));
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
reject(error);
|
|
54
|
+
});
|
|
48
55
|
});
|
|
49
56
|
if (cache.size >= 64)
|
|
50
57
|
cache.delete(cache.keys().next().value);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getHideGitIgnoredRootsSetting } from '../utils/settings.js';
|
|
1
2
|
import { readRecentCommitHistory } from '../utils/recentCommitHistory.js';
|
|
2
3
|
import { AUTH_COOKIE, isSessionValid } from '../utils/authProtection.js';
|
|
3
4
|
import { Router } from 'express';
|
|
@@ -2703,6 +2704,35 @@ async function getCachedNestedGitRoots(workspaceRoot, options = {}) {
|
|
|
2703
2704
|
}
|
|
2704
2705
|
return promise;
|
|
2705
2706
|
}
|
|
2707
|
+
// Ask Git in one batch so nested .gitignore, .git/info/exclude and global
|
|
2708
|
+
// exclusions follow Git semantics; NUL separators preserve unusual filenames.
|
|
2709
|
+
function getGitIgnoredPaths(directory, entries, signal) {
|
|
2710
|
+
if (entries.length === 0)
|
|
2711
|
+
return Promise.resolve(new Set());
|
|
2712
|
+
return new Promise((resolve, reject) => {
|
|
2713
|
+
const proc = execFile('git', ['check-ignore', '-z', '--stdin'], {
|
|
2714
|
+
cwd: directory,
|
|
2715
|
+
timeout: GIT_TIMEOUT_MS,
|
|
2716
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
2717
|
+
signal,
|
|
2718
|
+
}, (error, stdout, stderr) => {
|
|
2719
|
+
if (error && error.code !== 1) {
|
|
2720
|
+
// Ordinary non-repository directories remain browsable with the filter.
|
|
2721
|
+
if (error.code === 'ENOENT' || /not a git repository/i.test(stderr)) {
|
|
2722
|
+
resolve(new Set());
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
reject(error);
|
|
2726
|
+
}
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
resolve(new Set(stdout.split('\0').filter(Boolean)));
|
|
2730
|
+
});
|
|
2731
|
+
// Git may exit before consuming stdin (e.g. outside a repository).
|
|
2732
|
+
proc.stdin?.on('error', () => undefined);
|
|
2733
|
+
proc.stdin?.end(entries.map((entry) => path.join(directory, entry.name)).join('\0') + '\0');
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2706
2736
|
// Directory listing
|
|
2707
2737
|
router.get('/list', async (req, res) => {
|
|
2708
2738
|
const requestId = ++fsIoRequestSeq;
|
|
@@ -2735,7 +2765,19 @@ router.get('/list', async (req, res) => {
|
|
|
2735
2765
|
const sortMode = req.query.sort === 'modified' ? 'modified' : 'name';
|
|
2736
2766
|
const allEntries = await fs.promises.readdir(resolvedPath, { withFileTypes: true });
|
|
2737
2767
|
throwIfAborted(controller.signal, 'fs.list');
|
|
2738
|
-
|
|
2768
|
+
let visibleDirents = allEntries.filter(dirent => showHidden || !dirent.name.startsWith('.'));
|
|
2769
|
+
const gitIgnoreRoot = typeof req.query.gitIgnoreRoot === 'string' ? req.query.gitIgnoreRoot : null;
|
|
2770
|
+
if (gitIgnoreRoot && getHideGitIgnoredRootsSetting()[gitIgnoreRoot]) {
|
|
2771
|
+
const resolvedRoot = await pathValidator.validatePathAsync(gitIgnoreRoot);
|
|
2772
|
+
const relative = path.relative(resolvedRoot, resolvedPath);
|
|
2773
|
+
if (relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative))) {
|
|
2774
|
+
const ignored = await getGitIgnoredPaths(resolvedPath, visibleDirents, controller.signal);
|
|
2775
|
+
throwIfAborted(controller.signal, 'fs.list');
|
|
2776
|
+
// Filter before sorting and capping so ignored folders don't crowd
|
|
2777
|
+
// useful files out of the first page of a large directory.
|
|
2778
|
+
visibleDirents = visibleDirents.filter((entry) => entry.name !== '.git' && !ignored.has(path.join(resolvedPath, entry.name)));
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2739
2781
|
const entries = sortMode === 'modified'
|
|
2740
2782
|
? (await loadDirectoryEntriesWithModified(resolvedPath, visibleDirents, controller.signal))
|
|
2741
2783
|
.sort(compareDirectoryEntriesByModified)
|
|
@@ -19,13 +19,12 @@ import { execFile, spawn } from 'child_process';
|
|
|
19
19
|
import { createHash, randomBytes, randomUUID } from 'crypto';
|
|
20
20
|
import { promisify } from 'util';
|
|
21
21
|
import { caffeinateManager } from '../utils/caffeinate.js';
|
|
22
|
-
import { DesktopRuntimeOwnerClient } from '../utils/desktopRuntimeOwner.js';
|
|
23
22
|
import { gitStatusCache } from '../utils/gitStatus.js';
|
|
24
23
|
import { getPtyHostManager } from '../ptyhost/manager.js';
|
|
25
24
|
import { pathValidator } from '../utils/pathValidator.js';
|
|
26
25
|
import { TERMINAL, TMUX } from '../config.js';
|
|
27
26
|
import { localAccessManager } from '../utils/localAccess.js';
|
|
28
|
-
import { normalizeLocalAccessName, getLocaleSetting, setLocaleSetting, getContextDraftHeightSetting, setContextDraftHeightSetting, getAutoRenameAgentsSetting, setAutoRenameAgentsSetting, getAutoRenameNamerSetting, setAutoRenameNamerSetting, getAutoRenameModelsSetting, setAutoRenameModelsSetting, getAutoRenameIntervalMinutesSetting, setAutoRenameIntervalMinutesSetting, getAutoRenamePromptPreferenceSetting, setAutoRenamePromptPreferenceSetting, getAutoRenamePromptPayloadCharsSetting, setAutoRenamePromptPayloadCharsSetting, getNewSessionAgentSlugSetting, setNewSessionAgentSlugSetting, getRunningSessionButtonEnabledSetting, getAttentionButtonEnabledSetting, getCollaborationPanelsSetting, setCollaborationPanelSetting, getAndroidPanelSetting, setAndroidPanelSetting, normalizeAndroidPanel, getCollaborationFloatingGroupIdSetting, setCollaborationFloatingGroupIdSetting, getServiceSwitcherExpandedSetting, setServiceSwitcherExpandedSetting, setRunningSessionButtonEnabledSetting, setAttentionButtonEnabledSetting, getFileSortModesSetting, setFileSortModesSetting, setFileSortModeSetting, getNestedGitScanRootsSetting, setNestedGitScanRootSetting, getPinnedExplorerRootsSetting, setPinnedExplorerRootsSetting, setPinnedExplorerRootSetting, watchPinnedExplorerRootsSetting, getActiveGitReposSetting, setActiveGitRepoSetting, } from '../utils/settings.js';
|
|
27
|
+
import { normalizeLocalAccessName, getLocaleSetting, setLocaleSetting, getContextDraftHeightSetting, setContextDraftHeightSetting, getAutoRenameAgentsSetting, setAutoRenameAgentsSetting, getAutoRenameNamerSetting, setAutoRenameNamerSetting, getAutoRenameModelsSetting, setAutoRenameModelsSetting, getAutoRenameIntervalMinutesSetting, setAutoRenameIntervalMinutesSetting, getAutoRenamePromptPreferenceSetting, setAutoRenamePromptPreferenceSetting, getAutoRenamePromptPayloadCharsSetting, setAutoRenamePromptPayloadCharsSetting, getNewSessionAgentSlugSetting, setNewSessionAgentSlugSetting, getRunningSessionButtonEnabledSetting, getAttentionButtonEnabledSetting, getCollaborationPanelsSetting, setCollaborationPanelSetting, getAndroidPanelSetting, setAndroidPanelSetting, normalizeAndroidPanel, getCollaborationFloatingGroupIdSetting, setCollaborationFloatingGroupIdSetting, getServiceSwitcherExpandedSetting, setServiceSwitcherExpandedSetting, setRunningSessionButtonEnabledSetting, setAttentionButtonEnabledSetting, getFileSortModesSetting, getHideGitIgnoredRootsSetting, setHideGitIgnoredRootSetting, setFileSortModesSetting, setFileSortModeSetting, getNestedGitScanRootsSetting, setNestedGitScanRootSetting, getPinnedExplorerRootsSetting, setPinnedExplorerRootsSetting, setPinnedExplorerRootSetting, watchPinnedExplorerRootsSetting, getActiveGitReposSetting, setActiveGitRepoSetting, } from '../utils/settings.js';
|
|
29
28
|
import { loadContextDraft, saveContextDraft } from '../utils/contextDraft.js';
|
|
30
29
|
import { getOnboardingServerUrl } from '../onboardingServer.js';
|
|
31
30
|
import { inspectLinuxInotifyUsage, isWatchResourceExhaustion } from '../utils/fileWatchPolicy.js';
|
|
@@ -48,6 +47,8 @@ import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/col
|
|
|
48
47
|
import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
|
|
49
48
|
import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
50
49
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
50
|
+
import { NativeSessionSearch } from '../agent/nativeSessionSearch.js';
|
|
51
|
+
import { CollaborationError } from '../agent/collaborationProtocol.js';
|
|
51
52
|
import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
|
|
52
53
|
import { CollaborationRoutingStore, selectCollaborationPane, selectDrivePane } from '../agent/collaborationRouting.js';
|
|
53
54
|
import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
|
|
@@ -82,6 +83,7 @@ const collaborationDeliveryWorker = new CollaborationDeliveryWorker({
|
|
|
82
83
|
onError: (error) => console.warn('[collaboration] background delivery failed:', getErrorMessage(error)),
|
|
83
84
|
});
|
|
84
85
|
const sessionSearchStore = new SessionSearchStore(`${TERMDOCK_DIR}/session-search`);
|
|
86
|
+
const nativeSessionSearch = new NativeSessionSearch(`${TERMDOCK_DIR}/session-search/native`);
|
|
85
87
|
let atomicJsonWriteSequence = 0;
|
|
86
88
|
async function readJsonFileIfExists(filePath) {
|
|
87
89
|
try {
|
|
@@ -385,18 +387,7 @@ const npmAutoUpdateManager = new TermdockAutoUpdateManager({
|
|
|
385
387
|
requestRestart: requestServerRestartAfterUpdate,
|
|
386
388
|
log: (message) => console.warn(message),
|
|
387
389
|
});
|
|
388
|
-
|
|
389
|
-
? new DesktopRuntimeOwnerClient(process.env.TERMDOCK_DESKTOP_OWNER_SOCKET, TERMDOCK_VERSION, (state) => broadcastControlEvent({ type: 'update-state', state }))
|
|
390
|
-
: null;
|
|
391
|
-
// Packaged desktop builds already have a signed app/runtime updater. The npm
|
|
392
|
-
// updater only owns standalone CLI services, otherwise both mechanisms could
|
|
393
|
-
// race to replace the runtime.
|
|
394
|
-
if (desktopRuntimeOwner) {
|
|
395
|
-
desktopRuntimeOwner.start();
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
npmAutoUpdateManager.start();
|
|
399
|
-
}
|
|
390
|
+
npmAutoUpdateManager.start();
|
|
400
391
|
async function flushClientStateBroadcast() {
|
|
401
392
|
if (broadcastClientStateInFlight) {
|
|
402
393
|
broadcastClientStateNeedsRerun = true;
|
|
@@ -3415,8 +3406,8 @@ async function findCodexSessionFile(nativeSessionId) {
|
|
|
3415
3406
|
return cachedPath;
|
|
3416
3407
|
if (!/^[a-zA-Z0-9._-]{8,160}$/.test(nativeSessionId))
|
|
3417
3408
|
return null;
|
|
3418
|
-
const
|
|
3419
|
-
const pending = [
|
|
3409
|
+
const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
3410
|
+
const pending = [path.join(codexHome, 'sessions'), path.join(codexHome, 'archived_sessions')];
|
|
3420
3411
|
let visited = 0;
|
|
3421
3412
|
while (pending.length > 0 && visited < 20_000) {
|
|
3422
3413
|
const directory = pending.pop();
|
|
@@ -3448,7 +3439,9 @@ async function findExternalCodexWriter(target) {
|
|
|
3448
3439
|
return null;
|
|
3449
3440
|
try {
|
|
3450
3441
|
const { stdout } = await execFileAsync('lsof', ['-t', '--', sessionFile], { timeout: 2500, maxBuffer: 64 * 1024 });
|
|
3451
|
-
|
|
3442
|
+
// Our search worker reads these files in this process; that read-only
|
|
3443
|
+
// descriptor must not be mistaken for an external Codex conversation owner.
|
|
3444
|
+
const pid = stdout.split(/\s+/).map(Number).find((value) => Number.isInteger(value) && value > 1 && value !== process.pid);
|
|
3452
3445
|
return pid ?? null;
|
|
3453
3446
|
}
|
|
3454
3447
|
catch {
|
|
@@ -5588,6 +5581,31 @@ router.get('/operations/collaboration-directory', (req, res) => {
|
|
|
5588
5581
|
void service.refresh();
|
|
5589
5582
|
res.json(service.directory());
|
|
5590
5583
|
});
|
|
5584
|
+
router.delete('/operations/collaboration-connections/:serviceId', (req, res) => {
|
|
5585
|
+
try {
|
|
5586
|
+
assertPeerRegistrationAuthority(req);
|
|
5587
|
+
const service = req.app.locals.collaborationService;
|
|
5588
|
+
if (!service)
|
|
5589
|
+
return res.status(503).json({ error: '协作服务正在启动' });
|
|
5590
|
+
res.json(service.removePeer(req.params.serviceId));
|
|
5591
|
+
}
|
|
5592
|
+
catch (error) {
|
|
5593
|
+
res.status(error instanceof CollaborationError ? error.httpStatus : getErrorMessage(error) === 'AUTHORIZATION_DENIED' ? 403 : 400)
|
|
5594
|
+
.json({ error: getErrorMessage(error), ...(error instanceof CollaborationError ? { code: error.code, ...error.details } : {}) });
|
|
5595
|
+
}
|
|
5596
|
+
});
|
|
5597
|
+
router.post('/operations/collaboration-connections/:serviceId/restore', (req, res) => {
|
|
5598
|
+
try {
|
|
5599
|
+
assertPeerRegistrationAuthority(req);
|
|
5600
|
+
const service = req.app.locals.collaborationService;
|
|
5601
|
+
if (!service)
|
|
5602
|
+
return res.status(503).json({ error: '协作服务正在启动' });
|
|
5603
|
+
res.json(service.restorePeer(req.params.serviceId));
|
|
5604
|
+
}
|
|
5605
|
+
catch (error) {
|
|
5606
|
+
res.status(error instanceof CollaborationError ? error.httpStatus : getErrorMessage(error) === 'AUTHORIZATION_DENIED' ? 403 : 400).json({ error: getErrorMessage(error) });
|
|
5607
|
+
}
|
|
5608
|
+
});
|
|
5591
5609
|
export function collaborationDirectorySessions() { return globalSessionState.sessions.map(orchestrationSessionSnapshot); }
|
|
5592
5610
|
export function collaborationLocalActivity() {
|
|
5593
5611
|
return globalSessionState.sessions.map(record => {
|
|
@@ -5987,16 +6005,81 @@ router.use('/operations/orchestration', collaborationRoutes({ store: collaborati
|
|
|
5987
6005
|
broadcastClientState();
|
|
5988
6006
|
return { ok: true };
|
|
5989
6007
|
} }));
|
|
5990
|
-
router.get('/operations/session-search', (req, res) => {
|
|
5991
|
-
const query = typeof req.query.q === 'string' ? req.query.q : '';
|
|
5992
|
-
const
|
|
6008
|
+
router.get('/operations/session-search', async (req, res) => {
|
|
6009
|
+
const query = typeof req.query.q === 'string' ? req.query.q.trim().slice(0, 512) : '';
|
|
6010
|
+
const limit = Math.max(1, Math.min(5000, Math.floor(Number(req.query.limit)) || 50));
|
|
6011
|
+
const controller = new AbortController();
|
|
6012
|
+
const disconnected = () => { if (!res.writableEnded)
|
|
6013
|
+
controller.abort(); };
|
|
6014
|
+
res.once('close', disconnected);
|
|
6015
|
+
const terminalResults = sessionSearchStore.search(query, 100).map((result) => {
|
|
5993
6016
|
const live = globalSessionState.sessions.some((candidate) => candidate.sessionId === result.sessionId);
|
|
5994
6017
|
const resumeEntry = result.agentNativeSessionId
|
|
5995
|
-
? agentResumeHistory.list().find((entry) => entry.agent.sessionId === result.agentNativeSessionId)
|
|
6018
|
+
? agentResumeHistory.list().find((entry) => entry.agent.slug === result.agentSlug && entry.agent.sessionId === result.agentNativeSessionId)
|
|
5996
6019
|
: null;
|
|
5997
|
-
return { ...result, live, resumeHistoryId: resumeEntry?.id ?? null };
|
|
6020
|
+
return { ...result, source: 'terminal', nativeKey: null, live, resumeHistoryId: resumeEntry?.id ?? null };
|
|
5998
6021
|
});
|
|
5999
|
-
|
|
6022
|
+
try {
|
|
6023
|
+
// Preserve the names users see in Termdock as searchable aliases, including
|
|
6024
|
+
// custom titles for conversations that have already been closed.
|
|
6025
|
+
const titles = [
|
|
6026
|
+
...sessionSearchStore.search('', 100).flatMap(entry => entry.agentSlug && entry.agentNativeSessionId ? [{ agentSlug: entry.agentSlug, agentNativeSessionId: entry.agentNativeSessionId, title: entry.title }] : []),
|
|
6027
|
+
...agentResumeHistory.list().reverse().map(entry => ({ agentSlug: entry.agent.slug, agentNativeSessionId: entry.agent.sessionId, title: entry.title })),
|
|
6028
|
+
...globalSessionState.sessions.flatMap(entry => entry.customName && entry.agentResume?.sessionId ? [{ agentSlug: entry.agentResume.slug, agentNativeSessionId: entry.agentResume.sessionId, title: entry.name }] : []),
|
|
6029
|
+
];
|
|
6030
|
+
const native = await nativeSessionSearch.search(query, limit + terminalResults.length, titles, controller.signal);
|
|
6031
|
+
if (controller.signal.aborted)
|
|
6032
|
+
return;
|
|
6033
|
+
const matched = new Set(native.matchedSessions);
|
|
6034
|
+
const additional = terminalResults.filter(result => !result.agentNativeSessionId || !matched.has(`${result.agentSlug}:${result.agentNativeSessionId}`));
|
|
6035
|
+
const results = native.results.map(result => {
|
|
6036
|
+
const owner = findActiveAgentResumeOwner('', { slug: result.agentSlug, nativeSessionId: result.agentNativeSessionId, command: '' });
|
|
6037
|
+
const liveRecord = owner ? globalSessionState.sessions.find(candidate => candidate.backendSessionId === owner) : null;
|
|
6038
|
+
return { ...result, source: result.agentSlug, nativeKey: result.key, sessionId: liveRecord?.sessionId || `native:${result.key}`,
|
|
6039
|
+
live: Boolean(liveRecord), resumeHistoryId: null };
|
|
6040
|
+
});
|
|
6041
|
+
res.json({ query, results: [...results, ...additional].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, limit), total: native.total + additional.length, index: native.index });
|
|
6042
|
+
}
|
|
6043
|
+
catch (error) {
|
|
6044
|
+
if (controller.signal.aborted)
|
|
6045
|
+
return;
|
|
6046
|
+
res.json({ query, results: terminalResults.slice(0, limit), total: terminalResults.length,
|
|
6047
|
+
index: { total: 0, indexed: 0, building: false, failed: 0, warning: `历史会话暂时无法搜索:${getErrorMessage(error)}` } });
|
|
6048
|
+
}
|
|
6049
|
+
finally {
|
|
6050
|
+
res.off('close', disconnected);
|
|
6051
|
+
}
|
|
6052
|
+
});
|
|
6053
|
+
router.post('/operations/session-search/prepare', async (req, res) => {
|
|
6054
|
+
const key = typeof req.body?.nativeKey === 'string' ? req.body.nativeKey : '';
|
|
6055
|
+
if (!/^[a-f0-9]{64}$/.test(key))
|
|
6056
|
+
return res.status(400).json({ error: '无效的历史会话' });
|
|
6057
|
+
try {
|
|
6058
|
+
// Resolve the original file and command on the server; never accept a
|
|
6059
|
+
// transcript path, working directory or shell command from search clients.
|
|
6060
|
+
const entry = await nativeSessionSearch.get(key);
|
|
6061
|
+
if (!entry)
|
|
6062
|
+
return res.status(404).json({ error: '这条历史会话已不存在,请重新搜索' });
|
|
6063
|
+
const agent = agentBySlug(entry.agentSlug);
|
|
6064
|
+
const history = agentResumeHistory.list().find(candidate => candidate.agent.slug === entry.agentSlug && candidate.agent.sessionId === entry.agentNativeSessionId);
|
|
6065
|
+
const command = agent ? buildResumeCommand(agent, entry.agentNativeSessionId, history?.agent.launchArgv) : null;
|
|
6066
|
+
if (!command)
|
|
6067
|
+
return res.status(410).json({ error: '当前 Agent 不支持恢复这条历史会话' });
|
|
6068
|
+
const target = { slug: entry.agentSlug, nativeSessionId: entry.agentNativeSessionId, command };
|
|
6069
|
+
const owner = findActiveAgentResumeOwner('', target);
|
|
6070
|
+
const liveRecord = owner ? globalSessionState.sessions.find(candidate => candidate.backendSessionId === owner) : null;
|
|
6071
|
+
if (liveRecord)
|
|
6072
|
+
return res.json({ sessionId: liveRecord.sessionId });
|
|
6073
|
+
if (owner || await findExternalCodexWriter(target))
|
|
6074
|
+
return res.status(409).json({ error: '这条会话已在其他终端运行,请先切换到原终端或关闭后恢复', code: 'AGENT_SESSION_ACTIVE_ELSEWHERE' });
|
|
6075
|
+
if (!entry.cwd)
|
|
6076
|
+
return res.status(409).json({ error: '尚未找到这条会话的项目目录,请等待索引完成后重试' });
|
|
6077
|
+
const cwd = await resolveWorkingDirectory(req, entry.cwd);
|
|
6078
|
+
res.json({ command, cwd, title: entry.title, agent });
|
|
6079
|
+
}
|
|
6080
|
+
catch (error) {
|
|
6081
|
+
res.status(400).json({ error: `无法恢复会话:${getErrorMessage(error)}` });
|
|
6082
|
+
}
|
|
6000
6083
|
});
|
|
6001
6084
|
router.get('/agent-resume-history', (_req, res) => {
|
|
6002
6085
|
const entries = agentResumeHistory.list().flatMap((entry) => {
|
|
@@ -6238,6 +6321,7 @@ async function getSettingsPayload() {
|
|
|
6238
6321
|
collaborationFloatingGroupId: getCollaborationFloatingGroupIdSetting(),
|
|
6239
6322
|
serviceSwitcherExpanded: getServiceSwitcherExpandedSetting(),
|
|
6240
6323
|
fileSortModes: getFileSortModesSetting(),
|
|
6324
|
+
hideGitIgnoredRoots: getHideGitIgnoredRootsSetting(),
|
|
6241
6325
|
nestedGitScanRoots: getNestedGitScanRootsSetting(),
|
|
6242
6326
|
activeGitRepos: getActiveGitReposSetting(),
|
|
6243
6327
|
pinnedExplorerRoots: getPinnedExplorerRootsSetting(),
|
|
@@ -6257,14 +6341,10 @@ router.get('/auto-title/catalog', async (req, res) => {
|
|
|
6257
6341
|
res.json({ namers: await getTitleNamerCatalog(force) });
|
|
6258
6342
|
});
|
|
6259
6343
|
router.get('/update', (_req, res) => {
|
|
6260
|
-
res.json(
|
|
6344
|
+
res.json(npmAutoUpdateManager.getState());
|
|
6261
6345
|
});
|
|
6262
6346
|
router.post('/update/check', async (_req, res) => {
|
|
6263
6347
|
try {
|
|
6264
|
-
if (desktopRuntimeOwner) {
|
|
6265
|
-
res.json(await desktopRuntimeOwner.checkForUpdates());
|
|
6266
|
-
return;
|
|
6267
|
-
}
|
|
6268
6348
|
// npm 查询加全局安装动辄二三十秒,而加密传输对 30s 内收不到任何回包就判超时,
|
|
6269
6349
|
// 客户端会弹「服务响应超时」——更新其实装完了。检查照常跑,进度与结果都由控制
|
|
6270
6350
|
// WS 的 update-state 广播推送,这里立刻回当前状态(通常是 checking)即可。
|
|
@@ -6279,9 +6359,7 @@ router.post('/update/check', async (_req, res) => {
|
|
|
6279
6359
|
});
|
|
6280
6360
|
router.post('/update/restart', async (_req, res) => {
|
|
6281
6361
|
try {
|
|
6282
|
-
const state =
|
|
6283
|
-
? await desktopRuntimeOwner.restart()
|
|
6284
|
-
: npmAutoUpdateManager.confirmRestart();
|
|
6362
|
+
const state = npmAutoUpdateManager.confirmRestart();
|
|
6285
6363
|
res.status(202).json(state);
|
|
6286
6364
|
}
|
|
6287
6365
|
catch (error) {
|
|
@@ -6427,6 +6505,18 @@ router.put('/settings', async (req, res) => {
|
|
|
6427
6505
|
}
|
|
6428
6506
|
setFileSortModeSetting(preference.path, preference.mode);
|
|
6429
6507
|
}
|
|
6508
|
+
if (body.hideGitIgnoredRoot && typeof body.hideGitIgnoredRoot === 'object') {
|
|
6509
|
+
const preference = body.hideGitIgnoredRoot;
|
|
6510
|
+
const validPath = typeof preference.rootPath === 'string'
|
|
6511
|
+
&& preference.rootPath.length > 0
|
|
6512
|
+
&& preference.rootPath.length <= 4096
|
|
6513
|
+
&& (preference.rootPath.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(preference.rootPath));
|
|
6514
|
+
if (!validPath || typeof preference.enabled !== 'boolean') {
|
|
6515
|
+
res.status(400).json({ error: 'Invalid Git ignore visibility preference', code: 'GIT_IGNORE_VISIBILITY_INVALID' });
|
|
6516
|
+
return;
|
|
6517
|
+
}
|
|
6518
|
+
setHideGitIgnoredRootSetting(preference.rootPath, preference.enabled);
|
|
6519
|
+
}
|
|
6430
6520
|
if (body.nestedGitScanRoot && typeof body.nestedGitScanRoot === 'object') {
|
|
6431
6521
|
const preference = body.nestedGitScanRoot;
|
|
6432
6522
|
const isAbsoluteRootPath = (value) => typeof value === 'string'
|
|
@@ -8297,7 +8387,7 @@ export function handleControlWebSocket(ws, clientId) {
|
|
|
8297
8387
|
ws.send(JSON.stringify({ type: 'client-state', seq: initialSeq, state: globalSessionState, inventory: inventory ?? latestSessionInventory }));
|
|
8298
8388
|
ws.send(JSON.stringify({
|
|
8299
8389
|
type: 'update-state',
|
|
8300
|
-
state:
|
|
8390
|
+
state: npmAutoUpdateManager.getState(),
|
|
8301
8391
|
}));
|
|
8302
8392
|
// 服务健康快照:崩溃时服务自己已经死了、推不出任何东西,所以"红点"这件事
|
|
8303
8393
|
// 只能靠客户端重连时的这一发。客户端断线重连的既有逻辑正好覆盖了那个时刻。
|