termdock 1.4.356 → 1.4.357
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/README.md +22 -4
- package/dist/client/assets/AndroidMirrorView-BGagwKtL.js +3 -0
- package/dist/client/assets/AndroidMirrorView-BGagwKtL.js.br +0 -0
- package/dist/client/assets/AndroidMirrorView-BGagwKtL.js.gz +0 -0
- package/dist/client/assets/App-DpMhxwYW.js +178 -0
- package/dist/client/assets/App-DpMhxwYW.js.br +0 -0
- package/dist/client/assets/App-DpMhxwYW.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-Bd_l74EC.js +9 -0
- package/dist/client/assets/ArchitecturePanel-Bd_l74EC.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-Bd_l74EC.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-DO_zJfZr.js → ChangeWalkthroughPanel-6DN-2BdR.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-6DN-2BdR.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-6DN-2BdR.js.gz +0 -0
- package/dist/client/assets/{ChatRichContent-Cu1E1cJ8.js → ChatRichContent-DjwpmW7d.js} +29 -29
- package/dist/client/assets/ChatRichContent-DjwpmW7d.js.br +0 -0
- package/dist/client/assets/ChatRichContent-DjwpmW7d.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DPTJ9pCt.js +1 -0
- package/dist/client/assets/CollaborationMainWorkspace-DPTJ9pCt.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DPTJ9pCt.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-Cbu1Bjl0.js +1 -0
- package/dist/client/assets/ComputerControlView-CwB-wQqB.js +6 -0
- package/dist/client/assets/ComputerControlView-CwB-wQqB.js.br +0 -0
- package/dist/client/assets/ComputerControlView-CwB-wQqB.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-CPYwfryM.js → DagPlayground-DMn9Gyrw.js} +1 -1
- package/dist/client/assets/DagPlayground-DMn9Gyrw.js.br +0 -0
- package/dist/client/assets/DagPlayground-DMn9Gyrw.js.gz +0 -0
- package/dist/client/assets/{DiffLab-ptw96Q_4.js → DiffLab-YBWlCU2T.js} +1 -1
- package/dist/client/assets/DiffLab-YBWlCU2T.js.br +0 -0
- package/dist/client/assets/DiffLab-YBWlCU2T.js.gz +0 -0
- package/dist/client/assets/DiffReview-YOPDWqhv.js +1 -0
- package/dist/client/assets/DiffReview-YOPDWqhv.js.br +0 -0
- package/dist/client/assets/DiffReview-YOPDWqhv.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-eEi3y-0G.js → DiffReviewLab-SYomq14c.js} +1 -1
- package/dist/client/assets/DiffReviewLab-SYomq14c.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-SYomq14c.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-DqcQxp4Q.js → DiffViewer-mh9cBl5v.js} +1 -1
- package/dist/client/assets/DiffViewer-mh9cBl5v.js.br +0 -0
- package/dist/client/assets/DiffViewer-mh9cBl5v.js.gz +0 -0
- package/dist/client/assets/{GitLoadingSkeleton-DYrDIDF1.js → GitLoadingSkeleton-DKRTc5sz.js} +1 -1
- package/dist/client/assets/GitLoadingSkeleton-DKRTc5sz.js.br +0 -0
- package/dist/client/assets/GitLoadingSkeleton-DKRTc5sz.js.gz +0 -0
- package/dist/client/assets/Markdown-C8OP1j-g.js +23 -0
- package/dist/client/assets/Markdown-C8OP1j-g.js.br +0 -0
- package/dist/client/assets/Markdown-C8OP1j-g.js.gz +0 -0
- package/dist/client/assets/Markdown-DKQhs57e.css +1 -0
- package/dist/client/assets/Markdown-DKQhs57e.css.br +0 -0
- package/dist/client/assets/Markdown-DKQhs57e.css.gz +0 -0
- package/dist/client/assets/{ModelPreview-DjtHLlqP.js → ModelPreview-CsZry3Wd.js} +3 -3
- package/dist/client/assets/ModelPreview-CsZry3Wd.js.br +0 -0
- package/dist/client/assets/ModelPreview-CsZry3Wd.js.gz +0 -0
- package/dist/client/assets/RecordingSaveDialog-CoRGi97z.js +1 -0
- package/dist/client/assets/RecordingSaveDialog-CoRGi97z.js.br +0 -0
- package/dist/client/assets/RecordingSaveDialog-CoRGi97z.js.gz +0 -0
- package/dist/client/assets/RightSidebar-DNY2kC6B.js +1662 -0
- package/dist/client/assets/RightSidebar-DNY2kC6B.js.br +0 -0
- package/dist/client/assets/RightSidebar-DNY2kC6B.js.gz +0 -0
- package/dist/client/assets/SessionChatView-DXNpYUrk.js +13 -0
- package/dist/client/assets/SessionChatView-DXNpYUrk.js.br +0 -0
- package/dist/client/assets/SessionChatView-DXNpYUrk.js.gz +0 -0
- package/dist/client/assets/SessionResourceSidebar-B_379HBJ.js +2 -0
- package/dist/client/assets/SessionResourceSidebar-B_379HBJ.js.br +0 -0
- package/dist/client/assets/SessionResourceSidebar-B_379HBJ.js.gz +0 -0
- package/dist/client/assets/TodoWorkspace-DfwGOAHB.js +1 -0
- package/dist/client/assets/TodoWorkspace-DfwGOAHB.js.br +0 -0
- package/dist/client/assets/TodoWorkspace-DfwGOAHB.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-Cvf_KWZ0.js → abnfDiagram-VCTEODGH-C9M0gnl-.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-C9M0gnl-.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-C9M0gnl-.js.gz +0 -0
- package/dist/client/assets/app-CovJYQoW.js +37 -0
- package/dist/client/assets/app-CovJYQoW.js.br +0 -0
- package/dist/client/assets/app-CovJYQoW.js.gz +0 -0
- package/dist/client/assets/{arc-UvdowQOf.js → arc-q4wfkQcT.js} +1 -1
- package/dist/client/assets/arc-q4wfkQcT.js.br +0 -0
- package/dist/client/assets/arc-q4wfkQcT.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-DotsbCCA.js → architectureDiagram-5GKGNRK7-CCuna3ab.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CCuna3ab.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CCuna3ab.js.gz +0 -0
- package/dist/client/assets/{arrow-down-FGzRy9D-.js → arrow-down-WyzmLXwQ.js} +1 -1
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CoOfSG4k.js → blockDiagram-I7D4REHJ-BXK68Qyt.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-BXK68Qyt.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-BXK68Qyt.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-BAfjgs8B.js → c4Diagram-7LVT6UL2-BqwmmVmS.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-BqwmmVmS.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BqwmmVmS.js.gz +0 -0
- package/dist/client/assets/channel-Dd6WQGB7.js +1 -0
- package/dist/client/assets/{chevron-left-xqARwD-7.js → chevron-left-CxeWIsDK.js} +1 -1
- package/dist/client/assets/{chunk-2Q5K7J3B-DiiQ7BvG.js → chunk-2Q5K7J3B-esKGR38U.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CczSK8wW.js → chunk-5VM5RSS4-B3InxqDA.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-BI3Di4Ve.js → chunk-F27PBJKO-CjUVyF-E.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-CjUVyF-E.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CjUVyF-E.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-Cvvs1wmn.js → chunk-IMKFNOWR-BR8mmBs7.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-BR8mmBs7.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BR8mmBs7.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-lrJRD9WA.js → chunk-JWPE2WC7-CplXNOA7.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-CnPC9D8B.js → chunk-POPQ4Y6H-D0JWCGnb.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-BZfMhm64.js → chunk-SVP7TREG-BdJtJSmy.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-BdJtJSmy.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BdJtJSmy.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-DmkEaRK3.js → chunk-TICWLB2K-CoevFj8I.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-CoevFj8I.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-CoevFj8I.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-BP9Lccpe.js → chunk-XXDRQBXY-D7qYRhMH.js} +1 -1
- package/dist/client/assets/{circle-DGZOpG8V.js → circle-xgKDXpwG.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-BdIUq_D7.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-BdIUq_D7.js +1 -0
- package/dist/client/assets/{clipboard-rLx5CzbY.js → clipboard-DeK6pJD3.js} +1 -1
- package/dist/client/assets/clipboardText-BWOwer6N.js +1 -0
- package/dist/client/assets/clipboardText-BWOwer6N.js.br +0 -0
- package/dist/client/assets/clipboardText-BWOwer6N.js.gz +0 -0
- package/dist/client/assets/clipboardText-vX90C7eX.css +1 -0
- package/dist/client/assets/clipboardText-vX90C7eX.css.br +0 -0
- package/dist/client/assets/clipboardText-vX90C7eX.css.gz +0 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-0xPYMc2z.js → cose-bilkent-JH36ORCC-CvnsiaWP.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CvnsiaWP.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CvnsiaWP.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-BQ7-lCHm.js → cynefin-OW5HDTMX-BSGz8Olu.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-BSGz8Olu.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BSGz8Olu.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CazHuSj1.js → cynefinDiagram-5FMLGOSQ-DYYT0AHs.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DYYT0AHs.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DYYT0AHs.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-CJNgsyHQ.js → dagre-GXQ25YYZ-BM883MN7.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-BM883MN7.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-BM883MN7.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-D4xXIj1P.js → diagram-S7CK7UJ4-CYnxXy7R.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-CYnxXy7R.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CYnxXy7R.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-BWhpL-35.js → diagram-UQ7AKVKN-UOIBVxRf.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-UOIBVxRf.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-UOIBVxRf.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-DG9A7EgJ.js → diagram-VSXAHHWV-CqTJzfmm.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-CqTJzfmm.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CqTJzfmm.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-L5PSvynM.js → diagram-VX7I27RA-T9Y88CFB.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-T9Y88CFB.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-T9Y88CFB.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-moBH7ruR.js → diagram-Z3DM3KII-kIQDy7E0.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-kIQDy7E0.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-kIQDy7E0.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-DEiGgcRP.js → ebnfDiagram-PWID7BFC-z_VqR_JE.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-z_VqR_JE.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-z_VqR_JE.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-BboIleYi.js → erDiagram-RLTQ6QDP-0q52Z-pm.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-0q52Z-pm.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-0q52Z-pm.js.gz +0 -0
- package/dist/client/assets/{external-link-AZ3wjUg5.js → external-link-CsydVmO1.js} +1 -1
- package/dist/client/assets/{flowDiagram-HODETNUW-COxz7Rda.js → flowDiagram-HODETNUW-BrGqcUWn.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-BrGqcUWn.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BrGqcUWn.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-_XdRvgyZ.js → ganttDiagram-EL5Y4UJY-CHAN6vQv.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CHAN6vQv.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CHAN6vQv.js.gz +0 -0
- package/dist/client/assets/{git-compare-90v75Jal.js → git-compare-BmKAtQTt.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-CBb9xBNW.js → gitGraphDiagram-WWUBYQGX-C0ZV4FeB.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0ZV4FeB.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0ZV4FeB.js.gz +0 -0
- package/dist/client/assets/index-DEAMSl55.css +1 -0
- package/dist/client/assets/index-DEAMSl55.css.br +0 -0
- package/dist/client/assets/index-DEAMSl55.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-DjLdeNZi.js → infoDiagram-27XIBGKW-CrLj9y1o.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-Bcr-lhIE.js → ishikawaDiagram-5VMMS53U-BxGvUfLX.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BxGvUfLX.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BxGvUfLX.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-3vK6s9Pq.js → journeyDiagram-3NMN7TZE-rW9xlQ8G.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-rW9xlQ8G.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-rW9xlQ8G.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-DqDYDca0.js → kanban-definition-UXKFOSKX-CFI5iv9R.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-CFI5iv9R.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-CFI5iv9R.js.gz +0 -0
- package/dist/client/assets/{layout-C7_WwzCP.js → layout-Ct_an5XT.js} +1 -1
- package/dist/client/assets/layout-Ct_an5XT.js.br +0 -0
- package/dist/client/assets/layout-Ct_an5XT.js.gz +0 -0
- package/dist/client/assets/{linear-BQCRhl6P.js → linear-DpEFOA9l.js} +1 -1
- package/dist/client/assets/linear-DpEFOA9l.js.br +0 -0
- package/dist/client/assets/linear-DpEFOA9l.js.gz +0 -0
- package/dist/client/assets/{link-Cq14tNBm.js → link-34ChBc4F.js} +3 -3
- package/dist/client/assets/{link-Cq14tNBm.js.br → link-34ChBc4F.js.br} +0 -0
- package/dist/client/assets/link-34ChBc4F.js.gz +0 -0
- package/dist/client/assets/{list-tree-C281zrdg.js → list-tree-48dzbuUh.js} +1 -1
- package/dist/client/assets/{maximize-2-CovPxWdw.js → maximize-2-CbvSsu7O.js} +1 -1
- package/dist/client/assets/{mermaid.core-DGsccxt-.js → mermaid.core-DnVKnMmy.js} +5 -5
- package/dist/client/assets/mermaid.core-DnVKnMmy.js.br +0 -0
- package/dist/client/assets/mermaid.core-DnVKnMmy.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-l0eF1PQE.js → mindmap-definition-YA3MSWOX-C6shupTY.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-C6shupTY.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-C6shupTY.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-B5VzheZz.js → passwordBootstrap-Doe50DSa.js} +1 -1
- package/dist/client/assets/{passwordBootstrap-B5VzheZz.js.br → passwordBootstrap-Doe50DSa.js.br} +0 -0
- package/dist/client/assets/passwordBootstrap-Doe50DSa.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-fRrxGIDM.js → pegDiagram-XKGWAZYB-CDcnrvKX.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-CDcnrvKX.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-CDcnrvKX.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-B-EBa3qN.js → pieDiagram-E7YTZNPT-CrGc03Tg.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-CrGc03Tg.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-CrGc03Tg.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Dj4e6f4h.js → quadrantDiagram-AXDQQJYC-3-aldwKj.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-3-aldwKj.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-3-aldwKj.js.gz +0 -0
- package/dist/client/assets/{quote-COQfrXhi.js → quote-B5GeKEq1.js} +1 -1
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-CEMppxTC.js → railroadDiagram-O6MQD6OU-51uKwR2_.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-51uKwR2_.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-51uKwR2_.js.gz +0 -0
- package/dist/client/assets/rdpSession-ocpkj4XY.js +1 -0
- package/dist/client/assets/rdpSession-ocpkj4XY.js.br +0 -0
- package/dist/client/assets/rdpSession-ocpkj4XY.js.gz +0 -0
- package/dist/client/assets/referenceSelection-fvfRDbgp.js +4 -0
- package/dist/client/assets/referenceSelection-fvfRDbgp.js.br +0 -0
- package/dist/client/assets/referenceSelection-fvfRDbgp.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-Dri1k4_x.js → requirementDiagram-BXWQKSXE-CY6kp0_Y.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CY6kp0_Y.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-CY6kp0_Y.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CMsL_tYL.js → sankeyDiagram-P5KCCOFB-BsA0NkHG.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BsA0NkHG.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BsA0NkHG.js.gz +0 -0
- package/dist/client/assets/{search-Dg6HWygj.js → search-CbpNLSpd.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-4_4hZtOd.js → sequenceDiagram-WJ2MYXX4-BDzn7N2D.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-4_4hZtOd.js.br → sequenceDiagram-WJ2MYXX4-BDzn7N2D.js.br} +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BDzn7N2D.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-3QA6qj2j.js → sizeCapture-INFHLROL-gxtam7nc.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-gxtam7nc.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-gxtam7nc.js.gz +0 -0
- package/dist/client/assets/{stateDiagram-D77RDMKH-90IFPhD9.js → stateDiagram-D77RDMKH-C4XjYLZR.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-C4XjYLZR.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-C4XjYLZR.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BfHWFf-I.js +1 -0
- package/dist/client/assets/{sun-BAMMjiA8.js → sun-CMXn_ntS.js} +1 -1
- package/dist/client/assets/{swimlanes-42K2YHIH-BuHieVWC.js → swimlanes-42K2YHIH--a1wkQO8.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH--a1wkQO8.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH--a1wkQO8.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DsiDF1XD.js +8 -0
- package/dist/client/assets/swiper-CTPycjLq.js +1 -0
- package/dist/client/assets/swiper-CTPycjLq.js.br +0 -0
- package/dist/client/assets/swiper-CTPycjLq.js.gz +0 -0
- package/dist/client/assets/{terminal-Dea8wJF3.js → terminal-DaiFCKy_.js} +7 -7
- package/dist/client/assets/terminal-DaiFCKy_.js.br +0 -0
- package/dist/client/assets/terminal-DaiFCKy_.js.gz +0 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-C7FpaNwf.js → timeline-definition-24CTP7MA-DFmXXUuU.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DFmXXUuU.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DFmXXUuU.js.gz +0 -0
- package/dist/client/assets/triangle-alert-DgGU_xB4.js +1 -0
- package/dist/client/assets/{unplug-DN2SNXUL.js → unplug-CnZtWzVW.js} +1 -1
- package/dist/client/assets/use-gesture-react.esm-DHU0wi7Q.js +1 -0
- package/dist/client/assets/use-gesture-react.esm-DHU0wi7Q.js.br +0 -0
- package/dist/client/assets/use-gesture-react.esm-DHU0wi7Q.js.gz +0 -0
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CxoHHVcs.js → vennDiagram-4TSXK5OY-DgR4zroQ.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-DgR4zroQ.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-DgR4zroQ.js.gz +0 -0
- package/dist/client/assets/{videoCompression-rr2lu3uy.js → videoCompression-CcffRZdP.js} +1 -1
- package/dist/client/assets/videoCompression-CcffRZdP.js.br +0 -0
- package/dist/client/assets/videoCompression-CcffRZdP.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-B4Hsh-Ad.js → wardleyDiagram-VM6X3IG4-CY4SVUkf.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CY4SVUkf.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-CY4SVUkf.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-CghzsxQv.js → xychartDiagram-S5SC5T6Z-PblQhR-b.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-PblQhR-b.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-PblQhR-b.js.gz +0 -0
- package/dist/client/index.html +3 -3
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/sw-encrypted-background.js +1 -1
- package/dist/client/sw-encrypted-background.js.br +0 -0
- package/dist/client/sw-encrypted-background.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/collaborationPeerTransport.js +14 -69
- package/dist/server/agent/collaborationStore.js +5 -0
- package/dist/server/agent/installers.js +89 -33
- package/dist/server/agent/integrationServer.js +31 -2
- package/dist/server/agent/pluginPackages.js +51 -22
- package/dist/server/agent/sessionChatRoutes.js +45 -7
- package/dist/server/agentHook.js +56 -12
- package/dist/server/android/adb.js +24 -31
- package/dist/server/cli.js +52 -110
- package/dist/server/entry.js +17 -23
- package/dist/server/federation/cli.js +13 -47
- package/dist/server/federation/connectionErrors.js +37 -0
- package/dist/server/federation/connectionModel.js +83 -0
- package/dist/server/federation/desktopTargets.js +35 -13
- package/dist/server/federation/directRoutes.js +28 -36
- package/dist/server/federation/directTargetRegistry.js +106 -0
- package/dist/server/federation/httpUploadLimit.js +8 -0
- package/dist/server/federation/relay.js +13 -6
- package/dist/server/federation/relayTransport.js +147 -0
- package/dist/server/federation/routeAccess.js +5 -3
- package/dist/server/federation/runtime.js +4 -2
- package/dist/server/federation/selfRelay.js +10 -59
- package/dist/server/federation/serviceTransport.js +123 -0
- package/dist/server/federation/streamLimits.js +3 -0
- package/dist/server/federation/trustedCa.js +61 -0
- package/dist/server/routes/android.js +5 -7
- package/dist/server/routes/terminal.js +149 -111
- package/dist/server/shared/androidDevices.js +64 -0
- package/dist/server/shared/programStatus.js +174 -0
- package/dist/server/shared/shareResources.js +3 -2
- package/dist/server/todos/routes.js +36 -0
- package/dist/server/todos/store.js +31 -1
- package/dist/server/utils/bootCheck.js +41 -271
- package/dist/server/utils/localHttpsSetup.js +257 -0
- package/dist/server/utils/oscBuffer.js +9 -0
- package/dist/server/utils/sessionDisplayName.js +11 -9
- package/dist/server/utils/settings.js +6 -3
- package/dist/server/utils/tmuxDisplayGeometry.js +54 -0
- package/dist/server/utils/tmuxObserverSize.js +26 -0
- package/docs/integrations.md +2 -1
- package/package.json +5 -1
- package/runtime-manifest.json +3 -3
- package/scripts/install-utils.cjs +81 -0
- package/scripts/postinstall.mjs +19 -106
- package/dist/client/assets/AndroidMirrorView-DW9oRr0w.js +0 -3
- package/dist/client/assets/AndroidMirrorView-DW9oRr0w.js.br +0 -0
- package/dist/client/assets/AndroidMirrorView-DW9oRr0w.js.gz +0 -0
- package/dist/client/assets/App-DEZ2i9ZZ.js +0 -178
- package/dist/client/assets/App-DEZ2i9ZZ.js.br +0 -0
- package/dist/client/assets/App-DEZ2i9ZZ.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-Bf4b-6An.js +0 -9
- package/dist/client/assets/ArchitecturePanel-Bf4b-6An.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-Bf4b-6An.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DO_zJfZr.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DO_zJfZr.js.gz +0 -0
- package/dist/client/assets/ChatRichContent-Cu1E1cJ8.js.br +0 -0
- package/dist/client/assets/ChatRichContent-Cu1E1cJ8.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BhmpSEbf.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-BhmpSEbf.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-BhmpSEbf.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-CX0Aqdi2.js +0 -2
- package/dist/client/assets/ComputerControlView-DjQLzn10.js +0 -6
- package/dist/client/assets/ComputerControlView-DjQLzn10.js.br +0 -0
- package/dist/client/assets/ComputerControlView-DjQLzn10.js.gz +0 -0
- package/dist/client/assets/DagPlayground-CPYwfryM.js.br +0 -0
- package/dist/client/assets/DagPlayground-CPYwfryM.js.gz +0 -0
- package/dist/client/assets/DiffLab-ptw96Q_4.js.br +0 -0
- package/dist/client/assets/DiffLab-ptw96Q_4.js.gz +0 -0
- package/dist/client/assets/DiffReview-12E2zWyL.js +0 -1
- package/dist/client/assets/DiffReview-12E2zWyL.js.br +0 -0
- package/dist/client/assets/DiffReview-12E2zWyL.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-eEi3y-0G.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-eEi3y-0G.js.gz +0 -0
- package/dist/client/assets/DiffViewer-DqcQxp4Q.js.br +0 -0
- package/dist/client/assets/DiffViewer-DqcQxp4Q.js.gz +0 -0
- package/dist/client/assets/GitLoadingSkeleton-DYrDIDF1.js.br +0 -0
- package/dist/client/assets/GitLoadingSkeleton-DYrDIDF1.js.gz +0 -0
- package/dist/client/assets/MarkdownPreview-CsJGsDRL.js +0 -19
- package/dist/client/assets/MarkdownPreview-CsJGsDRL.js.br +0 -0
- package/dist/client/assets/MarkdownPreview-CsJGsDRL.js.gz +0 -0
- package/dist/client/assets/ModelPreview-DjtHLlqP.js.br +0 -0
- package/dist/client/assets/ModelPreview-DjtHLlqP.js.gz +0 -0
- package/dist/client/assets/RecordingSaveDialog-DUtwgIME.js +0 -1
- package/dist/client/assets/RecordingSaveDialog-DUtwgIME.js.br +0 -0
- package/dist/client/assets/RecordingSaveDialog-DUtwgIME.js.gz +0 -0
- package/dist/client/assets/RightSidebar-BltjUFwI.js +0 -1662
- package/dist/client/assets/RightSidebar-BltjUFwI.js.br +0 -0
- package/dist/client/assets/RightSidebar-BltjUFwI.js.gz +0 -0
- package/dist/client/assets/SessionChatView-CdKfHRp8.js +0 -13
- package/dist/client/assets/SessionChatView-CdKfHRp8.js.br +0 -0
- package/dist/client/assets/SessionChatView-CdKfHRp8.js.gz +0 -0
- package/dist/client/assets/SessionChatView-D8GoESjb.css +0 -1
- package/dist/client/assets/SessionChatView-D8GoESjb.css.br +0 -0
- package/dist/client/assets/SessionChatView-D8GoESjb.css.gz +0 -0
- package/dist/client/assets/SessionResourceSidebar-DmQOydhf.js +0 -2
- package/dist/client/assets/SessionResourceSidebar-DmQOydhf.js.br +0 -0
- package/dist/client/assets/SessionResourceSidebar-DmQOydhf.js.gz +0 -0
- package/dist/client/assets/TodoWorkspace-Bh1C035B.js +0 -1
- package/dist/client/assets/TodoWorkspace-Bh1C035B.js.br +0 -0
- package/dist/client/assets/TodoWorkspace-Bh1C035B.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-Cvf_KWZ0.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-Cvf_KWZ0.js.gz +0 -0
- package/dist/client/assets/app-DuUnqxq-.js +0 -37
- package/dist/client/assets/app-DuUnqxq-.js.br +0 -0
- package/dist/client/assets/app-DuUnqxq-.js.gz +0 -0
- package/dist/client/assets/arc-UvdowQOf.js.br +0 -0
- package/dist/client/assets/arc-UvdowQOf.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DotsbCCA.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-DotsbCCA.js.gz +0 -0
- package/dist/client/assets/arrow-up-DNkfesW0.js +0 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-CoOfSG4k.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CoOfSG4k.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BAfjgs8B.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BAfjgs8B.js.gz +0 -0
- package/dist/client/assets/channel-BzMU9Wks.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-BI3Di4Ve.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-BI3Di4Ve.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Cvvs1wmn.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Cvvs1wmn.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BZfMhm64.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BZfMhm64.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DmkEaRK3.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-DmkEaRK3.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-DtAtEoWU.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DtAtEoWU.js +0 -1
- package/dist/client/assets/clipboardText-cZXParp1.js +0 -1
- package/dist/client/assets/clipboardText-cZXParp1.js.br +0 -0
- package/dist/client/assets/clipboardText-cZXParp1.js.gz +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-0xPYMc2z.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-0xPYMc2z.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BQ7-lCHm.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BQ7-lCHm.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CazHuSj1.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CazHuSj1.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CJNgsyHQ.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CJNgsyHQ.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-D4xXIj1P.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-D4xXIj1P.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BWhpL-35.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BWhpL-35.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-DG9A7EgJ.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-DG9A7EgJ.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-L5PSvynM.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-L5PSvynM.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-moBH7ruR.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-moBH7ruR.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-DEiGgcRP.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-DEiGgcRP.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BboIleYi.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-BboIleYi.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-COxz7Rda.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-COxz7Rda.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-_XdRvgyZ.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-_XdRvgyZ.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CBb9xBNW.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CBb9xBNW.js.gz +0 -0
- package/dist/client/assets/index-nIgxJ6I3.css +0 -1
- package/dist/client/assets/index-nIgxJ6I3.css.br +0 -0
- package/dist/client/assets/index-nIgxJ6I3.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Bcr-lhIE.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Bcr-lhIE.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-3vK6s9Pq.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-3vK6s9Pq.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-DqDYDca0.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-DqDYDca0.js.gz +0 -0
- package/dist/client/assets/layout-C7_WwzCP.js.br +0 -0
- package/dist/client/assets/layout-C7_WwzCP.js.gz +0 -0
- package/dist/client/assets/linear-BQCRhl6P.js.br +0 -0
- package/dist/client/assets/linear-BQCRhl6P.js.gz +0 -0
- package/dist/client/assets/link-Cq14tNBm.js.gz +0 -0
- package/dist/client/assets/loader-circle-Df0zpx5N.js +0 -1
- package/dist/client/assets/mermaid.core-DGsccxt-.js.br +0 -0
- package/dist/client/assets/mermaid.core-DGsccxt-.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-l0eF1PQE.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-l0eF1PQE.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-B5VzheZz.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-fRrxGIDM.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-fRrxGIDM.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-B-EBa3qN.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-B-EBa3qN.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Dj4e6f4h.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Dj4e6f4h.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CEMppxTC.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CEMppxTC.js.gz +0 -0
- package/dist/client/assets/rdpSession-C5rS-ImV.js +0 -1
- package/dist/client/assets/rdpSession-C5rS-ImV.js.br +0 -0
- package/dist/client/assets/rdpSession-C5rS-ImV.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Dri1k4_x.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Dri1k4_x.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CMsL_tYL.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CMsL_tYL.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-4_4hZtOd.js.gz +0 -0
- package/dist/client/assets/settings-2-iGqzPez0.js +0 -1
- package/dist/client/assets/sizeCapture-INFHLROL-3QA6qj2j.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-3QA6qj2j.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-90IFPhD9.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-90IFPhD9.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CH-yvmL4.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BuHieVWC.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BuHieVWC.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-P_rDqbtg.js +0 -8
- package/dist/client/assets/swiper-DJjq1TYT.js +0 -1
- package/dist/client/assets/swiper-DJjq1TYT.js.br +0 -0
- package/dist/client/assets/swiper-DJjq1TYT.js.gz +0 -0
- package/dist/client/assets/syntaxHighlight-Cz6TPDW9.js +0 -4
- package/dist/client/assets/syntaxHighlight-Cz6TPDW9.js.br +0 -0
- package/dist/client/assets/syntaxHighlight-Cz6TPDW9.js.gz +0 -0
- package/dist/client/assets/terminal-Dea8wJF3.js.br +0 -0
- package/dist/client/assets/terminal-Dea8wJF3.js.gz +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-C7FpaNwf.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-C7FpaNwf.js.gz +0 -0
- package/dist/client/assets/triangle-alert-Oj-OsZDw.js +0 -1
- package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js +0 -1
- package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js.br +0 -0
- package/dist/client/assets/use-gesture-react.esm-DYpdiE_k.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CxoHHVcs.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CxoHHVcs.js.gz +0 -0
- package/dist/client/assets/videoCompression-rr2lu3uy.js.br +0 -0
- package/dist/client/assets/videoCompression-rr2lu3uy.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-B4Hsh-Ad.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-B4Hsh-Ad.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CghzsxQv.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CghzsxQv.js.gz +0 -0
|
@@ -4,15 +4,15 @@ import { dirname, resolve } from 'node:path';
|
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
5
|
import QRCode from 'qrcode';
|
|
6
6
|
import { RelayClient } from './relay.js';
|
|
7
|
+
import { openServiceSocket } from './serviceTransport.js';
|
|
8
|
+
import { serviceEndpoint } from './connectionModel.js';
|
|
7
9
|
function secureEndpoint(value, endpoint) {
|
|
8
10
|
const url = new URL(value);
|
|
9
11
|
if (!['https:', 'wss:'].includes(url.protocol) || url.username || url.password || url.search || url.hash)
|
|
10
12
|
throw new Error('Federation relay URLs must use HTTPS/WSS without credentials, query or fragment');
|
|
11
13
|
if (url.pathname !== '/' && url.pathname !== endpoint)
|
|
12
14
|
throw new Error('Federation relay URL must be a service origin or the exact tunnel endpoint');
|
|
13
|
-
url.
|
|
14
|
-
url.pathname = endpoint;
|
|
15
|
-
return url.toString();
|
|
15
|
+
return serviceEndpoint(url.origin, endpoint === '/api/federation/relay' ? 'relay' : 'secure').href;
|
|
16
16
|
}
|
|
17
17
|
export function parseRelayConfig(value) {
|
|
18
18
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -30,47 +30,12 @@ export function parseRelayConfig(value) {
|
|
|
30
30
|
}
|
|
31
31
|
return structuredClone(v);
|
|
32
32
|
}
|
|
33
|
-
function connect(url, ca, sockets, token, readyHandshake = false) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ws.once('error', failed);
|
|
40
|
-
ws.once('close', failed);
|
|
41
|
-
ws.once('open', () => {
|
|
42
|
-
ws.off('error', failed);
|
|
43
|
-
ws.off('close', failed);
|
|
44
|
-
ws.on('error', () => ws.terminate());
|
|
45
|
-
if (readyHandshake)
|
|
46
|
-
void awaitReady(ws).then(() => accept(ws), reject);
|
|
47
|
-
else
|
|
48
|
-
accept(ws);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
async function awaitReady(ws) {
|
|
53
|
-
return new Promise((accept, reject) => {
|
|
54
|
-
const timer = setTimeout(() => fail(), 10_000);
|
|
55
|
-
const cleanup = () => { clearTimeout(timer); ws.off('message', ready); ws.off('close', fail); };
|
|
56
|
-
const fail = () => { cleanup(); ws.terminate(); reject(new Error('Relay authentication failed')); };
|
|
57
|
-
const ready = (raw) => {
|
|
58
|
-
try {
|
|
59
|
-
const frame = JSON.parse(raw.toString());
|
|
60
|
-
if (frame.type !== 'ready') {
|
|
61
|
-
fail();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
cleanup();
|
|
65
|
-
accept();
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
fail();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
ws.once('message', ready);
|
|
72
|
-
ws.once('close', fail);
|
|
73
|
-
});
|
|
33
|
+
async function connect(url, ca, sockets, token, readyHandshake = false, signal) {
|
|
34
|
+
const ws = await openServiceSocket(url, { ca }, { maxPayload: 256 * 1024, handshakeTimeout: 10_000, relayReady: readyHandshake, signal,
|
|
35
|
+
...(token ? { headers: { Authorization: `Bearer ${token}` } } : {}) });
|
|
36
|
+
sockets.add(ws);
|
|
37
|
+
ws.once('close', () => sockets.delete(ws));
|
|
38
|
+
return ws;
|
|
74
39
|
}
|
|
75
40
|
export function pairingInviteUrl(serviceId, code, value) {
|
|
76
41
|
const url = new URL(value);
|
|
@@ -131,11 +96,12 @@ export async function runFederationCli(args) {
|
|
|
131
96
|
const loadCa = (file) => file ? readFileSync(resolve(dirname(configPath), file)) : undefined;
|
|
132
97
|
const entryCa = loadCa(config.caPath);
|
|
133
98
|
const targets = config.targets.map(t => ({ ...t, ca: loadCa(t.caPath ?? config.caPath) }));
|
|
99
|
+
const lifetime = new AbortController();
|
|
134
100
|
const sockets = new Set();
|
|
135
101
|
let stopped = false;
|
|
136
102
|
let client;
|
|
137
103
|
let wake;
|
|
138
|
-
const stop = () => { stopped = true; wake?.(); client?.close(); for (const socket of sockets)
|
|
104
|
+
const stop = () => { stopped = true; lifetime.abort(); wake?.(); client?.close(); for (const socket of sockets)
|
|
139
105
|
socket.terminate(); };
|
|
140
106
|
process.once('SIGINT', stop);
|
|
141
107
|
process.once('SIGTERM', stop);
|
|
@@ -143,12 +109,12 @@ export async function runFederationCli(args) {
|
|
|
143
109
|
try {
|
|
144
110
|
while (!stopped) {
|
|
145
111
|
try {
|
|
146
|
-
const upstream = await connect(secureEndpoint(config.entryUrl, '/api/federation/relay'), entryCa, sockets, config.relayToken, true);
|
|
112
|
+
const upstream = await connect(secureEndpoint(config.entryUrl, '/api/federation/relay'), entryCa, sockets, config.relayToken, true, lifetime.signal);
|
|
147
113
|
if (stopped) {
|
|
148
114
|
upstream.terminate();
|
|
149
115
|
break;
|
|
150
116
|
}
|
|
151
|
-
client = new RelayClient(upstream, { targets: new Map(targets.map(t => [t.serviceId, () => connect(secureEndpoint(t.url, '/api/federation/secure'), t.ca, sockets)])) });
|
|
117
|
+
client = new RelayClient(upstream, { targets: new Map(targets.map(t => [t.serviceId, (signal) => connect(secureEndpoint(t.url, '/api/federation/secure'), t.ca, sockets, undefined, false, AbortSignal.any([signal, lifetime.signal]))])) });
|
|
152
118
|
process.stderr.write('加密中继已连接。\n');
|
|
153
119
|
delay = 1000;
|
|
154
120
|
await new Promise(done => { if (upstream.readyState !== WebSocket.OPEN)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const messages = {
|
|
2
|
+
RELAY_AUTHORIZATION_REQUIRED: '没有有效中转授权', TARGET_AUTHORIZATION_REQUIRED: '目标服务需要重新登录',
|
|
3
|
+
PERMISSION_DENIED: '当前权限不允许此操作', TARGET_IDENTITY_MISMATCH: '服务身份与保存的身份不一致',
|
|
4
|
+
RELAY_SELF_TARGET: '不能通过服务中转到其自身;同一服务的其它地址请添加为备用地址。',
|
|
5
|
+
};
|
|
6
|
+
export class ConnectionAccessError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
context;
|
|
9
|
+
name = 'ConnectionAccessError';
|
|
10
|
+
constructor(code, context = {}) {
|
|
11
|
+
super(messages[code]);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.context = context;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function connectionAccessError(error, context = {}) {
|
|
17
|
+
if (error instanceof ConnectionAccessError) {
|
|
18
|
+
Object.assign(error.context, { ...context, ...error.context });
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
// Retained workspaces/test module reloads can carry the same protocol error
|
|
22
|
+
// from another module instance. Never accept arbitrary messages as a code.
|
|
23
|
+
if (error instanceof Error && error.name === 'ConnectionAccessError' && 'code' in error
|
|
24
|
+
&& typeof error.code === 'string' && Object.hasOwn(messages, error.code)) {
|
|
25
|
+
return new ConnectionAccessError(error.code, context);
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof Error && error.name === 'UnexpectedPeerError')
|
|
28
|
+
return new ConnectionAccessError('TARGET_IDENTITY_MISMATCH', context);
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export function relayAuthorizationError(error, context) {
|
|
32
|
+
if (error instanceof Error && (error.message === 'AUTHORIZATION_DENIED'
|
|
33
|
+
|| error instanceof ConnectionAccessError && error.code === 'PERMISSION_DENIED')) {
|
|
34
|
+
return new ConnectionAccessError('RELAY_AUTHORIZATION_REQUIRED', context);
|
|
35
|
+
}
|
|
36
|
+
return error instanceof Error ? error : new Error('中转连接暂不可用');
|
|
37
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ConnectionAccessError } from './connectionErrors.js';
|
|
2
|
+
export const MAX_SAVED_ROUTES = 4;
|
|
3
|
+
export function normalizeServiceAddress(value) {
|
|
4
|
+
if (typeof value !== 'string' || !value.trim() || value.length > 2048)
|
|
5
|
+
throw new Error('请输入服务地址。');
|
|
6
|
+
const raw = value.trim();
|
|
7
|
+
const url = new URL(/^[a-z][a-z\d+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`);
|
|
8
|
+
if (url.protocol === 'wss:')
|
|
9
|
+
url.protocol = 'https:';
|
|
10
|
+
if (url.protocol === 'ws:')
|
|
11
|
+
url.protocol = 'http:';
|
|
12
|
+
const loopback = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
13
|
+
if (!['https:', 'http:'].includes(url.protocol) || url.protocol === 'http:' && !loopback)
|
|
14
|
+
throw new Error('请使用 HTTPS 服务地址。');
|
|
15
|
+
if (url.username || url.password || url.search || url.hash || !['/', '/api/federation/secure'].includes(url.pathname))
|
|
16
|
+
throw new Error('请输入服务首页地址,不要包含账号或额外参数。');
|
|
17
|
+
return url.origin;
|
|
18
|
+
}
|
|
19
|
+
export function normalizedRoutes(record) {
|
|
20
|
+
if (record.entryServiceId && record.entryServiceId === record.targetPeerId)
|
|
21
|
+
throw new ConnectionAccessError('RELAY_SELF_TARGET', { entryServiceId: record.entryServiceId, targetServiceId: record.targetPeerId });
|
|
22
|
+
const candidates = Array.isArray(record.routes) ? record.routes.slice(0, 64) : [];
|
|
23
|
+
if (record.entryServiceId)
|
|
24
|
+
candidates.unshift({ url: record.url, targetPeerId: record.entryServiceId });
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
return candidates.flatMap(route => {
|
|
27
|
+
try {
|
|
28
|
+
if (!route || typeof route.targetPeerId !== 'string' || !/^[a-zA-Z0-9_.:-]{1,160}$/.test(route.targetPeerId))
|
|
29
|
+
return [];
|
|
30
|
+
const url = normalizeServiceAddress(route.url), key = JSON.stringify([route.targetPeerId, url]);
|
|
31
|
+
if (seen.has(key))
|
|
32
|
+
return [];
|
|
33
|
+
seen.add(key);
|
|
34
|
+
return [{ url, targetPeerId: route.targetPeerId }];
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}).slice(0, MAX_SAVED_ROUTES);
|
|
40
|
+
}
|
|
41
|
+
export function relayConnectionRoutes(record) {
|
|
42
|
+
return normalizedRoutes(record).filter(route => route.targetPeerId !== record.targetPeerId);
|
|
43
|
+
}
|
|
44
|
+
export function directConnectionAddresses(record) {
|
|
45
|
+
const routes = normalizedRoutes(record);
|
|
46
|
+
const entryUrls = new Set(routes.filter(route => route.targetPeerId !== record.targetPeerId).map(route => route.url));
|
|
47
|
+
const candidates = [record.serviceOrigin || record.url, ...routes.filter(route => route.targetPeerId === record.targetPeerId).map(route => route.url)];
|
|
48
|
+
const result = new Set();
|
|
49
|
+
for (const value of candidates)
|
|
50
|
+
try {
|
|
51
|
+
const address = normalizeServiceAddress(value);
|
|
52
|
+
if (!entryUrls.has(address))
|
|
53
|
+
result.add(address);
|
|
54
|
+
}
|
|
55
|
+
catch { /* An invalid address cannot substitute for a saved identity. */ }
|
|
56
|
+
return [...result];
|
|
57
|
+
}
|
|
58
|
+
export function hasIndependentConnectionRoute(record, deniedEntryServiceId, excludedEntries = new Set()) {
|
|
59
|
+
return directConnectionAddresses(record).length > 0
|
|
60
|
+
|| !!deniedEntryServiceId && relayConnectionRoutes(record).some(route => route.targetPeerId !== deniedEntryServiceId && !excludedEntries.has(route.targetPeerId));
|
|
61
|
+
}
|
|
62
|
+
/** Run after merges, including imports from old native clients. Otherwise an
|
|
63
|
+
* omitted serviceOrigin can retain an old entry URL from the previous record. */
|
|
64
|
+
export function canonicalConnectionRouting(record) {
|
|
65
|
+
const result = { ...record, url: normalizeServiceAddress(record.url) };
|
|
66
|
+
const routes = normalizedRoutes(record);
|
|
67
|
+
if (record.routes !== undefined)
|
|
68
|
+
result.routes = routes;
|
|
69
|
+
if (record.serviceOrigin) {
|
|
70
|
+
const origin = normalizeServiceAddress(record.serviceOrigin);
|
|
71
|
+
if (routes.some(route => route.targetPeerId !== record.targetPeerId && route.url === origin))
|
|
72
|
+
delete result.serviceOrigin;
|
|
73
|
+
else
|
|
74
|
+
result.serviceOrigin = origin;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
export function serviceEndpoint(address, kind = 'secure') {
|
|
79
|
+
const url = new URL(normalizeServiceAddress(address));
|
|
80
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
81
|
+
url.pathname = `/api/federation/${kind}`;
|
|
82
|
+
return url;
|
|
83
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { directConnectionAddresses } from './connectionModel.js';
|
|
2
4
|
const peer = (value) => typeof value === 'string' && /^[a-zA-Z0-9_.:-]{1,160}$/.test(value);
|
|
3
5
|
const fingerprint = (value) => typeof value === 'string' && /^(?:[\da-f]{2}:){31}[\da-f]{2}$/i.test(value);
|
|
4
6
|
function origin(value) {
|
|
@@ -18,7 +20,7 @@ function origin(value) {
|
|
|
18
20
|
* have been pinned there before its administrator can discover other entries.
|
|
19
21
|
* This supplies addresses, never the Desktop client's device authorization.
|
|
20
22
|
*/
|
|
21
|
-
export function desktopDirectTargets(file, localServiceId) {
|
|
23
|
+
export function desktopDirectTargets(file, localServiceId, format = 'electron') {
|
|
22
24
|
if (!localServiceId)
|
|
23
25
|
return [];
|
|
24
26
|
try {
|
|
@@ -27,7 +29,8 @@ export function desktopDirectTargets(file, localServiceId) {
|
|
|
27
29
|
const data = JSON.parse(readFileSync(file, 'utf8'));
|
|
28
30
|
if (data?.version !== 1 || !Array.isArray(data.connections) || data.connections.length > 256)
|
|
29
31
|
return [];
|
|
30
|
-
const
|
|
32
|
+
const removedKeys = format === 'preview' ? data.removedKeys : data.removedServiceKeys;
|
|
33
|
+
const removed = new Set(Array.isArray(removedKeys) ? removedKeys : []);
|
|
31
34
|
const connections = data.connections.filter((item) => item && ![item.id, item.targetPeerId, item.serviceOrigin || item.url].some(key => removed.has(key)));
|
|
32
35
|
if (!connections.some((item) => item.targetPeerId === localServiceId))
|
|
33
36
|
return [];
|
|
@@ -42,17 +45,23 @@ export function desktopDirectTargets(file, localServiceId) {
|
|
|
42
45
|
for (const item of connections) {
|
|
43
46
|
if (!peer(item.targetPeerId) || item.targetPeerId === localServiceId)
|
|
44
47
|
continue;
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
for (const address of directConnectionAddresses(item)) {
|
|
49
|
+
const url = origin(address);
|
|
50
|
+
if (!url)
|
|
51
|
+
continue;
|
|
52
|
+
const caFingerprint256 = trusts.get(url);
|
|
53
|
+
const pin = format === 'preview' ? data.certificatePins?.[url] : undefined;
|
|
54
|
+
// Preview pins the leaf certificate, whereas Electron approves a CA.
|
|
55
|
+
// Never reinterpret one as the other or discard an invalid saved pin.
|
|
56
|
+
if (pin !== undefined && (typeof pin !== 'string' || !/^[\da-f]{64}$/i.test(pin)))
|
|
57
|
+
continue;
|
|
58
|
+
targets.set(JSON.stringify([item.targetPeerId, url]), { serviceId: item.targetPeerId, url,
|
|
59
|
+
...(typeof item.label === 'string' && item.label.trim() ? { label: item.label.trim().slice(0, 120) } : {}),
|
|
60
|
+
...(format === 'electron' && caFingerprint256 ? { caFingerprint256 } : {}),
|
|
61
|
+
...(pin ? { certificateFingerprint256: pin.toUpperCase() } : {}) });
|
|
62
|
+
if (targets.size >= 64)
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
56
65
|
if (targets.size >= 64)
|
|
57
66
|
break;
|
|
58
67
|
}
|
|
@@ -62,3 +71,16 @@ export function desktopDirectTargets(file, localServiceId) {
|
|
|
62
71
|
return [];
|
|
63
72
|
}
|
|
64
73
|
}
|
|
74
|
+
/** Both installations keep their own bookmarks and trust. Reading Preview's
|
|
75
|
+
* native directory also supports already-installed Preview apps, without
|
|
76
|
+
* copying credentials or overwriting the Electron configuration. */
|
|
77
|
+
export function desktopServiceTargets(home, localServiceId, platform = process.platform) {
|
|
78
|
+
const previewRoot = platform === 'darwin' ? join(home, 'Library', 'Application Support')
|
|
79
|
+
: platform === 'win32' ? process.env.APPDATA || join(home, 'AppData', 'Roaming')
|
|
80
|
+
: process.env.XDG_CONFIG_HOME || join(home, '.config');
|
|
81
|
+
const key = (target) => JSON.stringify([target.serviceId, target.url]);
|
|
82
|
+
const targets = new Map(desktopDirectTargets(join(home, '.termdock', 'desktop.json'), localServiceId).map(target => [key(target), target]));
|
|
83
|
+
for (const target of desktopDirectTargets(join(previewRoot, 'com.jovines.termdock.preview', 'connections.json'), localServiceId, 'preview'))
|
|
84
|
+
targets.set(key(target), target);
|
|
85
|
+
return [...targets.values()].slice(0, 64);
|
|
86
|
+
}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { readPinnedCertificateAuthority } from './trustedCa.js';
|
|
2
1
|
import { createIdentity, secureConnection } from './secureProtocol.js';
|
|
3
2
|
import { socketDuplex } from './socketDuplex.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import WebSocket from 'ws';
|
|
3
|
+
import { openServiceSocket } from './serviceTransport.js';
|
|
4
|
+
import { serviceEndpoint } from './connectionModel.js';
|
|
7
5
|
function endpoint(config) {
|
|
8
|
-
if (!config || typeof config !== 'object' || Object.keys(config).some(key => !['serviceId', 'url', 'caPath', 'caFingerprint256', 'label'].includes(key)) || !/^[a-zA-Z0-9_.:-]{1,160}$/.test(config.serviceId) || typeof config.url !== 'string' || (config.caPath !== undefined && typeof config.caPath !== 'string'))
|
|
6
|
+
if (!config || typeof config !== 'object' || Object.keys(config).some(key => !['serviceId', 'url', 'caPath', 'caFingerprint256', 'certificateFingerprint256', 'label'].includes(key)) || !/^[a-zA-Z0-9_.:-]{1,160}$/.test(config.serviceId) || typeof config.url !== 'string' || (config.caPath !== undefined && typeof config.caPath !== 'string'))
|
|
9
7
|
throw new Error('Invalid direct target configuration');
|
|
10
8
|
if ((config.caFingerprint256 !== undefined && (typeof config.caFingerprint256 !== 'string' || !/^(?:[\da-f]{2}:){31}[\da-f]{2}$/i.test(config.caFingerprint256))) || (config.caPath && config.caFingerprint256) || (config.label !== undefined && (typeof config.label !== 'string' || config.label.length > 120)))
|
|
11
9
|
throw new Error('Invalid direct target metadata');
|
|
10
|
+
if (config.certificateFingerprint256 !== undefined && (typeof config.certificateFingerprint256 !== 'string' || !/^[\da-f]{64}$/i.test(config.certificateFingerprint256)
|
|
11
|
+
|| config.caPath !== undefined || config.caFingerprint256 !== undefined))
|
|
12
|
+
throw new Error('Invalid direct target certificate pin');
|
|
12
13
|
const url = new URL(config.url);
|
|
13
14
|
if (!['https:', 'wss:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || !['/', '/api/federation/secure'].includes(url.pathname))
|
|
14
15
|
throw new Error('Direct targets require a verified HTTPS/WSS origin');
|
|
15
|
-
url.
|
|
16
|
-
url.pathname = '/api/federation/secure';
|
|
17
|
-
return url.toString();
|
|
16
|
+
return serviceEndpoint(url.origin).href;
|
|
18
17
|
}
|
|
19
18
|
/** Registers only administrator-configured destinations. TLS validation and redirect
|
|
20
19
|
* refusal apply even though the inner session also has end-to-end encryption. */
|
|
@@ -24,37 +23,32 @@ export function attachRegisteredDirectTargets(router, configs, baseDir = process
|
|
|
24
23
|
const ids = new Set();
|
|
25
24
|
const targets = configs.map(config => {
|
|
26
25
|
const url = endpoint(config);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const key = JSON.stringify([config.serviceId, url]);
|
|
27
|
+
if (ids.has(key))
|
|
28
|
+
throw new Error('Duplicate direct target address');
|
|
29
|
+
ids.add(key);
|
|
30
|
+
return { config, serviceId: config.serviceId, url };
|
|
31
31
|
});
|
|
32
32
|
let stopped = false;
|
|
33
33
|
const sockets = new Set();
|
|
34
34
|
const cleanups = [];
|
|
35
|
-
const
|
|
35
|
+
const lifetime = new AbortController();
|
|
36
|
+
const cleanup = () => { stopped = true; lifetime.abort(); for (const close of cleanups.splice(0))
|
|
36
37
|
close(); for (const ws of sockets)
|
|
37
38
|
ws.terminate(); };
|
|
38
39
|
try {
|
|
39
40
|
for (const target of targets)
|
|
40
|
-
cleanups.push(router.registerDirect(target.serviceId, async () => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
ws.once('error', failed);
|
|
52
|
-
ws.once('close', failed);
|
|
53
|
-
ws.once('open', () => { ws.off('error', failed); ws.off('close', failed); ws.on('error', () => ws.terminate()); if (stopped)
|
|
54
|
-
failed();
|
|
55
|
-
else
|
|
56
|
-
accept(ws); });
|
|
57
|
-
});
|
|
41
|
+
cleanups.push(router.registerDirect(target.serviceId, async (signal) => {
|
|
42
|
+
if (stopped)
|
|
43
|
+
throw new Error('Direct target stopped');
|
|
44
|
+
const ws = await openServiceSocket(target.url, target.config, { baseDir, maxPayload: 256 * 1024, handshakeTimeout: 10000, signal: AbortSignal.any([signal, lifetime.signal]) });
|
|
45
|
+
if (stopped) {
|
|
46
|
+
ws.terminate();
|
|
47
|
+
throw new Error('Direct target stopped');
|
|
48
|
+
}
|
|
49
|
+
sockets.add(ws);
|
|
50
|
+
ws.once('close', () => sockets.delete(ws));
|
|
51
|
+
return ws;
|
|
58
52
|
}));
|
|
59
53
|
}
|
|
60
54
|
catch (error) {
|
|
@@ -65,14 +59,12 @@ export function attachRegisteredDirectTargets(router, configs, baseDir = process
|
|
|
65
59
|
}
|
|
66
60
|
/** An administrator enabling a new backup first proves the target's pinned
|
|
67
61
|
* identity; HTTPS validation remains enabled throughout. */
|
|
68
|
-
export async function verifyDirectTarget(config) {
|
|
62
|
+
export async function verifyDirectTarget(config, baseDir = process.cwd(), signal) {
|
|
69
63
|
const url = endpoint(config);
|
|
70
|
-
const
|
|
71
|
-
const ws = new WebSocket(url, { ca, rejectUnauthorized: true, followRedirects: false, handshakeTimeout: 5000, maxPayload: 256 * 1024 });
|
|
64
|
+
const ws = await openServiceSocket(url, config, { baseDir, maxPayload: 256 * 1024, signal });
|
|
72
65
|
const duplex = socketDuplex(ws);
|
|
73
66
|
try {
|
|
74
|
-
await
|
|
75
|
-
await secureConnection({ identity: await createIdentity(), initiator: true, targetPinnedPeerId: config.serviceId, duplex, signal: AbortSignal.timeout(5000) });
|
|
67
|
+
await secureConnection({ identity: await createIdentity(), initiator: true, targetPinnedPeerId: config.serviceId, duplex, signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(5000)]) : AbortSignal.timeout(5000) });
|
|
76
68
|
}
|
|
77
69
|
finally {
|
|
78
70
|
ws.close();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { attachRegisteredDirectTargets, verifyDirectTarget } from './directRoutes.js';
|
|
2
|
+
import { normalizeServiceAddress } from './connectionModel.js';
|
|
3
|
+
/** A static configuration is a claim, not proof that its URL serves its ID.
|
|
4
|
+
* Advertise direct providers only after a real TLS + pinned Noise handshake.
|
|
5
|
+
* Reverse providers remain independent and keep working during these checks. */
|
|
6
|
+
export class DirectTargetRegistry {
|
|
7
|
+
router;
|
|
8
|
+
baseDir;
|
|
9
|
+
now;
|
|
10
|
+
states = new Map();
|
|
11
|
+
stopped = false;
|
|
12
|
+
activeChecks = 0;
|
|
13
|
+
constructor(router, baseDir, now = () => performance.now()) {
|
|
14
|
+
this.router = router;
|
|
15
|
+
this.baseDir = baseDir;
|
|
16
|
+
this.now = now;
|
|
17
|
+
}
|
|
18
|
+
refresh(targets) {
|
|
19
|
+
if (this.stopped)
|
|
20
|
+
return;
|
|
21
|
+
const desired = new Map(targets.map(target => [this.key(target), target]));
|
|
22
|
+
const signature = (target) => JSON.stringify([target.url, target.caPath, target.caFingerprint256, target.certificateFingerprint256]);
|
|
23
|
+
for (const [id, state] of this.states) {
|
|
24
|
+
const target = desired.get(id);
|
|
25
|
+
if (!target || signature(target) !== state.signature) {
|
|
26
|
+
state.controller?.abort();
|
|
27
|
+
state.close?.();
|
|
28
|
+
this.states.delete(id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const [id, target] of desired) {
|
|
32
|
+
let state = this.states.get(id);
|
|
33
|
+
if (!state) {
|
|
34
|
+
state = { target, signature: signature(target), checking: false, nextCheck: 0, error: 'TARGET_CHECKING' };
|
|
35
|
+
this.states.set(id, state);
|
|
36
|
+
}
|
|
37
|
+
state.target = target;
|
|
38
|
+
}
|
|
39
|
+
this.pump();
|
|
40
|
+
}
|
|
41
|
+
key(target) { return JSON.stringify([target.serviceId, normalizeServiceAddress(target.url)]); }
|
|
42
|
+
providers(serviceId) { return [...this.states.values()].filter(state => state.target.serviceId === serviceId); }
|
|
43
|
+
pump() {
|
|
44
|
+
if (this.stopped)
|
|
45
|
+
return;
|
|
46
|
+
for (const state of this.states.values()) {
|
|
47
|
+
if (this.activeChecks >= 4)
|
|
48
|
+
break;
|
|
49
|
+
if (!state.checking && this.now() >= state.nextCheck)
|
|
50
|
+
void this.check(state.target, state);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
error(serviceId) {
|
|
54
|
+
const states = this.providers(serviceId);
|
|
55
|
+
if (states.some(state => state.close))
|
|
56
|
+
return;
|
|
57
|
+
return ['TARGET_CHECKING', 'TARGET_IDENTITY_MISMATCH', 'TARGET_UNREACHABLE'].find(error => states.some(state => state.error === error));
|
|
58
|
+
}
|
|
59
|
+
describe(target) {
|
|
60
|
+
const state = this.providers(target.serviceId).find(provider => provider.close);
|
|
61
|
+
const available = this.router.hasRoute(target.serviceId);
|
|
62
|
+
// A valid reverse tunnel may coexist with an incorrect static URL. Do not
|
|
63
|
+
// borrow the unverified URL/name and make A look like the entry B.
|
|
64
|
+
const metadata = state ? { serviceId: target.serviceId, url: normalizeServiceAddress(state.target.url), ...(state.target.label ? { label: state.target.label } : {}) }
|
|
65
|
+
: target.url ? { serviceId: target.serviceId } : target;
|
|
66
|
+
const error = this.error(target.serviceId);
|
|
67
|
+
return { ...metadata, available, ...(!available && error ? { error } : {}) };
|
|
68
|
+
}
|
|
69
|
+
async check(target, state) {
|
|
70
|
+
state.checking = true;
|
|
71
|
+
this.activeChecks++;
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
state.controller = controller;
|
|
74
|
+
const current = () => !this.stopped && this.states.get(this.key(target)) === state;
|
|
75
|
+
try {
|
|
76
|
+
await verifyDirectTarget(target, this.baseDir, controller.signal);
|
|
77
|
+
if (!current())
|
|
78
|
+
return;
|
|
79
|
+
state.close ??= attachRegisteredDirectTargets(this.router, [target], this.baseDir);
|
|
80
|
+
state.error = undefined;
|
|
81
|
+
state.nextCheck = this.now() + 30_000;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (!current())
|
|
85
|
+
return;
|
|
86
|
+
state.close?.();
|
|
87
|
+
state.close = undefined;
|
|
88
|
+
state.error = error instanceof Error && error.name === 'UnexpectedPeerError' ? 'TARGET_IDENTITY_MISMATCH' : 'TARGET_UNREACHABLE';
|
|
89
|
+
state.nextCheck = this.now() + 5000;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
state.checking = false;
|
|
93
|
+
state.controller = undefined;
|
|
94
|
+
this.activeChecks--;
|
|
95
|
+
this.pump();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
close() {
|
|
99
|
+
this.stopped = true;
|
|
100
|
+
for (const state of this.states.values()) {
|
|
101
|
+
state.controller?.abort();
|
|
102
|
+
state.close?.();
|
|
103
|
+
}
|
|
104
|
+
this.states.clear();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const MIB = 1024 * 1024;
|
|
2
|
+
/** File bodies include multipart framing; filesystem handlers still enforce
|
|
3
|
+
* the 100 MiB attachment limit. This budget never grants endpoint access. */
|
|
4
|
+
export function httpUploadLimit(pathname) {
|
|
5
|
+
const fileUpload = pathname === '/api/terminal/fs/upload'
|
|
6
|
+
|| /^\/api\/terminal\/[^/]+\/chat\/attachments$/.test(pathname);
|
|
7
|
+
return (fileUpload ? 110 : 5) * MIB;
|
|
8
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RELAY_PROVIDER_CONNECT_TIMEOUT_MS, RELAY_ROUTE_OPEN_TIMEOUT_MS } from './streamLimits.js';
|
|
1
|
+
import { RELAY_PROVIDER_CONNECT_TIMEOUT_MS, RELAY_ROUTE_OPEN_TIMEOUT_MS, RELAY_MAX_FRAME_BYTES, RELAY_MAX_BUFFERED_BYTES } from './streamLimits.js';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
|
-
const defaults = { maxFrameBytes:
|
|
4
|
+
const defaults = { maxFrameBytes: RELAY_MAX_FRAME_BYTES, maxBufferedBytes: RELAY_MAX_BUFFERED_BYTES,
|
|
5
5
|
maxStreams: 128, heartbeatMs: 30_000, maxRouteTtlMs: 90_000, connectTimeoutMs: RELAY_PROVIDER_CONNECT_TIMEOUT_MS, routeTimeoutMs: RELAY_ROUTE_OPEN_TIMEOUT_MS };
|
|
6
6
|
const identifier = (v) => typeof v === 'string' && /^[a-zA-Z0-9_.:-]{1,160}$/.test(v);
|
|
7
7
|
function parse(raw, limits) {
|
|
@@ -118,15 +118,16 @@ export class RelayRouter {
|
|
|
118
118
|
.map(([peer]) => peer);
|
|
119
119
|
}
|
|
120
120
|
hasRoute(serviceId) {
|
|
121
|
-
return
|
|
121
|
+
return this.providerCount(serviceId) > 0;
|
|
122
122
|
}
|
|
123
|
+
providerCount(serviceId) { return this.stopped ? 0 : this.providers(serviceId).length; }
|
|
123
124
|
activeRouteIds() {
|
|
124
125
|
return [...this.routes.keys()].filter(serviceId => this.hasRoute(serviceId));
|
|
125
126
|
}
|
|
126
127
|
/** Trusted local configuration only. Clients never supply a target URL or factory. */
|
|
127
128
|
registerDirect(serviceId, connect) {
|
|
128
|
-
if (this.stopped || !identifier(serviceId) || typeof connect !== 'function'
|
|
129
|
-
throw new Error('Invalid
|
|
129
|
+
if (this.stopped || !identifier(serviceId) || typeof connect !== 'function')
|
|
130
|
+
throw new Error('Invalid direct target');
|
|
130
131
|
const [routerWire, clientWire] = memoryPair();
|
|
131
132
|
// The private WeakMap marks this peer; its placeholder principal is never
|
|
132
133
|
// passed to externally supplied authentication/authorization callbacks.
|
|
@@ -342,6 +343,7 @@ export class RelayClient {
|
|
|
342
343
|
streams = new Map();
|
|
343
344
|
renewal;
|
|
344
345
|
pending = new Map();
|
|
346
|
+
connecting = new Map();
|
|
345
347
|
stopped = false;
|
|
346
348
|
constructor(upstream, options) {
|
|
347
349
|
this.upstream = upstream;
|
|
@@ -358,6 +360,8 @@ export class RelayClient {
|
|
|
358
360
|
drop(id, reason) {
|
|
359
361
|
clearTimeout(this.pending.get(id));
|
|
360
362
|
this.pending.delete(id);
|
|
363
|
+
this.connecting.get(id)?.abort();
|
|
364
|
+
this.connecting.delete(id);
|
|
361
365
|
const target = this.streams.get(id);
|
|
362
366
|
this.streams.delete(id);
|
|
363
367
|
target?.close();
|
|
@@ -379,9 +383,11 @@ export class RelayClient {
|
|
|
379
383
|
const timer = setTimeout(() => { this.drop(f.streamId, 'Target timeout'); }, this.limits.connectTimeoutMs);
|
|
380
384
|
timer.unref?.();
|
|
381
385
|
this.pending.set(f.streamId, timer);
|
|
386
|
+
const controller = new AbortController();
|
|
387
|
+
this.connecting.set(f.streamId, controller);
|
|
382
388
|
let target;
|
|
383
389
|
try {
|
|
384
|
-
target = await connect();
|
|
390
|
+
target = await connect(controller.signal);
|
|
385
391
|
}
|
|
386
392
|
catch {
|
|
387
393
|
if (this.pending.get(f.streamId) === timer)
|
|
@@ -395,6 +401,7 @@ export class RelayClient {
|
|
|
395
401
|
}
|
|
396
402
|
clearTimeout(timer);
|
|
397
403
|
this.pending.delete(f.streamId);
|
|
404
|
+
this.connecting.delete(f.streamId);
|
|
398
405
|
if (target.readyState !== 1) {
|
|
399
406
|
target.close();
|
|
400
407
|
this.drop(f.streamId, 'Target not ready');
|