9remote 2.5.5 → 3.0.0

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 (597) hide show
  1. package/README.md +91 -172
  2. package/dist/assets/trayIcon.ico +0 -0
  3. package/dist/cli.cjs +102 -101
  4. package/dist/ptyDaemon.cjs +10 -9
  5. package/dist/server.cjs +307 -138
  6. package/dist/ui/agents/codex.svg +1 -1
  7. package/dist/ui/assets/index-CAvf0VPf.css +1 -0
  8. package/dist/ui/assets/index-EXcI2V_p.js +8 -0
  9. package/dist/ui/favicon.ico +0 -0
  10. package/dist/ui/index.html +12 -4
  11. package/dist/vendor/scrcpy-server-v3.1 +0 -0
  12. package/dist/web/404.html +1 -0
  13. package/dist/web/__next.__PAGE__.txt +9 -0
  14. package/dist/web/__next._full.txt +25 -0
  15. package/dist/web/__next._head.txt +6 -0
  16. package/dist/web/__next._index.txt +10 -0
  17. package/dist/web/__next._tree.txt +5 -0
  18. package/dist/web/_next/static/AUHtJgkYALYyNwHop9pgT/_buildManifest.js +1 -0
  19. package/dist/web/_next/static/AUHtJgkYALYyNwHop9pgT/_ssgManifest.js +1 -0
  20. package/dist/web/_next/static/chunks/0a7753a9.4cb02a98e2e453b9.js +1 -0
  21. package/dist/web/_next/static/chunks/1222.aa4f7f7f397533e9.js +1 -0
  22. package/dist/web/_next/static/chunks/1228.d291278320bca9ca.js +1 -0
  23. package/dist/web/_next/static/chunks/1243.f35c2e6494079e05.js +1 -0
  24. package/dist/web/_next/static/chunks/1311.8c6b8930e22ceea0.js +1 -0
  25. package/dist/web/_next/static/chunks/132.6032826021079bce.js +1 -0
  26. package/dist/web/_next/static/chunks/133.1106654a28b5afda.js +1 -0
  27. package/dist/web/_next/static/chunks/1385.086b560acb0d7a68.js +115 -0
  28. package/dist/web/_next/static/chunks/1642.cac26d30ac331cb4.js +1 -0
  29. package/dist/web/_next/static/chunks/1733.f06c240b3ef8a428.js +77 -0
  30. package/dist/web/_next/static/chunks/1750.f7ba3332b56a8e68.js +1 -0
  31. package/dist/web/_next/static/chunks/1763.28beedca593b5e19.js +138 -0
  32. package/dist/web/_next/static/chunks/179.b9e8f385bd414d1c.js +62 -0
  33. package/dist/web/_next/static/chunks/1838.7c8990f903e3524a.js +215 -0
  34. package/dist/web/_next/static/chunks/20154aaa.868648f838e03bf1.js +1 -0
  35. package/dist/web/_next/static/chunks/2038.640b672702093b24.js +1 -0
  36. package/dist/web/_next/static/chunks/2112.d72eefbdf81659b3.js +23 -0
  37. package/dist/web/_next/static/chunks/2125.61c6e4a3541e30ad.js +1 -0
  38. package/dist/web/_next/static/chunks/2129.61afb2f4ff59e46a.js +24 -0
  39. package/dist/web/_next/static/chunks/213.68ce1361aee3281a.js +1 -0
  40. package/dist/web/_next/static/chunks/2179.3f55be0b79e3cca7.js +1 -0
  41. package/dist/web/_next/static/chunks/2180.e6bad8186f52ff89.js +1 -0
  42. package/dist/web/_next/static/chunks/2264.0fc1474d26067a01.js +1 -0
  43. package/dist/web/_next/static/chunks/2376.ed6302a91a9de1ae.js +36 -0
  44. package/dist/web/_next/static/chunks/2545.3ff192a933baf11e.js +1 -0
  45. package/dist/web/_next/static/chunks/2620.d17f4fac0eec88a5.js +1 -0
  46. package/dist/web/_next/static/chunks/2847.d3124853ca727b78.js +1 -0
  47. package/dist/web/_next/static/chunks/2849.f2bcad51c68e44b5.js +1 -0
  48. package/dist/web/_next/static/chunks/3189.64c6546d621eec0b.js +1 -0
  49. package/dist/web/_next/static/chunks/3348-9ef29f9cedf6c0df.js +1 -0
  50. package/dist/web/_next/static/chunks/3429.436b229808b33e81.js +1 -0
  51. package/dist/web/_next/static/chunks/3514.9643e815d690f2fb.js +1 -0
  52. package/dist/web/_next/static/chunks/3552.8dd9a3d2c3bcef65.js +86 -0
  53. package/dist/web/_next/static/chunks/3606.b59e695fd05b4eb3.js +74 -0
  54. package/dist/web/_next/static/chunks/3692.8c6b8930e22ceea0.js +1 -0
  55. package/dist/web/_next/static/chunks/3736.4a5662ceeecc6acb.js +94 -0
  56. package/dist/web/_next/static/chunks/392.842b338137824bb8.js +1 -0
  57. package/dist/web/_next/static/chunks/3973-9b18bcd9afc0bcb4.js +1 -0
  58. package/dist/web/_next/static/chunks/3977.8cfd8dec4cb8e893.js +1 -0
  59. package/dist/web/_next/static/chunks/4090.441fa61655da9b38.js +1 -0
  60. package/dist/web/_next/static/chunks/4303.54c4aa0b89834fc6.js +1 -0
  61. package/dist/web/_next/static/chunks/4314.6bbd89507909cad1.js +1 -0
  62. package/dist/web/_next/static/chunks/4551.d0d6723cacfb90b9.js +226 -0
  63. package/dist/web/_next/static/chunks/4566.d8e009823b8647d1.js +90 -0
  64. package/dist/web/_next/static/chunks/4643.dbb152682d076617.js +1 -0
  65. package/dist/web/_next/static/chunks/4732-76a574510722b2c3.js +11 -0
  66. package/dist/web/_next/static/chunks/4924.3f5c77a103ac69a9.js +8 -0
  67. package/dist/web/_next/static/chunks/4930.42e564644c94a93b.js +1 -0
  68. package/dist/web/_next/static/chunks/5155.d7e8db52ab4c95e0.js +27 -0
  69. package/dist/web/_next/static/chunks/5158-267908d069475cdc.js +32 -0
  70. package/dist/web/_next/static/chunks/543.5636e97aa68eb9c8.js +41 -0
  71. package/dist/web/_next/static/chunks/5506.98304a1821200439.js +1 -0
  72. package/dist/web/_next/static/chunks/5622.5ead5e748ecedbd1.js +1 -0
  73. package/dist/web/_next/static/chunks/565f33a6-fd3410bd29f39fdf.js +1 -0
  74. package/dist/web/_next/static/chunks/5708.22b4531ceebf3eca.js +1 -0
  75. package/dist/web/_next/static/chunks/5804.ff72a927135eb74c.js +93 -0
  76. package/dist/web/_next/static/chunks/588.78e2528ff24df85c.js +287 -0
  77. package/dist/web/_next/static/chunks/6001.e5bcb28bb9ce6571.js +1 -0
  78. package/dist/web/_next/static/chunks/6075.a6b8434732056526.js +1 -0
  79. package/dist/web/_next/static/chunks/6180.82fa683aa16f21df.js +1 -0
  80. package/dist/web/_next/static/chunks/6274.a3795ca6dacb3d62.js +32 -0
  81. package/dist/web/_next/static/chunks/635-d6ae421935055337.js +1 -0
  82. package/dist/web/_next/static/chunks/6419.842467d9a877e770.js +1 -0
  83. package/dist/web/_next/static/chunks/6420.bfa7bc86d03dcefc.js +1 -0
  84. package/dist/web/_next/static/chunks/6552-145e634541b5b66c.js +1 -0
  85. package/dist/web/_next/static/chunks/6647.740ec32a32201eab.js +64 -0
  86. package/dist/web/_next/static/chunks/6730.88fc6f2ccc49496c.js +1 -0
  87. package/dist/web/_next/static/chunks/6739.82570eba0a02cb26.js +1 -0
  88. package/dist/web/_next/static/chunks/674.2b5ffad0676aedbe.js +1 -0
  89. package/dist/web/_next/static/chunks/6742.8e137138a5d1c9e1.js +1 -0
  90. package/dist/web/_next/static/chunks/6987.743a63813c6288ce.js +79 -0
  91. package/dist/web/_next/static/chunks/7202.dad90ce4a1d78f67.js +1 -0
  92. package/dist/web/_next/static/chunks/7233.0f252f6c1cce8058.js +1 -0
  93. package/dist/web/_next/static/chunks/7248.d89bf328a0dc1b4c.js +1 -0
  94. package/dist/web/_next/static/chunks/7263.6f224c3924155294.js +1 -0
  95. package/dist/web/_next/static/chunks/7341.6958eac888a96823.js +34 -0
  96. package/dist/web/_next/static/chunks/7424.c8c9f85c8ea65885.js +1 -0
  97. package/dist/web/_next/static/chunks/7507.1204ae29f46e3f01.js +1 -0
  98. package/dist/web/_next/static/chunks/7629-16de8d4154923623.js +1 -0
  99. package/dist/web/_next/static/chunks/7811.19082afa63eb73ba.js +1 -0
  100. package/dist/web/_next/static/chunks/7853.d01982fa0afc1ee2.js +24 -0
  101. package/dist/web/_next/static/chunks/7908.432ab9feeb9ae756.js +1 -0
  102. package/dist/web/_next/static/chunks/7960.3b1c77b882ba4bb1.js +1 -0
  103. package/dist/web/_next/static/chunks/7967.6c62be01fad1603c.js +1 -0
  104. package/dist/web/_next/static/chunks/7c89255b.f727562586fa21cc.js +153 -0
  105. package/dist/web/_next/static/chunks/8044.5f87392585c9f3e4.js +38 -0
  106. package/dist/web/_next/static/chunks/8054.feeb01b8acdb31d4.js +1 -0
  107. package/dist/web/_next/static/chunks/8115.68d46fb961c724e0.js +172 -0
  108. package/dist/web/_next/static/chunks/817.cd0f4d221eca4c8b.js +104 -0
  109. package/dist/web/_next/static/chunks/8334-0e9493cfa1547244.js +1 -0
  110. package/dist/web/_next/static/chunks/8467.f34d938a32d40325.js +1 -0
  111. package/dist/web/_next/static/chunks/8495.d6cea1be03635fa8.js +148 -0
  112. package/dist/web/_next/static/chunks/8516-b3694c9156666b12.js +152 -0
  113. package/dist/web/_next/static/chunks/8638.03c6480d4aaeb5b3.js +1 -0
  114. package/dist/web/_next/static/chunks/8716.1ad6febb58e8560e.js +1 -0
  115. package/dist/web/_next/static/chunks/8839.1ad64faf06fb4a33.js +1 -0
  116. package/dist/web/_next/static/chunks/8922-a9cee4a104876005.js +1 -0
  117. package/dist/web/_next/static/chunks/910.43be9c3be85c7fa0.js +21 -0
  118. package/dist/web/_next/static/chunks/925.7ef504ee39f11840.js +1 -0
  119. package/dist/web/_next/static/chunks/9259.f4337cbb56e2e0b6.js +1 -0
  120. package/dist/web/_next/static/chunks/9299.af2a95f6ece07482.js +1 -0
  121. package/dist/web/_next/static/chunks/9387.96263a2d1602a078.js +1 -0
  122. package/dist/web/_next/static/chunks/9568-0bfadca4fcb105aa.js +1 -0
  123. package/dist/web/_next/static/chunks/9641.f6f2982fc4b4735f.js +1 -0
  124. package/dist/web/_next/static/chunks/9675.be4a175f92aa2f26.js +1 -0
  125. package/dist/web/_next/static/chunks/9860.05fa134ae44699e0.js +1 -0
  126. package/dist/web/_next/static/chunks/9b87412a.d2cfae4121762dec.js +122 -0
  127. package/dist/web/_next/static/chunks/a012e9f0.7dbb2fe05ec0b816.js +82 -0
  128. package/dist/web/_next/static/chunks/a096f98a-5a93977f5f5de413.js +1 -0
  129. package/dist/web/_next/static/chunks/ab1da478.d051e56375dab6d8.js +1 -0
  130. package/dist/web/_next/static/chunks/adcd17ec-9a2791632136b67b.js +18 -0
  131. package/dist/web/_next/static/chunks/app/_global-error/page-5bd6526483106080.js +1 -0
  132. package/dist/web/_next/static/chunks/app/_not-found/page-877523282abd5a7c.js +1 -0
  133. package/dist/web/_next/static/chunks/app/admin/admins/page-0f20b93a1fbe900c.js +1 -0
  134. package/dist/web/_next/static/chunks/app/admin/login/page-dbcfb21ebbd0bb00.js +1 -0
  135. package/dist/web/_next/static/chunks/app/admin/modes/page-8f292692166b56fc.js +1 -0
  136. package/dist/web/_next/static/chunks/app/admin/ota/page-c9fe76f24ac08288.js +1 -0
  137. package/dist/web/_next/static/chunks/app/admin/page-622381f5dac39ddb.js +1 -0
  138. package/dist/web/_next/static/chunks/app/admin/turn/page-8f6b835a319f3fe3.js +1 -0
  139. package/dist/web/_next/static/chunks/app/dev/question/page-5fd80ae293617365.js +1 -0
  140. package/dist/web/_next/static/chunks/app/layout-341400810c81f67e.js +1 -0
  141. package/dist/web/_next/static/chunks/app/login/page-31786ee656da76b1.js +1 -0
  142. package/dist/web/_next/static/chunks/app/page-1cf39061b48db156.js +22 -0
  143. package/dist/web/_next/static/chunks/app/workspace/browse/page-5bd6526483106080.js +1 -0
  144. package/dist/web/_next/static/chunks/app/workspace/editor/page-5bd6526483106080.js +1 -0
  145. package/dist/web/_next/static/chunks/app/workspace/files/page-5bd6526483106080.js +1 -0
  146. package/dist/web/_next/static/chunks/app/workspace/git/page-5bd6526483106080.js +1 -0
  147. package/dist/web/_next/static/chunks/app/workspace/layout-5e8f95fbc4ebaa6b.js +14 -0
  148. package/dist/web/_next/static/chunks/app/workspace/mobile/page-5bd6526483106080.js +1 -0
  149. package/dist/web/_next/static/chunks/app/workspace/page-5bd6526483106080.js +1 -0
  150. package/dist/web/_next/static/chunks/app/workspace/remote/page-5bd6526483106080.js +1 -0
  151. package/dist/web/_next/static/chunks/app/workspace/terminal/[sessionId]/page-5bd6526483106080.js +1 -0
  152. package/dist/web/_next/static/chunks/app/workspace/workspaces/page-5bd6526483106080.js +1 -0
  153. package/dist/web/_next/static/chunks/bbfbfb6c.069db3c4cc981ed3.js +1 -0
  154. package/dist/web/_next/static/chunks/c7879cf7-0d16fce1674078f9.js +1 -0
  155. package/dist/web/_next/static/chunks/c7ea4e13.6153499d4c2fb1f2.js +1 -0
  156. package/dist/web/_next/static/chunks/cdcce140.7a440644c3dd52b8.js +1 -0
  157. package/dist/web/_next/static/chunks/da9a53d6.df25e193f1f5bcae.js +62 -0
  158. package/dist/web/_next/static/chunks/framework-42053c69543a55f9.js +1 -0
  159. package/dist/web/_next/static/chunks/main-255c95c36766ce86.js +5 -0
  160. package/dist/web/_next/static/chunks/main-app-1831d4f902c3fcb9.js +1 -0
  161. package/dist/web/_next/static/chunks/next/dist/client/components/builtin/app-error-5bd6526483106080.js +1 -0
  162. package/dist/web/_next/static/chunks/next/dist/client/components/builtin/forbidden-5bd6526483106080.js +1 -0
  163. package/dist/web/_next/static/chunks/next/dist/client/components/builtin/global-error-76800531999827bd.js +1 -0
  164. package/dist/web/_next/static/chunks/next/dist/client/components/builtin/not-found-5bd6526483106080.js +1 -0
  165. package/dist/web/_next/static/chunks/next/dist/client/components/builtin/unauthorized-5bd6526483106080.js +1 -0
  166. package/dist/web/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  167. package/dist/web/_next/static/chunks/webpack-c971919003362f96.js +1 -0
  168. package/dist/web/_next/static/css/56adcd4d72bf2d84.css +1 -0
  169. package/dist/web/_next/static/css/5c40e58137a11306.css +1 -0
  170. package/dist/web/_next/static/css/b496025c9cd768cc.css +1 -0
  171. package/dist/web/_next/static/media/013b72fa676f92e0-s.woff2 +0 -0
  172. package/dist/web/_next/static/media/22a5144ee8d83bca-s.p.woff2 +0 -0
  173. package/dist/web/_next/static/media/2b5b02fc7e511755-s.woff2 +0 -0
  174. package/dist/web/_next/static/media/65f03d54ccadf4a8-s.woff2 +0 -0
  175. package/dist/web/_next/static/media/7d4881bb7e1bf84d-s.p.woff2 +0 -0
  176. package/dist/web/_next/static/media/9766a7e9e2e0ad5a-s.woff2 +0 -0
  177. package/dist/web/_next/static/media/aa016aab0e6d1295-s.woff2 +0 -0
  178. package/dist/web/_next/static/media/b66cf8e69499582a-s.woff2 +0 -0
  179. package/dist/web/_next/static/media/b9408752a0c24fb9-s.woff2 +0 -0
  180. package/dist/web/_next/static/media/e038a29029a234f2-s.woff2 +0 -0
  181. package/dist/web/_next/static/media/f639721981034f88-s.woff2 +0 -0
  182. package/dist/web/_not-found/__next._full.txt +23 -0
  183. package/dist/web/_not-found/__next._head.txt +6 -0
  184. package/dist/web/_not-found/__next._index.txt +10 -0
  185. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +5 -0
  186. package/dist/web/_not-found/__next._not-found.txt +5 -0
  187. package/dist/web/_not-found/__next._tree.txt +5 -0
  188. package/dist/web/_not-found.html +1 -0
  189. package/dist/web/_not-found.txt +23 -0
  190. package/dist/web/admin/__next._full.txt +27 -0
  191. package/dist/web/admin/__next._head.txt +6 -0
  192. package/dist/web/admin/__next._index.txt +10 -0
  193. package/dist/web/admin/__next._tree.txt +5 -0
  194. package/dist/web/admin/__next.admin.__PAGE__.txt +9 -0
  195. package/dist/web/admin/__next.admin.txt +5 -0
  196. package/dist/web/admin/admins/__next._full.txt +27 -0
  197. package/dist/web/admin/admins/__next._head.txt +6 -0
  198. package/dist/web/admin/admins/__next._index.txt +10 -0
  199. package/dist/web/admin/admins/__next._tree.txt +5 -0
  200. package/dist/web/admin/admins/__next.admin.admins.__PAGE__.txt +9 -0
  201. package/dist/web/admin/admins/__next.admin.admins.txt +5 -0
  202. package/dist/web/admin/admins/__next.admin.txt +5 -0
  203. package/dist/web/admin/admins.html +1 -0
  204. package/dist/web/admin/admins.txt +27 -0
  205. package/dist/web/admin/login/__next._full.txt +27 -0
  206. package/dist/web/admin/login/__next._head.txt +6 -0
  207. package/dist/web/admin/login/__next._index.txt +10 -0
  208. package/dist/web/admin/login/__next._tree.txt +5 -0
  209. package/dist/web/admin/login/__next.admin.login.__PAGE__.txt +9 -0
  210. package/dist/web/admin/login/__next.admin.login.txt +5 -0
  211. package/dist/web/admin/login/__next.admin.txt +5 -0
  212. package/dist/web/admin/login.html +1 -0
  213. package/dist/web/admin/login.txt +27 -0
  214. package/dist/web/admin/modes/__next._full.txt +27 -0
  215. package/dist/web/admin/modes/__next._head.txt +6 -0
  216. package/dist/web/admin/modes/__next._index.txt +10 -0
  217. package/dist/web/admin/modes/__next._tree.txt +5 -0
  218. package/dist/web/admin/modes/__next.admin.modes.__PAGE__.txt +9 -0
  219. package/dist/web/admin/modes/__next.admin.modes.txt +5 -0
  220. package/dist/web/admin/modes/__next.admin.txt +5 -0
  221. package/dist/web/admin/modes.html +1 -0
  222. package/dist/web/admin/modes.txt +27 -0
  223. package/dist/web/admin/ota/__next._full.txt +27 -0
  224. package/dist/web/admin/ota/__next._head.txt +6 -0
  225. package/dist/web/admin/ota/__next._index.txt +10 -0
  226. package/dist/web/admin/ota/__next._tree.txt +5 -0
  227. package/dist/web/admin/ota/__next.admin.ota.__PAGE__.txt +9 -0
  228. package/dist/web/admin/ota/__next.admin.ota.txt +5 -0
  229. package/dist/web/admin/ota/__next.admin.txt +5 -0
  230. package/dist/web/admin/ota.html +1 -0
  231. package/dist/web/admin/ota.txt +27 -0
  232. package/dist/web/admin/turn/__next._full.txt +27 -0
  233. package/dist/web/admin/turn/__next._head.txt +6 -0
  234. package/dist/web/admin/turn/__next._index.txt +10 -0
  235. package/dist/web/admin/turn/__next._tree.txt +5 -0
  236. package/dist/web/admin/turn/__next.admin.turn.__PAGE__.txt +9 -0
  237. package/dist/web/admin/turn/__next.admin.turn.txt +5 -0
  238. package/dist/web/admin/turn/__next.admin.txt +5 -0
  239. package/dist/web/admin/turn.html +1 -0
  240. package/dist/web/admin/turn.txt +27 -0
  241. package/dist/web/admin.html +1 -0
  242. package/dist/web/admin.txt +27 -0
  243. package/dist/web/agent-icons/aider.svg +1 -0
  244. package/dist/web/agent-icons/amp.png +0 -0
  245. package/dist/web/agent-icons/ante.png +0 -0
  246. package/dist/web/agent-icons/antigravity.png +0 -0
  247. package/dist/web/agent-icons/auggie.png +0 -0
  248. package/dist/web/agent-icons/autohand.png +0 -0
  249. package/dist/web/agent-icons/claude-ui.png +0 -0
  250. package/dist/web/agent-icons/claude.svg +1 -0
  251. package/dist/web/agent-icons/cline.png +0 -0
  252. package/dist/web/agent-icons/codebuff.png +0 -0
  253. package/dist/web/agent-icons/codex-ui.png +0 -0
  254. package/dist/web/agent-icons/codex.svg +1 -0
  255. package/dist/web/agent-icons/command-code.png +0 -0
  256. package/dist/web/agent-icons/continue.png +0 -0
  257. package/dist/web/agent-icons/copilot.png +0 -0
  258. package/dist/web/agent-icons/crush.png +0 -0
  259. package/dist/web/agent-icons/cursor.png +0 -0
  260. package/dist/web/agent-icons/devin.png +0 -0
  261. package/dist/web/agent-icons/droid.png +0 -0
  262. package/dist/web/agent-icons/gemini.png +0 -0
  263. package/dist/web/agent-icons/goose.png +0 -0
  264. package/dist/web/agent-icons/grok.png +0 -0
  265. package/dist/web/agent-icons/hermes.png +0 -0
  266. package/dist/web/agent-icons/kilo.png +0 -0
  267. package/dist/web/agent-icons/kimi.png +0 -0
  268. package/dist/web/agent-icons/kiro.png +0 -0
  269. package/dist/web/agent-icons/mimo-code.png +0 -0
  270. package/dist/web/agent-icons/mistral-vibe.png +0 -0
  271. package/dist/web/agent-icons/omp.svg +1 -0
  272. package/dist/web/agent-icons/openclaude.png +0 -0
  273. package/dist/web/agent-icons/openclaw.png +0 -0
  274. package/dist/web/agent-icons/opencode.png +0 -0
  275. package/dist/web/agent-icons/pi.svg +1 -0
  276. package/dist/web/agent-icons/prime-agent.png +0 -0
  277. package/dist/web/agent-icons/qwen-code.png +0 -0
  278. package/dist/web/agent-icons/rovo.png +0 -0
  279. package/dist/web/agent-icons/trae.png +0 -0
  280. package/dist/web/agents/claude.svg +1 -0
  281. package/dist/web/agents/codex.svg +1 -0
  282. package/dist/web/agents/gemini.svg +1 -0
  283. package/dist/web/agents/opencode.svg +1 -0
  284. package/dist/web/backgrounds/bg10.jpg +0 -0
  285. package/dist/web/backgrounds/bg11.jpg +0 -0
  286. package/dist/web/backgrounds/bg12.jpg +0 -0
  287. package/dist/web/backgrounds/bg13.jpg +0 -0
  288. package/dist/web/backgrounds/bg2.jpg +0 -0
  289. package/dist/web/backgrounds/bg3.jpg +0 -0
  290. package/dist/web/backgrounds/bg4.jpg +0 -0
  291. package/dist/web/backgrounds/bg5.jpg +0 -0
  292. package/dist/web/backgrounds/bg6.jpg +0 -0
  293. package/dist/web/backgrounds/bg7.jpg +0 -0
  294. package/dist/web/backgrounds/bg8.jpg +0 -0
  295. package/dist/web/backgrounds/bg9.jpg +0 -0
  296. package/dist/web/dev/question/__next._full.txt +27 -0
  297. package/dist/web/dev/question/__next._head.txt +6 -0
  298. package/dist/web/dev/question/__next._index.txt +10 -0
  299. package/dist/web/dev/question/__next._tree.txt +5 -0
  300. package/dist/web/dev/question/__next.dev.question.__PAGE__.txt +9 -0
  301. package/dist/web/dev/question/__next.dev.question.txt +5 -0
  302. package/dist/web/dev/question/__next.dev.txt +5 -0
  303. package/dist/web/dev/question.html +1 -0
  304. package/dist/web/dev/question.txt +27 -0
  305. package/dist/web/favicon.ico +0 -0
  306. package/dist/web/file.svg +1 -0
  307. package/dist/web/globe.svg +1 -0
  308. package/dist/web/icon-192.png +0 -0
  309. package/dist/web/icon-192.svg +3 -0
  310. package/dist/web/icon-512.png +0 -0
  311. package/dist/web/icon-512.svg +3 -0
  312. package/dist/web/icons/file-types/astro.svg +1 -0
  313. package/dist/web/icons/file-types/audio.svg +1 -0
  314. package/dist/web/icons/file-types/avif.svg +1 -0
  315. package/dist/web/icons/file-types/bat.svg +1 -0
  316. package/dist/web/icons/file-types/c.svg +1 -0
  317. package/dist/web/icons/file-types/cheader.svg +1 -0
  318. package/dist/web/icons/file-types/clojure.svg +1 -0
  319. package/dist/web/icons/file-types/clojurescript.svg +1 -0
  320. package/dist/web/icons/file-types/cloudfoundry.svg +1 -0
  321. package/dist/web/icons/file-types/cpp.svg +1 -0
  322. package/dist/web/icons/file-types/cppheader.svg +1 -0
  323. package/dist/web/icons/file-types/csharp.svg +1 -0
  324. package/dist/web/icons/file-types/css.svg +1 -0
  325. package/dist/web/icons/file-types/dartlang.svg +1 -0
  326. package/dist/web/icons/file-types/datadog.svg +1 -0
  327. package/dist/web/icons/file-types/default.svg +1 -0
  328. package/dist/web/icons/file-types/diff.svg +1 -0
  329. package/dist/web/icons/file-types/django.svg +1 -0
  330. package/dist/web/icons/file-types/docker.svg +1 -0
  331. package/dist/web/icons/file-types/dotenv.svg +1 -0
  332. package/dist/web/icons/file-types/editorconfig.svg +1 -0
  333. package/dist/web/icons/file-types/elixir.svg +1 -0
  334. package/dist/web/icons/file-types/erlang.svg +1 -0
  335. package/dist/web/icons/file-types/esphome.svg +1 -0
  336. package/dist/web/icons/file-types/font.svg +1 -0
  337. package/dist/web/icons/file-types/git.svg +1 -0
  338. package/dist/web/icons/file-types/gnu.svg +1 -0
  339. package/dist/web/icons/file-types/go.svg +1 -0
  340. package/dist/web/icons/file-types/graphql.svg +1 -0
  341. package/dist/web/icons/file-types/haskell.svg +1 -0
  342. package/dist/web/icons/file-types/html.svg +1 -0
  343. package/dist/web/icons/file-types/image.svg +1 -0
  344. package/dist/web/icons/file-types/ini.svg +1 -0
  345. package/dist/web/icons/file-types/janet.svg +1 -0
  346. package/dist/web/icons/file-types/jar.svg +1 -0
  347. package/dist/web/icons/file-types/java.svg +1 -0
  348. package/dist/web/icons/file-types/js.svg +1 -0
  349. package/dist/web/icons/file-types/json.svg +1 -0
  350. package/dist/web/icons/file-types/json5.svg +1 -0
  351. package/dist/web/icons/file-types/kotlin.svg +1 -0
  352. package/dist/web/icons/file-types/less.svg +1 -0
  353. package/dist/web/icons/file-types/license.svg +1 -0
  354. package/dist/web/icons/file-types/locale.svg +1 -0
  355. package/dist/web/icons/file-types/log.svg +1 -0
  356. package/dist/web/icons/file-types/lua.svg +1 -0
  357. package/dist/web/icons/file-types/markdown.svg +1 -0
  358. package/dist/web/icons/file-types/mdx.svg +1 -0
  359. package/dist/web/icons/file-types/npm.svg +1 -0
  360. package/dist/web/icons/file-types/objectivec.svg +1 -0
  361. package/dist/web/icons/file-types/ocaml.svg +1 -0
  362. package/dist/web/icons/file-types/parcel.svg +1 -0
  363. package/dist/web/icons/file-types/pddl_happenings.svg +1 -0
  364. package/dist/web/icons/file-types/pdf.svg +1 -0
  365. package/dist/web/icons/file-types/php.svg +1 -0
  366. package/dist/web/icons/file-types/powershell.svg +1 -0
  367. package/dist/web/icons/file-types/protobuf.svg +1 -0
  368. package/dist/web/icons/file-types/python.svg +1 -0
  369. package/dist/web/icons/file-types/r.svg +1 -0
  370. package/dist/web/icons/file-types/reactjs.svg +1 -0
  371. package/dist/web/icons/file-types/reactts.svg +1 -0
  372. package/dist/web/icons/file-types/ruby.svg +1 -0
  373. package/dist/web/icons/file-types/rust.svg +1 -0
  374. package/dist/web/icons/file-types/sass.svg +1 -0
  375. package/dist/web/icons/file-types/scala.svg +1 -0
  376. package/dist/web/icons/file-types/scss.svg +1 -0
  377. package/dist/web/icons/file-types/shell.svg +1 -0
  378. package/dist/web/icons/file-types/sql.svg +1 -0
  379. package/dist/web/icons/file-types/sqlite.svg +1 -0
  380. package/dist/web/icons/file-types/stylus.svg +1 -0
  381. package/dist/web/icons/file-types/svelte.svg +1 -0
  382. package/dist/web/icons/file-types/svg.svg +1 -0
  383. package/dist/web/icons/file-types/swift.svg +1 -0
  384. package/dist/web/icons/file-types/text.svg +1 -0
  385. package/dist/web/icons/file-types/toml.svg +1 -0
  386. package/dist/web/icons/file-types/typescript.svg +1 -0
  387. package/dist/web/icons/file-types/video.svg +1 -0
  388. package/dist/web/icons/file-types/vue.svg +1 -0
  389. package/dist/web/icons/file-types/xml.svg +1 -0
  390. package/dist/web/icons/file-types/yaml.svg +1 -0
  391. package/dist/web/icons/file-types/zip.svg +1 -0
  392. package/dist/web/icons/folder-types/android-open.svg +1 -0
  393. package/dist/web/icons/folder-types/android.svg +1 -0
  394. package/dist/web/icons/folder-types/api-open.svg +1 -0
  395. package/dist/web/icons/folder-types/api.svg +1 -0
  396. package/dist/web/icons/folder-types/app-open.svg +1 -0
  397. package/dist/web/icons/folder-types/app.svg +1 -0
  398. package/dist/web/icons/folder-types/asset-open.svg +1 -0
  399. package/dist/web/icons/folder-types/asset.svg +1 -0
  400. package/dist/web/icons/folder-types/azurepipelines-open.svg +1 -0
  401. package/dist/web/icons/folder-types/azurepipelines.svg +1 -0
  402. package/dist/web/icons/folder-types/blueprint-open.svg +1 -0
  403. package/dist/web/icons/folder-types/blueprint.svg +1 -0
  404. package/dist/web/icons/folder-types/bower-open.svg +1 -0
  405. package/dist/web/icons/folder-types/bower.svg +1 -0
  406. package/dist/web/icons/folder-types/component-open.svg +1 -0
  407. package/dist/web/icons/folder-types/component.svg +1 -0
  408. package/dist/web/icons/folder-types/config-open.svg +1 -0
  409. package/dist/web/icons/folder-types/config.svg +1 -0
  410. package/dist/web/icons/folder-types/controller-open.svg +1 -0
  411. package/dist/web/icons/folder-types/controller.svg +1 -0
  412. package/dist/web/icons/folder-types/css-open.svg +1 -0
  413. package/dist/web/icons/folder-types/css.svg +1 -0
  414. package/dist/web/icons/folder-types/db-open.svg +1 -0
  415. package/dist/web/icons/folder-types/db.svg +1 -0
  416. package/dist/web/icons/folder-types/default-open.svg +1 -0
  417. package/dist/web/icons/folder-types/default.svg +1 -0
  418. package/dist/web/icons/folder-types/dist-open.svg +1 -0
  419. package/dist/web/icons/folder-types/dist.svg +1 -0
  420. package/dist/web/icons/folder-types/docs-open.svg +1 -0
  421. package/dist/web/icons/folder-types/docs.svg +1 -0
  422. package/dist/web/icons/folder-types/fonts-open.svg +1 -0
  423. package/dist/web/icons/folder-types/fonts.svg +1 -0
  424. package/dist/web/icons/folder-types/git-open.svg +1 -0
  425. package/dist/web/icons/folder-types/git.svg +1 -0
  426. package/dist/web/icons/folder-types/github-open.svg +1 -0
  427. package/dist/web/icons/folder-types/github.svg +1 -0
  428. package/dist/web/icons/folder-types/helper-open.svg +1 -0
  429. package/dist/web/icons/folder-types/helper.svg +1 -0
  430. package/dist/web/icons/folder-types/hook-open.svg +1 -0
  431. package/dist/web/icons/folder-types/hook.svg +1 -0
  432. package/dist/web/icons/folder-types/husky-open.svg +1 -0
  433. package/dist/web/icons/folder-types/husky.svg +1 -0
  434. package/dist/web/icons/folder-types/idea-open.svg +1 -0
  435. package/dist/web/icons/folder-types/idea.svg +1 -0
  436. package/dist/web/icons/folder-types/images-open.svg +1 -0
  437. package/dist/web/icons/folder-types/images.svg +1 -0
  438. package/dist/web/icons/folder-types/interfaces-open.svg +1 -0
  439. package/dist/web/icons/folder-types/interfaces.svg +1 -0
  440. package/dist/web/icons/folder-types/ios-open.svg +1 -0
  441. package/dist/web/icons/folder-types/ios.svg +1 -0
  442. package/dist/web/icons/folder-types/library-open.svg +1 -0
  443. package/dist/web/icons/folder-types/library.svg +1 -0
  444. package/dist/web/icons/folder-types/locale-open.svg +1 -0
  445. package/dist/web/icons/folder-types/locale.svg +1 -0
  446. package/dist/web/icons/folder-types/middleware-open.svg +1 -0
  447. package/dist/web/icons/folder-types/middleware.svg +1 -0
  448. package/dist/web/icons/folder-types/model-open.svg +1 -0
  449. package/dist/web/icons/folder-types/model.svg +1 -0
  450. package/dist/web/icons/folder-types/node-open.svg +1 -0
  451. package/dist/web/icons/folder-types/node.svg +1 -0
  452. package/dist/web/icons/folder-types/public-open.svg +1 -0
  453. package/dist/web/icons/folder-types/public.svg +1 -0
  454. package/dist/web/icons/folder-types/redux-open.svg +1 -0
  455. package/dist/web/icons/folder-types/redux.svg +1 -0
  456. package/dist/web/icons/folder-types/route-open.svg +1 -0
  457. package/dist/web/icons/folder-types/route.svg +1 -0
  458. package/dist/web/icons/folder-types/script-open.svg +1 -0
  459. package/dist/web/icons/folder-types/script.svg +1 -0
  460. package/dist/web/icons/folder-types/seedkit-open.svg +1 -0
  461. package/dist/web/icons/folder-types/seedkit.svg +1 -0
  462. package/dist/web/icons/folder-types/server-open.svg +1 -0
  463. package/dist/web/icons/folder-types/server.svg +1 -0
  464. package/dist/web/icons/folder-types/services-open.svg +1 -0
  465. package/dist/web/icons/folder-types/services.svg +1 -0
  466. package/dist/web/icons/folder-types/src-open.svg +1 -0
  467. package/dist/web/icons/folder-types/src.svg +1 -0
  468. package/dist/web/icons/folder-types/story-open.svg +1 -0
  469. package/dist/web/icons/folder-types/story.svg +1 -0
  470. package/dist/web/icons/folder-types/style-open.svg +1 -0
  471. package/dist/web/icons/folder-types/style.svg +1 -0
  472. package/dist/web/icons/folder-types/template-open.svg +1 -0
  473. package/dist/web/icons/folder-types/template.svg +1 -0
  474. package/dist/web/icons/folder-types/test-open.svg +1 -0
  475. package/dist/web/icons/folder-types/test.svg +1 -0
  476. package/dist/web/icons/folder-types/theme-open.svg +1 -0
  477. package/dist/web/icons/folder-types/theme.svg +1 -0
  478. package/dist/web/icons/folder-types/tools-open.svg +1 -0
  479. package/dist/web/icons/folder-types/tools.svg +1 -0
  480. package/dist/web/icons/folder-types/typescript-open.svg +1 -0
  481. package/dist/web/icons/folder-types/typescript.svg +1 -0
  482. package/dist/web/icons/folder-types/typings-open.svg +1 -0
  483. package/dist/web/icons/folder-types/typings.svg +1 -0
  484. package/dist/web/icons/folder-types/view-open.svg +1 -0
  485. package/dist/web/icons/folder-types/view.svg +1 -0
  486. package/dist/web/icons/folder-types/vscode-open.svg +1 -0
  487. package/dist/web/icons/folder-types/vscode.svg +1 -0
  488. package/dist/web/index.html +1 -0
  489. package/dist/web/index.txt +25 -0
  490. package/dist/web/login/__next._full.txt +27 -0
  491. package/dist/web/login/__next._head.txt +6 -0
  492. package/dist/web/login/__next._index.txt +10 -0
  493. package/dist/web/login/__next._tree.txt +5 -0
  494. package/dist/web/login/__next.login.__PAGE__.txt +9 -0
  495. package/dist/web/login/__next.login.txt +5 -0
  496. package/dist/web/login.html +1 -0
  497. package/dist/web/login.txt +27 -0
  498. package/dist/web/manifest.json +53 -0
  499. package/dist/web/maskable-192.png +0 -0
  500. package/dist/web/maskable-512.png +0 -0
  501. package/dist/web/next.svg +1 -0
  502. package/dist/web/proxy.html +179 -0
  503. package/dist/web/screenshot-desktop.png +0 -0
  504. package/dist/web/screenshot-mobile.png +0 -0
  505. package/dist/web/screenshots/desktop-ide.webp +0 -0
  506. package/dist/web/screenshots/mobile-1.webp +0 -0
  507. package/dist/web/screenshots/mobile-2.webp +0 -0
  508. package/dist/web/screenshots/mobile-3.webp +0 -0
  509. package/dist/web/sw-site.js +283 -0
  510. package/dist/web/sw.js +88 -0
  511. package/dist/web/swBridgeClaim.js +57 -0
  512. package/dist/web/vercel.svg +1 -0
  513. package/dist/web/window.svg +1 -0
  514. package/dist/web/workspace/__next._full.txt +25 -0
  515. package/dist/web/workspace/__next._head.txt +6 -0
  516. package/dist/web/workspace/__next._index.txt +10 -0
  517. package/dist/web/workspace/__next._tree.txt +6 -0
  518. package/dist/web/workspace/__next.workspace.__PAGE__.txt +5 -0
  519. package/dist/web/workspace/__next.workspace.txt +8 -0
  520. package/dist/web/workspace/browse/__next._full.txt +27 -0
  521. package/dist/web/workspace/browse/__next._head.txt +6 -0
  522. package/dist/web/workspace/browse/__next._index.txt +10 -0
  523. package/dist/web/workspace/browse/__next._tree.txt +6 -0
  524. package/dist/web/workspace/browse/__next.workspace.browse.__PAGE__.txt +5 -0
  525. package/dist/web/workspace/browse/__next.workspace.browse.txt +5 -0
  526. package/dist/web/workspace/browse/__next.workspace.txt +8 -0
  527. package/dist/web/workspace/browse.html +1 -0
  528. package/dist/web/workspace/browse.txt +27 -0
  529. package/dist/web/workspace/editor/__next._full.txt +27 -0
  530. package/dist/web/workspace/editor/__next._head.txt +6 -0
  531. package/dist/web/workspace/editor/__next._index.txt +10 -0
  532. package/dist/web/workspace/editor/__next._tree.txt +6 -0
  533. package/dist/web/workspace/editor/__next.workspace.editor.__PAGE__.txt +5 -0
  534. package/dist/web/workspace/editor/__next.workspace.editor.txt +5 -0
  535. package/dist/web/workspace/editor/__next.workspace.txt +8 -0
  536. package/dist/web/workspace/editor.html +1 -0
  537. package/dist/web/workspace/editor.txt +27 -0
  538. package/dist/web/workspace/files/__next._full.txt +27 -0
  539. package/dist/web/workspace/files/__next._head.txt +6 -0
  540. package/dist/web/workspace/files/__next._index.txt +10 -0
  541. package/dist/web/workspace/files/__next._tree.txt +6 -0
  542. package/dist/web/workspace/files/__next.workspace.files.__PAGE__.txt +5 -0
  543. package/dist/web/workspace/files/__next.workspace.files.txt +5 -0
  544. package/dist/web/workspace/files/__next.workspace.txt +8 -0
  545. package/dist/web/workspace/files.html +1 -0
  546. package/dist/web/workspace/files.txt +27 -0
  547. package/dist/web/workspace/git/__next._full.txt +27 -0
  548. package/dist/web/workspace/git/__next._head.txt +6 -0
  549. package/dist/web/workspace/git/__next._index.txt +10 -0
  550. package/dist/web/workspace/git/__next._tree.txt +6 -0
  551. package/dist/web/workspace/git/__next.workspace.git.__PAGE__.txt +5 -0
  552. package/dist/web/workspace/git/__next.workspace.git.txt +5 -0
  553. package/dist/web/workspace/git/__next.workspace.txt +8 -0
  554. package/dist/web/workspace/git.html +1 -0
  555. package/dist/web/workspace/git.txt +27 -0
  556. package/dist/web/workspace/mobile/__next._full.txt +27 -0
  557. package/dist/web/workspace/mobile/__next._head.txt +6 -0
  558. package/dist/web/workspace/mobile/__next._index.txt +10 -0
  559. package/dist/web/workspace/mobile/__next._tree.txt +6 -0
  560. package/dist/web/workspace/mobile/__next.workspace.mobile.__PAGE__.txt +5 -0
  561. package/dist/web/workspace/mobile/__next.workspace.mobile.txt +5 -0
  562. package/dist/web/workspace/mobile/__next.workspace.txt +8 -0
  563. package/dist/web/workspace/mobile.html +1 -0
  564. package/dist/web/workspace/mobile.txt +27 -0
  565. package/dist/web/workspace/remote/__next._full.txt +27 -0
  566. package/dist/web/workspace/remote/__next._head.txt +6 -0
  567. package/dist/web/workspace/remote/__next._index.txt +10 -0
  568. package/dist/web/workspace/remote/__next._tree.txt +6 -0
  569. package/dist/web/workspace/remote/__next.workspace.remote.__PAGE__.txt +5 -0
  570. package/dist/web/workspace/remote/__next.workspace.remote.txt +5 -0
  571. package/dist/web/workspace/remote/__next.workspace.txt +8 -0
  572. package/dist/web/workspace/remote.html +1 -0
  573. package/dist/web/workspace/remote.txt +27 -0
  574. package/dist/web/workspace/terminal/default/__next._full.txt +27 -0
  575. package/dist/web/workspace/terminal/default/__next._head.txt +6 -0
  576. package/dist/web/workspace/terminal/default/__next._index.txt +10 -0
  577. package/dist/web/workspace/terminal/default/__next._tree.txt +6 -0
  578. package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.__PAGE__.txt +5 -0
  579. package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.txt +5 -0
  580. package/dist/web/workspace/terminal/default/__next.workspace.terminal.txt +5 -0
  581. package/dist/web/workspace/terminal/default/__next.workspace.txt +8 -0
  582. package/dist/web/workspace/terminal/default.html +1 -0
  583. package/dist/web/workspace/terminal/default.txt +27 -0
  584. package/dist/web/workspace/workspaces/__next._full.txt +27 -0
  585. package/dist/web/workspace/workspaces/__next._head.txt +6 -0
  586. package/dist/web/workspace/workspaces/__next._index.txt +10 -0
  587. package/dist/web/workspace/workspaces/__next._tree.txt +6 -0
  588. package/dist/web/workspace/workspaces/__next.workspace.txt +8 -0
  589. package/dist/web/workspace/workspaces/__next.workspace.workspaces.__PAGE__.txt +5 -0
  590. package/dist/web/workspace/workspaces/__next.workspace.workspaces.txt +5 -0
  591. package/dist/web/workspace/workspaces.html +1 -0
  592. package/dist/web/workspace/workspaces.txt +27 -0
  593. package/dist/web/workspace.html +1 -0
  594. package/dist/web/workspace.txt +25 -0
  595. package/package.json +4 -4
  596. package/dist/ui/assets/index-CLABbREo.css +0 -1
  597. package/dist/ui/assets/index-MX08IV5-.js +0 -58
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "9Remote Terminal",
3
+ "short_name": "9Remote",
4
+ "id": "/?source=pwa",
5
+ "description": "Access your terminal from anywhere - secure remote terminal access",
6
+ "start_url": "/login",
7
+ "display": "standalone",
8
+ "background_color": "#0f0f0f",
9
+ "theme_color": "#121212",
10
+ "icons": [
11
+ {
12
+ "src": "/icon-192.png",
13
+ "sizes": "192x192",
14
+ "type": "image/png",
15
+ "purpose": "any"
16
+ },
17
+ {
18
+ "src": "/icon-512.png",
19
+ "sizes": "512x512",
20
+ "type": "image/png",
21
+ "purpose": "any"
22
+ },
23
+ {
24
+ "src": "/maskable-192.png",
25
+ "sizes": "192x192",
26
+ "type": "image/png",
27
+ "purpose": "maskable"
28
+ },
29
+ {
30
+ "src": "/maskable-512.png",
31
+ "sizes": "512x512",
32
+ "type": "image/png",
33
+ "purpose": "maskable"
34
+ }
35
+ ],
36
+ "categories": ["utilities", "productivity"],
37
+ "screenshots": [
38
+ {
39
+ "src": "/screenshot-desktop.png",
40
+ "sizes": "1280x720",
41
+ "type": "image/png",
42
+ "form_factor": "wide",
43
+ "label": "9Remote on desktop"
44
+ },
45
+ {
46
+ "src": "/screenshot-mobile.png",
47
+ "sizes": "390x844",
48
+ "type": "image/png",
49
+ "form_factor": "narrow",
50
+ "label": "9Remote on mobile"
51
+ }
52
+ ]
53
+ }
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -0,0 +1,179 @@
1
+ <!doctype html>
2
+ <meta charset="utf-8">
3
+ <title>9Remote site proxy</title>
4
+ <style>
5
+ html, body { margin: 0; height: 100%; background: #0b1220; }
6
+ iframe { width: 100%; height: 100%; border: 0; display: block; }
7
+ p { font: 14px system-ui; color: #94a3b8; text-align: center; padding-top: 3rem; margin: 0; }
8
+ </style>
9
+ <p id="status">Connecting to local site…</p>
10
+ <script>
11
+ // Proxy shell for the sites origin.
12
+ //
13
+ // This page is ours, the site inside it is not. It lives here so the browsed
14
+ // server gets an origin with nothing worth stealing: the app's storage — the
15
+ // device-trust tail, the saved keys — belongs to the app's own origin and is
16
+ // unreachable from this one.
17
+ //
18
+ // It sits between two parties that cannot address each other. The service
19
+ // worker can only postMessage its own clients, and the transport socket lives
20
+ // in the app on the other origin, so this page carries each request across:
21
+ // SW --(MessageChannel port)--> proxy --(postMessage)--> app --> agent
22
+ // and the reply back along the same path.
23
+ (function () {
24
+ "use strict";
25
+
26
+ // Only the app may drive this page, and only this page's opener is told
27
+ // anything. An opaque origin sends the literal "null" — never a match.
28
+ //
29
+ // The agent serves its workspace on every loopback name, so this has to accept
30
+ // the app whichever one it was opened on — a single hardcoded "localhost"
31
+ // leaves the frame unable to talk to a page opened on 127.0.0.1, and it shows
32
+ // as a shell that never loads anything.
33
+ var APP_ORIGINS = (function () {
34
+ var h = location.hostname;
35
+ var port = location.port ? ":" + location.port : "";
36
+ if (h === "sites.9remote.cc") return ["https://9remote.cc"];
37
+ if (h === "sites-dev.9remote.cc") return ["https://dev.9remote.cc"];
38
+ if (h === "sites.localhost") return ["http://localhost" + port, "http://127.0.0.1" + port, "http://[::1]" + port];
39
+ return [];
40
+ })();
41
+
42
+ function isAppOrigin(origin) {
43
+ return APP_ORIGINS.indexOf(origin) !== -1;
44
+ }
45
+
46
+ var status = document.getElementById("status");
47
+ var frame = null;
48
+ var pending = new Map(); // reqId -> MessagePort back to the SW
49
+ var reqSeq = 0;
50
+
51
+ function fail(text) {
52
+ if (status) status.textContent = text;
53
+ }
54
+
55
+ function parseHash() {
56
+ var h = new URLSearchParams((location.hash || "").replace(/^#/, ""));
57
+ var port = Number(h.get("port"));
58
+ if (!Number.isInteger(port) || port < 1 || port > 65535) return null;
59
+ return { port: port, path: h.get("path") || "/" };
60
+ }
61
+
62
+ // ── SW side ───────────────────────────────────────────────────────────────
63
+
64
+ navigator.serviceWorker.addEventListener("message", function (event) {
65
+ var msg = event.data;
66
+ if (!msg || typeof msg !== "object") return;
67
+
68
+ if (msg.type === "http-request") {
69
+ // The SW handed us a port for this one request; remember it, ask the app,
70
+ // and answer on that port when the app replies.
71
+ var id = "px-" + (++reqSeq);
72
+ var replyPort = event.ports && event.ports[0];
73
+ if (!replyPort) return;
74
+ pending.set(id, replyPort);
75
+ post({ type: "site:request", id: id, payload: msg });
76
+ return;
77
+ }
78
+
79
+ if (msg.type === "site-nav") {
80
+ post({ type: "site:nav", port: msg.port, path: msg.path });
81
+ return;
82
+ }
83
+
84
+ // The worker could not serve a page at all. Its own reply is plain text that
85
+ // the frame renders as a bare white page, so relay the reason upward.
86
+ if (msg.type === "site-error") {
87
+ post({ type: "site-error", message: msg.message, port: msg.port });
88
+ return;
89
+ }
90
+
91
+ // The worker restarted and lost its bridge pointer — re-announce.
92
+ if (msg.type === "sw-hello") {
93
+ helloTo(event.source);
94
+ }
95
+ });
96
+
97
+ function helloTo(worker) {
98
+ try { worker && worker.postMessage({ type: "bridge-hello" }); } catch (e) { /* gone */ }
99
+ }
100
+
101
+ // ── App side ──────────────────────────────────────────────────────────────
102
+
103
+ // The shell speaks first (site:ready, and requests relayed from the worker),
104
+ // so it cannot wait to learn the parent's origin from a message. Posting to
105
+ // every allowed origin is enough: the browser delivers to the parent only for
106
+ // the one that matches it, and a parent outside the list receives nothing.
107
+ function post(message) {
108
+ for (var i = 0; i < APP_ORIGINS.length; i++) {
109
+ try { parent.postMessage(message, APP_ORIGINS[i]); } catch (e) { /* shell gone */ }
110
+ }
111
+ }
112
+
113
+ window.addEventListener("message", function (event) {
114
+ // Everything below acts on the app's word; an unchecked origin here would
115
+ // let any page that frames us speak for it.
116
+ if (!isAppOrigin(event.origin) || event.source !== parent) return;
117
+ var msg = event.data;
118
+ if (!msg || typeof msg !== "object") return;
119
+
120
+ if (msg.type === "site:reply") {
121
+ var port = pending.get(msg.id);
122
+ if (!port) return;
123
+ pending.delete(msg.id);
124
+ try { port.postMessage(msg.payload); } catch (e) { /* SW gone */ }
125
+ return;
126
+ }
127
+
128
+ });
129
+
130
+ // ── Boot ──────────────────────────────────────────────────────────────────
131
+
132
+ function show(port, path) {
133
+ var src = "/browse/" + Number(port) + (path || "/");
134
+ if (!frame) {
135
+ frame = document.createElement("iframe");
136
+ frame.title = "local site";
137
+ document.body.appendChild(frame);
138
+ if (status) status.remove();
139
+ }
140
+ frame.src = src;
141
+ }
142
+
143
+ if (!APP_ORIGINS.length) {
144
+ fail("9Remote: unknown host");
145
+ return;
146
+ }
147
+ if (!("serviceWorker" in navigator)) {
148
+ fail("Cannot connect: service worker unavailable");
149
+ return;
150
+ }
151
+
152
+ navigator.serviceWorker.register("/sw-site.js", { scope: "/" }).then(function (reg) {
153
+ var sayHello = function () { helloTo(reg.active || reg.waiting || reg.installing); };
154
+ sayHello();
155
+ reg.addEventListener("updatefound", function () {
156
+ var installing = reg.installing;
157
+ installing && installing.addEventListener("statechange", function () {
158
+ if (installing.state === "activated") sayHello();
159
+ });
160
+ });
161
+
162
+ // The frame must not load before the worker controls this origin, or the
163
+ // request goes to the edge and comes back as this same page.
164
+ var start = function () {
165
+ var target = parseHash();
166
+ if (!target) return fail("9Remote: no site address");
167
+ show(target.port, target.path);
168
+ post({ type: "site:ready" });
169
+ };
170
+ if (navigator.serviceWorker.controller) return start();
171
+ navigator.serviceWorker.addEventListener("controllerchange", start, { once: true });
172
+ // Activation without a controllerchange happens; don't hang forever on it.
173
+ setTimeout(function () { if (!frame) start(); }, 4000);
174
+ }).catch(function () {
175
+ fail("Cannot connect: service worker blocked");
176
+ post({ type: "site:swBlocked" });
177
+ });
178
+ })();
179
+ </script>
Binary file
Binary file
@@ -0,0 +1,283 @@
1
+ // Site proxy service worker: serves /browse/<port>/... documents via transport bridge.
2
+
3
+ importScripts("/swBridgeClaim.js");
4
+
5
+ const BROWSE_RE = /^\/browse\/(\d{1,5})(\/.*)?$/;
6
+ const BRIDGE_TIMEOUT_MS = 60000;
7
+ const BRIDGE_DISCOVER_MS = 3000;
8
+ const MAX_REQUEST_BODY_BYTES = 512 * 1024;
9
+ const REDIRECT_STATUSES = [301, 302, 303, 307, 308];
10
+
11
+ // Restrict framing and origin communication via Content Security Policy.
12
+ const SITE_CSP = [
13
+ "connect-src 'self'",
14
+ "form-action 'self'",
15
+ `frame-ancestors 'self' ${appOriginsFor(self.location.hostname, self.location.port)}`.trim(),
16
+ "base-uri 'self'"
17
+ ].join("; ");
18
+
19
+ let bridgeClient = null;
20
+ let bridgeDiscover = null;
21
+ let bridgeResolve = null;
22
+ let reqCounter = 0;
23
+
24
+ self.addEventListener("install", (event) => event.waitUntil(self.skipWaiting()));
25
+ self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
26
+
27
+ self.addEventListener("message", (event) => {
28
+ const msg = event.data;
29
+ if (!msg || typeof msg !== "object") return;
30
+ if (msg.type === "bridge-hello") {
31
+ // Verify source is shell to prevent cross-site privilege escalation.
32
+ if (!canBeBridge(event.source?.url, self.location.origin)) return;
33
+ bridgeClient = event.source;
34
+ if (bridgeResolve) {
35
+ const resolve = bridgeResolve;
36
+ bridgeDiscover = null;
37
+ bridgeResolve = null;
38
+ resolve();
39
+ }
40
+ return;
41
+ }
42
+ });
43
+
44
+ function base64ToBytes(b64) {
45
+ const bin = atob(b64 || "");
46
+ const out = new Uint8Array(bin.length);
47
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
48
+ return out;
49
+ }
50
+
51
+ function bytesToBase64(buffer) {
52
+ const bytes = new Uint8Array(buffer);
53
+ let bin = "";
54
+ const CHUNK = 0x8000;
55
+ for (let i = 0; i < bytes.length; i += CHUNK) {
56
+ bin += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
57
+ }
58
+ return btoa(bin);
59
+ }
60
+
61
+ function discoverBridge() {
62
+ if (bridgeClient) return Promise.resolve();
63
+ if (!bridgeDiscover) {
64
+ bridgeDiscover = new Promise((resolve) => {
65
+ bridgeResolve = resolve;
66
+ setTimeout(() => {
67
+ bridgeDiscover = null;
68
+ bridgeResolve = null;
69
+ resolve();
70
+ }, BRIDGE_DISCOVER_MS);
71
+ });
72
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
73
+ for (const client of clients) {
74
+ if (canBeBridge(client.url, self.location.origin)) client.postMessage({ type: "sw-hello" });
75
+ }
76
+ }).catch(() => {});
77
+ }
78
+ return bridgeDiscover;
79
+ }
80
+
81
+ // Injected script rewriting absolute links/forms into /browse/<port> scope.
82
+ function inScopeScript(port) {
83
+ return `<script data-9remote="site-proxy">
84
+ (function () {
85
+ if (window.__nineRemoteSiteProxy) return;
86
+ window.__nineRemoteSiteProxy = true;
87
+ var BASE = "/browse/${port}";
88
+ function scoped(path) {
89
+ if (!path || path.indexOf(BASE + "/") === 0 || path === BASE) return null;
90
+ return path.charAt(0) === "/" ? BASE + path : null;
91
+ }
92
+ // Bubble phase allows SPA routers to handle owned routes first.
93
+ document.addEventListener("click", function (e) {
94
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
95
+ var a = e.target && e.target.closest && e.target.closest("a[href]");
96
+ if (!a || a.target && a.target !== "_self" || a.hasAttribute("download")) return;
97
+ var url;
98
+ try { url = new URL(a.getAttribute("href"), location.href); } catch (err) { return; }
99
+ if (url.origin !== location.origin) return;
100
+ var next = scoped(url.pathname);
101
+ if (!next) return;
102
+ e.preventDefault();
103
+ location.replace(next + url.search + url.hash);
104
+ });
105
+ document.addEventListener("submit", function (e) {
106
+ if (e.defaultPrevented) return;
107
+ var form = e.target;
108
+ if (!form || form.tagName !== "FORM" || form.target && form.target !== "_self") return;
109
+ var action = form.getAttribute("action");
110
+ if (action === null) return;
111
+ var url;
112
+ try { url = new URL(action, location.href); } catch (err) { return; }
113
+ if (url.origin !== location.origin) return;
114
+ var next = scoped(url.pathname);
115
+ if (next) form.setAttribute("action", next + url.search);
116
+ }, true);
117
+ // Downgrade pushState to replaceState so iframe history doesn't swallow parent Back.
118
+ var nativeReplace = history.replaceState.bind(history);
119
+ ["pushState", "replaceState"].forEach(function (name) {
120
+ history[name] = function (state, title, path) {
121
+ if (typeof path === "string") {
122
+ try {
123
+ var url = new URL(path, location.href);
124
+ if (url.origin === location.origin) {
125
+ var next = scoped(url.pathname);
126
+ if (next) path = next + url.search + url.hash;
127
+ }
128
+ } catch (err) { /* leave path as-is */ }
129
+ }
130
+ return nativeReplace(state, title, path);
131
+ };
132
+ });
133
+ })();
134
+ </script>`;
135
+ }
136
+
137
+ function injectIntoHtml(html, port) {
138
+ const tag = inScopeScript(port);
139
+ const head = html.match(/<head[^>]*>/i);
140
+ if (head) return html.replace(head[0], () => head[0] + tag);
141
+ return tag + html;
142
+ }
143
+
144
+ async function portFromReferrer(request) {
145
+ try {
146
+ if (request.referrer) {
147
+ const match = new URL(request.referrer).pathname.match(BROWSE_RE);
148
+ if (match) return Number(match[1]);
149
+ }
150
+ } catch { /* bad referrer — fall through */ }
151
+ const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: false });
152
+ for (const client of clients) {
153
+ const match = new URL(client.url).pathname.match(BROWSE_RE);
154
+ if (match) return Number(match[1]);
155
+ }
156
+ return null;
157
+ }
158
+
159
+ // MessageChannel per request to keep concurrent requests isolated.
160
+ function requestViaBridge(reqId, port, method, target, headers, bodyB64) {
161
+ return new Promise((resolve, reject) => {
162
+ const channel = new MessageChannel();
163
+ const timer = setTimeout(() => {
164
+ channel.port1.close();
165
+ reject(new Error("bridge-timeout"));
166
+ }, BRIDGE_TIMEOUT_MS);
167
+ channel.port1.onmessage = (event) => {
168
+ clearTimeout(timer);
169
+ channel.port1.close();
170
+ resolve(event.data);
171
+ };
172
+ bridgeClient.postMessage(
173
+ { type: "http-request", reqId, port, method, target, headers, bodyB64 },
174
+ [channel.port2]
175
+ );
176
+ });
177
+ }
178
+
179
+ function fail(message, status, port) {
180
+ if (port) {
181
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
182
+ for (const client of clients) {
183
+ if (canBeBridge(client.url, self.location.origin)) client.postMessage({ type: "site-error", message, port });
184
+ }
185
+ }).catch(() => {});
186
+ }
187
+ return new Response(`9Remote: ${message}`, { status });
188
+ }
189
+
190
+ async function handle(request, url, inScope) {
191
+ const port = inScope ? Number(inScope[1]) : await portFromReferrer(request);
192
+ if (!port) return fail("no site context for this request", 503, null);
193
+
194
+ // Prevent cross-site subresource access within shared origin.
195
+ if (inScope && request.mode !== "navigate") {
196
+ const from = await portFromReferrer(request);
197
+ if (from && from !== port) {
198
+ return new Response("9Remote: cross-site request refused", { status: 403 });
199
+ }
200
+ }
201
+
202
+ await discoverBridge();
203
+ if (!bridgeClient) return fail("bridge not connected — open the 9Remote app", 503, port);
204
+
205
+ url.searchParams.delete("r");
206
+ const path = inScope ? (inScope[2] || "/") : url.pathname;
207
+ const target = path + (url.search || "");
208
+
209
+ if (request.mode === "navigate") {
210
+ bridgeClient.postMessage({ type: "site-nav", port, path });
211
+ }
212
+
213
+ const headers = {};
214
+ request.headers.forEach((value, key) => {
215
+ if (key.toLowerCase() !== "cookie") headers[key] = value;
216
+ });
217
+
218
+ let bodyB64 = null;
219
+ if (request.method !== "GET" && request.method !== "HEAD") {
220
+ const buf = await request.arrayBuffer();
221
+ if (buf.byteLength > MAX_REQUEST_BODY_BYTES) {
222
+ return new Response("9Remote: request body too large", { status: 413 });
223
+ }
224
+ if (buf.byteLength > 0) bodyB64 = bytesToBase64(buf);
225
+ }
226
+
227
+ const reqId = `sw-${Date.now()}-${++reqCounter}`;
228
+ let msg;
229
+ try {
230
+ msg = await requestViaBridge(reqId, port, request.method, target, headers, bodyB64);
231
+ } catch {
232
+ return fail("request timed out", 504, port);
233
+ }
234
+ if (!msg || msg.error) return fail(msg?.error || "no reply from the agent", 502, port);
235
+
236
+ const resHeaders = new Headers(msg.headers || {});
237
+
238
+ const location = resHeaders.get("location");
239
+ if (location && REDIRECT_STATUSES.includes(msg.status)) {
240
+ let next = null;
241
+ try {
242
+ const abs = new URL(location, `http://localhost:${port}`);
243
+ const sameSite = location.startsWith("/") || abs.host === `localhost:${port}`;
244
+ if (sameSite) next = abs.pathname + abs.search;
245
+ } catch { /* keep null */ }
246
+ if (next) return Response.redirect(`/browse/${port}${next}`, msg.status);
247
+ return Response.redirect(location, msg.status);
248
+ }
249
+
250
+ if (msg.status === 204 || msg.status === 304) {
251
+ return new Response(null, { status: msg.status, headers: resHeaders });
252
+ }
253
+
254
+ // Enforce CSP on proxied site content.
255
+ resHeaders.set("Content-Security-Policy", SITE_CSP);
256
+
257
+ const bytes = base64ToBytes(msg.bodyB64);
258
+ if ((resHeaders.get("content-type") || "").includes("text/html")) {
259
+ const html = new TextDecoder("utf-8").decode(bytes);
260
+ return new Response(injectIntoHtml(html, port), { status: msg.status, headers: resHeaders });
261
+ }
262
+ return new Response(bytes, { status: msg.status, headers: resHeaders });
263
+ }
264
+
265
+ self.addEventListener("fetch", (event) => {
266
+ const request = event.request;
267
+ let url;
268
+ try { url = new URL(request.url); } catch { return; }
269
+
270
+ if (url.origin !== self.location.origin) return;
271
+ if (url.pathname === "/sw-site.js") return;
272
+ if (url.pathname.startsWith("/cdn-cgi/")) return;
273
+
274
+ const inScope = url.pathname.match(BROWSE_RE);
275
+ if (inScope) {
276
+ event.respondWith(handle(request, url, inScope));
277
+ return;
278
+ }
279
+
280
+ // Handle root-relative subresource requests from in-scope pages.
281
+ if (request.mode === "navigate") return;
282
+ event.respondWith(handle(request, url, null));
283
+ });
package/dist/web/sw.js ADDED
@@ -0,0 +1,88 @@
1
+ // Service Worker - NO CACHE + WebPush Notifications
2
+ // Handles PWA installation and push notifications
3
+
4
+ self.addEventListener("install", () => {
5
+ self.skipWaiting();
6
+ });
7
+
8
+ self.addEventListener("activate", (event) => {
9
+ event.waitUntil(
10
+ caches.keys().then((cacheNames) => {
11
+ return Promise.all(
12
+ cacheNames.map((cacheName) => caches.delete(cacheName))
13
+ );
14
+ })
15
+ );
16
+ return self.clients.claim();
17
+ });
18
+
19
+ // Fetch event - Always network, no cache
20
+ self.addEventListener("fetch", (event) => {
21
+ event.respondWith(
22
+ fetch(event.request, {
23
+ cache: "no-store"
24
+ })
25
+ );
26
+ });
27
+
28
+ // Push notification received
29
+ self.addEventListener("push", (event) => {
30
+ if (!event.data) return;
31
+
32
+ event.waitUntil(
33
+ (async () => {
34
+ // Skip when a PWA window is focused — backstop against stale server focus state
35
+ const clientList = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
36
+ if (clientList.some((c) => c.visibilityState === "visible")) return;
37
+
38
+ try {
39
+ const data = event.data.json();
40
+ const title = data.title || "9Remote";
41
+ const options = {
42
+ body: data.body || "Notification",
43
+ icon: "/icon-192.png",
44
+ badge: "/icon-192.png",
45
+ data: data.data || { url: "/workspace" },
46
+ vibrate: [200, 100, 200],
47
+ tag: "9remote-notification",
48
+ renotify: true
49
+ };
50
+ await self.registration.showNotification(title, options);
51
+
52
+ // PWA icon badge (Android/desktop Chrome/Edge/Brave) — iOS silently ignores
53
+ if (self.registration.setAppBadge) {
54
+ const badgeCount = typeof data.badge === "number" ? data.badge : 1;
55
+ await self.registration.setAppBadge(badgeCount);
56
+ }
57
+ } catch (error) {
58
+ console.error("Push event error:", error);
59
+ }
60
+ })()
61
+ );
62
+ });
63
+
64
+ // Notification click - focus any same-origin client and deep-link into it
65
+ self.addEventListener("notificationclick", (event) => {
66
+ event.notification.close();
67
+
68
+ // User acknowledged → clear icon badge
69
+ if (self.registration.clearAppBadge) {
70
+ self.registration.clearAppBadge().catch(() => {});
71
+ }
72
+
73
+ const url = event.notification.data?.url || "/workspace";
74
+
75
+ event.waitUntil(
76
+ self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
77
+ // Focus any existing same-origin client to preserve auth/state
78
+ for (const client of clientList) {
79
+ if ("focus" in client) {
80
+ client.postMessage({ type: "NOTIFICATION_CLICK", url });
81
+ return client.focus();
82
+ }
83
+ }
84
+ // No existing client — open a new one
85
+ return self.clients.openWindow(url);
86
+ })
87
+ );
88
+ });
@@ -0,0 +1,57 @@
1
+ // Which client may act as the bridge.
2
+ //
3
+ // The worker forwards every browsed request to whichever client announced
4
+ // itself with "bridge-hello", and every browsed site lives on this same origin.
5
+ // So a dev server rendering something the developer did not write can claim the
6
+ // role, then see every other tab's requests — paths, headers, bodies — and
7
+ // answer them with content of its own.
8
+ //
9
+ // The shell is the one page entitled to it, and the worker can tell: the shell
10
+ // is served at /proxy.html, while sites are always under /browse/. Parsing the
11
+ // URL rather than matching on the string is what makes /browse/3000/proxy.html
12
+ // and /browse/3000/../proxy.html answer correctly.
13
+ //
14
+ // Loaded by sw-site.js through importScripts, so this is a classic script and
15
+ // defines a global rather than exporting. The test reads and evaluates it.
16
+ function canBeBridge(clientUrl, expectedOrigin) {
17
+ if (typeof clientUrl !== "string" || !clientUrl) return false;
18
+ let url;
19
+ try {
20
+ url = new URL(clientUrl);
21
+ } catch {
22
+ return false;
23
+ }
24
+ if (url.origin !== expectedOrigin) return false;
25
+ // pathname is already normalised by the URL parser, so ../ is resolved and a
26
+ // query or fragment cannot smuggle the shell's name into it.
27
+ return url.pathname === "/proxy.html";
28
+ }
29
+
30
+ // The app origins that may sit above a browsed site. frame-ancestors is checked
31
+ // against EVERY ancestor, not just the parent, so a site framed by the shell is
32
+ // also framed by the app — naming only the shell blocks the load.
33
+ //
34
+ // The loopback entry is the agent's own deploy: there the app and the sites host
35
+ // are two names on one server, and the app answers on any loopback name, so all
36
+ // of them have to be listed. A single fixed name leaves the site unframable for
37
+ // whichever spelling the app was not opened on.
38
+ const APP_HOSTS_BY_SITES_HOST = {
39
+ "sites.9remote.cc": ["https://9remote.cc"],
40
+ "sites-dev.9remote.cc": ["https://dev.9remote.cc"],
41
+ // No [::1]: CSP's frame-ancestors rejects an IPv6 literal as a source
42
+ // expression — Chrome drops the whole directive over it — so a page opened on
43
+ // that spelling cannot be named, and is left unsupported rather than silently
44
+ // unframable. localhost and 127.0.0.1 are what the agent is actually reached on.
45
+ "sites.localhost": ["http://localhost", "http://127.0.0.1"],
46
+ "sites.127.0.0.1": ["http://127.0.0.1"]
47
+ };
48
+
49
+ // "" for an unknown host: the policy then names the shell alone, which is the
50
+ // safe direction — a load fails rather than an unknown origin being allowed.
51
+ // `port` is the sites origin's own port, and the app answers on the same one.
52
+ function appOriginsFor(hostname, port) {
53
+ const host = String(hostname || "").toLowerCase();
54
+ const bases = APP_HOSTS_BY_SITES_HOST[host];
55
+ if (!bases) return "";
56
+ return bases.map((b) => (port ? `${b}:${port}` : b)).join(" ");
57
+ }