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

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