@walkhi/code-relax 0.1.0-beta.4 → 0.1.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (845) hide show
  1. package/README.md +83 -71
  2. package/dist/bin/cleanup-accounts.mjs +63 -0
  3. package/dist/bin/codex-remote.mjs +323 -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 +63 -0
  7. package/dist/bin/publish-harmony-release.mjs +46 -0
  8. package/dist/bin/release-upload-server.mjs +174 -0
  9. package/dist/bin/self-relay-admin.mjs +8 -0
  10. package/dist/bin/self-relay-demo.mjs +2 -2
  11. package/dist/bin/self-relay-server.mjs +43 -30
  12. package/dist/build-platform.json +1 -0
  13. package/dist/get-shared-service-url.ps1 +6 -6
  14. package/dist/shared/app-server-events.cjs +368 -238
  15. package/dist/shared/code-browser.cjs +159 -0
  16. package/dist/shared/file-changes.cjs +31 -0
  17. package/dist/shared/file-citations.cjs +117 -0
  18. package/dist/shared/file-mentions.cjs +92 -0
  19. package/dist/shared/message-delivery.cjs +100 -0
  20. package/dist/shared/p2p-data-channel.cjs +22 -0
  21. package/dist/shared/p2p-diagnostics.cjs +1 -1
  22. package/dist/shared/p2p-file-channel.cjs +87 -0
  23. package/dist/shared/p2p-ice-policy.cjs +22 -0
  24. package/dist/shared/plan-pricing.cjs +32 -0
  25. package/dist/shared/question-attention.cjs +69 -0
  26. package/dist/shared/server-requests.cjs +157 -5
  27. package/dist/shared/service-endpoints.cjs +21 -0
  28. package/dist/shared/timeline-sync.cjs +111 -5
  29. package/dist/skills/code-relax/SKILL.md +13 -8
  30. package/dist/src/archived-threads.mjs +22 -0
  31. package/dist/src/bounded-process-log.mjs +63 -0
  32. package/dist/src/celsius-client.mjs +107 -0
  33. package/dist/src/code-browser.mjs +100 -0
  34. package/dist/src/desktop-launcher.mjs +33 -38
  35. package/dist/src/dots-approval-timeline.mjs +99 -0
  36. package/dist/src/dots-avatar.mjs +42 -0
  37. package/dist/src/dots-chat-projection.mjs +50 -0
  38. package/dist/src/dots-chat-server.mjs +482 -0
  39. package/dist/src/dots-message-images.mjs +319 -0
  40. package/dist/src/dots-profile.mjs +77 -0
  41. package/dist/src/dots-room-activity.mjs +94 -0
  42. package/dist/src/dots-room-client.mjs +135 -0
  43. package/dist/src/dots-room-projection.mjs +94 -0
  44. package/dist/src/dots-schedules.mjs +66 -0
  45. package/dist/src/dots-summary-tasks.mjs +32 -0
  46. package/dist/src/dots-task-catalog.mjs +430 -0
  47. package/dist/src/dots-task-files.mjs +52 -0
  48. package/dist/src/file-transfer-range.mjs +22 -0
  49. package/dist/src/file-upload-session.mjs +130 -0
  50. package/dist/src/hosted-history-app-server.mjs +103 -0
  51. package/dist/src/hosted-history-cache.mjs +113 -0
  52. package/dist/src/lan-socket-server.mjs +241 -84
  53. package/dist/src/managed-app-server.mjs +54 -16
  54. package/dist/src/message-images.mjs +0 -18
  55. package/dist/src/message-upload-files.mjs +109 -0
  56. package/dist/src/onboarding.mjs +10 -29
  57. package/dist/src/plan-pricing.mjs +47 -0
  58. package/dist/src/platform/README.md +2 -2
  59. package/dist/src/platform/codex-protocol.mjs +46 -0
  60. package/dist/src/platform/codex-runtime.mjs +3 -0
  61. package/dist/src/platform/credential-store.mjs +3 -0
  62. package/dist/src/platform/desktop-host.mjs +3 -0
  63. package/dist/src/platform/desktop-project-registration.mjs +3 -0
  64. package/dist/src/platform/desktop-project-store.mjs +208 -0
  65. package/dist/src/platform/file-opener.mjs +9 -0
  66. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  67. package/dist/src/platform/linux/credential-store.mjs +28 -0
  68. package/dist/src/platform/linux/desktop-host.mjs +83 -0
  69. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  70. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  71. package/dist/src/platform/linux/network-info.mjs +11 -0
  72. package/dist/src/platform/linux/process.mjs +42 -0
  73. package/dist/src/platform/linux/project-locations.mjs +17 -0
  74. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  75. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  76. package/dist/src/platform/linux/service-host.mjs +18 -0
  77. package/dist/src/platform/linux/system.mjs +49 -0
  78. package/dist/src/platform/linux/user-environment.mjs +7 -0
  79. package/dist/src/platform/macos/codex-runtime.mjs +125 -0
  80. package/dist/src/platform/macos/credential-store.mjs +10 -0
  81. package/dist/src/platform/macos/desktop-host.mjs +82 -0
  82. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -0
  83. package/dist/src/platform/macos/network-info.mjs +8 -0
  84. package/dist/src/platform/macos/project-locations.mjs +8 -0
  85. package/dist/src/platform/macos/resident-startup.mjs +84 -0
  86. package/dist/src/platform/macos/retained-runtime.mjs +127 -0
  87. package/dist/src/platform/macos/service-host.mjs +18 -0
  88. package/dist/src/platform/macos/system.mjs +29 -0
  89. package/dist/src/platform/macos/user-environment.mjs +7 -0
  90. package/dist/src/platform/native-package.mjs +58 -0
  91. package/dist/src/platform/network-info.mjs +3 -0
  92. package/dist/src/platform/path-identity.mjs +5 -0
  93. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  94. package/dist/src/platform/posix/runtime-paths.mjs +25 -0
  95. package/dist/src/platform/power-inhibitor.mjs +22 -0
  96. package/dist/src/platform/project-locations.mjs +3 -0
  97. package/dist/src/platform/runtime-paths.mjs +8 -0
  98. package/dist/src/platform/select.mjs +5 -0
  99. package/dist/src/platform/service-host.mjs +3 -0
  100. package/dist/src/platform/user-environment.mjs +3 -0
  101. package/dist/src/platform/windows/activate-desktop.ps1 +48 -0
  102. package/dist/src/platform/windows/codex-runtime.mjs +215 -0
  103. package/dist/src/platform/windows/credential-store.mjs +27 -27
  104. package/dist/src/platform/windows/credential-store.ps1 +49 -49
  105. package/dist/src/platform/windows/desktop-helper.mjs +179 -0
  106. package/dist/src/platform/windows/desktop-host.mjs +43 -23
  107. package/dist/src/platform/windows/desktop-info.ps1 +14 -16
  108. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -0
  109. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
  110. package/dist/src/platform/windows/desktop-shortcuts.ps1 +21 -28
  111. package/dist/src/platform/windows/dots-profile.mjs +49 -0
  112. package/dist/src/platform/windows/keep-awake.ps1 +20 -0
  113. package/dist/src/platform/windows/launch-worker.mjs +17 -18
  114. package/dist/src/platform/windows/project-locations.mjs +16 -0
  115. package/dist/src/platform/windows/resident-start.js +20 -0
  116. package/dist/src/platform/windows/resident-startup.ps1 +76 -20
  117. package/dist/src/platform/windows/rustdesk-video-probe.mjs +168 -0
  118. package/dist/src/platform/windows/service-host.mjs +24 -36
  119. package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
  120. package/dist/src/platform/windows/user-environment.mjs +1 -1
  121. package/dist/src/project-create.mjs +121 -0
  122. package/dist/src/project-management.mjs +59 -0
  123. package/dist/src/project-remove.mjs +55 -0
  124. package/dist/src/project-update.mjs +59 -0
  125. package/dist/src/runtime-discovery.mjs +47 -0
  126. package/dist/src/self-relay/account-cleanup.mjs +30 -0
  127. package/dist/src/self-relay/account-connections.mjs +52 -0
  128. package/dist/src/self-relay/admin-cloud.mjs +101 -0
  129. package/dist/src/self-relay/admin-page.mjs +80 -0
  130. package/dist/src/self-relay/admin-server.mjs +74 -0
  131. package/dist/src/self-relay/admin-state.mjs +85 -19
  132. package/dist/src/self-relay/admin-style.mjs +14 -0
  133. package/dist/src/self-relay/android-releases.mjs +61 -0
  134. package/dist/src/self-relay/client.mjs +45 -45
  135. package/dist/src/self-relay/connector.mjs +421 -144
  136. package/dist/src/self-relay/demo.mjs +34 -35
  137. package/dist/src/self-relay/device-info.mjs +27 -0
  138. package/dist/src/self-relay/device-store.mjs +10 -2
  139. package/dist/src/self-relay/diagnostics.mjs +54 -0
  140. package/dist/src/self-relay/email-accounts.mjs +183 -0
  141. package/dist/src/self-relay/file-wire.mjs +13 -0
  142. package/dist/src/self-relay/lan-authorizations.mjs +44 -2
  143. package/dist/src/self-relay/lifecycle.mjs +250 -272
  144. package/dist/src/self-relay/long-pairing.mjs +40 -0
  145. package/dist/src/self-relay/mysql-state.mjs +118 -0
  146. package/dist/src/self-relay/p2p-probe.mjs +56 -11
  147. package/dist/src/self-relay/privacy-page.mjs +26 -0
  148. package/dist/src/self-relay/public-releases.mjs +52 -0
  149. package/dist/src/self-relay/rtc-file-bridge.mjs +41 -0
  150. package/dist/src/self-relay/server.mjs +652 -114
  151. package/dist/src/self-relay/usage-daily.mjs +55 -0
  152. package/dist/src/self-relay/usage-meter.mjs +141 -0
  153. package/dist/src/server.mjs +1519 -1793
  154. package/dist/src/service-control.mjs +17 -7
  155. package/dist/src/service-doctor.mjs +86 -33
  156. package/dist/src/service-health.mjs +2 -2
  157. package/dist/src/service-install.mjs +91 -85
  158. package/dist/src/service-lifecycle.mjs +117 -108
  159. package/dist/src/service-power.mjs +58 -0
  160. package/dist/src/service-restart.mjs +43 -10
  161. package/dist/src/service-update-worker.mjs +42 -0
  162. package/dist/src/service-update.mjs +71 -0
  163. package/dist/src/shared-app-server.mjs +561 -407
  164. package/dist/src/shared-catalog.mjs +95 -13
  165. package/dist/src/shared-recovery.mjs +15 -11
  166. package/dist/src/shared-thread-stream.mjs +147 -63
  167. package/dist/src/skill-install.mjs +0 -13
  168. package/dist/src/turn-model-history.mjs +47 -0
  169. package/dist/src/usage-reset.mjs +17 -0
  170. package/dist/src/workstation-downloads.mjs +120 -0
  171. package/package.json +35 -37
  172. package/tools/postinstall.mjs +86 -22
  173. package/dist/src/app-server-client.mjs +0 -468
  174. package/dist/src/app-server-tasks.mjs +0 -358
  175. package/dist/src/debug-ui.mjs +0 -131
  176. package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
  177. package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
  178. package/dist/src/platform/windows/desktop-tools.mjs +0 -48
  179. package/dist/src/thread-catalog.mjs +0 -47
  180. package/dist/tools/find-desktop-pipe.mjs +0 -10
  181. package/dist/web/activity-view.js +0 -283
  182. package/dist/web/approval-ui.js +0 -188
  183. package/dist/web/capabilities.js +0 -57
  184. package/dist/web/chat-transport.js +0 -202
  185. package/dist/web/chat.css +0 -782
  186. package/dist/web/chat.js +0 -3830
  187. package/dist/web/client-platform.js +0 -11
  188. package/dist/web/community-view.js +0 -20
  189. package/dist/web/community.css +0 -77
  190. package/dist/web/community.html +0 -37
  191. package/dist/web/composer-controller.js +0 -101
  192. package/dist/web/conversation-controller.js +0 -99
  193. package/dist/web/disclosure-state-controller.js +0 -95
  194. package/dist/web/draft-controller.js +0 -103
  195. package/dist/web/harmony-platform.js +0 -52
  196. package/dist/web/highlight-powershell.min.js +0 -39
  197. package/dist/web/highlight.min.js +0 -1262
  198. package/dist/web/history-cache.js +0 -240
  199. package/dist/web/history-controller.js +0 -167
  200. package/dist/web/index.html +0 -325
  201. package/dist/web/lan-session.js +0 -122
  202. package/dist/web/link-action-controller.js +0 -212
  203. package/dist/web/message-send-controller.js +0 -177
  204. package/dist/web/message-view.js +0 -98
  205. package/dist/web/p2p-data-channel.js +0 -50
  206. package/dist/web/p2p-diagnostics.js +0 -28
  207. package/dist/web/p2p-probe.js +0 -123
  208. package/dist/web/page-resume.js +0 -28
  209. package/dist/web/pending-message-store.js +0 -108
  210. package/dist/web/queue-controller.js +0 -82
  211. package/dist/web/resources.json +0 -1
  212. package/dist/web/self-relay-session.js +0 -129
  213. package/dist/web/server-requests.js +0 -67
  214. package/dist/web/station-connection-controller.js +0 -75
  215. package/dist/web/task-list-view.js +0 -296
  216. package/dist/web/thread-attention-controller.js +0 -124
  217. package/dist/web/thread-context-controller.js +0 -30
  218. package/dist/web/thread-list-controller.js +0 -61
  219. package/dist/web/thread-list-sync.js +0 -86
  220. package/dist/web/thread-title-controller.js +0 -57
  221. package/dist/web/timeline-formatters.js +0 -249
  222. package/dist/web/timeline-reducer.js +0 -81
  223. package/dist/web/timeline-renderer.js +0 -161
  224. package/dist/web/timeline-scroll-controller.js +0 -50
  225. package/dist/web/timeline-sync.js +0 -67
  226. package/dist/web/usage-controller.js +0 -258
  227. package/dist/web/vendor/README.md +0 -14
  228. package/dist/web/vendor/highlight-powershell.min.js +0 -39
  229. package/dist/web/vendor/highlight.LICENSE.txt +0 -29
  230. package/dist/web/vendor/highlight.min.js +0 -1262
  231. package/dist/web/vendor/lucide.LICENSE.txt +0 -17
  232. package/node_modules/@img/colour/LICENSE.md +0 -82
  233. package/node_modules/@img/colour/README.md +0 -15
  234. package/node_modules/@img/colour/color.cjs +0 -1596
  235. package/node_modules/@img/colour/index.cjs +0 -1
  236. package/node_modules/@img/colour/index.d.ts +0 -929
  237. package/node_modules/@img/colour/package.json +0 -58
  238. package/node_modules/@img/sharp-win32-x64/LICENSE +0 -191
  239. package/node_modules/@img/sharp-win32-x64/README.md +0 -59
  240. package/node_modules/@img/sharp-win32-x64/index.cjs +0 -1
  241. package/node_modules/@img/sharp-win32-x64/lib/libvips-42.dll +0 -0
  242. package/node_modules/@img/sharp-win32-x64/lib/libvips-cpp-8.18.6.dll +0 -0
  243. package/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64-0.35.4.node +0 -0
  244. package/node_modules/@img/sharp-win32-x64/package.json +0 -40
  245. package/node_modules/@img/sharp-win32-x64/versions.json +0 -30
  246. package/node_modules/@node-datachannel/win32-x64-msvc/node_datachannel.node +0 -0
  247. package/node_modules/@node-datachannel/win32-x64-msvc/package.json +0 -20
  248. package/node_modules/ansi-regex/index.d.ts +0 -37
  249. package/node_modules/ansi-regex/index.js +0 -10
  250. package/node_modules/ansi-regex/license +0 -9
  251. package/node_modules/ansi-regex/package.json +0 -55
  252. package/node_modules/ansi-regex/readme.md +0 -78
  253. package/node_modules/ansi-styles/index.d.ts +0 -345
  254. package/node_modules/ansi-styles/index.js +0 -163
  255. package/node_modules/ansi-styles/license +0 -9
  256. package/node_modules/ansi-styles/package.json +0 -56
  257. package/node_modules/ansi-styles/readme.md +0 -152
  258. package/node_modules/camelcase/index.d.ts +0 -63
  259. package/node_modules/camelcase/index.js +0 -76
  260. package/node_modules/camelcase/license +0 -9
  261. package/node_modules/camelcase/package.json +0 -43
  262. package/node_modules/camelcase/readme.md +0 -99
  263. package/node_modules/cliui/CHANGELOG.md +0 -76
  264. package/node_modules/cliui/LICENSE.txt +0 -14
  265. package/node_modules/cliui/README.md +0 -115
  266. package/node_modules/cliui/index.js +0 -354
  267. package/node_modules/cliui/package.json +0 -65
  268. package/node_modules/color-convert/CHANGELOG.md +0 -54
  269. package/node_modules/color-convert/LICENSE +0 -21
  270. package/node_modules/color-convert/README.md +0 -68
  271. package/node_modules/color-convert/conversions.js +0 -839
  272. package/node_modules/color-convert/index.js +0 -81
  273. package/node_modules/color-convert/package.json +0 -48
  274. package/node_modules/color-convert/route.js +0 -97
  275. package/node_modules/color-name/LICENSE +0 -8
  276. package/node_modules/color-name/README.md +0 -11
  277. package/node_modules/color-name/index.js +0 -152
  278. package/node_modules/color-name/package.json +0 -28
  279. package/node_modules/decamelize/index.js +0 -13
  280. package/node_modules/decamelize/license +0 -21
  281. package/node_modules/decamelize/package.json +0 -38
  282. package/node_modules/decamelize/readme.md +0 -48
  283. package/node_modules/detect-libc/LICENSE +0 -201
  284. package/node_modules/detect-libc/README.md +0 -163
  285. package/node_modules/detect-libc/index.d.ts +0 -14
  286. package/node_modules/detect-libc/lib/detect-libc.js +0 -313
  287. package/node_modules/detect-libc/lib/elf.js +0 -39
  288. package/node_modules/detect-libc/lib/filesystem.js +0 -51
  289. package/node_modules/detect-libc/lib/process.js +0 -24
  290. package/node_modules/detect-libc/package.json +0 -44
  291. package/node_modules/dijkstrajs/.travis.yml +0 -4
  292. package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
  293. package/node_modules/dijkstrajs/LICENSE.md +0 -19
  294. package/node_modules/dijkstrajs/README.md +0 -18
  295. package/node_modules/dijkstrajs/dijkstra.js +0 -165
  296. package/node_modules/dijkstrajs/package.json +0 -60
  297. package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
  298. package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  299. package/node_modules/emoji-regex/README.md +0 -73
  300. package/node_modules/emoji-regex/es2015/index.js +0 -6
  301. package/node_modules/emoji-regex/es2015/text.js +0 -6
  302. package/node_modules/emoji-regex/index.d.ts +0 -23
  303. package/node_modules/emoji-regex/index.js +0 -6
  304. package/node_modules/emoji-regex/package.json +0 -50
  305. package/node_modules/emoji-regex/text.js +0 -6
  306. package/node_modules/find-up/index.d.ts +0 -137
  307. package/node_modules/find-up/index.js +0 -89
  308. package/node_modules/find-up/license +0 -9
  309. package/node_modules/find-up/package.json +0 -53
  310. package/node_modules/find-up/readme.md +0 -156
  311. package/node_modules/get-caller-file/LICENSE.md +0 -6
  312. package/node_modules/get-caller-file/README.md +0 -41
  313. package/node_modules/get-caller-file/index.d.ts +0 -2
  314. package/node_modules/get-caller-file/index.js +0 -22
  315. package/node_modules/get-caller-file/index.js.map +0 -1
  316. package/node_modules/get-caller-file/package.json +0 -42
  317. package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
  318. package/node_modules/is-fullwidth-code-point/index.js +0 -50
  319. package/node_modules/is-fullwidth-code-point/license +0 -9
  320. package/node_modules/is-fullwidth-code-point/package.json +0 -42
  321. package/node_modules/is-fullwidth-code-point/readme.md +0 -39
  322. package/node_modules/locate-path/index.d.ts +0 -83
  323. package/node_modules/locate-path/index.js +0 -65
  324. package/node_modules/locate-path/license +0 -9
  325. package/node_modules/locate-path/package.json +0 -45
  326. package/node_modules/locate-path/readme.md +0 -122
  327. package/node_modules/node-datachannel/.clang-format +0 -17
  328. package/node_modules/node-datachannel/.editorconfig +0 -12
  329. package/node_modules/node-datachannel/.eslintignore +0 -8
  330. package/node_modules/node-datachannel/.eslintrc.json +0 -27
  331. package/node_modules/node-datachannel/.gitmodules +0 -3
  332. package/node_modules/node-datachannel/.prettierignore +0 -7
  333. package/node_modules/node-datachannel/.prettierrc +0 -13
  334. package/node_modules/node-datachannel/API.md +0 -247
  335. package/node_modules/node-datachannel/BULDING.md +0 -26
  336. package/node_modules/node-datachannel/CMakeLists.txt +0 -153
  337. package/node_modules/node-datachannel/LICENSE +0 -373
  338. package/node_modules/node-datachannel/README.md +0 -129
  339. package/node_modules/node-datachannel/dist/cjs/index.cjs +0 -11
  340. package/node_modules/node-datachannel/dist/cjs/index.cjs.map +0 -1
  341. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs +0 -101
  342. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs.map +0 -1
  343. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs +0 -91
  344. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs.map +0 -1
  345. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs +0 -94
  346. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs.map +0 -1
  347. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs +0 -45
  348. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs.map +0 -1
  349. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs +0 -8
  350. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs.map +0 -1
  351. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs +0 -86
  352. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs.map +0 -1
  353. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs +0 -34
  354. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs.map +0 -1
  355. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs +0 -34
  356. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs.map +0 -1
  357. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs +0 -214
  358. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs.map +0 -1
  359. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs +0 -53
  360. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +0 -1
  361. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs +0 -83
  362. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs.map +0 -1
  363. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs +0 -132
  364. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs.map +0 -1
  365. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs +0 -94
  366. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs.map +0 -1
  367. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs +0 -434
  368. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs.map +0 -1
  369. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs +0 -59
  370. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs.map +0 -1
  371. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs +0 -45
  372. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs.map +0 -1
  373. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs +0 -42
  374. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs.map +0 -1
  375. package/node_modules/node-datachannel/dist/esm/index.mjs +0 -8
  376. package/node_modules/node-datachannel/dist/esm/index.mjs.map +0 -1
  377. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs +0 -78
  378. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs.map +0 -1
  379. package/node_modules/node-datachannel/dist/esm/lib/index.mjs +0 -64
  380. package/node_modules/node-datachannel/dist/esm/lib/index.mjs.map +0 -1
  381. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs +0 -78
  382. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs.map +0 -1
  383. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs +0 -43
  384. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs.map +0 -1
  385. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs +0 -6
  386. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs.map +0 -1
  387. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs +0 -82
  388. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs.map +0 -1
  389. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs +0 -28
  390. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs.map +0 -1
  391. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs +0 -30
  392. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs.map +0 -1
  393. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs +0 -210
  394. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs.map +0 -1
  395. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs +0 -49
  396. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs.map +0 -1
  397. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs +0 -79
  398. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs.map +0 -1
  399. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs +0 -128
  400. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs.map +0 -1
  401. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs +0 -89
  402. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs.map +0 -1
  403. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs +0 -430
  404. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs.map +0 -1
  405. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs +0 -55
  406. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs.map +0 -1
  407. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs +0 -41
  408. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs.map +0 -1
  409. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs +0 -27
  410. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs.map +0 -1
  411. package/node_modules/node-datachannel/dist/types/lib/datachannel-stream.d.ts +0 -24
  412. package/node_modules/node-datachannel/dist/types/lib/index.d.ts +0 -241
  413. package/node_modules/node-datachannel/dist/types/lib/types.d.ts +0 -119
  414. package/node_modules/node-datachannel/dist/types/lib/websocket-server.d.ts +0 -13
  415. package/node_modules/node-datachannel/dist/types/lib/websocket.d.ts +0 -25
  416. package/node_modules/node-datachannel/dist/types/polyfill/Events.d.ts +0 -15
  417. package/node_modules/node-datachannel/dist/types/polyfill/RTCCertificate.d.ts +0 -9
  418. package/node_modules/node-datachannel/dist/types/polyfill/RTCDataChannel.d.ts +0 -29
  419. package/node_modules/node-datachannel/dist/types/polyfill/RTCDtlsTransport.d.ts +0 -15
  420. package/node_modules/node-datachannel/dist/types/polyfill/RTCError.d.ts +0 -17
  421. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceCandidate.d.ts +0 -21
  422. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceTransport.d.ts +0 -30
  423. package/node_modules/node-datachannel/dist/types/polyfill/RTCPeerConnection.d.ts +0 -64
  424. package/node_modules/node-datachannel/dist/types/polyfill/RTCSctpTransport.d.ts +0 -15
  425. package/node_modules/node-datachannel/dist/types/polyfill/RTCSessionDescription.d.ts +0 -10
  426. package/node_modules/node-datachannel/dist/types/polyfill/index.d.ts +0 -26
  427. package/node_modules/node-datachannel/jest.config.ts +0 -14
  428. package/node_modules/node-datachannel/package.json +0 -133
  429. package/node_modules/node-datachannel/rollup.config.mjs +0 -67
  430. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.cpp +0 -530
  431. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.h +0 -63
  432. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.cpp +0 -172
  433. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.h +0 -43
  434. package/node_modules/node-datachannel/src/cpp/main.cpp +0 -58
  435. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.cpp +0 -457
  436. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.h +0 -52
  437. package/node_modules/node-datachannel/src/cpp/media-av1packetization.cpp +0 -24
  438. package/node_modules/node-datachannel/src/cpp/media-av1packetization.h +0 -11
  439. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.cpp +0 -126
  440. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.h +0 -29
  441. package/node_modules/node-datachannel/src/cpp/media-direction.cpp +0 -43
  442. package/node_modules/node-datachannel/src/cpp/media-direction.h +0 -10
  443. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.cpp +0 -126
  444. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.h +0 -30
  445. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.cpp +0 -126
  446. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.h +0 -30
  447. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.cpp +0 -32
  448. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.h +0 -11
  449. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.cpp +0 -31
  450. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.h +0 -11
  451. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.cpp +0 -79
  452. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.h +0 -28
  453. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.cpp +0 -68
  454. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.h +0 -28
  455. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.cpp +0 -57
  456. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.h +0 -28
  457. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.cpp +0 -93
  458. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.h +0 -30
  459. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.cpp +0 -167
  460. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.h +0 -35
  461. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.cpp +0 -95
  462. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.h +0 -30
  463. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.cpp +0 -458
  464. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.h +0 -61
  465. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.cpp +0 -526
  466. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.h +0 -56
  467. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.cpp +0 -1298
  468. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.h +0 -89
  469. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.cpp +0 -205
  470. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.h +0 -24
  471. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.cpp +0 -57
  472. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.h +0 -47
  473. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.cpp +0 -275
  474. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.h +0 -41
  475. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.cpp +0 -796
  476. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.h +0 -63
  477. package/node_modules/node-datachannel/src/index.ts +0 -9
  478. package/node_modules/node-datachannel/src/lib/datachannel-stream.ts +0 -100
  479. package/node_modules/node-datachannel/src/lib/index.ts +0 -290
  480. package/node_modules/node-datachannel/src/lib/node-datachannel.ts +0 -84
  481. package/node_modules/node-datachannel/src/lib/types.ts +0 -169
  482. package/node_modules/node-datachannel/src/lib/websocket-server.ts +0 -37
  483. package/node_modules/node-datachannel/src/lib/websocket.ts +0 -26
  484. package/node_modules/node-datachannel/src/polyfill/Events.ts +0 -82
  485. package/node_modules/node-datachannel/src/polyfill/Exception.ts +0 -37
  486. package/node_modules/node-datachannel/src/polyfill/README.md +0 -41
  487. package/node_modules/node-datachannel/src/polyfill/RTCCertificate.ts +0 -21
  488. package/node_modules/node-datachannel/src/polyfill/RTCDataChannel.ts +0 -225
  489. package/node_modules/node-datachannel/src/polyfill/RTCDtlsTransport.ts +0 -46
  490. package/node_modules/node-datachannel/src/polyfill/RTCError.ts +0 -78
  491. package/node_modules/node-datachannel/src/polyfill/RTCIceCandidate.ts +0 -128
  492. package/node_modules/node-datachannel/src/polyfill/RTCIceTransport.ts +0 -90
  493. package/node_modules/node-datachannel/src/polyfill/RTCPeerConnection.ts +0 -527
  494. package/node_modules/node-datachannel/src/polyfill/RTCSctpTransport.ts +0 -51
  495. package/node_modules/node-datachannel/src/polyfill/RTCSessionDescription.ts +0 -41
  496. package/node_modules/node-datachannel/src/polyfill/index.ts +0 -38
  497. package/node_modules/node-datachannel/tsconfig.json +0 -21
  498. package/node_modules/p-limit/index.d.ts +0 -38
  499. package/node_modules/p-limit/index.js +0 -57
  500. package/node_modules/p-limit/license +0 -9
  501. package/node_modules/p-limit/package.json +0 -52
  502. package/node_modules/p-limit/readme.md +0 -101
  503. package/node_modules/p-locate/index.d.ts +0 -64
  504. package/node_modules/p-locate/index.js +0 -52
  505. package/node_modules/p-locate/license +0 -9
  506. package/node_modules/p-locate/package.json +0 -53
  507. package/node_modules/p-locate/readme.md +0 -90
  508. package/node_modules/p-try/index.d.ts +0 -39
  509. package/node_modules/p-try/index.js +0 -9
  510. package/node_modules/p-try/license +0 -9
  511. package/node_modules/p-try/package.json +0 -42
  512. package/node_modules/p-try/readme.md +0 -58
  513. package/node_modules/path-exists/index.d.ts +0 -28
  514. package/node_modules/path-exists/index.js +0 -23
  515. package/node_modules/path-exists/license +0 -9
  516. package/node_modules/path-exists/package.json +0 -39
  517. package/node_modules/path-exists/readme.md +0 -52
  518. package/node_modules/pngjs/.eslintignore +0 -2
  519. package/node_modules/pngjs/.eslintrc.json +0 -19
  520. package/node_modules/pngjs/.prettierignore +0 -3
  521. package/node_modules/pngjs/LICENSE +0 -20
  522. package/node_modules/pngjs/README.md +0 -429
  523. package/node_modules/pngjs/browser.js +0 -16096
  524. package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
  525. package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
  526. package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
  527. package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
  528. package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
  529. package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
  530. package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
  531. package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
  532. package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
  533. package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
  534. package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
  535. package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
  536. package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
  537. package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
  538. package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
  539. package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
  540. package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
  541. package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
  542. package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
  543. package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
  544. package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
  545. package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
  546. package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
  547. package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
  548. package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
  549. package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
  550. package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  551. package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
  552. package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
  553. package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
  554. package/node_modules/pngjs/coverage/lcov.info +0 -2177
  555. package/node_modules/pngjs/lib/bitmapper.js +0 -267
  556. package/node_modules/pngjs/lib/bitpacker.js +0 -158
  557. package/node_modules/pngjs/lib/chunkstream.js +0 -189
  558. package/node_modules/pngjs/lib/constants.js +0 -32
  559. package/node_modules/pngjs/lib/crc.js +0 -40
  560. package/node_modules/pngjs/lib/filter-pack.js +0 -171
  561. package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
  562. package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
  563. package/node_modules/pngjs/lib/filter-parse.js +0 -177
  564. package/node_modules/pngjs/lib/format-normaliser.js +0 -93
  565. package/node_modules/pngjs/lib/interlace.js +0 -95
  566. package/node_modules/pngjs/lib/packer-async.js +0 -50
  567. package/node_modules/pngjs/lib/packer-sync.js +0 -56
  568. package/node_modules/pngjs/lib/packer.js +0 -129
  569. package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
  570. package/node_modules/pngjs/lib/parser-async.js +0 -165
  571. package/node_modules/pngjs/lib/parser-sync.js +0 -108
  572. package/node_modules/pngjs/lib/parser.js +0 -290
  573. package/node_modules/pngjs/lib/png-sync.js +0 -12
  574. package/node_modules/pngjs/lib/png.js +0 -194
  575. package/node_modules/pngjs/lib/sync-inflate.js +0 -168
  576. package/node_modules/pngjs/lib/sync-reader.js +0 -45
  577. package/node_modules/pngjs/package.json +0 -73
  578. package/node_modules/qrcode/README.md +0 -770
  579. package/node_modules/qrcode/bin/qrcode +0 -159
  580. package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
  581. package/node_modules/qrcode/helper/to-sjis.js +0 -105
  582. package/node_modules/qrcode/lib/browser.js +0 -76
  583. package/node_modules/qrcode/lib/can-promise.js +0 -7
  584. package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
  585. package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
  586. package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
  587. package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
  588. package/node_modules/qrcode/lib/core/byte-data.js +0 -30
  589. package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
  590. package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
  591. package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
  592. package/node_modules/qrcode/lib/core/format-info.js +0 -29
  593. package/node_modules/qrcode/lib/core/galois-field.js +0 -69
  594. package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
  595. package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
  596. package/node_modules/qrcode/lib/core/mode.js +0 -167
  597. package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
  598. package/node_modules/qrcode/lib/core/polynomial.js +0 -62
  599. package/node_modules/qrcode/lib/core/qrcode.js +0 -495
  600. package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
  601. package/node_modules/qrcode/lib/core/regex.js +0 -31
  602. package/node_modules/qrcode/lib/core/segments.js +0 -330
  603. package/node_modules/qrcode/lib/core/utils.js +0 -63
  604. package/node_modules/qrcode/lib/core/version-check.js +0 -9
  605. package/node_modules/qrcode/lib/core/version.js +0 -163
  606. package/node_modules/qrcode/lib/index.js +0 -12
  607. package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
  608. package/node_modules/qrcode/lib/renderer/png.js +0 -78
  609. package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
  610. package/node_modules/qrcode/lib/renderer/svg.js +0 -19
  611. package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
  612. package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
  613. package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
  614. package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
  615. package/node_modules/qrcode/lib/renderer/utils.js +0 -99
  616. package/node_modules/qrcode/lib/server.js +0 -138
  617. package/node_modules/qrcode/license +0 -10
  618. package/node_modules/qrcode/package.json +0 -77
  619. package/node_modules/require-directory/.jshintrc +0 -67
  620. package/node_modules/require-directory/.npmignore +0 -1
  621. package/node_modules/require-directory/.travis.yml +0 -3
  622. package/node_modules/require-directory/LICENSE +0 -22
  623. package/node_modules/require-directory/README.markdown +0 -184
  624. package/node_modules/require-directory/index.js +0 -86
  625. package/node_modules/require-directory/package.json +0 -40
  626. package/node_modules/require-main-filename/CHANGELOG.md +0 -26
  627. package/node_modules/require-main-filename/LICENSE.txt +0 -14
  628. package/node_modules/require-main-filename/README.md +0 -26
  629. package/node_modules/require-main-filename/index.js +0 -18
  630. package/node_modules/require-main-filename/package.json +0 -35
  631. package/node_modules/semver/LICENSE +0 -15
  632. package/node_modules/semver/README.md +0 -680
  633. package/node_modules/semver/bin/semver.js +0 -195
  634. package/node_modules/semver/classes/comparator.js +0 -143
  635. package/node_modules/semver/classes/index.js +0 -7
  636. package/node_modules/semver/classes/range.js +0 -577
  637. package/node_modules/semver/classes/semver.js +0 -350
  638. package/node_modules/semver/functions/clean.js +0 -8
  639. package/node_modules/semver/functions/cmp.js +0 -54
  640. package/node_modules/semver/functions/coerce.js +0 -62
  641. package/node_modules/semver/functions/compare-build.js +0 -9
  642. package/node_modules/semver/functions/compare-loose.js +0 -5
  643. package/node_modules/semver/functions/compare.js +0 -7
  644. package/node_modules/semver/functions/diff.js +0 -60
  645. package/node_modules/semver/functions/eq.js +0 -5
  646. package/node_modules/semver/functions/gt.js +0 -5
  647. package/node_modules/semver/functions/gte.js +0 -5
  648. package/node_modules/semver/functions/inc.js +0 -21
  649. package/node_modules/semver/functions/lt.js +0 -5
  650. package/node_modules/semver/functions/lte.js +0 -5
  651. package/node_modules/semver/functions/major.js +0 -5
  652. package/node_modules/semver/functions/minor.js +0 -5
  653. package/node_modules/semver/functions/neq.js +0 -5
  654. package/node_modules/semver/functions/parse.js +0 -18
  655. package/node_modules/semver/functions/patch.js +0 -5
  656. package/node_modules/semver/functions/prerelease.js +0 -8
  657. package/node_modules/semver/functions/rcompare.js +0 -5
  658. package/node_modules/semver/functions/rsort.js +0 -5
  659. package/node_modules/semver/functions/satisfies.js +0 -12
  660. package/node_modules/semver/functions/sort.js +0 -5
  661. package/node_modules/semver/functions/truncate.js +0 -48
  662. package/node_modules/semver/functions/valid.js +0 -8
  663. package/node_modules/semver/index.js +0 -93
  664. package/node_modules/semver/internal/constants.js +0 -37
  665. package/node_modules/semver/internal/debug.js +0 -11
  666. package/node_modules/semver/internal/identifiers.js +0 -29
  667. package/node_modules/semver/internal/lrucache.js +0 -42
  668. package/node_modules/semver/internal/parse-options.js +0 -17
  669. package/node_modules/semver/internal/re.js +0 -223
  670. package/node_modules/semver/package.json +0 -78
  671. package/node_modules/semver/preload.js +0 -4
  672. package/node_modules/semver/range.bnf +0 -17
  673. package/node_modules/semver/ranges/gtr.js +0 -6
  674. package/node_modules/semver/ranges/intersects.js +0 -9
  675. package/node_modules/semver/ranges/ltr.js +0 -6
  676. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  677. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  678. package/node_modules/semver/ranges/min-version.js +0 -63
  679. package/node_modules/semver/ranges/outside.js +0 -82
  680. package/node_modules/semver/ranges/simplify.js +0 -49
  681. package/node_modules/semver/ranges/subset.js +0 -249
  682. package/node_modules/semver/ranges/to-comparators.js +0 -10
  683. package/node_modules/semver/ranges/valid.js +0 -13
  684. package/node_modules/set-blocking/CHANGELOG.md +0 -26
  685. package/node_modules/set-blocking/LICENSE.txt +0 -14
  686. package/node_modules/set-blocking/README.md +0 -31
  687. package/node_modules/set-blocking/index.js +0 -7
  688. package/node_modules/set-blocking/package.json +0 -42
  689. package/node_modules/sharp/LICENSE +0 -191
  690. package/node_modules/sharp/README.md +0 -112
  691. package/node_modules/sharp/dist/channel.cjs +0 -177
  692. package/node_modules/sharp/dist/channel.mjs +0 -177
  693. package/node_modules/sharp/dist/colour.cjs +0 -199
  694. package/node_modules/sharp/dist/colour.mjs +0 -199
  695. package/node_modules/sharp/dist/composite.cjs +0 -213
  696. package/node_modules/sharp/dist/composite.mjs +0 -213
  697. package/node_modules/sharp/dist/constructor.cjs +0 -511
  698. package/node_modules/sharp/dist/constructor.mjs +0 -511
  699. package/node_modules/sharp/dist/index.cjs +0 -25
  700. package/node_modules/sharp/dist/index.d.cts +0 -1999
  701. package/node_modules/sharp/dist/index.d.mts +0 -2046
  702. package/node_modules/sharp/dist/index.mjs +0 -25
  703. package/node_modules/sharp/dist/input.cjs +0 -819
  704. package/node_modules/sharp/dist/input.mjs +0 -819
  705. package/node_modules/sharp/dist/is.cjs +0 -143
  706. package/node_modules/sharp/dist/is.mjs +0 -143
  707. package/node_modules/sharp/dist/libvips.cjs +0 -212
  708. package/node_modules/sharp/dist/libvips.mjs +0 -212
  709. package/node_modules/sharp/dist/operation.cjs +0 -1002
  710. package/node_modules/sharp/dist/operation.mjs +0 -1002
  711. package/node_modules/sharp/dist/output.cjs +0 -1785
  712. package/node_modules/sharp/dist/output.mjs +0 -1785
  713. package/node_modules/sharp/dist/resize.cjs +0 -617
  714. package/node_modules/sharp/dist/resize.mjs +0 -617
  715. package/node_modules/sharp/dist/sharp.cjs +0 -174
  716. package/node_modules/sharp/dist/sharp.mjs +0 -174
  717. package/node_modules/sharp/dist/utility.cjs +0 -301
  718. package/node_modules/sharp/dist/utility.mjs +0 -301
  719. package/node_modules/sharp/install/build.js +0 -38
  720. package/node_modules/sharp/lib/index.d.ts +0 -1999
  721. package/node_modules/sharp/package.json +0 -227
  722. package/node_modules/sharp/src/binding.gyp +0 -303
  723. package/node_modules/sharp/src/common.cc +0 -1186
  724. package/node_modules/sharp/src/common.h +0 -421
  725. package/node_modules/sharp/src/metadata.cc +0 -404
  726. package/node_modules/sharp/src/metadata.h +0 -95
  727. package/node_modules/sharp/src/operations.cc +0 -516
  728. package/node_modules/sharp/src/operations.h +0 -137
  729. package/node_modules/sharp/src/pipeline.cc +0 -1948
  730. package/node_modules/sharp/src/pipeline.h +0 -422
  731. package/node_modules/sharp/src/sharp.cc +0 -43
  732. package/node_modules/sharp/src/stats.cc +0 -186
  733. package/node_modules/sharp/src/stats.h +0 -62
  734. package/node_modules/sharp/src/utilities.cc +0 -289
  735. package/node_modules/sharp/src/utilities.h +0 -22
  736. package/node_modules/string-width/index.d.ts +0 -29
  737. package/node_modules/string-width/index.js +0 -47
  738. package/node_modules/string-width/license +0 -9
  739. package/node_modules/string-width/package.json +0 -56
  740. package/node_modules/string-width/readme.md +0 -50
  741. package/node_modules/strip-ansi/index.d.ts +0 -17
  742. package/node_modules/strip-ansi/index.js +0 -4
  743. package/node_modules/strip-ansi/license +0 -9
  744. package/node_modules/strip-ansi/package.json +0 -54
  745. package/node_modules/strip-ansi/readme.md +0 -46
  746. package/node_modules/which-module/LICENSE +0 -13
  747. package/node_modules/which-module/README.md +0 -58
  748. package/node_modules/which-module/index.js +0 -9
  749. package/node_modules/which-module/package.json +0 -41
  750. package/node_modules/wrap-ansi/index.js +0 -186
  751. package/node_modules/wrap-ansi/license +0 -9
  752. package/node_modules/wrap-ansi/package.json +0 -61
  753. package/node_modules/wrap-ansi/readme.md +0 -97
  754. package/node_modules/ws/LICENSE +0 -20
  755. package/node_modules/ws/README.md +0 -548
  756. package/node_modules/ws/browser.js +0 -8
  757. package/node_modules/ws/index.js +0 -22
  758. package/node_modules/ws/lib/buffer-util.js +0 -131
  759. package/node_modules/ws/lib/constants.js +0 -19
  760. package/node_modules/ws/lib/event-target.js +0 -292
  761. package/node_modules/ws/lib/extension.js +0 -203
  762. package/node_modules/ws/lib/limiter.js +0 -55
  763. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  764. package/node_modules/ws/lib/receiver.js +0 -743
  765. package/node_modules/ws/lib/sender.js +0 -607
  766. package/node_modules/ws/lib/stream.js +0 -161
  767. package/node_modules/ws/lib/subprotocol.js +0 -62
  768. package/node_modules/ws/lib/validation.js +0 -152
  769. package/node_modules/ws/lib/websocket-server.js +0 -562
  770. package/node_modules/ws/lib/websocket.js +0 -1407
  771. package/node_modules/ws/package.json +0 -74
  772. package/node_modules/ws/wrapper.mjs +0 -21
  773. package/node_modules/y18n/CHANGELOG.md +0 -35
  774. package/node_modules/y18n/LICENSE +0 -13
  775. package/node_modules/y18n/README.md +0 -109
  776. package/node_modules/y18n/index.js +0 -188
  777. package/node_modules/y18n/package.json +0 -39
  778. package/node_modules/yargs/CHANGELOG.md +0 -420
  779. package/node_modules/yargs/LICENSE +0 -21
  780. package/node_modules/yargs/README.md +0 -140
  781. package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
  782. package/node_modules/yargs/build/lib/apply-extends.js +0 -65
  783. package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
  784. package/node_modules/yargs/build/lib/argsert.js +0 -65
  785. package/node_modules/yargs/build/lib/command.d.ts +0 -64
  786. package/node_modules/yargs/build/lib/command.js +0 -416
  787. package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
  788. package/node_modules/yargs/build/lib/common-types.js +0 -25
  789. package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
  790. package/node_modules/yargs/build/lib/completion-templates.js +0 -50
  791. package/node_modules/yargs/build/lib/completion.d.ts +0 -21
  792. package/node_modules/yargs/build/lib/completion.js +0 -135
  793. package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
  794. package/node_modules/yargs/build/lib/is-promise.js +0 -9
  795. package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
  796. package/node_modules/yargs/build/lib/levenshtein.js +0 -58
  797. package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
  798. package/node_modules/yargs/build/lib/middleware.js +0 -57
  799. package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
  800. package/node_modules/yargs/build/lib/obj-filter.js +0 -14
  801. package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
  802. package/node_modules/yargs/build/lib/parse-command.js +0 -36
  803. package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
  804. package/node_modules/yargs/build/lib/process-argv.js +0 -31
  805. package/node_modules/yargs/build/lib/usage.d.ts +0 -49
  806. package/node_modules/yargs/build/lib/usage.js +0 -540
  807. package/node_modules/yargs/build/lib/validation.d.ts +0 -34
  808. package/node_modules/yargs/build/lib/validation.js +0 -330
  809. package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
  810. package/node_modules/yargs/build/lib/yargs.js +0 -1190
  811. package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
  812. package/node_modules/yargs/build/lib/yerror.js +0 -11
  813. package/node_modules/yargs/index.js +0 -40
  814. package/node_modules/yargs/locales/be.json +0 -46
  815. package/node_modules/yargs/locales/de.json +0 -46
  816. package/node_modules/yargs/locales/en.json +0 -51
  817. package/node_modules/yargs/locales/es.json +0 -46
  818. package/node_modules/yargs/locales/fi.json +0 -49
  819. package/node_modules/yargs/locales/fr.json +0 -53
  820. package/node_modules/yargs/locales/hi.json +0 -49
  821. package/node_modules/yargs/locales/hu.json +0 -46
  822. package/node_modules/yargs/locales/id.json +0 -50
  823. package/node_modules/yargs/locales/it.json +0 -46
  824. package/node_modules/yargs/locales/ja.json +0 -51
  825. package/node_modules/yargs/locales/ko.json +0 -49
  826. package/node_modules/yargs/locales/nb.json +0 -44
  827. package/node_modules/yargs/locales/nl.json +0 -49
  828. package/node_modules/yargs/locales/nn.json +0 -44
  829. package/node_modules/yargs/locales/pirate.json +0 -13
  830. package/node_modules/yargs/locales/pl.json +0 -49
  831. package/node_modules/yargs/locales/pt.json +0 -45
  832. package/node_modules/yargs/locales/pt_BR.json +0 -48
  833. package/node_modules/yargs/locales/ru.json +0 -46
  834. package/node_modules/yargs/locales/th.json +0 -46
  835. package/node_modules/yargs/locales/tr.json +0 -48
  836. package/node_modules/yargs/locales/zh_CN.json +0 -48
  837. package/node_modules/yargs/locales/zh_TW.json +0 -47
  838. package/node_modules/yargs/package.json +0 -92
  839. package/node_modules/yargs/yargs.js +0 -14
  840. package/node_modules/yargs-parser/CHANGELOG.md +0 -601
  841. package/node_modules/yargs-parser/LICENSE.txt +0 -14
  842. package/node_modules/yargs-parser/README.md +0 -449
  843. package/node_modules/yargs-parser/index.js +0 -1032
  844. package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
  845. 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();
@@ -199,21 +576,23 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
199
576
  }
200
577
  catch { return fail(1008, 'Invalid LAN candidates'); }
201
578
  room = durable ? rooms.get(message.deviceId) : undefined;
202
- if (room && (room.status !== 'active' || !matches(message.deviceSecret, room.hostHash))) return fail();
203
579
  if (!room) {
204
- // Durable computers prove future ownership with their generated device secret.
205
- // The legacy deployment key remains accepted, but is no longer required for first registration.
206
580
  if (!durable && !matches(message.key, hostHash)) return fail();
207
- if (rooms.size >= 256) return fail(4429, 'Device limit reached');
208
- const now = new Date().toISOString();
209
- room = { id: durable ? message.deviceId : randomUUID(), durable,
210
- revision: 0, status: 'active', clients: [], connections: new Map(), observers: new Map(), createdAt: now, updatedAt: now,
211
- ...(durable ? { hostHash: digest(message.deviceSecret) } : {}) };
212
- commit(room, {}); rooms.set(room.id, room);
213
- }
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();
214
585
  const oldHost = room.host;
215
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
+ }
216
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');
217
596
  room.p2pProbe = message.p2pProbe === 1; room.p2pProbeRepeat = message.p2pProbeRepeat === true;
218
597
  role = 'host'; clearTimeout(authTimer);
219
598
  if (oldHost && oldHost !== ws) {
@@ -232,32 +611,50 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
232
611
  room = rooms.get(message.room);
233
612
  if (!room || room.status !== 'active') return fail();
234
613
  let credential;
235
- 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)))) {
236
618
  if (!room.host) return fail(4410, 'Computer offline; authorization retained');
237
619
  credential = message.credential || secret();
238
620
  clientId = message.clientId || (!room.durable ? randomUUID() : undefined);
239
621
  if (!validSecret(credential) || !validId(clientId)) return fail();
240
622
  const existing = room.clients.find(client => client.id === clientId);
241
- if (!existing && room.clients.length >= maxClientsPerHost) return fail(4429, 'Mobile device limit reached');
242
623
  const now = new Date().toISOString();
243
- const client = { id: clientId, hash: digest(credential), createdAt: existing?.createdAt || now, updatedAt: now };
244
- commit(room, { clients: [...room.clients.filter(item => item.id !== clientId), client], revision: room.revision + 1 });
245
- 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');
246
632
  send(room.host, { type: 'authorization-changed', clients: room.clients, revision: room.revision });
247
633
  } else {
248
634
  clientId = message.clientId;
249
635
  const client = room.clients.find(item => item.id === clientId) ||
250
636
  (!room.durable && !clientId ? room.clients.find(item => matches(message.credential, item.hash)) : undefined);
251
637
  if (client) clientId = client.id;
252
- 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)) {
253
639
  return fail(message.type === 'pair' ? 4408 : 4401, message.type === 'pair' ? 'Pairing code expired or used' : 'Device authorization rejected');
254
640
  }
255
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
+ }
256
653
  if (message.type === 'observe') {
257
654
  role = 'observer'; clearTimeout(authTimer); room.observers ||= new Map();
258
655
  const oldObserver = room.observers.get(clientId);
259
656
  if (oldObserver?.ws && oldObserver.ws !== ws) oldObserver.ws.close(4409, 'Metadata connection replaced by same identity');
260
- room.observers.set(clientId, { ws });
657
+ room.observers.set(clientId, { ws, userId });
261
658
  send(ws, { type: 'observing', online: Boolean(room.host) });
262
659
  send(ws, { type: 'debug-permissions', enabled: debugAllowed(debugAllowlist, room.id, room.clients.find(client => client.id === clientId)) });
263
660
  const announcement = adminState.read().announcement;
@@ -275,7 +672,8 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
275
672
  send(room.host, { type: 'detach', session: oldConnection.session });
276
673
  oldConnection.ws.close(4409, 'Phone connection replaced by same identity');
277
674
  }
278
- 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,
279
677
  repeat: room.p2pProbeRepeat, signals: 0, probeStarted: 0, outbound: Promise.resolve(), queuedBytes: 0,
280
678
  relayAdmitted: false, relayExempt: false,
281
679
  capacityExempt: debugAllowed(debugAllowlist, room.id, room.clients.find(client => client.id === clientId)) });
@@ -285,9 +683,99 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
285
683
  const announcement = adminState.read().announcement;
286
684
  if (announcement) send(ws, { type: 'announcement', announcement });
287
685
  send(room.host, { type: 'attach', session, clientId, p2pProbe: room.connections.get(clientId).p2pProbe ? 1 : 0 }); return;
288
- }
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
+ }
289
692
  if (role === 'host') {
290
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
+ }
291
779
  if (message.type === 'lan-candidates') {
292
780
  room.lanCandidates = normalizeLanCandidates(message.candidates);
293
781
  for (const connection of room.connections.values()) send(connection.ws, { type: 'lan-candidates', candidates: room.lanCandidates });
@@ -300,8 +788,23 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
300
788
  if (message.type === 'pair-code') {
301
789
  send(ws, { type: 'pair-code', commandId: message.commandId, room: room.id, ...invitation(room) }); return;
302
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
+ }
303
806
  if (message.type === 'revoke') {
304
- commit(room, { clients: [], revision: room.revision + 1 });
807
+ await commit(room, current => ({ clients: [], revision: current.revision + 1 }));
305
808
  room.pairingHash = undefined;
306
809
  for (const connection of room.connections.values()) {
307
810
  releaseRelay(connection);
@@ -309,8 +812,12 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
309
812
  connection.ws.close(4401, 'Device authorization revoked');
310
813
  }
311
814
  room.connections.clear();
815
+ for (const client of room.uploadClients?.values() || []) client.ws.close(4401, 'Device authorization revoked');
816
+ room.uploadClients?.clear();
312
817
  for (const observer of room.observers.values()) observer.ws.close(4401, 'Device authorization revoked');
313
818
  room.observers.clear();
819
+ for (const socket of room.deviceManagers?.keys() || []) socket.close(4401, 'Device authorization revoked');
820
+ room.deviceManagers?.clear();
314
821
  send(ws, { type: 'authorization-changed', clients: [], revision: room.revision });
315
822
  send(ws, { type: 'revoked', commandId: message.commandId, room: room.id }); return;
316
823
  }
@@ -320,49 +827,76 @@ export async function startSelfRelay({ port = 0, hostKey = secret(), pairingTtlM
320
827
  else if (message.type === 'ready') queueClientMessage(connection, { type: 'ready', p2pProbe: connection.p2pProbe ? 1 : 0,
321
828
  p2pProbeRepeat: connection.p2pProbe && connection.repeat });
322
829
  else if (message.type === 'p2p-probe' && connection.p2pProbe && validProbeSignal(message)) {
323
- if (connection.probeId !== message.probeId) return;
324
- 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');
325
835
  send(connection.ws, { ...message, session: undefined });
326
836
  }
327
837
  else if (message.type === 'detach') connection.ws.close(1011, 'Local Bridge unavailable');
328
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 });
329
851
  } else if (role === 'observer' && room.observers.get(clientId)?.ws === ws && message.type === 'official-computer') {
330
852
  const computer = normalizeOfficialComputer(message);
331
- commit(room, { officialComputer: computer });
853
+ await commit(room, { officialComputer: computer });
332
854
  send(ws, { type: 'official-computer-saved', computer });
333
855
  } else if (role === 'client' && room.connections.get(clientId)?.ws === ws && message.type === 'p2p-probe') {
334
856
  const connection = room.connections.get(clientId);
335
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';
336
864
  if (message.kind === 'start') {
337
- if (connection.probeId && (!connection.repeat || Date.now() - connection.probeStarted < 2000)) {
338
- 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;
339
868
  }
340
- connection.probeId = message.probeId; connection.signals = 0; connection.probeStarted = Date.now();
869
+ connection[idField] = message.probeId; connection[signalsField] = 0; connection[startedField] = Date.now();
341
870
  }
342
- if (connection.probeId !== message.probeId) return;
343
- 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');
344
873
  send(room.host, { ...message, session });
874
+ if (diagnostic && message.kind === 'close') connection.diagnosticProbeId = undefined;
345
875
  } else if (room.connections.get(clientId)?.ws === ws && message.type === 'p2p-route' && message.direct === 1) {
346
876
  releaseRelay(room.connections.get(clientId));
347
877
  } else if (room.connections.get(clientId)?.ws === ws && message.type === 'data' && typeof message.payload === 'string') {
348
878
  const connection = room.connections.get(clientId);
349
- 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)) {
350
881
  ws.close(4428, 'Server busy'); return;
351
882
  }
352
883
  send(room.host, { type: 'data', session, payload: message.payload });
353
- } else fail();
354
- } catch (error) { log({ event: 'request-error', role, message: 'Relay operation failed' }); ws.close(1011, 'Relay operation failed'); }
355
- });
356
- });
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
+ });
357
890
  await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port, '127.0.0.1', resolve); });
358
891
  listenUrl = `ws://127.0.0.1:${server.address().port}/relay`;
359
892
  return {
360
893
  url: listenUrl, hostKey,
361
- async close() {
362
- for (const ws of wss.clients) ws.terminate();
363
- await new Promise(resolve => wss.close(resolve));
364
- await new Promise(resolve => server.close(resolve));
365
- },
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
+ },
366
900
  };
367
901
  }
368
902
 
@@ -436,6 +970,20 @@ function plainText(value, limit, empty) {
436
970
  return typeof value === 'string' && (empty || value.trim().length > 0) && value.trim().length <= limit && !/[\u0000-\u001f\u007f]/.test(value);
437
971
  }
438
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
+
439
987
  function controlPayload(value) {
440
988
  try {
441
989
  const message = JSON.parse(value);
@@ -456,13 +1004,3 @@ function readBody(request, limit) {
456
1004
  request.on('end', () => resolve(value)); request.on('error', reject);
457
1005
  });
458
1006
  }
459
-
460
- function adminPage() {
461
- 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>
462
- 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>
463
- 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};
464
- 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}}
465
- 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);
466
- 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();
467
- </script></main></html>`;
468
- }