termdock 1.4.178 → 1.4.180
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 +1 -1
- package/dist/client/assets/App-DBds0RoW.js +150 -0
- package/dist/client/assets/App-DBds0RoW.js.br +0 -0
- package/dist/client/assets/App-DBds0RoW.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BNWqAnqc.js → ChangeWalkthroughPanel-DonkfMV2.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-DonkfMV2.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DonkfMV2.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-IlTGwr-I.js → DagPlayground-CYL8vhaU.js} +1 -1
- package/dist/client/assets/DagPlayground-CYL8vhaU.js.br +0 -0
- package/dist/client/assets/DagPlayground-CYL8vhaU.js.gz +0 -0
- package/dist/client/assets/{DiffLab-CwhNpJvo.js → DiffLab-COUjebir.js} +1 -1
- package/dist/client/assets/DiffLab-COUjebir.js.br +0 -0
- package/dist/client/assets/DiffLab-COUjebir.js.gz +0 -0
- package/dist/client/assets/{DiffReview-DyhINv0F.js → DiffReview-DqiRXomQ.js} +1 -1
- package/dist/client/assets/DiffReview-DqiRXomQ.js.br +0 -0
- package/dist/client/assets/DiffReview-DqiRXomQ.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-D9Kt5VwB.js → DiffReviewLab-Aqib8Qr6.js} +1 -1
- package/dist/client/assets/DiffReviewLab-Aqib8Qr6.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-Aqib8Qr6.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-3zz2PI-8.js → DiffViewer-KR2GhX72.js} +1 -1
- package/dist/client/assets/DiffViewer-KR2GhX72.js.br +0 -0
- package/dist/client/assets/DiffViewer-KR2GhX72.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-CN8xsiGC.js → ModelPreview-Bo6giTPm.js} +1 -1
- package/dist/client/assets/ModelPreview-Bo6giTPm.js.br +0 -0
- package/dist/client/assets/ModelPreview-Bo6giTPm.js.gz +0 -0
- package/dist/client/assets/RightSidebar-rROjRuPY.js +1678 -0
- package/dist/client/assets/RightSidebar-rROjRuPY.js.br +0 -0
- package/dist/client/assets/RightSidebar-rROjRuPY.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-DkdoJ2ZH.js → abnfDiagram-VCTEODGH-m85vHRkF.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-m85vHRkF.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-m85vHRkF.js.gz +0 -0
- package/dist/client/assets/app-Djvvt1Mi.js +34 -0
- package/dist/client/assets/app-Djvvt1Mi.js.br +0 -0
- package/dist/client/assets/app-Djvvt1Mi.js.gz +0 -0
- package/dist/client/assets/{arc-ctzz5PcK.js → arc-DmZ3EVhj.js} +1 -1
- package/dist/client/assets/arc-DmZ3EVhj.js.br +0 -0
- package/dist/client/assets/arc-DmZ3EVhj.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-DNLcYfGQ.js → architectureDiagram-5GKGNRK7-C8yGaNGM.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C8yGaNGM.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-C8yGaNGM.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-Coye1ou5.js → blockDiagram-I7D4REHJ-CZhh__cR.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-CZhh__cR.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CZhh__cR.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DTa8jyPp.js → c4Diagram-7LVT6UL2-py7O2OOO.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-py7O2OOO.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-py7O2OOO.js.gz +0 -0
- package/dist/client/assets/channel-BnKOV34C.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BpDyjNfs.js → chunk-2Q5K7J3B-SXGoQcYW.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-DgwdqXKl.js → chunk-5VM5RSS4-1ZVyPSGv.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DWf2gZ96.js → chunk-F27PBJKO-DkIqnjYj.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-DkIqnjYj.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DkIqnjYj.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-l42o_E2c.js → chunk-IMKFNOWR-CLS_EByq.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-CLS_EByq.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-CLS_EByq.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-8CkEf05l.js → chunk-JWPE2WC7-8LtK4UYW.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-BKKOfbdg.js → chunk-POPQ4Y6H-D1PhOQEv.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-RORrf2aM.js → chunk-SVP7TREG-cVWIfR_4.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-cVWIfR_4.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-cVWIfR_4.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-hPqafEtC.js → chunk-TICWLB2K-BiO8AIZ7.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-BiO8AIZ7.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BiO8AIZ7.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-CrvjJwms.js → chunk-XXDRQBXY-CwP0rAj8.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CZ5ElqWa.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CZ5ElqWa.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CwRSiQLa.js → cose-bilkent-JH36ORCC-Dv9GK27H.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Dv9GK27H.js.br +0 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CwRSiQLa.js.gz → cose-bilkent-JH36ORCC-Dv9GK27H.js.gz} +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CXnppZ8I.js → cynefin-OW5HDTMX-DDI6luT6.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-DDI6luT6.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-DDI6luT6.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BSDrHSIA.js → cynefinDiagram-5FMLGOSQ-DQpaBQcE.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DQpaBQcE.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DQpaBQcE.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-d-5j4vkq.js → dagre-GXQ25YYZ-CZ-1rzmK.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-CZ-1rzmK.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CZ-1rzmK.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-DizMBpGW.js → diagram-S7CK7UJ4-DeYvGv1c.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-DeYvGv1c.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DeYvGv1c.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-CNQK26rH.js → diagram-UQ7AKVKN-BaYQC1t6.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-BaYQC1t6.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BaYQC1t6.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-Dua-2_46.js → diagram-VSXAHHWV-B517BnK0.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-B517BnK0.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-B517BnK0.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-CIq-4729.js → diagram-VX7I27RA-DyjVlDcv.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-DyjVlDcv.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DyjVlDcv.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-BukQ0u-x.js → diagram-Z3DM3KII-Cy9JXHOz.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-Cy9JXHOz.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-Cy9JXHOz.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-D2zRkRBI.js → ebnfDiagram-PWID7BFC-xvEH045b.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-xvEH045b.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-xvEH045b.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DbX6A3ls.js → erDiagram-RLTQ6QDP-BTJDgwKq.js} +1 -1
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DbX6A3ls.js.br → erDiagram-RLTQ6QDP-BTJDgwKq.js.br} +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BTJDgwKq.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-YECDWA2G.js → flowDiagram-HODETNUW-6pq1SF-S.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-6pq1SF-S.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-6pq1SF-S.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-D3EJdUov.js → ganttDiagram-EL5Y4UJY-DS_l5v9m.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DS_l5v9m.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DS_l5v9m.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-D3IZpZS7.js → gestureArbiter-FtwbRpdp.js} +1 -1
- package/dist/client/assets/gestureArbiter-FtwbRpdp.js.br +0 -0
- package/dist/client/assets/gestureArbiter-FtwbRpdp.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-tr_HKoQK.js → gitGraphDiagram-WWUBYQGX-DEGFo3aa.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DEGFo3aa.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DEGFo3aa.js.gz +0 -0
- package/dist/client/assets/{index-C4Ny0NDQ.css → index-PWu1VEN_.css} +1 -1
- package/dist/client/assets/index-PWu1VEN_.css.br +0 -0
- package/dist/client/assets/index-PWu1VEN_.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-xBV63YYg.js → infoDiagram-27XIBGKW-CFVSyJdD.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BrYSZu1R.js → ishikawaDiagram-5VMMS53U-_YX0U_wu.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-_YX0U_wu.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-_YX0U_wu.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-C9Ft--aA.js → journeyDiagram-3NMN7TZE-DCujswss.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DCujswss.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DCujswss.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-FCxqSu11.js → kanban-definition-UXKFOSKX-DC6lqIBi.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-DC6lqIBi.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-DC6lqIBi.js.gz +0 -0
- package/dist/client/assets/{layout-BqjIyPqO.js → layout-D_hlL0MC.js} +1 -1
- package/dist/client/assets/layout-D_hlL0MC.js.br +0 -0
- package/dist/client/assets/layout-D_hlL0MC.js.gz +0 -0
- package/dist/client/assets/{linear-CEAGQX5N.js → linear-BxpjJ2yt.js} +1 -1
- package/dist/client/assets/linear-BxpjJ2yt.js.br +0 -0
- package/dist/client/assets/linear-BxpjJ2yt.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-DvAY78t_.js → mermaid.core-BRLYB1Uj.js} +5 -5
- package/dist/client/assets/mermaid.core-BRLYB1Uj.js.br +0 -0
- package/dist/client/assets/mermaid.core-BRLYB1Uj.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-D7_qIQ9H.js → mindmap-definition-YA3MSWOX-BF2f-Z54.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BF2f-Z54.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BF2f-Z54.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-Dksfdvk3.js → passwordBootstrap-hj2Lcgoj.js} +1 -1
- package/dist/client/assets/passwordBootstrap-hj2Lcgoj.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-hj2Lcgoj.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-DeTIBKGC.js → pegDiagram-XKGWAZYB-FxHPBhCy.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-FxHPBhCy.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-FxHPBhCy.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-z5IcoVwx.js → pieDiagram-E7YTZNPT-DqAcArGH.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqAcArGH.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqAcArGH.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B-zCZvES.js → quadrantDiagram-AXDQQJYC-31lk30-Y.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B-zCZvES.js.br → quadrantDiagram-AXDQQJYC-31lk30-Y.js.br} +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-31lk30-Y.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-DbpeB7P2.js → railroadDiagram-O6MQD6OU-C-Jt17Wl.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-C-Jt17Wl.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-C-Jt17Wl.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-p56g9xFb.js → referenceLongPress-BPFSnCDQ.js} +1 -1
- package/dist/client/assets/referenceLongPress-BPFSnCDQ.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BPFSnCDQ.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-UhJYMJ_U.js → requirementDiagram-BXWQKSXE-DdrbsuCJ.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DdrbsuCJ.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DdrbsuCJ.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-xKsuFR17.js → sankeyDiagram-P5KCCOFB-CZjs9KFo.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZjs9KFo.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZjs9KFo.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DrjBl65C.js → sequenceDiagram-WJ2MYXX4-CkD1f6v4.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CkD1f6v4.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CkD1f6v4.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-BIlGJ9tm.js → sizeCapture-INFHLROL-BMuKV0gL.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-BMuKV0gL.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BMuKV0gL.js.gz +0 -0
- package/dist/client/assets/{sparkles-B-uEi0Pa.js → sparkles-DuRvUCWG.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-C3k9XvA6.js → stateDiagram-D77RDMKH-B1lLo2WA.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-B1lLo2WA.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-B1lLo2WA.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-F5MbngH8.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-BN-x1rXp.js → swimlanes-42K2YHIH-CfC5CNjA.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-CfC5CNjA.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-CfC5CNjA.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BqbZwNvL.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-Dcedjtic.js → timeline-definition-24CTP7MA-0mM-Skdo.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-0mM-Skdo.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-0mM-Skdo.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-BSNAwyll.js → triangle-alert-wyGdnPOg.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-DAIk-5Rt.js → vennDiagram-4TSXK5OY-BYsJo_hU.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-BYsJo_hU.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BYsJo_hU.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-3iXOTpAg.js → wardleyDiagram-VM6X3IG4-ree3u-pI.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-ree3u-pI.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-ree3u-pI.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-OD1a_9Qg.js → xychartDiagram-S5SC5T6Z-DDono4Wc.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DDono4Wc.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DDono4Wc.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-downloads.js +40 -0
- package/dist/client/sw-downloads.js.br +0 -0
- package/dist/client/sw-downloads.js.gz +0 -0
- package/dist/client/sw-encrypted-background.js +25 -0
- package/dist/client/sw-encrypted-background.js.br +0 -0
- package/dist/client/sw-encrypted-background.js.gz +0 -0
- package/dist/client/sw-notifications.js +11 -43
- package/dist/client/sw-notifications.js.br +0 -0
- package/dist/client/sw-notifications.js.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 +10 -3
- package/dist/server/agent/collaborationDeliveryWorker.js +154 -0
- package/dist/server/agent/collaborationRoutes.js +15 -4
- package/dist/server/agent/collaborationRouting.js +94 -0
- package/dist/server/agent/collaborationStore.js +6 -1
- package/dist/server/agent/collaborationTmuxDelivery.js +10 -0
- package/dist/server/agent/sessionBindingRecovery.js +80 -0
- package/dist/server/cli.js +9 -5
- package/dist/server/entry.js +32 -13
- package/dist/server/federation/desktopTargets.js +64 -0
- package/dist/server/federation/directRoutes.js +27 -19
- package/dist/server/federation/requestContext.js +7 -1
- package/dist/server/federation/routeAccess.js +14 -7
- package/dist/server/federation/runtime.js +5 -3
- package/dist/server/federation/trustedCa.js +55 -0
- package/dist/server/notifications/pushService.js +3 -0
- package/dist/server/routes/notifications.js +4 -0
- package/dist/server/routes/terminal.js +235 -78
- package/dist/server/utils/publicSecurity.js +1 -1
- package/package.json +2 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-CAEtVL8Z.js +0 -150
- package/dist/client/assets/App-CAEtVL8Z.js.br +0 -0
- package/dist/client/assets/App-CAEtVL8Z.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BNWqAnqc.js.gz +0 -0
- package/dist/client/assets/DagPlayground-IlTGwr-I.js.br +0 -0
- package/dist/client/assets/DagPlayground-IlTGwr-I.js.gz +0 -0
- package/dist/client/assets/DiffLab-CwhNpJvo.js.br +0 -0
- package/dist/client/assets/DiffLab-CwhNpJvo.js.gz +0 -0
- package/dist/client/assets/DiffReview-DyhINv0F.js.br +0 -0
- package/dist/client/assets/DiffReview-DyhINv0F.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-D9Kt5VwB.js.gz +0 -0
- package/dist/client/assets/DiffViewer-3zz2PI-8.js.br +0 -0
- package/dist/client/assets/DiffViewer-3zz2PI-8.js.gz +0 -0
- package/dist/client/assets/ModelPreview-CN8xsiGC.js.br +0 -0
- package/dist/client/assets/ModelPreview-CN8xsiGC.js.gz +0 -0
- package/dist/client/assets/RightSidebar-DQk1cwKH.js +0 -79
- package/dist/client/assets/RightSidebar-DQk1cwKH.js.br +0 -0
- package/dist/client/assets/RightSidebar-DQk1cwKH.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-DkdoJ2ZH.js.gz +0 -0
- package/dist/client/assets/app-B42CoF1r.js +0 -34
- package/dist/client/assets/app-B42CoF1r.js.br +0 -0
- package/dist/client/assets/app-B42CoF1r.js.gz +0 -0
- package/dist/client/assets/arc-ctzz5PcK.js.br +0 -0
- package/dist/client/assets/arc-ctzz5PcK.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DNLcYfGQ.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-Coye1ou5.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DTa8jyPp.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DTa8jyPp.js.gz +0 -0
- package/dist/client/assets/channel-CzvSDv06.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DWf2gZ96.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-l42o_E2c.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-RORrf2aM.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-hPqafEtC.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-hPqafEtC.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CDknJfE0.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CDknJfE0.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CwRSiQLa.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CXnppZ8I.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BSDrHSIA.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-d-5j4vkq.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-DizMBpGW.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CNQK26rH.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-Dua-2_46.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-CIq-4729.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BukQ0u-x.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D2zRkRBI.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbX6A3ls.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-YECDWA2G.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-D3EJdUov.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-D3IZpZS7.js.br +0 -0
- package/dist/client/assets/gestureArbiter-D3IZpZS7.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tr_HKoQK.js.gz +0 -0
- package/dist/client/assets/index-C4Ny0NDQ.css.br +0 -0
- package/dist/client/assets/index-C4Ny0NDQ.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BrYSZu1R.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-C9Ft--aA.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-FCxqSu11.js.gz +0 -0
- package/dist/client/assets/layout-BqjIyPqO.js.br +0 -0
- package/dist/client/assets/layout-BqjIyPqO.js.gz +0 -0
- package/dist/client/assets/linear-CEAGQX5N.js.br +0 -0
- package/dist/client/assets/linear-CEAGQX5N.js.gz +0 -0
- package/dist/client/assets/mermaid.core-DvAY78t_.js.br +0 -0
- package/dist/client/assets/mermaid.core-DvAY78t_.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D7_qIQ9H.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-Dksfdvk3.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DeTIBKGC.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-z5IcoVwx.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B-zCZvES.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-DbpeB7P2.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-p56g9xFb.js.br +0 -0
- package/dist/client/assets/referenceLongPress-p56g9xFb.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-UhJYMJ_U.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-xKsuFR17.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DrjBl65C.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-BIlGJ9tm.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-C3k9XvA6.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-B7GnCu2o.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BN-x1rXp.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BH_gm34Y.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-Dcedjtic.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DAIk-5Rt.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-3iXOTpAg.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-OD1a_9Qg.js.gz +0 -0
|
@@ -39,6 +39,10 @@ import { CollaborationStore } from '../agent/collaborationStore.js';
|
|
|
39
39
|
import { formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
|
|
40
40
|
import { resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
|
|
41
41
|
import { SessionSearchStore } from '../agent/sessionSearchStore.js';
|
|
42
|
+
import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
|
|
43
|
+
import { CollaborationRoutingStore, selectCollaborationPane } from '../agent/collaborationRouting.js';
|
|
44
|
+
import { CollaborationDeliveryWorker } from '../agent/collaborationDeliveryWorker.js';
|
|
45
|
+
import { writeCollaborationTmuxPane } from '../agent/collaborationTmuxDelivery.js';
|
|
42
46
|
import { listAllHookAgents, refreshStaleHooksAtLaunch, installHooksForSlug, uninstallHooksForSlug, } from '../agent/installers.js';
|
|
43
47
|
import { loadPlugins, savePlugin, removePlugin, readPluginIcon, validateManifest, } from '../agent/plugins.js';
|
|
44
48
|
import { TmuxLifecycleCoordinator } from '../utils/tmuxLifecycle.js';
|
|
@@ -60,6 +64,14 @@ const execFileAsync = promisify(execFile);
|
|
|
60
64
|
const TERMDOCK_DIR = `${os.homedir()}/.termdock`;
|
|
61
65
|
const automationStore = new AutomationStore(`${TERMDOCK_DIR}/automations.json`);
|
|
62
66
|
const collaborationStore = new CollaborationStore(`${TERMDOCK_DIR}/collaboration-groups.json`);
|
|
67
|
+
const collaborationRouting = new CollaborationRoutingStore(`${TERMDOCK_DIR}/collaboration-routing.json`);
|
|
68
|
+
const collaborationDeliveryWorker = new CollaborationDeliveryWorker({
|
|
69
|
+
store: collaborationStore,
|
|
70
|
+
peers: () => collaborationStore.list().filter((group) => !group.deleted).flatMap((group) => group.sessionIds),
|
|
71
|
+
isLocal: (id) => !id.startsWith('remote:'),
|
|
72
|
+
resolve: resolveCollaborationRoute,
|
|
73
|
+
onError: (error) => console.warn('[collaboration] background delivery failed:', getErrorMessage(error)),
|
|
74
|
+
});
|
|
63
75
|
const sessionSearchStore = new SessionSearchStore(`${TERMDOCK_DIR}/session-search`);
|
|
64
76
|
let atomicJsonWriteSequence = 0;
|
|
65
77
|
async function readJsonFileIfExists(filePath) {
|
|
@@ -572,6 +584,7 @@ void (async () => {
|
|
|
572
584
|
});
|
|
573
585
|
pruneOrphanSessions();
|
|
574
586
|
await backfillPersistedTmuxMetadata();
|
|
587
|
+
collaborationDeliveryWorker.start();
|
|
575
588
|
})();
|
|
576
589
|
caffeinateManager.startNetworkMonitor();
|
|
577
590
|
// 清理磁盘恢复后后端已不存在的 session 引用。
|
|
@@ -1002,6 +1015,16 @@ function upsertGlobalSessionRecord(record) {
|
|
|
1002
1015
|
sessions: deduplicateGlobalSessions(next),
|
|
1003
1016
|
updatedAt: Date.now(),
|
|
1004
1017
|
};
|
|
1018
|
+
// Capture server-observed bindings when sessions are created/adopted. Client
|
|
1019
|
+
// projections may later lose their backend id; the routing registry must not.
|
|
1020
|
+
if (normalized.backendSessionId && terminalSessions.has(normalized.backendSessionId)) {
|
|
1021
|
+
try {
|
|
1022
|
+
resolveOrchestrationBackend(normalized);
|
|
1023
|
+
}
|
|
1024
|
+
catch (error) {
|
|
1025
|
+
console.warn('[collaboration] binding persistence failed:', getErrorMessage(error));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1005
1028
|
return normalized;
|
|
1006
1029
|
}
|
|
1007
1030
|
function removeGlobalSessionRecord(frontendSessionId) {
|
|
@@ -1011,8 +1034,10 @@ function removeGlobalSessionRecord(frontendSessionId) {
|
|
|
1011
1034
|
updatedAt: Date.now(),
|
|
1012
1035
|
};
|
|
1013
1036
|
const changed = globalSessionState.sessions.length !== before;
|
|
1014
|
-
if (changed)
|
|
1037
|
+
if (changed) {
|
|
1015
1038
|
collaborationStore.removeSession(frontendSessionId);
|
|
1039
|
+
collaborationRouting.remove(frontendSessionId);
|
|
1040
|
+
}
|
|
1016
1041
|
return changed;
|
|
1017
1042
|
}
|
|
1018
1043
|
function archiveAgentResumeRecord(record, reason) {
|
|
@@ -1263,19 +1288,48 @@ async function openInventorySession(req, input) {
|
|
|
1263
1288
|
reused,
|
|
1264
1289
|
};
|
|
1265
1290
|
}
|
|
1291
|
+
function resolveOrchestrationBackend(record) {
|
|
1292
|
+
const recovered = resolveCollaborationBackend(record, globalSessionState.sessions, terminalSessions, collaborationRouting);
|
|
1293
|
+
if (!recovered)
|
|
1294
|
+
return null;
|
|
1295
|
+
const [backendSessionId, backend] = recovered;
|
|
1296
|
+
const changed = record.backendSessionId !== backendSessionId || record.tmuxSessionName !== backend.tmuxSessionName || record.mode !== backend.mode;
|
|
1297
|
+
record.backendSessionId = backendSessionId;
|
|
1298
|
+
record.mode = backend.mode;
|
|
1299
|
+
record.tmuxSessionName = backend.tmuxSessionName;
|
|
1300
|
+
record.cwd = backend.cwd ?? record.cwd;
|
|
1301
|
+
if (changed) {
|
|
1302
|
+
globalSessionState.updatedAt = Date.now();
|
|
1303
|
+
schedulePersistGlobalState();
|
|
1304
|
+
const metadata = searchMetadataForBackend(backendSessionId, backend);
|
|
1305
|
+
if (metadata)
|
|
1306
|
+
sessionSearchStore.update(metadata);
|
|
1307
|
+
}
|
|
1308
|
+
return backend;
|
|
1309
|
+
}
|
|
1266
1310
|
function orchestrationSessionSnapshot(record) {
|
|
1267
|
-
const
|
|
1268
|
-
const
|
|
1269
|
-
const
|
|
1311
|
+
const binding = collaborationRouting.get(record.sessionId);
|
|
1312
|
+
const backendId = binding?.backendSessionId ?? record.backendSessionId;
|
|
1313
|
+
const backend = backendId ? terminalSessions.get(backendId) : null;
|
|
1314
|
+
const route = collaborationDeliveryWorker.state(record.sessionId);
|
|
1315
|
+
const slug = binding?.pane?.agentSlug ?? binding?.agentSlug ?? record.agentResume?.slug;
|
|
1316
|
+
const agent = slug ? agentBySlug(slug) : backend?.agent;
|
|
1317
|
+
const sameNativeSession = !binding?.pane || Boolean(binding.pane.nativeSessionId
|
|
1318
|
+
&& binding.pane.nativeSessionId === backend?.agentSession?.sessionId && binding.pane.agentSlug === backend?.agent?.slug);
|
|
1319
|
+
const turnState = sameNativeSession ? backend?.agentSession?.status : undefined;
|
|
1320
|
+
const latestPrompt = sameNativeSession ? backend?.autoTitlePromptPayloads.at(-1)?.trim() ?? '' : '';
|
|
1270
1321
|
return {
|
|
1271
|
-
...collaborationStore.sessionFacts(record.sessionId,
|
|
1322
|
+
...collaborationStore.sessionFacts(record.sessionId, route.state === 'ready', turnState),
|
|
1323
|
+
route_state: route.state,
|
|
1324
|
+
route_error: route.reason,
|
|
1325
|
+
route_checked_at: route.checkedAt,
|
|
1272
1326
|
sessionId: record.sessionId,
|
|
1273
|
-
agentNativeSessionId: backend?.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
|
|
1274
|
-
backendSessionId:
|
|
1327
|
+
agentNativeSessionId: binding?.pane ? binding.pane.nativeSessionId : binding?.nativeSessionId ?? backend?.agentSession?.sessionId ?? record.agentResume?.sessionId ?? null,
|
|
1328
|
+
backendSessionId: backendId ?? null,
|
|
1275
1329
|
name: record.name,
|
|
1276
1330
|
cwd: backend?.cwd ?? record.cwd ?? '',
|
|
1277
1331
|
agent: agent ? { slug: agent.slug, displayName: agent.displayName } : null,
|
|
1278
|
-
status:
|
|
1332
|
+
status: route.state === 'ready' ? turnState ?? 'ready' : route.state,
|
|
1279
1333
|
capability: agent
|
|
1280
1334
|
? [agent.displayName, ...(agent.capabilities ?? []), backend?.activeProgram?.command || record.activeProgram || 'Agent 会话'].join(' · ')
|
|
1281
1335
|
: (backend?.activeProgram?.command || record.activeProgram || 'Shell 终端'),
|
|
@@ -1356,17 +1410,7 @@ async function runAgentAutomation(automation, req) {
|
|
|
1356
1410
|
}
|
|
1357
1411
|
}
|
|
1358
1412
|
function resolveFrontendSessionId(input) {
|
|
1359
|
-
|
|
1360
|
-
if (sessionId && globalSessionState.sessions.some((record) => record.sessionId === sessionId))
|
|
1361
|
-
return sessionId;
|
|
1362
|
-
const backendSessionId = typeof input.backendSessionId === 'string' ? input.backendSessionId.trim() : '';
|
|
1363
|
-
if (backendSessionId) {
|
|
1364
|
-
const resolved = globalSessionState.sessions.find((record) => record.backendSessionId === backendSessionId)?.sessionId;
|
|
1365
|
-
if (resolved)
|
|
1366
|
-
return resolved;
|
|
1367
|
-
}
|
|
1368
|
-
const tmuxSessionName = typeof input.tmuxSessionName === 'string' ? input.tmuxSessionName.trim() : '';
|
|
1369
|
-
return globalSessionState.sessions.find((record) => record.tmuxSessionName === tmuxSessionName)?.sessionId ?? null;
|
|
1413
|
+
return resolveCollaborationSessionId(input, globalSessionState.sessions, collaborationRouting);
|
|
1370
1414
|
}
|
|
1371
1415
|
function collaborationRemoteSessions() {
|
|
1372
1416
|
return Array.from(new Map(collaborationStore.list().flatMap((group) => group.remoteSessions ?? [])
|
|
@@ -1381,45 +1425,154 @@ function tryDeliverCollaborationInbox(frontendSessionId) {
|
|
|
1381
1425
|
? `服务 ${remote.serviceLabel} 不可达:消息尚未送达,仅保存在待发送队列;请勿等待对方已收到的回复,服务重连且 Mac 客户端运行后重试投递。`
|
|
1382
1426
|
: '跨服务消息尚未确认送达,正在等待 Mac 客户端转发。' };
|
|
1383
1427
|
}
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
}
|
|
1392
|
-
const prompt = formatCollaborationDelivery({
|
|
1393
|
-
targetSessionId: frontendSessionId,
|
|
1394
|
-
messages: pending,
|
|
1428
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1429
|
+
const route = collaborationDeliveryWorker.state(frontendSessionId);
|
|
1430
|
+
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId),
|
|
1431
|
+
reason: route.reason ?? '消息已入队,后台正在恢复路由并投递' };
|
|
1432
|
+
}
|
|
1433
|
+
function formatLocalCollaborationMessages(frontendSessionId, messages) {
|
|
1434
|
+
return formatCollaborationDelivery({ targetSessionId: frontendSessionId, messages,
|
|
1395
1435
|
groups: collaborationStore.groupsForSession(frontendSessionId),
|
|
1396
|
-
sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(
|
|
1397
|
-
const snapshot = orchestrationSessionSnapshot(candidate);
|
|
1398
|
-
return {
|
|
1399
|
-
sessionId: snapshot.sessionId,
|
|
1400
|
-
agentNativeSessionId: snapshot.agentNativeSessionId,
|
|
1401
|
-
name: snapshot.name,
|
|
1402
|
-
status: snapshot.status,
|
|
1403
|
-
};
|
|
1404
|
-
})],
|
|
1436
|
+
sessions: [...collaborationRemoteSessions(), ...globalSessionState.sessions.map(orchestrationSessionSnapshot)],
|
|
1405
1437
|
});
|
|
1438
|
+
}
|
|
1439
|
+
async function inspectCollaborationTmux(binding, requestedPane) {
|
|
1440
|
+
if (!binding.tmuxSessionName)
|
|
1441
|
+
return { state: 'offline', reason: 'TMUX_BINDING_MISSING' };
|
|
1442
|
+
const name = `=${binding.tmuxSessionName}`;
|
|
1406
1443
|
try {
|
|
1407
|
-
session
|
|
1444
|
+
await runTmux(['has-session', '-t', name]);
|
|
1408
1445
|
}
|
|
1409
1446
|
catch (error) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
});
|
|
1415
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 2_000).unref?.();
|
|
1416
|
-
return { delivered: [], pending: collaborationStore.pendingCount(frontendSessionId), reason: '终端写入失败,消息已保留并等待重试' };
|
|
1447
|
+
if (/can't find session|no server running|no sessions|error connecting.*No such file/i.test(getErrorMessage(error))) {
|
|
1448
|
+
return { state: 'offline', reason: 'TMUX_SESSION_NOT_RUNNING' };
|
|
1449
|
+
}
|
|
1450
|
+
throw error;
|
|
1417
1451
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1452
|
+
const layout = await getTmuxLayout(binding.tmuxSessionName);
|
|
1453
|
+
if (layout.sessionName !== binding.tmuxSessionName)
|
|
1454
|
+
throw new Error('TMUX_SESSION_IDENTITY_CHANGED');
|
|
1455
|
+
const serverPid = Number((await runTmux(['display-message', '-p', '#{pid}'])).trim());
|
|
1456
|
+
if (!Number.isInteger(serverPid) || serverPid <= 0)
|
|
1457
|
+
throw new Error('TMUX_SERVER_IDENTITY_UNAVAILABLE');
|
|
1458
|
+
const panes = await Promise.all(layout.windows.flatMap((window) => window.panes).map(async (pane) => {
|
|
1459
|
+
const program = await resolveTmuxPaneProgram(pane);
|
|
1460
|
+
const agent = detectAgentFromCommand(program?.rawArgs ?? program?.command ?? '', agentCustomCommands());
|
|
1461
|
+
const nativeSessionId = agent && program?.rawArgs ? inferResumeSessionId(agent, splitCommandToArgv(program.rawArgs)) : null;
|
|
1462
|
+
return { serverPid, sessionId: layout.sessionId, paneId: pane.id, panePid: pane.pid,
|
|
1463
|
+
agentSlug: agent?.slug ?? '', nativeSessionId, cwd: pane.currentPath };
|
|
1464
|
+
}));
|
|
1465
|
+
return selectCollaborationPane(binding, requestedPane ? panes.filter((pane) => pane.paneId === requestedPane) : panes);
|
|
1466
|
+
}
|
|
1467
|
+
async function rebindCollaborationRoute(frontendSessionId, paneId) {
|
|
1468
|
+
await collaborationDeliveryWorker.reconfigure(frontendSessionId, async () => {
|
|
1469
|
+
const record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1470
|
+
if (!record)
|
|
1471
|
+
throw new Error('SESSION_REMOVED');
|
|
1472
|
+
const previous = collaborationRouting.get(frontendSessionId);
|
|
1473
|
+
const binding = {
|
|
1474
|
+
sessionId: frontendSessionId, backendSessionId: previous?.backendSessionId ?? record.backendSessionId,
|
|
1475
|
+
mode: record.mode, tmuxSessionName: record.tmuxSessionName ?? previous?.tmuxSessionName ?? null,
|
|
1476
|
+
agentSlug: null, nativeSessionId: null, pane: null,
|
|
1477
|
+
};
|
|
1478
|
+
if (binding.mode === 'tmux') {
|
|
1479
|
+
const inspected = await inspectCollaborationTmux(binding, paneId);
|
|
1480
|
+
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1481
|
+
throw new Error(inspected.reason ?? 'AGENT_NOT_RUNNING');
|
|
1482
|
+
const pane = inspected.pane;
|
|
1483
|
+
binding.pane = { serverPid: pane.serverPid, sessionId: pane.sessionId, paneId: pane.paneId, panePid: pane.panePid,
|
|
1484
|
+
agentSlug: pane.agentSlug, nativeSessionId: pane.nativeSessionId };
|
|
1485
|
+
binding.agentSlug = pane.agentSlug;
|
|
1486
|
+
binding.nativeSessionId = pane.nativeSessionId;
|
|
1487
|
+
}
|
|
1488
|
+
else {
|
|
1489
|
+
if (paneId)
|
|
1490
|
+
throw new Error('PANE_REQUIRES_TMUX');
|
|
1491
|
+
const backend = binding.backendSessionId ? terminalSessions.get(binding.backendSessionId) : null;
|
|
1492
|
+
if (!backend)
|
|
1493
|
+
throw new Error('SHELL_BACKEND_NOT_RUNNING');
|
|
1494
|
+
await refreshCollaborationAgentIdentity(binding.backendSessionId, backend);
|
|
1495
|
+
const agent = detectSessionAgent(backend);
|
|
1496
|
+
if (!agent)
|
|
1497
|
+
throw new Error('AGENT_NOT_RUNNING');
|
|
1498
|
+
binding.agentSlug = agent.slug;
|
|
1499
|
+
binding.nativeSessionId = backend.agentSession?.sessionId ?? null;
|
|
1500
|
+
}
|
|
1501
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1502
|
+
throw new Error('SESSION_REMOVED');
|
|
1503
|
+
collaborationRouting.bind(binding);
|
|
1504
|
+
});
|
|
1505
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1506
|
+
return { ...collaborationRouting.get(frontendSessionId), state: 'recovering' };
|
|
1507
|
+
}
|
|
1508
|
+
async function resolveCollaborationRoute(frontendSessionId) {
|
|
1509
|
+
let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1510
|
+
if (!record)
|
|
1511
|
+
return { state: 'offline', reason: 'SESSION_REMOVED' };
|
|
1512
|
+
let backend = resolveOrchestrationBackend(record);
|
|
1513
|
+
let binding = collaborationRouting.get(frontendSessionId) ?? {
|
|
1514
|
+
sessionId: record.sessionId, backendSessionId: null, mode: record.mode, tmuxSessionName: record.tmuxSessionName,
|
|
1515
|
+
agentSlug: record.agentResume?.slug ?? null, nativeSessionId: record.agentResume?.sessionId ?? null, pane: null,
|
|
1516
|
+
};
|
|
1517
|
+
if (binding.mode === 'tmux') {
|
|
1518
|
+
const inspected = await inspectCollaborationTmux(binding);
|
|
1519
|
+
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1520
|
+
return inspected;
|
|
1521
|
+
record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1522
|
+
if (!record)
|
|
1523
|
+
return { state: 'offline', reason: 'SESSION_REMOVED_DURING_RECOVERY' };
|
|
1524
|
+
const pane = inspected.pane;
|
|
1525
|
+
// Remember the target before attachment. A failed attachment must not let
|
|
1526
|
+
// a subsequent active-pane change silently choose another recipient.
|
|
1527
|
+
const pinned = { serverPid: pane.serverPid, sessionId: pane.sessionId, paneId: pane.paneId, panePid: pane.panePid,
|
|
1528
|
+
agentSlug: pane.agentSlug, nativeSessionId: pane.nativeSessionId ?? binding.nativeSessionId };
|
|
1529
|
+
binding = { ...binding, pane: pinned, agentSlug: pinned.agentSlug, nativeSessionId: pinned.nativeSessionId };
|
|
1530
|
+
collaborationRouting.bind(binding);
|
|
1531
|
+
if (!backend) {
|
|
1532
|
+
const cwd = pane.cwd || record.cwd;
|
|
1533
|
+
if (!cwd)
|
|
1534
|
+
return { state: 'detached', reason: 'TMUX_ATTACH_CWD_UNAVAILABLE' };
|
|
1535
|
+
await pathValidator.allowSessionCwd(cwd);
|
|
1536
|
+
// Attach only. Never recreate a vanished session or launch/resume an
|
|
1537
|
+
// Agent in response to a queued collaboration message.
|
|
1538
|
+
const spawned = await spawnTerminalSession({}, { cwd, mode: 'tmux',
|
|
1539
|
+
tmuxSessionName: binding.tmuxSessionName, attachExistingOnly: true, tmuxAttachTarget: pane.sessionId });
|
|
1540
|
+
record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
|
|
1541
|
+
if (!record)
|
|
1542
|
+
return { state: 'offline', reason: 'SESSION_REMOVED_DURING_RECOVERY' };
|
|
1543
|
+
if (!terminalSessions.has(spawned.sessionId))
|
|
1544
|
+
return { state: 'detached', reason: 'TMUX_ATTACH_EXITED' };
|
|
1545
|
+
binding = { ...binding, backendSessionId: spawned.sessionId };
|
|
1546
|
+
collaborationRouting.bind(binding);
|
|
1547
|
+
backend = resolveOrchestrationBackend(record);
|
|
1548
|
+
}
|
|
1549
|
+
if (!backend)
|
|
1550
|
+
return { state: 'detached', reason: 'TMUX_BACKEND_UNAVAILABLE' };
|
|
1551
|
+
return { state: 'ready', write: async (messages) => {
|
|
1552
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1553
|
+
throw new Error('SESSION_REMOVED');
|
|
1554
|
+
await writeCollaborationTmuxPane(runTmux, pinned, formatLocalCollaborationMessages(frontendSessionId, messages));
|
|
1555
|
+
backend.lastActivity = Date.now();
|
|
1556
|
+
} };
|
|
1557
|
+
}
|
|
1558
|
+
if (!backend || !binding.backendSessionId)
|
|
1559
|
+
return { state: 'offline', reason: 'SHELL_BACKEND_NOT_RUNNING' };
|
|
1560
|
+
await refreshCollaborationAgentIdentity(binding.backendSessionId, backend);
|
|
1561
|
+
if (!detectSessionAgent(backend))
|
|
1562
|
+
return { state: 'agent-exited', reason: 'AGENT_NOT_RUNNING' };
|
|
1563
|
+
if ((binding.agentSlug && backend.agent?.slug !== binding.agentSlug)
|
|
1564
|
+
|| (binding.nativeSessionId && backend.agentSession?.sessionId && backend.agentSession.sessionId !== binding.nativeSessionId)) {
|
|
1565
|
+
return { state: 'identity-mismatch', reason: 'AGENT_IDENTITY_CHANGED' };
|
|
1566
|
+
}
|
|
1567
|
+
const target = backend;
|
|
1568
|
+
return { state: 'ready', write: async (messages) => {
|
|
1569
|
+
if (!globalSessionState.sessions.some((candidate) => candidate.sessionId === frontendSessionId))
|
|
1570
|
+
throw new Error('SESSION_REMOVED');
|
|
1571
|
+
if (terminalSessions.get(binding.backendSessionId) !== target)
|
|
1572
|
+
throw new Error('SHELL_BACKEND_CHANGED');
|
|
1573
|
+
target.ptyProcess.write(buildBracketedSubmitBytes(formatLocalCollaborationMessages(frontendSessionId, messages)));
|
|
1574
|
+
target.lastActivity = Date.now();
|
|
1575
|
+
} };
|
|
1423
1576
|
}
|
|
1424
1577
|
async function refreshCollaborationAgentIdentity(backendSessionId, session) {
|
|
1425
1578
|
let activeProgram = null;
|
|
@@ -1434,33 +1587,12 @@ async function refreshCollaborationAgentIdentity(backendSessionId, session) {
|
|
|
1434
1587
|
? { ...resolved, updatedAt: Date.now() }
|
|
1435
1588
|
: getActiveProgramFromTmuxLayout(layout);
|
|
1436
1589
|
}
|
|
1437
|
-
if (!activeProgram)
|
|
1438
|
-
return;
|
|
1439
1590
|
session.activeProgram = activeProgram;
|
|
1440
|
-
persistActiveProgramBinding(backendSessionId, activeProgram
|
|
1591
|
+
persistActiveProgramBinding(backendSessionId, activeProgram?.command);
|
|
1441
1592
|
syncAgentIdentity(backendSessionId, session);
|
|
1442
1593
|
}
|
|
1443
1594
|
function deliverCollaborationInboxWhenAgentReady(frontendSessionId, backendSessionId, attempt = 0) {
|
|
1444
|
-
|
|
1445
|
-
if (!session)
|
|
1446
|
-
return;
|
|
1447
|
-
if (canDeliverPromptToAgent(session)) {
|
|
1448
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 300).unref?.();
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
if (attempt >= 60)
|
|
1452
|
-
return;
|
|
1453
|
-
void refreshCollaborationAgentIdentity(backendSessionId, session)
|
|
1454
|
-
.catch(() => undefined)
|
|
1455
|
-
.finally(() => {
|
|
1456
|
-
if (canDeliverPromptToAgent(session)) {
|
|
1457
|
-
setTimeout(() => tryDeliverCollaborationInbox(frontendSessionId), 300).unref?.();
|
|
1458
|
-
return;
|
|
1459
|
-
}
|
|
1460
|
-
setTimeout(() => {
|
|
1461
|
-
deliverCollaborationInboxWhenAgentReady(frontendSessionId, backendSessionId, attempt + 1);
|
|
1462
|
-
}, 250).unref?.();
|
|
1463
|
-
});
|
|
1595
|
+
collaborationDeliveryWorker.wake(frontendSessionId);
|
|
1464
1596
|
}
|
|
1465
1597
|
async function spawnCollaborationAgentSession(req, group, sourceSessionId, input) {
|
|
1466
1598
|
const agentSlug = typeof input.agentSlug === 'string' ? input.agentSlug.trim().toLowerCase() : '';
|
|
@@ -4435,7 +4567,22 @@ function ptyHostShellModeEnabled() {
|
|
|
4435
4567
|
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
4436
4568
|
return process.platform !== 'win32' && !isBun && process.env.TERMDOCK_PTY_HOST !== 'off';
|
|
4437
4569
|
}
|
|
4570
|
+
const tmuxBackendSpawns = new Map();
|
|
4438
4571
|
async function spawnTerminalSession(req, input) {
|
|
4572
|
+
const name = input.mode === 'tmux' && input.tmuxSessionName ? normalizeTmuxSessionName(input.tmuxSessionName) : null;
|
|
4573
|
+
if (!name)
|
|
4574
|
+
return spawnTerminalSessionUnlocked(req, input);
|
|
4575
|
+
const pending = tmuxBackendSpawns.get(name);
|
|
4576
|
+
if (pending)
|
|
4577
|
+
return pending;
|
|
4578
|
+
const existing = findBackendSessionForTmux(name);
|
|
4579
|
+
if (existing)
|
|
4580
|
+
return { sessionId: existing[0], session: existing[1], cols: existing[1].cols, rows: existing[1].rows };
|
|
4581
|
+
const operation = spawnTerminalSessionUnlocked(req, input).finally(() => tmuxBackendSpawns.delete(name));
|
|
4582
|
+
tmuxBackendSpawns.set(name, operation);
|
|
4583
|
+
return operation;
|
|
4584
|
+
}
|
|
4585
|
+
async function spawnTerminalSessionUnlocked(req, input) {
|
|
4439
4586
|
const cwd = await resolveWorkingDirectory(req, input.cwd);
|
|
4440
4587
|
const cols = input.cols || 80;
|
|
4441
4588
|
const rows = input.rows || 24;
|
|
@@ -4446,19 +4593,28 @@ async function spawnTerminalSession(req, input) {
|
|
|
4446
4593
|
if (mode === 'tmux') {
|
|
4447
4594
|
const tmuxStatus = await getTmuxStatus();
|
|
4448
4595
|
if (!tmuxStatus.available) {
|
|
4596
|
+
if (input.attachExistingOnly)
|
|
4597
|
+
throw new Error('TMUX_UNAVAILABLE');
|
|
4449
4598
|
console.warn('[termdock] tmux not available, falling back to shell mode:', tmuxStatus.reason);
|
|
4450
4599
|
mode = 'shell';
|
|
4451
4600
|
}
|
|
4452
4601
|
}
|
|
4453
4602
|
const tmuxSessionName = mode === 'tmux' ? normalizeTmuxSessionName(input.tmuxSessionName) : null;
|
|
4454
4603
|
if (mode === 'tmux' && tmuxSessionName) {
|
|
4455
|
-
|
|
4604
|
+
if (input.attachExistingOnly) {
|
|
4605
|
+
if (!await tmuxSessionExists(input.tmuxAttachTarget ?? `=${tmuxSessionName}`))
|
|
4606
|
+
throw new Error('TMUX_SESSION_NOT_RUNNING');
|
|
4607
|
+
}
|
|
4608
|
+
else
|
|
4609
|
+
await prepareManagedTmuxSession(tmuxSessionName, cwd);
|
|
4456
4610
|
}
|
|
4457
4611
|
const command = mode === 'tmux'
|
|
4458
4612
|
? getTmuxBinary()
|
|
4459
4613
|
: (process.platform === 'win32' ? 'powershell.exe' : resolveShellCandidates()[0]);
|
|
4614
|
+
const supportsFeatures = mode === 'tmux' && await supportsTmuxClientFeatures(command);
|
|
4460
4615
|
const args = mode === 'tmux' && tmuxSessionName
|
|
4461
|
-
? buildTmuxAttachArgs(tmuxSessionName,
|
|
4616
|
+
? [...buildTmuxAttachArgs(input.tmuxAttachTarget ?? tmuxSessionName, supportsFeatures),
|
|
4617
|
+
...(input.attachExistingOnly && supportsFeatures ? ['-f', 'ignore-size'] : [])]
|
|
4462
4618
|
: (process.platform === 'win32' ? buildPowerShellCwdHookArgs() : []);
|
|
4463
4619
|
const envPath = buildAugmentedPath();
|
|
4464
4620
|
const resolvedEnv = buildInteractiveColorEnvironment({
|
|
@@ -5393,7 +5549,8 @@ router.post('/operations/orchestration/spawn', async (req, res) => {
|
|
|
5393
5549
|
res.status(400).json({ error: getErrorMessage(error) });
|
|
5394
5550
|
}
|
|
5395
5551
|
});
|
|
5396
|
-
router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
|
|
5552
|
+
router.use('/operations/orchestration', collaborationRoutes({ store: collaborationStore, resolveSession: resolveFrontendSessionId,
|
|
5553
|
+
deliver: tryDeliverCollaborationInbox, rebind: rebindCollaborationRoute }));
|
|
5397
5554
|
router.get('/operations/session-search', (req, res) => {
|
|
5398
5555
|
const query = typeof req.query.q === 'string' ? req.query.q : '';
|
|
5399
5556
|
const results = sessionSearchStore.search(query, Number(req.query.limit) || 30).map((result) => {
|
|
@@ -34,7 +34,7 @@ export const securityHeaders = (req, res, next) => {
|
|
|
34
34
|
const shellSource = /^[a-z0-9.\-:\[\]]+$/i.test(host) ? `http://${host}/preview-shell.html https://${host}/preview-shell.html` : "'none'";
|
|
35
35
|
const shell = req.path === '/preview-shell.html';
|
|
36
36
|
res.setHeader('Content-Security-Policy', shell
|
|
37
|
-
? "sandbox allow-scripts; default-src 'none'; script-src 'unsafe-inline' blob: https: http:; style-src 'unsafe-inline' blob: https:; img-src blob: data: https:; font-src blob: data: https:; media-src blob: data: https:; connect-src https:; frame-src 'none'; frame-ancestors 'self'; object-src 'none'; base-uri https://termdock-preview.invalid; form-action 'none'"
|
|
37
|
+
? "sandbox allow-scripts; default-src 'none'; script-src 'unsafe-inline' 'wasm-unsafe-eval' blob: https: http:; style-src 'unsafe-inline' blob: https:; img-src blob: data: https:; font-src blob: data: https:; media-src blob: data: https:; connect-src blob: https: wss:; worker-src blob:; frame-src 'none'; frame-ancestors 'self'; object-src 'none'; base-uri https://termdock-preview.invalid; form-action 'none'"
|
|
38
38
|
: preview
|
|
39
39
|
? "sandbox allow-scripts; frame-ancestors 'self'; object-src 'none'; base-uri 'self'"
|
|
40
40
|
: `script-src 'self' 'wasm-unsafe-eval'; script-src-attr 'none'; worker-src 'self' blob:; frame-src ${shellSource}; frame-ancestors 'self'; object-src 'none'; base-uri 'self'`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "termdock",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.180",
|
|
4
4
|
"description": "A complete web-based terminal application with modern UI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".desktop-dist/main.js",
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"concurrently": "^8.2.0",
|
|
120
120
|
"cross-env": "^10.1.0",
|
|
121
121
|
"electron": "^43.2.0",
|
|
122
|
+
"es-module-shims": "^2.8.4",
|
|
122
123
|
"esbuild": "^0.27.7",
|
|
123
124
|
"eslint": "^9.39.2",
|
|
124
125
|
"jsdom": "^24.0.0",
|
package/runtime-manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.180",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-kSujny2SLOFnZ1ffxm1FYtli7g1vEN8d3vj1oQeLLdc=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-OJ83J67z97pdLmRW6AEkj8r5LkhdkmNIeAxu2L4niig=",
|
|
10
|
+
"clientBundleHash": "sha256-uS0kVZf+KwoYUsvNs5oQQ2EOdE7L45iUtfuHunLnQ2Q=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|