@walkhi/code-relax 0.1.0-beta.3 → 0.1.0-beta.31

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 (837) hide show
  1. package/README.md +83 -71
  2. package/dist/bin/cleanup-accounts.mjs +63 -0
  3. package/dist/bin/codex-remote.mjs +296 -184
  4. package/dist/bin/manage-relay-admin.mjs +20 -0
  5. package/dist/bin/migrate-relay-db.mjs +109 -0
  6. package/dist/bin/publish-android-release.mjs +62 -0
  7. package/dist/bin/self-relay-admin.mjs +8 -0
  8. package/dist/bin/self-relay-demo.mjs +2 -2
  9. package/dist/bin/self-relay-server.mjs +43 -30
  10. package/dist/build-platform.json +1 -0
  11. package/dist/get-shared-service-url.ps1 +6 -6
  12. package/dist/shared/app-server-events.cjs +368 -238
  13. package/dist/shared/code-browser.cjs +159 -0
  14. package/dist/shared/file-changes.cjs +31 -0
  15. package/dist/shared/file-citations.cjs +117 -0
  16. package/dist/shared/file-mentions.cjs +92 -0
  17. package/dist/shared/message-delivery.cjs +54 -0
  18. package/dist/shared/p2p-data-channel.cjs +22 -0
  19. package/dist/shared/p2p-file-channel.cjs +74 -0
  20. package/dist/shared/plan-pricing.cjs +32 -0
  21. package/dist/shared/question-attention.cjs +40 -0
  22. package/dist/shared/server-requests.cjs +157 -5
  23. package/dist/shared/service-endpoints.cjs +21 -0
  24. package/dist/shared/timeline-sync.cjs +111 -5
  25. package/dist/skills/code-relax/SKILL.md +13 -8
  26. package/dist/src/archived-threads.mjs +22 -0
  27. package/dist/src/bounded-process-log.mjs +63 -0
  28. package/dist/src/celsius-client.mjs +107 -0
  29. package/dist/src/code-browser.mjs +100 -0
  30. package/dist/src/desktop-launcher.mjs +33 -38
  31. package/dist/src/dots-approval-timeline.mjs +99 -0
  32. package/dist/src/dots-avatar.mjs +42 -0
  33. package/dist/src/dots-chat-projection.mjs +50 -0
  34. package/dist/src/dots-chat-server.mjs +461 -0
  35. package/dist/src/dots-message-images.mjs +319 -0
  36. package/dist/src/dots-profile.mjs +77 -0
  37. package/dist/src/dots-room-activity.mjs +94 -0
  38. package/dist/src/dots-room-client.mjs +135 -0
  39. package/dist/src/dots-room-projection.mjs +94 -0
  40. package/dist/src/dots-schedules.mjs +66 -0
  41. package/dist/src/dots-summary-tasks.mjs +32 -0
  42. package/dist/src/dots-task-catalog.mjs +430 -0
  43. package/dist/src/dots-task-files.mjs +52 -0
  44. package/dist/src/file-transfer-range.mjs +22 -0
  45. package/dist/src/file-upload-session.mjs +130 -0
  46. package/dist/src/hosted-history-app-server.mjs +103 -0
  47. package/dist/src/hosted-history-cache.mjs +113 -0
  48. package/dist/src/lan-socket-server.mjs +240 -84
  49. package/dist/src/managed-app-server.mjs +53 -16
  50. package/dist/src/message-images.mjs +0 -18
  51. package/dist/src/message-upload-files.mjs +74 -0
  52. package/dist/src/onboarding.mjs +10 -29
  53. package/dist/src/plan-pricing.mjs +47 -0
  54. package/dist/src/platform/README.md +2 -2
  55. package/dist/src/platform/codex-protocol.mjs +46 -0
  56. package/dist/src/platform/codex-runtime.mjs +3 -0
  57. package/dist/src/platform/credential-store.mjs +3 -0
  58. package/dist/src/platform/desktop-host.mjs +3 -0
  59. package/dist/src/platform/desktop-project-registration.mjs +3 -0
  60. package/dist/src/platform/desktop-project-store.mjs +208 -0
  61. package/dist/src/platform/file-opener.mjs +9 -0
  62. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  63. package/dist/src/platform/linux/credential-store.mjs +28 -0
  64. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  65. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  66. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  67. package/dist/src/platform/linux/network-info.mjs +11 -0
  68. package/dist/src/platform/linux/process.mjs +42 -0
  69. package/dist/src/platform/linux/project-locations.mjs +17 -0
  70. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  71. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  72. package/dist/src/platform/linux/service-host.mjs +18 -0
  73. package/dist/src/platform/linux/system.mjs +49 -0
  74. package/dist/src/platform/linux/user-environment.mjs +7 -0
  75. package/dist/src/platform/macos/codex-runtime.mjs +125 -0
  76. package/dist/src/platform/macos/credential-store.mjs +10 -0
  77. package/dist/src/platform/macos/desktop-host.mjs +82 -0
  78. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -0
  79. package/dist/src/platform/macos/network-info.mjs +8 -0
  80. package/dist/src/platform/macos/project-locations.mjs +8 -0
  81. package/dist/src/platform/macos/resident-startup.mjs +84 -0
  82. package/dist/src/platform/macos/retained-runtime.mjs +127 -0
  83. package/dist/src/platform/macos/service-host.mjs +18 -0
  84. package/dist/src/platform/macos/system.mjs +29 -0
  85. package/dist/src/platform/macos/user-environment.mjs +7 -0
  86. package/dist/src/platform/native-package.mjs +58 -0
  87. package/dist/src/platform/network-info.mjs +3 -0
  88. package/dist/src/platform/path-identity.mjs +5 -0
  89. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  90. package/dist/src/platform/posix/runtime-paths.mjs +25 -0
  91. package/dist/src/platform/project-locations.mjs +3 -0
  92. package/dist/src/platform/runtime-paths.mjs +8 -0
  93. package/dist/src/platform/select.mjs +5 -0
  94. package/dist/src/platform/service-host.mjs +3 -0
  95. package/dist/src/platform/user-environment.mjs +3 -0
  96. package/dist/src/platform/windows/activate-desktop.ps1 +48 -0
  97. package/dist/src/platform/windows/codex-runtime.mjs +215 -0
  98. package/dist/src/platform/windows/credential-store.mjs +27 -27
  99. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  100. package/dist/src/platform/windows/desktop-helper.mjs +179 -0
  101. package/dist/src/platform/windows/desktop-host.mjs +43 -23
  102. package/dist/src/platform/windows/desktop-info.ps1 +14 -16
  103. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -0
  104. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
  105. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -28
  106. package/dist/src/platform/windows/dots-profile.mjs +49 -0
  107. package/dist/src/platform/windows/launch-worker.mjs +17 -18
  108. package/dist/src/platform/windows/project-locations.mjs +16 -0
  109. package/dist/src/platform/windows/resident-start.js +20 -0
  110. package/dist/src/platform/windows/resident-startup.ps1 +76 -20
  111. package/dist/src/platform/windows/rustdesk-video-probe.mjs +168 -0
  112. package/dist/src/platform/windows/service-host.mjs +24 -36
  113. package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
  114. package/dist/src/platform/windows/user-environment.mjs +1 -1
  115. package/dist/src/project-archive.mjs +30 -0
  116. package/dist/src/project-create.mjs +121 -0
  117. package/dist/src/project-management.mjs +59 -0
  118. package/dist/src/project-remove.mjs +55 -0
  119. package/dist/src/project-update.mjs +56 -0
  120. package/dist/src/runtime-discovery.mjs +47 -0
  121. package/dist/src/self-relay/account-cleanup.mjs +30 -0
  122. package/dist/src/self-relay/account-connections.mjs +52 -0
  123. package/dist/src/self-relay/admin-cloud.mjs +101 -0
  124. package/dist/src/self-relay/admin-page.mjs +80 -0
  125. package/dist/src/self-relay/admin-server.mjs +74 -0
  126. package/dist/src/self-relay/admin-state.mjs +85 -19
  127. package/dist/src/self-relay/admin-style.mjs +14 -0
  128. package/dist/src/self-relay/android-releases.mjs +61 -0
  129. package/dist/src/self-relay/client.mjs +45 -45
  130. package/dist/src/self-relay/connector.mjs +421 -144
  131. package/dist/src/self-relay/demo.mjs +34 -35
  132. package/dist/src/self-relay/device-info.mjs +27 -0
  133. package/dist/src/self-relay/device-store.mjs +10 -2
  134. package/dist/src/self-relay/diagnostics.mjs +54 -0
  135. package/dist/src/self-relay/email-accounts.mjs +183 -0
  136. package/dist/src/self-relay/file-wire.mjs +13 -0
  137. package/dist/src/self-relay/lan-authorizations.mjs +44 -2
  138. package/dist/src/self-relay/lifecycle.mjs +256 -273
  139. package/dist/src/self-relay/long-pairing.mjs +40 -0
  140. package/dist/src/self-relay/mysql-state.mjs +118 -0
  141. package/dist/src/self-relay/p2p-probe.mjs +41 -5
  142. package/dist/src/self-relay/privacy-page.mjs +26 -0
  143. package/dist/src/self-relay/public-releases.mjs +34 -0
  144. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -0
  145. package/dist/src/self-relay/server.mjs +655 -115
  146. package/dist/src/self-relay/usage-daily.mjs +55 -0
  147. package/dist/src/self-relay/usage-meter.mjs +141 -0
  148. package/dist/src/server.mjs +1486 -1793
  149. package/dist/src/service-control.mjs +17 -7
  150. package/dist/src/service-doctor.mjs +80 -33
  151. package/dist/src/service-health.mjs +2 -2
  152. package/dist/src/service-install.mjs +85 -85
  153. package/dist/src/service-lifecycle.mjs +116 -108
  154. package/dist/src/service-restart.mjs +43 -10
  155. package/dist/src/shared-app-server.mjs +507 -404
  156. package/dist/src/shared-catalog.mjs +95 -13
  157. package/dist/src/shared-recovery.mjs +15 -11
  158. package/dist/src/shared-thread-stream.mjs +147 -63
  159. package/dist/src/skill-install.mjs +0 -13
  160. package/dist/src/turn-model-history.mjs +47 -0
  161. package/dist/src/usage-reset.mjs +17 -0
  162. package/dist/src/workstation-downloads.mjs +120 -0
  163. package/package.json +35 -37
  164. package/tools/postinstall.mjs +30 -21
  165. package/dist/src/app-server-client.mjs +0 -468
  166. package/dist/src/app-server-tasks.mjs +0 -358
  167. package/dist/src/debug-ui.mjs +0 -131
  168. package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
  169. package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
  170. package/dist/src/platform/windows/desktop-tools.mjs +0 -48
  171. package/dist/src/thread-catalog.mjs +0 -47
  172. package/dist/tools/find-desktop-pipe.mjs +0 -10
  173. package/dist/web/activity-view.js +0 -283
  174. package/dist/web/approval-ui.js +0 -188
  175. package/dist/web/capabilities.js +0 -57
  176. package/dist/web/chat-transport.js +0 -202
  177. package/dist/web/chat.css +0 -782
  178. package/dist/web/chat.js +0 -3830
  179. package/dist/web/client-platform.js +0 -11
  180. package/dist/web/community-view.js +0 -20
  181. package/dist/web/community.css +0 -77
  182. package/dist/web/community.html +0 -37
  183. package/dist/web/composer-controller.js +0 -101
  184. package/dist/web/conversation-controller.js +0 -99
  185. package/dist/web/disclosure-state-controller.js +0 -95
  186. package/dist/web/draft-controller.js +0 -103
  187. package/dist/web/harmony-platform.js +0 -52
  188. package/dist/web/highlight-powershell.min.js +0 -39
  189. package/dist/web/highlight.min.js +0 -1262
  190. package/dist/web/history-cache.js +0 -240
  191. package/dist/web/history-controller.js +0 -167
  192. package/dist/web/index.html +0 -325
  193. package/dist/web/lan-session.js +0 -122
  194. package/dist/web/link-action-controller.js +0 -212
  195. package/dist/web/message-send-controller.js +0 -177
  196. package/dist/web/message-view.js +0 -98
  197. package/dist/web/p2p-data-channel.js +0 -50
  198. package/dist/web/p2p-diagnostics.js +0 -28
  199. package/dist/web/p2p-probe.js +0 -123
  200. package/dist/web/page-resume.js +0 -28
  201. package/dist/web/pending-message-store.js +0 -108
  202. package/dist/web/queue-controller.js +0 -82
  203. package/dist/web/resources.json +0 -1
  204. package/dist/web/self-relay-session.js +0 -129
  205. package/dist/web/server-requests.js +0 -67
  206. package/dist/web/station-connection-controller.js +0 -75
  207. package/dist/web/task-list-view.js +0 -296
  208. package/dist/web/thread-attention-controller.js +0 -124
  209. package/dist/web/thread-context-controller.js +0 -30
  210. package/dist/web/thread-list-controller.js +0 -61
  211. package/dist/web/thread-list-sync.js +0 -86
  212. package/dist/web/thread-title-controller.js +0 -57
  213. package/dist/web/timeline-formatters.js +0 -249
  214. package/dist/web/timeline-reducer.js +0 -81
  215. package/dist/web/timeline-renderer.js +0 -161
  216. package/dist/web/timeline-scroll-controller.js +0 -50
  217. package/dist/web/timeline-sync.js +0 -67
  218. package/dist/web/usage-controller.js +0 -258
  219. package/dist/web/vendor/README.md +0 -14
  220. package/dist/web/vendor/highlight-powershell.min.js +0 -39
  221. package/dist/web/vendor/highlight.LICENSE.txt +0 -29
  222. package/dist/web/vendor/highlight.min.js +0 -1262
  223. package/dist/web/vendor/lucide.LICENSE.txt +0 -17
  224. package/node_modules/@img/colour/LICENSE.md +0 -82
  225. package/node_modules/@img/colour/README.md +0 -15
  226. package/node_modules/@img/colour/color.cjs +0 -1596
  227. package/node_modules/@img/colour/index.cjs +0 -1
  228. package/node_modules/@img/colour/index.d.ts +0 -929
  229. package/node_modules/@img/colour/package.json +0 -58
  230. package/node_modules/@img/sharp-win32-x64/LICENSE +0 -191
  231. package/node_modules/@img/sharp-win32-x64/README.md +0 -59
  232. package/node_modules/@img/sharp-win32-x64/index.cjs +0 -1
  233. package/node_modules/@img/sharp-win32-x64/lib/libvips-42.dll +0 -0
  234. package/node_modules/@img/sharp-win32-x64/lib/libvips-cpp-8.18.6.dll +0 -0
  235. package/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64-0.35.4.node +0 -0
  236. package/node_modules/@img/sharp-win32-x64/package.json +0 -40
  237. package/node_modules/@img/sharp-win32-x64/versions.json +0 -30
  238. package/node_modules/@node-datachannel/win32-x64-msvc/node_datachannel.node +0 -0
  239. package/node_modules/@node-datachannel/win32-x64-msvc/package.json +0 -20
  240. package/node_modules/ansi-regex/index.d.ts +0 -37
  241. package/node_modules/ansi-regex/index.js +0 -10
  242. package/node_modules/ansi-regex/license +0 -9
  243. package/node_modules/ansi-regex/package.json +0 -55
  244. package/node_modules/ansi-regex/readme.md +0 -78
  245. package/node_modules/ansi-styles/index.d.ts +0 -345
  246. package/node_modules/ansi-styles/index.js +0 -163
  247. package/node_modules/ansi-styles/license +0 -9
  248. package/node_modules/ansi-styles/package.json +0 -56
  249. package/node_modules/ansi-styles/readme.md +0 -152
  250. package/node_modules/camelcase/index.d.ts +0 -63
  251. package/node_modules/camelcase/index.js +0 -76
  252. package/node_modules/camelcase/license +0 -9
  253. package/node_modules/camelcase/package.json +0 -43
  254. package/node_modules/camelcase/readme.md +0 -99
  255. package/node_modules/cliui/CHANGELOG.md +0 -76
  256. package/node_modules/cliui/LICENSE.txt +0 -14
  257. package/node_modules/cliui/README.md +0 -115
  258. package/node_modules/cliui/index.js +0 -354
  259. package/node_modules/cliui/package.json +0 -65
  260. package/node_modules/color-convert/CHANGELOG.md +0 -54
  261. package/node_modules/color-convert/LICENSE +0 -21
  262. package/node_modules/color-convert/README.md +0 -68
  263. package/node_modules/color-convert/conversions.js +0 -839
  264. package/node_modules/color-convert/index.js +0 -81
  265. package/node_modules/color-convert/package.json +0 -48
  266. package/node_modules/color-convert/route.js +0 -97
  267. package/node_modules/color-name/LICENSE +0 -8
  268. package/node_modules/color-name/README.md +0 -11
  269. package/node_modules/color-name/index.js +0 -152
  270. package/node_modules/color-name/package.json +0 -28
  271. package/node_modules/decamelize/index.js +0 -13
  272. package/node_modules/decamelize/license +0 -21
  273. package/node_modules/decamelize/package.json +0 -38
  274. package/node_modules/decamelize/readme.md +0 -48
  275. package/node_modules/detect-libc/LICENSE +0 -201
  276. package/node_modules/detect-libc/README.md +0 -163
  277. package/node_modules/detect-libc/index.d.ts +0 -14
  278. package/node_modules/detect-libc/lib/detect-libc.js +0 -313
  279. package/node_modules/detect-libc/lib/elf.js +0 -39
  280. package/node_modules/detect-libc/lib/filesystem.js +0 -51
  281. package/node_modules/detect-libc/lib/process.js +0 -24
  282. package/node_modules/detect-libc/package.json +0 -44
  283. package/node_modules/dijkstrajs/.travis.yml +0 -4
  284. package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
  285. package/node_modules/dijkstrajs/LICENSE.md +0 -19
  286. package/node_modules/dijkstrajs/README.md +0 -18
  287. package/node_modules/dijkstrajs/dijkstra.js +0 -165
  288. package/node_modules/dijkstrajs/package.json +0 -60
  289. package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
  290. package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  291. package/node_modules/emoji-regex/README.md +0 -73
  292. package/node_modules/emoji-regex/es2015/index.js +0 -6
  293. package/node_modules/emoji-regex/es2015/text.js +0 -6
  294. package/node_modules/emoji-regex/index.d.ts +0 -23
  295. package/node_modules/emoji-regex/index.js +0 -6
  296. package/node_modules/emoji-regex/package.json +0 -50
  297. package/node_modules/emoji-regex/text.js +0 -6
  298. package/node_modules/find-up/index.d.ts +0 -137
  299. package/node_modules/find-up/index.js +0 -89
  300. package/node_modules/find-up/license +0 -9
  301. package/node_modules/find-up/package.json +0 -53
  302. package/node_modules/find-up/readme.md +0 -156
  303. package/node_modules/get-caller-file/LICENSE.md +0 -6
  304. package/node_modules/get-caller-file/README.md +0 -41
  305. package/node_modules/get-caller-file/index.d.ts +0 -2
  306. package/node_modules/get-caller-file/index.js +0 -22
  307. package/node_modules/get-caller-file/index.js.map +0 -1
  308. package/node_modules/get-caller-file/package.json +0 -42
  309. package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
  310. package/node_modules/is-fullwidth-code-point/index.js +0 -50
  311. package/node_modules/is-fullwidth-code-point/license +0 -9
  312. package/node_modules/is-fullwidth-code-point/package.json +0 -42
  313. package/node_modules/is-fullwidth-code-point/readme.md +0 -39
  314. package/node_modules/locate-path/index.d.ts +0 -83
  315. package/node_modules/locate-path/index.js +0 -65
  316. package/node_modules/locate-path/license +0 -9
  317. package/node_modules/locate-path/package.json +0 -45
  318. package/node_modules/locate-path/readme.md +0 -122
  319. package/node_modules/node-datachannel/.clang-format +0 -17
  320. package/node_modules/node-datachannel/.editorconfig +0 -12
  321. package/node_modules/node-datachannel/.eslintignore +0 -8
  322. package/node_modules/node-datachannel/.eslintrc.json +0 -27
  323. package/node_modules/node-datachannel/.gitmodules +0 -3
  324. package/node_modules/node-datachannel/.prettierignore +0 -7
  325. package/node_modules/node-datachannel/.prettierrc +0 -13
  326. package/node_modules/node-datachannel/API.md +0 -247
  327. package/node_modules/node-datachannel/BULDING.md +0 -26
  328. package/node_modules/node-datachannel/CMakeLists.txt +0 -153
  329. package/node_modules/node-datachannel/LICENSE +0 -373
  330. package/node_modules/node-datachannel/README.md +0 -129
  331. package/node_modules/node-datachannel/dist/cjs/index.cjs +0 -11
  332. package/node_modules/node-datachannel/dist/cjs/index.cjs.map +0 -1
  333. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs +0 -101
  334. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs.map +0 -1
  335. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs +0 -91
  336. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs.map +0 -1
  337. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs +0 -94
  338. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs.map +0 -1
  339. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs +0 -45
  340. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs.map +0 -1
  341. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs +0 -8
  342. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs.map +0 -1
  343. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs +0 -86
  344. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs.map +0 -1
  345. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs +0 -34
  346. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs.map +0 -1
  347. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs +0 -34
  348. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs.map +0 -1
  349. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs +0 -214
  350. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs.map +0 -1
  351. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs +0 -53
  352. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +0 -1
  353. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs +0 -83
  354. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs.map +0 -1
  355. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs +0 -132
  356. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs.map +0 -1
  357. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs +0 -94
  358. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs.map +0 -1
  359. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs +0 -434
  360. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs.map +0 -1
  361. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs +0 -59
  362. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs.map +0 -1
  363. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs +0 -45
  364. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs.map +0 -1
  365. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs +0 -42
  366. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs.map +0 -1
  367. package/node_modules/node-datachannel/dist/esm/index.mjs +0 -8
  368. package/node_modules/node-datachannel/dist/esm/index.mjs.map +0 -1
  369. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs +0 -78
  370. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs.map +0 -1
  371. package/node_modules/node-datachannel/dist/esm/lib/index.mjs +0 -64
  372. package/node_modules/node-datachannel/dist/esm/lib/index.mjs.map +0 -1
  373. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs +0 -78
  374. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs.map +0 -1
  375. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs +0 -43
  376. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs.map +0 -1
  377. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs +0 -6
  378. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs.map +0 -1
  379. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs +0 -82
  380. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs.map +0 -1
  381. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs +0 -28
  382. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs.map +0 -1
  383. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs +0 -30
  384. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs.map +0 -1
  385. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs +0 -210
  386. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs.map +0 -1
  387. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs +0 -49
  388. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs.map +0 -1
  389. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs +0 -79
  390. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs.map +0 -1
  391. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs +0 -128
  392. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs.map +0 -1
  393. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs +0 -89
  394. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs.map +0 -1
  395. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs +0 -430
  396. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs.map +0 -1
  397. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs +0 -55
  398. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs.map +0 -1
  399. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs +0 -41
  400. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs.map +0 -1
  401. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs +0 -27
  402. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs.map +0 -1
  403. package/node_modules/node-datachannel/dist/types/lib/datachannel-stream.d.ts +0 -24
  404. package/node_modules/node-datachannel/dist/types/lib/index.d.ts +0 -241
  405. package/node_modules/node-datachannel/dist/types/lib/types.d.ts +0 -119
  406. package/node_modules/node-datachannel/dist/types/lib/websocket-server.d.ts +0 -13
  407. package/node_modules/node-datachannel/dist/types/lib/websocket.d.ts +0 -25
  408. package/node_modules/node-datachannel/dist/types/polyfill/Events.d.ts +0 -15
  409. package/node_modules/node-datachannel/dist/types/polyfill/RTCCertificate.d.ts +0 -9
  410. package/node_modules/node-datachannel/dist/types/polyfill/RTCDataChannel.d.ts +0 -29
  411. package/node_modules/node-datachannel/dist/types/polyfill/RTCDtlsTransport.d.ts +0 -15
  412. package/node_modules/node-datachannel/dist/types/polyfill/RTCError.d.ts +0 -17
  413. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceCandidate.d.ts +0 -21
  414. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceTransport.d.ts +0 -30
  415. package/node_modules/node-datachannel/dist/types/polyfill/RTCPeerConnection.d.ts +0 -64
  416. package/node_modules/node-datachannel/dist/types/polyfill/RTCSctpTransport.d.ts +0 -15
  417. package/node_modules/node-datachannel/dist/types/polyfill/RTCSessionDescription.d.ts +0 -10
  418. package/node_modules/node-datachannel/dist/types/polyfill/index.d.ts +0 -26
  419. package/node_modules/node-datachannel/jest.config.ts +0 -14
  420. package/node_modules/node-datachannel/package.json +0 -133
  421. package/node_modules/node-datachannel/rollup.config.mjs +0 -67
  422. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.cpp +0 -530
  423. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.h +0 -63
  424. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.cpp +0 -172
  425. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.h +0 -43
  426. package/node_modules/node-datachannel/src/cpp/main.cpp +0 -58
  427. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.cpp +0 -457
  428. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.h +0 -52
  429. package/node_modules/node-datachannel/src/cpp/media-av1packetization.cpp +0 -24
  430. package/node_modules/node-datachannel/src/cpp/media-av1packetization.h +0 -11
  431. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.cpp +0 -126
  432. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.h +0 -29
  433. package/node_modules/node-datachannel/src/cpp/media-direction.cpp +0 -43
  434. package/node_modules/node-datachannel/src/cpp/media-direction.h +0 -10
  435. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.cpp +0 -126
  436. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.h +0 -30
  437. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.cpp +0 -126
  438. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.h +0 -30
  439. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.cpp +0 -32
  440. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.h +0 -11
  441. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.cpp +0 -31
  442. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.h +0 -11
  443. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.cpp +0 -79
  444. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.h +0 -28
  445. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.cpp +0 -68
  446. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.h +0 -28
  447. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.cpp +0 -57
  448. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.h +0 -28
  449. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.cpp +0 -93
  450. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.h +0 -30
  451. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.cpp +0 -167
  452. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.h +0 -35
  453. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.cpp +0 -95
  454. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.h +0 -30
  455. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.cpp +0 -458
  456. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.h +0 -61
  457. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.cpp +0 -526
  458. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.h +0 -56
  459. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.cpp +0 -1298
  460. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.h +0 -89
  461. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.cpp +0 -205
  462. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.h +0 -24
  463. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.cpp +0 -57
  464. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.h +0 -47
  465. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.cpp +0 -275
  466. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.h +0 -41
  467. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.cpp +0 -796
  468. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.h +0 -63
  469. package/node_modules/node-datachannel/src/index.ts +0 -9
  470. package/node_modules/node-datachannel/src/lib/datachannel-stream.ts +0 -100
  471. package/node_modules/node-datachannel/src/lib/index.ts +0 -290
  472. package/node_modules/node-datachannel/src/lib/node-datachannel.ts +0 -84
  473. package/node_modules/node-datachannel/src/lib/types.ts +0 -169
  474. package/node_modules/node-datachannel/src/lib/websocket-server.ts +0 -37
  475. package/node_modules/node-datachannel/src/lib/websocket.ts +0 -26
  476. package/node_modules/node-datachannel/src/polyfill/Events.ts +0 -82
  477. package/node_modules/node-datachannel/src/polyfill/Exception.ts +0 -37
  478. package/node_modules/node-datachannel/src/polyfill/README.md +0 -41
  479. package/node_modules/node-datachannel/src/polyfill/RTCCertificate.ts +0 -21
  480. package/node_modules/node-datachannel/src/polyfill/RTCDataChannel.ts +0 -225
  481. package/node_modules/node-datachannel/src/polyfill/RTCDtlsTransport.ts +0 -46
  482. package/node_modules/node-datachannel/src/polyfill/RTCError.ts +0 -78
  483. package/node_modules/node-datachannel/src/polyfill/RTCIceCandidate.ts +0 -128
  484. package/node_modules/node-datachannel/src/polyfill/RTCIceTransport.ts +0 -90
  485. package/node_modules/node-datachannel/src/polyfill/RTCPeerConnection.ts +0 -527
  486. package/node_modules/node-datachannel/src/polyfill/RTCSctpTransport.ts +0 -51
  487. package/node_modules/node-datachannel/src/polyfill/RTCSessionDescription.ts +0 -41
  488. package/node_modules/node-datachannel/src/polyfill/index.ts +0 -38
  489. package/node_modules/node-datachannel/tsconfig.json +0 -21
  490. package/node_modules/p-limit/index.d.ts +0 -38
  491. package/node_modules/p-limit/index.js +0 -57
  492. package/node_modules/p-limit/license +0 -9
  493. package/node_modules/p-limit/package.json +0 -52
  494. package/node_modules/p-limit/readme.md +0 -101
  495. package/node_modules/p-locate/index.d.ts +0 -64
  496. package/node_modules/p-locate/index.js +0 -52
  497. package/node_modules/p-locate/license +0 -9
  498. package/node_modules/p-locate/package.json +0 -53
  499. package/node_modules/p-locate/readme.md +0 -90
  500. package/node_modules/p-try/index.d.ts +0 -39
  501. package/node_modules/p-try/index.js +0 -9
  502. package/node_modules/p-try/license +0 -9
  503. package/node_modules/p-try/package.json +0 -42
  504. package/node_modules/p-try/readme.md +0 -58
  505. package/node_modules/path-exists/index.d.ts +0 -28
  506. package/node_modules/path-exists/index.js +0 -23
  507. package/node_modules/path-exists/license +0 -9
  508. package/node_modules/path-exists/package.json +0 -39
  509. package/node_modules/path-exists/readme.md +0 -52
  510. package/node_modules/pngjs/.eslintignore +0 -2
  511. package/node_modules/pngjs/.eslintrc.json +0 -19
  512. package/node_modules/pngjs/.prettierignore +0 -3
  513. package/node_modules/pngjs/LICENSE +0 -20
  514. package/node_modules/pngjs/README.md +0 -429
  515. package/node_modules/pngjs/browser.js +0 -16096
  516. package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
  517. package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
  518. package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
  519. package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
  520. package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
  521. package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
  522. package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
  523. package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
  524. package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
  525. package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
  526. package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
  527. package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
  528. package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
  529. package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
  530. package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
  531. package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
  532. package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
  533. package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
  534. package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
  535. package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
  536. package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
  537. package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
  538. package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
  539. package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
  540. package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
  541. package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
  542. package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  543. package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
  544. package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
  545. package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
  546. package/node_modules/pngjs/coverage/lcov.info +0 -2177
  547. package/node_modules/pngjs/lib/bitmapper.js +0 -267
  548. package/node_modules/pngjs/lib/bitpacker.js +0 -158
  549. package/node_modules/pngjs/lib/chunkstream.js +0 -189
  550. package/node_modules/pngjs/lib/constants.js +0 -32
  551. package/node_modules/pngjs/lib/crc.js +0 -40
  552. package/node_modules/pngjs/lib/filter-pack.js +0 -171
  553. package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
  554. package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
  555. package/node_modules/pngjs/lib/filter-parse.js +0 -177
  556. package/node_modules/pngjs/lib/format-normaliser.js +0 -93
  557. package/node_modules/pngjs/lib/interlace.js +0 -95
  558. package/node_modules/pngjs/lib/packer-async.js +0 -50
  559. package/node_modules/pngjs/lib/packer-sync.js +0 -56
  560. package/node_modules/pngjs/lib/packer.js +0 -129
  561. package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
  562. package/node_modules/pngjs/lib/parser-async.js +0 -165
  563. package/node_modules/pngjs/lib/parser-sync.js +0 -108
  564. package/node_modules/pngjs/lib/parser.js +0 -290
  565. package/node_modules/pngjs/lib/png-sync.js +0 -12
  566. package/node_modules/pngjs/lib/png.js +0 -194
  567. package/node_modules/pngjs/lib/sync-inflate.js +0 -168
  568. package/node_modules/pngjs/lib/sync-reader.js +0 -45
  569. package/node_modules/pngjs/package.json +0 -73
  570. package/node_modules/qrcode/README.md +0 -770
  571. package/node_modules/qrcode/bin/qrcode +0 -159
  572. package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
  573. package/node_modules/qrcode/helper/to-sjis.js +0 -105
  574. package/node_modules/qrcode/lib/browser.js +0 -76
  575. package/node_modules/qrcode/lib/can-promise.js +0 -7
  576. package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
  577. package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
  578. package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
  579. package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
  580. package/node_modules/qrcode/lib/core/byte-data.js +0 -30
  581. package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
  582. package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
  583. package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
  584. package/node_modules/qrcode/lib/core/format-info.js +0 -29
  585. package/node_modules/qrcode/lib/core/galois-field.js +0 -69
  586. package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
  587. package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
  588. package/node_modules/qrcode/lib/core/mode.js +0 -167
  589. package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
  590. package/node_modules/qrcode/lib/core/polynomial.js +0 -62
  591. package/node_modules/qrcode/lib/core/qrcode.js +0 -495
  592. package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
  593. package/node_modules/qrcode/lib/core/regex.js +0 -31
  594. package/node_modules/qrcode/lib/core/segments.js +0 -330
  595. package/node_modules/qrcode/lib/core/utils.js +0 -63
  596. package/node_modules/qrcode/lib/core/version-check.js +0 -9
  597. package/node_modules/qrcode/lib/core/version.js +0 -163
  598. package/node_modules/qrcode/lib/index.js +0 -12
  599. package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
  600. package/node_modules/qrcode/lib/renderer/png.js +0 -78
  601. package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
  602. package/node_modules/qrcode/lib/renderer/svg.js +0 -19
  603. package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
  604. package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
  605. package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
  606. package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
  607. package/node_modules/qrcode/lib/renderer/utils.js +0 -99
  608. package/node_modules/qrcode/lib/server.js +0 -138
  609. package/node_modules/qrcode/license +0 -10
  610. package/node_modules/qrcode/package.json +0 -77
  611. package/node_modules/require-directory/.jshintrc +0 -67
  612. package/node_modules/require-directory/.npmignore +0 -1
  613. package/node_modules/require-directory/.travis.yml +0 -3
  614. package/node_modules/require-directory/LICENSE +0 -22
  615. package/node_modules/require-directory/README.markdown +0 -184
  616. package/node_modules/require-directory/index.js +0 -86
  617. package/node_modules/require-directory/package.json +0 -40
  618. package/node_modules/require-main-filename/CHANGELOG.md +0 -26
  619. package/node_modules/require-main-filename/LICENSE.txt +0 -14
  620. package/node_modules/require-main-filename/README.md +0 -26
  621. package/node_modules/require-main-filename/index.js +0 -18
  622. package/node_modules/require-main-filename/package.json +0 -35
  623. package/node_modules/semver/LICENSE +0 -15
  624. package/node_modules/semver/README.md +0 -680
  625. package/node_modules/semver/bin/semver.js +0 -195
  626. package/node_modules/semver/classes/comparator.js +0 -143
  627. package/node_modules/semver/classes/index.js +0 -7
  628. package/node_modules/semver/classes/range.js +0 -577
  629. package/node_modules/semver/classes/semver.js +0 -350
  630. package/node_modules/semver/functions/clean.js +0 -8
  631. package/node_modules/semver/functions/cmp.js +0 -54
  632. package/node_modules/semver/functions/coerce.js +0 -62
  633. package/node_modules/semver/functions/compare-build.js +0 -9
  634. package/node_modules/semver/functions/compare-loose.js +0 -5
  635. package/node_modules/semver/functions/compare.js +0 -7
  636. package/node_modules/semver/functions/diff.js +0 -60
  637. package/node_modules/semver/functions/eq.js +0 -5
  638. package/node_modules/semver/functions/gt.js +0 -5
  639. package/node_modules/semver/functions/gte.js +0 -5
  640. package/node_modules/semver/functions/inc.js +0 -21
  641. package/node_modules/semver/functions/lt.js +0 -5
  642. package/node_modules/semver/functions/lte.js +0 -5
  643. package/node_modules/semver/functions/major.js +0 -5
  644. package/node_modules/semver/functions/minor.js +0 -5
  645. package/node_modules/semver/functions/neq.js +0 -5
  646. package/node_modules/semver/functions/parse.js +0 -18
  647. package/node_modules/semver/functions/patch.js +0 -5
  648. package/node_modules/semver/functions/prerelease.js +0 -8
  649. package/node_modules/semver/functions/rcompare.js +0 -5
  650. package/node_modules/semver/functions/rsort.js +0 -5
  651. package/node_modules/semver/functions/satisfies.js +0 -12
  652. package/node_modules/semver/functions/sort.js +0 -5
  653. package/node_modules/semver/functions/truncate.js +0 -48
  654. package/node_modules/semver/functions/valid.js +0 -8
  655. package/node_modules/semver/index.js +0 -93
  656. package/node_modules/semver/internal/constants.js +0 -37
  657. package/node_modules/semver/internal/debug.js +0 -11
  658. package/node_modules/semver/internal/identifiers.js +0 -29
  659. package/node_modules/semver/internal/lrucache.js +0 -42
  660. package/node_modules/semver/internal/parse-options.js +0 -17
  661. package/node_modules/semver/internal/re.js +0 -223
  662. package/node_modules/semver/package.json +0 -78
  663. package/node_modules/semver/preload.js +0 -4
  664. package/node_modules/semver/range.bnf +0 -17
  665. package/node_modules/semver/ranges/gtr.js +0 -6
  666. package/node_modules/semver/ranges/intersects.js +0 -9
  667. package/node_modules/semver/ranges/ltr.js +0 -6
  668. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  669. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  670. package/node_modules/semver/ranges/min-version.js +0 -63
  671. package/node_modules/semver/ranges/outside.js +0 -82
  672. package/node_modules/semver/ranges/simplify.js +0 -49
  673. package/node_modules/semver/ranges/subset.js +0 -249
  674. package/node_modules/semver/ranges/to-comparators.js +0 -10
  675. package/node_modules/semver/ranges/valid.js +0 -13
  676. package/node_modules/set-blocking/CHANGELOG.md +0 -26
  677. package/node_modules/set-blocking/LICENSE.txt +0 -14
  678. package/node_modules/set-blocking/README.md +0 -31
  679. package/node_modules/set-blocking/index.js +0 -7
  680. package/node_modules/set-blocking/package.json +0 -42
  681. package/node_modules/sharp/LICENSE +0 -191
  682. package/node_modules/sharp/README.md +0 -112
  683. package/node_modules/sharp/dist/channel.cjs +0 -177
  684. package/node_modules/sharp/dist/channel.mjs +0 -177
  685. package/node_modules/sharp/dist/colour.cjs +0 -199
  686. package/node_modules/sharp/dist/colour.mjs +0 -199
  687. package/node_modules/sharp/dist/composite.cjs +0 -213
  688. package/node_modules/sharp/dist/composite.mjs +0 -213
  689. package/node_modules/sharp/dist/constructor.cjs +0 -511
  690. package/node_modules/sharp/dist/constructor.mjs +0 -511
  691. package/node_modules/sharp/dist/index.cjs +0 -25
  692. package/node_modules/sharp/dist/index.d.cts +0 -1999
  693. package/node_modules/sharp/dist/index.d.mts +0 -2046
  694. package/node_modules/sharp/dist/index.mjs +0 -25
  695. package/node_modules/sharp/dist/input.cjs +0 -819
  696. package/node_modules/sharp/dist/input.mjs +0 -819
  697. package/node_modules/sharp/dist/is.cjs +0 -143
  698. package/node_modules/sharp/dist/is.mjs +0 -143
  699. package/node_modules/sharp/dist/libvips.cjs +0 -212
  700. package/node_modules/sharp/dist/libvips.mjs +0 -212
  701. package/node_modules/sharp/dist/operation.cjs +0 -1002
  702. package/node_modules/sharp/dist/operation.mjs +0 -1002
  703. package/node_modules/sharp/dist/output.cjs +0 -1785
  704. package/node_modules/sharp/dist/output.mjs +0 -1785
  705. package/node_modules/sharp/dist/resize.cjs +0 -617
  706. package/node_modules/sharp/dist/resize.mjs +0 -617
  707. package/node_modules/sharp/dist/sharp.cjs +0 -174
  708. package/node_modules/sharp/dist/sharp.mjs +0 -174
  709. package/node_modules/sharp/dist/utility.cjs +0 -301
  710. package/node_modules/sharp/dist/utility.mjs +0 -301
  711. package/node_modules/sharp/install/build.js +0 -38
  712. package/node_modules/sharp/lib/index.d.ts +0 -1999
  713. package/node_modules/sharp/package.json +0 -227
  714. package/node_modules/sharp/src/binding.gyp +0 -303
  715. package/node_modules/sharp/src/common.cc +0 -1186
  716. package/node_modules/sharp/src/common.h +0 -421
  717. package/node_modules/sharp/src/metadata.cc +0 -404
  718. package/node_modules/sharp/src/metadata.h +0 -95
  719. package/node_modules/sharp/src/operations.cc +0 -516
  720. package/node_modules/sharp/src/operations.h +0 -137
  721. package/node_modules/sharp/src/pipeline.cc +0 -1948
  722. package/node_modules/sharp/src/pipeline.h +0 -422
  723. package/node_modules/sharp/src/sharp.cc +0 -43
  724. package/node_modules/sharp/src/stats.cc +0 -186
  725. package/node_modules/sharp/src/stats.h +0 -62
  726. package/node_modules/sharp/src/utilities.cc +0 -289
  727. package/node_modules/sharp/src/utilities.h +0 -22
  728. package/node_modules/string-width/index.d.ts +0 -29
  729. package/node_modules/string-width/index.js +0 -47
  730. package/node_modules/string-width/license +0 -9
  731. package/node_modules/string-width/package.json +0 -56
  732. package/node_modules/string-width/readme.md +0 -50
  733. package/node_modules/strip-ansi/index.d.ts +0 -17
  734. package/node_modules/strip-ansi/index.js +0 -4
  735. package/node_modules/strip-ansi/license +0 -9
  736. package/node_modules/strip-ansi/package.json +0 -54
  737. package/node_modules/strip-ansi/readme.md +0 -46
  738. package/node_modules/which-module/LICENSE +0 -13
  739. package/node_modules/which-module/README.md +0 -58
  740. package/node_modules/which-module/index.js +0 -9
  741. package/node_modules/which-module/package.json +0 -41
  742. package/node_modules/wrap-ansi/index.js +0 -186
  743. package/node_modules/wrap-ansi/license +0 -9
  744. package/node_modules/wrap-ansi/package.json +0 -61
  745. package/node_modules/wrap-ansi/readme.md +0 -97
  746. package/node_modules/ws/LICENSE +0 -20
  747. package/node_modules/ws/README.md +0 -548
  748. package/node_modules/ws/browser.js +0 -8
  749. package/node_modules/ws/index.js +0 -22
  750. package/node_modules/ws/lib/buffer-util.js +0 -131
  751. package/node_modules/ws/lib/constants.js +0 -19
  752. package/node_modules/ws/lib/event-target.js +0 -292
  753. package/node_modules/ws/lib/extension.js +0 -203
  754. package/node_modules/ws/lib/limiter.js +0 -55
  755. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  756. package/node_modules/ws/lib/receiver.js +0 -743
  757. package/node_modules/ws/lib/sender.js +0 -607
  758. package/node_modules/ws/lib/stream.js +0 -161
  759. package/node_modules/ws/lib/subprotocol.js +0 -62
  760. package/node_modules/ws/lib/validation.js +0 -152
  761. package/node_modules/ws/lib/websocket-server.js +0 -562
  762. package/node_modules/ws/lib/websocket.js +0 -1407
  763. package/node_modules/ws/package.json +0 -74
  764. package/node_modules/ws/wrapper.mjs +0 -21
  765. package/node_modules/y18n/CHANGELOG.md +0 -35
  766. package/node_modules/y18n/LICENSE +0 -13
  767. package/node_modules/y18n/README.md +0 -109
  768. package/node_modules/y18n/index.js +0 -188
  769. package/node_modules/y18n/package.json +0 -39
  770. package/node_modules/yargs/CHANGELOG.md +0 -420
  771. package/node_modules/yargs/LICENSE +0 -21
  772. package/node_modules/yargs/README.md +0 -140
  773. package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
  774. package/node_modules/yargs/build/lib/apply-extends.js +0 -65
  775. package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
  776. package/node_modules/yargs/build/lib/argsert.js +0 -65
  777. package/node_modules/yargs/build/lib/command.d.ts +0 -64
  778. package/node_modules/yargs/build/lib/command.js +0 -416
  779. package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
  780. package/node_modules/yargs/build/lib/common-types.js +0 -25
  781. package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
  782. package/node_modules/yargs/build/lib/completion-templates.js +0 -50
  783. package/node_modules/yargs/build/lib/completion.d.ts +0 -21
  784. package/node_modules/yargs/build/lib/completion.js +0 -135
  785. package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
  786. package/node_modules/yargs/build/lib/is-promise.js +0 -9
  787. package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
  788. package/node_modules/yargs/build/lib/levenshtein.js +0 -58
  789. package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
  790. package/node_modules/yargs/build/lib/middleware.js +0 -57
  791. package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
  792. package/node_modules/yargs/build/lib/obj-filter.js +0 -14
  793. package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
  794. package/node_modules/yargs/build/lib/parse-command.js +0 -36
  795. package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
  796. package/node_modules/yargs/build/lib/process-argv.js +0 -31
  797. package/node_modules/yargs/build/lib/usage.d.ts +0 -49
  798. package/node_modules/yargs/build/lib/usage.js +0 -540
  799. package/node_modules/yargs/build/lib/validation.d.ts +0 -34
  800. package/node_modules/yargs/build/lib/validation.js +0 -330
  801. package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
  802. package/node_modules/yargs/build/lib/yargs.js +0 -1190
  803. package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
  804. package/node_modules/yargs/build/lib/yerror.js +0 -11
  805. package/node_modules/yargs/index.js +0 -40
  806. package/node_modules/yargs/locales/be.json +0 -46
  807. package/node_modules/yargs/locales/de.json +0 -46
  808. package/node_modules/yargs/locales/en.json +0 -51
  809. package/node_modules/yargs/locales/es.json +0 -46
  810. package/node_modules/yargs/locales/fi.json +0 -49
  811. package/node_modules/yargs/locales/fr.json +0 -53
  812. package/node_modules/yargs/locales/hi.json +0 -49
  813. package/node_modules/yargs/locales/hu.json +0 -46
  814. package/node_modules/yargs/locales/id.json +0 -50
  815. package/node_modules/yargs/locales/it.json +0 -46
  816. package/node_modules/yargs/locales/ja.json +0 -51
  817. package/node_modules/yargs/locales/ko.json +0 -49
  818. package/node_modules/yargs/locales/nb.json +0 -44
  819. package/node_modules/yargs/locales/nl.json +0 -49
  820. package/node_modules/yargs/locales/nn.json +0 -44
  821. package/node_modules/yargs/locales/pirate.json +0 -13
  822. package/node_modules/yargs/locales/pl.json +0 -49
  823. package/node_modules/yargs/locales/pt.json +0 -45
  824. package/node_modules/yargs/locales/pt_BR.json +0 -48
  825. package/node_modules/yargs/locales/ru.json +0 -46
  826. package/node_modules/yargs/locales/th.json +0 -46
  827. package/node_modules/yargs/locales/tr.json +0 -48
  828. package/node_modules/yargs/locales/zh_CN.json +0 -48
  829. package/node_modules/yargs/locales/zh_TW.json +0 -47
  830. package/node_modules/yargs/package.json +0 -92
  831. package/node_modules/yargs/yargs.js +0 -14
  832. package/node_modules/yargs-parser/CHANGELOG.md +0 -601
  833. package/node_modules/yargs-parser/LICENSE.txt +0 -14
  834. package/node_modules/yargs-parser/README.md +0 -449
  835. package/node_modules/yargs-parser/index.js +0 -1032
  836. package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
  837. package/node_modules/yargs-parser/package.json +0 -46
@@ -1,18 +1,40 @@
1
- import http from 'node:http';
2
- import { deviceStore, validId } from './device-store.mjs';
3
- import { randomUUID } from 'node:crypto';
4
- import { WebSocketServer } from 'ws';
1
+ import { privacyPage } from './privacy-page.mjs';
2
+ import { createAndroidReleaseHandler } from './android-releases.mjs';
3
+ import { androidReleaseStorage } from '../../relay-admin/android-release-storage.mjs';
4
+ import { longPairing } from './long-pairing.mjs';
5
+ import http from 'node:http';
6
+ import fs from 'node:fs';
7
+ import { isIP } from 'node:net';
8
+ import { deviceStore, validId } from './device-store.mjs';
9
+ import { randomUUID, timingSafeEqual } from 'node:crypto';
10
+ import { WebSocketServer } from 'ws';
5
11
  import { secret, digest, matches, validSecret, send, decode } from './wire.mjs';
12
+ import { routedFileBytes, parseFileBytes } from './file-wire.mjs';
6
13
  import { readDebugAllowlist, debugAllowed } from './debug-allowlist.mjs';
7
14
  import { validProbeSignal } from './p2p-probe.mjs';
8
- import { relayAdminState } from './admin-state.mjs';
15
+ import { MAX_ADMIN_REQUEST_BYTES, relayAdminState } from './admin-state.mjs';
16
+ import { openRelayMysql } from './mysql-state.mjs';
17
+ import { openEmailAccounts } from './email-accounts.mjs';
18
+ import { accountConnections } from './account-connections.mjs';
19
+ import { usageMeter, usageReport } from './usage-meter.mjs';
20
+ import { relayDiagnostics } from './diagnostics.mjs';
21
+ import { connectionInfo, deviceInfo } from './device-info.mjs';
9
22
 
10
- // Always loopback; a TLS reverse proxy supplies the private-trial Internet endpoint.
23
+ // Always loopback; a TLS reverse proxy supplies the private-trial Internet endpoint.
11
24
  export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlMs = 120000,
12
- maxConnections = 32, maxClientsPerHost = 16, maxPayload = 8 * 1024 * 1024, stateFile,
13
- maxRelaySessions = 8, adminStateFile, publicRelayUrl = '', appDownloadUrl = '', debugAllowlistFile, log = () => {} } = {}) {
25
+ maxConnections = 128, maxPayload = 8 * 1024 * 1024, stateFile,
26
+ maxRelaySessions = 8, adminStateFile, mysqlConfigFile, accountConfigFile, usageDiagnosticKeyFile,
27
+ publicRelayUrl = '', appDownloadUrl = '', debugAllowlistFile, longPairingFile,
28
+ androidReleaseConfigFile, androidReleaseCredentialFile, log = () => {} } = {}) {
29
+ const androidReleases = createAndroidReleaseHandler({
30
+ ...(androidReleaseConfigFile ? androidReleaseStorage(androidReleaseConfigFile, androidReleaseCredentialFile) : {}), log });
31
+ const longInvitations = longPairing(longPairingFile);
32
+ longInvitations.list();
33
+ function longInvitation(code) {
34
+ try { return longInvitations.find(code); }
35
+ catch (error) { log({ event: 'long-pairing-error', message: error.message }); return undefined; }
36
+ }
14
37
  if (!validSecret(hostKey)) throw new Error('Invalid host credential');
15
- if (!Number.isSafeInteger(maxClientsPerHost) || maxClientsPerHost < 1 || maxClientsPerHost > 64) throw new Error('Invalid mobile device limit');
16
38
  if (!Number.isSafeInteger(maxRelaySessions) || maxRelaySessions < 1 || maxRelaySessions > 256) throw new Error('Invalid relay session limit');
17
39
  if (publicRelayUrl) relayPublicAddress(publicRelayUrl);
18
40
  // Harmony's native WebSocket supplies the endpoint Origin automatically.
@@ -20,24 +42,112 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
20
42
  const nativeOrigins = publicRelayUrl ? ['http:', 'https:'].map(protocol => `${protocol}//${new URL(publicRelayUrl).host}`) : [];
21
43
  if (appDownloadUrl && !/^https:\/\/[^\s]+$/.test(appDownloadUrl)) throw new Error('Invalid app download URL');
22
44
  const hostHash = digest(hostKey), rooms = new Map();
23
- const store = deviceStore(stateFile);
45
+ const database = mysqlConfigFile ? await openRelayMysql(mysqlConfigFile, maxRelaySessions) : null;
46
+ if (accountConfigFile && !database?.schemaVersions.includes('003_relay_usage')) throw new Error('Relay account and usage schema is required');
47
+ const accounts = accountConfigFile ? openEmailAccounts(database.pool, accountConfigFile) : null;
48
+ const accountSockets = accountConnections();
49
+ const meter = accounts ? usageMeter(database.pool) : null;
50
+ const cancelledAccountIds = new Set();
51
+ const accountCancellationWork = new Map();
52
+ const diagnostics = database?.schemaVersions.includes('006_relay_diagnostics')
53
+ ? relayDiagnostics(database.pool, error => log({ event: 'relay-diagnostic-error', message: error.message })) : null;
54
+ const adminAccessFile = '/etc/codex-relay/admin-access.json';
55
+ const adminAccessKey = fs.existsSync(adminAccessFile) ? JSON.parse(fs.readFileSync(adminAccessFile, 'utf8')).key : '';
56
+ if (adminAccessKey && !validSecret(adminAccessKey)) throw new Error('Invalid admin access key');
57
+ const diagnosticKey = usageDiagnosticKeyFile ? fs.readFileSync(usageDiagnosticKeyFile, 'utf8').trim() : '';
58
+ if (diagnosticKey && !/^[A-Za-z0-9_-]{43}$/.test(diagnosticKey)) throw new Error('Invalid usage diagnostic key');
59
+ const store = database?.devices || deviceStore(stateFile);
24
60
  const debugAllowlist = readDebugAllowlist(debugAllowlistFile);
25
- const adminState = relayAdminState(adminStateFile, maxRelaySessions);
61
+ const adminState = database?.admin || relayAdminState(adminStateFile, maxRelaySessions);
26
62
  let activeRelaySessions = 0;
27
- for (const record of store.entries()) rooms.set(record.id, { ...record, durable: true, connections: new Map(), observers: new Map() });
63
+ for (const record of store.entries()) rooms.set(record.id, { ...record, durable: true, connections: new Map(), observers: new Map(), uploadClients: new Map() });
64
+ let commitTail = Promise.resolve();
65
+ function queueStore(operation) {
66
+ const pending = commitTail.then(operation);
67
+ commitTail = pending.catch(() => {});
68
+ return pending;
69
+ }
28
70
  function commit(room, changes) {
29
- const next = { ...room, ...changes, updatedAt: new Date().toISOString() };
30
- if (room.durable) {
31
- const { id, hostHash, clients, revision, status, createdAt, updatedAt, officialComputer } = next;
32
- store.save({ id, hostHash, clients, revision, status, createdAt, updatedAt, ...(officialComputer ? { officialComputer } : {}) });
33
- }
34
- Object.assign(room, changes, { updatedAt: next.updatedAt });
35
- }
36
- function invitation(room) {
37
- const code = secret(); room.pairingHash = digest(code); room.expires = Date.now() + pairingTtlMs;
38
- return { code, expires: room.expires };
39
- }
71
+ return queueStore(async () => {
72
+ changes = typeof changes === 'function' ? changes(room) : changes;
73
+ const next = { ...room, ...changes, updatedAt: new Date().toISOString() };
74
+ if (room.durable) {
75
+ const { id, hostHash, clients, revision, status, createdAt, updatedAt, officialComputer } = next;
76
+ await store.save({ id, hostHash, clients, revision, status, createdAt, updatedAt, ...(officialComputer ? { officialComputer } : {}) });
77
+ }
78
+ Object.assign(room, changes, { updatedAt: next.updatedAt });
79
+ });
80
+ }
81
+ function registerRoom(id, durable, credential) {
82
+ return queueStore(async () => {
83
+ // Resolve concurrent first connections before writing the device identity.
84
+ if (durable && rooms.has(id)) return rooms.get(id);
85
+ const now = new Date().toISOString();
86
+ const record = { id, revision: 0, status: 'active', clients: [], createdAt: now, updatedAt: now,
87
+ ...(durable ? { hostHash: digest(credential) } : {}) };
88
+ if (durable) await store.save(record);
89
+ const room = { ...record, durable, connections: new Map(), observers: new Map(), uploadClients: new Map() };
90
+ rooms.set(id, room);
91
+ return room;
92
+ });
93
+ }
94
+ function recordConnection(room, clientId, at, info) {
95
+ return queueStore(async () => {
96
+ const client = room.clients.find(item => item.id === clientId);
97
+ if (!client) return false;
98
+ const updated = room.durable ? await store.recordConnection(room.id, clientId, at, info) : connectionInfo(client, at, info);
99
+ if (!updated) return false;
100
+ room.clients = room.clients.map(item => item.id === clientId ? updated : item);
101
+ return true;
102
+ });
103
+ }
104
+ async function manageDevices(room, ws, message, currentClientId) {
105
+ try {
106
+ if (!validId(message.commandId)) throw new Error('Invalid device management request');
107
+ if (message.type === 'devices-list') {
108
+ await commitTail;
109
+ if (currentClientId && !room.clients.some(item => item.id === currentClientId)) throw new Error('配对授权已撤销');
110
+ const clients = room.clients.map(({ id, createdAt, lastConnectedAt, platform, deviceName }) => ({
111
+ id, createdAt, lastConnectedAt: lastConnectedAt || null, platform: platform || null, deviceName: deviceName || null,
112
+ current: id === currentClientId,
113
+ })).sort((a, b) => (b.lastConnectedAt || b.createdAt).localeCompare(a.lastConnectedAt || a.createdAt));
114
+ send(ws, { type: message.type, commandId: message.commandId, clients, workstationOnline: Boolean(room.host) });
115
+ return;
116
+ }
117
+ if (!validId(message.id) || message.confirmed !== true) throw new Error('请确认要撤销的配对授权');
118
+ await commit(room, current => {
119
+ if (currentClientId && !current.clients.some(item => item.id === currentClientId)) throw new Error('配对授权已撤销');
120
+ return { clients: current.clients.filter(item => item.id !== message.id), revision: current.revision + 1 };
121
+ });
122
+ if (room.host) send(room.host, { type: 'authorization-changed', clients: room.clients, revision: room.revision });
123
+ // Acknowledge self-revocation before closing this connection.
124
+ send(ws, { type: message.type, commandId: message.commandId, id: message.id, revoked: true, workstationOnline: Boolean(room.host) });
125
+ const connection = room.connections.get(message.id);
126
+ if (connection) {
127
+ releaseRelay(connection);
128
+ if (room.host) send(room.host, { type: 'detach', session: connection.session });
129
+ room.connections.delete(message.id);
130
+ connection.ws.close(4401, 'Device authorization revoked');
131
+ }
132
+ const observer = room.observers?.get(message.id);
133
+ room.observers?.delete(message.id);
134
+ observer?.ws.close(4401, 'Device authorization revoked');
135
+ for (const [key, upload] of room.uploadClients || []) if (upload.clientId === message.id) {
136
+ room.uploadClients.delete(key);
137
+ if (room.uploadHost) send(room.uploadHost, { type: 'file-close', session: upload.session });
138
+ upload.ws.close(4401, 'Device authorization revoked');
139
+ }
140
+ for (const [socket, id] of room.deviceManagers || []) if (id === message.id) {
141
+ room.deviceManagers.delete(socket); socket.close(4401, 'Device authorization revoked');
142
+ }
143
+ } catch (error) { send(ws, { type: message.type, commandId: message.commandId, error: error.message }); }
144
+ }
145
+ function invitation(room) {
146
+ const code = secret(); room.pairingHash = digest(code); room.expires = Date.now() + pairingTtlMs;
147
+ return { code, expires: room.expires };
148
+ }
40
149
  let listenUrl = '';
150
+ let adminTail = Promise.resolve();
41
151
  function relayUrl(request) {
42
152
  if (publicRelayUrl) return relayPublicAddress(publicRelayUrl);
43
153
  const host = request.headers.host || new URL(listenUrl).host;
@@ -46,12 +156,31 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
46
156
  }
47
157
  function findInvitation(code) {
48
158
  if (!validSecret(code)) return undefined;
49
- return [...rooms.values()].find(room => room.status === 'active' && room.pairingHash && Date.now() < room.expires && matches(code, room.pairingHash));
159
+ const room = [...rooms.values()].find(room => room.status === 'active' && room.pairingHash && Date.now() < room.expires && matches(code, room.pairingHash));
160
+ if (room) return { room, expires: room.expires };
161
+ const invite = longInvitation(code), target = invite && rooms.get(invite.room);
162
+ return target?.status === 'active' ? { room: target, expires: invite.expires } : undefined;
50
163
  }
51
164
  const server = http.createServer((request, response) => {
52
165
  const target = new URL(request.url || '/', `http://${request.headers.host || 'localhost'}`);
166
+ if (target.pathname.startsWith('/code-relax/android/')) {
167
+ void androidReleases(request, response, target); return;
168
+ }
53
169
  if (target.pathname === '/admin' || target.pathname.startsWith('/admin/')) {
54
- void handleAdmin(request, response, target); return;
170
+ adminTail = adminTail.then(() => handleAdmin(request, response, target)).catch(() => {
171
+ if (!response.headersSent) adminReply(response, 500, { error: 'Admin operation failed' });
172
+ });
173
+ return;
174
+ }
175
+ if (target.pathname === '/account/privacy' && request.method === 'GET') {
176
+ response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache',
177
+ 'Content-Security-Policy': "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
178
+ 'X-Content-Type-Options': 'nosniff' });
179
+ response.end(privacyPage); return;
180
+ }
181
+ if (accounts && target.pathname.startsWith('/account/')) {
182
+ void handleAccount(request, response, target).catch(() => accountReply(response, 500, { error: 'Account operation failed' }));
183
+ return;
55
184
  }
56
185
  if (request.method === 'GET' && target.pathname === '/announcement') {
57
186
  const body = JSON.stringify({ announcement: adminState.read().announcement });
@@ -61,19 +190,19 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
61
190
  response.writeHead(404, { 'Cache-Control': 'no-store' }); response.end(); return;
62
191
  }
63
192
  const code = target.searchParams.get('invite') || '';
64
- const room = findInvitation(code);
65
- if (!room) {
193
+ const invitation = findInvitation(code);
194
+ if (!invitation) {
66
195
  const json = target.pathname === '/pair/resolve' || /application\/json/i.test(request.headers.accept || '');
67
196
  const body = json ? JSON.stringify({ error: '配对邀请已过期或已使用' }) : pairingPage({ expired: true });
68
197
  response.writeHead(410, pairingHeaders(json ? 'application/json; charset=utf-8' : 'text/html; charset=utf-8', body)); response.end(body); return;
69
198
  }
70
199
  if (target.pathname === '/pair/resolve' || /application\/json/i.test(request.headers.accept || '')) {
71
- const body = JSON.stringify({ version: 6, relayUrl: relayUrl(request), room: room.id, expires: room.expires });
200
+ const body = JSON.stringify({ version: 6, relayUrl: relayUrl(request), room: invitation.room.id, expires: invitation.expires });
72
201
  response.writeHead(200, pairingHeaders('application/json; charset=utf-8', body)); response.end(body); return;
73
202
  }
74
203
  const secure = request.socket.encrypted || request.headers['x-forwarded-proto'] === 'https';
75
204
  const current = new URL(target.pathname + target.search, `${secure ? 'https' : 'http'}://${request.headers.host || new URL(listenUrl).host}`);
76
- const body = pairingPage({ deepLink: `codex-remote://pair?url=${encodeURIComponent(current.href)}`, expires: room.expires, appDownloadUrl });
205
+ const body = pairingPage({ deepLink: `codex-remote://pair?url=${encodeURIComponent(current.href)}`, expires: invitation.expires, appDownloadUrl });
77
206
  response.writeHead(200, pairingHeaders('text/html; charset=utf-8', body)); response.end(body);
78
207
  });
79
208
  function adminReply(response, status, body, type = 'application/json; charset=utf-8') {
@@ -82,30 +211,156 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
82
211
  'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "default-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'" });
83
212
  response.end(value);
84
213
  }
85
- async function handleAdmin(request, response, target) {
86
- if (request.method === 'GET' && target.pathname === '/admin') {
87
- adminReply(response, 200, adminPage(), 'text/html; charset=utf-8'); return;
214
+ function accountReply(response, status, body) {
215
+ if (response.headersSent || response.destroyed) return;
216
+ const value = JSON.stringify(body);
217
+ response.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': Buffer.byteLength(value),
218
+ 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });
219
+ response.end(value);
220
+ }
221
+ function cancelAccountConnections(userId, token) {
222
+ const pending = accountCancellationWork.get(userId);
223
+ if (pending) return pending;
224
+ if (cancelledAccountIds.has(userId)) return Promise.resolve();
225
+ cancelledAccountIds.add(userId);
226
+ const work = (async () => {
227
+ try { await accounts.cancelAccount(token); }
228
+ catch (failure) { cancelledAccountIds.delete(userId); throw failure; }
229
+ for (const room of rooms.values()) {
230
+ for (const [key, arm] of room.usageArms || []) if (arm.userId === userId) room.usageArms.delete(key);
231
+ for (const [key, task] of room.usageTasks || []) if (task.userId === userId) room.usageTasks.delete(key);
232
+ for (const [clientId, connection] of room.connections) {
233
+ if (connection.userId !== userId) continue;
234
+ releaseRelay(connection); room.connections.delete(clientId);
235
+ if (room.host) send(room.host, { type: 'detach', session: connection.session });
236
+ connection.ws.close(4431, 'Account cancelled');
237
+ }
238
+ for (const [clientId, observer] of room.observers) {
239
+ if (observer.userId !== userId) continue;
240
+ room.observers.delete(clientId); observer.ws.close(4431, 'Account cancelled');
241
+ }
242
+ for (const [clientId, upload] of room.uploadClients) {
243
+ if (upload.userId !== userId) continue;
244
+ room.uploadClients.delete(clientId);
245
+ if (room.uploadHost) send(room.uploadHost, { type: 'file-close', session: upload.session });
246
+ upload.ws.close(4431, 'Account cancelled');
247
+ }
248
+ }
249
+ })().finally(() => accountCancellationWork.delete(userId));
250
+ accountCancellationWork.set(userId, work);
251
+ return work;
252
+ }
253
+ async function handleAccount(request, response, target) {
254
+ try {
255
+ if (target.pathname === '/account/config' && request.method === 'GET') {
256
+ accountReply(response, 200, { captchaAppId: accounts.captchaAppId }); return;
257
+ }
258
+ const bearer = /^Bearer ([A-Za-z0-9_-]{43})$/.exec(request.headers.authorization || '')?.[1];
259
+ if (target.pathname === '/account/me' && request.method === 'GET') {
260
+ const user = await accounts.current(bearer);
261
+ accountReply(response, 200, { user, usage: await accounts.usage(user.id) }); return;
262
+ }
263
+ if (target.pathname === '/account/logout' && request.method === 'POST') {
264
+ const result = await accounts.logout(bearer);
265
+ accountSockets.revoke(bearer);
266
+ accountReply(response, 200, result); return;
267
+ }
268
+ if (target.pathname === '/account/me' && request.method === 'DELETE') {
269
+ const user = await accounts.current(bearer);
270
+ await cancelAccountConnections(user.id, bearer);
271
+ accountReply(response, 200, { ok: true }); return;
272
+ }
273
+ if (request.method !== 'POST' || request.headers['content-type'] !== 'application/json') {
274
+ accountReply(response, 404, { error: 'Account route not found' }); return;
275
+ }
276
+ const body = JSON.parse(await readBody(request, 4096));
277
+ if (!body || typeof body !== 'object' || Array.isArray(body)) { accountReply(response, 400, { error: 'Invalid request body' }); return; }
278
+ if (target.pathname === '/account/email/request') {
279
+ const ip = request.headers['x-real-ip'] || request.socket.remoteAddress || '';
280
+ if (typeof ip !== 'string' || !isIP(ip)) { accountReply(response, 400, { error: 'Invalid client address' }); return; }
281
+ accountReply(response, 202, await accounts.requestCode(body, ip));
282
+ } else if (target.pathname === '/account/email/verify') {
283
+ accountReply(response, 200, await accounts.verifyCode(body));
284
+ } else accountReply(response, 404, { error: 'Account route not found' });
285
+ } catch (failure) {
286
+ accountReply(response, failure.status || 503, { error: failure.status ? failure.message : 'Account service unavailable' });
88
287
  }
288
+ }
289
+ async function handleAdmin(request, response, target) {
290
+ const accessKey = request.headers['x-code-relax-admin-key'];
291
+ const internalAccess = adminAccessKey && validSecret(accessKey) &&
292
+ timingSafeEqual(Buffer.from(accessKey), Buffer.from(adminAccessKey));
293
+ const token = /^Bearer ([A-Za-z0-9_-]{43})$/.exec(request.headers.authorization || '')?.[1];
294
+ const diagnosticRoute = ['/admin/api/usage-diagnostics', '/admin/api/usage-diagnostics/drain'].includes(target.pathname);
295
+ const diagnosticAccess = diagnosticRoute && diagnosticKey && token &&
296
+ timingSafeEqual(Buffer.from(token), Buffer.from(diagnosticKey));
297
+ if (!internalAccess && !diagnosticAccess) { adminReply(response, 404, { error: 'Not found' }); return; }
89
298
  if (request.method === 'GET' && target.pathname === '/admin/api/status') {
90
299
  const state = adminState.read();
91
300
  adminReply(response, 200, { maxRelaySessions: state.maxRelaySessions, activeRelaySessions,
92
301
  activeExemptRelaySessions: [...rooms.values()].reduce((sum, room) => sum + [...room.connections.values()].filter(connection => connection.relayExempt).length, 0),
93
302
  websocketConnections: wss.clients.size, onlineComputers: [...rooms.values()].filter(room => room.host).length,
94
- activePhones: [...rooms.values()].reduce((sum, room) => sum + room.connections.size, 0), announcement: state.announcement }); return;
303
+ activePhones: [...rooms.values()].reduce((sum, room) => sum + room.connections.size, 0),
304
+ processRssBytes: process.memoryUsage().rss, processUptimeSeconds: process.uptime(),
305
+ announcement: state.announcement, announcements: state.announcements }); return;
306
+ }
307
+ if (request.method === 'GET' && target.pathname === '/admin/api/usage' && meter) {
308
+ const page = Number(target.searchParams.get('page') || '1');
309
+ const view = target.searchParams.get('view') || 'user';
310
+ if (!Number.isSafeInteger(page) || page < 1 || page > 100000 ||
311
+ !['user', 'computer'].includes(view) || target.searchParams.has('user') ||
312
+ target.searchParams.has('computer')) {
313
+ adminReply(response, 400, { error: 'Invalid usage query' }); return;
314
+ }
315
+ adminReply(response, 200, await usageReport(database.pool, page, view)); return;
316
+ }
317
+ const diagnosticSource = target.searchParams.get('source') || 'usage';
318
+ if (target.pathname.startsWith('/admin/api/usage-diagnostics') && !['usage', 'account-cleanup'].includes(diagnosticSource)) {
319
+ adminReply(response, 400, { error: 'Invalid diagnostic source' }); return;
320
+ }
321
+ if (diagnostics && target.pathname === '/admin/api/usage-diagnostics') {
322
+ if (request.method === 'GET') adminReply(response, 200, await diagnostics.read(diagnosticSource));
323
+ else if (request.method === 'DELETE') adminReply(response, 200, await diagnostics.clear(diagnosticSource));
324
+ else adminReply(response, 405, { error: 'Unsupported diagnostic request' });
325
+ return;
326
+ }
327
+ if (diagnostics && target.pathname === '/admin/api/usage-diagnostics/drain') {
328
+ if (request.method === 'POST') adminReply(response, 200, await diagnostics.drain(diagnosticSource));
329
+ else adminReply(response, 405, { error: 'Unsupported diagnostic request' });
330
+ return;
95
331
  }
96
332
  if (!['PUT', 'DELETE'].includes(request.method || '') || request.headers['content-type'] !== 'application/json' ||
97
333
  request.headers['x-code-relax-admin'] !== '1') { adminReply(response, 405, { error: 'Unsupported admin request' }); return; }
98
334
  try {
99
- const body = request.method === 'DELETE' ? {} : JSON.parse(await readBody(request, 4096));
335
+ const body = request.method === 'DELETE' ? {} : JSON.parse(await readBody(request, MAX_ADMIN_REQUEST_BYTES));
100
336
  if (target.pathname === '/admin/api/settings' && request.method === 'PUT') {
101
- const state = adminState.setMaxRelaySessions(body.maxRelaySessions);
337
+ const state = await adminState.setMaxRelaySessions(body.maxRelaySessions);
102
338
  log({ event: 'admin-relay-limit-updated', maxRelaySessions: state.maxRelaySessions });
103
- } else if (target.pathname === '/admin/api/announcement' && request.method === 'PUT') {
104
- const state = adminState.publish(body.title, body.message); broadcastAnnouncement(state.announcement);
105
- log({ event: 'admin-announcement-published', announcementId: state.announcement.id });
106
- } else if (target.pathname === '/admin/api/announcement' && request.method === 'DELETE') {
107
- const state = adminState.withdraw(); broadcastAnnouncement(state.announcement);
108
- log({ event: 'admin-announcement-withdrawn' });
339
+ } else if (target.pathname === '/admin/api/announcements' && request.method === 'PUT') {
340
+ const state = await adminState.createAnnouncement(body.title, body.message, body.publish === true);
341
+ if (body.publish === true) {
342
+ broadcastAnnouncement(state.announcement);
343
+ log({ event: 'admin-announcement-published', announcementId: state.announcement.id });
344
+ } else log({ event: 'admin-announcement-draft-created', announcementId: state.announcements[0].id });
345
+ } else if (target.pathname.startsWith('/admin/api/announcements/')) {
346
+ const parts = target.pathname.slice('/admin/api/announcements/'.length).split('/');
347
+ const id = parts[0];
348
+ if (!/^[\w-]{16,128}$/.test(id)) { adminReply(response, 404, { error: 'Admin route not found' }); return; }
349
+ if (parts.length === 1 && request.method === 'PUT') {
350
+ await adminState.updateDraft(id, body.title, body.message);
351
+ log({ event: 'admin-announcement-draft-updated', announcementId: id });
352
+ } else if (parts.length === 1 && request.method === 'DELETE') {
353
+ await adminState.deleteAnnouncement(id);
354
+ log({ event: 'admin-announcement-deleted', announcementId: id });
355
+ } else if (parts.length === 2 && parts[1] === 'publish' && request.method === 'PUT') {
356
+ const state = await adminState.publishDraft(id, body.title, body.message);
357
+ broadcastAnnouncement(state.announcement);
358
+ log({ event: 'admin-announcement-published', announcementId: id });
359
+ } else if (parts.length === 2 && parts[1] === 'withdraw' && request.method === 'PUT') {
360
+ const state = await adminState.withdraw(id);
361
+ broadcastAnnouncement(state.announcement);
362
+ log({ event: 'admin-announcement-withdrawn', announcementId: id });
363
+ } else { adminReply(response, 404, { error: 'Admin route not found' }); return; }
109
364
  } else { adminReply(response, 404, { error: 'Admin route not found' }); return; }
110
365
  adminReply(response, 200, { ok: true });
111
366
  } catch (error) { adminReply(response, 400, { error: error.message }); }
@@ -137,23 +392,40 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
137
392
  serverNoContextTakeover: true, clientNoContextTakeover: true,
138
393
  threshold: 1024, concurrencyLimit: 4, zlibDeflateOptions: { level: 3 },
139
394
  } });
140
- let admissionStart = Date.now(), admissions = 0;
141
- server.on('upgrade', (request, socket, head) => {
395
+ let admissionStart = Date.now(), admissions = 0;
396
+ server.on('upgrade', (request, socket, head) => {
142
397
  if (request.url !== '/relay' || (request.headers.origin && request.headers.origin !== 'null' && !nativeOrigins.includes(request.headers.origin))) {
143
398
  log({ event: 'upgrade-rejected', reason: request.url !== '/relay' ? 'path' : 'origin' });
144
- socket.end('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n'); return;
145
- }
146
- if (Date.now() - admissionStart >= 60000) { admissionStart = Date.now(); admissions = 0; }
147
- if (wss.clients.size >= maxConnections || ++admissions > 60) {
148
- socket.end('HTTP/1.1 429 Too Many Requests\r\nConnection: close\r\n\r\n'); return;
149
- }
150
- wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws));
151
- });
399
+ socket.end('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n'); return;
400
+ }
401
+ if (Date.now() - admissionStart >= 60000) { admissionStart = Date.now(); admissions = 0; }
402
+ if (wss.clients.size >= maxConnections || ++admissions > 240) {
403
+ socket.end('HTTP/1.1 429 Too Many Requests\r\nConnection: close\r\n\r\n'); return;
404
+ }
405
+ wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws));
406
+ });
152
407
  wss.on('connection', ws => {
153
408
  let role, room, session, clientId, alive = true;
154
- const authTimer = setTimeout(() => ws.close(1008, 'Authentication timeout'), 5000);
155
- ws.on('pong', () => { alive = true; });
156
- const heartbeat = setInterval(() => { if (!alive) ws.terminate(); else { alive = false; ws.ping(); } }, 30000);
409
+ let accountConnection;
410
+ function invalidateAccountConnection() {
411
+ if (role === 'client' && room?.connections.get(clientId)?.ws === ws) {
412
+ releaseRelay(room.connections.get(clientId));
413
+ room.connections.delete(clientId);
414
+ // Detach also closes the workstation RTC peer; closing only the
415
+ // phone's signaling socket would leave the direct data path alive.
416
+ if (room.host) send(room.host, { type: 'detach', session });
417
+ } else if (role === 'file-client' && room?.uploadClients.get(session)?.ws === ws) {
418
+ room.uploadClients.delete(session);
419
+ if (room.uploadHost) send(room.uploadHost, { type: 'file-close', session });
420
+ }
421
+ ws.close(4431, 'Account session invalid');
422
+ }
423
+ const authTimer = setTimeout(() => ws.close(1008, 'Authentication timeout'), 5000);
424
+ ws.on('pong', () => { alive = true; });
425
+ const heartbeat = setInterval(() => {
426
+ sampleInboundQueue();
427
+ if (!alive) ws.terminate(); else { alive = false; ws.ping(); }
428
+ }, 30000);
157
429
  const fail = (code = 4401, reason = 'Device authorization rejected') => ws.close(code, reason);
158
430
  const queueClientMessage = (connection, message, pause = 0) => {
159
431
  const bytes = Buffer.byteLength(JSON.stringify(message));
@@ -167,27 +439,132 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
167
439
  send(connection.ws, message);
168
440
  }).catch(() => connection.ws.close(1011, 'Relay delivery failed'));
169
441
  };
170
- ws.on('error', () => {}); // close owns routing cleanup.
171
- ws.on('close', (code, reason) => {
172
- log({ event: 'closed', role: role || 'unauthenticated', device: room?.id, code, reason: reason.toString() });
173
- clearTimeout(authTimer); clearInterval(heartbeat);
442
+ ws.on('error', () => {}); // close owns routing cleanup.
443
+ ws.on('close', (code, reason) => {
444
+ log({ event: 'closed', role: role || 'unauthenticated', device: room?.id, code, reason: reason.toString() });
445
+ clearTimeout(authTimer); clearInterval(heartbeat);
446
+ accountConnection?.dispose();
174
447
  if (role === 'host' && room?.host === ws) {
448
+ for (const task of room.usageTasks?.values() || []) {
449
+ void meter?.lost(task).catch(error => log({ event: 'usage-error', message: error.message }));
450
+ }
451
+ room.usageTasks?.clear(); room.usageArms?.clear();
175
452
  room.host = undefined; room.hostName = undefined; room.lanCandidates = [];
453
+ room.uploadHost?.close(4410, 'Computer offline');
454
+ for (const client of room.uploadClients?.values() || []) client.ws.close(code === 4432 ? 4432 : 4410, code === 4432 ? 'Computer inbound queue limit exceeded' : 'Computer offline');
176
455
  for (const observer of room.observers.values()) send(observer.ws, { type: 'observing', online: false });
177
456
  if (!room.durable) rooms.delete(room.id);
178
- for (const connection of room.connections.values()) connection.ws.close(4410, 'Computer offline; authorization retained');
457
+ for (const connection of room.connections.values()) connection.ws.close(
458
+ code === 4432 ? 4432 : 4410,
459
+ code === 4432 ? 'Computer inbound queue limit exceeded' : 'Computer offline; authorization retained');
179
460
  } else if (role === 'client' && room?.connections.get(clientId)?.ws === ws) {
180
- releaseRelay(room.connections.get(clientId));
461
+ const connection = room.connections.get(clientId);
462
+ releaseRelay(connection);
181
463
  room.connections.delete(clientId);
182
464
  if (room.host) send(room.host, { type: 'detach', session });
183
465
  } else if (role === 'observer' && room?.observers.get(clientId)?.ws === ws) {
184
466
  room.observers.delete(clientId);
185
- }
186
- });
187
- ws.on('message', (bytes, binary) => {
188
- try {
189
- const message = decode(bytes, binary);
190
- if (!role) {
467
+ } else if (role === 'file-host' && room?.uploadHost === ws) {
468
+ room.uploadHost = undefined;
469
+ for (const client of room.uploadClients?.values() || []) client.ws.close(code === 4432 ? 4432 : 4410, code === 4432 ? 'File receiver inbound queue limit exceeded' : 'File receiver offline');
470
+ } else if (role === 'file-client' && room?.uploadClients?.get(session)?.ws === ws) {
471
+ room.uploadClients.delete(session);
472
+ if (room.uploadHost) send(room.uploadHost, { type: 'file-close', session });
473
+ }
474
+ });
475
+ let messageTail = Promise.resolve();
476
+ // Include the in-flight message: async database work must not permit an
477
+ // unbounded chain of closures retaining incoming payloads.
478
+ let pendingMessages = 0, pendingBytes = 0;
479
+ const maxPendingMessages = 128, maxPendingBytes = 16 * 1024 * 1024;
480
+ let sampleStarted = Date.now(), peakMessages = 0, peakBytes = 0, maxWaitMs = 0, receivedMessages = 0;
481
+ function sampleInboundQueue() {
482
+ const now = Date.now();
483
+ if (now - sampleStarted < 60000) return;
484
+ // Summarize only intervals with an actual backlog; never log payloads or credentials.
485
+ if (peakMessages > 1) log({ event: 'inbound-queue-sample', role: role || 'unauthenticated',
486
+ windowMs: now - sampleStarted, receivedMessages, peakMessages, peakBytes, maxWaitMs,
487
+ pendingMessages, pendingBytes });
488
+ sampleStarted = now; peakMessages = pendingMessages; peakBytes = pendingBytes;
489
+ maxWaitMs = 0; receivedMessages = 0;
490
+ }
491
+ ws.on('message', (bytes, binary) => {
492
+ if (ws.readyState !== ws.OPEN) return;
493
+ const size = bytes.length;
494
+ if (pendingMessages >= maxPendingMessages || pendingBytes + size > maxPendingBytes) {
495
+ log({ event: 'inbound-queue-limit', role: role || 'unauthenticated',
496
+ pendingMessages, pendingBytes, incomingBytes: size, maxWaitMs });
497
+ ws.close(4432, 'Inbound queue limit exceeded');
498
+ return;
499
+ }
500
+ pendingMessages++; pendingBytes += size;
501
+ receivedMessages++;
502
+ peakMessages = Math.max(peakMessages, pendingMessages); peakBytes = Math.max(peakBytes, pendingBytes);
503
+ const enqueuedAt = Date.now();
504
+ messageTail = messageTail.then(async () => {
505
+ try {
506
+ maxWaitMs = Math.max(maxWaitMs, Date.now() - enqueuedAt);
507
+ if (ws.readyState !== ws.OPEN) return;
508
+ if (accountConnection && !accountConnection.valid()) return;
509
+ if (binary) {
510
+ if (role === 'file-host' && room.uploadHost === ws) {
511
+ const frame = parseFileBytes(bytes);
512
+ const client = room.uploadClients.get(frame.session);
513
+ if (!client || client.ws.readyState !== ws.OPEN) return;
514
+ if (client.ws.bufferedAmount > 1024 * 1024) return client.ws.close(1013, 'File receiver too slow');
515
+ client.ws.send(frame.data); return;
516
+ }
517
+ if (role === 'file-client' && room.uploadClients?.get(session)?.ws === ws) {
518
+ if (room.uploadHost?.readyState !== ws.OPEN) return fail(4410, 'File receiver offline');
519
+ if (room.uploadHost.bufferedAmount > 1024 * 1024) return fail(1013, 'File receiver too slow');
520
+ room.uploadHost.send(routedFileBytes(session, bytes)); return;
521
+ }
522
+ return fail(1008, 'Invalid binary frame');
523
+ }
524
+ const message = decode(bytes, binary);
525
+ if (!role) {
526
+ let userId;
527
+ if (!['host', 'file-host', 'observe', 'device-management'].includes(message.type) &&
528
+ !(['pair', 'resume'].includes(message.type) && message.management === true)) {
529
+ if (!['pair', 'resume', 'observe', 'file-client'].includes(message.type)) return fail();
530
+ if (!accounts || !validSecret(message.accountToken)) return fail(4431, 'Account login required');
531
+ accountConnection = accountSockets.track(message.accountToken, invalidateAccountConnection);
532
+ try {
533
+ const user = await accounts.current(message.accountToken);
534
+ if (!accountConnection.activate(user.expiresAt)) return;
535
+ userId = user.id;
536
+ }
537
+ catch { return fail(4431, 'Account session invalid'); }
538
+ if (ws.readyState !== ws.OPEN) return;
539
+ if (cancelledAccountIds.has(userId)) return fail(4431, 'Account cancelled');
540
+ }
541
+ if (message.type === 'file-host') {
542
+ room = rooms.get(message.deviceId);
543
+ if (!room?.host || room.status !== 'active' || !matches(message.deviceSecret, room.hostHash)) return fail();
544
+ role = 'file-host'; clearTimeout(authTimer);
545
+ room.uploadClients ||= new Map();
546
+ room.uploadHost?.close(4409, 'File receiver replaced');
547
+ room.uploadHost = ws;
548
+ send(ws, { type: 'file-host-ready' }); return;
549
+ }
550
+ if (message.type === 'file-client') {
551
+ room = rooms.get(message.room);
552
+ const client = room?.clients.find(item => item.id === message.clientId);
553
+ if (!room?.host || !room.uploadHost || room.status !== 'active' || !client ||
554
+ !matches(message.credential, client.hash)) return fail();
555
+ room.uploadClients ||= new Map();
556
+ if (!['upload', 'download'].includes(message.purpose)) return fail(1008, 'Invalid file purpose');
557
+ const same = [...room.uploadClients.values()].filter(item => item.clientId === message.clientId && item.purpose === message.purpose);
558
+ const all = [...rooms.values()].flatMap(item => [...(item.uploadClients?.values() || [])]);
559
+ if (same.length >= (message.purpose === 'upload' ? 1 : 3) ||
560
+ all.length >= 8 || (message.purpose === 'upload' && all.filter(item => item.purpose === 'upload').length >= 2)) {
561
+ return fail(4429, 'Too many file transfers');
562
+ }
563
+ role = 'file-client'; clientId = message.clientId; session = randomUUID(); clearTimeout(authTimer);
564
+ if (!await recordConnection(room, clientId, new Date().toISOString(), deviceInfo(message))) return fail(4401, 'Device authorization revoked');
565
+ room.uploadClients.set(session, { ws, session, clientId, userId, purpose: message.purpose });
566
+ send(room.uploadHost, { type: 'file-open', session, purpose: message.purpose }); return;
567
+ }
191
568
  if (message.type === 'host') {
192
569
  const durable = message.deviceId !== undefined;
193
570
  if (durable && (!validId(message.deviceId) || !validSecret(message.deviceSecret))) return fail();
@@ -198,20 +575,24 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
198
575
  hostName = normalizeHostName(message.hostName);
199
576
  }
200
577
  catch { return fail(1008, 'Invalid LAN candidates'); }
201
- room = durable ? rooms.get(message.deviceId) : undefined;
202
- if (room && (room.status !== 'active' || !matches(message.deviceSecret, room.hostHash))) return fail();
203
- if (!room) {
204
- if (!matches(message.key, hostHash)) return fail();
205
- if (rooms.size >= 256) return fail(4429, 'Device limit reached');
206
- const now = new Date().toISOString();
207
- room = { id: durable ? message.deviceId : randomUUID(), durable,
208
- revision: 0, status: 'active', clients: [], connections: new Map(), observers: new Map(), createdAt: now, updatedAt: now,
209
- ...(durable ? { hostHash: digest(message.deviceSecret) } : {}) };
210
- commit(room, {}); rooms.set(room.id, room);
211
- }
578
+ room = durable ? rooms.get(message.deviceId) : undefined;
579
+ if (!room) {
580
+ if (!durable && !matches(message.key, hostHash)) return fail();
581
+ room = await registerRoom(durable ? message.deviceId : randomUUID(), durable, message.deviceSecret);
582
+ if (ws.readyState !== ws.OPEN) return;
583
+ }
584
+ if (durable && (room.status !== 'active' || !matches(message.deviceSecret, room.hostHash))) return fail();
212
585
  const oldHost = room.host;
213
586
  room.observers ||= new Map();
587
+ if (oldHost && oldHost !== ws) {
588
+ for (const task of room.usageTasks?.values() || []) {
589
+ try { await meter?.lost(task); } catch (error) { log({ event: 'usage-error', message: error.message }); }
590
+ }
591
+ room.usageTasks?.clear(); room.usageArms?.clear();
592
+ }
214
593
  room.host = ws; room.hostName = hostName; room.lanCandidates = lanCandidates;
594
+ room.usageTasks ||= new Map(); room.usageArms ||= new Map();
595
+ if (oldHost && oldHost !== ws) room.uploadHost?.close(4409, 'Computer connection replaced');
215
596
  room.p2pProbe = message.p2pProbe === 1; room.p2pProbeRepeat = message.p2pProbeRepeat === true;
216
597
  role = 'host'; clearTimeout(authTimer);
217
598
  if (oldHost && oldHost !== ws) {
@@ -230,32 +611,50 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
230
611
  room = rooms.get(message.room);
231
612
  if (!room || room.status !== 'active') return fail();
232
613
  let credential;
233
- if (message.type === 'pair' && room.pairingHash && Date.now() < room.expires && matches(message.code, room.pairingHash)) {
614
+ const longInvite = message.type === 'pair' ? longInvitation(message.code) : undefined;
615
+ const reusableInvite = longInvite?.room === room.id;
616
+ if (message.type === 'pair' && !room.pairingInProgress && (reusableInvite ||
617
+ (room.pairingHash && Date.now() < room.expires && matches(message.code, room.pairingHash)))) {
234
618
  if (!room.host) return fail(4410, 'Computer offline; authorization retained');
235
619
  credential = message.credential || secret();
236
620
  clientId = message.clientId || (!room.durable ? randomUUID() : undefined);
237
621
  if (!validSecret(credential) || !validId(clientId)) return fail();
238
622
  const existing = room.clients.find(client => client.id === clientId);
239
- if (!existing && room.clients.length >= maxClientsPerHost) return fail(4429, 'Mobile device limit reached');
240
623
  const now = new Date().toISOString();
241
- const client = { id: clientId, hash: digest(credential), createdAt: existing?.createdAt || now, updatedAt: now };
242
- commit(room, { clients: [...room.clients.filter(item => item.id !== clientId), client], revision: room.revision + 1 });
243
- room.pairingHash = undefined;
624
+ const client = { ...existing, id: clientId, hash: digest(credential), createdAt: existing?.createdAt || now, updatedAt: now,
625
+ ...deviceInfo(message), lastConnectedAt: now };
626
+ room.pairingInProgress = true;
627
+ try {
628
+ await commit(room, current => ({ clients: [...current.clients.filter(item => item.id !== clientId), client], revision: current.revision + 1 }));
629
+ if (!reusableInvite) room.pairingHash = undefined;
630
+ } finally { room.pairingInProgress = false; }
631
+ if (!room.host) return fail(4410, 'Computer offline; authorization retained');
244
632
  send(room.host, { type: 'authorization-changed', clients: room.clients, revision: room.revision });
245
633
  } else {
246
634
  clientId = message.clientId;
247
635
  const client = room.clients.find(item => item.id === clientId) ||
248
636
  (!room.durable && !clientId ? room.clients.find(item => matches(message.credential, item.hash)) : undefined);
249
637
  if (client) clientId = client.id;
250
- if (!['resume', 'pair', 'observe'].includes(message.type) || !client || !matches(message.credential, client.hash)) {
638
+ if (!['resume', 'pair', 'observe', 'device-management'].includes(message.type) || !client || !matches(message.credential, client.hash)) {
251
639
  return fail(message.type === 'pair' ? 4408 : 4401, message.type === 'pair' ? 'Pairing code expired or used' : 'Device authorization rejected');
252
640
  }
253
641
  }
642
+ if (ws.readyState !== ws.OPEN) return;
643
+ if (accountConnection && !accountConnection.valid()) return;
644
+ if (cancelledAccountIds.has(userId)) return fail(4431, 'Account cancelled');
645
+ if (!await recordConnection(room, clientId, new Date().toISOString(), deviceInfo(message))) return fail(4401, 'Device authorization revoked');
646
+ if (ws.readyState !== ws.OPEN) return;
647
+ if (message.type === 'device-management') {
648
+ role = 'device-manager'; clearTimeout(authTimer); room.deviceManagers ||= new Map();
649
+ room.deviceManagers.set(ws, clientId);
650
+ ws.once('close', () => room.deviceManagers.delete(ws));
651
+ send(ws, { type: 'device-management-ready' }); return;
652
+ }
254
653
  if (message.type === 'observe') {
255
654
  role = 'observer'; clearTimeout(authTimer); room.observers ||= new Map();
256
655
  const oldObserver = room.observers.get(clientId);
257
656
  if (oldObserver?.ws && oldObserver.ws !== ws) oldObserver.ws.close(4409, 'Metadata connection replaced by same identity');
258
- room.observers.set(clientId, { ws });
657
+ room.observers.set(clientId, { ws, userId });
259
658
  send(ws, { type: 'observing', online: Boolean(room.host) });
260
659
  send(ws, { type: 'debug-permissions', enabled: debugAllowed(debugAllowlist, room.id, room.clients.find(client => client.id === clientId)) });
261
660
  const announcement = adminState.read().announcement;
@@ -273,7 +672,8 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
273
672
  send(room.host, { type: 'detach', session: oldConnection.session });
274
673
  oldConnection.ws.close(4409, 'Phone connection replaced by same identity');
275
674
  }
276
- room.connections.set(clientId, { ws, session, p2pProbe: room.p2pProbe && message.p2pProbe === 1,
675
+ room.connections.set(clientId, { ws, session, p2pProbe: Boolean(userId) && room.p2pProbe && message.p2pProbe === 1,
676
+ userId,
277
677
  repeat: room.p2pProbeRepeat, signals: 0, probeStarted: 0, outbound: Promise.resolve(), queuedBytes: 0,
278
678
  relayAdmitted: false, relayExempt: false,
279
679
  capacityExempt: debugAllowed(debugAllowlist, room.id, room.clients.find(client => client.id === clientId)) });
@@ -283,9 +683,99 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
283
683
  const announcement = adminState.read().announcement;
284
684
  if (announcement) send(ws, { type: 'announcement', announcement });
285
685
  send(room.host, { type: 'attach', session, clientId, p2pProbe: room.connections.get(clientId).p2pProbe ? 1 : 0 }); return;
286
- }
686
+ }
687
+ if (role === 'device-manager') {
688
+ if (!room.clients.some(item => item.id === clientId)) return fail(4401, 'Device authorization revoked');
689
+ if (!['devices-list', 'device-revoke'].includes(message.type)) return fail(1008, 'Invalid device management command');
690
+ await manageDevices(room, ws, message, clientId); return;
691
+ }
287
692
  if (role === 'host') {
288
693
  if (room.host !== ws) return;
694
+ if (['devices-list', 'device-revoke'].includes(message.type)) { await manageDevices(room, ws, message); return; }
695
+ if (message.type === 'device-connected') {
696
+ const at = new Date(Math.min(Date.parse(message.at), Date.now())).toISOString();
697
+ if (!validId(message.clientId)) return fail(1008, 'Invalid mobile identity');
698
+ await recordConnection(room, message.clientId, at, deviceInfo(message));
699
+ return;
700
+ }
701
+ if (meter && ['usage-arm', 'usage-message', 'usage-cancel', 'usage-turn', 'usage-observer-lost'].includes(message.type)) {
702
+ if (message.type === 'usage-observer-lost') {
703
+ for (const task of room.usageTasks.values()) {
704
+ try { await meter.lost(task); } catch (error) { log({ event: 'usage-error', message: error.message }); }
705
+ }
706
+ room.usageTasks.clear();
707
+ return;
708
+ }
709
+ const armKey = `${message.session}:${message.requestId}`;
710
+ const connection = [...room.connections.values()].find(item => item.session === message.session);
711
+ if (message.type === 'usage-arm') {
712
+ if (!connection?.userId || !Number.isSafeInteger(message.requestId) || message.requestId < 1 ||
713
+ (message.threadId !== null && (!plainText(message.threadId, 128, false)))) {
714
+ diagnostics?.record('usage', 'arm-rejected', !connection?.userId ? 'account-missing' : 'invalid-request');
715
+ return;
716
+ }
717
+ for (const [key, arm] of room.usageArms) if (Date.now() - arm.at > 120000) room.usageArms.delete(key);
718
+ if (room.usageArms.size >= 256) return;
719
+ room.usageArms.set(armKey, { userId: connection.userId,
720
+ clientId: [...room.connections.entries()].find(([, item]) => item === connection)?.[0],
721
+ session: message.session, at: Date.now() });
722
+ return;
723
+ }
724
+ if (message.type === 'usage-cancel') {
725
+ room.usageArms.delete(armKey); return;
726
+ }
727
+ if (message.type === 'usage-message') {
728
+ const arm = room.usageArms.get(armKey);
729
+ room.usageArms.delete(armKey);
730
+ if (!arm || Date.now() - arm.at > 120000 || !plainText(message.messageKey, 128, false)) {
731
+ diagnostics?.record('usage', 'message-rejected', !arm ? 'arm-missing' : 'expired-or-invalid');
732
+ return;
733
+ }
734
+ try {
735
+ await meter.action(arm, room.id, arm.clientId, 'message', message.messageKey);
736
+ if (['start', 'steer'].includes(message.turnKind) &&
737
+ plainText(message.threadId, 80, false) && plainText(message.turnId, 80, false)) {
738
+ const key = `${message.threadId}:${message.turnId}`;
739
+ if (key.length <= 128) {
740
+ const counted = await meter.action(arm, room.id, arm.clientId, 'turn', key);
741
+ if (counted && message.turnKind === 'steer') diagnostics?.record('usage', 'turn-recovered', 'steer');
742
+ if (!room.usageTasks.has(key) && message.observerReady === true && message.completed !== true &&
743
+ (message.turnKind === 'start' || message.active === true)) {
744
+ const task = await meter.turnOrigin(room.id, key);
745
+ if (task) {
746
+ await meter.pulse(task, true, room.id, task.clientId, counted ? key : randomUUID());
747
+ room.usageTasks.set(key, task);
748
+ }
749
+ }
750
+ }
751
+ }
752
+ } catch (error) { diagnostics?.record('usage', 'message-error', 'database'); log({ event: 'usage-error', message: error.message }); }
753
+ return;
754
+ }
755
+ if (!['turn/started', 'turn/completed', 'heartbeat', 'resume', 'inactive'].includes(message.method) ||
756
+ !plainText(message.threadId, 80, false) || !plainText(message.turnId, 80, false)) return;
757
+ const key = `${message.threadId}:${message.turnId}`;
758
+ if (key.length > 128) return;
759
+ let task = room.usageTasks.get(key);
760
+ try {
761
+ if (message.method === 'resume' && !task) {
762
+ task = await meter.turnOrigin(room.id, key);
763
+ if (!task) { diagnostics?.record('usage', 'turn-resume', 'origin-missing'); return; }
764
+ await meter.pulse(task, true, room.id, task.clientId, `${key}:${randomUUID()}`);
765
+ diagnostics?.record('usage', 'turn-resume', 'confirmed');
766
+ room.usageTasks.set(key, task);
767
+ } else if (task && message.method === 'heartbeat') await meter.pulse(task, true, room.id, task.clientId);
768
+ else if (task && message.method === 'inactive') {
769
+ await meter.lost(task);
770
+ room.usageTasks.delete(key);
771
+ }
772
+ else if (task && message.method === 'turn/completed') {
773
+ await meter.pulse(task, false, room.id, task.clientId);
774
+ room.usageTasks.delete(key);
775
+ }
776
+ } catch (error) { log({ event: 'usage-error', message: error.message }); }
777
+ return;
778
+ }
289
779
  if (message.type === 'lan-candidates') {
290
780
  room.lanCandidates = normalizeLanCandidates(message.candidates);
291
781
  for (const connection of room.connections.values()) send(connection.ws, { type: 'lan-candidates', candidates: room.lanCandidates });
@@ -298,8 +788,23 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
298
788
  if (message.type === 'pair-code') {
299
789
  send(ws, { type: 'pair-code', commandId: message.commandId, room: room.id, ...invitation(room) }); return;
300
790
  }
791
+ if (['invite-create', 'invite-list', 'invite-revoke'].includes(message.type)) {
792
+ try {
793
+ if (!room.durable) throw new Error('长期邀请需要持久工作站身份');
794
+ let result;
795
+ if (message.type === 'invite-create') result = longInvitations.create(room.id, message.expires);
796
+ else if (message.type === 'invite-list') result = { invitations: longInvitations.list().filter(item => item.room === room.id) };
797
+ else {
798
+ if (!longInvitations.list().some(item => item.id === message.id && item.room === room.id)) throw new Error('未找到本工作站的邀请');
799
+ longInvitations.revoke(message.id);
800
+ result = { id: message.id, revoked: true };
801
+ }
802
+ send(ws, { type: message.type, commandId: message.commandId, ...result });
803
+ } catch (error) { send(ws, { type: message.type, commandId: message.commandId, error: error.message }); }
804
+ return;
805
+ }
301
806
  if (message.type === 'revoke') {
302
- commit(room, { clients: [], revision: room.revision + 1 });
807
+ await commit(room, current => ({ clients: [], revision: current.revision + 1 }));
303
808
  room.pairingHash = undefined;
304
809
  for (const connection of room.connections.values()) {
305
810
  releaseRelay(connection);
@@ -307,8 +812,12 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
307
812
  connection.ws.close(4401, 'Device authorization revoked');
308
813
  }
309
814
  room.connections.clear();
815
+ for (const client of room.uploadClients?.values() || []) client.ws.close(4401, 'Device authorization revoked');
816
+ room.uploadClients?.clear();
310
817
  for (const observer of room.observers.values()) observer.ws.close(4401, 'Device authorization revoked');
311
818
  room.observers.clear();
819
+ for (const socket of room.deviceManagers?.keys() || []) socket.close(4401, 'Device authorization revoked');
820
+ room.deviceManagers?.clear();
312
821
  send(ws, { type: 'authorization-changed', clients: [], revision: room.revision });
313
822
  send(ws, { type: 'revoked', commandId: message.commandId, room: room.id }); return;
314
823
  }
@@ -318,49 +827,76 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
318
827
  else if (message.type === 'ready') queueClientMessage(connection, { type: 'ready', p2pProbe: connection.p2pProbe ? 1 : 0,
319
828
  p2pProbeRepeat: connection.p2pProbe && connection.repeat });
320
829
  else if (message.type === 'p2p-probe' && connection.p2pProbe && validProbeSignal(message)) {
321
- if (connection.probeId !== message.probeId) return;
322
- if (++connection.signals > 150) return fail(1008, 'Probe signal limit');
830
+ const diagnostic = message.purpose === 'diagnostic';
831
+ const file = message.purpose === 'file';
832
+ if (message.purpose && !diagnostic && !file) return fail(1008, 'Invalid probe purpose');
833
+ if (connection[file ? 'fileProbeId' : diagnostic ? 'diagnosticProbeId' : 'probeId'] !== message.probeId) return;
834
+ if (++connection[file ? 'fileSignals' : diagnostic ? 'diagnosticSignals' : 'signals'] > 150) return fail(1008, 'Probe signal limit');
323
835
  send(connection.ws, { ...message, session: undefined });
324
836
  }
325
837
  else if (message.type === 'detach') connection.ws.close(1011, 'Local Bridge unavailable');
326
838
  else fail();
839
+ } else if (role === 'file-host' && room.uploadHost === ws) {
840
+ const client = room.uploadClients.get(message.session);
841
+ if (!client) return;
842
+ if (message.type === 'file-frame' && typeof message.payload === 'string' && message.payload.length <= 512 * 1024) {
843
+ if (client.ws.bufferedAmount > 1024 * 1024) return client.ws.close(1013, 'File receiver too slow');
844
+ send(client.ws, { type: 'file-frame', payload: message.payload });
845
+ } else if (message.type === 'file-close') client.ws.close(1011, 'File receiver unavailable');
846
+ else fail(1008, 'Invalid file frame');
847
+ } else if (role === 'file-client' && room.uploadClients?.get(session)?.ws === ws) {
848
+ if (message.type !== 'file-frame' || typeof message.payload !== 'string' || message.payload.length > 512 * 1024) return fail(1008, 'Invalid file frame');
849
+ if (room.uploadHost?.bufferedAmount > 1024 * 1024) return fail(1013, 'File receiver too slow');
850
+ send(room.uploadHost, { type: 'file-frame', session, payload: message.payload });
327
851
  } else if (role === 'observer' && room.observers.get(clientId)?.ws === ws && message.type === 'official-computer') {
328
852
  const computer = normalizeOfficialComputer(message);
329
- commit(room, { officialComputer: computer });
853
+ await commit(room, { officialComputer: computer });
330
854
  send(ws, { type: 'official-computer-saved', computer });
331
855
  } else if (role === 'client' && room.connections.get(clientId)?.ws === ws && message.type === 'p2p-probe') {
332
856
  const connection = room.connections.get(clientId);
333
857
  if (!connection.p2pProbe || !validProbeSignal(message)) return fail(1008, 'Invalid probe signal');
858
+ const diagnostic = message.purpose === 'diagnostic';
859
+ const file = message.purpose === 'file';
860
+ if (message.purpose && !diagnostic && !file) return fail(1008, 'Invalid probe purpose');
861
+ const idField = file ? 'fileProbeId' : diagnostic ? 'diagnosticProbeId' : 'probeId';
862
+ const signalsField = file ? 'fileSignals' : diagnostic ? 'diagnosticSignals' : 'signals';
863
+ const startedField = file ? 'fileProbeStarted' : diagnostic ? 'diagnosticProbeStarted' : 'probeStarted';
334
864
  if (message.kind === 'start') {
335
- if (connection.probeId && (!connection.repeat || Date.now() - connection.probeStarted < 2000)) {
336
- send(ws, { type: 'p2p-probe', version: 1, probeId: message.probeId, kind: 'close' }); return;
865
+ if (connection[idField] && (!connection.repeat || Date.now() - connection[startedField] < 2000)) {
866
+ send(ws, { type: 'p2p-probe', version: 1, probeId: message.probeId,
867
+ ...(message.purpose ? { purpose: message.purpose } : {}), kind: 'close' }); return;
337
868
  }
338
- connection.probeId = message.probeId; connection.signals = 0; connection.probeStarted = Date.now();
869
+ connection[idField] = message.probeId; connection[signalsField] = 0; connection[startedField] = Date.now();
339
870
  }
340
- if (connection.probeId !== message.probeId) return;
341
- if (++connection.signals > 150) return fail(1008, 'Probe signal limit');
871
+ if (connection[idField] !== message.probeId) return;
872
+ if (++connection[signalsField] > 150) return fail(1008, 'Probe signal limit');
342
873
  send(room.host, { ...message, session });
874
+ if (diagnostic && message.kind === 'close') connection.diagnosticProbeId = undefined;
343
875
  } else if (room.connections.get(clientId)?.ws === ws && message.type === 'p2p-route' && message.direct === 1) {
344
876
  releaseRelay(room.connections.get(clientId));
345
877
  } else if (room.connections.get(clientId)?.ws === ws && message.type === 'data' && typeof message.payload === 'string') {
346
878
  const connection = room.connections.get(clientId);
347
- if (!controlPayload(message.payload) && !admitRelay(connection, room)) {
879
+ if (!connection.userId && !managementPayload(message.payload)) return fail(4431, 'Account login required');
880
+ if (connection.userId && !controlPayload(message.payload) && !admitRelay(connection, room)) {
348
881
  ws.close(4428, 'Server busy'); return;
349
882
  }
350
883
  send(room.host, { type: 'data', session, payload: message.payload });
351
- } else fail();
352
- } catch (error) { log({ event: 'request-error', role, message: 'Relay operation failed' }); ws.close(1011, 'Relay operation failed'); }
353
- });
354
- });
884
+ } else fail();
885
+ } catch (error) { log({ event: 'request-error', role, message: 'Relay operation failed' }); ws.close(1011, 'Relay operation failed'); }
886
+ finally { pendingMessages--; pendingBytes -= size; }
887
+ }).catch(() => ws.close(1011, 'Relay operation failed'));
888
+ });
889
+ });
355
890
  await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port, '127.0.0.1', resolve); });
356
891
  listenUrl = `ws://127.0.0.1:${server.address().port}/relay`;
357
892
  return {
358
893
  url: listenUrl, hostKey,
359
- async close() {
360
- for (const ws of wss.clients) ws.terminate();
361
- await new Promise(resolve => wss.close(resolve));
362
- await new Promise(resolve => server.close(resolve));
363
- },
894
+ async close() {
895
+ for (const ws of wss.clients) ws.terminate();
896
+ await new Promise(resolve => wss.close(resolve));
897
+ await new Promise(resolve => server.close(resolve));
898
+ if (database) await database.close();
899
+ },
364
900
  };
365
901
  }
366
902
 
@@ -434,6 +970,20 @@ function plainText(value, limit, empty) {
434
970
  return typeof value === 'string' && (empty || value.trim().length > 0) && value.trim().length <= limit && !/[\u0000-\u001f\u007f]/.test(value);
435
971
  }
436
972
 
973
+ // Device-authorized management must not become an anonymous chat tunnel.
974
+ function managementPayload(value) {
975
+ try {
976
+ const message = JSON.parse(value);
977
+ if (['unsubscribe', 'cancel', 'response-ack'].includes(message.type)) return true;
978
+ if (message.type === 'subscribe') return message.path === '/api/shared-recovery/events';
979
+ if (message.type !== 'request') return false;
980
+ const method = message.method || 'GET';
981
+ if (method === 'GET') return ['/api/health', '/api/official-remote/status'].includes(message.path) ||
982
+ /^\/api\/threads\/[0-9a-f-]{28,64}\/model-changes(?:\?|$)/i.test(message.path);
983
+ return method === 'POST' && message.path === '/api/official-remote/pairing';
984
+ } catch { return false; }
985
+ }
986
+
437
987
  function controlPayload(value) {
438
988
  try {
439
989
  const message = JSON.parse(value);
@@ -454,13 +1004,3 @@ function readBody(request, limit) {
454
1004
  request.on('end', () => resolve(value)); request.on('error', reject);
455
1005
  });
456
1006
  }
457
-
458
- function adminPage() {
459
- return `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Code Relax 管理</title><style>
460
- body{margin:0;background:#0d1210;color:#eef4f0;font:15px system-ui,sans-serif}main{max-width:760px;margin:40px auto;padding:0 20px}section{background:#151d19;border:1px solid #2a3931;border-radius:14px;padding:18px;margin:16px 0}h1{font-size:24px}h2{font-size:17px}label{display:block;margin:12px 0 6px;color:#aab7af}input,textarea{box-sizing:border-box;width:100%;padding:11px;border:1px solid #43564b;border-radius:9px;background:#0d1210;color:#eef4f0}textarea{min-height:120px;resize:vertical}button{margin:12px 8px 0 0;padding:10px 16px;border:0;border-radius:9px;background:#2c7653;color:white}button.secondary{background:#39443e}pre{white-space:pre-wrap;color:#b9c8bf}.row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.metric{padding:12px;background:#0d1210;border-radius:9px}.metric b{display:block;font-size:22px}</style><main><h1>Code Relax 管理</h1><section><h2>运行状态</h2><div id="metrics" class="row"></div><button onclick="load()">刷新</button></section><section><h2>服务器转发容量</h2><label>最大并发 WSS 转发会话</label><input id="limit" type="number" min="1" max="256"><button onclick="saveLimit()">保存</button></section><section><h2>系统公告</h2><label>标题</label><input id="title" maxlength="80"><label>正文</label><textarea id="message" maxlength="2000"></textarea><button onclick="publish()">发布</button><button class="secondary" onclick="withdraw()">撤下</button><pre id="current"></pre></section><pre id="result"></pre><script>
461
- const request=async(path,options={})=>{const response=await fetch(path,options);const body=await response.json();if(!response.ok)throw new Error(body.error||('HTTP '+response.status));return body};
462
- async function load(){try{const s=await request('/admin/api/status');limit.value=s.maxRelaySessions;metrics.innerHTML='<div class="metric"><b>'+s.activeRelaySessions+'</b>受限转发 / '+s.maxRelaySessions+'</div><div class="metric"><b>'+s.activeExemptRelaySessions+'</b>白名单转发</div><div class="metric"><b>'+s.websocketConnections+'</b>WebSocket</div><div class="metric"><b>'+s.onlineComputers+'</b>在线工作站</div><div class="metric"><b>'+s.activePhones+'</b>活动手机</div>';current.textContent=s.announcement?s.announcement.title+'\\n'+s.announcement.message+'\\n'+s.announcement.publishedAt:'当前无公告';}catch(e){result.textContent=e.message}}
463
- const write=(method,path,body)=>request(path,{method,headers:{'Content-Type':'application/json','X-Code-Relax-Admin':'1'},body:JSON.stringify(body)}).then(()=>{result.textContent='已保存';return load()}).catch(e=>result.textContent=e.message);
464
- function saveLimit(){return write('PUT','/admin/api/settings',{maxRelaySessions:Number(limit.value)})}function publish(){return write('PUT','/admin/api/announcement',{title:title.value,message:message.value})}function withdraw(){return write('DELETE','/admin/api/announcement',{})}load();
465
- </script></main></html>`;
466
- }