termdock 1.4.283 → 1.4.285
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--hZmWk-u.js +157 -0
- package/dist/client/assets/App--hZmWk-u.js.br +0 -0
- package/dist/client/assets/App--hZmWk-u.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-D3P0LWfU.js → ChangeWalkthroughPanel-BQ1hdwzt.js} +1 -1
- package/dist/client/assets/ChangeWalkthroughPanel-BQ1hdwzt.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BQ1hdwzt.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-DHLTKfYe.js → DagPlayground-BERXHwib.js} +1 -1
- package/dist/client/assets/DagPlayground-BERXHwib.js.br +0 -0
- package/dist/client/assets/DagPlayground-BERXHwib.js.gz +0 -0
- package/dist/client/assets/{DiffLab-DyapQhFp.js → DiffLab-DF14_TAz.js} +1 -1
- package/dist/client/assets/DiffLab-DF14_TAz.js.br +0 -0
- package/dist/client/assets/DiffLab-DF14_TAz.js.gz +0 -0
- package/dist/client/assets/{DiffReview-CtlVXUuM.js → DiffReview-BQcIn0zl.js} +1 -1
- package/dist/client/assets/DiffReview-BQcIn0zl.js.br +0 -0
- package/dist/client/assets/DiffReview-BQcIn0zl.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-CcATFm0V.js → DiffReviewLab-CgChwzQr.js} +1 -1
- package/dist/client/assets/DiffReviewLab-CgChwzQr.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CgChwzQr.js.gz +0 -0
- package/dist/client/assets/DiffViewer-hDpe-wvG.js +37 -0
- package/dist/client/assets/DiffViewer-hDpe-wvG.js.br +0 -0
- package/dist/client/assets/DiffViewer-hDpe-wvG.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-DFsugreu.js → ModelPreview-vg3vRIhx.js} +1 -1
- package/dist/client/assets/ModelPreview-vg3vRIhx.js.br +0 -0
- package/dist/client/assets/ModelPreview-vg3vRIhx.js.gz +0 -0
- package/dist/client/assets/RightSidebar-5H4vhn3k.js +1678 -0
- package/dist/client/assets/RightSidebar-5H4vhn3k.js.br +0 -0
- package/dist/client/assets/RightSidebar-5H4vhn3k.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-C6Qn78Kl.js → abnfDiagram-VCTEODGH-DI7938HB.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-DI7938HB.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DI7938HB.js.gz +0 -0
- package/dist/client/assets/app-BeVXUQb-.js +35 -0
- package/dist/client/assets/app-BeVXUQb-.js.br +0 -0
- package/dist/client/assets/app-BeVXUQb-.js.gz +0 -0
- package/dist/client/assets/{arc-DlrdXzuL.js → arc-D4qDm5Yn.js} +1 -1
- package/dist/client/assets/arc-D4qDm5Yn.js.br +0 -0
- package/dist/client/assets/arc-D4qDm5Yn.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-yDm6ZlLB.js → architectureDiagram-5GKGNRK7-C4IZCNC9.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C4IZCNC9.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C4IZCNC9.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CQ_ev6HF.js → blockDiagram-I7D4REHJ-DHVOr_jQ.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-DHVOr_jQ.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-DHVOr_jQ.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-Xc0eRnWn.js → c4Diagram-7LVT6UL2-kQqkOkIe.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-kQqkOkIe.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-kQqkOkIe.js.gz +0 -0
- package/dist/client/assets/channel-DNlwrmA4.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-DOea--M6.js → chunk-2Q5K7J3B-D2kOR60I.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-5BTB5aOY.js → chunk-5VM5RSS4-CxaDg0nL.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-D4bPuUCL.js → chunk-F27PBJKO-Bxf7XPNj.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-Bxf7XPNj.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-Bxf7XPNj.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-CcpsYCGR.js → chunk-IMKFNOWR-BTifpaB5.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-BTifpaB5.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BTifpaB5.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-DrVzbPfl.js → chunk-JWPE2WC7-Deieql_u.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-D2vQ6I_d.js → chunk-POPQ4Y6H-CqZwUpjK.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-BK9PycPu.js → chunk-SVP7TREG-Bm1spPMb.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-Bm1spPMb.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-Bm1spPMb.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-LPhjhi7S.js → chunk-TICWLB2K-BGLj-zGP.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-BGLj-zGP.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BGLj-zGP.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-qujR76f5.js → chunk-XXDRQBXY-CR77bUfc.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CUt4JCwJ.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CUt4JCwJ.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-BArSIiIO.js → cose-bilkent-JH36ORCC-GTawhnoV.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-GTawhnoV.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-GTawhnoV.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CCs1or7w.js → cynefin-OW5HDTMX-DHdRkQdW.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-DHdRkQdW.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DHdRkQdW.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CZtiCwpH.js → cynefinDiagram-5FMLGOSQ-Cj2ytLun.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Cj2ytLun.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Cj2ytLun.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-sxi6dz48.js → dagre-GXQ25YYZ-BdFZmtIj.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-BdFZmtIj.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BdFZmtIj.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-CqA9RxCi.js → diagram-S7CK7UJ4-D5WQNuKI.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-D5WQNuKI.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-D5WQNuKI.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-DI6HGWgF.js → diagram-UQ7AKVKN-ChwjfMce.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-ChwjfMce.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-ChwjfMce.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-CMZi8lNh.js → diagram-VSXAHHWV-DT4FET5f.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-DT4FET5f.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-DT4FET5f.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-C7C5Wauj.js → diagram-VX7I27RA-BLJsAGx6.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-BLJsAGx6.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-BLJsAGx6.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-5gZWUza2.js → diagram-Z3DM3KII-C1ofLllK.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-C1ofLllK.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-C1ofLllK.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-_UeIqZQ9.js → ebnfDiagram-PWID7BFC-apxDEeqe.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-apxDEeqe.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-apxDEeqe.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Xmw2qbA7.js → erDiagram-RLTQ6QDP-Dq9hBYpH.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dq9hBYpH.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dq9hBYpH.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-KRYKHFDk.js → flowDiagram-HODETNUW-Juk-Qv9f.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-Juk-Qv9f.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-Juk-Qv9f.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-UcoHnpCx.js → ganttDiagram-EL5Y4UJY-mmwd0mxs.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-mmwd0mxs.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-mmwd0mxs.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-B6OZhQjV.js → gestureArbiter-CM_xJnCS.js} +1 -1
- package/dist/client/assets/gestureArbiter-CM_xJnCS.js.br +0 -0
- package/dist/client/assets/gestureArbiter-CM_xJnCS.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-Evwvg64n.js → gitGraphDiagram-WWUBYQGX--UL7HV4P.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX--UL7HV4P.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX--UL7HV4P.js.gz +0 -0
- package/dist/client/assets/index-pZsldTFS.css +1 -0
- package/dist/client/assets/index-pZsldTFS.css.br +0 -0
- package/dist/client/assets/index-pZsldTFS.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-CdTYa6sH.js → infoDiagram-27XIBGKW-CcsxyqYJ.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-D1-1rWNL.js → ishikawaDiagram-5VMMS53U-CNVFPx1_.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CNVFPx1_.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-CNVFPx1_.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-DNF2Xeph.js → journeyDiagram-3NMN7TZE-BtIuJLUp.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BtIuJLUp.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BtIuJLUp.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-nNy4MKl1.js → kanban-definition-UXKFOSKX-gOFjuFZV.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-gOFjuFZV.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-gOFjuFZV.js.gz +0 -0
- package/dist/client/assets/{layout-eOsGqyyy.js → layout-KEP24zEE.js} +1 -1
- package/dist/client/assets/layout-KEP24zEE.js.br +0 -0
- package/dist/client/assets/layout-KEP24zEE.js.gz +0 -0
- package/dist/client/assets/{linear-BvPFVZ5r.js → linear-BwUXWDLn.js} +1 -1
- package/dist/client/assets/linear-BwUXWDLn.js.br +0 -0
- package/dist/client/assets/linear-BwUXWDLn.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-xgi3W3HJ.js → mermaid.core-DkzJYwWa.js} +5 -5
- package/dist/client/assets/mermaid.core-DkzJYwWa.js.br +0 -0
- package/dist/client/assets/mermaid.core-DkzJYwWa.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-C0Y8K-f6.js → mindmap-definition-YA3MSWOX-BE1H8WPd.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BE1H8WPd.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BE1H8WPd.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-dH2p1UXM.js → passwordBootstrap-Dhzj9-U2.js} +1 -1
- package/dist/client/assets/passwordBootstrap-Dhzj9-U2.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Dhzj9-U2.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DDphzW5Y.js → pegDiagram-XKGWAZYB-CorBwvfP.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-CorBwvfP.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-CorBwvfP.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-GDu--HqE.js → pieDiagram-E7YTZNPT-A017Uk-W.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-A017Uk-W.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-A017Uk-W.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-NSIlEYZd.js → quadrantDiagram-AXDQQJYC-CNNywvhG.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CNNywvhG.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CNNywvhG.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-BMr8_470.js → railroadDiagram-O6MQD6OU-DIxiWn5A.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DIxiWn5A.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DIxiWn5A.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-5KOTIE_2.js → referenceLongPress-Bp8q50sv.js} +1 -1
- package/dist/client/assets/referenceLongPress-Bp8q50sv.js.br +0 -0
- package/dist/client/assets/referenceLongPress-Bp8q50sv.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-BYfx-BnY.js → requirementDiagram-BXWQKSXE-Ob4lS23U.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Ob4lS23U.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Ob4lS23U.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-Sjw1moBs.js → sankeyDiagram-P5KCCOFB-Cc5e7umb.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Cc5e7umb.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Cc5e7umb.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BdB4bAuv.js → sequenceDiagram-WJ2MYXX4-CLTRKFht.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CLTRKFht.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CLTRKFht.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-Bqjs5SmV.js → sizeCapture-INFHLROL-h6hFhQw6.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-h6hFhQw6.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-h6hFhQw6.js.gz +0 -0
- package/dist/client/assets/{sparkles-BqYxpm2T.js → sparkles-9OJtJfyK.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-CeGiiS-A.js → stateDiagram-D77RDMKH-DSaJxX_r.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DSaJxX_r.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DSaJxX_r.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CQ9iS9Nl.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-CfKdzOcE.js → swimlanes-42K2YHIH-CP7ABWyE.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-CP7ABWyE.js.br +0 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-CfKdzOcE.js.gz → swimlanes-42K2YHIH-CP7ABWyE.js.gz} +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BbfCRvRt.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-BQHm94LP.js → timeline-definition-24CTP7MA-CdwVIVF6.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-CdwVIVF6.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-CdwVIVF6.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-9JebA9Zi.js → triangle-alert-BvT4KSYf.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CiDKvpY7.js → vennDiagram-4TSXK5OY-BXGgQFVY.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-BXGgQFVY.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BXGgQFVY.js.gz +0 -0
- package/dist/client/assets/{videoCompression-BZCAJl_H.js → videoCompression-BFXJyhpz.js} +1 -1
- package/dist/client/assets/videoCompression-BFXJyhpz.js.br +0 -0
- package/dist/client/assets/videoCompression-BFXJyhpz.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-DOUAhlES.js → wardleyDiagram-VM6X3IG4-CtLophqU.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CtLophqU.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CtLophqU.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BV7ihxFK.js → xychartDiagram-S5SC5T6Z-YZ9bnI1e.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-YZ9bnI1e.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-YZ9bnI1e.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/federation/trustedCa.js +9 -2
- package/dist/server/routes/filesystem.js +43 -1
- package/dist/server/routes/terminal.js +148 -19
- package/dist/server/utils/settings.js +14 -0
- package/dist/server/workers/nativeSessionSearchWorker.js +416 -0
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-DIYNv8nX.js +0 -157
- package/dist/client/assets/App-DIYNv8nX.js.br +0 -0
- package/dist/client/assets/App-DIYNv8nX.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-D3P0LWfU.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-D3P0LWfU.js.gz +0 -0
- package/dist/client/assets/DagPlayground-DHLTKfYe.js.br +0 -0
- package/dist/client/assets/DagPlayground-DHLTKfYe.js.gz +0 -0
- package/dist/client/assets/DiffLab-DyapQhFp.js.br +0 -0
- package/dist/client/assets/DiffLab-DyapQhFp.js.gz +0 -0
- package/dist/client/assets/DiffReview-CtlVXUuM.js.br +0 -0
- package/dist/client/assets/DiffReview-CtlVXUuM.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CcATFm0V.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CcATFm0V.js.gz +0 -0
- package/dist/client/assets/DiffViewer-DbAcE-rO.js +0 -37
- package/dist/client/assets/DiffViewer-DbAcE-rO.js.br +0 -0
- package/dist/client/assets/DiffViewer-DbAcE-rO.js.gz +0 -0
- package/dist/client/assets/ModelPreview-DFsugreu.js.br +0 -0
- package/dist/client/assets/ModelPreview-DFsugreu.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BljTZjyo.js +0 -1678
- package/dist/client/assets/RightSidebar-BljTZjyo.js.br +0 -0
- package/dist/client/assets/RightSidebar-BljTZjyo.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-C6Qn78Kl.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-C6Qn78Kl.js.gz +0 -0
- package/dist/client/assets/app-BT_37rUb.js +0 -35
- package/dist/client/assets/app-BT_37rUb.js.br +0 -0
- package/dist/client/assets/app-BT_37rUb.js.gz +0 -0
- package/dist/client/assets/arc-DlrdXzuL.js.br +0 -0
- package/dist/client/assets/arc-DlrdXzuL.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-yDm6ZlLB.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-yDm6ZlLB.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CQ_ev6HF.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CQ_ev6HF.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Xc0eRnWn.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Xc0eRnWn.js.gz +0 -0
- package/dist/client/assets/channel-JBVMoCs6.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-D4bPuUCL.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-D4bPuUCL.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CcpsYCGR.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CcpsYCGR.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BK9PycPu.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BK9PycPu.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-LPhjhi7S.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-LPhjhi7S.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-BkraiclO.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BkraiclO.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-BArSIiIO.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-BArSIiIO.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CCs1or7w.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CCs1or7w.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CZtiCwpH.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CZtiCwpH.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-sxi6dz48.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-sxi6dz48.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CqA9RxCi.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CqA9RxCi.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DI6HGWgF.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DI6HGWgF.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CMZi8lNh.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CMZi8lNh.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C7C5Wauj.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-C7C5Wauj.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-5gZWUza2.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-5gZWUza2.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-_UeIqZQ9.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-_UeIqZQ9.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Xmw2qbA7.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Xmw2qbA7.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-KRYKHFDk.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-KRYKHFDk.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-UcoHnpCx.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-UcoHnpCx.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-B6OZhQjV.js.br +0 -0
- package/dist/client/assets/gestureArbiter-B6OZhQjV.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-Evwvg64n.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-Evwvg64n.js.gz +0 -0
- package/dist/client/assets/index-IknuJMxJ.css +0 -1
- package/dist/client/assets/index-IknuJMxJ.css.br +0 -0
- package/dist/client/assets/index-IknuJMxJ.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-D1-1rWNL.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-D1-1rWNL.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DNF2Xeph.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DNF2Xeph.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-nNy4MKl1.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-nNy4MKl1.js.gz +0 -0
- package/dist/client/assets/layout-eOsGqyyy.js.br +0 -0
- package/dist/client/assets/layout-eOsGqyyy.js.gz +0 -0
- package/dist/client/assets/linear-BvPFVZ5r.js.br +0 -0
- package/dist/client/assets/linear-BvPFVZ5r.js.gz +0 -0
- package/dist/client/assets/mermaid.core-xgi3W3HJ.js.br +0 -0
- package/dist/client/assets/mermaid.core-xgi3W3HJ.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-C0Y8K-f6.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-C0Y8K-f6.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-dH2p1UXM.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-dH2p1UXM.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DDphzW5Y.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DDphzW5Y.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-GDu--HqE.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-GDu--HqE.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-NSIlEYZd.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-NSIlEYZd.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BMr8_470.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-BMr8_470.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-5KOTIE_2.js.br +0 -0
- package/dist/client/assets/referenceLongPress-5KOTIE_2.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BYfx-BnY.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-BYfx-BnY.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Sjw1moBs.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Sjw1moBs.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BdB4bAuv.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BdB4bAuv.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Bqjs5SmV.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Bqjs5SmV.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CeGiiS-A.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-CeGiiS-A.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-DAcs8J97.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-CfKdzOcE.js.br +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-ByPh86Cc.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-BQHm94LP.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-BQHm94LP.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CiDKvpY7.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CiDKvpY7.js.gz +0 -0
- package/dist/client/assets/videoCompression-BZCAJl_H.js.br +0 -0
- package/dist/client/assets/videoCompression-BZCAJl_H.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DOUAhlES.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DOUAhlES.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BV7ihxFK.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BV7ihxFK.js.gz +0 -0
|
@@ -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);
|
|
@@ -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)
|
|
@@ -25,7 +25,7 @@ import { getPtyHostManager } from '../ptyhost/manager.js';
|
|
|
25
25
|
import { pathValidator } from '../utils/pathValidator.js';
|
|
26
26
|
import { TERMINAL, TMUX } from '../config.js';
|
|
27
27
|
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';
|
|
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, getHideGitIgnoredRootsSetting, setHideGitIgnoredRootSetting, setFileSortModesSetting, setFileSortModeSetting, getNestedGitScanRootsSetting, setNestedGitScanRootSetting, getPinnedExplorerRootsSetting, setPinnedExplorerRootsSetting, setPinnedExplorerRootSetting, watchPinnedExplorerRootsSetting, getActiveGitReposSetting, setActiveGitRepoSetting, } from '../utils/settings.js';
|
|
29
29
|
import { loadContextDraft, saveContextDraft } from '../utils/contextDraft.js';
|
|
30
30
|
import { getOnboardingServerUrl } from '../onboardingServer.js';
|
|
31
31
|
import { inspectLinuxInotifyUsage, isWatchResourceExhaustion } from '../utils/fileWatchPolicy.js';
|
|
@@ -48,6 +48,8 @@ import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/col
|
|
|
48
48
|
import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
|
|
49
49
|
import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
50
50
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
51
|
+
import { NativeSessionSearch } from '../agent/nativeSessionSearch.js';
|
|
52
|
+
import { CollaborationError } from '../agent/collaborationProtocol.js';
|
|
51
53
|
import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
|
|
52
54
|
import { CollaborationRoutingStore, selectCollaborationPane, selectDrivePane } from '../agent/collaborationRouting.js';
|
|
53
55
|
import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
|
|
@@ -82,6 +84,7 @@ const collaborationDeliveryWorker = new CollaborationDeliveryWorker({
|
|
|
82
84
|
onError: (error) => console.warn('[collaboration] background delivery failed:', getErrorMessage(error)),
|
|
83
85
|
});
|
|
84
86
|
const sessionSearchStore = new SessionSearchStore(`${TERMDOCK_DIR}/session-search`);
|
|
87
|
+
const nativeSessionSearch = new NativeSessionSearch(`${TERMDOCK_DIR}/session-search/native`);
|
|
85
88
|
let atomicJsonWriteSequence = 0;
|
|
86
89
|
async function readJsonFileIfExists(filePath) {
|
|
87
90
|
try {
|
|
@@ -244,6 +247,9 @@ function getClientStateSemanticSignature(state, inventory) {
|
|
|
244
247
|
connected: session.connected,
|
|
245
248
|
live: session.live,
|
|
246
249
|
restorable: session.restorable,
|
|
250
|
+
activeProgram: session.activeProgram ?? null,
|
|
251
|
+
cwd: session.cwd ?? null,
|
|
252
|
+
shellTitle: session.shellTitle ?? null,
|
|
247
253
|
})),
|
|
248
254
|
tmuxSessions: inventory.tmuxSessions.map((session) => ({
|
|
249
255
|
name: session.name,
|
|
@@ -936,6 +942,9 @@ function normalizePersistedClientSession(input) {
|
|
|
936
942
|
activeProgram: typeof candidate.activeProgram === 'string' && candidate.activeProgram.trim().length > 0
|
|
937
943
|
? candidate.activeProgram
|
|
938
944
|
: null,
|
|
945
|
+
shellTitle: typeof candidate.shellTitle === 'string' && candidate.shellTitle.trim().length > 0
|
|
946
|
+
? candidate.shellTitle
|
|
947
|
+
: null,
|
|
939
948
|
agentResume: normalizePersistedAgentResumeInfo(candidate.agentResume),
|
|
940
949
|
};
|
|
941
950
|
}
|
|
@@ -2383,6 +2392,7 @@ async function buildSessionInventory() {
|
|
|
2383
2392
|
return {
|
|
2384
2393
|
...tmux,
|
|
2385
2394
|
program: metadata.program,
|
|
2395
|
+
shellTitle: metadata.shellTitle,
|
|
2386
2396
|
cwd: metadata.cwd,
|
|
2387
2397
|
label: metadata.label,
|
|
2388
2398
|
lastActiveAt: Date.now(),
|
|
@@ -2405,13 +2415,14 @@ async function buildSessionInventory() {
|
|
|
2405
2415
|
synchronizedPersistedFields = true;
|
|
2406
2416
|
}
|
|
2407
2417
|
}
|
|
2408
|
-
//
|
|
2409
|
-
// backend 掉线后 inventory hint
|
|
2418
|
+
// 展示名数据(程序 / 目录 / 终端标题)回写:last-known 持久化,
|
|
2419
|
+
// server 重启 / backend 掉线后 inventory hint 仍能给出上次的值。
|
|
2410
2420
|
// live 值只以非空覆盖——检测空窗(null)不抹掉已存结果。
|
|
2411
2421
|
const backend = next.backendSessionId ? terminalSessions.get(next.backendSessionId) : undefined;
|
|
2412
2422
|
const tmuxMeta = next.tmuxSessionName ? liveTmuxByName.get(next.tmuxSessionName) : undefined;
|
|
2413
2423
|
const liveProgram = backend?.activeProgram?.command ?? tmuxMeta?.program ?? null;
|
|
2414
2424
|
const liveCwd = backend?.cwd ?? tmuxMeta?.cwd ?? null;
|
|
2425
|
+
const liveShellTitle = tmuxMeta?.shellTitle ?? backend?.lastOscTitle ?? null;
|
|
2415
2426
|
if (liveProgram && liveProgram !== (next.activeProgram ?? null)) {
|
|
2416
2427
|
next = { ...next, activeProgram: liveProgram };
|
|
2417
2428
|
synchronizedPersistedFields = true;
|
|
@@ -2420,6 +2431,10 @@ async function buildSessionInventory() {
|
|
|
2420
2431
|
next = { ...next, cwd: liveCwd };
|
|
2421
2432
|
synchronizedPersistedFields = true;
|
|
2422
2433
|
}
|
|
2434
|
+
if (liveShellTitle && liveShellTitle !== (next.shellTitle ?? null)) {
|
|
2435
|
+
next = { ...next, shellTitle: liveShellTitle };
|
|
2436
|
+
synchronizedPersistedFields = true;
|
|
2437
|
+
}
|
|
2423
2438
|
return next;
|
|
2424
2439
|
}),
|
|
2425
2440
|
updatedAt: synchronizedPersistedFields ? Date.now() : globalSessionState.updatedAt,
|
|
@@ -2439,6 +2454,7 @@ async function buildSessionInventory() {
|
|
|
2439
2454
|
const tmuxMeta = session.tmuxSessionName ? liveTmuxByName.get(session.tmuxSessionName) : undefined;
|
|
2440
2455
|
const activeProgram = backend?.activeProgram?.command ?? tmuxMeta?.program ?? session.activeProgram ?? null;
|
|
2441
2456
|
const cwd = backend?.cwd ?? tmuxMeta?.cwd ?? session.cwd ?? null;
|
|
2457
|
+
const shellTitle = tmuxMeta?.shellTitle ?? backend?.lastOscTitle ?? session.shellTitle ?? null;
|
|
2442
2458
|
return {
|
|
2443
2459
|
...session,
|
|
2444
2460
|
frontendSessionId: session.sessionId,
|
|
@@ -2452,6 +2468,7 @@ async function buildSessionInventory() {
|
|
|
2452
2468
|
|| (session.mode === 'shell' && canRestoreDeadAgentShell(session))),
|
|
2453
2469
|
activeProgram,
|
|
2454
2470
|
cwd,
|
|
2471
|
+
shellTitle,
|
|
2455
2472
|
};
|
|
2456
2473
|
});
|
|
2457
2474
|
const clientByTmux = new Map();
|
|
@@ -3401,8 +3418,8 @@ async function findCodexSessionFile(nativeSessionId) {
|
|
|
3401
3418
|
return cachedPath;
|
|
3402
3419
|
if (!/^[a-zA-Z0-9._-]{8,160}$/.test(nativeSessionId))
|
|
3403
3420
|
return null;
|
|
3404
|
-
const
|
|
3405
|
-
const pending = [
|
|
3421
|
+
const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
3422
|
+
const pending = [path.join(codexHome, 'sessions'), path.join(codexHome, 'archived_sessions')];
|
|
3406
3423
|
let visited = 0;
|
|
3407
3424
|
while (pending.length > 0 && visited < 20_000) {
|
|
3408
3425
|
const directory = pending.pop();
|
|
@@ -3434,7 +3451,9 @@ async function findExternalCodexWriter(target) {
|
|
|
3434
3451
|
return null;
|
|
3435
3452
|
try {
|
|
3436
3453
|
const { stdout } = await execFileAsync('lsof', ['-t', '--', sessionFile], { timeout: 2500, maxBuffer: 64 * 1024 });
|
|
3437
|
-
|
|
3454
|
+
// Our search worker reads these files in this process; that read-only
|
|
3455
|
+
// descriptor must not be mistaken for an external Codex conversation owner.
|
|
3456
|
+
const pid = stdout.split(/\s+/).map(Number).find((value) => Number.isInteger(value) && value > 1 && value !== process.pid);
|
|
3438
3457
|
return pid ?? null;
|
|
3439
3458
|
}
|
|
3440
3459
|
catch {
|
|
@@ -3670,12 +3689,15 @@ async function resolveLiveTmuxMetadata(tmuxSessionName) {
|
|
|
3670
3689
|
const fallback = getActiveProgramFromTmuxLayout(layout);
|
|
3671
3690
|
const program = resolved?.command ?? fallback?.command ?? null;
|
|
3672
3691
|
const cwd = getCwdFromTmuxLayout(layout);
|
|
3673
|
-
return
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3692
|
+
return {
|
|
3693
|
+
...buildRuntimeTmuxMetadata({
|
|
3694
|
+
tmuxSessionName,
|
|
3695
|
+
program,
|
|
3696
|
+
cwd,
|
|
3697
|
+
rawArgs: resolved?.rawArgs ?? null,
|
|
3698
|
+
}),
|
|
3699
|
+
shellTitle: activePane.title || null,
|
|
3700
|
+
};
|
|
3679
3701
|
}
|
|
3680
3702
|
// ── label builder (mirrors the frontend `getSessionDisplayLines` semantics) ──
|
|
3681
3703
|
//
|
|
@@ -5571,6 +5593,31 @@ router.get('/operations/collaboration-directory', (req, res) => {
|
|
|
5571
5593
|
void service.refresh();
|
|
5572
5594
|
res.json(service.directory());
|
|
5573
5595
|
});
|
|
5596
|
+
router.delete('/operations/collaboration-connections/:serviceId', (req, res) => {
|
|
5597
|
+
try {
|
|
5598
|
+
assertPeerRegistrationAuthority(req);
|
|
5599
|
+
const service = req.app.locals.collaborationService;
|
|
5600
|
+
if (!service)
|
|
5601
|
+
return res.status(503).json({ error: '协作服务正在启动' });
|
|
5602
|
+
res.json(service.removePeer(req.params.serviceId));
|
|
5603
|
+
}
|
|
5604
|
+
catch (error) {
|
|
5605
|
+
res.status(error instanceof CollaborationError ? error.httpStatus : getErrorMessage(error) === 'AUTHORIZATION_DENIED' ? 403 : 400)
|
|
5606
|
+
.json({ error: getErrorMessage(error), ...(error instanceof CollaborationError ? { code: error.code, ...error.details } : {}) });
|
|
5607
|
+
}
|
|
5608
|
+
});
|
|
5609
|
+
router.post('/operations/collaboration-connections/:serviceId/restore', (req, res) => {
|
|
5610
|
+
try {
|
|
5611
|
+
assertPeerRegistrationAuthority(req);
|
|
5612
|
+
const service = req.app.locals.collaborationService;
|
|
5613
|
+
if (!service)
|
|
5614
|
+
return res.status(503).json({ error: '协作服务正在启动' });
|
|
5615
|
+
res.json(service.restorePeer(req.params.serviceId));
|
|
5616
|
+
}
|
|
5617
|
+
catch (error) {
|
|
5618
|
+
res.status(error instanceof CollaborationError ? error.httpStatus : getErrorMessage(error) === 'AUTHORIZATION_DENIED' ? 403 : 400).json({ error: getErrorMessage(error) });
|
|
5619
|
+
}
|
|
5620
|
+
});
|
|
5574
5621
|
export function collaborationDirectorySessions() { return globalSessionState.sessions.map(orchestrationSessionSnapshot); }
|
|
5575
5622
|
export function collaborationLocalActivity() {
|
|
5576
5623
|
return globalSessionState.sessions.map(record => {
|
|
@@ -5970,16 +6017,81 @@ router.use('/operations/orchestration', collaborationRoutes({ store: collaborati
|
|
|
5970
6017
|
broadcastClientState();
|
|
5971
6018
|
return { ok: true };
|
|
5972
6019
|
} }));
|
|
5973
|
-
router.get('/operations/session-search', (req, res) => {
|
|
5974
|
-
const query = typeof req.query.q === 'string' ? req.query.q : '';
|
|
5975
|
-
const
|
|
6020
|
+
router.get('/operations/session-search', async (req, res) => {
|
|
6021
|
+
const query = typeof req.query.q === 'string' ? req.query.q.trim().slice(0, 512) : '';
|
|
6022
|
+
const limit = Math.max(1, Math.min(5000, Math.floor(Number(req.query.limit)) || 50));
|
|
6023
|
+
const controller = new AbortController();
|
|
6024
|
+
const disconnected = () => { if (!res.writableEnded)
|
|
6025
|
+
controller.abort(); };
|
|
6026
|
+
res.once('close', disconnected);
|
|
6027
|
+
const terminalResults = sessionSearchStore.search(query, 100).map((result) => {
|
|
5976
6028
|
const live = globalSessionState.sessions.some((candidate) => candidate.sessionId === result.sessionId);
|
|
5977
6029
|
const resumeEntry = result.agentNativeSessionId
|
|
5978
|
-
? agentResumeHistory.list().find((entry) => entry.agent.sessionId === result.agentNativeSessionId)
|
|
6030
|
+
? agentResumeHistory.list().find((entry) => entry.agent.slug === result.agentSlug && entry.agent.sessionId === result.agentNativeSessionId)
|
|
5979
6031
|
: null;
|
|
5980
|
-
return { ...result, live, resumeHistoryId: resumeEntry?.id ?? null };
|
|
6032
|
+
return { ...result, source: 'terminal', nativeKey: null, live, resumeHistoryId: resumeEntry?.id ?? null };
|
|
5981
6033
|
});
|
|
5982
|
-
|
|
6034
|
+
try {
|
|
6035
|
+
// Preserve the names users see in Termdock as searchable aliases, including
|
|
6036
|
+
// custom titles for conversations that have already been closed.
|
|
6037
|
+
const titles = [
|
|
6038
|
+
...sessionSearchStore.search('', 100).flatMap(entry => entry.agentSlug && entry.agentNativeSessionId ? [{ agentSlug: entry.agentSlug, agentNativeSessionId: entry.agentNativeSessionId, title: entry.title }] : []),
|
|
6039
|
+
...agentResumeHistory.list().reverse().map(entry => ({ agentSlug: entry.agent.slug, agentNativeSessionId: entry.agent.sessionId, title: entry.title })),
|
|
6040
|
+
...globalSessionState.sessions.flatMap(entry => entry.customName && entry.agentResume?.sessionId ? [{ agentSlug: entry.agentResume.slug, agentNativeSessionId: entry.agentResume.sessionId, title: entry.name }] : []),
|
|
6041
|
+
];
|
|
6042
|
+
const native = await nativeSessionSearch.search(query, limit + terminalResults.length, titles, controller.signal);
|
|
6043
|
+
if (controller.signal.aborted)
|
|
6044
|
+
return;
|
|
6045
|
+
const matched = new Set(native.matchedSessions);
|
|
6046
|
+
const additional = terminalResults.filter(result => !result.agentNativeSessionId || !matched.has(`${result.agentSlug}:${result.agentNativeSessionId}`));
|
|
6047
|
+
const results = native.results.map(result => {
|
|
6048
|
+
const owner = findActiveAgentResumeOwner('', { slug: result.agentSlug, nativeSessionId: result.agentNativeSessionId, command: '' });
|
|
6049
|
+
const liveRecord = owner ? globalSessionState.sessions.find(candidate => candidate.backendSessionId === owner) : null;
|
|
6050
|
+
return { ...result, source: result.agentSlug, nativeKey: result.key, sessionId: liveRecord?.sessionId || `native:${result.key}`,
|
|
6051
|
+
live: Boolean(liveRecord), resumeHistoryId: null };
|
|
6052
|
+
});
|
|
6053
|
+
res.json({ query, results: [...results, ...additional].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, limit), total: native.total + additional.length, index: native.index });
|
|
6054
|
+
}
|
|
6055
|
+
catch (error) {
|
|
6056
|
+
if (controller.signal.aborted)
|
|
6057
|
+
return;
|
|
6058
|
+
res.json({ query, results: terminalResults.slice(0, limit), total: terminalResults.length,
|
|
6059
|
+
index: { total: 0, indexed: 0, building: false, failed: 0, warning: `历史会话暂时无法搜索:${getErrorMessage(error)}` } });
|
|
6060
|
+
}
|
|
6061
|
+
finally {
|
|
6062
|
+
res.off('close', disconnected);
|
|
6063
|
+
}
|
|
6064
|
+
});
|
|
6065
|
+
router.post('/operations/session-search/prepare', async (req, res) => {
|
|
6066
|
+
const key = typeof req.body?.nativeKey === 'string' ? req.body.nativeKey : '';
|
|
6067
|
+
if (!/^[a-f0-9]{64}$/.test(key))
|
|
6068
|
+
return res.status(400).json({ error: '无效的历史会话' });
|
|
6069
|
+
try {
|
|
6070
|
+
// Resolve the original file and command on the server; never accept a
|
|
6071
|
+
// transcript path, working directory or shell command from search clients.
|
|
6072
|
+
const entry = await nativeSessionSearch.get(key);
|
|
6073
|
+
if (!entry)
|
|
6074
|
+
return res.status(404).json({ error: '这条历史会话已不存在,请重新搜索' });
|
|
6075
|
+
const agent = agentBySlug(entry.agentSlug);
|
|
6076
|
+
const history = agentResumeHistory.list().find(candidate => candidate.agent.slug === entry.agentSlug && candidate.agent.sessionId === entry.agentNativeSessionId);
|
|
6077
|
+
const command = agent ? buildResumeCommand(agent, entry.agentNativeSessionId, history?.agent.launchArgv) : null;
|
|
6078
|
+
if (!command)
|
|
6079
|
+
return res.status(410).json({ error: '当前 Agent 不支持恢复这条历史会话' });
|
|
6080
|
+
const target = { slug: entry.agentSlug, nativeSessionId: entry.agentNativeSessionId, command };
|
|
6081
|
+
const owner = findActiveAgentResumeOwner('', target);
|
|
6082
|
+
const liveRecord = owner ? globalSessionState.sessions.find(candidate => candidate.backendSessionId === owner) : null;
|
|
6083
|
+
if (liveRecord)
|
|
6084
|
+
return res.json({ sessionId: liveRecord.sessionId });
|
|
6085
|
+
if (owner || await findExternalCodexWriter(target))
|
|
6086
|
+
return res.status(409).json({ error: '这条会话已在其他终端运行,请先切换到原终端或关闭后恢复', code: 'AGENT_SESSION_ACTIVE_ELSEWHERE' });
|
|
6087
|
+
if (!entry.cwd)
|
|
6088
|
+
return res.status(409).json({ error: '尚未找到这条会话的项目目录,请等待索引完成后重试' });
|
|
6089
|
+
const cwd = await resolveWorkingDirectory(req, entry.cwd);
|
|
6090
|
+
res.json({ command, cwd, title: entry.title, agent });
|
|
6091
|
+
}
|
|
6092
|
+
catch (error) {
|
|
6093
|
+
res.status(400).json({ error: `无法恢复会话:${getErrorMessage(error)}` });
|
|
6094
|
+
}
|
|
5983
6095
|
});
|
|
5984
6096
|
router.get('/agent-resume-history', (_req, res) => {
|
|
5985
6097
|
const entries = agentResumeHistory.list().flatMap((entry) => {
|
|
@@ -6221,6 +6333,7 @@ async function getSettingsPayload() {
|
|
|
6221
6333
|
collaborationFloatingGroupId: getCollaborationFloatingGroupIdSetting(),
|
|
6222
6334
|
serviceSwitcherExpanded: getServiceSwitcherExpandedSetting(),
|
|
6223
6335
|
fileSortModes: getFileSortModesSetting(),
|
|
6336
|
+
hideGitIgnoredRoots: getHideGitIgnoredRootsSetting(),
|
|
6224
6337
|
nestedGitScanRoots: getNestedGitScanRootsSetting(),
|
|
6225
6338
|
activeGitRepos: getActiveGitReposSetting(),
|
|
6226
6339
|
pinnedExplorerRoots: getPinnedExplorerRootsSetting(),
|
|
@@ -6410,6 +6523,18 @@ router.put('/settings', async (req, res) => {
|
|
|
6410
6523
|
}
|
|
6411
6524
|
setFileSortModeSetting(preference.path, preference.mode);
|
|
6412
6525
|
}
|
|
6526
|
+
if (body.hideGitIgnoredRoot && typeof body.hideGitIgnoredRoot === 'object') {
|
|
6527
|
+
const preference = body.hideGitIgnoredRoot;
|
|
6528
|
+
const validPath = typeof preference.rootPath === 'string'
|
|
6529
|
+
&& preference.rootPath.length > 0
|
|
6530
|
+
&& preference.rootPath.length <= 4096
|
|
6531
|
+
&& (preference.rootPath.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(preference.rootPath));
|
|
6532
|
+
if (!validPath || typeof preference.enabled !== 'boolean') {
|
|
6533
|
+
res.status(400).json({ error: 'Invalid Git ignore visibility preference', code: 'GIT_IGNORE_VISIBILITY_INVALID' });
|
|
6534
|
+
return;
|
|
6535
|
+
}
|
|
6536
|
+
setHideGitIgnoredRootSetting(preference.rootPath, preference.enabled);
|
|
6537
|
+
}
|
|
6413
6538
|
if (body.nestedGitScanRoot && typeof body.nestedGitScanRoot === 'object') {
|
|
6414
6539
|
const preference = body.nestedGitScanRoot;
|
|
6415
6540
|
const isAbsoluteRootPath = (value) => typeof value === 'string'
|
|
@@ -8396,8 +8521,12 @@ async function reconcileClientState() {
|
|
|
8396
8521
|
}
|
|
8397
8522
|
}
|
|
8398
8523
|
}
|
|
8399
|
-
if (toRemove.length === 0 && toDetach.length === 0)
|
|
8524
|
+
if (toRemove.length === 0 && toDetach.length === 0) {
|
|
8525
|
+
// The refreshed inventory may contain new titles for unopened panes.
|
|
8526
|
+
// Semantic deduplication suppresses broadcasts when nothing changed.
|
|
8527
|
+
broadcastClientState();
|
|
8400
8528
|
return;
|
|
8529
|
+
}
|
|
8401
8530
|
const detached = new Set(toDetach);
|
|
8402
8531
|
globalSessionState = {
|
|
8403
8532
|
sessions: globalSessionState.sessions
|