@rynx-ai/server 0.1.11-beta.4 → 0.1.11-beta.41

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 (497) hide show
  1. package/dist/control-api.d.ts +17 -4
  2. package/dist/control-api.js +722 -84
  3. package/dist/control-web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  4. package/dist/control-web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  5. package/dist/control-web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  6. package/dist/control-web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  7. package/dist/control-web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  8. package/dist/control-web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  9. package/dist/control-web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  10. package/dist/control-web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  11. package/dist/control-web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  12. package/dist/control-web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  13. package/dist/control-web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  14. package/dist/control-web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  15. package/dist/control-web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  16. package/dist/control-web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  17. package/dist/control-web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  18. package/dist/control-web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  19. package/dist/control-web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  20. package/dist/control-web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  21. package/dist/control-web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  22. package/dist/control-web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  23. package/dist/control-web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  24. package/dist/control-web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  25. package/dist/control-web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  26. package/dist/control-web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  27. package/dist/control-web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  28. package/dist/control-web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  29. package/dist/control-web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  30. package/dist/control-web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  31. package/dist/control-web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  32. package/dist/control-web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  33. package/dist/control-web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  34. package/dist/control-web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  35. package/dist/control-web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  36. package/dist/control-web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  37. package/dist/control-web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  38. package/dist/control-web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  39. package/dist/control-web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  40. package/dist/control-web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  41. package/dist/control-web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  42. package/dist/control-web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  43. package/dist/control-web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  44. package/dist/control-web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  45. package/dist/control-web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  46. package/dist/control-web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  47. package/dist/control-web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  48. package/dist/control-web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  49. package/dist/control-web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  50. package/dist/control-web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  51. package/dist/control-web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  52. package/dist/control-web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  53. package/dist/control-web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  54. package/dist/control-web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  55. package/dist/control-web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  56. package/dist/control-web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  57. package/dist/control-web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  58. package/dist/control-web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  59. package/dist/control-web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  60. package/dist/control-web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  61. package/dist/control-web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  62. package/dist/control-web/assets/abap-CLvhMVsD.js +1 -0
  63. package/dist/control-web/assets/abnfDiagram-VRR7QNED-D_NL89dn.js +1 -0
  64. package/dist/control-web/assets/actionscript-3--17pq3dv.js +1 -0
  65. package/dist/control-web/assets/ada-C5qYipkI.js +1 -0
  66. package/dist/control-web/assets/andromeeda-vGVdxbeo.js +1 -0
  67. package/dist/control-web/assets/angular-html-DlZBZkSp.js +1 -0
  68. package/dist/control-web/assets/angular-ts-BVPGOT0K.js +1 -0
  69. package/dist/control-web/assets/apache-U0d_L8uA.js +1 -0
  70. package/dist/control-web/assets/apex-CGTLDQj6.js +1 -0
  71. package/dist/control-web/assets/apl-cDmXqmZ-.js +1 -0
  72. package/dist/control-web/assets/applescript-CCn79oCD.js +1 -0
  73. package/dist/control-web/assets/ara-4CJ0cIlV.js +1 -0
  74. package/dist/control-web/assets/arc-Pry-7G1_.js +1 -0
  75. package/dist/control-web/assets/architecture-TIHT7OUA-C9wU1KtW.js +1 -0
  76. package/dist/control-web/assets/architectureDiagram-ZJ3FMSHR-CV0rxVH6.js +36 -0
  77. package/dist/control-web/assets/array-BifhSqXX.js +1 -0
  78. package/dist/control-web/assets/asciidoc-DE70LPWp.js +1 -0
  79. package/dist/control-web/assets/asm-Cmm7eHzH.js +1 -0
  80. package/dist/control-web/assets/astro-B1Q6BwBx.js +1 -0
  81. package/dist/control-web/assets/aurora-x-CDeNXAV0.js +1 -0
  82. package/dist/control-web/assets/awk-BWXHIvNe.js +1 -0
  83. package/dist/control-web/assets/ayu-dark-DluEY0Gj.js +1 -0
  84. package/dist/control-web/assets/ayu-light-C3h-C4tm.js +1 -0
  85. package/dist/control-web/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  86. package/dist/control-web/assets/ballerina-B7ZEbQpA.js +1 -0
  87. package/dist/control-web/assets/bat-Bo4NYOV-.js +1 -0
  88. package/dist/control-web/assets/beancount-D-usSTwE.js +1 -0
  89. package/dist/control-web/assets/berry-DKpUyyne.js +1 -0
  90. package/dist/control-web/assets/bibtex-Ci_nEsc7.js +1 -0
  91. package/dist/control-web/assets/bicep-CUHmPFLl.js +1 -0
  92. package/dist/control-web/assets/bird2-C2hNVINV.js +1 -0
  93. package/dist/control-web/assets/blade-Xpu-LGTm.js +1 -0
  94. package/dist/control-web/assets/blockDiagram-677ZJIJ3-Cc7nrkZh.js +132 -0
  95. package/dist/control-web/assets/bsl-BkkzgIyY.js +1 -0
  96. package/dist/control-web/assets/c-iCihek07.js +1 -0
  97. package/dist/control-web/assets/c3-CnJL0r0V.js +1 -0
  98. package/dist/control-web/assets/c4Diagram-LMCZKHZV-BPRU9m-F.js +10 -0
  99. package/dist/control-web/assets/cadence-CQ2zXKGN.js +1 -0
  100. package/dist/control-web/assets/cairo-DLTphjLi.js +1 -0
  101. package/dist/control-web/assets/catppuccin-frappe-3VR1Za6u.js +1 -0
  102. package/dist/control-web/assets/catppuccin-latte-DwIHMF0Q.js +1 -0
  103. package/dist/control-web/assets/catppuccin-macchiato-DYnBP6_5.js +1 -0
  104. package/dist/control-web/assets/catppuccin-mocha-DYhrFGRu.js +1 -0
  105. package/dist/control-web/assets/channel-BFm2_j8K.js +1 -0
  106. package/dist/control-web/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  107. package/dist/control-web/assets/chunk-32BRIVSS-Bbq3O54m.js +1 -0
  108. package/dist/control-web/assets/chunk-52WLFC77-sD4al-Ob.js +10 -0
  109. package/dist/control-web/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  110. package/dist/control-web/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  111. package/dist/control-web/assets/chunk-C7G6YPKG-DqHaDS1O.js +1 -0
  112. package/dist/control-web/assets/chunk-EX3LRPZG-36GNQ5wp.js +231 -0
  113. package/dist/control-web/assets/chunk-HOUHSVGY-DeZ63q1j.js +1 -0
  114. package/dist/control-web/assets/chunk-ICXQ74PX-D2yF6lD_.js +2 -0
  115. package/dist/control-web/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  116. package/dist/control-web/assets/chunk-KEIR6QF5-BICK3FdT.js +161 -0
  117. package/dist/control-web/assets/chunk-MOJQB5TN-CdaKyBz5.js +88 -0
  118. package/dist/control-web/assets/chunk-OGEWGWER-eOXC7kiH.js +1 -0
  119. package/dist/control-web/assets/chunk-PUDLZKDR-6w0LILL_.js +156 -0
  120. package/dist/control-web/assets/chunk-Q4XR5HBZ-CXgD5TGL.js +70 -0
  121. package/dist/control-web/assets/chunk-RYQCIY6F-BsoV8yK2.js +1 -0
  122. package/dist/control-web/assets/chunk-V7JOEXUC-BfoZ78b6.js +206 -0
  123. package/dist/control-web/assets/chunk-VR4S4FIN-BW2gCEMQ.js +1 -0
  124. package/dist/control-web/assets/chunk-WYO6CB5R-CHn7hulw.js +128 -0
  125. package/dist/control-web/assets/chunk-XXDRQBXY-DqAYeljN.js +1 -0
  126. package/dist/control-web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  127. package/dist/control-web/assets/chunk-ZGVPDNZ5-CmFP0-fr.js +62 -0
  128. package/dist/control-web/assets/clarity-SemFz856.js +1 -0
  129. package/dist/control-web/assets/classDiagram-OUVF2IWQ-CUe_bETB.js +1 -0
  130. package/dist/control-web/assets/classDiagram-v2-EOCWNBFH-CUe_bETB.js +1 -0
  131. package/dist/control-web/assets/clojure-DqKBuwfJ.js +1 -0
  132. package/dist/control-web/assets/cmake-Bj61d0ZC.js +1 -0
  133. package/dist/control-web/assets/cobol-BsV-839J.js +1 -0
  134. package/dist/control-web/assets/codeowners-C8r90Shi.js +1 -0
  135. package/dist/control-web/assets/codeql-oeQT6MSM.js +1 -0
  136. package/dist/control-web/assets/coffee-CiiSLhB6.js +1 -0
  137. package/dist/control-web/assets/common-lisp-Cv5bFMCO.js +1 -0
  138. package/dist/control-web/assets/coq-BrsZFFmf.js +1 -0
  139. package/dist/control-web/assets/cose-bilkent-JH36ORCC-lJEhNiw_.js +1 -0
  140. package/dist/control-web/assets/cpp-DjoE5qBx.js +1 -0
  141. package/dist/control-web/assets/crystal-DDxHDkHz.js +1 -0
  142. package/dist/control-web/assets/csharp-Ct8U2NOr.js +1 -0
  143. package/dist/control-web/assets/css-LjpOdHib.js +1 -0
  144. package/dist/control-web/assets/csv-Dx-8-gkx.js +1 -0
  145. package/dist/control-web/assets/cue-CE9AQfxI.js +1 -0
  146. package/dist/control-web/assets/cynefin-VYW2F7L2-NaLr7Qlq.js +1 -0
  147. package/dist/control-web/assets/cynefinDiagram-TSTJHNR4-C90wpjAI.js +62 -0
  148. package/dist/control-web/assets/cypher-ClKdZ_lG.js +1 -0
  149. package/dist/control-web/assets/cytoscape.esm-B-NFISlW.js +321 -0
  150. package/dist/control-web/assets/d-qD-0Kul2.js +1 -0
  151. package/dist/control-web/assets/dagre-Buvkdvvj.js +1 -0
  152. package/dist/control-web/assets/dagre-VKFMJZFB-BxrPFWTb.js +4 -0
  153. package/dist/control-web/assets/dark-plus-Cs2F2srj.js +1 -0
  154. package/dist/control-web/assets/dart-DkHntEIa.js +1 -0
  155. package/dist/control-web/assets/dax-BkyTk9wS.js +1 -0
  156. package/dist/control-web/assets/defaultLocale-BFoDCU3G.js +1 -0
  157. package/dist/control-web/assets/desktop-Dlh5hvp9.js +1 -0
  158. package/dist/control-web/assets/diagram-FQU43EPY-VIQINsvz.js +3 -0
  159. package/dist/control-web/assets/diagram-G47NLZAW-DFeAfYXn.js +24 -0
  160. package/dist/control-web/assets/diagram-NH7WQ7WH-ATzdppDd.js +24 -0
  161. package/dist/control-web/assets/diagram-OA4YK3LP-DJZpWmbl.js +30 -0
  162. package/dist/control-web/assets/diagram-WEI45ONY-CqDHcW3g.js +41 -0
  163. package/dist/control-web/assets/diff-woXpYk--.js +1 -0
  164. package/dist/control-web/assets/dist-Dx9YWgcW.js +153 -0
  165. package/dist/control-web/assets/dist-v5Q1xZ2K.js +1 -0
  166. package/dist/control-web/assets/docker-IyjqRm3v.js +1 -0
  167. package/dist/control-web/assets/dotenv-_5a1GRtc.js +1 -0
  168. package/dist/control-web/assets/dracula-BHWKrbxM.js +1 -0
  169. package/dist/control-web/assets/dracula-soft-5eyTD99u.js +1 -0
  170. package/dist/control-web/assets/dream-maker-DW3nJb8Q.js +1 -0
  171. package/dist/control-web/assets/ebnfDiagram-CCIWWBDH-DnP6dHND.js +1 -0
  172. package/dist/control-web/assets/edge-8z7Jp9dO.js +1 -0
  173. package/dist/control-web/assets/elixir-CUV54ev5.js +1 -0
  174. package/dist/control-web/assets/elm-o4NJxfws.js +1 -0
  175. package/dist/control-web/assets/emacs-lisp-C9PiwqqW.js +1 -0
  176. package/dist/control-web/assets/erDiagram-Q63AITRT-Bo8bFqrZ.js +85 -0
  177. package/dist/control-web/assets/erb-CIsHcwm1.js +1 -0
  178. package/dist/control-web/assets/erlang-Cphh6RMH.js +1 -0
  179. package/dist/control-web/assets/eventmodeling-45OFAUF4-DTN8wqsu.js +1 -0
  180. package/dist/control-web/assets/everforest-dark-sB-x3p7T.js +1 -0
  181. package/dist/control-web/assets/everforest-light-Df2xbC6M.js +1 -0
  182. package/dist/control-web/assets/fennel-DQxkIbk2.js +1 -0
  183. package/dist/control-web/assets/fish-BJitypiv.js +1 -0
  184. package/dist/control-web/assets/flowDiagram-23GEKE2U-BwjqRyvd.js +1 -0
  185. package/dist/control-web/assets/fluent-C03EYrpw.js +1 -0
  186. package/dist/control-web/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
  187. package/dist/control-web/assets/fortran-free-form-CYNrtFtB.js +1 -0
  188. package/dist/control-web/assets/fsharp-D13ZGOAj.js +1 -0
  189. package/dist/control-web/assets/ganttDiagram-NO4QXBWP-kxQaBTzd.js +292 -0
  190. package/dist/control-web/assets/gdresource-C0sCabJj.js +1 -0
  191. package/dist/control-web/assets/gdscript-Cp2uCuqX.js +1 -0
  192. package/dist/control-web/assets/gdshader-CBce3t8t.js +1 -0
  193. package/dist/control-web/assets/genie-CV2tkWYe.js +1 -0
  194. package/dist/control-web/assets/gherkin-DExj1W_8.js +1 -0
  195. package/dist/control-web/assets/git-commit-BSykSTBG.js +1 -0
  196. package/dist/control-web/assets/git-rebase-BDPpS8w6.js +1 -0
  197. package/dist/control-web/assets/gitGraph-TEB2WS4Q-a646NnuJ.js +1 -0
  198. package/dist/control-web/assets/gitGraphDiagram-IHSO6WYX-BBe0q0At.js +106 -0
  199. package/dist/control-web/assets/github-dark-C-LZuMrd.js +1 -0
  200. package/dist/control-web/assets/github-dark-default-DXG-b-1a.js +1 -0
  201. package/dist/control-web/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  202. package/dist/control-web/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  203. package/dist/control-web/assets/github-light-EUqPIrTm.js +1 -0
  204. package/dist/control-web/assets/github-light-default-BXViO-2h.js +1 -0
  205. package/dist/control-web/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  206. package/dist/control-web/assets/gleam-CSRkHgEL.js +1 -0
  207. package/dist/control-web/assets/glimmer-js-OEO36pDk.js +1 -0
  208. package/dist/control-web/assets/glimmer-ts-BnAPyISE.js +1 -0
  209. package/dist/control-web/assets/glsl-D5MA2a_X.js +1 -0
  210. package/dist/control-web/assets/gn-ilITqXS6.js +1 -0
  211. package/dist/control-web/assets/gnuplot-7GGW24-e.js +1 -0
  212. package/dist/control-web/assets/go-BJwz_mda.js +1 -0
  213. package/dist/control-web/assets/graphlib-DS17s2tU.js +1 -0
  214. package/dist/control-web/assets/graphql-Ctjuxyvl.js +1 -0
  215. package/dist/control-web/assets/groovy-CacY0gHj.js +1 -0
  216. package/dist/control-web/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  217. package/dist/control-web/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  218. package/dist/control-web/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  219. package/dist/control-web/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  220. package/dist/control-web/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  221. package/dist/control-web/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  222. package/dist/control-web/assets/hack-D_mkkoLd.js +1 -0
  223. package/dist/control-web/assets/haml-mpgecWZ6.js +1 -0
  224. package/dist/control-web/assets/handlebars-CAx_777f.js +1 -0
  225. package/dist/control-web/assets/haskell-D8IpX4py.js +1 -0
  226. package/dist/control-web/assets/haxe-OTjmBuCE.js +1 -0
  227. package/dist/control-web/assets/hcl-Dh228itO.js +1 -0
  228. package/dist/control-web/assets/highlighted-body-OFNGDK62-DVCHy9oI.js +1 -0
  229. package/dist/control-web/assets/hjson-CxZEssPk.js +1 -0
  230. package/dist/control-web/assets/hlsl-Cvrh5tZx.js +1 -0
  231. package/dist/control-web/assets/horizon-CE9ld1lL.js +1 -0
  232. package/dist/control-web/assets/horizon-bright-Br1oVSNq.js +1 -0
  233. package/dist/control-web/assets/houston-CsvMBhTu.js +1 -0
  234. package/dist/control-web/assets/html-B2lrdnWr.js +1 -0
  235. package/dist/control-web/assets/html-derivative-CDfZYcul.js +1 -0
  236. package/dist/control-web/assets/http-kmH8AyfE.js +1 -0
  237. package/dist/control-web/assets/hurl-DCZHXKU4.js +1 -0
  238. package/dist/control-web/assets/hxml-B0Qn7Nwc.js +1 -0
  239. package/dist/control-web/assets/hy-CZbG8q4J.js +1 -0
  240. package/dist/control-web/assets/imba-DsUTQ-LC.js +1 -0
  241. package/dist/control-web/assets/index-D7URT6K_.js +140 -0
  242. package/dist/control-web/assets/index-uEpvRMX9.css +1 -0
  243. package/dist/control-web/assets/info-DKCQHKI2-Cp_JrY-b.js +1 -0
  244. package/dist/control-web/assets/infoDiagram-FWYZ7A6U-CAm-h4F_.js +2 -0
  245. package/dist/control-web/assets/ini-B5eOa1yu.js +1 -0
  246. package/dist/control-web/assets/init-C-OQMol4.js +1 -0
  247. package/dist/control-web/assets/ishikawaDiagram-FXEZZL3T-O17pqcWr.js +70 -0
  248. package/dist/control-web/assets/java-DdHNQ9hk.js +1 -0
  249. package/dist/control-web/assets/javascript-obGGvMPs.js +1 -0
  250. package/dist/control-web/assets/jinja-BMiPVedm.js +1 -0
  251. package/dist/control-web/assets/jison-CBjhYtSs.js +1 -0
  252. package/dist/control-web/assets/journeyDiagram-5HDEW3XC-DQ-JuSoR.js +139 -0
  253. package/dist/control-web/assets/json-DAgxKhMr.js +1 -0
  254. package/dist/control-web/assets/json5-BR5RXkoi.js +1 -0
  255. package/dist/control-web/assets/jsonc-CYpm1nAK.js +1 -0
  256. package/dist/control-web/assets/jsonl-CmCQp5Yx.js +1 -0
  257. package/dist/control-web/assets/jsonnet-CJTPZ8u_.js +1 -0
  258. package/dist/control-web/assets/jssm-DXw9l8Rf.js +1 -0
  259. package/dist/control-web/assets/jsx-gTgmArGx.js +1 -0
  260. package/dist/control-web/assets/julia-CjHxn3vf.js +1 -0
  261. package/dist/control-web/assets/just-DMArCiVH.js +1 -0
  262. package/dist/control-web/assets/kanagawa-dragon-CXtmUGW6.js +1 -0
  263. package/dist/control-web/assets/kanagawa-lotus-BN08jTvb.js +1 -0
  264. package/dist/control-web/assets/kanagawa-wave-CTweb8Dz.js +1 -0
  265. package/dist/control-web/assets/kanban-definition-HUTT4EX6-Cgv3kPbm.js +89 -0
  266. package/dist/control-web/assets/katex-CyHG4N6b.js +1 -0
  267. package/dist/control-web/assets/kdl-CsD5j6eV.js +1 -0
  268. package/dist/control-web/assets/kotlin-DhhofPvG.js +1 -0
  269. package/dist/control-web/assets/kusto-BUv0MjJC.js +1 -0
  270. package/dist/control-web/assets/laserwave-C_8bwKvT.js +1 -0
  271. package/dist/control-web/assets/latex-D9bvcAJr.js +1 -0
  272. package/dist/control-web/assets/lean-CewbzKMR.js +1 -0
  273. package/dist/control-web/assets/less-DVTAwKKz.js +1 -0
  274. package/dist/control-web/assets/lib-BZ_F5vVH.js +1 -0
  275. package/dist/control-web/assets/light-plus-DVQuIRkW.js +1 -0
  276. package/dist/control-web/assets/line-DUsXzGJZ.js +1 -0
  277. package/dist/control-web/assets/linear-DmucvK78.js +1 -0
  278. package/dist/control-web/assets/liquid-CCX5qouA.js +1 -0
  279. package/dist/control-web/assets/llvm-Cm23YOpf.js +1 -0
  280. package/dist/control-web/assets/log-BNLmms1o.js +1 -0
  281. package/dist/control-web/assets/logo-Cluzi2Zq.js +1 -0
  282. package/dist/control-web/assets/lua-FONpmTUP.js +1 -0
  283. package/dist/control-web/assets/luau-CNKltnaQ.js +1 -0
  284. package/dist/control-web/assets/make-Dixweg8N.js +1 -0
  285. package/dist/control-web/assets/map-BaFkSB1l.js +1 -0
  286. package/dist/control-web/assets/markdown-BYOwaDjH.js +1 -0
  287. package/dist/control-web/assets/markdown-streamdown-4N4AcNp5.js +433 -0
  288. package/dist/control-web/assets/markdown-streamdown-DEcVZfaU.css +1 -0
  289. package/dist/control-web/assets/marko-oPGh3KfH.js +1 -0
  290. package/dist/control-web/assets/material-theme-Bm3Qr25_.js +1 -0
  291. package/dist/control-web/assets/material-theme-darker-2IIEA8gg.js +1 -0
  292. package/dist/control-web/assets/material-theme-lighter-uhdI0v04.js +1 -0
  293. package/dist/control-web/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  294. package/dist/control-web/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  295. package/dist/control-web/assets/matlab-D7qyCx1q.js +1 -0
  296. package/dist/control-web/assets/mdc-B_uQHOlt.js +1 -0
  297. package/dist/control-web/assets/mdx-DQZ5AkYe.js +1 -0
  298. package/dist/control-web/assets/mermaid-Bk4SNUv9.js +1 -0
  299. package/dist/control-web/assets/mermaid-GHXKKRXX-BElZGKGd.js +1 -0
  300. package/dist/control-web/assets/mermaid-parser.core-VMln12Kd.js +7 -0
  301. package/dist/control-web/assets/min-dark-BSWPekZh.js +1 -0
  302. package/dist/control-web/assets/min-light-DDpmG2fV.js +1 -0
  303. package/dist/control-web/assets/mindmap-definition-LN4V7U3C-Bn0lH-UM.js +96 -0
  304. package/dist/control-web/assets/mipsasm-BMqwQI7S.js +1 -0
  305. package/dist/control-web/assets/mojo-BgCJLMeH.js +1 -0
  306. package/dist/control-web/assets/monokai-CdkpiU2Y.js +1 -0
  307. package/dist/control-web/assets/moonbit-CaWjb8XO.js +1 -0
  308. package/dist/control-web/assets/move-B1IS1UjX.js +1 -0
  309. package/dist/control-web/assets/narrat-_X_XdTYD.js +1 -0
  310. package/dist/control-web/assets/nextflow-Bbiyy34d.js +1 -0
  311. package/dist/control-web/assets/nextflow-groovy-Dc_ddanL.js +1 -0
  312. package/dist/control-web/assets/nginx-BTMw9COX.js +1 -0
  313. package/dist/control-web/assets/night-owl-DhmEMT88.js +1 -0
  314. package/dist/control-web/assets/night-owl-light-eJ-hLW7d.js +1 -0
  315. package/dist/control-web/assets/nim-8PQFL6ZB.js +1 -0
  316. package/dist/control-web/assets/nix-IvuFDN5E.js +1 -0
  317. package/dist/control-web/assets/nord-Cb4Vim4T.js +1 -0
  318. package/dist/control-web/assets/nushell-DcLAeLz5.js +1 -0
  319. package/dist/control-web/assets/objective-c-D1A_Heim.js +1 -0
  320. package/dist/control-web/assets/objective-cpp-BsSzOQcm.js +1 -0
  321. package/dist/control-web/assets/ocaml-O90oeIOV.js +1 -0
  322. package/dist/control-web/assets/odin-B1RWQWA5.js +1 -0
  323. package/dist/control-web/assets/one-dark-pro-CLwyXe_n.js +1 -0
  324. package/dist/control-web/assets/one-light-D7Lr4KcI.js +1 -0
  325. package/dist/control-web/assets/openscad-BUDT5pXO.js +1 -0
  326. package/dist/control-web/assets/ordinal-BDEzSJ7C.js +1 -0
  327. package/dist/control-web/assets/packet-7NZHBO7P-Zw8iJ8lo.js +1 -0
  328. package/dist/control-web/assets/pascal-4ZHwLPI5.js +1 -0
  329. package/dist/control-web/assets/path-fybaL0A-.js +1 -0
  330. package/dist/control-web/assets/pegDiagram-2B236MQR-CtF7vVs_.js +1 -0
  331. package/dist/control-web/assets/perl-Vf0i0hAx.js +1 -0
  332. package/dist/control-web/assets/php-am1OsKhm.js +1 -0
  333. package/dist/control-web/assets/pie-RZYD4A2V-su_3QGU2.js +1 -0
  334. package/dist/control-web/assets/pieDiagram-ENE6RG2P-BkMUBLE1.js +39 -0
  335. package/dist/control-web/assets/pkl-ot-7Btpt.js +1 -0
  336. package/dist/control-web/assets/plastic-DQwYfKfQ.js +1 -0
  337. package/dist/control-web/assets/plsql-DGHpHOYJ.js +1 -0
  338. package/dist/control-web/assets/po-BiJDBrnU.js +1 -0
  339. package/dist/control-web/assets/poimandres-DRFjx7u4.js +1 -0
  340. package/dist/control-web/assets/polar-C7UOKdEL.js +1 -0
  341. package/dist/control-web/assets/postcss-BXeXVLqQ.js +1 -0
  342. package/dist/control-web/assets/powerquery-DNMTfnFr.js +1 -0
  343. package/dist/control-web/assets/powershell-DshXNtvi.js +1 -0
  344. package/dist/control-web/assets/prisma-BsRQq5mF.js +1 -0
  345. package/dist/control-web/assets/prolog-iXnhIJG7.js +1 -0
  346. package/dist/control-web/assets/proto-DB4EqR-F.js +1 -0
  347. package/dist/control-web/assets/pug-DO6yvBr3.js +1 -0
  348. package/dist/control-web/assets/puppet-CDv2pdJW.js +1 -0
  349. package/dist/control-web/assets/purescript-9MfHhQsQ.js +1 -0
  350. package/dist/control-web/assets/python-gzcpVVnB.js +1 -0
  351. package/dist/control-web/assets/qml-_a-QhfPD.js +1 -0
  352. package/dist/control-web/assets/qmldir-DCQb3MpD.js +1 -0
  353. package/dist/control-web/assets/qss-Fe1Jh2GI.js +1 -0
  354. package/dist/control-web/assets/quadrantDiagram-ABIIQ3AL-ba-VaqMe.js +7 -0
  355. package/dist/control-web/assets/r-D6UWEOL2.js +1 -0
  356. package/dist/control-web/assets/racket-DcIDlBhZ.js +1 -0
  357. package/dist/control-web/assets/radar-I7S5WNFK-Dat0rqem.js +1 -0
  358. package/dist/control-web/assets/railroad-3IZDKUUU-Dh2-s8r8.js +1 -0
  359. package/dist/control-web/assets/railroad-abnf-AHOZXSZD-CNUwq0JR.js +1 -0
  360. package/dist/control-web/assets/railroad-ebnf-EBAXGLYW-RHr0duea.js +1 -0
  361. package/dist/control-web/assets/railroad-peg-LSFZ7HO6-CVBWS2eU.js +1 -0
  362. package/dist/control-web/assets/railroadDiagram-RFXS5EU6-BiJX3SY-.js +1 -0
  363. package/dist/control-web/assets/raku-B3gFvitq.js +1 -0
  364. package/dist/control-web/assets/razor-CMVBVp3T.js +1 -0
  365. package/dist/control-web/assets/red-CJ3rzSJv.js +1 -0
  366. package/dist/control-web/assets/reg-CRGYupPL.js +1 -0
  367. package/dist/control-web/assets/regexp-DR20uMnK.js +1 -0
  368. package/dist/control-web/assets/rel-BtDbiS_P.js +1 -0
  369. package/dist/control-web/assets/requirementDiagram-TGXJPOKE-CFfakCzs.js +84 -0
  370. package/dist/control-web/assets/riscv-Ckw8ddFX.js +1 -0
  371. package/dist/control-web/assets/rolldown-runtime-hePW80VL.js +1 -0
  372. package/dist/control-web/assets/ron-VUp2lXgN.js +1 -0
  373. package/dist/control-web/assets/rose-pine-BthvhNj6.js +1 -0
  374. package/dist/control-web/assets/rose-pine-dawn-Dg85fqjY.js +1 -0
  375. package/dist/control-web/assets/rose-pine-moon-hon4tzzS.js +1 -0
  376. package/dist/control-web/assets/rosmsg-CAekHB0j.js +1 -0
  377. package/dist/control-web/assets/rough.esm-Dy-Kn_BL.js +1 -0
  378. package/dist/control-web/assets/rst-CgIrbW_q.js +1 -0
  379. package/dist/control-web/assets/ruby-Dp6Zud9H.js +1 -0
  380. package/dist/control-web/assets/rust-Cfkwpbl8.js +1 -0
  381. package/dist/control-web/assets/sankeyDiagram-HTMAVEWB-jgDi0God.js +40 -0
  382. package/dist/control-web/assets/sas-B6p8-ynO.js +1 -0
  383. package/dist/control-web/assets/sass-DXrisJhu.js +1 -0
  384. package/dist/control-web/assets/scala-DKOlJaKm.js +1 -0
  385. package/dist/control-web/assets/scheme-DQCgrYNe.js +1 -0
  386. package/dist/control-web/assets/scss-DcoSS-ti.js +1 -0
  387. package/dist/control-web/assets/sdbl-bTVj8UrX.js +1 -0
  388. package/dist/control-web/assets/sequenceDiagram-DBY2YBRQ-DBfyyb0N.js +162 -0
  389. package/dist/control-web/assets/shaderlab-TOUzSsQk.js +1 -0
  390. package/dist/control-web/assets/shellscript-Da78Ox-3.js +1 -0
  391. package/dist/control-web/assets/shellsession-CVsQGYfZ.js +1 -0
  392. package/dist/control-web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  393. package/dist/control-web/assets/slack-dark-DnToyrRv.js +1 -0
  394. package/dist/control-web/assets/slack-ochin-B2OO5cIa.js +1 -0
  395. package/dist/control-web/assets/smalltalk-B16xEiuN.js +1 -0
  396. package/dist/control-web/assets/snazzy-light-4G7pJPwS.js +1 -0
  397. package/dist/control-web/assets/solarized-dark-DV17i1UV.js +1 -0
  398. package/dist/control-web/assets/solarized-light-DSh2HLQt.js +1 -0
  399. package/dist/control-web/assets/solidity-CKzVLygQ.js +1 -0
  400. package/dist/control-web/assets/soy-B5n_eHSp.js +1 -0
  401. package/dist/control-web/assets/sparql-D_iOobhT.js +1 -0
  402. package/dist/control-web/assets/splunk-BC2Px7Mm.js +1 -0
  403. package/dist/control-web/assets/sql-ClgswhA6.js +1 -0
  404. package/dist/control-web/assets/src-B6xuSHsQ.js +1 -0
  405. package/dist/control-web/assets/ssh-config-BgfXC-Er.js +1 -0
  406. package/dist/control-web/assets/stata-RoaAgbqU.js +1 -0
  407. package/dist/control-web/assets/stateDiagram-2N3HPSRC-Dwb_ncVi.js +1 -0
  408. package/dist/control-web/assets/stateDiagram-v2-6OUMAXLB-DI0opaA1.js +1 -0
  409. package/dist/control-web/assets/stylus-B6D30XZt.js +1 -0
  410. package/dist/control-web/assets/surrealql-DIn4NJsS.js +1 -0
  411. package/dist/control-web/assets/svelte-CIt8Fl9I.js +1 -0
  412. package/dist/control-web/assets/swift-DonLKvLd.js +1 -0
  413. package/dist/control-web/assets/swimlanes-5IMT3BWC-DW0LuiIV.js +2 -0
  414. package/dist/control-web/assets/swimlanesDiagram-G3AALYLV-DDTVjcOR.js +8 -0
  415. package/dist/control-web/assets/synthwave-84-nFMaYfgc.js +1 -0
  416. package/dist/control-web/assets/system-verilog-DJ5XKQeo.js +1 -0
  417. package/dist/control-web/assets/systemd-BxMlprV5.js +1 -0
  418. package/dist/control-web/assets/talonscript-CohzipZa.js +1 -0
  419. package/dist/control-web/assets/tasl-DMoTqEGO.js +1 -0
  420. package/dist/control-web/assets/tcl-CZd0xW_V.js +1 -0
  421. package/dist/control-web/assets/templ-BREsJpNc.js +1 -0
  422. package/dist/control-web/assets/terraform-DswuEJGm.js +1 -0
  423. package/dist/control-web/assets/tex-0w6-Dtgt.js +1 -0
  424. package/dist/control-web/assets/timeline-definition-FHXFAJF6-DeX4B_B8.js +120 -0
  425. package/dist/control-web/assets/tokyo-night-oM2G3aXe.js +1 -0
  426. package/dist/control-web/assets/toml-CcmNWLt0.js +1 -0
  427. package/dist/control-web/assets/treeView-QDETBFTQ-D1UXtICj.js +1 -0
  428. package/dist/control-web/assets/treemap-6X3UGDF4-CrppzjZH.js +1 -0
  429. package/dist/control-web/assets/ts-tags-q3pptdMi.js +1 -0
  430. package/dist/control-web/assets/tsv-sltzmVWM.js +1 -0
  431. package/dist/control-web/assets/tsx-CUOciP0b.js +1 -0
  432. package/dist/control-web/assets/turtle-ByJddavk.js +1 -0
  433. package/dist/control-web/assets/twig-LIeLXgf5.js +1 -0
  434. package/dist/control-web/assets/typescript-BydbNFcO.js +1 -0
  435. package/dist/control-web/assets/typespec-B88KGewJ.js +1 -0
  436. package/dist/control-web/assets/typst-DI99ib-x.js +1 -0
  437. package/dist/control-web/assets/v-DETTlOr0.js +1 -0
  438. package/dist/control-web/assets/vala-zf12oZj6.js +1 -0
  439. package/dist/control-web/assets/vb-Djn5o6TS.js +1 -0
  440. package/dist/control-web/assets/vennDiagram-L72KCM5P-BYpf-7VR.js +34 -0
  441. package/dist/control-web/assets/verilog-CiiDBU1e.js +1 -0
  442. package/dist/control-web/assets/vesper-D5bVUKB1.js +1 -0
  443. package/dist/control-web/assets/vhdl-BroJfC0k.js +1 -0
  444. package/dist/control-web/assets/viml-DvXPmvsu.js +1 -0
  445. package/dist/control-web/assets/vitesse-black-fwtXNY1n.js +1 -0
  446. package/dist/control-web/assets/vitesse-dark-BZCL-v6S.js +1 -0
  447. package/dist/control-web/assets/vitesse-light-VbXTXTou.js +1 -0
  448. package/dist/control-web/assets/vue-DVSfBueU.js +1 -0
  449. package/dist/control-web/assets/vue-html-DkKkQnZL.js +1 -0
  450. package/dist/control-web/assets/vue-vine-pWqEcBkW.js +1 -0
  451. package/dist/control-web/assets/vyper-CgoNMtux.js +1 -0
  452. package/dist/control-web/assets/wardley-OPB4EBWU-B9rA0UsV.js +1 -0
  453. package/dist/control-web/assets/wardleyDiagram-EHGQE667-BdvAUa5F.js +78 -0
  454. package/dist/control-web/assets/wasm-BnjxR4X6.js +1 -0
  455. package/dist/control-web/assets/wasm-ByWQv1Qj.js +1 -0
  456. package/dist/control-web/assets/wenyan-C8pVoKbM.js +1 -0
  457. package/dist/control-web/assets/wgsl-BsKzXJz4.js +1 -0
  458. package/dist/control-web/assets/wikitext-ClFFjSW2.js +1 -0
  459. package/dist/control-web/assets/wit-DdvCle-K.js +1 -0
  460. package/dist/control-web/assets/wolfram-DLL8P-h_.js +1 -0
  461. package/dist/control-web/assets/xml-BlnrEAEy.js +1 -0
  462. package/dist/control-web/assets/xsl-BOPS8gLt.js +1 -0
  463. package/dist/control-web/assets/xychartDiagram-FW5EYKEG-BaEV3pGm.js +7 -0
  464. package/dist/control-web/assets/yaml-Du_6cdrH.js +1 -0
  465. package/dist/control-web/assets/zenscript-BnlCZFoB.js +1 -0
  466. package/dist/control-web/assets/zig-CMLA9XwU.js +1 -0
  467. package/dist/control-web/index.html +3 -2
  468. package/dist/control-web-dist.d.ts +1 -1
  469. package/dist/control-web-dist.js +4 -2
  470. package/dist/desktop-browser-host.d.ts +3 -1
  471. package/dist/desktop-browser-host.js +39 -4
  472. package/dist/direct-runtime-server.js +187 -153
  473. package/dist/machine-session-service.d.ts +105 -15
  474. package/dist/machine-session-service.js +781 -87
  475. package/dist/remote-runtime-dispatcher.d.ts +2 -2
  476. package/dist/remote-runtime-dispatcher.js +42 -2
  477. package/dist/remote-runtime-session-projection.d.ts +1 -0
  478. package/dist/remote-runtime-session-projection.js +12 -0
  479. package/dist/server.d.ts +64 -11
  480. package/dist/server.js +422 -55
  481. package/dist/session-browser-service.d.ts +24 -4
  482. package/dist/session-browser-service.js +127 -1
  483. package/dist/session-pending-inputs.d.ts +42 -0
  484. package/dist/session-pending-inputs.js +87 -0
  485. package/dist/session-portal.d.ts +54 -0
  486. package/dist/session-portal.js +997 -0
  487. package/dist/session-runtime-index.d.ts +3 -6
  488. package/dist/session-runtime-index.js +96 -38
  489. package/dist/session-terminal-host.d.ts +22 -2
  490. package/dist/session-terminal-host.js +66 -83
  491. package/dist/terminal-ws.d.ts +4 -1
  492. package/dist/terminal-ws.js +93 -27
  493. package/package.json +7 -7
  494. package/dist/control-web/assets/highlighted-body-OFNGDK62-DWWsUtHF.js +0 -1
  495. package/dist/control-web/assets/index-BnX7NWJX.js +0 -689
  496. package/dist/control-web/assets/index-CUoWRX2i.css +0 -32
  497. package/dist/control-web/assets/mermaid-GHXKKRXX-C1fxAgiw.js +0 -131
@@ -0,0 +1,997 @@
1
+ import { request as httpRequest, } from "node:http";
2
+ import { pipeline } from "node:stream/promises";
3
+ import Router from "@koa/router";
4
+ import Koa from "koa";
5
+ import { WebSocket, WebSocketServer } from "ws";
6
+ import { sendSpaFile } from "./control-web-dist.js";
7
+ import { isBrowserTerminalResizeFrame } from "./terminal-ws.js";
8
+ export const SESSION_PORTAL_HEADER = "x-rynx-session-portal";
9
+ export const SESSION_PORTAL_QUERY = "portal";
10
+ export const SESSION_PORTAL_SECRET_HEADER = "x-rynx-session-portal-secret";
11
+ export const SESSION_PORTAL_SECRET_QUERY = "portal_secret";
12
+ export const SESSION_PORTAL_EMBED_ORIGIN_QUERY = "embed_origin";
13
+ const MAX_TICKET_BYTES = 256;
14
+ const MAX_FILTERED_RESPONSE_BYTES = 16 * 1024 * 1024;
15
+ const MAX_PENDING_SOCKET_FRAMES = 128;
16
+ const MAX_PENDING_SOCKET_BYTES = 1024 * 1024;
17
+ const MAX_SOCKET_BUFFERED_BYTES = 32 * 1024 * 1024;
18
+ const PORTAL_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
19
+ const RUNTIME_SESSION_PATH = /^\/runtimes\/local\/sessions(?:\/([^/]+))?\/?$/;
20
+ const RUNTIME_SESSION_API_PATH = /^\/api\/runtimes\/local\/sessions(?:\/([^/]+))?(?:\/|$)/;
21
+ const SESSION_COLLECTION_API_PATH = "/api/runtimes/local/sessions";
22
+ const TERMINAL_SOCKET_PATH = /^\/api\/runtimes\/([^/]+)\/sessions\/([^/]+)\/terminal$/;
23
+ const BROWSER_SOCKET_PATH = /^\/api\/runtimes\/([^/]+)\/sessions\/([^/]+)\/browser\/pages\/[^/]+\/surface$/;
24
+ const EMULATOR_SOCKET_PATH = /^\/api\/runtimes\/([^/]+)\/sessions\/([^/]+)\/emulator\/surface$/;
25
+ const HOP_BY_HOP_HEADERS = new Set([
26
+ "connection",
27
+ "keep-alive",
28
+ "proxy-authenticate",
29
+ "proxy-authorization",
30
+ "proxy-connection",
31
+ "te",
32
+ "trailer",
33
+ "transfer-encoding",
34
+ "upgrade",
35
+ ]);
36
+ class SessionPortalTransportRegistry {
37
+ transports = new Map();
38
+ track(portalId, close) {
39
+ let finish;
40
+ const done = new Promise((resolve) => { finish = resolve; });
41
+ const entry = { close, done, finish };
42
+ const entries = this.transports.get(portalId) ?? new Set();
43
+ entries.add(entry);
44
+ this.transports.set(portalId, entries);
45
+ let finished = false;
46
+ return () => {
47
+ if (finished)
48
+ return;
49
+ finished = true;
50
+ entries.delete(entry);
51
+ if (entries.size === 0)
52
+ this.transports.delete(portalId);
53
+ finish();
54
+ };
55
+ }
56
+ async revoke(portalIds) {
57
+ const draining = [];
58
+ for (const portalId of portalIds) {
59
+ const entries = [...(this.transports.get(portalId) ?? [])];
60
+ this.transports.delete(portalId);
61
+ for (const entry of entries) {
62
+ draining.push(entry.done);
63
+ entry.close();
64
+ }
65
+ }
66
+ await Promise.all(draining);
67
+ }
68
+ }
69
+ /** Session Portal is a narrow adapter in front of the one Control service. It
70
+ * serves the native SPA, validates Portal grants, and forwards only authorized
71
+ * Session HTTP calls to Control; it owns no Session or Runtime handlers. */
72
+ export function createSessionPortalApp(options, transports = new SessionPortalTransportRegistry()) {
73
+ const app = new Koa();
74
+ const bootstrapRouter = new Router();
75
+ app.use(async (ctx, next) => {
76
+ ctx.set("Cache-Control", "no-store");
77
+ ctx.set("Referrer-Policy", "no-referrer");
78
+ ctx.set("X-Content-Type-Options", "nosniff");
79
+ const embedded = embeddedPageBootstrap(ctx, options.authorization);
80
+ if (embedded)
81
+ ctx.state.sessionPortalEmbed = embedded;
82
+ // The redeem shell contains no Session data and must be frameable before
83
+ // its fragment ticket can be posted. A real Session page is frameable only
84
+ // after its portal id resolves to the exact still-active embedded grant.
85
+ const frameOrigin = ctx.path === "/session-portal/redeem"
86
+ ? sessionPortalEmbedOrigin(ctx)
87
+ : embedded?.frameOrigin;
88
+ if (!frameOrigin)
89
+ ctx.set("X-Frame-Options", "DENY");
90
+ ctx.set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; " +
91
+ "connect-src 'self' ws: wss:; img-src 'self' data: blob:; font-src 'self' data:; " +
92
+ `base-uri 'none'; form-action 'self'; frame-ancestors ${frameOrigin ?? "'none'"}`);
93
+ if (options.shutdownSignal?.aborted) {
94
+ serviceUnavailable(ctx);
95
+ return;
96
+ }
97
+ await next();
98
+ });
99
+ bootstrapRouter.get("/health", (ctx) => {
100
+ ctx.body = { ok: true, surface: "session-portal" };
101
+ });
102
+ bootstrapRouter.get("/session-portal/redeem", (ctx) => {
103
+ ctx.type = "html";
104
+ ctx.body = redeemPage();
105
+ });
106
+ bootstrapRouter.post("/session-portal/redeem", async (ctx) => {
107
+ const body = await readJsonBody(ctx.req).catch(() => undefined);
108
+ const ticket = typeof body?.ticket === "string" ? body.ticket : "";
109
+ if (!ticket || Buffer.byteLength(ticket, "utf8") > MAX_TICKET_BYTES) {
110
+ ctx.status = 400;
111
+ ctx.body = { error: "invalid_ticket" };
112
+ return;
113
+ }
114
+ const requestedEmbedOrigin = sessionPortalEmbedOrigin(ctx);
115
+ const grant = options.authorization.redeem(ticket, requestedEmbedOrigin);
116
+ if (!grant) {
117
+ ctx.status = 401;
118
+ ctx.body = { error: "ticket_expired_or_used" };
119
+ return;
120
+ }
121
+ ctx.cookies.set(cookieName(grant.portalId), grant.secret, {
122
+ httpOnly: true,
123
+ sameSite: "strict",
124
+ secure: options.scheme === "https",
125
+ overwrite: true,
126
+ path: "/",
127
+ expires: new Date(grant.expiresAt),
128
+ });
129
+ const path = grant.scope === "single_session"
130
+ ? `/runtimes/local/sessions/${encodeURIComponent(grant.sessionId)}`
131
+ : "/runtimes/local/sessions";
132
+ const location = new URL(path, "http://session-portal.invalid");
133
+ location.searchParams.set(SESSION_PORTAL_QUERY, grant.portalId);
134
+ if (grant.embedOrigin) {
135
+ location.searchParams.set(SESSION_PORTAL_EMBED_ORIGIN_QUERY, grant.embedOrigin);
136
+ }
137
+ ctx.body = {
138
+ location: `${location.pathname}${location.search}`,
139
+ expiresAt: grant.expiresAt,
140
+ ...(grant.embedOrigin
141
+ ? {
142
+ authorization: {
143
+ ...publicAuthorization(grant),
144
+ secret: grant.secret,
145
+ },
146
+ }
147
+ : {}),
148
+ };
149
+ });
150
+ app.use(bootstrapRouter.routes());
151
+ app.use(bootstrapRouter.allowedMethods());
152
+ app.use(async (ctx, next) => {
153
+ if (isStaticAssetRequest(ctx))
154
+ return next();
155
+ const embedded = ctx.state.sessionPortalEmbed;
156
+ if (embedded) {
157
+ ctx.state.sessionPortalEmbed = embedded;
158
+ return next();
159
+ }
160
+ const grant = authorizeHttp(ctx, options.authorization);
161
+ if (!grant)
162
+ return;
163
+ ctx.state.sessionPortal = grant;
164
+ const revocation = new AbortController();
165
+ ctx.state.sessionPortalRevocationSignal = revocation.signal;
166
+ const untrack = transports.track(grant.portalId, () => {
167
+ revocation.abort(new Error("Session Portal grant revoked"));
168
+ if (!ctx.res.writableEnded)
169
+ ctx.res.destroy(new Error("Session Portal grant revoked"));
170
+ });
171
+ try {
172
+ // Close the authorize -> registry race: if revoke committed between
173
+ // those steps, its notification may have had no transport to close yet.
174
+ if (!options.authorization.inspect(grant.portalId)) {
175
+ ctx.status = 401;
176
+ ctx.body = { error: "session_portal_grant_expired_or_invalid" };
177
+ return;
178
+ }
179
+ expireHttpResponseAtGrantDeadline(ctx, grant.expiresAt);
180
+ if (ctx.path === "/api/session-portal/access") {
181
+ if (ctx.method !== "GET")
182
+ return methodNotAllowed(ctx);
183
+ ctx.body = publicAuthorization(grant);
184
+ return;
185
+ }
186
+ if (!authorizePortalRoute(ctx, grant, options.authorization))
187
+ return;
188
+ if (ctx.path.startsWith("/api/")) {
189
+ await proxyControlHttp(ctx, grant, options);
190
+ return;
191
+ }
192
+ await next();
193
+ }
194
+ finally {
195
+ // This is operation completion, not merely client disconnect. Revoke
196
+ // waits for it, so an already-admitted Control call cannot outlive the
197
+ // Host RPC that revoked its grant.
198
+ untrack();
199
+ }
200
+ });
201
+ app.use(async (ctx, next) => {
202
+ if (ctx.method !== "GET" && ctx.method !== "HEAD")
203
+ return next();
204
+ if (ctx.path.startsWith("/api/") || ctx.path.startsWith("/session-portal/"))
205
+ return next();
206
+ const grant = ctx.state.sessionPortal;
207
+ const embedded = ctx.state.sessionPortalEmbed;
208
+ await sendSpaFile(ctx, options.controlWebDist, "/", grant
209
+ ? (html) => injectPortalBootstrap(html, grant)
210
+ : embedded
211
+ ? (html) => injectEmbeddedPortalBootstrap(html, embedded.portalId)
212
+ : undefined);
213
+ });
214
+ return app;
215
+ }
216
+ export async function startSessionPortal(options) {
217
+ if (options.shutdownSignal?.aborted)
218
+ throw new Error("Control service is shutting down");
219
+ const lifecycle = new AbortController();
220
+ let shutdownPortal;
221
+ const stopForControlShutdown = () => {
222
+ lifecycle.abort(options.shutdownSignal?.reason ?? new Error("Control service is shutting down"));
223
+ void shutdownPortal?.().catch(() => undefined);
224
+ };
225
+ options.shutdownSignal?.addEventListener("abort", stopForControlShutdown, { once: true });
226
+ const transports = new SessionPortalTransportRegistry();
227
+ const unsubscribeRevocations = options.authorization.subscribeRevocations?.((portalIds) => transports.revoke(portalIds));
228
+ const app = createSessionPortalApp({ ...options, shutdownSignal: lifecycle.signal }, transports);
229
+ let server;
230
+ try {
231
+ server = await new Promise((resolve, reject) => {
232
+ const candidate = app.listen(options.port, options.host, () => {
233
+ candidate.off("error", reject);
234
+ resolve(candidate);
235
+ });
236
+ candidate.once("error", reject);
237
+ });
238
+ }
239
+ catch (error) {
240
+ unsubscribeRevocations?.();
241
+ options.shutdownSignal?.removeEventListener("abort", stopForControlShutdown);
242
+ throw error;
243
+ }
244
+ let sockets;
245
+ try {
246
+ sockets = attachPortalWebSocketProxy(server, {
247
+ authorization: options.authorization,
248
+ controlOrigin: options.controlOrigin,
249
+ shutdownSignal: lifecycle.signal,
250
+ transports,
251
+ });
252
+ }
253
+ catch (error) {
254
+ unsubscribeRevocations?.();
255
+ options.shutdownSignal?.removeEventListener("abort", stopForControlShutdown);
256
+ await closeHttpServerImmediately(server);
257
+ throw error;
258
+ }
259
+ let shutdownPromise;
260
+ const shutdown = () => {
261
+ if (shutdownPromise)
262
+ return shutdownPromise;
263
+ lifecycle.abort(new Error("Control service is shutting down"));
264
+ for (const client of sockets.external.clients)
265
+ client.terminate();
266
+ for (const upstream of sockets.upstream)
267
+ upstream.terminate();
268
+ shutdownPromise = Promise.all([
269
+ closeWebSocketServer(sockets.external),
270
+ closeHttpServerImmediately(server),
271
+ ]).then(() => {
272
+ unsubscribeRevocations?.();
273
+ options.shutdownSignal?.removeEventListener("abort", stopForControlShutdown);
274
+ });
275
+ return shutdownPromise;
276
+ };
277
+ shutdownPortal = shutdown;
278
+ if (lifecycle.signal.aborted) {
279
+ await shutdown();
280
+ throw new Error("Control service is shutting down");
281
+ }
282
+ return {
283
+ server,
284
+ stopAccepting: () => { void shutdown().catch(() => undefined); },
285
+ shutdown,
286
+ };
287
+ }
288
+ export function authorizeSessionPortalWebSocket(request, ports, runtimeSelector, sessionId) {
289
+ let url;
290
+ try {
291
+ url = new URL(request.url ?? "", "http://localhost");
292
+ }
293
+ catch {
294
+ return undefined;
295
+ }
296
+ const portalId = url.searchParams.get(SESSION_PORTAL_QUERY) ?? "";
297
+ if (!PORTAL_ID_PATTERN.test(portalId))
298
+ return undefined;
299
+ const secret = cookieValue(request.headers.cookie, cookieName(portalId)) ||
300
+ url.searchParams.get(SESSION_PORTAL_SECRET_QUERY) || undefined;
301
+ if (!secret)
302
+ return undefined;
303
+ const grant = ports.authorize(portalId, secret);
304
+ if (!grant)
305
+ return undefined;
306
+ if (runtimeSelector !== "local")
307
+ return undefined;
308
+ if (grant.scope === "single_session" && grant.sessionId !== sessionId)
309
+ return undefined;
310
+ if (!ports.ownsSession(grant.pluginId, sessionId))
311
+ return undefined;
312
+ return grant;
313
+ }
314
+ export function rejectSessionPortalUpgrade(socket) {
315
+ rejectUpgrade(socket, 401, "session portal grant expired or invalid");
316
+ }
317
+ function authorizeHttp(ctx, ports) {
318
+ const queryPortal = typeof ctx.query[SESSION_PORTAL_QUERY] === "string"
319
+ ? ctx.query[SESSION_PORTAL_QUERY]
320
+ : "";
321
+ const candidate = ctx.path.startsWith("/api/")
322
+ ? ctx.get(SESSION_PORTAL_HEADER) || queryPortal
323
+ : queryPortal;
324
+ const portalId = candidate.trim();
325
+ const secret = PORTAL_ID_PATTERN.test(portalId)
326
+ ? ctx.cookies.get(cookieName(portalId)) ||
327
+ ctx.get(SESSION_PORTAL_SECRET_HEADER).trim() || undefined
328
+ : undefined;
329
+ const grant = secret ? ports.authorize(portalId, secret) : undefined;
330
+ if (!grant) {
331
+ ctx.status = 401;
332
+ ctx.body = { error: "session_portal_grant_expired_or_invalid" };
333
+ }
334
+ return grant;
335
+ }
336
+ function authorizePortalRoute(ctx, grant, ports) {
337
+ if (ctx.path === "/") {
338
+ const target = grant.scope === "single_session"
339
+ ? `/runtimes/local/sessions/${encodeURIComponent(grant.sessionId)}`
340
+ : "/runtimes/local/sessions";
341
+ ctx.redirect(`${target}?${SESSION_PORTAL_QUERY}=${encodeURIComponent(grant.portalId)}`);
342
+ return false;
343
+ }
344
+ const pageMatch = RUNTIME_SESSION_PATH.exec(ctx.path);
345
+ if (pageMatch) {
346
+ if (ctx.method !== "GET" && ctx.method !== "HEAD")
347
+ return methodNotAllowed(ctx);
348
+ const requested = decoded(pageMatch[1]);
349
+ if (grant.scope === "single_session" && requested && requested !== grant.sessionId)
350
+ return notFound(ctx);
351
+ if (requested && !ports.ownsSession(grant.pluginId, requested))
352
+ return notFound(ctx);
353
+ return true;
354
+ }
355
+ if (ctx.path === "/api/runtimes/capability" || ctx.path === "/api/runtimes") {
356
+ if (ctx.method !== "GET")
357
+ return methodNotAllowed(ctx);
358
+ return true;
359
+ }
360
+ if (ctx.path === SESSION_COLLECTION_API_PATH) {
361
+ return ctx.method === "GET" ? true : methodNotAllowed(ctx);
362
+ }
363
+ const apiMatch = RUNTIME_SESSION_API_PATH.exec(ctx.path);
364
+ const sessionId = decoded(apiMatch?.[1]);
365
+ if (sessionId) {
366
+ if (grant.scope === "single_session" && sessionId !== grant.sessionId)
367
+ return notFound(ctx);
368
+ if (!ports.ownsSession(grant.pluginId, sessionId))
369
+ return notFound(ctx);
370
+ if (isBrowserRequestHeadersRoute(ctx, apiMatch[1])) {
371
+ if (ctx.method !== "GET" && ctx.method !== "PUT")
372
+ return methodNotAllowed(ctx);
373
+ return denyMutation(ctx, grant);
374
+ }
375
+ if (ctx.method === "GET" || ctx.method === "HEAD")
376
+ return true;
377
+ if (!isAllowedSessionMutation(ctx, apiMatch[1]))
378
+ return notFound(ctx);
379
+ return denyMutation(ctx, grant);
380
+ }
381
+ return notFound(ctx);
382
+ }
383
+ async function proxyControlHttp(ctx, grant, options) {
384
+ const revocationSignal = ctx.state.sessionPortalRevocationSignal;
385
+ // Reads/streams can be aborted immediately. An already-admitted mutation is
386
+ // different: keep its Control request alive and let the registry wait for
387
+ // the response, so revoke cannot return while a write is still executing.
388
+ const mutating = ctx.method !== "GET" && ctx.method !== "HEAD";
389
+ const signal = mutating
390
+ ? options.shutdownSignal
391
+ : options.shutdownSignal && revocationSignal
392
+ ? AbortSignal.any([options.shutdownSignal, revocationSignal])
393
+ : options.shutdownSignal ?? revocationSignal;
394
+ let response;
395
+ try {
396
+ response = await openControlRequest(ctx, options.controlOrigin, signal);
397
+ }
398
+ catch (error) {
399
+ if (signal?.aborted)
400
+ return;
401
+ ctx.status = 502;
402
+ ctx.body = { error: "control_service_unavailable" };
403
+ return;
404
+ }
405
+ if (shouldFilterControlResponse(ctx)) {
406
+ try {
407
+ const data = await readBoundedResponse(response, MAX_FILTERED_RESPONSE_BYTES, signal);
408
+ applyControlResponseHeaders(ctx, response.headers, true);
409
+ ctx.status = response.statusCode ?? 502;
410
+ const body = JSON.parse(data.toString("utf8"));
411
+ narrowPortalResponse(ctx.path, body, grant, options.authorization);
412
+ ctx.body = body;
413
+ }
414
+ catch {
415
+ if (signal?.aborted)
416
+ return;
417
+ ctx.status = 502;
418
+ ctx.body = { error: "invalid_control_response" };
419
+ }
420
+ return;
421
+ }
422
+ ctx.status = response.statusCode ?? 502;
423
+ applyControlResponseHeaders(ctx, response.headers, false);
424
+ ctx.respond = false;
425
+ ctx.res.statusCode = ctx.status;
426
+ try {
427
+ await pipeline(response, ctx.res, ...(signal
428
+ ? [{ signal }]
429
+ : []));
430
+ }
431
+ catch {
432
+ if (!ctx.res.destroyed)
433
+ ctx.res.destroy();
434
+ }
435
+ }
436
+ function openControlRequest(ctx, controlOrigin, signal) {
437
+ const target = new URL(`${ctx.path}${ctx.search}`, controlOrigin);
438
+ target.searchParams.delete(SESSION_PORTAL_QUERY);
439
+ target.searchParams.delete(SESSION_PORTAL_SECRET_QUERY);
440
+ target.searchParams.delete(SESSION_PORTAL_EMBED_ORIGIN_QUERY);
441
+ return new Promise((resolve, reject) => {
442
+ const request = httpRequest(target, {
443
+ method: ctx.method,
444
+ headers: controlRequestHeaders(ctx.req.headers, target),
445
+ ...(signal ? { signal } : {}),
446
+ }, resolve);
447
+ request.once("error", reject);
448
+ ctx.req.once("aborted", () => request.destroy(new Error("Portal request aborted")));
449
+ ctx.req.pipe(request);
450
+ });
451
+ }
452
+ function controlRequestHeaders(headers, target) {
453
+ const forwarded = {};
454
+ for (const [name, value] of Object.entries(headers)) {
455
+ const lower = name.toLowerCase();
456
+ if (value === undefined ||
457
+ HOP_BY_HOP_HEADERS.has(lower) ||
458
+ lower === "cookie" ||
459
+ lower === "host" ||
460
+ lower === "origin" ||
461
+ lower === "referer" ||
462
+ lower === "accept-encoding" ||
463
+ lower === SESSION_PORTAL_HEADER ||
464
+ lower === SESSION_PORTAL_SECRET_HEADER ||
465
+ lower.startsWith("forwarded") ||
466
+ lower.startsWith("x-forwarded-"))
467
+ continue;
468
+ forwarded[lower] = value;
469
+ }
470
+ forwarded.host = target.host;
471
+ forwarded.origin = target.origin;
472
+ forwarded["sec-fetch-site"] = "same-origin";
473
+ forwarded["accept-encoding"] = "identity";
474
+ return forwarded;
475
+ }
476
+ function applyControlResponseHeaders(ctx, headers, bodyChanged) {
477
+ for (const [name, value] of Object.entries(headers)) {
478
+ const lower = name.toLowerCase();
479
+ if (value === undefined ||
480
+ HOP_BY_HOP_HEADERS.has(lower) ||
481
+ lower === "set-cookie" ||
482
+ (bodyChanged && (lower === "content-length" || lower === "content-encoding")))
483
+ continue;
484
+ ctx.set(name, Array.isArray(value) ? value : String(value));
485
+ }
486
+ }
487
+ function shouldFilterControlResponse(ctx) {
488
+ return ctx.method === "GET" && (ctx.path === "/api/runtimes" || ctx.path === SESSION_COLLECTION_API_PATH);
489
+ }
490
+ function narrowPortalResponse(path, body, grant, ports) {
491
+ if (!body || typeof body !== "object")
492
+ return;
493
+ if (path === "/api/runtimes") {
494
+ const value = body;
495
+ if (Array.isArray(value.runtimes)) {
496
+ value.runtimes = value.runtimes.filter((runtime) => runtime.selector === "local");
497
+ }
498
+ }
499
+ if (path === SESSION_COLLECTION_API_PATH) {
500
+ const value = body;
501
+ if (Array.isArray(value.sessions)) {
502
+ value.sessions = value.sessions.filter((session) => typeof session.id === "string" &&
503
+ ports.ownsSession(grant.pluginId, session.id) &&
504
+ (grant.scope !== "single_session" || session.id === grant.sessionId));
505
+ }
506
+ }
507
+ }
508
+ function attachPortalWebSocketProxy(server, options) {
509
+ const external = new WebSocketServer({ noServer: true, perMessageDeflate: false });
510
+ const upstreamSockets = new Set();
511
+ server.on("upgrade", (request, socket, head) => {
512
+ if (options.shutdownSignal.aborted)
513
+ return rejectUpgrade(socket, 503, "Control service is shutting down");
514
+ const route = portalSocketRoute(request.url);
515
+ if (!route)
516
+ return rejectUpgrade(socket, 404, "Session Portal socket not found");
517
+ const credential = sessionPortalSocketCredential(request);
518
+ if (!credential)
519
+ return rejectSessionPortalUpgrade(socket);
520
+ const grant = authorizeSessionPortalWebSocket(request, options.authorization, route.runtimeSelector, route.sessionId);
521
+ if (!grant || !readOnlySocketRoleAllowed(route.kind, request.url, grant)) {
522
+ rejectSessionPortalUpgrade(socket);
523
+ return;
524
+ }
525
+ try {
526
+ external.handleUpgrade(request, socket, head, (client) => {
527
+ bridgePortalWebSocket(client, route.kind, request.url, grant, {
528
+ controlOrigin: options.controlOrigin,
529
+ shutdownSignal: options.shutdownSignal,
530
+ upstreamSockets,
531
+ transports: options.transports,
532
+ portalId: grant.portalId,
533
+ reauthorize: () => options.authorization.authorize(credential.portalId, credential.secret),
534
+ });
535
+ });
536
+ }
537
+ catch {
538
+ rejectUpgrade(socket, 400, "invalid Session Portal WebSocket upgrade");
539
+ }
540
+ });
541
+ return { external, upstream: upstreamSockets };
542
+ }
543
+ function bridgePortalWebSocket(client, kind, requestUrl, grant, options) {
544
+ const externalTarget = new URL(requestUrl, "http://session-portal.invalid");
545
+ const target = new URL(`${externalTarget.pathname}${externalTarget.search}`, options.controlOrigin);
546
+ target.protocol = target.protocol === "https:" ? "wss:" : "ws:";
547
+ target.searchParams.delete(SESSION_PORTAL_QUERY);
548
+ target.searchParams.delete(SESSION_PORTAL_SECRET_QUERY);
549
+ target.searchParams.delete(SESSION_PORTAL_EMBED_ORIGIN_QUERY);
550
+ const upstream = new WebSocket(target, {
551
+ origin: options.controlOrigin,
552
+ headers: { "sec-fetch-site": "same-origin" },
553
+ perMessageDeflate: false,
554
+ maxPayload: MAX_SOCKET_BUFFERED_BYTES,
555
+ });
556
+ options.upstreamSockets.add(upstream);
557
+ const untrack = options.transports.track(options.portalId, () => {
558
+ closeSocket(client, 4403, "Session Portal grant revoked");
559
+ upstream.terminate();
560
+ const forceClose = setTimeout(() => client.terminate(), 100);
561
+ forceClose.unref?.();
562
+ client.once("close", () => clearTimeout(forceClose));
563
+ });
564
+ // Close the authorize -> transport-registry race just like the HTTP path.
565
+ // Revoke may have committed and returned while handleUpgrade was accepting
566
+ // this socket but before it became visible to the registry; in that case the
567
+ // fresh lookup must fail before the upstream can receive any frame.
568
+ const initialRefresh = options.reauthorize();
569
+ if (!initialRefresh || (grant.access === "read_write" && initialRefresh.access !== "read_write")) {
570
+ closeSocket(client, 4403, initialRefresh ? "Session Portal access changed" : "Session Portal grant revoked");
571
+ // `ws` emits an error when a CONNECTING client is terminated; this early
572
+ // branch returns before the normal upstream error handler is installed.
573
+ upstream.on("error", () => undefined);
574
+ upstream.terminate();
575
+ options.upstreamSockets.delete(upstream);
576
+ untrack();
577
+ return;
578
+ }
579
+ const pending = [];
580
+ let pendingBytes = 0;
581
+ let closed = false;
582
+ const clearExpiry = scheduleSessionPortalExpiry(grant.expiresAt, () => {
583
+ closeSocket(client, 4403, "Session Portal grant expired");
584
+ closeSocket(upstream, 4403, "Session Portal grant expired");
585
+ });
586
+ const stopForShutdown = () => {
587
+ client.terminate();
588
+ upstream.terminate();
589
+ };
590
+ options.shutdownSignal.addEventListener("abort", stopForShutdown, { once: true });
591
+ const cleanup = () => {
592
+ if (closed)
593
+ return;
594
+ closed = true;
595
+ clearExpiry();
596
+ pending.length = 0;
597
+ pendingBytes = 0;
598
+ options.upstreamSockets.delete(upstream);
599
+ untrack();
600
+ options.shutdownSignal.removeEventListener("abort", stopForShutdown);
601
+ };
602
+ const reauthorizeOrClose = () => {
603
+ const current = options.reauthorize();
604
+ const downgraded = grant.access === "read_write" && current?.access !== "read_write";
605
+ if (current && !downgraded)
606
+ return current;
607
+ pending.length = 0;
608
+ pendingBytes = 0;
609
+ const reason = downgraded
610
+ ? "Session Portal access changed"
611
+ : "Session Portal grant revoked";
612
+ closeSocket(client, 4403, reason);
613
+ closeSocket(upstream, 4403, reason);
614
+ return undefined;
615
+ };
616
+ client.on("message", (data, isBinary) => {
617
+ const current = reauthorizeOrClose();
618
+ if (!current)
619
+ return;
620
+ // The role of an established socket never becomes more permissive. A
621
+ // grant upgraded in storage must reconnect before it can gain write access.
622
+ if (grant.access === "read_only" || current.access === "read_only") {
623
+ if (!allowedReadOnlySocketFrame(kind, data, isBinary)) {
624
+ // A read-only terminal intentionally ignores keystrokes and malformed
625
+ // controls while keeping the live output stream attached. Only its
626
+ // canonical resize control is forwarded to the tmux client.
627
+ if (kind === "terminal")
628
+ return;
629
+ closeSocket(client, 4403, "Session Portal is read only");
630
+ closeSocket(upstream, 4403, "Session Portal is read only");
631
+ return;
632
+ }
633
+ }
634
+ const frame = rawDataBuffer(data);
635
+ if (upstream.readyState === WebSocket.OPEN) {
636
+ forwardSocketFrame(client, upstream, frame, isBinary);
637
+ return;
638
+ }
639
+ if (pending.length >= MAX_PENDING_SOCKET_FRAMES ||
640
+ pendingBytes + frame.byteLength > MAX_PENDING_SOCKET_BYTES) {
641
+ closeSocket(client, 4408, "Session Portal socket queue is full");
642
+ upstream.terminate();
643
+ return;
644
+ }
645
+ pending.push({ data: frame, isBinary });
646
+ pendingBytes += frame.byteLength;
647
+ });
648
+ upstream.on("open", () => {
649
+ if (!reauthorizeOrClose())
650
+ return;
651
+ for (const frame of pending.splice(0)) {
652
+ pendingBytes -= frame.data.byteLength;
653
+ if (!forwardSocketFrame(client, upstream, frame.data, frame.isBinary))
654
+ break;
655
+ }
656
+ });
657
+ upstream.on("message", (data, isBinary) => {
658
+ if (!reauthorizeOrClose())
659
+ return;
660
+ forwardSocketFrame(upstream, client, data, isBinary);
661
+ });
662
+ client.on("close", (code, reason) => {
663
+ closeSocket(upstream, normalizedCloseCode(code), reason.toString());
664
+ cleanup();
665
+ });
666
+ upstream.on("close", (code, reason) => {
667
+ closeSocket(client, normalizedCloseCode(code), reason.toString());
668
+ cleanup();
669
+ });
670
+ client.on("error", () => {
671
+ upstream.terminate();
672
+ cleanup();
673
+ });
674
+ upstream.on("error", () => {
675
+ closeSocket(client, 1011, "Control service unavailable");
676
+ });
677
+ }
678
+ function sessionPortalSocketCredential(request) {
679
+ let url;
680
+ try {
681
+ url = new URL(request.url ?? "", "http://localhost");
682
+ }
683
+ catch {
684
+ return undefined;
685
+ }
686
+ const portalId = url.searchParams.get(SESSION_PORTAL_QUERY) ?? "";
687
+ if (!PORTAL_ID_PATTERN.test(portalId))
688
+ return undefined;
689
+ const secret = cookieValue(request.headers.cookie, cookieName(portalId)) ||
690
+ url.searchParams.get(SESSION_PORTAL_SECRET_QUERY) || undefined;
691
+ return secret ? { portalId, secret } : undefined;
692
+ }
693
+ function portalSocketRoute(rawUrl) {
694
+ let pathname;
695
+ try {
696
+ pathname = new URL(rawUrl ?? "", "http://localhost").pathname;
697
+ }
698
+ catch {
699
+ return undefined;
700
+ }
701
+ for (const [kind, pattern] of [
702
+ ["terminal", TERMINAL_SOCKET_PATH],
703
+ ["browser", BROWSER_SOCKET_PATH],
704
+ ["emulator", EMULATOR_SOCKET_PATH],
705
+ ]) {
706
+ const match = pattern.exec(pathname);
707
+ if (!match)
708
+ continue;
709
+ const runtimeSelector = decoded(match[1]);
710
+ const sessionId = decoded(match[2]);
711
+ if (runtimeSelector && sessionId)
712
+ return { kind, runtimeSelector, sessionId };
713
+ }
714
+ return undefined;
715
+ }
716
+ function readOnlySocketRoleAllowed(kind, rawUrl, grant) {
717
+ if (grant.access !== "read_only" || kind === "emulator")
718
+ return true;
719
+ const role = new URL(rawUrl ?? "", "http://localhost").searchParams.get("role");
720
+ return kind === "terminal" ? role === "read-only" : role === "view";
721
+ }
722
+ function allowedReadOnlySocketFrame(kind, data, isBinary) {
723
+ if (kind === "terminal")
724
+ return isBrowserTerminalResizeFrame(data, isBinary);
725
+ if (kind !== "browser" || isBinary)
726
+ return false;
727
+ try {
728
+ const value = JSON.parse(rawDataBuffer(data).toString("utf8"));
729
+ return value.type === "browser.surface.frame.ack";
730
+ }
731
+ catch {
732
+ return false;
733
+ }
734
+ }
735
+ function forwardSocketFrame(source, target, data, isBinary) {
736
+ const bytes = rawDataBytes(data);
737
+ if (target.readyState !== WebSocket.OPEN ||
738
+ target.bufferedAmount + bytes > MAX_SOCKET_BUFFERED_BYTES) {
739
+ closeSocket(source, 4408, "Session Portal socket is too slow");
740
+ closeSocket(target, 4408, "Session Portal socket is too slow");
741
+ return false;
742
+ }
743
+ target.send(data, { binary: isBinary }, (error) => {
744
+ if (error) {
745
+ source.terminate();
746
+ target.terminate();
747
+ }
748
+ });
749
+ return true;
750
+ }
751
+ function rawDataBuffer(data) {
752
+ if (Buffer.isBuffer(data))
753
+ return Buffer.from(data);
754
+ if (data instanceof ArrayBuffer)
755
+ return Buffer.from(data);
756
+ if (Array.isArray(data))
757
+ return Buffer.concat(data);
758
+ throw new TypeError("Unsupported WebSocket frame payload");
759
+ }
760
+ function rawDataBytes(data) {
761
+ if (Buffer.isBuffer(data))
762
+ return data.byteLength;
763
+ if (data instanceof ArrayBuffer)
764
+ return data.byteLength;
765
+ if (Array.isArray(data))
766
+ return data.reduce((total, part) => total + part.byteLength, 0);
767
+ throw new TypeError("Unsupported WebSocket frame payload");
768
+ }
769
+ function closeSocket(socket, code, reason) {
770
+ if (socket.readyState === WebSocket.CONNECTING) {
771
+ socket.terminate();
772
+ return;
773
+ }
774
+ if (socket.readyState === WebSocket.OPEN)
775
+ socket.close(code, reason.slice(0, 120));
776
+ }
777
+ function normalizedCloseCode(code) {
778
+ return code >= 1000 && code <= 4999 && code !== 1005 && code !== 1006 ? code : 1011;
779
+ }
780
+ function rejectUpgrade(socket, status, message) {
781
+ if (socket.destroyed)
782
+ return;
783
+ const body = `${message}\n`;
784
+ const statusText = status === 401
785
+ ? "Unauthorized"
786
+ : status === 404
787
+ ? "Not Found"
788
+ : status === 503
789
+ ? "Service Unavailable"
790
+ : "Bad Request";
791
+ socket.end(`HTTP/1.1 ${status} ${statusText}\r\nConnection: close\r\n` +
792
+ "Content-Type: text/plain; charset=utf-8\r\n" +
793
+ `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`);
794
+ }
795
+ function isAllowedSessionMutation(ctx, encodedSessionId) {
796
+ const prefix = `${SESSION_COLLECTION_API_PATH}/${encodedSessionId}`;
797
+ const suffix = ctx.path.slice(prefix.length);
798
+ if (ctx.method === "POST") {
799
+ return /^(?:\/interrupt|\/messages(?:\/queue)?|\/terminal\/start|\/resources|\/interactions\/[^/]+\/resolve|\/browser\/(?:open|close|pages)|\/browser\/pages\/[^/]+\/(?:close|activate|back|forward|reload|navigate)|\/emulator\/(?:attach|detach|tap|button|type|rotate|launch|gesture))$/.test(suffix);
800
+ }
801
+ if (ctx.method === "PUT") {
802
+ return suffix === "/execution" || suffix === "/browser/request-headers";
803
+ }
804
+ return ctx.method === "DELETE" && /^\/resources\/[^/]+$/.test(suffix);
805
+ }
806
+ function isBrowserRequestHeadersRoute(ctx, encodedSessionId) {
807
+ const prefix = `${SESSION_COLLECTION_API_PATH}/${encodedSessionId}`;
808
+ return ctx.path.slice(prefix.length) === "/browser/request-headers";
809
+ }
810
+ function denyMutation(ctx, grant) {
811
+ if (grant.access === "read_write")
812
+ return true;
813
+ ctx.status = 403;
814
+ ctx.body = { error: "session_portal_read_only" };
815
+ return false;
816
+ }
817
+ function expireHttpResponseAtGrantDeadline(ctx, expiresAt) {
818
+ const clear = scheduleSessionPortalExpiry(expiresAt, () => {
819
+ if (!ctx.res.writableEnded)
820
+ ctx.res.destroy(new Error("Session Portal grant expired"));
821
+ });
822
+ ctx.res.once("finish", clear);
823
+ ctx.res.once("close", clear);
824
+ }
825
+ export function scheduleSessionPortalExpiry(expiresAt, onExpire) {
826
+ const delay = Math.max(0, Date.parse(expiresAt) - Date.now());
827
+ const timer = setTimeout(onExpire, delay);
828
+ timer.unref?.();
829
+ return () => clearTimeout(timer);
830
+ }
831
+ function publicAuthorization(grant) {
832
+ return {
833
+ portalId: grant.portalId,
834
+ scope: grant.scope,
835
+ access: grant.access,
836
+ ...(grant.embedOrigin ? { embedOrigin: grant.embedOrigin } : {}),
837
+ ...(grant.sessionId ? { sessionId: grant.sessionId } : {}),
838
+ expiresAt: grant.expiresAt,
839
+ };
840
+ }
841
+ function injectEmbeddedPortalBootstrap(html, portalId) {
842
+ const id = JSON.stringify(portalId).replace(/</g, "\\u003c");
843
+ const script = `<script>(()=>{try{const raw=sessionStorage.getItem('rynx_session_portal_'+${id});` +
844
+ `const value=raw?JSON.parse(raw):null;if(value&&value.portalId===${id})window.rynxSessionPortal=value}` +
845
+ `catch{}})()</script>`;
846
+ return html.replace("</head>", `${script}</head>`);
847
+ }
848
+ function embeddedPageBootstrap(ctx, ports) {
849
+ if (ctx.method !== "GET" && ctx.method !== "HEAD")
850
+ return undefined;
851
+ const match = RUNTIME_SESSION_PATH.exec(ctx.path);
852
+ if (!match)
853
+ return undefined;
854
+ const portalId = typeof ctx.query[SESSION_PORTAL_QUERY] === "string"
855
+ ? ctx.query[SESSION_PORTAL_QUERY].trim()
856
+ : "";
857
+ const frameOrigin = sessionPortalEmbedOrigin(ctx);
858
+ if (!PORTAL_ID_PATTERN.test(portalId) || !frameOrigin)
859
+ return undefined;
860
+ const grant = ports.inspect(portalId);
861
+ if (!grant || grant.embedOrigin !== frameOrigin)
862
+ return undefined;
863
+ const requestedSessionId = decoded(match[1]);
864
+ if (grant.scope === "single_session" &&
865
+ requestedSessionId !== grant.sessionId)
866
+ return undefined;
867
+ if (requestedSessionId && !ports.ownsSession(grant.pluginId, requestedSessionId)) {
868
+ return undefined;
869
+ }
870
+ return { portalId, frameOrigin };
871
+ }
872
+ function sessionPortalEmbedOrigin(ctx) {
873
+ const value = typeof ctx.query[SESSION_PORTAL_EMBED_ORIGIN_QUERY] === "string"
874
+ ? ctx.query[SESSION_PORTAL_EMBED_ORIGIN_QUERY]
875
+ : "";
876
+ return normalizedFrameOrigin(value);
877
+ }
878
+ function normalizedFrameOrigin(value) {
879
+ const candidate = value.trim();
880
+ if (!candidate || candidate.length > 512)
881
+ return undefined;
882
+ try {
883
+ const parsed = new URL(candidate);
884
+ if ((parsed.protocol !== "http:" && parsed.protocol !== "https:") ||
885
+ parsed.origin !== candidate)
886
+ return undefined;
887
+ return parsed.origin;
888
+ }
889
+ catch {
890
+ return undefined;
891
+ }
892
+ }
893
+ function injectPortalBootstrap(html, grant) {
894
+ const value = JSON.stringify(publicAuthorization(grant)).replace(/</g, "\\u003c");
895
+ return html.replace("</head>", `<script>window.rynxSessionPortal=${value}</script></head>`);
896
+ }
897
+ function isStaticAssetRequest(ctx) {
898
+ return (ctx.method === "GET" || ctx.method === "HEAD") &&
899
+ (ctx.path.startsWith("/assets/") || ctx.path === "/favicon.png" || ctx.path === "/favicon.svg");
900
+ }
901
+ function cookieName(portalId) {
902
+ return `rynx_session_portal_${portalId}`;
903
+ }
904
+ function cookieValue(raw, name) {
905
+ for (const part of raw?.split(";") ?? []) {
906
+ const index = part.indexOf("=");
907
+ if (index < 0 || part.slice(0, index).trim() !== name)
908
+ continue;
909
+ try {
910
+ return decodeURIComponent(part.slice(index + 1).trim());
911
+ }
912
+ catch {
913
+ return undefined;
914
+ }
915
+ }
916
+ return undefined;
917
+ }
918
+ function decoded(value) {
919
+ if (!value)
920
+ return undefined;
921
+ try {
922
+ return decodeURIComponent(value);
923
+ }
924
+ catch {
925
+ return undefined;
926
+ }
927
+ }
928
+ function notFound(ctx) {
929
+ ctx.status = 404;
930
+ ctx.body = { error: "session_not_found" };
931
+ return false;
932
+ }
933
+ function methodNotAllowed(ctx) {
934
+ ctx.status = 405;
935
+ ctx.body = { error: "method_not_allowed" };
936
+ return false;
937
+ }
938
+ function serviceUnavailable(ctx) {
939
+ ctx.status = 503;
940
+ ctx.body = { error: "control_service_shutting_down" };
941
+ }
942
+ async function readJsonBody(request) {
943
+ const chunks = [];
944
+ let bytes = 0;
945
+ for await (const chunk of request) {
946
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
947
+ bytes += buffer.length;
948
+ if (bytes > 4_096)
949
+ throw new Error("request body too large");
950
+ chunks.push(buffer);
951
+ }
952
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
953
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
954
+ throw new Error("request body must be an object");
955
+ }
956
+ return parsed;
957
+ }
958
+ async function readBoundedResponse(response, maxBytes, signal) {
959
+ const chunks = [];
960
+ let bytes = 0;
961
+ const abort = () => response.destroy(signal?.reason instanceof Error ? signal.reason : undefined);
962
+ if (signal?.aborted)
963
+ abort();
964
+ else
965
+ signal?.addEventListener("abort", abort, { once: true });
966
+ try {
967
+ for await (const chunk of response) {
968
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
969
+ bytes += buffer.byteLength;
970
+ if (bytes > maxBytes)
971
+ throw new Error("Control response is too large");
972
+ chunks.push(buffer);
973
+ }
974
+ return Buffer.concat(chunks);
975
+ }
976
+ finally {
977
+ signal?.removeEventListener("abort", abort);
978
+ }
979
+ }
980
+ function closeWebSocketServer(server) {
981
+ return new Promise((resolve, reject) => {
982
+ server.close((error) => error ? reject(error) : resolve());
983
+ });
984
+ }
985
+ function closeHttpServerImmediately(server) {
986
+ return new Promise((resolve, reject) => {
987
+ if (!server.listening) {
988
+ resolve();
989
+ return;
990
+ }
991
+ server.close((error) => error ? reject(error) : resolve());
992
+ server.closeAllConnections?.();
993
+ });
994
+ }
995
+ function redeemPage() {
996
+ return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Rynx Sessions</title><style>body{font:14px Inter,system-ui;margin:0;display:grid;min-height:100vh;place-items:center;background:#f6f6f8;color:#17171b}.box{text-align:center}.spin{width:22px;height:22px;margin:0 auto 14px;border:2px solid #d8d8df;border-top-color:#6d5ce7;border-radius:50%;animation:s .8s linear infinite}@keyframes s{to{transform:rotate(360deg)}}</style></head><body><main class="box"><div class="spin"></div><p id="status">正在打开 Rynx Sessions…</p></main><script>(()=>{const el=document.getElementById('status');const p=new URLSearchParams(location.hash.slice(1));const ticket=p.get('ticket');history.replaceState(null,'',location.pathname+location.search);if(!ticket){el.textContent='访问链接缺少票据。';return}fetch(location.pathname+location.search,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ticket})}).then(async r=>{const b=await r.json();if(!r.ok)throw new Error(b.error||'票据兑换失败');if(b.authorization){sessionStorage.setItem('rynx_session_portal_'+b.authorization.portalId,JSON.stringify(b.authorization))}location.replace(b.location)}).catch(e=>{el.textContent='无法打开:'+e.message})})();</script></body></html>`;
997
+ }