termdock 1.4.330 → 1.4.332
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/THIRD_PARTY_NOTICES.md +12 -0
- package/dist/client/assets/{App-JL8Fd8uO.js → App-BB9Zns7R.js} +37 -37
- package/dist/client/assets/App-BB9Zns7R.js.br +0 -0
- package/dist/client/assets/App-BB9Zns7R.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-DCxpe1Bj.js +9 -0
- package/dist/client/assets/ArchitecturePanel-DCxpe1Bj.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-DCxpe1Bj.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BF3p_M6Y.js → ChangeWalkthroughPanel-CAyVfccZ.js} +1 -1
- package/dist/client/assets/ChangeWalkthroughPanel-CAyVfccZ.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-CAyVfccZ.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-Bey7tYQF.js +1 -0
- package/dist/client/assets/CollaborationMainWorkspace-Bey7tYQF.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-Bey7tYQF.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-Q5KzPAOz.js +2 -0
- package/dist/client/assets/{ComputerControlView-DWlZmlCH.js → ComputerControlView-CojIu02o.js} +3 -3
- package/dist/client/assets/ComputerControlView-CojIu02o.js.br +0 -0
- package/dist/client/assets/ComputerControlView-CojIu02o.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-IbzlcDVX.js → DagPlayground-BeyWYMaL.js} +1 -1
- package/dist/client/assets/DagPlayground-BeyWYMaL.js.br +0 -0
- package/dist/client/assets/DagPlayground-BeyWYMaL.js.gz +0 -0
- package/dist/client/assets/{DiffLab-DEAYEwfY.js → DiffLab-DW3LP4Zk.js} +1 -1
- package/dist/client/assets/DiffLab-DW3LP4Zk.js.br +0 -0
- package/dist/client/assets/DiffLab-DW3LP4Zk.js.gz +0 -0
- package/dist/client/assets/{DiffReview-BPHfC8nZ.js → DiffReview-CsQjyKYd.js} +1 -1
- package/dist/client/assets/DiffReview-CsQjyKYd.js.br +0 -0
- package/dist/client/assets/DiffReview-CsQjyKYd.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-2iKu20Ik.js → DiffReviewLab-DRvhZLgu.js} +1 -1
- package/dist/client/assets/DiffReviewLab-DRvhZLgu.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-DRvhZLgu.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-C4S3oEQV.js → DiffViewer-CwTOIjTT.js} +1 -1
- package/dist/client/assets/DiffViewer-CwTOIjTT.js.br +0 -0
- package/dist/client/assets/DiffViewer-CwTOIjTT.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-D_KABt7i.js → ModelPreview-BHmMm0Pj.js} +1 -1
- package/dist/client/assets/ModelPreview-BHmMm0Pj.js.br +0 -0
- package/dist/client/assets/ModelPreview-BHmMm0Pj.js.gz +0 -0
- package/dist/client/assets/RightSidebar-Coc7x6Q-.js +1677 -0
- package/dist/client/assets/RightSidebar-Coc7x6Q-.js.br +0 -0
- package/dist/client/assets/RightSidebar-Coc7x6Q-.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-OAQJcJ5a.js → abnfDiagram-VCTEODGH-wY9KgDKQ.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-wY9KgDKQ.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-wY9KgDKQ.js.gz +0 -0
- package/dist/client/assets/app-D-AilvVY.js +35 -0
- package/dist/client/assets/app-D-AilvVY.js.br +0 -0
- package/dist/client/assets/app-D-AilvVY.js.gz +0 -0
- package/dist/client/assets/{arc-B29HLhod.js → arc-DVBtoEGf.js} +1 -1
- package/dist/client/assets/arc-DVBtoEGf.js.br +0 -0
- package/dist/client/assets/arc-DVBtoEGf.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-CQeKeLzl.js → architectureDiagram-5GKGNRK7-CQPsSdTB.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CQPsSdTB.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CQPsSdTB.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CgQQGQN_.js → blockDiagram-I7D4REHJ-OB_hCQwv.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-OB_hCQwv.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-OB_hCQwv.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-BTeUIAS9.js → c4Diagram-7LVT6UL2-Cp72_koy.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-Cp72_koy.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-Cp72_koy.js.gz +0 -0
- package/dist/client/assets/channel-CYCNCfSN.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-Bao8D59Y.js → chunk-2Q5K7J3B-wZpiUjnH.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-RIpn4ucJ.js → chunk-5VM5RSS4-DZA-nY7e.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-Cu3ypVNN.js → chunk-F27PBJKO-DWjLrbpP.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-DWjLrbpP.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DWjLrbpP.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-BioTa0jG.js → chunk-IMKFNOWR-Dmq185X2.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-Dmq185X2.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-Dmq185X2.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-BPE5l-1s.js → chunk-JWPE2WC7-Bw9j-pi5.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-DUF-0HB1.js → chunk-POPQ4Y6H-BjWtrh1M.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-BHaV6_ki.js → chunk-SVP7TREG-VIH7H3Pr.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-VIH7H3Pr.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-VIH7H3Pr.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-SU15adqY.js → chunk-TICWLB2K-D8lo3HVO.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-D8lo3HVO.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-D8lo3HVO.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-oLy1dZJs.js → chunk-XXDRQBXY-DTbG8Qn9.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CV6z7LP1.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CV6z7LP1.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-EE27IbbX.js → cose-bilkent-JH36ORCC-Cey1iRcB.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Cey1iRcB.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Cey1iRcB.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CpY9J9iu.js → cynefin-OW5HDTMX-BSP2A8yQ.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-BSP2A8yQ.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-BSP2A8yQ.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-mJxcQnkp.js → cynefinDiagram-5FMLGOSQ-Wq4INzwW.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Wq4INzwW.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Wq4INzwW.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-DAUZrxwq.js → dagre-GXQ25YYZ-UgOLsTAJ.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-UgOLsTAJ.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-UgOLsTAJ.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-3rquuCu6.js → diagram-S7CK7UJ4-CXi8ez4a.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-CXi8ez4a.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-CXi8ez4a.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-CZWjaDbi.js → diagram-UQ7AKVKN-WD44iVkL.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-WD44iVkL.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-WD44iVkL.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-CNQEg3jR.js → diagram-VSXAHHWV-DeeTr7nX.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-DeeTr7nX.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-DeeTr7nX.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-DYorDR1n.js → diagram-VX7I27RA-r3wdM6Nm.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-r3wdM6Nm.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-r3wdM6Nm.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-BPgvVftJ.js → diagram-Z3DM3KII-q2jyvUdh.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-q2jyvUdh.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-q2jyvUdh.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-KqpJv-fX.js → ebnfDiagram-PWID7BFC-Cb7oWq7z.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Cb7oWq7z.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Cb7oWq7z.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Bwj_TPoi.js → erDiagram-RLTQ6QDP-CNuNvK3_.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-CNuNvK3_.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-CNuNvK3_.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-BYALZ5I6.js → flowDiagram-HODETNUW-BYdnedTN.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-BYdnedTN.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BYdnedTN.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DwrR8yW_.js → ganttDiagram-EL5Y4UJY-CaxdXqhz.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CaxdXqhz.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CaxdXqhz.js.gz +0 -0
- package/dist/client/assets/{gestureArbiter-4NWfZprH.js → gestureArbiter-DPJowqRM.js} +1 -1
- package/dist/client/assets/gestureArbiter-DPJowqRM.js.br +0 -0
- package/dist/client/assets/{gestureArbiter-4NWfZprH.js.gz → gestureArbiter-DPJowqRM.js.gz} +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-C3pH5ZGR.js → gitGraphDiagram-WWUBYQGX-DRO8nFRo.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DRO8nFRo.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DRO8nFRo.js.gz +0 -0
- package/dist/client/assets/index-CRxcDATF.css +1 -0
- package/dist/client/assets/index-CRxcDATF.css.br +0 -0
- package/dist/client/assets/index-CRxcDATF.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-DpnbClE9.js → infoDiagram-27XIBGKW-D8154Axl.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BFhiY6uy.js → ishikawaDiagram-5VMMS53U-C-m2YcvO.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C-m2YcvO.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-C-m2YcvO.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-Beg2Cmg4.js → journeyDiagram-3NMN7TZE-BaYbc4fI.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BaYbc4fI.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BaYbc4fI.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-CNb51-_F.js → kanban-definition-UXKFOSKX-C4ZmM3W8.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-C4ZmM3W8.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-C4ZmM3W8.js.gz +0 -0
- package/dist/client/assets/{layout-B07GM-uc.js → layout-TUsrKU-t.js} +1 -1
- package/dist/client/assets/layout-TUsrKU-t.js.br +0 -0
- package/dist/client/assets/layout-TUsrKU-t.js.gz +0 -0
- package/dist/client/assets/{linear-DRJlaLbJ.js → linear-nScEH6bm.js} +1 -1
- package/dist/client/assets/linear-nScEH6bm.js.br +0 -0
- package/dist/client/assets/linear-nScEH6bm.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-BySznxI_.js → mermaid.core-WM_j9CSC.js} +5 -5
- package/dist/client/assets/mermaid.core-WM_j9CSC.js.br +0 -0
- package/dist/client/assets/mermaid.core-WM_j9CSC.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-BtStMFHK.js → mindmap-definition-YA3MSWOX-D5bLjroL.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D5bLjroL.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-D5bLjroL.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-B-T8YSfQ.js → passwordBootstrap-BHUt4zht.js} +1 -1
- package/dist/client/assets/passwordBootstrap-BHUt4zht.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-BHUt4zht.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-BNJzxFGK.js → pegDiagram-XKGWAZYB-BCJ-HO-6.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-BCJ-HO-6.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BCJ-HO-6.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-Df1Y6zou.js → pieDiagram-E7YTZNPT-Dg3u_zy9.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-Dg3u_zy9.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-Dg3u_zy9.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Q3jN6--F.js → quadrantDiagram-AXDQQJYC-Bi9SzqYi.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Bi9SzqYi.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Bi9SzqYi.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-c-1SN7pC.js → railroadDiagram-O6MQD6OU-Kbg4uhta.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Kbg4uhta.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-Kbg4uhta.js.gz +0 -0
- package/dist/client/assets/{rdpSession-B6j6_C4D.js → rdpSession-D_TLn9lr.js} +1 -1
- package/dist/client/assets/rdpSession-D_TLn9lr.js.br +0 -0
- package/dist/client/assets/rdpSession-D_TLn9lr.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-Bln9X6SW.js → referenceLongPress-CalVNheA.js} +1 -1
- package/dist/client/assets/referenceLongPress-CalVNheA.js.br +0 -0
- package/dist/client/assets/referenceLongPress-CalVNheA.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-wcsUZu82.js → requirementDiagram-BXWQKSXE-DY1nJYdk.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DY1nJYdk.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DY1nJYdk.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CfXyROPC.js → sankeyDiagram-P5KCCOFB-Ch9oVvbH.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Ch9oVvbH.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-Ch9oVvbH.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-D-IasKbj.js → sequenceDiagram-WJ2MYXX4-BXQhyqVP.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BXQhyqVP.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BXQhyqVP.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-Cyoh7jyx.js → sizeCapture-INFHLROL-CHCCkDzJ.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-CHCCkDzJ.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CHCCkDzJ.js.gz +0 -0
- package/dist/client/assets/{sparkles-kooHQYLJ.js → sparkles-CbQl9qpO.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-BFeLYGOW.js → stateDiagram-D77RDMKH-BsAiJFKS.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-BsAiJFKS.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-BsAiJFKS.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-joTNV3tX.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-BtMhgPB2.js → swimlanes-42K2YHIH-9JFTgCXC.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-9JFTgCXC.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-9JFTgCXC.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-C-2Ixi86.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-BccUO5W0.js → timeline-definition-24CTP7MA-tkuoWWzM.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-tkuoWWzM.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-tkuoWWzM.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-rgLWcpfP.js → triangle-alert-DTiOfHLO.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-hNN_O594.js → vennDiagram-4TSXK5OY-CEnwO3RG.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-CEnwO3RG.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CEnwO3RG.js.gz +0 -0
- package/dist/client/assets/{videoCompression-DS0r00L6.js → videoCompression-q9AAJQ5R.js} +1 -1
- package/dist/client/assets/videoCompression-q9AAJQ5R.js.br +0 -0
- package/dist/client/assets/videoCompression-q9AAJQ5R.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-C6h02bPv.js → wardleyDiagram-VM6X3IG4-BFLZVXWw.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BFLZVXWw.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BFLZVXWw.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DvNnLmXs.js → xychartDiagram-S5SC5T6Z-CyZtg8vv.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CyZtg8vv.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-CyZtg8vv.js.gz +0 -0
- package/dist/client/fonts/MapleMono-Bold.woff2 +0 -0
- package/dist/client/fonts/MapleMono-BoldItalic.woff2 +0 -0
- package/dist/client/fonts/MapleMono-Italic.woff2 +0 -0
- package/dist/client/fonts/MapleMono-OFL.txt +94 -0
- package/dist/client/fonts/MapleMono-OFL.txt.br +0 -0
- package/dist/client/fonts/MapleMono-OFL.txt.gz +0 -0
- package/dist/client/fonts/MapleMono-Regular.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Bold-ascii-71449796b4f8.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Bold-text-57c71f86a6f8.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-BoldItalic-ascii-6b6deb59a8cd.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-BoldItalic-text-b2e1a12c67be.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Italic-ascii-cd2f6088ed50.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Italic-text-a346c77141da.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Regular-ascii-9a80fd4a5743.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockMaple-Regular-text-58438803bf4c.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockPromptIcons-Bold-icons-fa87935d39d0.woff2 +0 -0
- package/dist/client/fonts/terminal/TermdockPromptIcons-Regular-icons-0d64d12759b8.woff2 +0 -0
- package/dist/client/fonts/terminal/manifest.json +98 -0
- package/dist/client/fonts/terminal/manifest.json.br +0 -0
- package/dist/client/fonts/terminal/manifest.json.gz +0 -0
- package/dist/client/index.html +12 -11
- 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 +4 -3
- package/dist/server/entry.js +2 -1
- package/dist/server/federation/relay.js +121 -37
- package/dist/server/federation/selfRelay.js +12 -8
- package/dist/server/federation/streamLimits.js +5 -0
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/scripts/generate-font-subsets.py +64 -9
- package/dist/client/assets/App-JL8Fd8uO.js.br +0 -0
- package/dist/client/assets/App-JL8Fd8uO.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-CwvZXpvS.js +0 -9
- package/dist/client/assets/ArchitecturePanel-CwvZXpvS.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CwvZXpvS.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BF3p_M6Y.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BF3p_M6Y.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CYpn4j_x.js +0 -1
- package/dist/client/assets/CollaborationMainWorkspace-CYpn4j_x.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-CYpn4j_x.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-BjjX05Cv.js +0 -2
- package/dist/client/assets/ComputerControlView-DWlZmlCH.js.br +0 -0
- package/dist/client/assets/ComputerControlView-DWlZmlCH.js.gz +0 -0
- package/dist/client/assets/DagPlayground-IbzlcDVX.js.br +0 -0
- package/dist/client/assets/DagPlayground-IbzlcDVX.js.gz +0 -0
- package/dist/client/assets/DiffLab-DEAYEwfY.js.br +0 -0
- package/dist/client/assets/DiffLab-DEAYEwfY.js.gz +0 -0
- package/dist/client/assets/DiffReview-BPHfC8nZ.js.br +0 -0
- package/dist/client/assets/DiffReview-BPHfC8nZ.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-2iKu20Ik.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-2iKu20Ik.js.gz +0 -0
- package/dist/client/assets/DiffViewer-C4S3oEQV.js.br +0 -0
- package/dist/client/assets/DiffViewer-C4S3oEQV.js.gz +0 -0
- package/dist/client/assets/ModelPreview-D_KABt7i.js.br +0 -0
- package/dist/client/assets/ModelPreview-D_KABt7i.js.gz +0 -0
- package/dist/client/assets/RightSidebar-scaBEaFJ.js +0 -1677
- package/dist/client/assets/RightSidebar-scaBEaFJ.js.br +0 -0
- package/dist/client/assets/RightSidebar-scaBEaFJ.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-OAQJcJ5a.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-OAQJcJ5a.js.gz +0 -0
- package/dist/client/assets/app-aNkllBsm.js +0 -35
- package/dist/client/assets/app-aNkllBsm.js.br +0 -0
- package/dist/client/assets/app-aNkllBsm.js.gz +0 -0
- package/dist/client/assets/arc-B29HLhod.js.br +0 -0
- package/dist/client/assets/arc-B29HLhod.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CQeKeLzl.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CQeKeLzl.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CgQQGQN_.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-CgQQGQN_.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BTeUIAS9.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-BTeUIAS9.js.gz +0 -0
- package/dist/client/assets/channel-DPAqeRss.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-Cu3ypVNN.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-Cu3ypVNN.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BioTa0jG.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-BioTa0jG.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BHaV6_ki.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BHaV6_ki.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-SU15adqY.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-SU15adqY.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-kQeU2Z7d.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-kQeU2Z7d.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-EE27IbbX.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-EE27IbbX.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CpY9J9iu.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CpY9J9iu.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-mJxcQnkp.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-mJxcQnkp.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-DAUZrxwq.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-DAUZrxwq.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-3rquuCu6.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-3rquuCu6.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CZWjaDbi.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CZWjaDbi.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CNQEg3jR.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CNQEg3jR.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DYorDR1n.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DYorDR1n.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BPgvVftJ.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BPgvVftJ.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-KqpJv-fX.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-KqpJv-fX.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Bwj_TPoi.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Bwj_TPoi.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BYALZ5I6.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BYALZ5I6.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DwrR8yW_.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-DwrR8yW_.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-4NWfZprH.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C3pH5ZGR.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C3pH5ZGR.js.gz +0 -0
- package/dist/client/assets/index-e1PkqIqN.css +0 -1
- package/dist/client/assets/index-e1PkqIqN.css.br +0 -0
- package/dist/client/assets/index-e1PkqIqN.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BFhiY6uy.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BFhiY6uy.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-Beg2Cmg4.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-Beg2Cmg4.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-CNb51-_F.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-CNb51-_F.js.gz +0 -0
- package/dist/client/assets/layout-B07GM-uc.js.br +0 -0
- package/dist/client/assets/layout-B07GM-uc.js.gz +0 -0
- package/dist/client/assets/linear-DRJlaLbJ.js.br +0 -0
- package/dist/client/assets/linear-DRJlaLbJ.js.gz +0 -0
- package/dist/client/assets/mermaid.core-BySznxI_.js.br +0 -0
- package/dist/client/assets/mermaid.core-BySznxI_.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BtStMFHK.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-BtStMFHK.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-B-T8YSfQ.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-B-T8YSfQ.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BNJzxFGK.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BNJzxFGK.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-Df1Y6zou.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-Df1Y6zou.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Q3jN6--F.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-Q3jN6--F.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-c-1SN7pC.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-c-1SN7pC.js.gz +0 -0
- package/dist/client/assets/rdpSession-B6j6_C4D.js.br +0 -0
- package/dist/client/assets/rdpSession-B6j6_C4D.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-Bln9X6SW.js.br +0 -0
- package/dist/client/assets/referenceLongPress-Bln9X6SW.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-wcsUZu82.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-wcsUZu82.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CfXyROPC.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CfXyROPC.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D-IasKbj.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D-IasKbj.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Cyoh7jyx.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-Cyoh7jyx.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-BFeLYGOW.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-BFeLYGOW.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-Cn5inEsZ.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BtMhgPB2.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BtMhgPB2.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-Di2Vfydj.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-BccUO5W0.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-BccUO5W0.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-hNN_O594.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-hNN_O594.js.gz +0 -0
- package/dist/client/assets/videoCompression-DS0r00L6.js.br +0 -0
- package/dist/client/assets/videoCompression-DS0r00L6.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-C6h02bPv.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-C6h02bPv.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DvNnLmXs.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DvNnLmXs.js.gz +0 -0
|
Binary file
|
|
Binary file
|
package/dist/client/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const n=(n,i)=>(n=new URL(n+".js",i).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(i,a)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let
|
|
1
|
+
if(!self.define){let e,s={};const n=(n,i)=>(n=new URL(n+".js",i).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(i,a)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let c={};const r=e=>n(e,o),d={module:{uri:o},exports:c,require:r};s[o]=Promise.all(i.map(e=>d[e]||r(e))).then(e=>(a(...e),c))}}define(["./workbox-9845bf53"],function(e){"use strict";importScripts("sw-encrypted-background.js","sw-notifications.js","sw-secure-api.js","sw-downloads.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"e861e4f0fa06e0ad9bb72cc43ebf650e"},{url:"assets/app-D-AilvVY.js",revision:null},{url:"assets/react-XDCjal2T.js",revision:null},{url:"assets/dnd-BCFAPC0L.js",revision:null},{url:"assets/terminal-C7X98Amf.js",revision:null},{url:"assets/index-CRxcDATF.css",revision:null},{url:"icons/agents/opencode.svg",revision:"851c2931d95821ba2b39b68587b48976"},{url:"icons/agents/kimi.svg",revision:"5366c710f4f763022fe159a69cf17409"},{url:"icons/agents/grok.svg",revision:"cd745b0b964545bc7a6b4f36021c3298"},{url:"icons/agents/goose.svg",revision:"c79b927278983c19098d77b4b0c11ab3"},{url:"icons/agents/gemini.svg",revision:"7a64f6d6f80a908255e9c0256903882e"},{url:"icons/agents/droid.svg",revision:"f28723b8c5c115e0908ba3c72613e256"},{url:"icons/agents/cursor.svg",revision:"a1528f3d3cda497f75a5db28defb19de"},{url:"icons/agents/copilot.svg",revision:"dd52e96c1eac0b6ec4bedcd0ddd3b52d"},{url:"icons/agents/codex.svg",revision:"608e55babb0b217e1f2da287342b46d3"},{url:"icons/agents/claude.svg",revision:"08af0225b7acdd8e8f573f3247e5e1c3"},{url:"icons/agents/amp.svg",revision:"af12f8b33faa81e4d46ea7b93c7e43f3"},{url:"apple-touch-icon-180x180.png",revision:"628c2e2f28a9b545aa57be1352025643"},{url:"boot-logo.png",revision:"2e3a25efd9b0b8ca1eab53b5f2284556"},{url:"favicon.ico",revision:"2994debfa6f59a0898e24eb0c54d64b5"},{url:"ipad-status-bar.js",revision:"3a8080edb8a5cc658a7908f6ad69537f"},{url:"maskable-icon-512x512.png",revision:"4d489331fd81f0713c400b26426442b9"},{url:"pwa-192x192.png",revision:"4d6f899888cfb8ccd7a1fd3c9e583ad2"},{url:"pwa-512x512.png",revision:"0885dd7ec8932e7f878b013d51a380a4"},{url:"robots.txt",revision:"d9339907142917dee4aac78494ac9cfe"},{url:"sw-downloads.js",revision:"48dab2e78bd93b4eee27fca487eccfde"},{url:"sw-notifications.js",revision:"fde578590ea8b0540d488ef448b48f45"},{url:"sw-secure-api.js",revision:"cf2bdbd03d41d66d60344c0b5a1075de"},{url:"manifest.webmanifest",revision:"83f9a43b74c236012adafb077fb6d774"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"),{denylist:[/^\/api\//,/^\/__termdock-download\//,/^\/health$/]})),e.registerRoute(/\/assets\/.*\.(?:js|css)$/,new e.CacheFirst({cacheName:"termdock-lazy-assets-v1",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:96,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/terminal\/[^/]+-[a-f0-9]{12}\.woff2$/,new e.CacheFirst({cacheName:"termdock-fonts-v2",plugins:[new e.CacheableResponsePlugin({statuses:[0,200]}),new e.ExpirationPlugin({maxEntries:384,maxAgeSeconds:2592e3})]}),"GET"),e.registerRoute(/\/fonts\/.*\.(?:woff2|ttf)$/,new e.StaleWhileRevalidate({cacheName:"termdock-fonts-v1",plugins:[new e.ExpirationPlugin({maxEntries:24,maxAgeSeconds:2592e3})]}),"GET")});
|
package/dist/client/sw.js.br
CHANGED
|
Binary file
|
package/dist/client/sw.js.gz
CHANGED
|
Binary file
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RELAY_CLIENT_CONNECT_TIMEOUT_MS } from '../federation/streamLimits.js';
|
|
1
2
|
import { encryptedRequestSubject } from '../federation/requestContext.js';
|
|
2
3
|
import { randomUUID } from 'node:crypto';
|
|
3
4
|
import { readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
|
|
@@ -122,9 +123,9 @@ export class CollaborationRpc {
|
|
|
122
123
|
this.pending.clear();
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
|
-
export async function connectCollaborationRpc(identity, peer, endpoint, receive) {
|
|
126
|
+
export async function connectCollaborationRpc(identity, peer, endpoint, receive, configuredCa) {
|
|
126
127
|
validateCollaborationNode(peer);
|
|
127
|
-
const ca = peer.caFingerprint256 ? await readPinnedCertificateAuthority(peer.origin, peer.caFingerprint256) :
|
|
128
|
+
const ca = peer.caFingerprint256 ? await readPinnedCertificateAuthority(peer.origin, peer.caFingerprint256) : configuredCa;
|
|
128
129
|
const url = endpoint ?? `${peer.origin.replace(/^https:/, 'wss:')}/api/federation/secure`;
|
|
129
130
|
const socket = new WebSocket(url, { ca, rejectUnauthorized: true, followRedirects: false, handshakeTimeout: 3000, maxPayload: 1024 * 1024 });
|
|
130
131
|
const relay = endpoint ? relayDuplex(socket, peer.serviceId) : undefined;
|
|
@@ -164,7 +165,7 @@ function relayDuplex(socket, target) {
|
|
|
164
165
|
let readyResolve, readyReject;
|
|
165
166
|
const ready = new Promise((resolve, reject) => { readyResolve = resolve; readyReject = reject; });
|
|
166
167
|
void ready.catch(() => { });
|
|
167
|
-
const timer = setTimeout(() => fail(new Error('ROUTE_TIMEOUT')),
|
|
168
|
+
const timer = setTimeout(() => fail(new Error('ROUTE_TIMEOUT')), RELAY_CLIENT_CONNECT_TIMEOUT_MS);
|
|
168
169
|
const fail = (error) => { clearTimeout(timer); readyReject(error); queue.end(error); socket.close(); };
|
|
169
170
|
const send = (frame) => {
|
|
170
171
|
if (socket.readyState !== 1 || socket.bufferedAmount > 1024 * 1024)
|
package/dist/server/entry.js
CHANGED
|
@@ -677,7 +677,8 @@ export function startServer(options = {}) {
|
|
|
677
677
|
const localHostname = ['0.0.0.0', '::'].includes(host) ? 'localhost' : host;
|
|
678
678
|
const localAuthority = localHostname.includes(':') && !localHostname.startsWith('[') ? `[${localHostname}]` : localHostname;
|
|
679
679
|
const selfRelay = new DesktopSelfRelay({
|
|
680
|
-
desktopFile: path.join(homedir(), '.termdock', 'desktop.json'),
|
|
680
|
+
desktopFile: path.join(homedir(), '.termdock', 'desktop.json'),
|
|
681
|
+
targets: () => routeAccess.configuredDirectTargets(), baseDir: path.join(homedir(), '.termdock', 'federation'), identity: runtime.identity, serviceId: runtime.serviceId,
|
|
681
682
|
localOrigin: `${scheme}://${localAuthority}:${port}`, ...(localCa ? { localCa } : {}),
|
|
682
683
|
});
|
|
683
684
|
selfRelay.start();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { RELAY_PROVIDER_CONNECT_TIMEOUT_MS, RELAY_ROUTE_OPEN_TIMEOUT_MS } from './streamLimits.js';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { EventEmitter } from 'node:events';
|
|
3
4
|
const defaults = { maxFrameBytes: 256 * 1024, maxBufferedBytes: 1024 * 1024,
|
|
4
|
-
maxStreams: 128, heartbeatMs: 30_000, maxRouteTtlMs: 90_000, connectTimeoutMs:
|
|
5
|
+
maxStreams: 128, heartbeatMs: 30_000, maxRouteTtlMs: 90_000, connectTimeoutMs: RELAY_PROVIDER_CONNECT_TIMEOUT_MS, routeTimeoutMs: RELAY_ROUTE_OPEN_TIMEOUT_MS };
|
|
5
6
|
const identifier = (v) => typeof v === 'string' && /^[a-zA-Z0-9_.:-]{1,160}$/.test(v);
|
|
6
7
|
function parse(raw, limits) {
|
|
7
8
|
if (Buffer.byteLength(raw instanceof Array ? Buffer.concat(raw) : raw) > limits.maxFrameBytes)
|
|
@@ -90,9 +91,11 @@ export class RelayRouter {
|
|
|
90
91
|
this.options = options;
|
|
91
92
|
this.limits = { ...defaults, ...options };
|
|
92
93
|
this.sweep = setInterval(() => {
|
|
93
|
-
for (const [id,
|
|
94
|
-
|
|
95
|
-
this.
|
|
94
|
+
for (const [id, routes] of this.routes)
|
|
95
|
+
for (const [peer, route] of routes) {
|
|
96
|
+
if (route.expires <= Date.now() || peer.ws.readyState !== 1 || !this.canRegister(peer, id))
|
|
97
|
+
this.withdraw(id, peer);
|
|
98
|
+
}
|
|
96
99
|
for (const peer of this.peers)
|
|
97
100
|
for (const stream of [...peer.streams.values()]) {
|
|
98
101
|
if (stream.owner === peer && !this.canRoute(peer, stream.serviceId))
|
|
@@ -107,16 +110,22 @@ export class RelayRouter {
|
|
|
107
110
|
canRoute(peer, serviceId) {
|
|
108
111
|
return !this.directPeers.has(peer) && this.options.allowRoute(peer.principal, serviceId);
|
|
109
112
|
}
|
|
113
|
+
providers(serviceId) {
|
|
114
|
+
return [...(this.routes.get(serviceId) || [])]
|
|
115
|
+
.filter(([peer, route]) => route.expires > Date.now() && peer.ws.readyState === 1 && this.canRegister(peer, serviceId))
|
|
116
|
+
// Recent target failures lower priority but never make the only route disappear.
|
|
117
|
+
.sort((a, b) => Math.max(0, a[1].retryAfter - Date.now()) - Math.max(0, b[1].retryAfter - Date.now()))
|
|
118
|
+
.map(([peer]) => peer);
|
|
119
|
+
}
|
|
110
120
|
hasRoute(serviceId) {
|
|
111
|
-
|
|
112
|
-
return !this.stopped && !!route && route.expires > Date.now() && route.peer.ws.readyState === 1 && this.canRegister(route.peer, serviceId);
|
|
121
|
+
return !this.stopped && this.providers(serviceId).length > 0;
|
|
113
122
|
}
|
|
114
123
|
activeRouteIds() {
|
|
115
124
|
return [...this.routes.keys()].filter(serviceId => this.hasRoute(serviceId));
|
|
116
125
|
}
|
|
117
126
|
/** Trusted local configuration only. Clients never supply a target URL or factory. */
|
|
118
127
|
registerDirect(serviceId, connect) {
|
|
119
|
-
if (this.stopped || !identifier(serviceId) || typeof connect !== 'function' || this.
|
|
128
|
+
if (this.stopped || !identifier(serviceId) || typeof connect !== 'function' || [...this.peers].some(peer => this.directPeers.get(peer) === serviceId))
|
|
120
129
|
throw new Error('Invalid or already registered direct target');
|
|
121
130
|
const [routerWire, clientWire] = memoryPair();
|
|
122
131
|
// The private WeakMap marks this peer; its placeholder principal is never
|
|
@@ -139,6 +148,10 @@ export class RelayRouter {
|
|
|
139
148
|
}
|
|
140
149
|
/** Caller awaits attach before completing its application-level ready handshake. */
|
|
141
150
|
async attach(ws, context) {
|
|
151
|
+
if (this.stopped) {
|
|
152
|
+
ws.close(1001, 'Router stopped');
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
142
155
|
// Reject early application frames rather than buffering untrusted traffic while authenticating.
|
|
143
156
|
let early = false;
|
|
144
157
|
const rejectEarly = () => { early = true; ws.close(1008, 'Authentication pending'); };
|
|
@@ -151,7 +164,7 @@ export class RelayRouter {
|
|
|
151
164
|
principal = null;
|
|
152
165
|
}
|
|
153
166
|
ws.off('message', rejectEarly);
|
|
154
|
-
if (principal === null || early || ws.readyState !== 1) {
|
|
167
|
+
if (this.stopped || principal === null || early || ws.readyState !== 1) {
|
|
155
168
|
ws.close(1008, 'Relay authentication required');
|
|
156
169
|
return false;
|
|
157
170
|
}
|
|
@@ -169,72 +182,131 @@ export class RelayRouter {
|
|
|
169
182
|
return true;
|
|
170
183
|
}
|
|
171
184
|
withdraw(serviceId, peer) {
|
|
172
|
-
|
|
185
|
+
const routes = this.routes.get(serviceId);
|
|
186
|
+
routes?.delete(peer);
|
|
187
|
+
if (!routes?.size)
|
|
173
188
|
this.routes.delete(serviceId);
|
|
174
189
|
for (const stream of [...peer.streams.values()])
|
|
175
190
|
if (stream.serviceId === serviceId && stream.provider === peer)
|
|
176
|
-
this.
|
|
191
|
+
this.providerFailed(stream, 'Route unavailable');
|
|
177
192
|
}
|
|
178
193
|
remove(peer) {
|
|
179
194
|
this.peers.delete(peer);
|
|
180
|
-
|
|
181
|
-
|
|
195
|
+
// Consumers cannot fail over after disconnecting; remove them first.
|
|
196
|
+
for (const stream of [...peer.streams.values()])
|
|
197
|
+
if (stream.owner === peer)
|
|
198
|
+
this.closeStream(stream, 'Relay disconnected');
|
|
199
|
+
for (const [id, routes] of this.routes)
|
|
200
|
+
if (routes.has(peer))
|
|
182
201
|
this.withdraw(id, peer);
|
|
183
202
|
for (const stream of [...peer.streams.values()])
|
|
184
|
-
this.
|
|
203
|
+
this.providerFailed(stream, 'Relay disconnected');
|
|
185
204
|
}
|
|
186
205
|
closeStream(s, reason) {
|
|
206
|
+
if (s.owner.streams.get(s.ownerId) !== s)
|
|
207
|
+
return;
|
|
208
|
+
clearTimeout(s.deadline);
|
|
187
209
|
s.owner.streams.delete(s.ownerId);
|
|
188
|
-
s.provider.streams.
|
|
210
|
+
if (s.provider.streams.get(s.providerId) === s) {
|
|
211
|
+
s.provider.streams.delete(s.providerId);
|
|
212
|
+
send(s.provider.ws, { type: 'close', streamId: s.providerId, reason }, this.limits);
|
|
213
|
+
}
|
|
189
214
|
send(s.owner.ws, { type: 'close', streamId: s.ownerId, reason }, this.limits);
|
|
215
|
+
}
|
|
216
|
+
providerFailed(s, reason) {
|
|
217
|
+
if (s.owner.streams.get(s.ownerId) !== s)
|
|
218
|
+
return;
|
|
219
|
+
clearTimeout(s.deadline);
|
|
220
|
+
s.provider.streams.delete(s.providerId);
|
|
190
221
|
send(s.provider.ws, { type: 'close', streamId: s.providerId, reason }, this.limits);
|
|
222
|
+
const route = this.routes.get(s.serviceId)?.get(s.provider);
|
|
223
|
+
if (route)
|
|
224
|
+
route.retryAfter = Date.now() + 5000;
|
|
225
|
+
// Never replay ciphertext (or terminal input) onto a replacement transport.
|
|
226
|
+
if (s.opened || !this.openProvider(s))
|
|
227
|
+
this.closeStream(s, reason);
|
|
228
|
+
}
|
|
229
|
+
openProvider(s) {
|
|
230
|
+
if (this.stopped || Date.now() >= s.expires || s.owner.ws.readyState !== 1 || !this.canRoute(s.owner, s.serviceId))
|
|
231
|
+
return false;
|
|
232
|
+
for (const provider of this.providers(s.serviceId)) {
|
|
233
|
+
if (provider === s.owner || s.attempted.has(provider) || provider.streams.size >= this.limits.maxStreams)
|
|
234
|
+
continue;
|
|
235
|
+
s.attempted.add(provider);
|
|
236
|
+
s.provider = provider;
|
|
237
|
+
s.providerId = randomUUID();
|
|
238
|
+
provider.streams.set(s.providerId, s);
|
|
239
|
+
if (!send(provider.ws, { type: 'open', streamId: s.providerId, serviceId: s.serviceId }, this.limits)) {
|
|
240
|
+
provider.streams.delete(s.providerId);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
s.deadline = setTimeout(() => this.providerFailed(s, 'Target timeout'), Math.min(this.limits.connectTimeoutMs, s.expires - Date.now()));
|
|
244
|
+
s.deadline.unref?.();
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
191
248
|
}
|
|
192
249
|
receive(peer, f) {
|
|
193
250
|
if (f.type === 'register') {
|
|
194
251
|
if (!f.serviceIds.every(id => this.canRegister(peer, id)))
|
|
195
252
|
throw new Error('Registration denied');
|
|
253
|
+
for (const [id, routes] of this.routes)
|
|
254
|
+
if (routes.has(peer) && !f.serviceIds.includes(id))
|
|
255
|
+
this.withdraw(id, peer);
|
|
196
256
|
for (const id of f.serviceIds) {
|
|
197
|
-
|
|
198
|
-
if (
|
|
199
|
-
|
|
257
|
+
let routes = this.routes.get(id);
|
|
258
|
+
if (!routes) {
|
|
259
|
+
routes = new Map();
|
|
260
|
+
this.routes.set(id, routes);
|
|
261
|
+
}
|
|
262
|
+
routes.set(peer, { expires: Date.now() + Math.min(f.ttlMs, this.limits.maxRouteTtlMs), retryAfter: routes.get(peer)?.retryAfter ?? 0 });
|
|
200
263
|
}
|
|
201
|
-
for (const [id, r] of this.routes)
|
|
202
|
-
if (r.peer === peer && !f.serviceIds.includes(id))
|
|
203
|
-
this.withdraw(id, peer);
|
|
204
|
-
for (const id of f.serviceIds)
|
|
205
|
-
this.routes.set(id, { peer, expires: Date.now() + Math.min(f.ttlMs, this.limits.maxRouteTtlMs) });
|
|
206
264
|
return;
|
|
207
265
|
}
|
|
208
266
|
if (f.type === 'open') {
|
|
209
|
-
const route = this.routes.get(f.serviceId);
|
|
210
267
|
if (peer.streams.has(f.streamId))
|
|
211
268
|
throw new Error('Duplicate stream');
|
|
212
|
-
if (!this.canRoute(peer, f.serviceId) || !
|
|
269
|
+
if (!this.canRoute(peer, f.serviceId) || !this.hasRoute(f.serviceId)) {
|
|
213
270
|
send(peer.ws, { type: 'close', streamId: f.streamId, reason: 'Route unavailable or denied' }, this.limits);
|
|
214
271
|
return;
|
|
215
272
|
}
|
|
216
|
-
if (peer.streams.size >= this.limits.maxStreams
|
|
273
|
+
if (peer.streams.size >= this.limits.maxStreams)
|
|
217
274
|
throw new Error('Stream budget exceeded');
|
|
218
|
-
const s = { owner: peer, ownerId: f.streamId, provider:
|
|
275
|
+
const s = { owner: peer, ownerId: f.streamId, provider: peer, providerId: '', serviceId: f.serviceId, opened: false, expires: Date.now() + this.limits.routeTimeoutMs, attempted: new Set() };
|
|
219
276
|
peer.streams.set(s.ownerId, s);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
this.closeStream(s, 'Relay disconnected');
|
|
277
|
+
if (!this.openProvider(s))
|
|
278
|
+
this.closeStream(s, 'Route unavailable or congested');
|
|
223
279
|
return;
|
|
224
280
|
}
|
|
225
281
|
const s = peer.streams.get(f.streamId);
|
|
226
282
|
if (!s)
|
|
227
283
|
return;
|
|
228
|
-
if (!this.canRoute(s.owner, s.serviceId)
|
|
284
|
+
if (!this.canRoute(s.owner, s.serviceId)) {
|
|
229
285
|
this.closeStream(s, 'Authorization revoked');
|
|
230
286
|
return;
|
|
231
287
|
}
|
|
288
|
+
const registration = this.routes.get(s.serviceId)?.get(s.provider);
|
|
289
|
+
if (!registration || registration.expires <= Date.now() || !this.canRegister(s.provider, s.serviceId)) {
|
|
290
|
+
this.withdraw(s.serviceId, s.provider);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
232
293
|
if (f.type === 'close') {
|
|
233
|
-
|
|
294
|
+
if (peer === s.provider)
|
|
295
|
+
this.providerFailed(s, 'Channel closed');
|
|
296
|
+
else
|
|
297
|
+
this.closeStream(s, 'Channel closed');
|
|
234
298
|
return;
|
|
235
299
|
}
|
|
236
300
|
if (f.type === 'opened' && peer !== s.provider)
|
|
237
301
|
throw new Error('Invalid channel direction');
|
|
302
|
+
if (f.type === 'opened') {
|
|
303
|
+
if (s.opened)
|
|
304
|
+
throw new Error('Channel already opened');
|
|
305
|
+
clearTimeout(s.deadline);
|
|
306
|
+
s.opened = true;
|
|
307
|
+
}
|
|
308
|
+
else if (!s.opened)
|
|
309
|
+
throw new Error('Channel not ready');
|
|
238
310
|
const target = peer === s.owner ? s.provider : s.owner;
|
|
239
311
|
if (!send(target.ws, { ...f, streamId: peer === s.owner ? s.providerId : s.ownerId }, this.limits))
|
|
240
312
|
this.closeStream(s, 'Relay congestion');
|
|
@@ -251,6 +323,7 @@ export class RelayClient {
|
|
|
251
323
|
limits;
|
|
252
324
|
streams = new Map();
|
|
253
325
|
renewal;
|
|
326
|
+
pending = new Map();
|
|
254
327
|
stopped = false;
|
|
255
328
|
constructor(upstream, options) {
|
|
256
329
|
this.upstream = upstream;
|
|
@@ -265,6 +338,8 @@ export class RelayClient {
|
|
|
265
338
|
upstream.on('message', raw => { void this.receive(raw).catch(() => upstream.close(1008, 'Invalid relay frame')); });
|
|
266
339
|
}
|
|
267
340
|
drop(id, reason) {
|
|
341
|
+
clearTimeout(this.pending.get(id));
|
|
342
|
+
this.pending.delete(id);
|
|
268
343
|
const target = this.streams.get(id);
|
|
269
344
|
this.streams.delete(id);
|
|
270
345
|
target?.close();
|
|
@@ -276,26 +351,35 @@ export class RelayClient {
|
|
|
276
351
|
throw new Error('Unexpected registration');
|
|
277
352
|
if (f.type === 'open') {
|
|
278
353
|
const connect = this.options.targets.get(f.serviceId);
|
|
279
|
-
if (
|
|
354
|
+
if (this.streams.has(f.streamId))
|
|
355
|
+
throw new Error('Duplicate stream');
|
|
356
|
+
if (!connect || this.streams.size >= this.limits.maxStreams) {
|
|
280
357
|
this.drop(f.streamId, 'Target unavailable');
|
|
281
358
|
return;
|
|
282
359
|
}
|
|
283
360
|
this.streams.set(f.streamId, null);
|
|
284
|
-
|
|
285
|
-
const timer = setTimeout(() => { timedOut = true; this.drop(f.streamId, 'Target timeout'); }, this.limits.connectTimeoutMs);
|
|
361
|
+
const timer = setTimeout(() => { this.drop(f.streamId, 'Target timeout'); }, this.limits.connectTimeoutMs);
|
|
286
362
|
timer.unref?.();
|
|
363
|
+
this.pending.set(f.streamId, timer);
|
|
287
364
|
let target;
|
|
288
365
|
try {
|
|
289
366
|
target = await connect();
|
|
290
367
|
}
|
|
291
368
|
catch {
|
|
292
|
-
|
|
293
|
-
|
|
369
|
+
if (this.pending.get(f.streamId) === timer)
|
|
370
|
+
this.drop(f.streamId, 'Target unavailable');
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
// A canceled attempt may complete after the same ID has been reused.
|
|
374
|
+
if (this.pending.get(f.streamId) !== timer || this.stopped) {
|
|
375
|
+
target.close();
|
|
294
376
|
return;
|
|
295
377
|
}
|
|
296
378
|
clearTimeout(timer);
|
|
297
|
-
|
|
379
|
+
this.pending.delete(f.streamId);
|
|
380
|
+
if (target.readyState !== 1) {
|
|
298
381
|
target.close();
|
|
382
|
+
this.drop(f.streamId, 'Target not ready');
|
|
299
383
|
return;
|
|
300
384
|
}
|
|
301
385
|
this.streams.set(f.streamId, target);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
2
4
|
import { desktopDirectTargets } from './desktopTargets.js';
|
|
3
5
|
import { RelayClient } from './relay.js';
|
|
4
6
|
import { readPinnedCertificateAuthority } from './trustedCa.js';
|
|
@@ -57,8 +59,8 @@ function waitForReady(socket) {
|
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* A local Termdock service may publish only its own stable identity to other
|
|
60
|
-
* services pinned by the same Desktop
|
|
61
|
-
* and
|
|
62
|
+
* administrator-configured peers or services pinned by the same Desktop
|
|
63
|
+
* installation. Configuration supplies addresses and TLS trust only; the service proves itself with its own Noise identity and
|
|
62
64
|
* no browser/device authorization is copied into this process.
|
|
63
65
|
*/
|
|
64
66
|
export class DesktopSelfRelay {
|
|
@@ -89,11 +91,12 @@ export class DesktopSelfRelay {
|
|
|
89
91
|
refresh() {
|
|
90
92
|
if (this.stopped)
|
|
91
93
|
return;
|
|
92
|
-
const desired = new Map(desktopDirectTargets(this.options.desktopFile, this.options.serviceId)
|
|
93
|
-
.
|
|
94
|
+
const desired = new Map([...desktopDirectTargets(this.options.desktopFile, this.options.serviceId), ...(this.options.targets?.() || [])]
|
|
95
|
+
.filter(target => target.serviceId !== this.options.serviceId)
|
|
96
|
+
.map(target => [target.serviceId, { ...target, url: new URL(target.url.replace(/^wss:/, 'https:')).origin }]));
|
|
94
97
|
for (const [serviceId, state] of this.states) {
|
|
95
98
|
const target = desired.get(serviceId);
|
|
96
|
-
const signature = target && JSON.stringify([target.url, target.caFingerprint256]);
|
|
99
|
+
const signature = target && JSON.stringify([target.url, target.caFingerprint256, target.caPath]);
|
|
97
100
|
if (!target || signature !== state.signature) {
|
|
98
101
|
this.stopState(state);
|
|
99
102
|
this.states.delete(serviceId);
|
|
@@ -101,7 +104,7 @@ export class DesktopSelfRelay {
|
|
|
101
104
|
}
|
|
102
105
|
for (const [serviceId, target] of desired)
|
|
103
106
|
if (!this.states.has(serviceId)) {
|
|
104
|
-
const state = { signature: JSON.stringify([target.url, target.caFingerprint256]), stopped: false };
|
|
107
|
+
const state = { signature: JSON.stringify([target.url, target.caFingerprint256, target.caPath]), stopped: false };
|
|
105
108
|
this.states.set(serviceId, state);
|
|
106
109
|
void this.run(state, target);
|
|
107
110
|
}
|
|
@@ -137,7 +140,8 @@ export class DesktopSelfRelay {
|
|
|
137
140
|
}
|
|
138
141
|
async connect(state, target) {
|
|
139
142
|
const peer = { serviceId: target.serviceId, origin: target.url, ...(target.caFingerprint256 ? { caFingerprint256: target.caFingerprint256 } : {}) };
|
|
140
|
-
const
|
|
143
|
+
const configuredCa = target.caPath ? readFileSync(resolve(this.options.baseDir || process.cwd(), target.caPath)) : undefined;
|
|
144
|
+
const rpc = await connectCollaborationRpc(this.options.identity, peer, undefined, undefined, configuredCa);
|
|
141
145
|
let selfRelayToken;
|
|
142
146
|
try {
|
|
143
147
|
const result = await rpc.request({ type: 'self-relay-ticket' });
|
|
@@ -151,7 +155,7 @@ export class DesktopSelfRelay {
|
|
|
151
155
|
}
|
|
152
156
|
if (this.stopped || state.stopped)
|
|
153
157
|
return;
|
|
154
|
-
const ca = target.caFingerprint256 ? await readPinnedCertificateAuthority(target.url, target.caFingerprint256) :
|
|
158
|
+
const ca = target.caFingerprint256 ? await readPinnedCertificateAuthority(target.url, target.caFingerprint256) : configuredCa;
|
|
155
159
|
const upstream = await openSocket(relayEndpoint(target.url, selfRelayToken), ca);
|
|
156
160
|
state.upstream = upstream;
|
|
157
161
|
try {
|
|
@@ -3,3 +3,8 @@
|
|
|
3
3
|
export const MAX_ACTIVE_HTTP_REQUESTS = 8;
|
|
4
4
|
export const MAX_SERVER_HTTP_REQUESTS = 32;
|
|
5
5
|
export const MAX_OPEN_SECURE_SOCKETS = 128;
|
|
6
|
+
// Relay selection must finish before foreground/background clients abandon the
|
|
7
|
+
// transport, leaving time for the final pinned Noise handshake.
|
|
8
|
+
export const RELAY_PROVIDER_CONNECT_TIMEOUT_MS = 10_000;
|
|
9
|
+
export const RELAY_ROUTE_OPEN_TIMEOUT_MS = 15_000;
|
|
10
|
+
export const RELAY_CLIENT_CONNECT_TIMEOUT_MS = 20_000;
|
package/package.json
CHANGED
package/runtime-manifest.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"packageName": "termdock",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.332",
|
|
5
5
|
"runtimeProtocolVersion": 1,
|
|
6
6
|
"minimumDesktopVersion": "1.4.46",
|
|
7
7
|
"nodeMajor": 22,
|
|
8
8
|
"dependencyHash": "sha256-oaGYi/P5ob8sd6VnNBOwshKozjhdV+hYiCmLAsn5x00=",
|
|
9
|
-
"serverBundleHash": "sha256-
|
|
10
|
-
"clientBundleHash": "sha256-
|
|
9
|
+
"serverBundleHash": "sha256-lmN7Or7gUKZy9ehDwSJ+vAJQSDcbNpEVqSGxxotR2c4=",
|
|
10
|
+
"clientBundleHash": "sha256-tvY+AzsMFBHQkZySF87ojAlWXBRrkv6IZIUGHJyllaI=",
|
|
11
11
|
"entrypoint": "dist/server/cli.js",
|
|
12
12
|
"clientEntrypoint": "dist/client/index.html"
|
|
13
13
|
}
|
|
@@ -7,6 +7,7 @@ Keep cmap coverage and advance widths exactly equal to the source faces.
|
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import argparse
|
|
10
11
|
import hashlib
|
|
11
12
|
import io
|
|
12
13
|
import json
|
|
@@ -18,6 +19,12 @@ from fontTools.ttLib import TTFont
|
|
|
18
19
|
ROOT = Path(__file__).resolve().parents[1]
|
|
19
20
|
FONTS = ROOT / "public/fonts"
|
|
20
21
|
OUTPUT = FONTS / "terminal"
|
|
22
|
+
FAMILY_SOURCES = [
|
|
23
|
+
("Termdock Prompt Icons", [f"JetBrainsMonoNLNerdFontMono-{w}" for w in ["Regular", "Bold"]]),
|
|
24
|
+
("Termdock Maple", [f"MapleMono-{w}" for w in ["Regular", "Bold", "Italic", "BoldItalic"]]),
|
|
25
|
+
("Termdock Mono", [f"JetBrainsMonoNLNerdFontMono-{w}" for w in ["Regular", "Bold", "Italic", "BoldItalic"]]),
|
|
26
|
+
("Noto Sans Mono CJK SC", ["NotoSansMonoCJKsc-Subset", "NotoSansMonoCJKsc-Bold-Subset"]),
|
|
27
|
+
]
|
|
21
28
|
|
|
22
29
|
|
|
23
30
|
def ranges(points: list[int]) -> str:
|
|
@@ -30,22 +37,44 @@ def ranges(points: list[int]) -> str:
|
|
|
30
37
|
return ",".join(f"U+{a:X}" if a == b else f"U+{a:X}-{b:X}" for a, b in spans)
|
|
31
38
|
|
|
32
39
|
|
|
33
|
-
def main() -> None:
|
|
40
|
+
def main(families: list[str] | None = None) -> None:
|
|
34
41
|
OUTPUT.mkdir(exist_ok=True)
|
|
35
42
|
css: list[str] = ["/* Generated by scripts/generate-font-subsets.py; do not edit. */"]
|
|
36
43
|
manifest: dict[str, object] = {"schemaVersion": 1, "faces": []}
|
|
37
44
|
expected: set[str] = set()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
(
|
|
41
|
-
|
|
45
|
+
if families:
|
|
46
|
+
previous = json.loads((OUTPUT / "manifest.json").read_text())
|
|
47
|
+
def stored_family(face: dict) -> str:
|
|
48
|
+
return face.get("family") or ("Termdock Maple" if face["source"].startswith("MapleMono-")
|
|
49
|
+
else "Termdock Mono" if face["source"].startswith("JetBrainsMono")
|
|
50
|
+
else "Noto Sans Mono CJK SC")
|
|
51
|
+
manifest["faces"] = [face for face in previous["faces"] if stored_family(face) not in families]
|
|
52
|
+
expected.update(shard["file"] for face in manifest["faces"] for shard in face["shards"])
|
|
53
|
+
replaced_families = set(families)
|
|
54
|
+
if "Termdock Maple" in replaced_families:
|
|
55
|
+
replaced_families.add("Maple Mono")
|
|
56
|
+
css.extend(line for line in (ROOT / "src/lib/terminal/fontFaces.css").read_text().splitlines()
|
|
57
|
+
if line.startswith("@font-face") and not any(f'font-family:"{family}";' in line for family in replaced_families))
|
|
58
|
+
for family, stems in FAMILY_SOURCES:
|
|
59
|
+
if families and family not in families:
|
|
60
|
+
continue
|
|
42
61
|
for stem in stems:
|
|
43
62
|
source = FONTS / f"{stem}.woff2"
|
|
44
63
|
original = TTFont(source)
|
|
45
64
|
cmap = original.getBestCmap()
|
|
65
|
+
if family == "Termdock Prompt Icons":
|
|
66
|
+
spec = json.loads((ROOT / "tools/font/terminal-prompt-icons.json").read_text())
|
|
67
|
+
selected = {int(point, 16) for point in spec["codepoints"]}
|
|
68
|
+
for start, end in spec["ranges"]:
|
|
69
|
+
selected.update(range(int(start, 16), int(end, 16) + 1))
|
|
70
|
+
cmap = {cp: glyph for cp, glyph in cmap.items() if cp in selected}
|
|
46
71
|
groups: dict[str, list[int]] = {}
|
|
47
72
|
for cp in sorted(cmap):
|
|
48
|
-
if family == "Termdock
|
|
73
|
+
if family == "Termdock Prompt Icons":
|
|
74
|
+
key = "icons"
|
|
75
|
+
elif family == "Termdock Maple":
|
|
76
|
+
key = "ascii" if cp == 0xD or 0x20 <= cp <= 0x7E else "text"
|
|
77
|
+
elif family == "Termdock Mono":
|
|
49
78
|
key = f"icons-{cp // 1024:x}" if 0xE000 <= cp <= 0xF8FF or 0xF0000 <= cp <= 0x10FFFF else "text"
|
|
50
79
|
else:
|
|
51
80
|
key = f"cjk-{cp // 256:x}" if 0x3400 <= cp <= 0x9FFF else "punctuation"
|
|
@@ -58,10 +87,30 @@ def main() -> None:
|
|
|
58
87
|
subsetter = subset.Subsetter(options=options)
|
|
59
88
|
subsetter.populate(unicodes=points)
|
|
60
89
|
subsetter.subset(font)
|
|
90
|
+
if family == "Termdock Maple":
|
|
91
|
+
# Maple Mono is an OFL reserved name. Our web subsets keep
|
|
92
|
+
# its glyphs/metrics but carry their own derivative name.
|
|
93
|
+
for record in font["name"].names:
|
|
94
|
+
if record.nameID not in (1, 3, 4, 6, 16):
|
|
95
|
+
continue
|
|
96
|
+
value = record.toUnicode()
|
|
97
|
+
renamed = value.replace("Maple Mono", "Termdock Maple").replace("MapleMono", "TermdockMaple")
|
|
98
|
+
if renamed != value:
|
|
99
|
+
record.string = renamed.encode(record.getEncoding())
|
|
100
|
+
if family == "Termdock Prompt Icons":
|
|
101
|
+
suffix = "Bold" if "Bold" in stem else "Regular"
|
|
102
|
+
names = {1: family, 16: family, 3: f"TermdockPromptIcons-{suffix}",
|
|
103
|
+
4: f"{family} {suffix}", 6: f"TermdockPromptIcons-{suffix}"}
|
|
104
|
+
for record in font["name"].names:
|
|
105
|
+
if record.nameID in names:
|
|
106
|
+
record.string = names[record.nameID].encode(record.getEncoding())
|
|
61
107
|
buffer = io.BytesIO()
|
|
62
108
|
font.save(buffer)
|
|
63
109
|
body = buffer.getvalue()
|
|
64
|
-
|
|
110
|
+
output_stem = stem.replace("MapleMono", "TermdockMaple")
|
|
111
|
+
if family == "Termdock Prompt Icons":
|
|
112
|
+
output_stem = stem.replace("JetBrainsMonoNLNerdFontMono", "TermdockPromptIcons")
|
|
113
|
+
name = f"{output_stem}-{key}-{hashlib.sha256(body).hexdigest()[:12]}.woff2"
|
|
65
114
|
(OUTPUT / name).write_bytes(body)
|
|
66
115
|
expected.add(name)
|
|
67
116
|
# Verify both coverage and metrics rather than trusting the subsetter.
|
|
@@ -74,7 +123,10 @@ def main() -> None:
|
|
|
74
123
|
style = "italic" if "Italic" in stem else "normal"
|
|
75
124
|
css.append(f'@font-face{{font-family:"{family}";src:url("/fonts/terminal/{name}") format("woff2");font-weight:{weight};font-style:{style};font-display:swap;unicode-range:{ranges(points)};}}')
|
|
76
125
|
records.append({"file": name, "bytes": len(body), "codepoints": len(points), "group": key})
|
|
77
|
-
|
|
126
|
+
record = {"source": source.name, "sourceSha256": hashlib.sha256(source.read_bytes()).hexdigest(), "shards": records}
|
|
127
|
+
if family == "Termdock Prompt Icons":
|
|
128
|
+
record["family"] = family
|
|
129
|
+
manifest["faces"].append(record)
|
|
78
130
|
# Delete only generated font shards; the full source font pack is untouched.
|
|
79
131
|
for old in OUTPUT.glob("*.woff2"):
|
|
80
132
|
if old.name not in expected:
|
|
@@ -85,4 +137,7 @@ def main() -> None:
|
|
|
85
137
|
|
|
86
138
|
|
|
87
139
|
if __name__ == "__main__":
|
|
88
|
-
|
|
140
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
141
|
+
parser.add_argument("--family", action="append", choices=[family for family, _ in FAMILY_SOURCES],
|
|
142
|
+
help="Regenerate only this family, retaining other existing shards and manifest entries.")
|
|
143
|
+
main(parser.parse_args().family)
|
|
Binary file
|
|
Binary file
|