remote-codex 0.11.1 → 0.11.3

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 (336) hide show
  1. package/apps/relay-server/dist/index.js +1221 -0
  2. package/apps/supervisor-api/dist/index.js +2822 -780
  3. package/apps/supervisor-web/dist/assets/core-DIQen2lE.js +12 -0
  4. package/apps/supervisor-web/dist/assets/{css-DPfMkruS.js → css-CLj8gQPS.js} +1 -1
  5. package/apps/supervisor-web/dist/assets/engine-javascript-DBd1bXLz.js +141 -0
  6. package/apps/supervisor-web/dist/assets/graph-vendor-C5ap-Sga.css +1 -0
  7. package/apps/supervisor-web/dist/assets/graph-vendor-CGzY-MFv.js +23 -0
  8. package/apps/supervisor-web/dist/assets/{html-GMplVEZG.js → html-pp8916En.js} +1 -1
  9. package/apps/supervisor-web/dist/assets/index-CBIze1VS.css +1 -0
  10. package/apps/supervisor-web/dist/assets/index-YpGAPjED.js +4 -0
  11. package/apps/supervisor-web/dist/assets/markdown-vendor-hBDTCSB-.js +291 -0
  12. package/apps/supervisor-web/dist/assets/react-vendor-o1Xrx7m4.js +60 -0
  13. package/apps/supervisor-web/dist/assets/{shellscript-Yzrsuije.js → shellscript-CEILq0vU.js} +1 -1
  14. package/apps/supervisor-web/dist/assets/{sql-BLtJtn59.js → sql-CRqJ_cUM.js} +1 -1
  15. package/apps/supervisor-web/dist/assets/terminal-vendor-Beg8tuEN.css +32 -0
  16. package/apps/supervisor-web/dist/assets/{xterm-D92BViLH.js → terminal-vendor-CLGgN91S.js} +17 -6
  17. package/apps/supervisor-web/dist/assets/thread-ui-BEieA99i.css +1 -0
  18. package/apps/supervisor-web/dist/assets/thread-ui-CF80LEEN.js +3613 -0
  19. package/apps/supervisor-web/dist/assets/ui-vendor-CW6egZBG.js +430 -0
  20. package/apps/supervisor-web/dist/index.html +11 -2
  21. package/apps/supervisor-web/dist/vendor/3Dmol-min.js +2 -0
  22. package/bin/remote-codex-plugin-mcp.mjs +140 -0
  23. package/bin/remote-codex.mjs +73 -16
  24. package/package.json +7 -1
  25. package/packages/agent-runtime/src/types.ts +1 -0
  26. package/packages/agent-runtime/src/unavailable-runtime.ts +5 -13
  27. package/packages/claude/src/runtimeAdapter.ts +7 -5
  28. package/packages/codex/src/appServerManager.ts +3 -3
  29. package/packages/codex/src/historyItems.test.ts +2 -4
  30. package/packages/codex/src/historyItems.ts +13 -18
  31. package/packages/codex/src/runtime-errors.ts +0 -5
  32. package/packages/codex/src/runtimeAdapter.ts +3 -0
  33. package/packages/codex/src/types.ts +1 -0
  34. package/packages/db/migrations/0018_shell_session_label.sql +1 -0
  35. package/packages/db/src/repositories.ts +12 -0
  36. package/packages/db/src/schema.ts +1 -0
  37. package/packages/opencode/src/historyItems.ts +0 -17
  38. package/packages/plugin-terminal/package.json +17 -0
  39. package/packages/plugin-terminal/plugin.json +25 -0
  40. package/packages/plugin-terminal/src/index.ts +2 -0
  41. package/packages/plugin-terminal/src/manifest.ts +51 -0
  42. package/packages/shared/src/index.ts +213 -0
  43. package/apps/supervisor-api/dist/chunk-6M32PPHZ.js +0 -24507
  44. package/apps/supervisor-api/dist/chunk-7AA2MFXK.js +0 -24499
  45. package/apps/supervisor-api/dist/chunk-HKBFCPHH.js +0 -24511
  46. package/apps/supervisor-api/dist/worker-index.js +0 -33
  47. package/apps/supervisor-web/dist/assets/abap-BdImnpbu.js +0 -1
  48. package/apps/supervisor-web/dist/assets/actionscript-3-CoDkCxhg.js +0 -1
  49. package/apps/supervisor-web/dist/assets/ada-bCR0ucgS.js +0 -1
  50. package/apps/supervisor-web/dist/assets/addon-fit-YJmn1quW.js +0 -12
  51. package/apps/supervisor-web/dist/assets/andromeeda-C4gqWexZ.js +0 -1
  52. package/apps/supervisor-web/dist/assets/angular-html-CU67Zn6k.js +0 -1
  53. package/apps/supervisor-web/dist/assets/angular-ts-BwZT4LLn.js +0 -1
  54. package/apps/supervisor-web/dist/assets/apache-Pmp26Uib.js +0 -1
  55. package/apps/supervisor-web/dist/assets/apex-D8_7TLub.js +0 -1
  56. package/apps/supervisor-web/dist/assets/apl-dKokRX4l.js +0 -1
  57. package/apps/supervisor-web/dist/assets/applescript-Co6uUVPk.js +0 -1
  58. package/apps/supervisor-web/dist/assets/ara-BRHolxvo.js +0 -1
  59. package/apps/supervisor-web/dist/assets/asciidoc-Ve4PFQV2.js +0 -1
  60. package/apps/supervisor-web/dist/assets/asm-D_Q5rh1f.js +0 -1
  61. package/apps/supervisor-web/dist/assets/astro-CbQHKStN.js +0 -1
  62. package/apps/supervisor-web/dist/assets/aurora-x-D-2ljcwZ.js +0 -1
  63. package/apps/supervisor-web/dist/assets/awk-DMzUqQB5.js +0 -1
  64. package/apps/supervisor-web/dist/assets/ayu-mirage-32ctXXKs.js +0 -1
  65. package/apps/supervisor-web/dist/assets/ballerina-BFfxhgS-.js +0 -1
  66. package/apps/supervisor-web/dist/assets/bat-BkioyH1T.js +0 -1
  67. package/apps/supervisor-web/dist/assets/beancount-k_qm7-4y.js +0 -1
  68. package/apps/supervisor-web/dist/assets/berry-uYugtg8r.js +0 -1
  69. package/apps/supervisor-web/dist/assets/bibtex-CHM0blh-.js +0 -1
  70. package/apps/supervisor-web/dist/assets/bicep-Bmn6On1c.js +0 -1
  71. package/apps/supervisor-web/dist/assets/bird2-DPOp833l.js +0 -1
  72. package/apps/supervisor-web/dist/assets/blade-D4QpJJKB.js +0 -1
  73. package/apps/supervisor-web/dist/assets/bsl-BO_Y6i37.js +0 -1
  74. package/apps/supervisor-web/dist/assets/c-BIGW1oBm.js +0 -1
  75. package/apps/supervisor-web/dist/assets/c3-eo99z4R2.js +0 -1
  76. package/apps/supervisor-web/dist/assets/cadence-Bv_4Rxtq.js +0 -1
  77. package/apps/supervisor-web/dist/assets/cairo-KRGpt6FW.js +0 -1
  78. package/apps/supervisor-web/dist/assets/catppuccin-frappe-DFWUc33u.js +0 -1
  79. package/apps/supervisor-web/dist/assets/catppuccin-latte-C9dUb6Cb.js +0 -1
  80. package/apps/supervisor-web/dist/assets/catppuccin-macchiato-DQyhUUbL.js +0 -1
  81. package/apps/supervisor-web/dist/assets/catppuccin-mocha-D87Tk5Gz.js +0 -1
  82. package/apps/supervisor-web/dist/assets/clarity-D53aC0YG.js +0 -1
  83. package/apps/supervisor-web/dist/assets/clojure-P80f7IUj.js +0 -1
  84. package/apps/supervisor-web/dist/assets/cmake-D1j8_8rp.js +0 -1
  85. package/apps/supervisor-web/dist/assets/cobol-nwyudZeR.js +0 -1
  86. package/apps/supervisor-web/dist/assets/codeowners-Bp6g37R7.js +0 -1
  87. package/apps/supervisor-web/dist/assets/codeql-DsOJ9woJ.js +0 -1
  88. package/apps/supervisor-web/dist/assets/coffee-Ch7k5sss.js +0 -1
  89. package/apps/supervisor-web/dist/assets/common-lisp-Cg-RD9OK.js +0 -1
  90. package/apps/supervisor-web/dist/assets/coq-DkFqJrB1.js +0 -1
  91. package/apps/supervisor-web/dist/assets/cpp-CofmeUqb.js +0 -1
  92. package/apps/supervisor-web/dist/assets/crystal-tKQVLTB8.js +0 -1
  93. package/apps/supervisor-web/dist/assets/csharp-COcwbKMJ.js +0 -1
  94. package/apps/supervisor-web/dist/assets/cue-D82EKSYY.js +0 -1
  95. package/apps/supervisor-web/dist/assets/cypher-COkxafJQ.js +0 -1
  96. package/apps/supervisor-web/dist/assets/d-85-TOEBH.js +0 -1
  97. package/apps/supervisor-web/dist/assets/dark-plus-C3mMm8J8.js +0 -1
  98. package/apps/supervisor-web/dist/assets/dart-CF10PKvl.js +0 -1
  99. package/apps/supervisor-web/dist/assets/dax-CEL-wOlO.js +0 -1
  100. package/apps/supervisor-web/dist/assets/desktop-BmXAJ9_W.js +0 -1
  101. package/apps/supervisor-web/dist/assets/diff-D97Zzqfu.js +0 -1
  102. package/apps/supervisor-web/dist/assets/docker-BcOcwvcX.js +0 -1
  103. package/apps/supervisor-web/dist/assets/dotenv-Da5cRb03.js +0 -1
  104. package/apps/supervisor-web/dist/assets/dracula-BzJJZx-M.js +0 -1
  105. package/apps/supervisor-web/dist/assets/dracula-soft-BXkSAIEj.js +0 -1
  106. package/apps/supervisor-web/dist/assets/dream-maker-BtqSS_iP.js +0 -1
  107. package/apps/supervisor-web/dist/assets/edge-BkV0erSs.js +0 -1
  108. package/apps/supervisor-web/dist/assets/elixir-CDX3lj18.js +0 -1
  109. package/apps/supervisor-web/dist/assets/elm-DbKCFpqz.js +0 -1
  110. package/apps/supervisor-web/dist/assets/emacs-lisp-C9XAeP06.js +0 -1
  111. package/apps/supervisor-web/dist/assets/erb-B12qg9BL.js +0 -1
  112. package/apps/supervisor-web/dist/assets/erlang-DsQrWhSR.js +0 -1
  113. package/apps/supervisor-web/dist/assets/everforest-dark-BgDCqdQA.js +0 -1
  114. package/apps/supervisor-web/dist/assets/everforest-light-C8M2exoo.js +0 -1
  115. package/apps/supervisor-web/dist/assets/fennel-BYunw83y.js +0 -1
  116. package/apps/supervisor-web/dist/assets/fish-BvzEVeQv.js +0 -1
  117. package/apps/supervisor-web/dist/assets/fluent-C4IJs8-o.js +0 -1
  118. package/apps/supervisor-web/dist/assets/fortran-fixed-form-CkoXwp7k.js +0 -1
  119. package/apps/supervisor-web/dist/assets/fortran-free-form-BxgE0vQu.js +0 -1
  120. package/apps/supervisor-web/dist/assets/fsharp-CXgrBDvD.js +0 -1
  121. package/apps/supervisor-web/dist/assets/gdresource-BOOCDP_w.js +0 -1
  122. package/apps/supervisor-web/dist/assets/gdscript-C5YyOfLZ.js +0 -1
  123. package/apps/supervisor-web/dist/assets/gdshader-DkwncUOv.js +0 -1
  124. package/apps/supervisor-web/dist/assets/genie-D0YGMca9.js +0 -1
  125. package/apps/supervisor-web/dist/assets/gherkin-DyxjwDmM.js +0 -1
  126. package/apps/supervisor-web/dist/assets/git-commit-F4YmCXRG.js +0 -1
  127. package/apps/supervisor-web/dist/assets/git-rebase-r7XF79zn.js +0 -1
  128. package/apps/supervisor-web/dist/assets/github-dark-DHJKELXO.js +0 -1
  129. package/apps/supervisor-web/dist/assets/github-dark-default-Cuk6v7N8.js +0 -1
  130. package/apps/supervisor-web/dist/assets/github-dark-dimmed-DH5Ifo-i.js +0 -1
  131. package/apps/supervisor-web/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +0 -1
  132. package/apps/supervisor-web/dist/assets/github-light-DAi9KRSo.js +0 -1
  133. package/apps/supervisor-web/dist/assets/github-light-default-D7oLnXFd.js +0 -1
  134. package/apps/supervisor-web/dist/assets/github-light-high-contrast-BfjtVDDH.js +0 -1
  135. package/apps/supervisor-web/dist/assets/gleam-BspZqrRM.js +0 -1
  136. package/apps/supervisor-web/dist/assets/glimmer-js-Rg0-pVw9.js +0 -1
  137. package/apps/supervisor-web/dist/assets/glimmer-ts-U6CK756n.js +0 -1
  138. package/apps/supervisor-web/dist/assets/glsl-DplSGwfg.js +0 -1
  139. package/apps/supervisor-web/dist/assets/gn-n2N0HUVH.js +0 -1
  140. package/apps/supervisor-web/dist/assets/gnuplot-DdkO51Og.js +0 -1
  141. package/apps/supervisor-web/dist/assets/go-CxLEBnE3.js +0 -1
  142. package/apps/supervisor-web/dist/assets/graphql-ChdNCCLP.js +0 -1
  143. package/apps/supervisor-web/dist/assets/groovy-gcz8RCvz.js +0 -1
  144. package/apps/supervisor-web/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +0 -1
  145. package/apps/supervisor-web/dist/assets/gruvbox-dark-medium-GsRaNv29.js +0 -1
  146. package/apps/supervisor-web/dist/assets/gruvbox-dark-soft-CVdnzihN.js +0 -1
  147. package/apps/supervisor-web/dist/assets/gruvbox-light-hard-CH1njM8p.js +0 -1
  148. package/apps/supervisor-web/dist/assets/gruvbox-light-medium-DRw_LuNl.js +0 -1
  149. package/apps/supervisor-web/dist/assets/gruvbox-light-soft-hJgmCMqR.js +0 -1
  150. package/apps/supervisor-web/dist/assets/hack-CaT9iCJl.js +0 -1
  151. package/apps/supervisor-web/dist/assets/haml-B8DHNrY2.js +0 -1
  152. package/apps/supervisor-web/dist/assets/handlebars-BL8al0AC.js +0 -1
  153. package/apps/supervisor-web/dist/assets/haskell-Df6bDoY_.js +0 -1
  154. package/apps/supervisor-web/dist/assets/haxe-CzTSHFRz.js +0 -1
  155. package/apps/supervisor-web/dist/assets/hcl-BWvSN4gD.js +0 -1
  156. package/apps/supervisor-web/dist/assets/highlighted-body-OFNGDK62-p31aS0f0.js +0 -1
  157. package/apps/supervisor-web/dist/assets/hjson-D5-asLiD.js +0 -1
  158. package/apps/supervisor-web/dist/assets/hlsl-D3lLCCz7.js +0 -1
  159. package/apps/supervisor-web/dist/assets/horizon-BUw7H-hv.js +0 -1
  160. package/apps/supervisor-web/dist/assets/horizon-bright-Cn-bp-IR.js +0 -1
  161. package/apps/supervisor-web/dist/assets/houston-DnULxvSX.js +0 -1
  162. package/apps/supervisor-web/dist/assets/html-derivative-BFtXZ54Q.js +0 -1
  163. package/apps/supervisor-web/dist/assets/http-jrhK8wxY.js +0 -1
  164. package/apps/supervisor-web/dist/assets/hurl-irOxFIW8.js +0 -1
  165. package/apps/supervisor-web/dist/assets/hxml-Bvhsp5Yf.js +0 -1
  166. package/apps/supervisor-web/dist/assets/hy-DFXneXwc.js +0 -1
  167. package/apps/supervisor-web/dist/assets/imba-DGztddWO.js +0 -1
  168. package/apps/supervisor-web/dist/assets/index-BiuFei_K.css +0 -32
  169. package/apps/supervisor-web/dist/assets/index-D1R9CUnx.js +0 -2161
  170. package/apps/supervisor-web/dist/assets/ini-BEwlwnbL.js +0 -1
  171. package/apps/supervisor-web/dist/assets/java-CylS5w8V.js +0 -1
  172. package/apps/supervisor-web/dist/assets/jinja-4LBKfQ-Z.js +0 -1
  173. package/apps/supervisor-web/dist/assets/jison-wvAkD_A8.js +0 -1
  174. package/apps/supervisor-web/dist/assets/json5-C9tS-k6U.js +0 -1
  175. package/apps/supervisor-web/dist/assets/jsonc-Des-eS-w.js +0 -1
  176. package/apps/supervisor-web/dist/assets/jsonl-DcaNXYhu.js +0 -1
  177. package/apps/supervisor-web/dist/assets/jsonnet-DFQXde-d.js +0 -1
  178. package/apps/supervisor-web/dist/assets/jssm-C2t-YnRu.js +0 -1
  179. package/apps/supervisor-web/dist/assets/julia-CxzCAyBv.js +0 -1
  180. package/apps/supervisor-web/dist/assets/just-Cw27pwNe.js +0 -1
  181. package/apps/supervisor-web/dist/assets/kanagawa-dragon-CkXjmgJE.js +0 -1
  182. package/apps/supervisor-web/dist/assets/kanagawa-lotus-CfQXZHmo.js +0 -1
  183. package/apps/supervisor-web/dist/assets/kanagawa-wave-DWedfzmr.js +0 -1
  184. package/apps/supervisor-web/dist/assets/kdl-DV7GczEv.js +0 -1
  185. package/apps/supervisor-web/dist/assets/kotlin-BdnUsdx6.js +0 -1
  186. package/apps/supervisor-web/dist/assets/kusto-DZf3V79B.js +0 -1
  187. package/apps/supervisor-web/dist/assets/laserwave-DUszq2jm.js +0 -1
  188. package/apps/supervisor-web/dist/assets/latex-CWtU0Tv5.js +0 -1
  189. package/apps/supervisor-web/dist/assets/lean-BZvkOJ9d.js +0 -1
  190. package/apps/supervisor-web/dist/assets/less-B1dDrJ26.js +0 -1
  191. package/apps/supervisor-web/dist/assets/light-plus-B7mTdjB0.js +0 -1
  192. package/apps/supervisor-web/dist/assets/liquid-DYVedYrR.js +0 -1
  193. package/apps/supervisor-web/dist/assets/llvm-DjAJT7YJ.js +0 -1
  194. package/apps/supervisor-web/dist/assets/log-2UxHyX5q.js +0 -1
  195. package/apps/supervisor-web/dist/assets/logo-BtOb2qkB.js +0 -1
  196. package/apps/supervisor-web/dist/assets/lua-BaeVxFsk.js +0 -1
  197. package/apps/supervisor-web/dist/assets/luau-C-HG3fhB.js +0 -1
  198. package/apps/supervisor-web/dist/assets/make-CHLpvVh8.js +0 -1
  199. package/apps/supervisor-web/dist/assets/marko-CnJfTvn9.js +0 -1
  200. package/apps/supervisor-web/dist/assets/material-theme-D5KoaKCx.js +0 -1
  201. package/apps/supervisor-web/dist/assets/material-theme-darker-BfHTSMKl.js +0 -1
  202. package/apps/supervisor-web/dist/assets/material-theme-lighter-B0m2ddpp.js +0 -1
  203. package/apps/supervisor-web/dist/assets/material-theme-ocean-CyktbL80.js +0 -1
  204. package/apps/supervisor-web/dist/assets/material-theme-palenight-Csfq5Kiy.js +0 -1
  205. package/apps/supervisor-web/dist/assets/matlab-D7o27uSR.js +0 -1
  206. package/apps/supervisor-web/dist/assets/mdc-BMNejdWA.js +0 -1
  207. package/apps/supervisor-web/dist/assets/mdx-Cmh6b_Ma.js +0 -1
  208. package/apps/supervisor-web/dist/assets/mermaid-mWjccvbQ.js +0 -1
  209. package/apps/supervisor-web/dist/assets/min-dark-CafNBF8u.js +0 -1
  210. package/apps/supervisor-web/dist/assets/min-light-CTRr51gU.js +0 -1
  211. package/apps/supervisor-web/dist/assets/mipsasm-CKIfxQSi.js +0 -1
  212. package/apps/supervisor-web/dist/assets/mojo-rZm6bMo-.js +0 -1
  213. package/apps/supervisor-web/dist/assets/monokai-D4h5O-jR.js +0 -1
  214. package/apps/supervisor-web/dist/assets/moonbit-_H4v1dQx.js +0 -1
  215. package/apps/supervisor-web/dist/assets/move-IF9eRakj.js +0 -1
  216. package/apps/supervisor-web/dist/assets/narrat-DRg8JJMk.js +0 -1
  217. package/apps/supervisor-web/dist/assets/nextflow-Zz6hmt5N.js +0 -1
  218. package/apps/supervisor-web/dist/assets/nextflow-groovy-BeH2EWoN.js +0 -1
  219. package/apps/supervisor-web/dist/assets/nginx-BpAMiNFr.js +0 -1
  220. package/apps/supervisor-web/dist/assets/night-owl-C39BiMTA.js +0 -1
  221. package/apps/supervisor-web/dist/assets/night-owl-light-CMTm3GFP.js +0 -1
  222. package/apps/supervisor-web/dist/assets/nim-CVrawwO9.js +0 -1
  223. package/apps/supervisor-web/dist/assets/nix-CwoSXNpI.js +0 -1
  224. package/apps/supervisor-web/dist/assets/nord-Ddv68eIx.js +0 -1
  225. package/apps/supervisor-web/dist/assets/nushell-Cz2AlsmD.js +0 -1
  226. package/apps/supervisor-web/dist/assets/objective-c-DXmwc3jG.js +0 -1
  227. package/apps/supervisor-web/dist/assets/objective-cpp-CLxacb5B.js +0 -1
  228. package/apps/supervisor-web/dist/assets/ocaml-C0hk2d4L.js +0 -1
  229. package/apps/supervisor-web/dist/assets/odin-BBf5iR-q.js +0 -1
  230. package/apps/supervisor-web/dist/assets/one-dark-pro-DVMEJ2y_.js +0 -1
  231. package/apps/supervisor-web/dist/assets/one-light-C3Wv6jpd.js +0 -1
  232. package/apps/supervisor-web/dist/assets/openscad-C4EeE6gA.js +0 -1
  233. package/apps/supervisor-web/dist/assets/pascal-D93ZcfNL.js +0 -1
  234. package/apps/supervisor-web/dist/assets/perl-C0TMdlhV.js +0 -1
  235. package/apps/supervisor-web/dist/assets/php-Dhbhpdrm.js +0 -1
  236. package/apps/supervisor-web/dist/assets/pkl-u5AG7uiY.js +0 -1
  237. package/apps/supervisor-web/dist/assets/plastic-3e1v2bzS.js +0 -1
  238. package/apps/supervisor-web/dist/assets/plsql-ChMvpjG-.js +0 -1
  239. package/apps/supervisor-web/dist/assets/po-BTJTHyun.js +0 -1
  240. package/apps/supervisor-web/dist/assets/poimandres-CS3Unz2-.js +0 -1
  241. package/apps/supervisor-web/dist/assets/polar-C0HS_06l.js +0 -1
  242. package/apps/supervisor-web/dist/assets/postcss-CXtECtnM.js +0 -1
  243. package/apps/supervisor-web/dist/assets/powerquery-CEu0bR-o.js +0 -1
  244. package/apps/supervisor-web/dist/assets/powershell-Dpen1YoG.js +0 -1
  245. package/apps/supervisor-web/dist/assets/prisma-Dd19v3D-.js +0 -1
  246. package/apps/supervisor-web/dist/assets/prolog-CbFg5uaA.js +0 -1
  247. package/apps/supervisor-web/dist/assets/proto-C7zT0LnQ.js +0 -1
  248. package/apps/supervisor-web/dist/assets/pug-CGlum2m_.js +0 -1
  249. package/apps/supervisor-web/dist/assets/puppet-BMWR74SV.js +0 -1
  250. package/apps/supervisor-web/dist/assets/purescript-CklMAg4u.js +0 -1
  251. package/apps/supervisor-web/dist/assets/qml-3beO22l8.js +0 -1
  252. package/apps/supervisor-web/dist/assets/qmldir-C8lEn-DE.js +0 -1
  253. package/apps/supervisor-web/dist/assets/qss-IeuSbFQv.js +0 -1
  254. package/apps/supervisor-web/dist/assets/r-Dspwwk_N.js +0 -1
  255. package/apps/supervisor-web/dist/assets/racket-BqYA7rlc.js +0 -1
  256. package/apps/supervisor-web/dist/assets/raku-DXvB9xmW.js +0 -1
  257. package/apps/supervisor-web/dist/assets/razor-Uh8Bk_45.js +0 -1
  258. package/apps/supervisor-web/dist/assets/red-bN70gL4F.js +0 -1
  259. package/apps/supervisor-web/dist/assets/reg-C-SQnVFl.js +0 -1
  260. package/apps/supervisor-web/dist/assets/regexp-CDVJQ6XC.js +0 -1
  261. package/apps/supervisor-web/dist/assets/rel-C3B-1QV4.js +0 -1
  262. package/apps/supervisor-web/dist/assets/riscv-BM1_JUlF.js +0 -1
  263. package/apps/supervisor-web/dist/assets/ron-D8l8udqQ.js +0 -1
  264. package/apps/supervisor-web/dist/assets/rose-pine-dawn-DHQR4-dF.js +0 -1
  265. package/apps/supervisor-web/dist/assets/rose-pine-moon-D4_iv3hh.js +0 -1
  266. package/apps/supervisor-web/dist/assets/rose-pine-qdsjHGoJ.js +0 -1
  267. package/apps/supervisor-web/dist/assets/rosmsg-BJDFO7_C.js +0 -1
  268. package/apps/supervisor-web/dist/assets/rst-BrH8l1NY.js +0 -1
  269. package/apps/supervisor-web/dist/assets/ruby-Dw2BHqvy.js +0 -1
  270. package/apps/supervisor-web/dist/assets/rust-B1yitclQ.js +0 -1
  271. package/apps/supervisor-web/dist/assets/sas-cz2c8ADy.js +0 -1
  272. package/apps/supervisor-web/dist/assets/sass-Cj5Yp3dK.js +0 -1
  273. package/apps/supervisor-web/dist/assets/scala-C151Ov-r.js +0 -1
  274. package/apps/supervisor-web/dist/assets/scheme-C98Dy4si.js +0 -1
  275. package/apps/supervisor-web/dist/assets/scss-OYdSNvt2.js +0 -1
  276. package/apps/supervisor-web/dist/assets/sdbl-DVxCFoDh.js +0 -1
  277. package/apps/supervisor-web/dist/assets/shaderlab-Dg9Lc6iA.js +0 -1
  278. package/apps/supervisor-web/dist/assets/shellsession-BADoaaVG.js +0 -1
  279. package/apps/supervisor-web/dist/assets/slack-dark-BthQWCQV.js +0 -1
  280. package/apps/supervisor-web/dist/assets/slack-ochin-DqwNpetd.js +0 -1
  281. package/apps/supervisor-web/dist/assets/smalltalk-BERRCDM3.js +0 -1
  282. package/apps/supervisor-web/dist/assets/snazzy-light-Bw305WKR.js +0 -1
  283. package/apps/supervisor-web/dist/assets/solarized-dark-DXbdFlpD.js +0 -1
  284. package/apps/supervisor-web/dist/assets/solarized-light-L9t79GZl.js +0 -1
  285. package/apps/supervisor-web/dist/assets/solidity-rGO070M0.js +0 -1
  286. package/apps/supervisor-web/dist/assets/soy-Brmx7dQM.js +0 -1
  287. package/apps/supervisor-web/dist/assets/sparql-rVzFXLq3.js +0 -1
  288. package/apps/supervisor-web/dist/assets/splunk-BtCnVYZw.js +0 -1
  289. package/apps/supervisor-web/dist/assets/ssh-config-_ykCGR6B.js +0 -1
  290. package/apps/supervisor-web/dist/assets/stata-BH5u7GGu.js +0 -1
  291. package/apps/supervisor-web/dist/assets/stylus-BEDo0Tqx.js +0 -1
  292. package/apps/supervisor-web/dist/assets/surrealql-Bq5Q-fJD.js +0 -1
  293. package/apps/supervisor-web/dist/assets/svelte-C_ipcX3V.js +0 -1
  294. package/apps/supervisor-web/dist/assets/swift-D82vCrfD.js +0 -1
  295. package/apps/supervisor-web/dist/assets/synthwave-84-CbfX1IO0.js +0 -1
  296. package/apps/supervisor-web/dist/assets/system-verilog-CnnmHF94.js +0 -1
  297. package/apps/supervisor-web/dist/assets/systemd-4A_iFExJ.js +0 -1
  298. package/apps/supervisor-web/dist/assets/talonscript-CkByrt1z.js +0 -1
  299. package/apps/supervisor-web/dist/assets/tasl-QIJgUcNo.js +0 -1
  300. package/apps/supervisor-web/dist/assets/tcl-dwOrl1Do.js +0 -1
  301. package/apps/supervisor-web/dist/assets/templ-P3uqSqPl.js +0 -1
  302. package/apps/supervisor-web/dist/assets/terraform-BETggiCN.js +0 -1
  303. package/apps/supervisor-web/dist/assets/tex-idrVyKtj.js +0 -1
  304. package/apps/supervisor-web/dist/assets/tokyo-night-hegEt444.js +0 -1
  305. package/apps/supervisor-web/dist/assets/ts-tags-zn1MmPIZ.js +0 -1
  306. package/apps/supervisor-web/dist/assets/tsv-B_m7g4N7.js +0 -1
  307. package/apps/supervisor-web/dist/assets/turtle-BsS91CYL.js +0 -1
  308. package/apps/supervisor-web/dist/assets/twig-DNn4PbVi.js +0 -1
  309. package/apps/supervisor-web/dist/assets/typespec-BGHnOYBU.js +0 -1
  310. package/apps/supervisor-web/dist/assets/typst-DHCkPAjA.js +0 -1
  311. package/apps/supervisor-web/dist/assets/v-BcVCzyr7.js +0 -1
  312. package/apps/supervisor-web/dist/assets/vala-CsfeWuGM.js +0 -1
  313. package/apps/supervisor-web/dist/assets/vb-D17OF-Vu.js +0 -1
  314. package/apps/supervisor-web/dist/assets/verilog-BQ8w6xss.js +0 -1
  315. package/apps/supervisor-web/dist/assets/vesper-DU1UobuO.js +0 -1
  316. package/apps/supervisor-web/dist/assets/vhdl-CeAyd5Ju.js +0 -1
  317. package/apps/supervisor-web/dist/assets/viml-CJc9bBzg.js +0 -1
  318. package/apps/supervisor-web/dist/assets/vitesse-black-Bkuqu6BP.js +0 -1
  319. package/apps/supervisor-web/dist/assets/vitesse-dark-D0r3Knsf.js +0 -1
  320. package/apps/supervisor-web/dist/assets/vitesse-light-CVO1_9PV.js +0 -1
  321. package/apps/supervisor-web/dist/assets/vue-DN_0RTcg.js +0 -1
  322. package/apps/supervisor-web/dist/assets/vue-html-AaS7Mt5G.js +0 -1
  323. package/apps/supervisor-web/dist/assets/vue-vine-CQOfvN7w.js +0 -1
  324. package/apps/supervisor-web/dist/assets/vyper-CDx5xZoG.js +0 -1
  325. package/apps/supervisor-web/dist/assets/wasm-CG6Dc4jp.js +0 -1
  326. package/apps/supervisor-web/dist/assets/wasm-MzD3tlZU.js +0 -1
  327. package/apps/supervisor-web/dist/assets/wenyan-BV7otONQ.js +0 -1
  328. package/apps/supervisor-web/dist/assets/wgsl-Dx-B1_4e.js +0 -1
  329. package/apps/supervisor-web/dist/assets/wikitext-BhOHFoWU.js +0 -1
  330. package/apps/supervisor-web/dist/assets/wit-5i3qLPDT.js +0 -1
  331. package/apps/supervisor-web/dist/assets/wolfram-lXgVvXCa.js +0 -1
  332. package/apps/supervisor-web/dist/assets/xml-sdJ4AIDG.js +0 -1
  333. package/apps/supervisor-web/dist/assets/xsl-CtQFsRM5.js +0 -1
  334. package/apps/supervisor-web/dist/assets/zenscript-DVFEvuxE.js +0 -1
  335. package/apps/supervisor-web/dist/assets/zig-VOosw3JB.js +0 -1
  336. /package/apps/{supervisor-api/dist/worker-index.d.ts → relay-server/dist/index.d.ts} +0 -0
@@ -0,0 +1,1221 @@
1
+ // src/index.ts
2
+ import fs4 from "fs";
3
+ import { ZodError } from "zod";
4
+
5
+ // src/app.ts
6
+ import websocket from "@fastify/websocket";
7
+ import Fastify from "fastify";
8
+ import fs2 from "fs";
9
+ import fsp2 from "fs/promises";
10
+ import path2 from "path";
11
+ import { randomUUID } from "crypto";
12
+ import { z } from "zod";
13
+
14
+ // src/request-broker.ts
15
+ var RelayRequestBroker = class {
16
+ constructor(timeoutMs) {
17
+ this.timeoutMs = timeoutMs;
18
+ }
19
+ timeoutMs;
20
+ pendingRequests = /* @__PURE__ */ new Map();
21
+ forward(socket, message) {
22
+ return new Promise((resolve, reject) => {
23
+ if (message.type !== "relay.request") {
24
+ reject(new Error("Only relay.request messages can be forwarded."));
25
+ return;
26
+ }
27
+ const timeout = setTimeout(() => {
28
+ this.pendingRequests.delete(message.requestId);
29
+ reject(new Error("Supervisor relay request timed out."));
30
+ }, this.timeoutMs);
31
+ this.pendingRequests.set(message.requestId, {
32
+ resolve,
33
+ reject,
34
+ timeout
35
+ });
36
+ socket.send(JSON.stringify(message));
37
+ });
38
+ }
39
+ accept(message) {
40
+ if (message.type !== "relay.response") {
41
+ return false;
42
+ }
43
+ const pending = this.pendingRequests.get(message.requestId);
44
+ if (!pending) {
45
+ return false;
46
+ }
47
+ clearTimeout(pending.timeout);
48
+ this.pendingRequests.delete(message.requestId);
49
+ pending.resolve(message.payload);
50
+ return true;
51
+ }
52
+ rejectAll(error) {
53
+ for (const [requestId, pending] of this.pendingRequests) {
54
+ clearTimeout(pending.timeout);
55
+ this.pendingRequests.delete(requestId);
56
+ pending.reject(error);
57
+ }
58
+ }
59
+ };
60
+
61
+ // src/relay-store.ts
62
+ import crypto from "crypto";
63
+ import fs from "fs";
64
+ import fsp from "fs/promises";
65
+ import path from "path";
66
+ var SESSION_TTL_MS = 1e3 * 60 * 60 * 24 * 14;
67
+ var RelayStore = class _RelayStore {
68
+ constructor(filePath, sessionSecret, registrationEnabled) {
69
+ this.filePath = filePath;
70
+ this.sessionSecret = sessionSecret;
71
+ this.data = this.readData(registrationEnabled);
72
+ }
73
+ filePath;
74
+ sessionSecret;
75
+ data;
76
+ static fromDataDir(dataDir, sessionSecret, registrationEnabled) {
77
+ return new _RelayStore(
78
+ path.join(path.resolve(dataDir), "relay-store.json"),
79
+ sessionSecret,
80
+ registrationEnabled
81
+ );
82
+ }
83
+ seedAdmin(input) {
84
+ const username = normalizeUsername(input.username);
85
+ const existing = this.data.users.find((user2) => user2.role === "admin");
86
+ if (existing) {
87
+ return this.publicUser(existing);
88
+ }
89
+ const user = this.createStoredUser({
90
+ email: input.email ?? `${username}@relay.local`,
91
+ username,
92
+ password: input.password,
93
+ role: "admin"
94
+ });
95
+ this.data.users.push(user);
96
+ void this.persist();
97
+ return this.publicUser(user);
98
+ }
99
+ register(input) {
100
+ if (!this.data.registrationEnabled) {
101
+ throw new RelayStoreError(403, "forbidden", "Registration is currently disabled.");
102
+ }
103
+ const user = this.createStoredUser({
104
+ email: input.email,
105
+ username: input.username,
106
+ password: input.password,
107
+ role: "user"
108
+ });
109
+ this.data.users.push(user);
110
+ void this.persist();
111
+ return this.createLoginResult(user);
112
+ }
113
+ login(input) {
114
+ const normalizedIdentifier = input.identifier.trim().toLowerCase();
115
+ const user = this.data.users.find(
116
+ (entry) => entry.email.toLowerCase() === normalizedIdentifier || entry.username.toLowerCase() === normalizedIdentifier
117
+ );
118
+ if (!user || !user.enabled) {
119
+ throw new RelayStoreError(401, "unauthorized", "Invalid username or password.");
120
+ }
121
+ if (!verifySecret(input.password, user.passwordSalt, user.passwordHash)) {
122
+ throw new RelayStoreError(401, "unauthorized", "Invalid username or password.");
123
+ }
124
+ return this.createLoginResult(user);
125
+ }
126
+ verifySession(token) {
127
+ if (!token) {
128
+ return this.emptySession();
129
+ }
130
+ const payload = this.verifyToken(token);
131
+ if (!payload) {
132
+ return this.emptySession();
133
+ }
134
+ const user = this.data.users.find((entry) => entry.id === payload.userId);
135
+ if (!user || !user.enabled) {
136
+ return this.emptySession();
137
+ }
138
+ return {
139
+ authenticated: true,
140
+ user: this.publicUser(user),
141
+ registrationEnabled: this.data.registrationEnabled
142
+ };
143
+ }
144
+ createDevice(ownerUserId, input) {
145
+ const user = this.requireUser(ownerUserId);
146
+ const token = `rcd_${crypto.randomBytes(24).toString("base64url")}`;
147
+ const device = {
148
+ id: crypto.randomUUID(),
149
+ ownerUserId: user.id,
150
+ name: input.name.trim() || "Remote Codex device",
151
+ tokenHash: sha256(token),
152
+ tokenPreview: previewToken(token),
153
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
154
+ };
155
+ this.data.devices.push(device);
156
+ void this.persist();
157
+ return {
158
+ device: this.publicDevice(device, null),
159
+ token
160
+ };
161
+ }
162
+ deleteDevice(userId, deviceId) {
163
+ const index = this.data.devices.findIndex(
164
+ (device) => device.id === deviceId && device.ownerUserId === userId
165
+ );
166
+ if (index < 0) {
167
+ throw new RelayStoreError(404, "not_found", "Device was not found.");
168
+ }
169
+ this.data.devices.splice(index, 1);
170
+ this.data.shares = this.data.shares.filter((share) => share.deviceId !== deviceId);
171
+ void this.persist();
172
+ }
173
+ verifyDeviceToken(token) {
174
+ if (!token) {
175
+ return null;
176
+ }
177
+ const tokenHash = sha256(token);
178
+ return this.data.devices.find((device) => device.tokenHash === tokenHash) ?? null;
179
+ }
180
+ createShare(ownerUserId, input) {
181
+ const owner = this.requireUser(ownerUserId);
182
+ const device = this.data.devices.find(
183
+ (entry) => entry.id === input.deviceId && entry.ownerUserId === ownerUserId
184
+ );
185
+ if (!device) {
186
+ throw new RelayStoreError(404, "not_found", "Device was not found.");
187
+ }
188
+ const target = this.data.users.find(
189
+ (entry) => entry.username.toLowerCase() === input.targetUsername.trim().toLowerCase()
190
+ );
191
+ if (!target || !target.enabled) {
192
+ throw new RelayStoreError(404, "not_found", "Target user was not found.");
193
+ }
194
+ if (target.id === ownerUserId) {
195
+ throw new RelayStoreError(400, "bad_request", "You cannot share a session with yourself.");
196
+ }
197
+ const existing = this.data.shares.find(
198
+ (share2) => share2.ownerUserId === ownerUserId && share2.targetUserId === target.id && share2.deviceId === input.deviceId && share2.threadId === input.threadId && !share2.revokedAt
199
+ );
200
+ if (existing) {
201
+ return existing;
202
+ }
203
+ const share = {
204
+ id: crypto.randomUUID(),
205
+ ownerUserId,
206
+ ownerUsername: owner.username,
207
+ targetUserId: target.id,
208
+ targetUsername: target.username,
209
+ deviceId: input.deviceId,
210
+ deviceName: device.name,
211
+ threadId: input.threadId,
212
+ label: input.label?.trim() || null,
213
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
214
+ revokedAt: null
215
+ };
216
+ this.data.shares.push(share);
217
+ void this.persist();
218
+ return share;
219
+ }
220
+ revokeShare(userId, shareId) {
221
+ const share = this.data.shares.find(
222
+ (entry) => entry.id === shareId && entry.ownerUserId === userId
223
+ );
224
+ if (!share) {
225
+ throw new RelayStoreError(404, "not_found", "Share was not found.");
226
+ }
227
+ share.revokedAt = (/* @__PURE__ */ new Date()).toISOString();
228
+ void this.persist();
229
+ return share;
230
+ }
231
+ canAccessDevice(userId, deviceId, threadId) {
232
+ const owned = this.data.devices.some(
233
+ (device) => device.id === deviceId && device.ownerUserId === userId
234
+ );
235
+ if (owned) {
236
+ return true;
237
+ }
238
+ if (!threadId) {
239
+ return false;
240
+ }
241
+ return this.data.shares.some(
242
+ (share) => share.targetUserId === userId && share.deviceId === deviceId && !share.revokedAt && share.threadId === threadId
243
+ );
244
+ }
245
+ portalSummary(userId, connectedDevices) {
246
+ const user = this.requireUser(userId);
247
+ return {
248
+ user: this.publicUser(user),
249
+ devices: this.data.devices.filter((device) => device.ownerUserId === userId).map((device) => this.publicDevice(device, connectedDevices.get(device.id) ?? null)),
250
+ sharedWithMe: this.data.shares.filter(
251
+ (share) => share.targetUserId === userId && !share.revokedAt
252
+ ).map((share) => this.publicShare(share)),
253
+ sharedByMe: this.data.shares.filter(
254
+ (share) => share.ownerUserId === userId && !share.revokedAt
255
+ ).map((share) => this.publicShare(share))
256
+ };
257
+ }
258
+ adminSummary(connectedDevices) {
259
+ return {
260
+ users: this.data.users.map((user) => this.publicUser(user)),
261
+ devices: this.data.devices.map(
262
+ (device) => this.publicDevice(device, connectedDevices.get(device.id) ?? null)
263
+ ),
264
+ registrationEnabled: this.data.registrationEnabled
265
+ };
266
+ }
267
+ setRegistrationEnabled(enabled) {
268
+ this.data.registrationEnabled = enabled;
269
+ void this.persist();
270
+ return enabled;
271
+ }
272
+ setUserEnabled(userId, enabled) {
273
+ const user = this.requireUser(userId);
274
+ if (user.role === "admin" && !enabled) {
275
+ throw new RelayStoreError(400, "bad_request", "The admin user cannot be disabled.");
276
+ }
277
+ user.enabled = enabled;
278
+ void this.persist();
279
+ return this.publicUser(user);
280
+ }
281
+ emptySession() {
282
+ return {
283
+ authenticated: false,
284
+ user: null,
285
+ registrationEnabled: this.data.registrationEnabled
286
+ };
287
+ }
288
+ publicDevice(device, status) {
289
+ return {
290
+ id: device.id,
291
+ ownerUserId: device.ownerUserId,
292
+ name: device.name,
293
+ tokenPreview: device.tokenPreview,
294
+ connected: Boolean(status?.connected),
295
+ connectedAt: status?.connectedAt ?? null,
296
+ lastHeartbeatAt: status?.lastHeartbeatAt ?? null,
297
+ createdAt: device.createdAt
298
+ };
299
+ }
300
+ publicShare(share) {
301
+ const owner = this.data.users.find((user) => user.id === share.ownerUserId);
302
+ const target = this.data.users.find((user) => user.id === share.targetUserId);
303
+ const device = this.data.devices.find((entry) => entry.id === share.deviceId);
304
+ return {
305
+ ...share,
306
+ ownerUsername: share.ownerUsername ?? owner?.username ?? "unknown",
307
+ targetUsername: share.targetUsername ?? target?.username ?? "unknown",
308
+ deviceName: share.deviceName ?? device?.name ?? "Remote Codex device"
309
+ };
310
+ }
311
+ createStoredUser(input) {
312
+ const email = input.email.trim().toLowerCase();
313
+ const username = normalizeUsername(input.username);
314
+ if (!email.includes("@")) {
315
+ throw new RelayStoreError(400, "bad_request", "A valid email address is required.");
316
+ }
317
+ if (username.length < 3) {
318
+ throw new RelayStoreError(400, "bad_request", "Username must be at least 3 characters.");
319
+ }
320
+ if (input.password.length < 8) {
321
+ throw new RelayStoreError(400, "bad_request", "Password must be at least 8 characters.");
322
+ }
323
+ if (this.data.users.some(
324
+ (user) => user.email.toLowerCase() === email || user.username.toLowerCase() === username
325
+ )) {
326
+ throw new RelayStoreError(409, "conflict", "A user with that email or username already exists.");
327
+ }
328
+ const passwordSalt = crypto.randomBytes(16).toString("base64url");
329
+ return {
330
+ id: crypto.randomUUID(),
331
+ email,
332
+ username,
333
+ role: input.role,
334
+ enabled: true,
335
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
336
+ passwordSalt,
337
+ passwordHash: hashSecret(input.password, passwordSalt)
338
+ };
339
+ }
340
+ createLoginResult(user) {
341
+ const token = this.signSession(user.id);
342
+ return {
343
+ token,
344
+ session: {
345
+ authenticated: true,
346
+ user: this.publicUser(user),
347
+ registrationEnabled: this.data.registrationEnabled
348
+ }
349
+ };
350
+ }
351
+ signSession(userId) {
352
+ const payload = {
353
+ userId,
354
+ expiresAt: Date.now() + SESSION_TTL_MS,
355
+ nonce: crypto.randomBytes(16).toString("base64url")
356
+ };
357
+ const payloadText = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
358
+ const signature = crypto.createHmac("sha256", this.sessionSecret).update(payloadText).digest("base64url");
359
+ return `${payloadText}.${signature}`;
360
+ }
361
+ verifyToken(token) {
362
+ const [payloadText, signature, extra] = token.split(".");
363
+ if (!payloadText || !signature || extra !== void 0) {
364
+ return null;
365
+ }
366
+ const expected = crypto.createHmac("sha256", this.sessionSecret).update(payloadText).digest("base64url");
367
+ if (!safeEqual(signature, expected)) {
368
+ return null;
369
+ }
370
+ try {
371
+ const payload = JSON.parse(Buffer.from(payloadText, "base64url").toString("utf8"));
372
+ if (typeof payload?.userId !== "string" || typeof payload?.expiresAt !== "number" || typeof payload?.nonce !== "string" || payload.expiresAt <= Date.now()) {
373
+ return null;
374
+ }
375
+ return payload;
376
+ } catch {
377
+ return null;
378
+ }
379
+ }
380
+ requireUser(userId) {
381
+ const user = this.data.users.find((entry) => entry.id === userId);
382
+ if (!user) {
383
+ throw new RelayStoreError(404, "not_found", "User was not found.");
384
+ }
385
+ return user;
386
+ }
387
+ publicUser(user) {
388
+ return {
389
+ id: user.id,
390
+ email: user.email,
391
+ username: user.username,
392
+ role: user.role,
393
+ enabled: user.enabled,
394
+ createdAt: user.createdAt
395
+ };
396
+ }
397
+ readData(registrationEnabled) {
398
+ try {
399
+ const parsed = JSON.parse(fs.readFileSync(this.filePath, "utf8"));
400
+ return {
401
+ registrationEnabled: typeof parsed.registrationEnabled === "boolean" ? parsed.registrationEnabled : registrationEnabled,
402
+ users: Array.isArray(parsed.users) ? parsed.users : [],
403
+ devices: Array.isArray(parsed.devices) ? parsed.devices : [],
404
+ shares: Array.isArray(parsed.shares) ? parsed.shares : []
405
+ };
406
+ } catch {
407
+ return {
408
+ registrationEnabled,
409
+ users: [],
410
+ devices: [],
411
+ shares: []
412
+ };
413
+ }
414
+ }
415
+ async persist() {
416
+ await fsp.mkdir(path.dirname(this.filePath), { recursive: true });
417
+ await fsp.writeFile(this.filePath, `${JSON.stringify(this.data, null, 2)}
418
+ `, "utf8");
419
+ }
420
+ };
421
+ var RelayStoreError = class extends Error {
422
+ constructor(statusCode, code, message) {
423
+ super(message);
424
+ this.statusCode = statusCode;
425
+ this.code = code;
426
+ }
427
+ statusCode;
428
+ code;
429
+ };
430
+ function normalizeUsername(value) {
431
+ return value.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "");
432
+ }
433
+ function hashSecret(secret, salt) {
434
+ return crypto.scryptSync(secret, salt, 32).toString("base64url");
435
+ }
436
+ function verifySecret(secret, salt, hash) {
437
+ return safeEqual(hashSecret(secret, salt), hash);
438
+ }
439
+ function sha256(value) {
440
+ return crypto.createHash("sha256").update(value).digest("base64url");
441
+ }
442
+ function previewToken(token) {
443
+ return `${token.slice(0, 7)}...${token.slice(-4)}`;
444
+ }
445
+ function safeEqual(left, right) {
446
+ const leftBuffer = Buffer.from(left);
447
+ const rightBuffer = Buffer.from(right);
448
+ if (leftBuffer.length !== rightBuffer.length) {
449
+ return false;
450
+ }
451
+ return crypto.timingSafeEqual(leftBuffer, rightBuffer);
452
+ }
453
+
454
+ // src/app.ts
455
+ var RELAY_REQUEST_TIMEOUT_MS = 3e4;
456
+ var WEBSOCKET_OPEN = 1;
457
+ var RELAY_COOKIE_NAME = "remote_codex_relay_session";
458
+ var THREAD_SHARED_HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PATCH"]);
459
+ var loginSchema = z.object({
460
+ identifier: z.string().trim().min(1),
461
+ password: z.string().min(1)
462
+ });
463
+ var registerSchema = z.object({
464
+ email: z.string().trim().email(),
465
+ username: z.string().trim().min(3),
466
+ password: z.string().min(8)
467
+ });
468
+ var createDeviceSchema = z.object({
469
+ name: z.string().trim().min(1).max(120)
470
+ });
471
+ var createShareSchema = z.object({
472
+ targetUsername: z.string().trim().min(3),
473
+ deviceId: z.string().uuid(),
474
+ threadId: z.string().trim().min(1),
475
+ label: z.string().trim().min(1).max(160).optional()
476
+ });
477
+ var setEnabledSchema = z.object({
478
+ enabled: z.boolean()
479
+ });
480
+ function buildRelayServer(config2) {
481
+ const app2 = Fastify({ logger: false });
482
+ const store = RelayStore.fromDataDir(
483
+ config2.dataDir,
484
+ config2.sessionSecret,
485
+ config2.registrationEnabled
486
+ );
487
+ store.seedAdmin({
488
+ username: config2.adminUsername,
489
+ email: config2.adminEmail,
490
+ password: config2.adminPassword
491
+ });
492
+ const state = {
493
+ supervisors: /* @__PURE__ */ new Map()
494
+ };
495
+ app2.get("/healthz", async () => {
496
+ const primary = [...state.supervisors.values()][0] ?? null;
497
+ return {
498
+ status: "ok",
499
+ supervisorConnected: state.supervisors.size > 0,
500
+ supervisorConnectedAt: primary?.connectedAt ?? null,
501
+ lastSupervisorHeartbeatAt: primary?.lastHeartbeatAt ?? null,
502
+ supervisorCount: state.supervisors.size
503
+ };
504
+ });
505
+ app2.get("/relay/auth/session", async (request) => {
506
+ return store.verifySession(readRelaySessionToken(request));
507
+ });
508
+ app2.post("/relay/auth/register", async (request, reply) => {
509
+ const body = registerSchema.parse(request.body ?? {});
510
+ const result = store.register(body);
511
+ attachRelayCookie(reply, result.token);
512
+ return result;
513
+ });
514
+ app2.post("/relay/auth/login", async (request, reply) => {
515
+ const body = loginSchema.parse(request.body ?? {});
516
+ const result = store.login(body);
517
+ attachRelayCookie(reply, result.token);
518
+ return result;
519
+ });
520
+ app2.post("/relay/auth/logout", async (_request, reply) => {
521
+ clearRelayCookie(reply);
522
+ return store.emptySession();
523
+ });
524
+ app2.get("/relay/portal", async (request, reply) => {
525
+ const user = requireRelayUser(request, reply, store);
526
+ if (!user) {
527
+ return;
528
+ }
529
+ return store.portalSummary(user.id, connectionStatus(state));
530
+ });
531
+ app2.post("/relay/devices", async (request, reply) => {
532
+ const user = requireRelayUser(request, reply, store);
533
+ if (!user) {
534
+ return;
535
+ }
536
+ const body = createDeviceSchema.parse(request.body ?? {});
537
+ return store.createDevice(user.id, body);
538
+ });
539
+ app2.delete("/relay/devices/:deviceId", async (request, reply) => {
540
+ const user = requireRelayUser(request, reply, store);
541
+ if (!user) {
542
+ return;
543
+ }
544
+ const { deviceId } = z.object({ deviceId: z.string().uuid() }).parse(request.params);
545
+ store.deleteDevice(user.id, deviceId);
546
+ return { id: deviceId };
547
+ });
548
+ app2.post("/relay/shares", async (request, reply) => {
549
+ const user = requireRelayUser(request, reply, store);
550
+ if (!user) {
551
+ return;
552
+ }
553
+ const body = createShareSchema.parse(request.body ?? {});
554
+ return store.createShare(user.id, {
555
+ targetUsername: body.targetUsername,
556
+ deviceId: body.deviceId,
557
+ threadId: body.threadId,
558
+ label: body.label ?? null
559
+ });
560
+ });
561
+ app2.delete("/relay/shares/:shareId", async (request, reply) => {
562
+ const user = requireRelayUser(request, reply, store);
563
+ if (!user) {
564
+ return;
565
+ }
566
+ const { shareId } = z.object({ shareId: z.string().uuid() }).parse(request.params);
567
+ return store.revokeShare(user.id, shareId);
568
+ });
569
+ app2.get("/relay/admin", async (request, reply) => {
570
+ const user = requireRelayUser(request, reply, store, { admin: true });
571
+ if (!user) {
572
+ return;
573
+ }
574
+ return store.adminSummary(connectionStatus(state));
575
+ });
576
+ app2.patch("/relay/admin/settings/registration", async (request, reply) => {
577
+ const user = requireRelayUser(request, reply, store, { admin: true });
578
+ if (!user) {
579
+ return;
580
+ }
581
+ const body = setEnabledSchema.parse(request.body ?? {});
582
+ return { registrationEnabled: store.setRegistrationEnabled(body.enabled) };
583
+ });
584
+ app2.patch("/relay/admin/users/:userId", async (request, reply) => {
585
+ const user = requireRelayUser(request, reply, store, { admin: true });
586
+ if (!user) {
587
+ return;
588
+ }
589
+ const { userId } = z.object({ userId: z.string().uuid() }).parse(request.params);
590
+ const body = setEnabledSchema.parse(request.body ?? {});
591
+ return store.setUserEnabled(userId, body.enabled);
592
+ });
593
+ app2.all("/relay/devices/:deviceId/api/*", async (request, reply) => {
594
+ const user = requireRelayUser(request, reply, store);
595
+ if (!user) {
596
+ return;
597
+ }
598
+ const { deviceId } = z.object({ deviceId: z.string().uuid() }).parse(request.params);
599
+ const targetPath = request.url.replace(/^\/relay\/devices\/[^/]+/, "") || "/";
600
+ await forwardRelayHttp({
601
+ request,
602
+ reply,
603
+ state,
604
+ store,
605
+ user,
606
+ deviceId,
607
+ targetPath
608
+ });
609
+ });
610
+ app2.all("/relay/api/*", async (request, reply) => {
611
+ const user = requireRelayUser(request, reply, store);
612
+ if (!user) {
613
+ return;
614
+ }
615
+ const deviceId = firstAccessibleConnectedDevice(state, store, user.id);
616
+ if (!deviceId) {
617
+ reply.status(503).send({
618
+ code: "service_unavailable",
619
+ message: "No accessible supervisor is connected to this relay."
620
+ });
621
+ return;
622
+ }
623
+ const targetPath = request.url.slice("/relay".length) || "/";
624
+ await forwardRelayHttp({
625
+ request,
626
+ reply,
627
+ state,
628
+ store,
629
+ user,
630
+ deviceId,
631
+ targetPath
632
+ });
633
+ });
634
+ app2.register(async (realtimeApp) => {
635
+ await realtimeApp.register(websocket);
636
+ realtimeApp.route({
637
+ method: "GET",
638
+ url: "/supervisor/tunnel",
639
+ handler: (_request, reply) => {
640
+ reply.status(426).send({
641
+ code: "bad_request",
642
+ message: "Upgrade to websocket is required."
643
+ });
644
+ },
645
+ wsHandler: (socket, request) => {
646
+ const legacyToken = bearerToken(request.headers.authorization) ?? queryToken(request.query);
647
+ const deviceToken = queryToken(request.query, "deviceToken") ?? legacyToken;
648
+ const device = store.verifyDeviceToken(deviceToken);
649
+ const deviceId = device?.id ?? (config2.supervisorToken && legacyToken === config2.supervisorToken ? "legacy-default" : null);
650
+ if (!deviceId) {
651
+ socket.close(1008, "Supervisor relay token is invalid.");
652
+ return;
653
+ }
654
+ const connectedAt = (/* @__PURE__ */ new Date()).toISOString();
655
+ const existing = state.supervisors.get(deviceId);
656
+ existing?.socket.send(JSON.stringify({
657
+ type: "relay.connected",
658
+ timestamp: connectedAt,
659
+ deviceId
660
+ }));
661
+ existing?.clientSockets.forEach((clientConnection) => clientConnection.socket.close());
662
+ const connection = {
663
+ deviceId,
664
+ socket,
665
+ requestBroker: new RelayRequestBroker(RELAY_REQUEST_TIMEOUT_MS),
666
+ clientSockets: /* @__PURE__ */ new Map(),
667
+ connected: true,
668
+ connectedAt,
669
+ lastHeartbeatAt: connectedAt
670
+ };
671
+ state.supervisors.set(deviceId, connection);
672
+ socket.send(
673
+ JSON.stringify({
674
+ type: "relay.connected",
675
+ timestamp: connectedAt,
676
+ deviceId
677
+ })
678
+ );
679
+ socket.on("message", (rawMessage) => {
680
+ let parsed;
681
+ try {
682
+ parsed = JSON.parse(rawMessage.toString());
683
+ } catch {
684
+ return;
685
+ }
686
+ if (parsed.type === "relay.heartbeat") {
687
+ connection.lastHeartbeatAt = parsed.timestamp;
688
+ return;
689
+ }
690
+ if (parsed.type === "relay.server.message") {
691
+ const clientConnection = connection.clientSockets.get(parsed.clientId);
692
+ if (clientConnection && clientConnection.socket.readyState === WEBSOCKET_OPEN && shouldForwardSocketEvent(parsed.payload, clientConnection.threadId)) {
693
+ clientConnection.socket.send(JSON.stringify(parsed.payload));
694
+ }
695
+ return;
696
+ }
697
+ connection.requestBroker.accept(parsed);
698
+ });
699
+ socket.on("close", () => {
700
+ if (state.supervisors.get(deviceId)?.socket === socket) {
701
+ state.supervisors.delete(deviceId);
702
+ }
703
+ connection.requestBroker.rejectAll(new Error("Supervisor tunnel closed."));
704
+ for (const [clientId, clientConnection] of connection.clientSockets) {
705
+ connection.clientSockets.delete(clientId);
706
+ clientConnection.socket.close();
707
+ }
708
+ });
709
+ }
710
+ });
711
+ realtimeApp.route({
712
+ method: "GET",
713
+ url: "/relay/devices/:deviceId/ws",
714
+ handler: (_request, reply) => {
715
+ reply.status(426).send({
716
+ code: "bad_request",
717
+ message: "Upgrade to websocket is required."
718
+ });
719
+ },
720
+ wsHandler: (socket, request) => {
721
+ const session = store.verifySession(readRelaySessionToken(request));
722
+ const deviceId = pathParam(request.params, "deviceId");
723
+ const threadId = queryString(request.query, "threadId");
724
+ if (!session.authenticated || !session.user || !deviceId) {
725
+ socket.close(1008, "Relay login is required.");
726
+ return;
727
+ }
728
+ if (!store.canAccessDevice(session.user.id, deviceId, threadId)) {
729
+ socket.close(1008, "Device access is not allowed.");
730
+ return;
731
+ }
732
+ const supervisor = state.supervisors.get(deviceId);
733
+ if (!supervisor || supervisor.socket.readyState !== WEBSOCKET_OPEN) {
734
+ socket.close(1013, "No supervisor is connected for this device.");
735
+ return;
736
+ }
737
+ connectRelayWebsocket(supervisor, socket, threadId);
738
+ }
739
+ });
740
+ realtimeApp.route({
741
+ method: "GET",
742
+ url: "/relay/ws",
743
+ handler: (_request, reply) => {
744
+ reply.status(426).send({
745
+ code: "bad_request",
746
+ message: "Upgrade to websocket is required."
747
+ });
748
+ },
749
+ wsHandler: (socket, request) => {
750
+ const session = store.verifySession(readRelaySessionToken(request));
751
+ if (!session.authenticated || !session.user) {
752
+ socket.close(1008, "Relay login is required.");
753
+ return;
754
+ }
755
+ const threadId = queryString(request.query, "threadId");
756
+ const deviceId = firstAccessibleConnectedDevice(state, store, session.user.id, threadId);
757
+ const supervisor = deviceId ? state.supervisors.get(deviceId) : null;
758
+ if (!deviceId || !supervisor || supervisor.socket.readyState !== WEBSOCKET_OPEN) {
759
+ socket.close(1013, "No accessible supervisor is connected to this relay.");
760
+ return;
761
+ }
762
+ connectRelayWebsocket(supervisor, socket, threadId);
763
+ }
764
+ });
765
+ });
766
+ if (config2.webDistDir) {
767
+ registerRelayWebApp(app2, config2.webDistDir);
768
+ }
769
+ app2.setErrorHandler((error, _request, reply) => {
770
+ if (error instanceof RelayStoreError) {
771
+ reply.status(error.statusCode).send({
772
+ code: error.code,
773
+ message: error.message
774
+ });
775
+ return;
776
+ }
777
+ if (error instanceof z.ZodError) {
778
+ reply.status(400).send({
779
+ code: "bad_request",
780
+ message: "The request payload is invalid.",
781
+ details: {
782
+ issues: error.issues
783
+ }
784
+ });
785
+ return;
786
+ }
787
+ reply.status(500).send({
788
+ code: "internal_error",
789
+ message: "An unexpected relay server error occurred."
790
+ });
791
+ });
792
+ return app2;
793
+ }
794
+ async function forwardRelayHttp(input) {
795
+ const threadId = threadIdFromPath(input.targetPath);
796
+ if (!input.store.canAccessDevice(input.user.id, input.deviceId, threadId)) {
797
+ input.reply.status(403).send({
798
+ code: "forbidden",
799
+ message: "Device access is not allowed."
800
+ });
801
+ return;
802
+ }
803
+ if (!isAllowedForRelayUser(input.store, input.user.id, input.deviceId, input.request.method, input.targetPath)) {
804
+ input.reply.status(403).send({
805
+ code: "forbidden",
806
+ message: "This shared session does not allow that operation."
807
+ });
808
+ return;
809
+ }
810
+ const supervisor = input.state.supervisors.get(input.deviceId);
811
+ if (!supervisor || supervisor.socket.readyState !== WEBSOCKET_OPEN) {
812
+ input.reply.status(503).send({
813
+ code: "service_unavailable",
814
+ message: "No supervisor is connected for this device."
815
+ });
816
+ return;
817
+ }
818
+ if (!isAllowedRelayTarget(input.targetPath)) {
819
+ input.reply.status(403).send({
820
+ code: "forbidden",
821
+ message: "This relay path is not allowed."
822
+ });
823
+ return;
824
+ }
825
+ try {
826
+ const requestId = randomUUID();
827
+ const response = await supervisor.requestBroker.forward(supervisor.socket, {
828
+ type: "relay.request",
829
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
830
+ requestId,
831
+ deviceId: input.deviceId,
832
+ payload: {
833
+ method: input.request.method,
834
+ path: input.targetPath,
835
+ headers: relayRequestHeaders(input.request.headers),
836
+ body: relayRequestBody(input.request.body)
837
+ }
838
+ });
839
+ for (const [name, value] of Object.entries(response.headers)) {
840
+ if (canForwardResponseHeader(name)) {
841
+ input.reply.header(name, value);
842
+ }
843
+ }
844
+ input.reply.status(response.statusCode).send(response.body);
845
+ } catch (error) {
846
+ const message = error instanceof Error ? error.message : "Relay request failed.";
847
+ input.reply.status(message.includes("timed out") ? 504 : 503).send({
848
+ code: "service_unavailable",
849
+ message
850
+ });
851
+ }
852
+ }
853
+ function connectRelayWebsocket(supervisor, socket, threadId) {
854
+ const clientId = randomUUID();
855
+ supervisor.clientSockets.set(clientId, { socket, threadId });
856
+ sendToSupervisor(supervisor, {
857
+ type: "relay.client.connected",
858
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
859
+ clientId
860
+ });
861
+ socket.on("message", (rawMessage) => {
862
+ let payload;
863
+ try {
864
+ payload = JSON.parse(rawMessage.toString());
865
+ } catch {
866
+ return;
867
+ }
868
+ sendToSupervisor(supervisor, {
869
+ type: "relay.client.message",
870
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
871
+ clientId,
872
+ payload
873
+ });
874
+ });
875
+ socket.on("close", () => {
876
+ supervisor.clientSockets.delete(clientId);
877
+ sendToSupervisor(supervisor, {
878
+ type: "relay.client.disconnected",
879
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
880
+ clientId
881
+ });
882
+ });
883
+ }
884
+ function sendToSupervisor(supervisor, message) {
885
+ if (supervisor.socket.readyState === WEBSOCKET_OPEN) {
886
+ supervisor.socket.send(JSON.stringify(message));
887
+ }
888
+ }
889
+ function registerRelayWebApp(app2, distDirInput) {
890
+ const distDir = path2.resolve(distDirInput);
891
+ const indexFile = path2.join(distDir, "index.html");
892
+ const mimeTypes = /* @__PURE__ */ new Map([
893
+ [".css", "text/css; charset=utf-8"],
894
+ [".html", "text/html; charset=utf-8"],
895
+ [".js", "text/javascript; charset=utf-8"],
896
+ [".json", "application/json; charset=utf-8"],
897
+ [".png", "image/png"],
898
+ [".svg", "image/svg+xml"],
899
+ [".webp", "image/webp"],
900
+ [".woff", "font/woff"],
901
+ [".woff2", "font/woff2"]
902
+ ]);
903
+ app2.get("/*", async (request, reply) => {
904
+ if (!fs2.existsSync(indexFile)) {
905
+ reply.status(503).type("text/plain; charset=utf-8").send("Relay web frontend is not built.");
906
+ return;
907
+ }
908
+ const pathname = new URL(request.url, "http://relay.local").pathname;
909
+ if (pathname.startsWith("/relay/") || pathname === "/healthz" || pathname === "/supervisor/tunnel") {
910
+ reply.status(404).send({
911
+ code: "not_found",
912
+ message: "The requested relay endpoint was not found."
913
+ });
914
+ return;
915
+ }
916
+ const assetPath = await resolveAssetPath(distDir, indexFile, pathname);
917
+ if (!assetPath) {
918
+ reply.status(404).type("text/plain; charset=utf-8").send("Not Found");
919
+ return;
920
+ }
921
+ if (assetPath === indexFile) {
922
+ const html = await fsp2.readFile(indexFile, "utf8");
923
+ const payload = injectRelayBootstrap(html);
924
+ reply.header("cache-control", "no-cache").header("content-length", Buffer.byteLength(payload)).type("text/html; charset=utf-8");
925
+ return reply.send(payload);
926
+ }
927
+ const stat = await fsp2.stat(assetPath);
928
+ reply.header("cache-control", "public, max-age=31536000, immutable").header("content-length", stat.size).type(mimeTypes.get(path2.extname(assetPath).toLowerCase()) ?? "application/octet-stream");
929
+ return reply.send(fs2.createReadStream(assetPath));
930
+ });
931
+ }
932
+ function injectRelayBootstrap(html) {
933
+ const script = `<script>window.__REMOTE_CODEX_BOOTSTRAP__=${JSON.stringify({
934
+ mode: "relay",
935
+ relayApiBase: "/relay"
936
+ })};</script>`;
937
+ if (html.includes("</head>")) {
938
+ return html.replace("</head>", `${script}</head>`);
939
+ }
940
+ return `${script}${html}`;
941
+ }
942
+ async function resolveAssetPath(distDir, indexFile, pathname) {
943
+ let decodedPath;
944
+ try {
945
+ decodedPath = decodeURIComponent(pathname);
946
+ } catch {
947
+ return null;
948
+ }
949
+ const relativePath = decodedPath === "/" ? "index.html" : decodedPath.replace(/^\/+/, "");
950
+ const candidate = path2.resolve(distDir, relativePath);
951
+ if (candidate !== distDir && !candidate.startsWith(`${distDir}${path2.sep}`)) {
952
+ return null;
953
+ }
954
+ const stat = await safeStat(candidate);
955
+ if (stat?.isFile()) {
956
+ return candidate;
957
+ }
958
+ if (path2.posix.extname(decodedPath)) {
959
+ return null;
960
+ }
961
+ return indexFile;
962
+ }
963
+ function requireRelayUser(request, reply, store, options = {}) {
964
+ const session = store.verifySession(readRelaySessionToken(request));
965
+ if (!session.authenticated || !session.user) {
966
+ reply.status(401).send({
967
+ code: "unauthorized",
968
+ message: "Relay login is required."
969
+ });
970
+ return null;
971
+ }
972
+ if (options.admin && session.user.role !== "admin") {
973
+ reply.status(403).send({
974
+ code: "forbidden",
975
+ message: "Admin access is required."
976
+ });
977
+ return null;
978
+ }
979
+ request.relayUser = session.user;
980
+ return session.user;
981
+ }
982
+ function readRelaySessionToken(request) {
983
+ return bearerToken(request.headers.authorization) ?? queryToken(request.query, "relaySession") ?? readCookie(request.headers.cookie, RELAY_COOKIE_NAME);
984
+ }
985
+ function attachRelayCookie(reply, token) {
986
+ reply.header(
987
+ "set-cookie",
988
+ `${RELAY_COOKIE_NAME}=${encodeURIComponent(token)}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${60 * 60 * 24 * 14}`
989
+ );
990
+ }
991
+ function clearRelayCookie(reply) {
992
+ reply.header(
993
+ "set-cookie",
994
+ `${RELAY_COOKIE_NAME}=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0`
995
+ );
996
+ }
997
+ function connectionStatus(state) {
998
+ const statuses = /* @__PURE__ */ new Map();
999
+ for (const [deviceId, supervisor] of state.supervisors) {
1000
+ statuses.set(deviceId, {
1001
+ connected: true,
1002
+ connectedAt: supervisor.connectedAt,
1003
+ lastHeartbeatAt: supervisor.lastHeartbeatAt
1004
+ });
1005
+ }
1006
+ return statuses;
1007
+ }
1008
+ function firstAccessibleConnectedDevice(state, store, userId, threadId) {
1009
+ for (const deviceId of state.supervisors.keys()) {
1010
+ if (store.canAccessDevice(userId, deviceId, threadId)) {
1011
+ return deviceId;
1012
+ }
1013
+ }
1014
+ return null;
1015
+ }
1016
+ function isAllowedRelayTarget(pathValue) {
1017
+ const pathname = new URL(pathValue, "http://relay.local").pathname;
1018
+ return pathname === "/healthz" || pathname.startsWith("/api/");
1019
+ }
1020
+ function threadIdFromPath(pathValue) {
1021
+ const pathname = new URL(pathValue, "http://relay.local").pathname;
1022
+ const match = /^\/api\/threads\/([^/?#]+)/.exec(pathname);
1023
+ return match ? decodeURIComponent(match[1]) : null;
1024
+ }
1025
+ function isAllowedForRelayUser(store, userId, deviceId, method, pathValue) {
1026
+ if (store.canAccessDevice(userId, deviceId, null)) {
1027
+ return true;
1028
+ }
1029
+ const pathname = new URL(pathValue, "http://relay.local").pathname;
1030
+ const threadId = threadIdFromPath(pathValue);
1031
+ if (!threadId || !store.canAccessDevice(userId, deviceId, threadId)) {
1032
+ return false;
1033
+ }
1034
+ if (!THREAD_SHARED_HTTP_METHODS.has(method.toUpperCase())) {
1035
+ return false;
1036
+ }
1037
+ const escapedThreadId = escapeRegExp(encodeURIComponent(threadId));
1038
+ const allowed = [
1039
+ new RegExp(`^/api/threads/${escapedThreadId}$`),
1040
+ new RegExp(`^/api/threads/${escapedThreadId}/items/[^/]+/detail$`),
1041
+ new RegExp(`^/api/threads/${escapedThreadId}/export-turns$`),
1042
+ new RegExp(`^/api/threads/${escapedThreadId}/goal$`),
1043
+ new RegExp(`^/api/threads/${escapedThreadId}/skills$`),
1044
+ new RegExp(`^/api/threads/${escapedThreadId}/mcp-servers$`),
1045
+ new RegExp(`^/api/threads/${escapedThreadId}/hooks$`),
1046
+ new RegExp(`^/api/threads/${escapedThreadId}/resume$`),
1047
+ new RegExp(`^/api/threads/${escapedThreadId}/prompt$`),
1048
+ new RegExp(`^/api/threads/${escapedThreadId}/interrupt$`),
1049
+ new RegExp(`^/api/threads/${escapedThreadId}/requests/[^/]+/respond$`)
1050
+ ];
1051
+ return allowed.some((pattern) => pattern.test(pathname));
1052
+ }
1053
+ function shouldForwardSocketEvent(event, threadId) {
1054
+ if (!threadId) {
1055
+ return true;
1056
+ }
1057
+ if (event.type === "supervisor.connected" || event.type === "supervisor.pong") {
1058
+ return true;
1059
+ }
1060
+ return "threadId" in event && event.threadId === threadId;
1061
+ }
1062
+ function relayRequestBody(body) {
1063
+ if (body === void 0 || body === null) {
1064
+ return null;
1065
+ }
1066
+ if (typeof body === "string") {
1067
+ return body;
1068
+ }
1069
+ if (Buffer.isBuffer(body)) {
1070
+ return body.toString("utf8");
1071
+ }
1072
+ return JSON.stringify(body);
1073
+ }
1074
+ function relayRequestHeaders(headers) {
1075
+ const output = {};
1076
+ for (const [name, value] of Object.entries(headers)) {
1077
+ if (name.toLowerCase() === "authorization") {
1078
+ continue;
1079
+ }
1080
+ if (Array.isArray(value)) {
1081
+ output[name] = value.join(", ");
1082
+ } else if (value !== void 0) {
1083
+ output[name] = value;
1084
+ }
1085
+ }
1086
+ return output;
1087
+ }
1088
+ function canForwardResponseHeader(name) {
1089
+ const lower = name.toLowerCase();
1090
+ return lower !== "content-length" && lower !== "transfer-encoding";
1091
+ }
1092
+ function bearerToken(value) {
1093
+ const match = /^Bearer\s+(.+)$/i.exec(value ?? "");
1094
+ return match?.[1]?.trim() ?? null;
1095
+ }
1096
+ function queryToken(query, name = "token") {
1097
+ if (!query || typeof query !== "object" || !(name in query)) {
1098
+ return null;
1099
+ }
1100
+ const token = query[name];
1101
+ return typeof token === "string" && token.trim() ? token.trim() : null;
1102
+ }
1103
+ function readCookie(cookie, name) {
1104
+ if (!cookie) {
1105
+ return null;
1106
+ }
1107
+ for (const entry of cookie.split(";")) {
1108
+ const [entryName, ...valueParts] = entry.trim().split("=");
1109
+ if (entryName === name) {
1110
+ return decodeURIComponent(valueParts.join("="));
1111
+ }
1112
+ }
1113
+ return null;
1114
+ }
1115
+ function pathParam(params, name) {
1116
+ if (!params || typeof params !== "object" || !(name in params)) {
1117
+ return null;
1118
+ }
1119
+ const value = params[name];
1120
+ return typeof value === "string" ? value : null;
1121
+ }
1122
+ function queryString(query, name) {
1123
+ if (!query || typeof query !== "object" || !(name in query)) {
1124
+ return null;
1125
+ }
1126
+ const value = query[name];
1127
+ if (typeof value !== "string") {
1128
+ return null;
1129
+ }
1130
+ const trimmed = value.trim();
1131
+ return trimmed ? trimmed : null;
1132
+ }
1133
+ function escapeRegExp(value) {
1134
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1135
+ }
1136
+ async function safeStat(filePath) {
1137
+ try {
1138
+ return await fsp2.stat(filePath);
1139
+ } catch {
1140
+ return null;
1141
+ }
1142
+ }
1143
+
1144
+ // src/config.ts
1145
+ import fs3 from "fs";
1146
+ import path3 from "path";
1147
+ import { fileURLToPath } from "url";
1148
+ import { z as z2 } from "zod";
1149
+ var envSchema = z2.object({
1150
+ HOST: z2.string().min(1).optional(),
1151
+ PORT: z2.coerce.number().int().positive().optional(),
1152
+ REMOTE_CODEX_RELAY_SUPERVISOR_TOKEN: z2.string().min(1).optional(),
1153
+ REMOTE_CODEX_RELAY_CLIENT_TOKEN: z2.string().min(1).optional(),
1154
+ REMOTE_CODEX_ADMIN_USERNAME: z2.string().min(3),
1155
+ REMOTE_CODEX_ADMIN_PASSWORD: z2.string().min(8),
1156
+ REMOTE_CODEX_ADMIN_EMAIL: z2.string().email().optional(),
1157
+ REMOTE_CODEX_RELAY_DATA_DIR: z2.string().min(1).optional(),
1158
+ REMOTE_CODEX_RELAY_SESSION_SECRET: z2.string().min(16).optional(),
1159
+ REMOTE_CODEX_RELAY_REGISTRATION_ENABLED: z2.string().optional(),
1160
+ REMOTE_CODEX_RELAY_WEB_DIST_DIR: z2.string().min(1).optional()
1161
+ });
1162
+ function loadRelayServerConfig(env = process.env) {
1163
+ const parsed = envSchema.parse(env);
1164
+ return {
1165
+ host: parsed.HOST ?? "0.0.0.0",
1166
+ port: parsed.PORT ?? 8788,
1167
+ supervisorToken: parsed.REMOTE_CODEX_RELAY_SUPERVISOR_TOKEN ?? null,
1168
+ clientToken: parsed.REMOTE_CODEX_RELAY_CLIENT_TOKEN ?? null,
1169
+ adminUsername: parsed.REMOTE_CODEX_ADMIN_USERNAME,
1170
+ adminEmail: parsed.REMOTE_CODEX_ADMIN_EMAIL ?? `${parsed.REMOTE_CODEX_ADMIN_USERNAME}@relay.local`,
1171
+ adminPassword: parsed.REMOTE_CODEX_ADMIN_PASSWORD,
1172
+ dataDir: parsed.REMOTE_CODEX_RELAY_DATA_DIR ?? ".local/relay-server",
1173
+ sessionSecret: parsed.REMOTE_CODEX_RELAY_SESSION_SECRET ?? parsed.REMOTE_CODEX_ADMIN_PASSWORD,
1174
+ registrationEnabled: parsed.REMOTE_CODEX_RELAY_REGISTRATION_ENABLED === void 0 ? true : ["1", "true", "yes", "on"].includes(
1175
+ parsed.REMOTE_CODEX_RELAY_REGISTRATION_ENABLED.toLowerCase()
1176
+ ),
1177
+ webDistDir: parsed.REMOTE_CODEX_RELAY_WEB_DIST_DIR ?? defaultRelayWebDistDir()
1178
+ };
1179
+ }
1180
+ function defaultRelayWebDistDir() {
1181
+ const moduleDir = path3.dirname(fileURLToPath(import.meta.url));
1182
+ const candidates = [
1183
+ path3.resolve("apps/supervisor-web/dist"),
1184
+ path3.resolve(moduleDir, "../../supervisor-web/dist")
1185
+ ];
1186
+ for (const candidate of candidates) {
1187
+ if (fs3.existsSync(path3.join(candidate, "index.html"))) {
1188
+ return candidate;
1189
+ }
1190
+ }
1191
+ return null;
1192
+ }
1193
+
1194
+ // src/index.ts
1195
+ if (fs4.existsSync(".env")) {
1196
+ process.loadEnvFile?.(".env");
1197
+ }
1198
+ var config;
1199
+ try {
1200
+ config = loadRelayServerConfig();
1201
+ } catch (error) {
1202
+ if (error instanceof ZodError) {
1203
+ console.error("Remote Codex relay configuration is invalid.");
1204
+ for (const issue of error.issues) {
1205
+ const name = issue.path.join(".") || "environment";
1206
+ console.error(`- ${name}: ${issue.message}`);
1207
+ }
1208
+ console.error("");
1209
+ console.error("Required: REMOTE_CODEX_ADMIN_USERNAME, REMOTE_CODEX_ADMIN_PASSWORD");
1210
+ console.error("Optional: REMOTE_CODEX_ADMIN_EMAIL, REMOTE_CODEX_RELAY_SUPERVISOR_TOKEN, REMOTE_CODEX_RELAY_CLIENT_TOKEN, REMOTE_CODEX_RELAY_SESSION_SECRET, REMOTE_CODEX_RELAY_DATA_DIR, REMOTE_CODEX_RELAY_WEB_DIST_DIR, HOST, PORT");
1211
+ process.exit(1);
1212
+ }
1213
+ throw error;
1214
+ }
1215
+ var app = buildRelayServer(config);
1216
+ app.listen({ host: config.host, port: config.port }).then(() => {
1217
+ app.log.info(`Remote Codex relay listening on http://${config.host}:${config.port}`);
1218
+ }).catch((error) => {
1219
+ app.log.error(error);
1220
+ process.exit(1);
1221
+ });