termdock 1.4.174 → 1.4.175

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.
Files changed (355) hide show
  1. package/.desktop-dist/main.js +154 -7
  2. package/dist/client/assets/App-BXYZyI_k.js +150 -0
  3. package/dist/client/assets/App-BXYZyI_k.js.br +0 -0
  4. package/dist/client/assets/App-BXYZyI_k.js.gz +0 -0
  5. package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js +2 -0
  6. package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.br +0 -0
  7. package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.gz +0 -0
  8. package/dist/client/assets/DagPlayground-ZIqLZoMo.js +1 -0
  9. package/dist/client/assets/DagPlayground-ZIqLZoMo.js.br +0 -0
  10. package/dist/client/assets/DagPlayground-ZIqLZoMo.js.gz +0 -0
  11. package/dist/client/assets/{DiffLab-CdbPsjKS.js → DiffLab-CSMf8So2.js} +2 -2
  12. package/dist/client/assets/DiffLab-CSMf8So2.js.br +0 -0
  13. package/dist/client/assets/DiffLab-CSMf8So2.js.gz +0 -0
  14. package/dist/client/assets/DiffReview-CE5lW1zQ.js +1 -0
  15. package/dist/client/assets/DiffReview-CE5lW1zQ.js.br +0 -0
  16. package/dist/client/assets/DiffReview-CE5lW1zQ.js.gz +0 -0
  17. package/dist/client/assets/DiffReviewLab-CpUW75fZ.js +2 -0
  18. package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.br +0 -0
  19. package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.gz +0 -0
  20. package/dist/client/assets/{DiffViewer-hdxcM9yF.js → DiffViewer-BomvZ4kq.js} +5 -5
  21. package/dist/client/assets/DiffViewer-BomvZ4kq.js.br +0 -0
  22. package/dist/client/assets/DiffViewer-BomvZ4kq.js.gz +0 -0
  23. package/dist/client/assets/{ModelPreview-BZ454pgM.js → ModelPreview-BWdZFyhh.js} +3 -3
  24. package/dist/client/assets/ModelPreview-BWdZFyhh.js.br +0 -0
  25. package/dist/client/assets/ModelPreview-BWdZFyhh.js.gz +0 -0
  26. package/dist/client/assets/RightSidebar-CYVwFbOc.js +79 -0
  27. package/dist/client/assets/RightSidebar-CYVwFbOc.js.br +0 -0
  28. package/dist/client/assets/RightSidebar-CYVwFbOc.js.gz +0 -0
  29. package/dist/client/assets/{abnfDiagram-VCTEODGH-C185Omck.js → abnfDiagram-VCTEODGH-BIPsLStE.js} +1 -1
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.br +0 -0
  31. package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.gz +0 -0
  32. package/dist/client/assets/app-D6KzR8B3.js +34 -0
  33. package/dist/client/assets/app-D6KzR8B3.js.br +0 -0
  34. package/dist/client/assets/app-D6KzR8B3.js.gz +0 -0
  35. package/dist/client/assets/{arc-DNt-DTCl.js → arc-DzeT6hBj.js} +1 -1
  36. package/dist/client/assets/arc-DzeT6hBj.js.br +0 -0
  37. package/dist/client/assets/arc-DzeT6hBj.js.gz +0 -0
  38. package/dist/client/assets/{architectureDiagram-5GKGNRK7-C5Nwm_Qz.js → architectureDiagram-5GKGNRK7-B1qsxd_5.js} +1 -1
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.br +0 -0
  40. package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.gz +0 -0
  41. package/dist/client/assets/{blockDiagram-I7D4REHJ-C5aR_7V9.js → blockDiagram-I7D4REHJ-NLsa29Jp.js} +1 -1
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.br +0 -0
  43. package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.gz +0 -0
  44. package/dist/client/assets/{c4Diagram-7LVT6UL2-DjV6ZNwd.js → c4Diagram-7LVT6UL2-DBAUvou3.js} +1 -1
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.br +0 -0
  46. package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.gz +0 -0
  47. package/dist/client/assets/channel-BhwBAWhq.js +1 -0
  48. package/dist/client/assets/{chunk-2Q5K7J3B-Ct7nd4cL.js → chunk-2Q5K7J3B-BsVAXGLa.js} +1 -1
  49. package/dist/client/assets/{chunk-5VM5RSS4-CXem4UYP.js → chunk-5VM5RSS4-BKtILsst.js} +1 -1
  50. package/dist/client/assets/{chunk-F27PBJKO-DyMNEawE.js → chunk-F27PBJKO-Dn9zcQZG.js} +1 -1
  51. package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.br +0 -0
  52. package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.gz +0 -0
  53. package/dist/client/assets/{chunk-IMKFNOWR-BsW7iIrh.js → chunk-IMKFNOWR-MVtVQeTr.js} +1 -1
  54. package/dist/client/assets/chunk-IMKFNOWR-MVtVQeTr.js.br +0 -0
  55. package/dist/client/assets/{chunk-IMKFNOWR-BsW7iIrh.js.gz → chunk-IMKFNOWR-MVtVQeTr.js.gz} +0 -0
  56. package/dist/client/assets/{chunk-JWPE2WC7-EO8E1q1J.js → chunk-JWPE2WC7-DliIIMe7.js} +1 -1
  57. package/dist/client/assets/{chunk-POPQ4Y6H-0One8T9x.js → chunk-POPQ4Y6H-XBSus3IC.js} +1 -1
  58. package/dist/client/assets/{chunk-SVP7TREG-CDu41dkj.js → chunk-SVP7TREG-wleSQcJR.js} +1 -1
  59. package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.br +0 -0
  60. package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.gz +0 -0
  61. package/dist/client/assets/{chunk-TICWLB2K-BACjgX9v.js → chunk-TICWLB2K-Dq6LdswN.js} +1 -1
  62. package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.br +0 -0
  63. package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.gz +0 -0
  64. package/dist/client/assets/{chunk-XXDRQBXY-BJWowF6E.js → chunk-XXDRQBXY-xafyKlM-.js} +1 -1
  65. package/dist/client/assets/classDiagram-ZZMXUADV-slQB0ns2.js +1 -0
  66. package/dist/client/assets/classDiagram-v2-VYDZK3BY-slQB0ns2.js +1 -0
  67. package/dist/client/assets/{cose-bilkent-JH36ORCC-WTwHOCdm.js → cose-bilkent-JH36ORCC-BR7Ohnm_.js} +1 -1
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.br +0 -0
  69. package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.gz +0 -0
  70. package/dist/client/assets/{cynefin-OW5HDTMX-D4y8TkaO.js → cynefin-OW5HDTMX-CmPjQgER.js} +1 -1
  71. package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.br +0 -0
  72. package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.gz +0 -0
  73. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js → cynefinDiagram-5FMLGOSQ-CFyR9RvM.js} +1 -1
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.br +0 -0
  75. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.gz +0 -0
  76. package/dist/client/assets/{dagre-GXQ25YYZ-CwMYtt5E.js → dagre-GXQ25YYZ-D_RVMRO1.js} +1 -1
  77. package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.br +0 -0
  78. package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.gz +0 -0
  79. package/dist/client/assets/{diagram-S7CK7UJ4-BnTeHiAb.js → diagram-S7CK7UJ4-6fr-ZoPj.js} +1 -1
  80. package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.br +0 -0
  81. package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.gz +0 -0
  82. package/dist/client/assets/{diagram-UQ7AKVKN-BNlh251g.js → diagram-UQ7AKVKN-CgwiUtz0.js} +1 -1
  83. package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.br +0 -0
  84. package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.gz +0 -0
  85. package/dist/client/assets/{diagram-VSXAHHWV-B5FftXkQ.js → diagram-VSXAHHWV-CLxzTB9k.js} +1 -1
  86. package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.br +0 -0
  87. package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.gz +0 -0
  88. package/dist/client/assets/{diagram-VX7I27RA-C51ZNXR2.js → diagram-VX7I27RA-DjxDAvdZ.js} +1 -1
  89. package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.br +0 -0
  90. package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.gz +0 -0
  91. package/dist/client/assets/{diagram-Z3DM3KII-CXoZH2qf.js → diagram-Z3DM3KII-CDPOjiz4.js} +1 -1
  92. package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.br +0 -0
  93. package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.gz +0 -0
  94. package/dist/client/assets/{ebnfDiagram-PWID7BFC-C7YFLlx4.js → ebnfDiagram-PWID7BFC-BLbwyF1O.js} +1 -1
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.br +0 -0
  96. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.gz +0 -0
  97. package/dist/client/assets/{erDiagram-RLTQ6QDP-BNQlS7-g.js → erDiagram-RLTQ6QDP-DbjtZnJx.js} +1 -1
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-DbjtZnJx.js.br +0 -0
  99. package/dist/client/assets/erDiagram-RLTQ6QDP-DbjtZnJx.js.gz +0 -0
  100. package/dist/client/assets/{flowDiagram-HODETNUW-DIQXZ06I.js → flowDiagram-HODETNUW-BirJInAN.js} +1 -1
  101. package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.br +0 -0
  102. package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.gz +0 -0
  103. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BaJQfOqD.js → ganttDiagram-EL5Y4UJY-d6Q9vSbv.js} +1 -1
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.br +0 -0
  105. package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.gz +0 -0
  106. package/dist/client/assets/gestureArbiter-C2J0D0kV.css +1 -0
  107. package/dist/client/assets/gestureArbiter-C2J0D0kV.css.br +0 -0
  108. package/dist/client/assets/gestureArbiter-C2J0D0kV.css.gz +0 -0
  109. package/dist/client/assets/gestureArbiter-D1SNS_gK.js +1 -0
  110. package/dist/client/assets/gestureArbiter-D1SNS_gK.js.br +0 -0
  111. package/dist/client/assets/gestureArbiter-D1SNS_gK.js.gz +0 -0
  112. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js → gitGraphDiagram-WWUBYQGX-BWBoIeS6.js} +1 -1
  113. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.br +0 -0
  114. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.gz +0 -0
  115. package/dist/client/assets/{index-DlxiaPv1.css → index-DUI8KRf6.css} +1 -1
  116. package/dist/client/assets/index-DUI8KRf6.css.br +0 -0
  117. package/dist/client/assets/index-DUI8KRf6.css.gz +0 -0
  118. package/dist/client/assets/{infoDiagram-27XIBGKW-DTOUeQrN.js → infoDiagram-27XIBGKW-BH4AynkM.js} +1 -1
  119. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-8sBi-gla.js → ishikawaDiagram-5VMMS53U-BQw90AKR.js} +1 -1
  120. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-8sBi-gla.js.br → ishikawaDiagram-5VMMS53U-BQw90AKR.js.br} +0 -0
  121. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BQw90AKR.js.gz +0 -0
  122. package/dist/client/assets/{journeyDiagram-3NMN7TZE-ZwikziWh.js → journeyDiagram-3NMN7TZE-DISkQocf.js} +1 -1
  123. package/dist/client/assets/journeyDiagram-3NMN7TZE-DISkQocf.js.br +0 -0
  124. package/dist/client/assets/{journeyDiagram-3NMN7TZE-ZwikziWh.js.gz → journeyDiagram-3NMN7TZE-DISkQocf.js.gz} +0 -0
  125. package/dist/client/assets/{kanban-definition-UXKFOSKX-wjuw2jQ4.js → kanban-definition-UXKFOSKX-Qg2LyEVy.js} +1 -1
  126. package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.br +0 -0
  127. package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.gz +0 -0
  128. package/dist/client/assets/{layout-nASMIj-E.js → layout-Cd7-4_Vr.js} +1 -1
  129. package/dist/client/assets/layout-Cd7-4_Vr.js.br +0 -0
  130. package/dist/client/assets/layout-Cd7-4_Vr.js.gz +0 -0
  131. package/dist/client/assets/{linear-pGaQeqW5.js → linear-DFTi2fTn.js} +1 -1
  132. package/dist/client/assets/linear-DFTi2fTn.js.br +0 -0
  133. package/dist/client/assets/linear-DFTi2fTn.js.gz +0 -0
  134. package/dist/client/assets/{mermaid.core-CKEBWbQz.js → mermaid.core-2v726lUU.js} +5 -5
  135. package/dist/client/assets/mermaid.core-2v726lUU.js.br +0 -0
  136. package/dist/client/assets/mermaid.core-2v726lUU.js.gz +0 -0
  137. package/dist/client/assets/{mindmap-definition-YA3MSWOX-CBjYcnwd.js → mindmap-definition-YA3MSWOX-_FpwteCy.js} +4 -4
  138. package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.br +0 -0
  139. package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.gz +0 -0
  140. package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js +2 -0
  141. package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.br +0 -0
  142. package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.gz +0 -0
  143. package/dist/client/assets/{pegDiagram-XKGWAZYB-DJqvclIp.js → pegDiagram-XKGWAZYB-DLFf-HmZ.js} +1 -1
  144. package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.br +0 -0
  145. package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.gz +0 -0
  146. package/dist/client/assets/{pieDiagram-E7YTZNPT-DLV4WPwc.js → pieDiagram-E7YTZNPT-DqTnfXHG.js} +1 -1
  147. package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.br +0 -0
  148. package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.gz +0 -0
  149. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BN7Zpsb9.js → quadrantDiagram-AXDQQJYC-B_oBH52Q.js} +1 -1
  150. package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.br +0 -0
  151. package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.gz +0 -0
  152. package/dist/client/assets/{railroadDiagram-O6MQD6OU-DfHX6MUm.js → railroadDiagram-O6MQD6OU-HxFQ7Pce.js} +1 -1
  153. package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.br +0 -0
  154. package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.gz +0 -0
  155. package/dist/client/assets/referenceLongPress-B3NGIjvv.js +1 -0
  156. package/dist/client/assets/referenceLongPress-B3NGIjvv.js.br +0 -0
  157. package/dist/client/assets/referenceLongPress-B3NGIjvv.js.gz +0 -0
  158. package/dist/client/assets/{requirementDiagram-BXWQKSXE-tQVSFg_k.js → requirementDiagram-BXWQKSXE-NZdfaZFk.js} +1 -1
  159. package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.br +0 -0
  160. package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.gz +0 -0
  161. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-C7JW1-Wh.js → sankeyDiagram-P5KCCOFB-BgCctv6X.js} +1 -1
  162. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.br +0 -0
  163. package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.gz +0 -0
  164. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BVeDiFDI.js → sequenceDiagram-WJ2MYXX4-Br42Za1R.js} +1 -1
  165. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.br +0 -0
  166. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.gz +0 -0
  167. package/dist/client/assets/{sizeCapture-INFHLROL-CBRaCpeP.js → sizeCapture-INFHLROL-FT4z4S1b.js} +1 -1
  168. package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.br +0 -0
  169. package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.gz +0 -0
  170. package/dist/client/assets/sparkles-BaGaevME.js +1 -0
  171. package/dist/client/assets/{stateDiagram-D77RDMKH-CiLpGRuf.js → stateDiagram-D77RDMKH-DD0t9f84.js} +1 -1
  172. package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.br +0 -0
  173. package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.gz +0 -0
  174. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BhvVVB56.js +1 -0
  175. package/dist/client/assets/{swimlanes-42K2YHIH-BkzVtkVY.js → swimlanes-42K2YHIH-DcC8pGLx.js} +1 -1
  176. package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.br +0 -0
  177. package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.gz +0 -0
  178. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DSC5lz0i.js +8 -0
  179. package/dist/client/assets/{timeline-definition-24CTP7MA-DKyfGITP.js → timeline-definition-24CTP7MA-DnwO_Pxd.js} +1 -1
  180. package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.br +0 -0
  181. package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.gz +0 -0
  182. package/dist/client/assets/triangle-alert-BCQO9Ap9.js +1 -0
  183. package/dist/client/assets/{vennDiagram-4TSXK5OY-CRgkkrgx.js → vennDiagram-4TSXK5OY-IVNSrZDM.js} +1 -1
  184. package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.br +0 -0
  185. package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.gz +0 -0
  186. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js → wardleyDiagram-VM6X3IG4-BnGetaQL.js} +1 -1
  187. package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.br +0 -0
  188. package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.gz +0 -0
  189. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DkPYl6E0.js → xychartDiagram-S5SC5T6Z-C01eBN-W.js} +1 -1
  190. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.js.br +0 -0
  191. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.js.gz +0 -0
  192. package/dist/client/index.html +2 -2
  193. package/dist/client/index.html.br +0 -0
  194. package/dist/client/index.html.gz +0 -0
  195. package/dist/client/preview-shell.html +1 -0
  196. package/dist/client/preview-shell.js +20 -0
  197. package/dist/client/preview-shell.js.br +0 -0
  198. package/dist/client/preview-shell.js.gz +0 -0
  199. package/dist/client/sw-secure-api.js +132 -0
  200. package/dist/client/sw-secure-api.js.br +0 -0
  201. package/dist/client/sw-secure-api.js.gz +0 -0
  202. package/dist/client/sw.js +1 -1
  203. package/dist/client/sw.js.br +0 -0
  204. package/dist/client/sw.js.gz +0 -0
  205. package/dist/server/agent/collaborationCli.js +298 -0
  206. package/dist/server/agent/collaborationPrompt.js +10 -2
  207. package/dist/server/agent/collaborationProtocol.js +50 -0
  208. package/dist/server/agent/collaborationRoutes.js +86 -0
  209. package/dist/server/agent/collaborationStore.js +249 -21
  210. package/dist/server/cli.js +28 -168
  211. package/dist/server/entry.js +114 -51
  212. package/dist/server/federation/accessPolicy.js +122 -0
  213. package/dist/server/federation/authorization.js +226 -0
  214. package/dist/server/federation/bootstrapCors.js +23 -0
  215. package/dist/server/federation/cli.js +185 -0
  216. package/dist/server/federation/deviceNames.js +29 -0
  217. package/dist/server/federation/directRoutes.js +72 -0
  218. package/dist/server/federation/invitations.js +123 -0
  219. package/dist/server/federation/openAccess.js +25 -0
  220. package/dist/server/federation/originPolicy.js +19 -0
  221. package/dist/server/federation/packets.js +116 -0
  222. package/dist/server/federation/passwordBootstrap.js +86 -0
  223. package/dist/server/federation/passwordLoginRoutes.js +98 -0
  224. package/dist/server/federation/relay.js +346 -0
  225. package/dist/server/federation/requestContext.js +4 -0
  226. package/dist/server/federation/routeAccess.js +124 -0
  227. package/dist/server/federation/routeInvitations.js +160 -0
  228. package/dist/server/federation/runtime.js +537 -0
  229. package/dist/server/federation/secureProtocol.js +223 -0
  230. package/dist/server/federation/socketDuplex.js +42 -0
  231. package/dist/server/federation/streamLimits.js +5 -0
  232. package/dist/server/routes/filesystem.js +2 -1
  233. package/dist/server/routes/terminal.js +40 -72
  234. package/dist/server/utils/authProtection.js +13 -0
  235. package/dist/server/utils/csrfProtection.js +3 -0
  236. package/dist/server/utils/publicSecurity.js +8 -3
  237. package/package.json +10 -3
  238. package/runtime-manifest.json +4 -4
  239. package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js +0 -2
  240. package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js.br +0 -0
  241. package/dist/client/assets/ChangeWalkthroughPanel-hj8I8Cwy.js.gz +0 -0
  242. package/dist/client/assets/DagPlayground-BRoB8JX5.js +0 -1
  243. package/dist/client/assets/DagPlayground-BRoB8JX5.js.br +0 -0
  244. package/dist/client/assets/DagPlayground-BRoB8JX5.js.gz +0 -0
  245. package/dist/client/assets/DiffLab-CdbPsjKS.js.br +0 -0
  246. package/dist/client/assets/DiffLab-CdbPsjKS.js.gz +0 -0
  247. package/dist/client/assets/DiffReview-MQXjrRsW.js +0 -1
  248. package/dist/client/assets/DiffReview-MQXjrRsW.js.br +0 -0
  249. package/dist/client/assets/DiffReview-MQXjrRsW.js.gz +0 -0
  250. package/dist/client/assets/DiffReviewLab-C47CNvDM.js +0 -2
  251. package/dist/client/assets/DiffReviewLab-C47CNvDM.js.br +0 -0
  252. package/dist/client/assets/DiffReviewLab-C47CNvDM.js.gz +0 -0
  253. package/dist/client/assets/DiffViewer-hdxcM9yF.js.br +0 -0
  254. package/dist/client/assets/DiffViewer-hdxcM9yF.js.gz +0 -0
  255. package/dist/client/assets/ModelPreview-BZ454pgM.js.br +0 -0
  256. package/dist/client/assets/ModelPreview-BZ454pgM.js.gz +0 -0
  257. package/dist/client/assets/RightSidebar-i0Oq68YI.js +0 -55
  258. package/dist/client/assets/RightSidebar-i0Oq68YI.js.br +0 -0
  259. package/dist/client/assets/RightSidebar-i0Oq68YI.js.gz +0 -0
  260. package/dist/client/assets/abnfDiagram-VCTEODGH-C185Omck.js.br +0 -0
  261. package/dist/client/assets/abnfDiagram-VCTEODGH-C185Omck.js.gz +0 -0
  262. package/dist/client/assets/app-DAp8rVMc.js +0 -175
  263. package/dist/client/assets/app-DAp8rVMc.js.br +0 -0
  264. package/dist/client/assets/app-DAp8rVMc.js.gz +0 -0
  265. package/dist/client/assets/arc-DNt-DTCl.js.br +0 -0
  266. package/dist/client/assets/arc-DNt-DTCl.js.gz +0 -0
  267. package/dist/client/assets/architectureDiagram-5GKGNRK7-C5Nwm_Qz.js.br +0 -0
  268. package/dist/client/assets/architectureDiagram-5GKGNRK7-C5Nwm_Qz.js.gz +0 -0
  269. package/dist/client/assets/blockDiagram-I7D4REHJ-C5aR_7V9.js.br +0 -0
  270. package/dist/client/assets/blockDiagram-I7D4REHJ-C5aR_7V9.js.gz +0 -0
  271. package/dist/client/assets/c4Diagram-7LVT6UL2-DjV6ZNwd.js.br +0 -0
  272. package/dist/client/assets/c4Diagram-7LVT6UL2-DjV6ZNwd.js.gz +0 -0
  273. package/dist/client/assets/channel-uhLrATdy.js +0 -1
  274. package/dist/client/assets/chunk-F27PBJKO-DyMNEawE.js.br +0 -0
  275. package/dist/client/assets/chunk-F27PBJKO-DyMNEawE.js.gz +0 -0
  276. package/dist/client/assets/chunk-IMKFNOWR-BsW7iIrh.js.br +0 -0
  277. package/dist/client/assets/chunk-SVP7TREG-CDu41dkj.js.br +0 -0
  278. package/dist/client/assets/chunk-SVP7TREG-CDu41dkj.js.gz +0 -0
  279. package/dist/client/assets/chunk-TICWLB2K-BACjgX9v.js.br +0 -0
  280. package/dist/client/assets/chunk-TICWLB2K-BACjgX9v.js.gz +0 -0
  281. package/dist/client/assets/classDiagram-ZZMXUADV-CnxFfzm9.js +0 -1
  282. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CnxFfzm9.js +0 -1
  283. package/dist/client/assets/cose-bilkent-JH36ORCC-WTwHOCdm.js.br +0 -0
  284. package/dist/client/assets/cose-bilkent-JH36ORCC-WTwHOCdm.js.gz +0 -0
  285. package/dist/client/assets/cynefin-OW5HDTMX-D4y8TkaO.js.br +0 -0
  286. package/dist/client/assets/cynefin-OW5HDTMX-D4y8TkaO.js.gz +0 -0
  287. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js.br +0 -0
  288. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BzN8Ggk_.js.gz +0 -0
  289. package/dist/client/assets/dagre-GXQ25YYZ-CwMYtt5E.js.br +0 -0
  290. package/dist/client/assets/dagre-GXQ25YYZ-CwMYtt5E.js.gz +0 -0
  291. package/dist/client/assets/diagram-S7CK7UJ4-BnTeHiAb.js.br +0 -0
  292. package/dist/client/assets/diagram-S7CK7UJ4-BnTeHiAb.js.gz +0 -0
  293. package/dist/client/assets/diagram-UQ7AKVKN-BNlh251g.js.br +0 -0
  294. package/dist/client/assets/diagram-UQ7AKVKN-BNlh251g.js.gz +0 -0
  295. package/dist/client/assets/diagram-VSXAHHWV-B5FftXkQ.js.br +0 -0
  296. package/dist/client/assets/diagram-VSXAHHWV-B5FftXkQ.js.gz +0 -0
  297. package/dist/client/assets/diagram-VX7I27RA-C51ZNXR2.js.br +0 -0
  298. package/dist/client/assets/diagram-VX7I27RA-C51ZNXR2.js.gz +0 -0
  299. package/dist/client/assets/diagram-Z3DM3KII-CXoZH2qf.js.br +0 -0
  300. package/dist/client/assets/diagram-Z3DM3KII-CXoZH2qf.js.gz +0 -0
  301. package/dist/client/assets/ebnfDiagram-PWID7BFC-C7YFLlx4.js.br +0 -0
  302. package/dist/client/assets/ebnfDiagram-PWID7BFC-C7YFLlx4.js.gz +0 -0
  303. package/dist/client/assets/erDiagram-RLTQ6QDP-BNQlS7-g.js.br +0 -0
  304. package/dist/client/assets/erDiagram-RLTQ6QDP-BNQlS7-g.js.gz +0 -0
  305. package/dist/client/assets/flowDiagram-HODETNUW-DIQXZ06I.js.br +0 -0
  306. package/dist/client/assets/flowDiagram-HODETNUW-DIQXZ06I.js.gz +0 -0
  307. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BaJQfOqD.js.br +0 -0
  308. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BaJQfOqD.js.gz +0 -0
  309. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js.br +0 -0
  310. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-tQ4E6pwN.js.gz +0 -0
  311. package/dist/client/assets/index-DlxiaPv1.css.br +0 -0
  312. package/dist/client/assets/index-DlxiaPv1.css.gz +0 -0
  313. package/dist/client/assets/ishikawaDiagram-5VMMS53U-8sBi-gla.js.gz +0 -0
  314. package/dist/client/assets/journeyDiagram-3NMN7TZE-ZwikziWh.js.br +0 -0
  315. package/dist/client/assets/kanban-definition-UXKFOSKX-wjuw2jQ4.js.br +0 -0
  316. package/dist/client/assets/kanban-definition-UXKFOSKX-wjuw2jQ4.js.gz +0 -0
  317. package/dist/client/assets/layout-nASMIj-E.js.br +0 -0
  318. package/dist/client/assets/layout-nASMIj-E.js.gz +0 -0
  319. package/dist/client/assets/linear-pGaQeqW5.js.br +0 -0
  320. package/dist/client/assets/linear-pGaQeqW5.js.gz +0 -0
  321. package/dist/client/assets/mermaid.core-CKEBWbQz.js.br +0 -0
  322. package/dist/client/assets/mermaid.core-CKEBWbQz.js.gz +0 -0
  323. package/dist/client/assets/mindmap-definition-YA3MSWOX-CBjYcnwd.js.br +0 -0
  324. package/dist/client/assets/mindmap-definition-YA3MSWOX-CBjYcnwd.js.gz +0 -0
  325. package/dist/client/assets/pegDiagram-XKGWAZYB-DJqvclIp.js.br +0 -0
  326. package/dist/client/assets/pegDiagram-XKGWAZYB-DJqvclIp.js.gz +0 -0
  327. package/dist/client/assets/pieDiagram-E7YTZNPT-DLV4WPwc.js.br +0 -0
  328. package/dist/client/assets/pieDiagram-E7YTZNPT-DLV4WPwc.js.gz +0 -0
  329. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BN7Zpsb9.js.br +0 -0
  330. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BN7Zpsb9.js.gz +0 -0
  331. package/dist/client/assets/railroadDiagram-O6MQD6OU-DfHX6MUm.js.br +0 -0
  332. package/dist/client/assets/railroadDiagram-O6MQD6OU-DfHX6MUm.js.gz +0 -0
  333. package/dist/client/assets/requirementDiagram-BXWQKSXE-tQVSFg_k.js.br +0 -0
  334. package/dist/client/assets/requirementDiagram-BXWQKSXE-tQVSFg_k.js.gz +0 -0
  335. package/dist/client/assets/sankeyDiagram-P5KCCOFB-C7JW1-Wh.js.br +0 -0
  336. package/dist/client/assets/sankeyDiagram-P5KCCOFB-C7JW1-Wh.js.gz +0 -0
  337. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BVeDiFDI.js.br +0 -0
  338. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BVeDiFDI.js.gz +0 -0
  339. package/dist/client/assets/sizeCapture-INFHLROL-CBRaCpeP.js.br +0 -0
  340. package/dist/client/assets/sizeCapture-INFHLROL-CBRaCpeP.js.gz +0 -0
  341. package/dist/client/assets/sparkles-D0cwIvB0.js +0 -1
  342. package/dist/client/assets/stateDiagram-D77RDMKH-CiLpGRuf.js.br +0 -0
  343. package/dist/client/assets/stateDiagram-D77RDMKH-CiLpGRuf.js.gz +0 -0
  344. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BRbS0rv2.js +0 -1
  345. package/dist/client/assets/swimlanes-42K2YHIH-BkzVtkVY.js.br +0 -0
  346. package/dist/client/assets/swimlanes-42K2YHIH-BkzVtkVY.js.gz +0 -0
  347. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CeWFezkt.js +0 -8
  348. package/dist/client/assets/timeline-definition-24CTP7MA-DKyfGITP.js.br +0 -0
  349. package/dist/client/assets/timeline-definition-24CTP7MA-DKyfGITP.js.gz +0 -0
  350. package/dist/client/assets/vennDiagram-4TSXK5OY-CRgkkrgx.js.br +0 -0
  351. package/dist/client/assets/vennDiagram-4TSXK5OY-CRgkkrgx.js.gz +0 -0
  352. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js.br +0 -0
  353. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cr7yS4CZ.js.gz +0 -0
  354. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DkPYl6E0.js.br +0 -0
  355. package/dist/client/assets/xychartDiagram-S5SC5T6Z-DkPYl6E0.js.gz +0 -0
@@ -0,0 +1,98 @@
1
+ import express from 'express';
2
+ import { PasswordBootstrapServer } from './passwordBootstrap.js';
3
+ import { getLoginBlockMs, getPasswordVerifier, recordLoginFailure, recordLoginSuccess } from '../utils/authProtection.js';
4
+ import { getPublicOrigin } from '../utils/publicSecurity.js';
5
+ import { bootstrapCors } from './bootstrapCors.js';
6
+ export function createPasswordLoginRouter(getRuntime) {
7
+ const router = express.Router();
8
+ let bootstrap;
9
+ let runtime;
10
+ let computing = 0;
11
+ const starts = [];
12
+ const pending = new Map();
13
+ router.use(bootstrapCors);
14
+ router.use((_req, res, next) => {
15
+ const ready = getRuntime();
16
+ if (!ready) {
17
+ res.status(503).json({ error: 'Encrypted login is starting' });
18
+ return;
19
+ }
20
+ if (ready !== runtime) {
21
+ runtime = ready;
22
+ bootstrap = new PasswordBootstrapServer({ getPasswordHash: getPasswordVerifier, serverIdentity: ready.serviceId });
23
+ pending.clear();
24
+ }
25
+ res.setHeader('Cache-Control', 'no-store');
26
+ next();
27
+ });
28
+ router.get('/parameters', (_req, res) => {
29
+ try {
30
+ res.json(bootstrap.parameters());
31
+ }
32
+ catch {
33
+ res.status(503).json({ error: 'Password login unavailable' });
34
+ }
35
+ });
36
+ router.use(express.json({ limit: '8kb' }));
37
+ router.post('/start', async (req, res) => {
38
+ const ip = req.socket.remoteAddress ?? 'unknown';
39
+ const now = Date.now();
40
+ while (starts.length && starts[0] <= now - 60_000)
41
+ starts.shift();
42
+ for (const [id, entry] of pending)
43
+ if (entry.expiresAt <= now)
44
+ pending.delete(id);
45
+ if (getLoginBlockMs(ip) > 0 || starts.length >= 30 || computing >= 2) {
46
+ res.status(429).json({ error: 'Too many login attempts' });
47
+ return;
48
+ }
49
+ if (typeof req.body?.clientIdentity !== 'string' || typeof req.body?.startLoginRequest !== 'string' ||
50
+ Object.keys(req.body).some(key => !['clientIdentity', 'startLoginRequest'].includes(key))) {
51
+ res.status(400).json({ error: 'Invalid login request' });
52
+ return;
53
+ }
54
+ const origin = req.headers.origin ?? getPublicOrigin() ?? `${req.protocol}://${req.headers.host}`;
55
+ starts.push(now);
56
+ computing++;
57
+ recordLoginFailure(ip);
58
+ try {
59
+ const result = await bootstrap.start({ ...req.body, origin });
60
+ pending.set(result.attemptId, { ip, origin, expiresAt: Date.now() + 60_000 });
61
+ res.json(result);
62
+ }
63
+ catch {
64
+ res.status(401).json({ error: 'Password authentication failed' });
65
+ }
66
+ finally {
67
+ computing--;
68
+ }
69
+ });
70
+ router.post('/finish', async (req, res) => {
71
+ const ip = req.socket.remoteAddress ?? 'unknown';
72
+ const origin = req.headers.origin ?? getPublicOrigin() ?? `${req.protocol}://${req.headers.host}`;
73
+ if (typeof req.body?.attemptId !== 'string' || typeof req.body?.finishLoginRequest !== 'string' ||
74
+ Object.keys(req.body).some(key => !['attemptId', 'finishLoginRequest'].includes(key))) {
75
+ res.status(400).json({ error: 'Invalid login request' });
76
+ return;
77
+ }
78
+ const entry = pending.get(req.body.attemptId);
79
+ if (!entry || entry.ip !== ip || entry.origin !== origin || entry.expiresAt <= Date.now()) {
80
+ res.status(401).json({ error: 'Password authentication failed' });
81
+ return;
82
+ }
83
+ pending.delete(req.body.attemptId);
84
+ try {
85
+ const credential = getPasswordVerifier();
86
+ const result = await bootstrap.finish(req.body);
87
+ if (!credential || credential !== getPasswordVerifier())
88
+ throw new Error('Password changed');
89
+ runtime.store.grantPassword(result.clientIdentity);
90
+ recordLoginSuccess(ip);
91
+ res.json({ ok: true });
92
+ }
93
+ catch {
94
+ res.status(401).json({ error: 'Password authentication failed' });
95
+ }
96
+ });
97
+ return router;
98
+ }
@@ -0,0 +1,346 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+ const defaults = { maxFrameBytes: 256 * 1024, maxBufferedBytes: 1024 * 1024,
4
+ maxStreams: 128, heartbeatMs: 30_000, maxRouteTtlMs: 90_000, connectTimeoutMs: 10_000 };
5
+ const identifier = (v) => typeof v === 'string' && /^[a-zA-Z0-9_.:-]{1,160}$/.test(v);
6
+ function parse(raw, limits) {
7
+ if (Buffer.byteLength(raw instanceof Array ? Buffer.concat(raw) : raw) > limits.maxFrameBytes)
8
+ throw new Error('Frame exceeds budget');
9
+ const f = JSON.parse(raw.toString());
10
+ if (f.type === 'register') {
11
+ if (!Array.isArray(f.serviceIds) || f.serviceIds.length > 64 || !f.serviceIds.every(identifier)
12
+ || !Number.isFinite(f.ttlMs) || f.ttlMs <= 0)
13
+ throw new Error('Invalid registration');
14
+ }
15
+ else if (!identifier(f.streamId) || !['open', 'opened', 'data', 'close'].includes(f.type)
16
+ || (f.type === 'open' && !identifier(f.serviceId))
17
+ || (f.type === 'data' && (typeof f.payload !== 'string' || (f.encoding !== undefined && f.encoding !== 'base64'))))
18
+ throw new Error('Invalid frame');
19
+ return f;
20
+ }
21
+ function send(ws, frame, limits) {
22
+ const data = JSON.stringify(frame);
23
+ if (ws.readyState !== 1)
24
+ return false;
25
+ if (Buffer.byteLength(data) > limits.maxFrameBytes || ws.bufferedAmount + Buffer.byteLength(data) > limits.maxBufferedBytes) {
26
+ ws.close(1013, 'Relay budget exceeded');
27
+ return false;
28
+ }
29
+ ws.send(data);
30
+ return true;
31
+ }
32
+ function watch(ws, limits, cleanup) {
33
+ let alive = true;
34
+ const timer = setInterval(() => {
35
+ if (!alive || ws.readyState !== 1) {
36
+ ws.terminate();
37
+ return;
38
+ }
39
+ alive = false;
40
+ ws.ping();
41
+ }, limits.heartbeatMs);
42
+ timer.unref?.();
43
+ ws.on('pong', () => { alive = true; });
44
+ ws.once('close', () => { clearInterval(timer); cleanup(); });
45
+ ws.on('error', () => { ws.terminate(); });
46
+ }
47
+ /** In-process wire used only for explicitly configured direct routes. */
48
+ class MemoryWire extends EventEmitter {
49
+ readyState = 1;
50
+ bufferedAmount = 0;
51
+ peer;
52
+ send(data) {
53
+ if (this.readyState !== 1 || this.peer.readyState !== 1)
54
+ return;
55
+ const bytes = Buffer.from(data);
56
+ this.bufferedAmount += bytes.length;
57
+ queueMicrotask(() => {
58
+ this.bufferedAmount -= bytes.length;
59
+ if (this.readyState === 1 && this.peer.readyState === 1)
60
+ this.peer.emit('message', bytes, false);
61
+ });
62
+ }
63
+ ping() { queueMicrotask(() => { if (this.readyState === 1 && this.peer.readyState === 1)
64
+ this.emit('pong'); }); }
65
+ close(code = 1000, reason = '') {
66
+ if (this.readyState === 3)
67
+ return;
68
+ this.readyState = 3;
69
+ this.emit('close', code, Buffer.from(reason));
70
+ this.peer.close(code, reason);
71
+ }
72
+ terminate() { this.close(1006); }
73
+ }
74
+ function memoryPair() {
75
+ const left = new MemoryWire();
76
+ const right = new MemoryWire();
77
+ left.peer = right;
78
+ right.peer = left;
79
+ return [left, right];
80
+ }
81
+ export class RelayRouter {
82
+ options;
83
+ limits;
84
+ peers = new Set();
85
+ routes = new Map();
86
+ sweep;
87
+ directPeers = new WeakMap();
88
+ stopped = false;
89
+ constructor(options) {
90
+ this.options = options;
91
+ this.limits = { ...defaults, ...options };
92
+ this.sweep = setInterval(() => {
93
+ for (const [id, route] of this.routes)
94
+ if (route.expires <= Date.now() || !this.canRegister(route.peer, id))
95
+ this.withdraw(id, route.peer);
96
+ for (const peer of this.peers)
97
+ for (const stream of [...peer.streams.values()]) {
98
+ if (stream.owner === peer && !this.canRoute(peer, stream.serviceId))
99
+ this.closeStream(stream, 'Authorization revoked');
100
+ }
101
+ }, Math.min(this.limits.heartbeatMs, 1000));
102
+ this.sweep.unref?.();
103
+ }
104
+ canRegister(peer, serviceId) {
105
+ return this.directPeers.has(peer) ? this.directPeers.get(peer) === serviceId : this.options.allowRegister(peer.principal, serviceId);
106
+ }
107
+ canRoute(peer, serviceId) {
108
+ return !this.directPeers.has(peer) && this.options.allowRoute(peer.principal, serviceId);
109
+ }
110
+ hasRoute(serviceId) {
111
+ const route = this.routes.get(serviceId);
112
+ return !this.stopped && !!route && route.expires > Date.now() && route.peer.ws.readyState === 1 && this.canRegister(route.peer, serviceId);
113
+ }
114
+ /** Trusted local configuration only. Clients never supply a target URL or factory. */
115
+ registerDirect(serviceId, connect) {
116
+ if (this.stopped || !identifier(serviceId) || typeof connect !== 'function' || this.hasRoute(serviceId))
117
+ throw new Error('Invalid or already registered direct target');
118
+ const [routerWire, clientWire] = memoryPair();
119
+ // The private WeakMap marks this peer; its placeholder principal is never
120
+ // passed to externally supplied authentication/authorization callbacks.
121
+ const peer = { ws: routerWire, principal: undefined, streams: new Map() };
122
+ this.directPeers.set(peer, serviceId);
123
+ this.peers.add(peer);
124
+ watch(routerWire, this.limits, () => this.remove(peer));
125
+ routerWire.on('message', raw => {
126
+ try {
127
+ this.receive(peer, parse(raw, this.limits));
128
+ }
129
+ catch {
130
+ routerWire.close(1008, 'Invalid direct route');
131
+ }
132
+ });
133
+ this.receive(peer, { type: 'register', serviceIds: [serviceId], ttlMs: this.limits.maxRouteTtlMs });
134
+ const client = new RelayClient(clientWire, { ...this.limits, targets: new Map([[serviceId, connect]]) });
135
+ return () => { client.close(); this.remove(peer); routerWire.close(); };
136
+ }
137
+ /** Caller awaits attach before completing its application-level ready handshake. */
138
+ async attach(ws, context) {
139
+ // Reject early application frames rather than buffering untrusted traffic while authenticating.
140
+ let early = false;
141
+ const rejectEarly = () => { early = true; ws.close(1008, 'Authentication pending'); };
142
+ ws.on('message', rejectEarly);
143
+ let principal;
144
+ try {
145
+ principal = await this.options.authenticate(context);
146
+ }
147
+ catch {
148
+ principal = null;
149
+ }
150
+ ws.off('message', rejectEarly);
151
+ if (principal === null || early || ws.readyState !== 1) {
152
+ ws.close(1008, 'Relay authentication required');
153
+ return false;
154
+ }
155
+ const peer = { ws, principal, streams: new Map() };
156
+ this.peers.add(peer);
157
+ watch(ws, this.limits, () => this.remove(peer));
158
+ ws.on('message', raw => {
159
+ try {
160
+ this.receive(peer, parse(raw, this.limits));
161
+ }
162
+ catch {
163
+ ws.close(1008, 'Invalid relay request');
164
+ }
165
+ });
166
+ return true;
167
+ }
168
+ withdraw(serviceId, peer) {
169
+ if (this.routes.get(serviceId)?.peer === peer)
170
+ this.routes.delete(serviceId);
171
+ for (const stream of [...peer.streams.values()])
172
+ if (stream.serviceId === serviceId && stream.provider === peer)
173
+ this.closeStream(stream, 'Route unavailable');
174
+ }
175
+ remove(peer) {
176
+ this.peers.delete(peer);
177
+ for (const [id, route] of this.routes)
178
+ if (route.peer === peer)
179
+ this.withdraw(id, peer);
180
+ for (const stream of [...peer.streams.values()])
181
+ this.closeStream(stream, 'Relay disconnected');
182
+ }
183
+ closeStream(s, reason) {
184
+ s.owner.streams.delete(s.ownerId);
185
+ s.provider.streams.delete(s.providerId);
186
+ send(s.owner.ws, { type: 'close', streamId: s.ownerId, reason }, this.limits);
187
+ send(s.provider.ws, { type: 'close', streamId: s.providerId, reason }, this.limits);
188
+ }
189
+ receive(peer, f) {
190
+ if (f.type === 'register') {
191
+ if (!f.serviceIds.every(id => this.canRegister(peer, id)))
192
+ throw new Error('Registration denied');
193
+ for (const id of f.serviceIds) {
194
+ const old = this.routes.get(id);
195
+ if (old && old.peer !== peer && old.expires > Date.now())
196
+ throw new Error('Route already registered');
197
+ }
198
+ for (const [id, r] of this.routes)
199
+ if (r.peer === peer && !f.serviceIds.includes(id))
200
+ this.withdraw(id, peer);
201
+ for (const id of f.serviceIds)
202
+ this.routes.set(id, { peer, expires: Date.now() + Math.min(f.ttlMs, this.limits.maxRouteTtlMs) });
203
+ return;
204
+ }
205
+ if (f.type === 'open') {
206
+ const route = this.routes.get(f.serviceId);
207
+ if (peer.streams.has(f.streamId))
208
+ throw new Error('Duplicate stream');
209
+ if (!this.canRoute(peer, f.serviceId) || !route || route.expires <= Date.now() || route.peer === peer) {
210
+ send(peer.ws, { type: 'close', streamId: f.streamId, reason: 'Route unavailable or denied' }, this.limits);
211
+ return;
212
+ }
213
+ if (peer.streams.size >= this.limits.maxStreams || route.peer.streams.size >= this.limits.maxStreams)
214
+ throw new Error('Stream budget exceeded');
215
+ const s = { owner: peer, ownerId: f.streamId, provider: route.peer, providerId: randomUUID(), serviceId: f.serviceId };
216
+ peer.streams.set(s.ownerId, s);
217
+ route.peer.streams.set(s.providerId, s);
218
+ if (!send(route.peer.ws, { ...f, streamId: s.providerId }, this.limits))
219
+ this.closeStream(s, 'Relay disconnected');
220
+ return;
221
+ }
222
+ const s = peer.streams.get(f.streamId);
223
+ if (!s)
224
+ return;
225
+ if (!this.canRoute(s.owner, s.serviceId) || !this.canRegister(s.provider, s.serviceId)) {
226
+ this.closeStream(s, 'Authorization revoked');
227
+ return;
228
+ }
229
+ if (f.type === 'close') {
230
+ this.closeStream(s, 'Channel closed');
231
+ return;
232
+ }
233
+ if (f.type === 'opened' && peer !== s.provider)
234
+ throw new Error('Invalid channel direction');
235
+ const target = peer === s.owner ? s.provider : s.owner;
236
+ if (!send(target.ws, { ...f, streamId: peer === s.owner ? s.providerId : s.ownerId }, this.limits))
237
+ this.closeStream(s, 'Relay congestion');
238
+ }
239
+ close() { this.stopped = true; clearInterval(this.sweep); for (const p of [...this.peers]) {
240
+ this.remove(p);
241
+ p.ws.close(1001, 'Router stopped');
242
+ } }
243
+ }
244
+ /** Shared Mac/CLI middle hop. Target factories resolve only after their sockets are open. */
245
+ export class RelayClient {
246
+ upstream;
247
+ options;
248
+ limits;
249
+ streams = new Map();
250
+ renewal;
251
+ stopped = false;
252
+ constructor(upstream, options) {
253
+ this.upstream = upstream;
254
+ this.options = options;
255
+ this.limits = { ...defaults, ...options };
256
+ const ttl = Math.min(options.routeTtlMs ?? 60_000, this.limits.maxRouteTtlMs);
257
+ const register = () => send(upstream, { type: 'register', serviceIds: [...options.targets.keys()], ttlMs: ttl }, this.limits);
258
+ register();
259
+ this.renewal = setInterval(register, Math.max(1, Math.floor(ttl / 2)));
260
+ this.renewal.unref?.();
261
+ watch(upstream, this.limits, () => this.close());
262
+ upstream.on('message', raw => { void this.receive(raw).catch(() => upstream.close(1008, 'Invalid relay frame')); });
263
+ }
264
+ drop(id, reason) {
265
+ const target = this.streams.get(id);
266
+ this.streams.delete(id);
267
+ target?.close();
268
+ send(this.upstream, { type: 'close', streamId: id, reason }, this.limits);
269
+ }
270
+ async receive(raw) {
271
+ const f = parse(raw, this.limits);
272
+ if (f.type === 'register')
273
+ throw new Error('Unexpected registration');
274
+ if (f.type === 'open') {
275
+ const connect = this.options.targets.get(f.serviceId);
276
+ if (!connect || this.streams.has(f.streamId) || this.streams.size >= this.limits.maxStreams) {
277
+ this.drop(f.streamId, 'Target unavailable');
278
+ return;
279
+ }
280
+ this.streams.set(f.streamId, null);
281
+ let timedOut = false;
282
+ const timer = setTimeout(() => { timedOut = true; this.drop(f.streamId, 'Target timeout'); }, this.limits.connectTimeoutMs);
283
+ timer.unref?.();
284
+ let target;
285
+ try {
286
+ target = await connect();
287
+ }
288
+ catch {
289
+ clearTimeout(timer);
290
+ this.drop(f.streamId, 'Target unavailable');
291
+ return;
292
+ }
293
+ clearTimeout(timer);
294
+ if (timedOut || this.stopped || !this.streams.has(f.streamId)) {
295
+ target.close();
296
+ return;
297
+ }
298
+ this.streams.set(f.streamId, target);
299
+ watch(target, this.limits, () => { if (this.streams.get(f.streamId) === target)
300
+ this.drop(f.streamId, 'Target disconnected'); });
301
+ target.on('message', (data, isBinary) => {
302
+ if (Buffer.byteLength(data instanceof Array ? Buffer.concat(data) : data) > this.limits.maxFrameBytes) {
303
+ this.drop(f.streamId, 'Unsupported target frame');
304
+ return;
305
+ }
306
+ const bytes = data instanceof Array ? Buffer.concat(data) : Buffer.from(data);
307
+ const payload = isBinary ? bytes.toString('base64') : bytes.toString();
308
+ if (!send(this.upstream, { type: 'data', streamId: f.streamId, payload, ...(isBinary ? { encoding: 'base64' } : {}) }, this.limits))
309
+ this.drop(f.streamId, 'Relay congestion');
310
+ });
311
+ send(this.upstream, { type: 'opened', streamId: f.streamId }, this.limits);
312
+ return;
313
+ }
314
+ if (f.type === 'close') {
315
+ this.drop(f.streamId, 'Channel closed');
316
+ return;
317
+ }
318
+ if (f.type !== 'data')
319
+ throw new Error('Unexpected frame');
320
+ const target = this.streams.get(f.streamId);
321
+ if (!target || target.readyState !== 1) {
322
+ this.drop(f.streamId, 'Target not ready');
323
+ return;
324
+ }
325
+ if (target.bufferedAmount + Buffer.byteLength(f.payload) > this.limits.maxBufferedBytes) {
326
+ this.drop(f.streamId, 'Target congestion');
327
+ return;
328
+ }
329
+ if (f.encoding === 'base64') {
330
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(f.payload))
331
+ throw new Error('Invalid ciphertext encoding');
332
+ target.send(Buffer.from(f.payload, 'base64'));
333
+ }
334
+ else
335
+ target.send(f.payload);
336
+ }
337
+ close() {
338
+ if (this.stopped)
339
+ return;
340
+ this.stopped = true;
341
+ clearInterval(this.renewal);
342
+ for (const id of [...this.streams.keys()])
343
+ this.drop(id, 'Relay stopped');
344
+ this.upstream.close(1001, 'Relay stopped');
345
+ }
346
+ }
@@ -0,0 +1,4 @@
1
+ // Object identity is deliberately used instead of a remotely forgeable header.
2
+ const verified = new WeakSet();
3
+ export function markEncryptedRequest(request) { verified.add(request); }
4
+ export function isEncryptedRequest(request) { return verified.has(request); }
@@ -0,0 +1,124 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
4
+ const validId = (x) => typeof x === 'string' && /^[a-zA-Z0-9_.:-]{1,160}$/.test(x);
5
+ const digest = (s) => createHash('sha256').update(s).digest();
6
+ /** Target directory is re-read on every permission check; invalid config denies all. */
7
+ export class RouteAccess {
8
+ filePath;
9
+ now;
10
+ canRouteSubject;
11
+ tickets = new Map();
12
+ constructor(filePath, now = Date.now, canRouteSubject = () => false) {
13
+ this.filePath = filePath;
14
+ this.now = now;
15
+ this.canRouteSubject = canRouteSubject;
16
+ }
17
+ registry() {
18
+ const empty = { relays: [], directTargets: [] };
19
+ try {
20
+ const data = JSON.parse(readFileSync(this.filePath, 'utf8'));
21
+ if (!data || typeof data !== 'object' || Array.isArray(data) || Object.keys(data).some(k => !['relays', 'directTargets'].includes(k))
22
+ || (data.relays !== undefined && !Array.isArray(data.relays)) || (data.directTargets !== undefined && !Array.isArray(data.directTargets)))
23
+ return empty;
24
+ const relays = data.relays ?? [], directTargets = data.directTargets ?? [];
25
+ if (relays.length > 64 || directTargets.length > 64)
26
+ return empty;
27
+ const ids = new Set();
28
+ for (const r of relays) {
29
+ if (!r || Object.keys(r).some(k => !['id', 'token', 'targets'].includes(k)) || !validId(r.id) || ids.has(r.id) || typeof r.token !== 'string' || r.token.length < 32 || /[\r\n]/.test(r.token) || !Array.isArray(r.targets) || r.targets.length > 64 || !r.targets.every(validId))
30
+ return empty;
31
+ ids.add(r.id);
32
+ }
33
+ const targetIds = new Set();
34
+ for (const target of directTargets) {
35
+ if (!target || typeof target !== 'object' || Array.isArray(target) || Object.keys(target).some(key => !['serviceId', 'url', 'caPath'].includes(key))
36
+ || !validId(target.serviceId) || targetIds.has(target.serviceId) || typeof target.url !== 'string' || (target.caPath !== undefined && typeof target.caPath !== 'string'))
37
+ return empty;
38
+ const url = new URL(target.url);
39
+ if (!['https:', 'wss:'].includes(url.protocol) || url.username || url.password || url.search || url.hash || !['/', '/api/federation/secure'].includes(url.pathname))
40
+ return empty;
41
+ targetIds.add(target.serviceId);
42
+ }
43
+ return { relays, directTargets };
44
+ }
45
+ catch {
46
+ return empty;
47
+ }
48
+ }
49
+ relays() { return this.registry().relays; }
50
+ issueRouteTicket(subjectId, serviceId) {
51
+ if (!validId(subjectId) || !validId(serviceId) || !this.canRouteSubject(subjectId, serviceId) || !this.hasConfiguredTarget(serviceId))
52
+ throw new Error('ROUTE_UNAVAILABLE');
53
+ for (const [key, value] of this.tickets)
54
+ if (value.expiresAt <= this.now())
55
+ this.tickets.delete(key);
56
+ if (this.tickets.size >= 1024)
57
+ throw new Error('ROUTE_TICKET_LIMIT');
58
+ const routeToken = randomBytes(32).toString('base64url');
59
+ const expiresAt = this.now() + 30_000;
60
+ this.tickets.set(digest(routeToken).toString('hex'), { subjectId, serviceId, expiresAt });
61
+ return { routeToken, expiresAt };
62
+ }
63
+ /** Called once before upgrading. A route ticket is consumed even if expired. */
64
+ authenticate(authorization, routeToken) {
65
+ if (authorization && routeToken)
66
+ return null;
67
+ if (authorization) {
68
+ if (!authorization.startsWith('Bearer ') || authorization.length > 4096)
69
+ return null;
70
+ const tokenHash = digest(authorization.slice(7));
71
+ const relay = this.relays().find(r => timingSafeEqual(digest(r.token), tokenHash));
72
+ return relay ? { kind: 'relay', id: relay.id, tokenHash: tokenHash.toString('hex') } : null;
73
+ }
74
+ if (!routeToken || routeToken.length > 512)
75
+ return null;
76
+ const key = digest(routeToken).toString('hex');
77
+ const ticket = this.tickets.get(key);
78
+ this.tickets.delete(key);
79
+ if (!ticket || ticket.expiresAt <= this.now() || !this.canRouteSubject(ticket.subjectId, ticket.serviceId) || !this.hasConfiguredTarget(ticket.serviceId))
80
+ return null;
81
+ return { kind: 'client', subjectId: ticket.subjectId, serviceId: ticket.serviceId };
82
+ }
83
+ allowRegister(principal, serviceId) {
84
+ return principal.kind === 'relay' && this.relays().some(r => r.id === principal.id && digest(r.token).toString('hex') === principal.tokenHash && r.targets.includes(serviceId));
85
+ }
86
+ allowRoute(principal, serviceId) {
87
+ return principal.kind === 'client' && principal.serviceId === serviceId && this.canRouteSubject(principal.subjectId, serviceId) && this.hasConfiguredTarget(serviceId);
88
+ }
89
+ hasConfiguredTarget(serviceId) {
90
+ if (!validId(serviceId))
91
+ return false;
92
+ const registry = this.registry();
93
+ return registry.relays.some(relay => relay.targets.includes(serviceId)) || registry.directTargets.some(target => target.serviceId === serviceId);
94
+ }
95
+ addDirectTarget(target) {
96
+ const url = new URL(target.url);
97
+ if (!validId(target.serviceId) || url.protocol !== 'https:' || url.username || url.password || url.search || url.hash || url.pathname !== '/')
98
+ throw new Error('INVALID_DIRECT_TARGET');
99
+ let data;
100
+ try {
101
+ data = JSON.parse(readFileSync(this.filePath, 'utf8'));
102
+ }
103
+ catch (error) {
104
+ if (error.code !== 'ENOENT')
105
+ throw error;
106
+ data = {};
107
+ }
108
+ const registry = this.registry();
109
+ if (!data || typeof data !== 'object' || Array.isArray(data) || Object.keys(data).some(key => !['relays', 'directTargets'].includes(key))
110
+ || (data.relays !== undefined && (!Array.isArray(data.relays) || data.relays.length !== registry.relays.length))
111
+ || (data.directTargets !== undefined && (!Array.isArray(data.directTargets) || data.directTargets.length !== registry.directTargets.length)))
112
+ throw new Error('INVALID_ROUTE_REGISTRY');
113
+ if (this.hasConfiguredTarget(target.serviceId))
114
+ return;
115
+ if (registry.directTargets.length >= 64)
116
+ throw new Error('ROUTE_TARGET_LIMIT');
117
+ data.directTargets = [...registry.directTargets, { serviceId: target.serviceId, url: url.origin }];
118
+ mkdirSync(dirname(this.filePath), { recursive: true, mode: 0o700 });
119
+ const temporary = `${this.filePath}.tmp`;
120
+ writeFileSync(temporary, JSON.stringify(data, null, 2), { mode: 0o600 });
121
+ renameSync(temporary, this.filePath);
122
+ }
123
+ configuredDirectTargets() { return this.registry().directTargets; }
124
+ }