@walkhi/code-relax 0.1.0-beta.7 → 0.1.0-beta.76

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 (855) hide show
  1. package/README.md +67 -45
  2. package/dist/bin/cleanup-accounts.mjs +63 -0
  3. package/dist/bin/codex-remote.mjs +364 -236
  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 +190 -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/bin/set-relay-admin-password.mjs +11 -0
  13. package/dist/build-platform.json +1 -0
  14. package/dist/get-shared-service-url.ps1 +12 -4
  15. package/dist/shared/app-server-events.cjs +457 -238
  16. package/dist/shared/code-browser.cjs +159 -0
  17. package/dist/shared/file-changes.cjs +31 -0
  18. package/dist/shared/file-citations.cjs +117 -0
  19. package/dist/shared/file-mentions.cjs +92 -0
  20. package/dist/shared/message-delivery.cjs +115 -0
  21. package/dist/shared/p2p-data-channel.cjs +22 -0
  22. package/dist/shared/p2p-diagnostics.cjs +1 -1
  23. package/dist/shared/p2p-file-channel.cjs +87 -0
  24. package/dist/shared/p2p-ice-policy.cjs +22 -0
  25. package/dist/shared/plan-pricing.cjs +32 -0
  26. package/dist/shared/question-attention.cjs +69 -0
  27. package/dist/shared/release-version.cjs +31 -0
  28. package/dist/shared/server-requests.cjs +311 -67
  29. package/dist/shared/service-endpoints.cjs +21 -0
  30. package/dist/shared/timeline-sync.cjs +173 -67
  31. package/dist/skills/code-relax/SKILL.md +22 -17
  32. package/dist/src/archived-threads.mjs +22 -0
  33. package/dist/src/bounded-process-log.mjs +63 -0
  34. package/dist/src/celsius-client.mjs +107 -0
  35. package/dist/src/code-browser.mjs +100 -0
  36. package/dist/src/desktop-launcher.mjs +62 -42
  37. package/dist/src/dots-approval-timeline.mjs +99 -0
  38. package/dist/src/dots-avatar.mjs +42 -0
  39. package/dist/src/dots-chat-projection.mjs +50 -0
  40. package/dist/src/dots-chat-server.mjs +531 -0
  41. package/dist/src/dots-message-images.mjs +319 -0
  42. package/dist/src/dots-profile.mjs +77 -0
  43. package/dist/src/dots-room-activity.mjs +199 -0
  44. package/dist/src/dots-room-client.mjs +157 -0
  45. package/dist/src/dots-room-projection.mjs +94 -0
  46. package/dist/src/dots-schedules.mjs +66 -0
  47. package/dist/src/dots-summary-tasks.mjs +32 -0
  48. package/dist/src/dots-task-catalog.mjs +433 -0
  49. package/dist/src/dots-task-files.mjs +52 -0
  50. package/dist/src/file-transfer-range.mjs +22 -0
  51. package/dist/src/file-upload-session.mjs +146 -0
  52. package/dist/src/hosted-history-app-server.mjs +103 -0
  53. package/dist/src/hosted-history-cache.mjs +113 -0
  54. package/dist/src/lan-socket-server.mjs +268 -37
  55. package/dist/src/managed-app-server.mjs +398 -213
  56. package/dist/src/managed-endpoint.mjs +25 -0
  57. package/dist/src/message-images.mjs +0 -18
  58. package/dist/src/message-upload-files.mjs +109 -0
  59. package/dist/src/onboarding.mjs +9 -28
  60. package/dist/src/plan-pricing.mjs +47 -0
  61. package/dist/src/platform/README.md +7 -12
  62. package/dist/src/platform/codex-protocol.mjs +51 -0
  63. package/dist/src/platform/codex-runtime.mjs +4 -0
  64. package/dist/src/platform/credential-store.mjs +19 -0
  65. package/dist/src/platform/desktop-host.mjs +3 -0
  66. package/dist/src/platform/desktop-permission-mode.mjs +17 -0
  67. package/dist/src/platform/desktop-project-registration.mjs +3 -0
  68. package/dist/src/platform/desktop-project-store.mjs +219 -0
  69. package/dist/src/platform/file-opener.mjs +9 -0
  70. package/dist/src/platform/linux/codex-runtime.mjs +56 -0
  71. package/dist/src/platform/linux/desktop-host.mjs +87 -0
  72. package/dist/src/platform/linux/desktop-installation.mjs +36 -0
  73. package/dist/src/platform/linux/desktop-project-registration.mjs +11 -0
  74. package/dist/src/platform/linux/network-info.mjs +11 -0
  75. package/dist/src/platform/linux/process.mjs +53 -0
  76. package/dist/src/platform/linux/project-locations.mjs +17 -0
  77. package/dist/src/platform/linux/resident-startup.mjs +52 -0
  78. package/dist/src/platform/linux/retained-runtime.mjs +76 -0
  79. package/dist/src/platform/linux/service-host.mjs +18 -0
  80. package/dist/src/platform/linux/system.mjs +50 -0
  81. package/dist/src/platform/linux/user-environment.mjs +9 -0
  82. package/dist/src/platform/macos/codex-runtime.mjs +121 -0
  83. package/dist/src/platform/macos/desktop-host.mjs +86 -0
  84. package/dist/src/platform/macos/desktop-project-registration.mjs +11 -0
  85. package/dist/src/platform/macos/network-info.mjs +8 -0
  86. package/dist/src/platform/macos/project-locations.mjs +8 -0
  87. package/dist/src/platform/macos/resident-startup.mjs +84 -0
  88. package/dist/src/platform/macos/retained-runtime.mjs +127 -0
  89. package/dist/src/platform/macos/service-host.mjs +18 -0
  90. package/dist/src/platform/macos/system.mjs +29 -0
  91. package/dist/src/platform/macos/user-environment.mjs +9 -0
  92. package/dist/src/platform/native-package.mjs +58 -0
  93. package/dist/src/platform/network-info.mjs +3 -0
  94. package/dist/src/platform/path-identity.mjs +5 -0
  95. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  96. package/dist/src/platform/posix/runtime-paths.mjs +25 -0
  97. package/dist/src/platform/power-inhibitor.mjs +22 -0
  98. package/dist/src/platform/project-locations.mjs +3 -0
  99. package/dist/src/platform/runtime-paths.mjs +8 -0
  100. package/dist/src/platform/select.mjs +5 -0
  101. package/dist/src/platform/service-host.mjs +3 -0
  102. package/dist/src/platform/user-environment.mjs +3 -0
  103. package/dist/src/platform/windows/activate-desktop.ps1 +49 -0
  104. package/dist/src/platform/windows/codex-runtime.mjs +225 -0
  105. package/dist/src/platform/windows/desktop-helper.mjs +179 -0
  106. package/dist/src/platform/windows/desktop-host.mjs +45 -18
  107. package/dist/src/platform/windows/desktop-info.ps1 +27 -5
  108. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -0
  109. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
  110. package/dist/src/platform/windows/desktop-shortcuts.ps1 +7 -14
  111. package/dist/src/platform/windows/dots-profile.mjs +49 -0
  112. package/dist/src/platform/windows/keep-awake.ps1 +20 -0
  113. package/dist/src/platform/windows/launch-worker.mjs +41 -42
  114. package/dist/src/platform/windows/project-locations.mjs +16 -0
  115. package/dist/src/platform/windows/resident-start.js +20 -0
  116. package/dist/src/platform/windows/resident-startup.ps1 +76 -20
  117. package/dist/src/platform/windows/rustdesk-video-probe.mjs +168 -0
  118. package/dist/src/platform/windows/service-host.mjs +15 -23
  119. package/dist/src/platform/windows/start-hidden-console.ps1 +11 -2
  120. package/dist/src/platform/windows/stop-desktop.ps1 +7 -4
  121. package/dist/src/platform/windows/user-environment.mjs +52 -50
  122. package/dist/src/project-create.mjs +121 -0
  123. package/dist/src/project-management.mjs +59 -0
  124. package/dist/src/project-remove.mjs +55 -0
  125. package/dist/src/project-update.mjs +59 -0
  126. package/dist/src/relay-config-import.mjs +45 -0
  127. package/dist/src/runtime-discovery.mjs +67 -0
  128. package/dist/src/self-relay/account-cleanup.mjs +30 -0
  129. package/dist/src/self-relay/account-connections.mjs +52 -0
  130. package/dist/src/self-relay/admin-auth.mjs +59 -0
  131. package/dist/src/self-relay/admin-cloud.mjs +101 -0
  132. package/dist/src/self-relay/admin-login-page.mjs +7 -0
  133. package/dist/src/self-relay/admin-page.mjs +87 -0
  134. package/dist/src/self-relay/admin-server.mjs +105 -0
  135. package/dist/src/self-relay/admin-state.mjs +85 -19
  136. package/dist/src/self-relay/admin-style.mjs +58 -0
  137. package/dist/src/self-relay/admin-workstations-page.mjs +95 -0
  138. package/dist/src/self-relay/admin-workstations.mjs +143 -0
  139. package/dist/src/self-relay/android-releases.mjs +61 -0
  140. package/dist/src/self-relay/client.mjs +45 -45
  141. package/dist/src/self-relay/connection-reporting.mjs +38 -0
  142. package/dist/src/self-relay/connector.mjs +456 -145
  143. package/dist/src/self-relay/demo.mjs +3 -4
  144. package/dist/src/self-relay/device-info.mjs +27 -0
  145. package/dist/src/self-relay/device-store.mjs +10 -2
  146. package/dist/src/self-relay/diagnostics.mjs +54 -0
  147. package/dist/src/self-relay/email-accounts.mjs +183 -0
  148. package/dist/src/self-relay/file-wire.mjs +76 -0
  149. package/dist/src/self-relay/lan-authorizations.mjs +44 -2
  150. package/dist/src/self-relay/lifecycle.mjs +255 -262
  151. package/dist/src/self-relay/long-pairing.mjs +40 -0
  152. package/dist/src/self-relay/mysql-state.mjs +118 -0
  153. package/dist/src/self-relay/p2p-probe.mjs +168 -123
  154. package/dist/src/self-relay/phone-connections.mjs +55 -0
  155. package/dist/src/self-relay/privacy-page.mjs +26 -0
  156. package/dist/src/self-relay/public-releases.mjs +67 -0
  157. package/dist/src/self-relay/rtc-file-bridge.mjs +46 -0
  158. package/dist/src/self-relay/server.mjs +1064 -428
  159. package/dist/src/self-relay/usage-daily.mjs +55 -0
  160. package/dist/src/self-relay/usage-meter.mjs +141 -0
  161. package/dist/src/server.mjs +1344 -1534
  162. package/dist/src/service-control.mjs +17 -7
  163. package/dist/src/service-doctor.mjs +150 -65
  164. package/dist/src/service-health.mjs +2 -2
  165. package/dist/src/service-install.mjs +16 -10
  166. package/dist/src/service-lifecycle.mjs +51 -46
  167. package/dist/src/service-power.mjs +58 -0
  168. package/dist/src/service-restart.mjs +45 -12
  169. package/dist/src/service-update-worker.mjs +42 -0
  170. package/dist/src/service-update.mjs +80 -0
  171. package/dist/src/shared-app-server.mjs +489 -305
  172. package/dist/src/shared-catalog.mjs +132 -50
  173. package/dist/src/shared-recovery.mjs +24 -14
  174. package/dist/src/shared-thread-stream.mjs +136 -51
  175. package/dist/src/skill-install.mjs +0 -13
  176. package/dist/src/turn-model-history.mjs +47 -0
  177. package/dist/src/usage-reset.mjs +17 -0
  178. package/dist/src/workstation-downloads.mjs +120 -0
  179. package/package.json +35 -37
  180. package/tools/postinstall.mjs +186 -143
  181. package/dist/src/app-server-client.mjs +0 -468
  182. package/dist/src/app-server-tasks.mjs +0 -358
  183. package/dist/src/debug-ui.mjs +0 -131
  184. package/dist/src/platform/windows/credential-store.mjs +0 -27
  185. package/dist/src/platform/windows/credential-store.ps1 +0 -49
  186. package/dist/src/platform/windows/desktop-monitor.mjs +0 -34
  187. package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
  188. package/dist/src/platform/windows/desktop-tools.mjs +0 -48
  189. package/dist/src/thread-catalog.mjs +0 -47
  190. package/dist/tools/find-desktop-pipe.mjs +0 -10
  191. package/dist/web/activity-view.js +0 -283
  192. package/dist/web/approval-ui.js +0 -188
  193. package/dist/web/capabilities.js +0 -57
  194. package/dist/web/chat-transport.js +0 -202
  195. package/dist/web/chat.css +0 -782
  196. package/dist/web/chat.js +0 -3830
  197. package/dist/web/client-platform.js +0 -11
  198. package/dist/web/community-view.js +0 -20
  199. package/dist/web/community.css +0 -77
  200. package/dist/web/community.html +0 -37
  201. package/dist/web/composer-controller.js +0 -101
  202. package/dist/web/conversation-controller.js +0 -99
  203. package/dist/web/disclosure-state-controller.js +0 -95
  204. package/dist/web/draft-controller.js +0 -103
  205. package/dist/web/harmony-platform.js +0 -52
  206. package/dist/web/highlight-powershell.min.js +0 -39
  207. package/dist/web/highlight.min.js +0 -1262
  208. package/dist/web/history-cache.js +0 -240
  209. package/dist/web/history-controller.js +0 -167
  210. package/dist/web/index.html +0 -325
  211. package/dist/web/lan-session.js +0 -122
  212. package/dist/web/link-action-controller.js +0 -212
  213. package/dist/web/message-send-controller.js +0 -177
  214. package/dist/web/message-view.js +0 -98
  215. package/dist/web/p2p-data-channel.js +0 -50
  216. package/dist/web/p2p-diagnostics.js +0 -28
  217. package/dist/web/p2p-probe.js +0 -123
  218. package/dist/web/page-resume.js +0 -28
  219. package/dist/web/pending-message-store.js +0 -108
  220. package/dist/web/queue-controller.js +0 -82
  221. package/dist/web/resources.json +0 -1
  222. package/dist/web/self-relay-session.js +0 -129
  223. package/dist/web/server-requests.js +0 -67
  224. package/dist/web/station-connection-controller.js +0 -75
  225. package/dist/web/task-list-view.js +0 -296
  226. package/dist/web/thread-attention-controller.js +0 -124
  227. package/dist/web/thread-context-controller.js +0 -30
  228. package/dist/web/thread-list-controller.js +0 -61
  229. package/dist/web/thread-list-sync.js +0 -86
  230. package/dist/web/thread-title-controller.js +0 -57
  231. package/dist/web/timeline-formatters.js +0 -249
  232. package/dist/web/timeline-reducer.js +0 -81
  233. package/dist/web/timeline-renderer.js +0 -161
  234. package/dist/web/timeline-scroll-controller.js +0 -50
  235. package/dist/web/timeline-sync.js +0 -67
  236. package/dist/web/usage-controller.js +0 -258
  237. package/dist/web/vendor/README.md +0 -14
  238. package/dist/web/vendor/highlight-powershell.min.js +0 -39
  239. package/dist/web/vendor/highlight.LICENSE.txt +0 -29
  240. package/dist/web/vendor/highlight.min.js +0 -1262
  241. package/dist/web/vendor/lucide.LICENSE.txt +0 -17
  242. package/node_modules/@img/colour/LICENSE.md +0 -82
  243. package/node_modules/@img/colour/README.md +0 -15
  244. package/node_modules/@img/colour/color.cjs +0 -1596
  245. package/node_modules/@img/colour/index.cjs +0 -1
  246. package/node_modules/@img/colour/index.d.ts +0 -929
  247. package/node_modules/@img/colour/package.json +0 -58
  248. package/node_modules/@img/sharp-win32-x64/LICENSE +0 -191
  249. package/node_modules/@img/sharp-win32-x64/README.md +0 -59
  250. package/node_modules/@img/sharp-win32-x64/index.cjs +0 -1
  251. package/node_modules/@img/sharp-win32-x64/lib/libvips-42.dll +0 -0
  252. package/node_modules/@img/sharp-win32-x64/lib/libvips-cpp-8.18.6.dll +0 -0
  253. package/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64-0.35.4.node +0 -0
  254. package/node_modules/@img/sharp-win32-x64/package.json +0 -40
  255. package/node_modules/@img/sharp-win32-x64/versions.json +0 -30
  256. package/node_modules/@node-datachannel/win32-x64-msvc/node_datachannel.node +0 -0
  257. package/node_modules/@node-datachannel/win32-x64-msvc/package.json +0 -20
  258. package/node_modules/ansi-regex/index.d.ts +0 -37
  259. package/node_modules/ansi-regex/index.js +0 -10
  260. package/node_modules/ansi-regex/license +0 -9
  261. package/node_modules/ansi-regex/package.json +0 -55
  262. package/node_modules/ansi-regex/readme.md +0 -78
  263. package/node_modules/ansi-styles/index.d.ts +0 -345
  264. package/node_modules/ansi-styles/index.js +0 -163
  265. package/node_modules/ansi-styles/license +0 -9
  266. package/node_modules/ansi-styles/package.json +0 -56
  267. package/node_modules/ansi-styles/readme.md +0 -152
  268. package/node_modules/camelcase/index.d.ts +0 -63
  269. package/node_modules/camelcase/index.js +0 -76
  270. package/node_modules/camelcase/license +0 -9
  271. package/node_modules/camelcase/package.json +0 -43
  272. package/node_modules/camelcase/readme.md +0 -99
  273. package/node_modules/cliui/CHANGELOG.md +0 -76
  274. package/node_modules/cliui/LICENSE.txt +0 -14
  275. package/node_modules/cliui/README.md +0 -115
  276. package/node_modules/cliui/index.js +0 -354
  277. package/node_modules/cliui/package.json +0 -65
  278. package/node_modules/color-convert/CHANGELOG.md +0 -54
  279. package/node_modules/color-convert/LICENSE +0 -21
  280. package/node_modules/color-convert/README.md +0 -68
  281. package/node_modules/color-convert/conversions.js +0 -839
  282. package/node_modules/color-convert/index.js +0 -81
  283. package/node_modules/color-convert/package.json +0 -48
  284. package/node_modules/color-convert/route.js +0 -97
  285. package/node_modules/color-name/LICENSE +0 -8
  286. package/node_modules/color-name/README.md +0 -11
  287. package/node_modules/color-name/index.js +0 -152
  288. package/node_modules/color-name/package.json +0 -28
  289. package/node_modules/decamelize/index.js +0 -13
  290. package/node_modules/decamelize/license +0 -21
  291. package/node_modules/decamelize/package.json +0 -38
  292. package/node_modules/decamelize/readme.md +0 -48
  293. package/node_modules/detect-libc/LICENSE +0 -201
  294. package/node_modules/detect-libc/README.md +0 -163
  295. package/node_modules/detect-libc/index.d.ts +0 -14
  296. package/node_modules/detect-libc/lib/detect-libc.js +0 -313
  297. package/node_modules/detect-libc/lib/elf.js +0 -39
  298. package/node_modules/detect-libc/lib/filesystem.js +0 -51
  299. package/node_modules/detect-libc/lib/process.js +0 -24
  300. package/node_modules/detect-libc/package.json +0 -44
  301. package/node_modules/dijkstrajs/.travis.yml +0 -4
  302. package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
  303. package/node_modules/dijkstrajs/LICENSE.md +0 -19
  304. package/node_modules/dijkstrajs/README.md +0 -18
  305. package/node_modules/dijkstrajs/dijkstra.js +0 -165
  306. package/node_modules/dijkstrajs/package.json +0 -60
  307. package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
  308. package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  309. package/node_modules/emoji-regex/README.md +0 -73
  310. package/node_modules/emoji-regex/es2015/index.js +0 -6
  311. package/node_modules/emoji-regex/es2015/text.js +0 -6
  312. package/node_modules/emoji-regex/index.d.ts +0 -23
  313. package/node_modules/emoji-regex/index.js +0 -6
  314. package/node_modules/emoji-regex/package.json +0 -50
  315. package/node_modules/emoji-regex/text.js +0 -6
  316. package/node_modules/find-up/index.d.ts +0 -137
  317. package/node_modules/find-up/index.js +0 -89
  318. package/node_modules/find-up/license +0 -9
  319. package/node_modules/find-up/package.json +0 -53
  320. package/node_modules/find-up/readme.md +0 -156
  321. package/node_modules/get-caller-file/LICENSE.md +0 -6
  322. package/node_modules/get-caller-file/README.md +0 -41
  323. package/node_modules/get-caller-file/index.d.ts +0 -2
  324. package/node_modules/get-caller-file/index.js +0 -22
  325. package/node_modules/get-caller-file/index.js.map +0 -1
  326. package/node_modules/get-caller-file/package.json +0 -42
  327. package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
  328. package/node_modules/is-fullwidth-code-point/index.js +0 -50
  329. package/node_modules/is-fullwidth-code-point/license +0 -9
  330. package/node_modules/is-fullwidth-code-point/package.json +0 -42
  331. package/node_modules/is-fullwidth-code-point/readme.md +0 -39
  332. package/node_modules/locate-path/index.d.ts +0 -83
  333. package/node_modules/locate-path/index.js +0 -65
  334. package/node_modules/locate-path/license +0 -9
  335. package/node_modules/locate-path/package.json +0 -45
  336. package/node_modules/locate-path/readme.md +0 -122
  337. package/node_modules/node-datachannel/.clang-format +0 -17
  338. package/node_modules/node-datachannel/.editorconfig +0 -12
  339. package/node_modules/node-datachannel/.eslintignore +0 -8
  340. package/node_modules/node-datachannel/.eslintrc.json +0 -27
  341. package/node_modules/node-datachannel/.gitmodules +0 -3
  342. package/node_modules/node-datachannel/.prettierignore +0 -7
  343. package/node_modules/node-datachannel/.prettierrc +0 -13
  344. package/node_modules/node-datachannel/API.md +0 -247
  345. package/node_modules/node-datachannel/BULDING.md +0 -26
  346. package/node_modules/node-datachannel/CMakeLists.txt +0 -153
  347. package/node_modules/node-datachannel/LICENSE +0 -373
  348. package/node_modules/node-datachannel/README.md +0 -129
  349. package/node_modules/node-datachannel/dist/cjs/index.cjs +0 -11
  350. package/node_modules/node-datachannel/dist/cjs/index.cjs.map +0 -1
  351. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs +0 -101
  352. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs.map +0 -1
  353. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs +0 -91
  354. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs.map +0 -1
  355. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs +0 -94
  356. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs.map +0 -1
  357. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs +0 -45
  358. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs.map +0 -1
  359. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs +0 -8
  360. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs.map +0 -1
  361. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs +0 -86
  362. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs.map +0 -1
  363. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs +0 -34
  364. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs.map +0 -1
  365. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs +0 -34
  366. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs.map +0 -1
  367. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs +0 -214
  368. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs.map +0 -1
  369. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs +0 -53
  370. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +0 -1
  371. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs +0 -83
  372. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs.map +0 -1
  373. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs +0 -132
  374. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs.map +0 -1
  375. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs +0 -94
  376. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs.map +0 -1
  377. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs +0 -434
  378. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs.map +0 -1
  379. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs +0 -59
  380. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs.map +0 -1
  381. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs +0 -45
  382. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs.map +0 -1
  383. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs +0 -42
  384. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs.map +0 -1
  385. package/node_modules/node-datachannel/dist/esm/index.mjs +0 -8
  386. package/node_modules/node-datachannel/dist/esm/index.mjs.map +0 -1
  387. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs +0 -78
  388. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs.map +0 -1
  389. package/node_modules/node-datachannel/dist/esm/lib/index.mjs +0 -64
  390. package/node_modules/node-datachannel/dist/esm/lib/index.mjs.map +0 -1
  391. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs +0 -78
  392. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs.map +0 -1
  393. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs +0 -43
  394. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs.map +0 -1
  395. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs +0 -6
  396. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs.map +0 -1
  397. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs +0 -82
  398. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs.map +0 -1
  399. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs +0 -28
  400. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs.map +0 -1
  401. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs +0 -30
  402. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs.map +0 -1
  403. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs +0 -210
  404. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs.map +0 -1
  405. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs +0 -49
  406. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs.map +0 -1
  407. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs +0 -79
  408. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs.map +0 -1
  409. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs +0 -128
  410. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs.map +0 -1
  411. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs +0 -89
  412. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs.map +0 -1
  413. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs +0 -430
  414. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs.map +0 -1
  415. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs +0 -55
  416. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs.map +0 -1
  417. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs +0 -41
  418. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs.map +0 -1
  419. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs +0 -27
  420. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs.map +0 -1
  421. package/node_modules/node-datachannel/dist/types/lib/datachannel-stream.d.ts +0 -24
  422. package/node_modules/node-datachannel/dist/types/lib/index.d.ts +0 -241
  423. package/node_modules/node-datachannel/dist/types/lib/types.d.ts +0 -119
  424. package/node_modules/node-datachannel/dist/types/lib/websocket-server.d.ts +0 -13
  425. package/node_modules/node-datachannel/dist/types/lib/websocket.d.ts +0 -25
  426. package/node_modules/node-datachannel/dist/types/polyfill/Events.d.ts +0 -15
  427. package/node_modules/node-datachannel/dist/types/polyfill/RTCCertificate.d.ts +0 -9
  428. package/node_modules/node-datachannel/dist/types/polyfill/RTCDataChannel.d.ts +0 -29
  429. package/node_modules/node-datachannel/dist/types/polyfill/RTCDtlsTransport.d.ts +0 -15
  430. package/node_modules/node-datachannel/dist/types/polyfill/RTCError.d.ts +0 -17
  431. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceCandidate.d.ts +0 -21
  432. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceTransport.d.ts +0 -30
  433. package/node_modules/node-datachannel/dist/types/polyfill/RTCPeerConnection.d.ts +0 -64
  434. package/node_modules/node-datachannel/dist/types/polyfill/RTCSctpTransport.d.ts +0 -15
  435. package/node_modules/node-datachannel/dist/types/polyfill/RTCSessionDescription.d.ts +0 -10
  436. package/node_modules/node-datachannel/dist/types/polyfill/index.d.ts +0 -26
  437. package/node_modules/node-datachannel/jest.config.ts +0 -14
  438. package/node_modules/node-datachannel/package.json +0 -133
  439. package/node_modules/node-datachannel/rollup.config.mjs +0 -67
  440. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.cpp +0 -530
  441. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.h +0 -63
  442. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.cpp +0 -172
  443. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.h +0 -43
  444. package/node_modules/node-datachannel/src/cpp/main.cpp +0 -58
  445. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.cpp +0 -457
  446. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.h +0 -52
  447. package/node_modules/node-datachannel/src/cpp/media-av1packetization.cpp +0 -24
  448. package/node_modules/node-datachannel/src/cpp/media-av1packetization.h +0 -11
  449. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.cpp +0 -126
  450. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.h +0 -29
  451. package/node_modules/node-datachannel/src/cpp/media-direction.cpp +0 -43
  452. package/node_modules/node-datachannel/src/cpp/media-direction.h +0 -10
  453. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.cpp +0 -126
  454. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.h +0 -30
  455. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.cpp +0 -126
  456. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.h +0 -30
  457. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.cpp +0 -32
  458. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.h +0 -11
  459. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.cpp +0 -31
  460. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.h +0 -11
  461. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.cpp +0 -79
  462. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.h +0 -28
  463. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.cpp +0 -68
  464. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.h +0 -28
  465. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.cpp +0 -57
  466. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.h +0 -28
  467. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.cpp +0 -93
  468. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.h +0 -30
  469. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.cpp +0 -167
  470. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.h +0 -35
  471. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.cpp +0 -95
  472. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.h +0 -30
  473. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.cpp +0 -458
  474. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.h +0 -61
  475. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.cpp +0 -526
  476. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.h +0 -56
  477. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.cpp +0 -1298
  478. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.h +0 -89
  479. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.cpp +0 -205
  480. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.h +0 -24
  481. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.cpp +0 -57
  482. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.h +0 -47
  483. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.cpp +0 -275
  484. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.h +0 -41
  485. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.cpp +0 -796
  486. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.h +0 -63
  487. package/node_modules/node-datachannel/src/index.ts +0 -9
  488. package/node_modules/node-datachannel/src/lib/datachannel-stream.ts +0 -100
  489. package/node_modules/node-datachannel/src/lib/index.ts +0 -290
  490. package/node_modules/node-datachannel/src/lib/node-datachannel.ts +0 -84
  491. package/node_modules/node-datachannel/src/lib/types.ts +0 -169
  492. package/node_modules/node-datachannel/src/lib/websocket-server.ts +0 -37
  493. package/node_modules/node-datachannel/src/lib/websocket.ts +0 -26
  494. package/node_modules/node-datachannel/src/polyfill/Events.ts +0 -82
  495. package/node_modules/node-datachannel/src/polyfill/Exception.ts +0 -37
  496. package/node_modules/node-datachannel/src/polyfill/README.md +0 -41
  497. package/node_modules/node-datachannel/src/polyfill/RTCCertificate.ts +0 -21
  498. package/node_modules/node-datachannel/src/polyfill/RTCDataChannel.ts +0 -225
  499. package/node_modules/node-datachannel/src/polyfill/RTCDtlsTransport.ts +0 -46
  500. package/node_modules/node-datachannel/src/polyfill/RTCError.ts +0 -78
  501. package/node_modules/node-datachannel/src/polyfill/RTCIceCandidate.ts +0 -128
  502. package/node_modules/node-datachannel/src/polyfill/RTCIceTransport.ts +0 -90
  503. package/node_modules/node-datachannel/src/polyfill/RTCPeerConnection.ts +0 -527
  504. package/node_modules/node-datachannel/src/polyfill/RTCSctpTransport.ts +0 -51
  505. package/node_modules/node-datachannel/src/polyfill/RTCSessionDescription.ts +0 -41
  506. package/node_modules/node-datachannel/src/polyfill/index.ts +0 -38
  507. package/node_modules/node-datachannel/tsconfig.json +0 -21
  508. package/node_modules/p-limit/index.d.ts +0 -38
  509. package/node_modules/p-limit/index.js +0 -57
  510. package/node_modules/p-limit/license +0 -9
  511. package/node_modules/p-limit/package.json +0 -52
  512. package/node_modules/p-limit/readme.md +0 -101
  513. package/node_modules/p-locate/index.d.ts +0 -64
  514. package/node_modules/p-locate/index.js +0 -52
  515. package/node_modules/p-locate/license +0 -9
  516. package/node_modules/p-locate/package.json +0 -53
  517. package/node_modules/p-locate/readme.md +0 -90
  518. package/node_modules/p-try/index.d.ts +0 -39
  519. package/node_modules/p-try/index.js +0 -9
  520. package/node_modules/p-try/license +0 -9
  521. package/node_modules/p-try/package.json +0 -42
  522. package/node_modules/p-try/readme.md +0 -58
  523. package/node_modules/path-exists/index.d.ts +0 -28
  524. package/node_modules/path-exists/index.js +0 -23
  525. package/node_modules/path-exists/license +0 -9
  526. package/node_modules/path-exists/package.json +0 -39
  527. package/node_modules/path-exists/readme.md +0 -52
  528. package/node_modules/pngjs/.eslintignore +0 -2
  529. package/node_modules/pngjs/.eslintrc.json +0 -19
  530. package/node_modules/pngjs/.prettierignore +0 -3
  531. package/node_modules/pngjs/LICENSE +0 -20
  532. package/node_modules/pngjs/README.md +0 -429
  533. package/node_modules/pngjs/browser.js +0 -16096
  534. package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
  535. package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
  536. package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
  537. package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
  538. package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
  539. package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
  540. package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
  541. package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
  542. package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
  543. package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
  544. package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
  545. package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
  546. package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
  547. package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
  548. package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
  549. package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
  550. package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
  551. package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
  552. package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
  553. package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
  554. package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
  555. package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
  556. package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
  557. package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
  558. package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
  559. package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
  560. package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  561. package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
  562. package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
  563. package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
  564. package/node_modules/pngjs/coverage/lcov.info +0 -2177
  565. package/node_modules/pngjs/lib/bitmapper.js +0 -267
  566. package/node_modules/pngjs/lib/bitpacker.js +0 -158
  567. package/node_modules/pngjs/lib/chunkstream.js +0 -189
  568. package/node_modules/pngjs/lib/constants.js +0 -32
  569. package/node_modules/pngjs/lib/crc.js +0 -40
  570. package/node_modules/pngjs/lib/filter-pack.js +0 -171
  571. package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
  572. package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
  573. package/node_modules/pngjs/lib/filter-parse.js +0 -177
  574. package/node_modules/pngjs/lib/format-normaliser.js +0 -93
  575. package/node_modules/pngjs/lib/interlace.js +0 -95
  576. package/node_modules/pngjs/lib/packer-async.js +0 -50
  577. package/node_modules/pngjs/lib/packer-sync.js +0 -56
  578. package/node_modules/pngjs/lib/packer.js +0 -129
  579. package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
  580. package/node_modules/pngjs/lib/parser-async.js +0 -165
  581. package/node_modules/pngjs/lib/parser-sync.js +0 -108
  582. package/node_modules/pngjs/lib/parser.js +0 -290
  583. package/node_modules/pngjs/lib/png-sync.js +0 -12
  584. package/node_modules/pngjs/lib/png.js +0 -194
  585. package/node_modules/pngjs/lib/sync-inflate.js +0 -168
  586. package/node_modules/pngjs/lib/sync-reader.js +0 -45
  587. package/node_modules/pngjs/package.json +0 -73
  588. package/node_modules/qrcode/README.md +0 -770
  589. package/node_modules/qrcode/bin/qrcode +0 -159
  590. package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
  591. package/node_modules/qrcode/helper/to-sjis.js +0 -105
  592. package/node_modules/qrcode/lib/browser.js +0 -76
  593. package/node_modules/qrcode/lib/can-promise.js +0 -7
  594. package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
  595. package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
  596. package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
  597. package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
  598. package/node_modules/qrcode/lib/core/byte-data.js +0 -30
  599. package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
  600. package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
  601. package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
  602. package/node_modules/qrcode/lib/core/format-info.js +0 -29
  603. package/node_modules/qrcode/lib/core/galois-field.js +0 -69
  604. package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
  605. package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
  606. package/node_modules/qrcode/lib/core/mode.js +0 -167
  607. package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
  608. package/node_modules/qrcode/lib/core/polynomial.js +0 -62
  609. package/node_modules/qrcode/lib/core/qrcode.js +0 -495
  610. package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
  611. package/node_modules/qrcode/lib/core/regex.js +0 -31
  612. package/node_modules/qrcode/lib/core/segments.js +0 -330
  613. package/node_modules/qrcode/lib/core/utils.js +0 -63
  614. package/node_modules/qrcode/lib/core/version-check.js +0 -9
  615. package/node_modules/qrcode/lib/core/version.js +0 -163
  616. package/node_modules/qrcode/lib/index.js +0 -12
  617. package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
  618. package/node_modules/qrcode/lib/renderer/png.js +0 -78
  619. package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
  620. package/node_modules/qrcode/lib/renderer/svg.js +0 -19
  621. package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
  622. package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
  623. package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
  624. package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
  625. package/node_modules/qrcode/lib/renderer/utils.js +0 -99
  626. package/node_modules/qrcode/lib/server.js +0 -138
  627. package/node_modules/qrcode/license +0 -10
  628. package/node_modules/qrcode/package.json +0 -77
  629. package/node_modules/require-directory/.jshintrc +0 -67
  630. package/node_modules/require-directory/.npmignore +0 -1
  631. package/node_modules/require-directory/.travis.yml +0 -3
  632. package/node_modules/require-directory/LICENSE +0 -22
  633. package/node_modules/require-directory/README.markdown +0 -184
  634. package/node_modules/require-directory/index.js +0 -86
  635. package/node_modules/require-directory/package.json +0 -40
  636. package/node_modules/require-main-filename/CHANGELOG.md +0 -26
  637. package/node_modules/require-main-filename/LICENSE.txt +0 -14
  638. package/node_modules/require-main-filename/README.md +0 -26
  639. package/node_modules/require-main-filename/index.js +0 -18
  640. package/node_modules/require-main-filename/package.json +0 -35
  641. package/node_modules/semver/LICENSE +0 -15
  642. package/node_modules/semver/README.md +0 -680
  643. package/node_modules/semver/bin/semver.js +0 -195
  644. package/node_modules/semver/classes/comparator.js +0 -143
  645. package/node_modules/semver/classes/index.js +0 -7
  646. package/node_modules/semver/classes/range.js +0 -577
  647. package/node_modules/semver/classes/semver.js +0 -350
  648. package/node_modules/semver/functions/clean.js +0 -8
  649. package/node_modules/semver/functions/cmp.js +0 -54
  650. package/node_modules/semver/functions/coerce.js +0 -62
  651. package/node_modules/semver/functions/compare-build.js +0 -9
  652. package/node_modules/semver/functions/compare-loose.js +0 -5
  653. package/node_modules/semver/functions/compare.js +0 -7
  654. package/node_modules/semver/functions/diff.js +0 -60
  655. package/node_modules/semver/functions/eq.js +0 -5
  656. package/node_modules/semver/functions/gt.js +0 -5
  657. package/node_modules/semver/functions/gte.js +0 -5
  658. package/node_modules/semver/functions/inc.js +0 -21
  659. package/node_modules/semver/functions/lt.js +0 -5
  660. package/node_modules/semver/functions/lte.js +0 -5
  661. package/node_modules/semver/functions/major.js +0 -5
  662. package/node_modules/semver/functions/minor.js +0 -5
  663. package/node_modules/semver/functions/neq.js +0 -5
  664. package/node_modules/semver/functions/parse.js +0 -18
  665. package/node_modules/semver/functions/patch.js +0 -5
  666. package/node_modules/semver/functions/prerelease.js +0 -8
  667. package/node_modules/semver/functions/rcompare.js +0 -5
  668. package/node_modules/semver/functions/rsort.js +0 -5
  669. package/node_modules/semver/functions/satisfies.js +0 -12
  670. package/node_modules/semver/functions/sort.js +0 -5
  671. package/node_modules/semver/functions/truncate.js +0 -48
  672. package/node_modules/semver/functions/valid.js +0 -8
  673. package/node_modules/semver/index.js +0 -93
  674. package/node_modules/semver/internal/constants.js +0 -37
  675. package/node_modules/semver/internal/debug.js +0 -11
  676. package/node_modules/semver/internal/identifiers.js +0 -29
  677. package/node_modules/semver/internal/lrucache.js +0 -42
  678. package/node_modules/semver/internal/parse-options.js +0 -17
  679. package/node_modules/semver/internal/re.js +0 -223
  680. package/node_modules/semver/package.json +0 -78
  681. package/node_modules/semver/preload.js +0 -4
  682. package/node_modules/semver/range.bnf +0 -17
  683. package/node_modules/semver/ranges/gtr.js +0 -6
  684. package/node_modules/semver/ranges/intersects.js +0 -9
  685. package/node_modules/semver/ranges/ltr.js +0 -6
  686. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  687. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  688. package/node_modules/semver/ranges/min-version.js +0 -63
  689. package/node_modules/semver/ranges/outside.js +0 -82
  690. package/node_modules/semver/ranges/simplify.js +0 -49
  691. package/node_modules/semver/ranges/subset.js +0 -249
  692. package/node_modules/semver/ranges/to-comparators.js +0 -10
  693. package/node_modules/semver/ranges/valid.js +0 -13
  694. package/node_modules/set-blocking/CHANGELOG.md +0 -26
  695. package/node_modules/set-blocking/LICENSE.txt +0 -14
  696. package/node_modules/set-blocking/README.md +0 -31
  697. package/node_modules/set-blocking/index.js +0 -7
  698. package/node_modules/set-blocking/package.json +0 -42
  699. package/node_modules/sharp/LICENSE +0 -191
  700. package/node_modules/sharp/README.md +0 -112
  701. package/node_modules/sharp/dist/channel.cjs +0 -177
  702. package/node_modules/sharp/dist/channel.mjs +0 -177
  703. package/node_modules/sharp/dist/colour.cjs +0 -199
  704. package/node_modules/sharp/dist/colour.mjs +0 -199
  705. package/node_modules/sharp/dist/composite.cjs +0 -213
  706. package/node_modules/sharp/dist/composite.mjs +0 -213
  707. package/node_modules/sharp/dist/constructor.cjs +0 -511
  708. package/node_modules/sharp/dist/constructor.mjs +0 -511
  709. package/node_modules/sharp/dist/index.cjs +0 -25
  710. package/node_modules/sharp/dist/index.d.cts +0 -1999
  711. package/node_modules/sharp/dist/index.d.mts +0 -2046
  712. package/node_modules/sharp/dist/index.mjs +0 -25
  713. package/node_modules/sharp/dist/input.cjs +0 -819
  714. package/node_modules/sharp/dist/input.mjs +0 -819
  715. package/node_modules/sharp/dist/is.cjs +0 -143
  716. package/node_modules/sharp/dist/is.mjs +0 -143
  717. package/node_modules/sharp/dist/libvips.cjs +0 -212
  718. package/node_modules/sharp/dist/libvips.mjs +0 -212
  719. package/node_modules/sharp/dist/operation.cjs +0 -1002
  720. package/node_modules/sharp/dist/operation.mjs +0 -1002
  721. package/node_modules/sharp/dist/output.cjs +0 -1785
  722. package/node_modules/sharp/dist/output.mjs +0 -1785
  723. package/node_modules/sharp/dist/resize.cjs +0 -617
  724. package/node_modules/sharp/dist/resize.mjs +0 -617
  725. package/node_modules/sharp/dist/sharp.cjs +0 -174
  726. package/node_modules/sharp/dist/sharp.mjs +0 -174
  727. package/node_modules/sharp/dist/utility.cjs +0 -301
  728. package/node_modules/sharp/dist/utility.mjs +0 -301
  729. package/node_modules/sharp/install/build.js +0 -38
  730. package/node_modules/sharp/lib/index.d.ts +0 -1999
  731. package/node_modules/sharp/package.json +0 -227
  732. package/node_modules/sharp/src/binding.gyp +0 -303
  733. package/node_modules/sharp/src/common.cc +0 -1186
  734. package/node_modules/sharp/src/common.h +0 -421
  735. package/node_modules/sharp/src/metadata.cc +0 -404
  736. package/node_modules/sharp/src/metadata.h +0 -95
  737. package/node_modules/sharp/src/operations.cc +0 -516
  738. package/node_modules/sharp/src/operations.h +0 -137
  739. package/node_modules/sharp/src/pipeline.cc +0 -1948
  740. package/node_modules/sharp/src/pipeline.h +0 -422
  741. package/node_modules/sharp/src/sharp.cc +0 -43
  742. package/node_modules/sharp/src/stats.cc +0 -186
  743. package/node_modules/sharp/src/stats.h +0 -62
  744. package/node_modules/sharp/src/utilities.cc +0 -289
  745. package/node_modules/sharp/src/utilities.h +0 -22
  746. package/node_modules/string-width/index.d.ts +0 -29
  747. package/node_modules/string-width/index.js +0 -47
  748. package/node_modules/string-width/license +0 -9
  749. package/node_modules/string-width/package.json +0 -56
  750. package/node_modules/string-width/readme.md +0 -50
  751. package/node_modules/strip-ansi/index.d.ts +0 -17
  752. package/node_modules/strip-ansi/index.js +0 -4
  753. package/node_modules/strip-ansi/license +0 -9
  754. package/node_modules/strip-ansi/package.json +0 -54
  755. package/node_modules/strip-ansi/readme.md +0 -46
  756. package/node_modules/which-module/LICENSE +0 -13
  757. package/node_modules/which-module/README.md +0 -58
  758. package/node_modules/which-module/index.js +0 -9
  759. package/node_modules/which-module/package.json +0 -41
  760. package/node_modules/wrap-ansi/index.js +0 -186
  761. package/node_modules/wrap-ansi/license +0 -9
  762. package/node_modules/wrap-ansi/package.json +0 -61
  763. package/node_modules/wrap-ansi/readme.md +0 -97
  764. package/node_modules/ws/LICENSE +0 -20
  765. package/node_modules/ws/README.md +0 -548
  766. package/node_modules/ws/browser.js +0 -8
  767. package/node_modules/ws/index.js +0 -22
  768. package/node_modules/ws/lib/buffer-util.js +0 -131
  769. package/node_modules/ws/lib/constants.js +0 -19
  770. package/node_modules/ws/lib/event-target.js +0 -292
  771. package/node_modules/ws/lib/extension.js +0 -203
  772. package/node_modules/ws/lib/limiter.js +0 -55
  773. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  774. package/node_modules/ws/lib/receiver.js +0 -743
  775. package/node_modules/ws/lib/sender.js +0 -607
  776. package/node_modules/ws/lib/stream.js +0 -161
  777. package/node_modules/ws/lib/subprotocol.js +0 -62
  778. package/node_modules/ws/lib/validation.js +0 -152
  779. package/node_modules/ws/lib/websocket-server.js +0 -562
  780. package/node_modules/ws/lib/websocket.js +0 -1407
  781. package/node_modules/ws/package.json +0 -74
  782. package/node_modules/ws/wrapper.mjs +0 -21
  783. package/node_modules/y18n/CHANGELOG.md +0 -35
  784. package/node_modules/y18n/LICENSE +0 -13
  785. package/node_modules/y18n/README.md +0 -109
  786. package/node_modules/y18n/index.js +0 -188
  787. package/node_modules/y18n/package.json +0 -39
  788. package/node_modules/yargs/CHANGELOG.md +0 -420
  789. package/node_modules/yargs/LICENSE +0 -21
  790. package/node_modules/yargs/README.md +0 -140
  791. package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
  792. package/node_modules/yargs/build/lib/apply-extends.js +0 -65
  793. package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
  794. package/node_modules/yargs/build/lib/argsert.js +0 -65
  795. package/node_modules/yargs/build/lib/command.d.ts +0 -64
  796. package/node_modules/yargs/build/lib/command.js +0 -416
  797. package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
  798. package/node_modules/yargs/build/lib/common-types.js +0 -25
  799. package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
  800. package/node_modules/yargs/build/lib/completion-templates.js +0 -50
  801. package/node_modules/yargs/build/lib/completion.d.ts +0 -21
  802. package/node_modules/yargs/build/lib/completion.js +0 -135
  803. package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
  804. package/node_modules/yargs/build/lib/is-promise.js +0 -9
  805. package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
  806. package/node_modules/yargs/build/lib/levenshtein.js +0 -58
  807. package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
  808. package/node_modules/yargs/build/lib/middleware.js +0 -57
  809. package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
  810. package/node_modules/yargs/build/lib/obj-filter.js +0 -14
  811. package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
  812. package/node_modules/yargs/build/lib/parse-command.js +0 -36
  813. package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
  814. package/node_modules/yargs/build/lib/process-argv.js +0 -31
  815. package/node_modules/yargs/build/lib/usage.d.ts +0 -49
  816. package/node_modules/yargs/build/lib/usage.js +0 -540
  817. package/node_modules/yargs/build/lib/validation.d.ts +0 -34
  818. package/node_modules/yargs/build/lib/validation.js +0 -330
  819. package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
  820. package/node_modules/yargs/build/lib/yargs.js +0 -1190
  821. package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
  822. package/node_modules/yargs/build/lib/yerror.js +0 -11
  823. package/node_modules/yargs/index.js +0 -40
  824. package/node_modules/yargs/locales/be.json +0 -46
  825. package/node_modules/yargs/locales/de.json +0 -46
  826. package/node_modules/yargs/locales/en.json +0 -51
  827. package/node_modules/yargs/locales/es.json +0 -46
  828. package/node_modules/yargs/locales/fi.json +0 -49
  829. package/node_modules/yargs/locales/fr.json +0 -53
  830. package/node_modules/yargs/locales/hi.json +0 -49
  831. package/node_modules/yargs/locales/hu.json +0 -46
  832. package/node_modules/yargs/locales/id.json +0 -50
  833. package/node_modules/yargs/locales/it.json +0 -46
  834. package/node_modules/yargs/locales/ja.json +0 -51
  835. package/node_modules/yargs/locales/ko.json +0 -49
  836. package/node_modules/yargs/locales/nb.json +0 -44
  837. package/node_modules/yargs/locales/nl.json +0 -49
  838. package/node_modules/yargs/locales/nn.json +0 -44
  839. package/node_modules/yargs/locales/pirate.json +0 -13
  840. package/node_modules/yargs/locales/pl.json +0 -49
  841. package/node_modules/yargs/locales/pt.json +0 -45
  842. package/node_modules/yargs/locales/pt_BR.json +0 -48
  843. package/node_modules/yargs/locales/ru.json +0 -46
  844. package/node_modules/yargs/locales/th.json +0 -46
  845. package/node_modules/yargs/locales/tr.json +0 -48
  846. package/node_modules/yargs/locales/zh_CN.json +0 -48
  847. package/node_modules/yargs/locales/zh_TW.json +0 -47
  848. package/node_modules/yargs/package.json +0 -92
  849. package/node_modules/yargs/yargs.js +0 -14
  850. package/node_modules/yargs-parser/CHANGELOG.md +0 -601
  851. package/node_modules/yargs-parser/LICENSE.txt +0 -14
  852. package/node_modules/yargs-parser/README.md +0 -449
  853. package/node_modules/yargs-parser/index.js +0 -1032
  854. package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
  855. package/node_modules/yargs-parser/package.json +0 -46
@@ -0,0 +1,59 @@
1
+ import fs from 'node:fs';
2
+ import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
3
+ import { promisify } from 'node:util';
4
+
5
+ const derive = promisify(scrypt);
6
+ const COOKIE = '__Host-code-relax-admin';
7
+ const SESSION_MS = 12 * 60 * 60 * 1000;
8
+ const cookie = (value, seconds) => `${COOKIE}=${value}; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=${seconds}`;
9
+
10
+ export async function passwordRecord(password) {
11
+ if (typeof password !== 'string' || password.length < 10 || Buffer.byteLength(password) > 256) {
12
+ throw new Error('Admin password must have at least 10 characters and at most 256 bytes');
13
+ }
14
+ const salt = randomBytes(16).toString('hex');
15
+ return { salt, hash: (await derive(password, salt, 64)).toString('hex') };
16
+ }
17
+
18
+ export function createAdminAuth(passwordFile) {
19
+ const { salt, hash } = JSON.parse(fs.readFileSync(passwordFile, 'utf8'));
20
+ if (!/^[a-f0-9]{32}$/.test(salt) || !/^[a-f0-9]{128}$/.test(hash)) throw new Error('Invalid admin password record');
21
+ const expected = Buffer.from(hash, 'hex');
22
+ const sessions = new Map();
23
+ let windowStart = 0, attempts = 0, verifying = false;
24
+ const token = request => (request.headers.cookie || '').split(';').map(value => value.trim())
25
+ .find(value => value.startsWith(`${COOKIE}=`))?.slice(COOKIE.length + 1);
26
+ function purge() {
27
+ for (const [id, expires] of sessions) if (expires <= Date.now()) sessions.delete(id);
28
+ }
29
+ return {
30
+ authenticated(request) { purge(); return sessions.has(token(request)); },
31
+ async login(password, request, response) {
32
+ const now = Date.now();
33
+ if (now - windowStart >= 60000) { windowStart = now; attempts = 0; }
34
+ // This is a single-owner console: bound both guessing and concurrent scrypt memory.
35
+ if (attempts >= 10 || verifying) {
36
+ response.setHeader('Retry-After', '60');
37
+ return { status: 429, error: '尝试过于频繁,请一分钟后重试' };
38
+ }
39
+ attempts++;
40
+ if (typeof password !== 'string' || Buffer.byteLength(password) > 256) return { status: 400, error: '请输入有效密码' };
41
+ verifying = true;
42
+ try {
43
+ const actual = await derive(password, salt, 64);
44
+ if (!timingSafeEqual(actual, expected)) return { status: 401, error: '密码不正确' };
45
+ purge();
46
+ sessions.delete(token(request));
47
+ if (sessions.size >= 32) sessions.delete(sessions.keys().next().value);
48
+ const id = randomBytes(32).toString('base64url');
49
+ sessions.set(id, Date.now() + SESSION_MS);
50
+ response.setHeader('Set-Cookie', cookie(id, SESSION_MS / 1000));
51
+ return { status: 200 };
52
+ } finally { verifying = false; }
53
+ },
54
+ logout(request, response) {
55
+ sessions.delete(token(request));
56
+ response.setHeader('Set-Cookie', cookie('', 0));
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,101 @@
1
+ import fs from 'node:fs';
2
+ import tencentcloud from 'tencentcloud-sdk-nodejs-common';
3
+
4
+ const INSTANCE_ID = 'lhins-byrs7n8o';
5
+ const REGION = 'ap-beijing';
6
+ const { CommonClient } = tencentcloud;
7
+ const METRICS = [
8
+ ['LighthouseOutratio', 'outRatioPercent', 10],
9
+ ['LighthouseOuttraffic', 'outMbps', 10],
10
+ ['LighthouseIntraffic', 'inMbps', 10],
11
+ ['CpuUsage', 'cpuPercent', 10],
12
+ ['MemUsage', 'memoryPercent', 10],
13
+ ['DiskUsage', 'diskPercent', 60],
14
+ ['CpuLoad1', 'cpuLoad1', 10],
15
+ ];
16
+
17
+ function latestPoint(result) {
18
+ const point = result?.DataPoints?.[0];
19
+ if (!point?.Values?.length || !point?.Timestamps?.length) return null;
20
+ const index = point.Values.length - 1;
21
+ const value = Number(point.Values[index]);
22
+ const timestamp = Number(point.Timestamps[index]);
23
+ return Number.isFinite(value) && Number.isFinite(timestamp)
24
+ ? { value, sampledAt: new Date(timestamp * 1000).toISOString() } : null;
25
+ }
26
+
27
+ export function startAdminCloud({ credentialFile = process.env.CREDENTIALS_DIRECTORY
28
+ ? `${process.env.CREDENTIALS_DIRECTORY}/tencent-monitor.json` : null,
29
+ metricIntervalMs = 60000, trafficIntervalMs = 300000 } = {}) {
30
+ if (!credentialFile) return { read: () => ({ configured: false }), close() {} };
31
+ const { secretId, secretKey } = JSON.parse(fs.readFileSync(credentialFile, 'utf8'));
32
+ if (!secretId || !secretKey) throw new Error('Tencent monitor credentials are incomplete');
33
+ const credential = { secretId, secretKey };
34
+ const profile = { httpProfile: { reqTimeout: 8 } };
35
+ const monitor = new CommonClient('monitor.tencentcloudapi.com', '2018-07-24',
36
+ { credential, region: REGION, profile });
37
+ const lighthouse = new CommonClient('lighthouse.tencentcloudapi.com', '2020-03-24',
38
+ { credential, region: REGION, profile });
39
+ let closed = false;
40
+ let metricBusy = false;
41
+ let trafficBusy = false;
42
+ const current = { configured: true, metricAvailable: false, trafficAvailable: false,
43
+ outRatioPercent: null, outMbps: null, inMbps: null, metricSampledAt: null,
44
+ cpuPercent: null, memoryPercent: null, diskPercent: null, cpuLoad1: null,
45
+ trafficUsedBytes: null, trafficTotalBytes: null, trafficRemainingBytes: null,
46
+ trafficStartAt: null, trafficEndAt: null, trafficSampledAt: null };
47
+
48
+ async function sampleMetrics() {
49
+ if (metricBusy || closed) return;
50
+ metricBusy = true;
51
+ const end = new Date();
52
+ const base = { Namespace: 'QCE/LIGHTHOUSE',
53
+ StartTime: new Date(end.getTime() - 180000).toISOString(), EndTime: end.toISOString(),
54
+ Instances: [{ Dimensions: [{ Name: 'InstanceId', Value: INSTANCE_ID }] }] };
55
+ try {
56
+ const values = await Promise.allSettled(METRICS.map(([MetricName, , Period]) =>
57
+ monitor.request('GetMonitorData', { ...base, MetricName, Period })));
58
+ if (closed) return;
59
+ const samples = values.map(result => result.status === 'fulfilled' ? latestPoint(result.value) : null);
60
+ for (let index = 0; index < METRICS.length; index++) {
61
+ const [, field] = METRICS[index];
62
+ const value = samples[index]?.value ?? null;
63
+ current[field] = value !== null && (field === 'outMbps' || field === 'inMbps') ? value * 8 : value;
64
+ }
65
+ current.metricSampledAt = samples.reduce((latest, sample) => sample?.sampledAt > latest
66
+ ? sample.sampledAt : latest, '') || null;
67
+ current.metricAvailable = Boolean(current.metricSampledAt);
68
+ } catch {
69
+ current.metricAvailable = false;
70
+ } finally { metricBusy = false; }
71
+ }
72
+
73
+ async function sampleTraffic() {
74
+ if (trafficBusy || closed) return;
75
+ trafficBusy = true;
76
+ try {
77
+ const data = await lighthouse.request('DescribeInstancesTrafficPackages', { InstanceIds: [INSTANCE_ID] });
78
+ if (closed) return;
79
+ const packages = data?.InstanceTrafficPackageSet?.find(item => item.InstanceId === INSTANCE_ID)?.TrafficPackageSet || [];
80
+ const active = packages.find(item => Date.parse(item.StartTime) <= Date.now()
81
+ && Date.parse(item.EndTime) >= Date.now()) || packages[0];
82
+ current.trafficUsedBytes = active?.TrafficUsed ?? null;
83
+ current.trafficTotalBytes = active?.TrafficPackageTotal ?? null;
84
+ current.trafficRemainingBytes = active?.TrafficPackageRemaining ?? null;
85
+ current.trafficStartAt = active?.StartTime ?? null;
86
+ current.trafficEndAt = active?.EndTime ?? null;
87
+ current.trafficSampledAt = active ? new Date().toISOString() : null;
88
+ current.trafficAvailable = Boolean(active);
89
+ } catch {
90
+ current.trafficAvailable = false;
91
+ } finally { trafficBusy = false; }
92
+ }
93
+
94
+ void sampleMetrics();
95
+ void sampleTraffic();
96
+ const metricTimer = setInterval(sampleMetrics, metricIntervalMs);
97
+ const trafficTimer = setInterval(sampleTraffic, trafficIntervalMs);
98
+ metricTimer.unref();
99
+ trafficTimer.unref();
100
+ return { read: () => ({ ...current }), close() { closed = true; clearInterval(metricTimer); clearInterval(trafficTimer); } };
101
+ }
@@ -0,0 +1,7 @@
1
+ import { adminUiTokens, adminFooterStyles, adminAuthStyles } from './admin-style.mjs';
2
+
3
+ export function adminLoginPage() {
4
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>登录 · Code Relax 管理</title><style>${adminUiTokens}${adminFooterStyles}${adminAuthStyles}</style></head>
5
+ <body class="admin-login"><main><form id="login" class="login-card"><h1>Code Relax 管理</h1><p>输入管理密码,进入后台。</p><label for="password">管理密码</label><input id="password" name="password" type="password" autocomplete="current-password" required maxlength="256" autofocus><button id="submit" type="submit">登录</button><p id="result" role="alert" aria-live="polite"></p></form></main><footer class="admin-footer">京ICP备2026027643号-2</footer>
6
+ <script>const form=document.getElementById('login'),button=document.getElementById('submit'),result=document.getElementById('result');form.addEventListener('submit',async event=>{event.preventDefault();if(button.disabled)return;button.disabled=true;result.textContent='正在登录…';try{const response=await fetch('/admin/login',{method:'POST',headers:{'Content-Type':'application/json','X-Code-Relax-Admin':'1'},body:JSON.stringify({password:form.password.value})});const body=await response.json();if(!response.ok)throw new Error(body.error||'登录失败');form.reset();location.replace('/')}catch(error){result.textContent=error.message}finally{button.disabled=false}});</script></body></html>`;
7
+ }
@@ -0,0 +1,87 @@
1
+ import { adminUiTokens, adminFooterStyles, adminAuthStyles, adminWorkstationStyles } from './admin-style.mjs';
2
+ import { adminWorkstationsMarkup, adminWorkstationsScript } from './admin-workstations-page.mjs';
3
+
4
+ export function adminPage() {
5
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="only light"><meta name="theme-color" content="#f5f7fa"><title>Code Relax 管理</title><style>${adminUiTokens}${adminFooterStyles}</style>
6
+ <style>@media(max-width:600px){body main{padding:12px 12px 40px}header.page-head{align-items:center;gap:8px;margin-bottom:8px}header.page-head h1{font-size:20px;line-height:1.2;margin:0}main .nav{gap:0;margin-bottom:12px}main .nav button{padding:8px 11px}main .stack,main .grid{gap:10px}main .card{padding:12px}main .hero strong{font-size:20px}main .throughput{grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-top:10px}main .metrics{gap:6px}main .metric{display:flex;flex-direction:row-reverse;align-items:center;justify-content:space-between;gap:6px;min-width:0;padding:9px 10px}main .metric strong{font-size:17px;margin:0;white-space:nowrap}main .metric span{font-size:11px}main .card-head{margin-bottom:10px}}</style>
7
+ <style>@media(max-width:600px){main .throughput .metric strong{font-size:17px}}</style>
8
+ <style>.metric.warn{border-color:#946d38;background:#352a1b}.metric.warn strong{color:#ffd192}.metric.critical{border-color:#a65b58;background:#38211f}.metric.critical strong{color:#ffb1a9}.state.critical{background:#72342e;color:#ffe1dc}#trafficTimeline.critical .quota-fill{background:#d46a60}</style>
9
+ <style>.quota-timeline{display:grid;gap:9px;margin-top:18px}.quota-timeline[hidden]{display:none}.quota-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#b7c8ba;font-size:12px;font-variant-numeric:tabular-nums}.quota-heading strong{font-size:13px}.quota-pace{font-weight:700;color:#7bc993}.quota-pace.ahead{color:#e9b46f}.quota-track{position:relative;height:13px;border:1px solid #526154;border-radius:7px;background:#5c6e6030}.quota-fill{position:absolute;inset:0 auto 0 0;border-radius:6px;background:#48ae78}.quota-timeline.ahead .quota-fill{background:#d39b4c}.quota-now{position:absolute;top:-5px;bottom:-5px;width:2px;background:#d8e3db;box-shadow:0 0 0 1px #162019;transform:translateX(-1px)}.quota-now:before{position:absolute;top:-1px;left:50%;width:7px;height:7px;background:#d8e3db;transform:translate(-50%,-50%) rotate(45deg);content:""}.quota-period{display:flex;justify-content:space-between;gap:12px;color:#91a394;font-size:11px}.quota-period span:last-child{text-align:right}</style>
10
+ <style>
11
+ :root{color-scheme:only light;font:14px/1.5 system-ui,"Microsoft YaHei",sans-serif;background:#f5f7fa;color:#17212b}*{box-sizing:border-box}body{margin:0}button,input,textarea{font:inherit}button{cursor:pointer;border:1px solid transparent;border-radius:9px;padding:9px 14px;background:#397e58;color:white;font-weight:600}button:hover{background:#46966b}button:disabled{opacity:.45;cursor:default}button.secondary{background:#26352c;color:#c8d7cd;border-color:#405548}button.danger{background:transparent;color:#eaa29c;border-color:#734947}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid #8ad2a0;outline-offset:2px}
12
+ main{max-width:1180px;margin:auto;padding:28px 24px 70px}h1{font-size:29px;line-height:1.2;margin:6px 0 8px}h2{font-size:18px;margin:0}h3{font-size:15px;margin:0}p{margin:0}.eyebrow{color:#83b598;letter-spacing:.12em;font-size:11px;font-weight:700}.subtle,.hint,.note{color:#a3b4a8}.hint,.note{font-size:12px}.note{margin-top:15px}.page-head{display:flex;align-items:end;justify-content:space-between;gap:16px;margin-bottom:22px}.page-head .subtle{margin-top:6px}.nav{display:flex;gap:7px;border-bottom:1px solid #34463a;margin-bottom:22px;overflow:auto}.nav button{border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:#b6c9bb;padding:12px 17px;white-space:nowrap}.nav button[aria-current=page]{color:#b9efca;border-bottom-color:#8ad2a0;background:#1a2b20}.page[hidden]{display:none}.stack{display:grid;gap:18px}.grid{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(290px,.75fr);gap:18px;align-items:start}.card{background:#19241d;border:1px solid #34463a;border-radius:16px;padding:22px;box-shadow:0 12px 30px #080e0a24}.card-head{margin-bottom:18px}.hero{display:flex;align-items:start;justify-content:space-between;gap:18px;flex-wrap:wrap}.hero strong{font-size:26px}.state{border-radius:100px;padding:5px 10px;font-size:12px;font-weight:700;color:#c9f2d5;background:#28583b}.state.warn{background:#62491c;color:#ffe4a8}.state.down{background:#63302c;color:#ffd1c8}.throughput,.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.throughput{margin-top:20px}.metrics{grid-template-columns:repeat(3,minmax(0,1fr))}.metric{background:#111b15;border:1px solid #2b3c30;border-radius:11px;padding:14px;color:#9eafa3;font-size:12px}.metric strong{display:block;color:#f0f6f0;font-size:25px;line-height:1.2;margin-bottom:5px;font-variant-numeric:tabular-nums}.metric small{display:block;color:#80b893}.throughput .metric strong{font-size:32px}.detail{display:grid;gap:10px}.detail div{display:flex;justify-content:space-between;gap:12px;border-bottom:1px solid #2b3c30;padding-bottom:10px}.detail div:last-child{border:0;padding:0}.field{display:grid;gap:7px;margin:15px 0}.field label{font-size:13px;font-weight:600;color:#c5d7c9}input,textarea{width:100%;background:#101913;border:1px solid #45614d;border-radius:9px;color:#f1f7f1;padding:10px 12px}textarea{min-height:145px;resize:vertical}.actions{display:flex;flex-wrap:wrap;gap:9px;align-items:center}.current{border:1px solid #385841;background:#112017;border-radius:11px;padding:15px;margin-bottom:18px}.current.empty{border-color:#34463a;background:#111b15}.badge{display:inline-block;background:#28583b;color:#c9f2d5;border-radius:100px;padding:2px 8px;font-size:11px;font-weight:700}.current h3,.history h3{margin:8px 0 4px;overflow-wrap:anywhere}.current p,.history p{white-space:pre-wrap;overflow-wrap:anywhere;color:#c1d1c5}.date{font-size:12px;color:#94a99a}.history{display:grid;gap:10px}.history-item{border:1px solid #34463a;background:#141e17;border-radius:11px;padding:15px}.history-item.active{border-color:#4d865e}.history-item p{margin:8px 0 12px;max-height:4.5em;overflow:hidden}.history-item .actions button{padding:6px 10px;font-size:12px}.empty-note{padding:24px;border:1px dashed #415748;border-radius:10px;color:#a3b4a8;text-align:center}.status{min-height:24px;margin-top:16px;color:#96d4a9}.status.error{color:#f0aaa2}@media(max-width:800px){main{padding:24px 16px 50px}.grid{grid-template-columns:1fr}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:500px){.page-head{align-items:start}.card{padding:17px}.throughput{grid-template-columns:1fr}.metric{padding:10px}.metric strong{font-size:21px}}
13
+ </style><style>
14
+ .announcement-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:18px}.announcement-toolbar h2{font-size:20px}.announcement-controls{display:flex;align-items:center;gap:9px;flex-wrap:wrap}.announcement-controls input{width:min(230px,100%)}.announcement-controls select{width:auto;min-width:108px}select{font:inherit;background:#101913;border:1px solid #45614d;border-radius:9px;color:#f1f7f1;padding:10px 12px}.announcement-list{display:grid;gap:9px}.announcement-row{display:grid;grid-template-columns:minmax(0,1fr) 90px 145px minmax(220px,auto);align-items:center;gap:14px;border:1px solid #34463a;border-radius:11px;padding:14px;background:#141e17}.announcement-row.active{border-color:#4d865e}.announcement-row h3{overflow-wrap:anywhere}.announcement-row p{color:#a3b4a8;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.announcement-row .actions{justify-content:flex-end;gap:6px}.announcement-row .actions button{padding:6px 9px;font-size:12px}.announcement-row .date{font-variant-numeric:tabular-nums}.announcement-status{display:inline-block;width:max-content;padding:3px 8px;border-radius:100px;font-size:11px;font-weight:700;color:#cad7ce;background:#334038}.announcement-status.active{background:#28583b;color:#c9f2d5}.announcement-status.draft{background:#4d462c;color:#ffe4a8}.announcement-empty{padding:32px;text-align:center;color:#a3b4a8}dialog{width:min(570px,calc(100% - 28px));max-height:calc(100vh - 36px);overflow:auto;background:#19241d;color:#edf4ee;border:1px solid #45614d;border-radius:16px;padding:22px;box-shadow:0 24px 70px #050905bb}dialog::backdrop{background:#050905bb}.editor-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.editor-head button{padding:4px 10px}.editor-actions{display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-top:18px}.editor-actions[hidden]{display:none}dialog textarea{min-height:200px}dialog input:disabled,dialog textarea:disabled{opacity:.8}.hero-meta{display:flex;align-items:center;gap:10px}.hero-meta .hint{white-space:nowrap}.quota-card .card-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.quota-card .card-head .hint{white-space:nowrap}@media(max-width:800px){.announcement-row{grid-template-columns:minmax(0,1fr) auto;gap:8px}.announcement-row .date{grid-column:1}.announcement-row .actions{grid-column:2;grid-row:2;align-self:end}.announcement-row .announcement-status{grid-column:2;grid-row:1}.announcement-toolbar{align-items:stretch}.announcement-controls{width:100%}.announcement-controls input{flex:1;min-width:110px}}@media(max-width:500px){.announcement-row{padding:11px}.announcement-row .actions button{padding:5px 7px;font-size:11px}.announcement-controls select{min-width:96px}.hero{align-items:center;flex-wrap:nowrap}.hero strong{font-size:20px}.hero-meta{gap:6px}.hero-meta .hint{font-size:11px}.hero-meta .state{padding:3px 7px}}
15
+ </style><style>
16
+ :root{color-scheme:only light;background:#f5f7fa;color:#17212b}body{min-height:100vh;background:radial-gradient(circle at 16% 15%,#cfe6d7aa 0,transparent 28%),radial-gradient(circle at 86% 76%,#dbece0aa 0,transparent 26%),linear-gradient(#1f4a2d0b 1px,transparent 1px),linear-gradient(90deg,#1f4a2d0b 1px,transparent 1px),#f5f7fa;background-size:auto,auto,32px 32px,32px 32px,auto}
17
+ button{background:#e0f0e5;color:#235d3a;border-color:#a9cbb4}button:hover{background:#cce5d4;color:#194d31;border-color:#84b995}button.secondary{background:#f4f8f5;color:#315f42;border-color:#c8daca}button.secondary:hover{background:#e8f2eb;color:#235d3a;border-color:#a9cbb4}button.danger{background:#fff8f6;color:#a34c43;border-color:#e6c5c0}button.danger:hover{background:#fdece8;color:#993b32;border-color:#dba7a0}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline-color:#4b9b66}
18
+ .subtle,.hint,.note{color:#607168}.page-head{justify-content:flex-start;align-items:center;gap:26px;margin-bottom:18px}.page-head h1{flex:none;color:#26332b}.page-head .nav{min-width:0;gap:4px;margin:0;border:0}.page-head .nav button{flex:none}.nav button{color:#607168}.nav button:hover{background:#eef5f0;color:#286e43}.nav button[aria-current=page]{color:#286e43;border-bottom-color:#68b582;background:#e4f1e8}.nav button[aria-current=page]:hover{background:#d8ebdd;color:#235d3a}.card{background:#ffffffee;border-color:#d5ddd8;box-shadow:0 10px 24px #263a2d0a}.metric{background:#f8faf8;border-color:#dce5df;color:#607168}.metric strong{color:#26332b}.metric small{color:#43865b}.metric.warn{background:#fff6e7;border-color:#e8c890}.metric.warn strong{color:#985d16}.metric.critical{background:#fff0ec;border-color:#dfaaa1}.metric.critical strong{color:#a5443b}
19
+ .state,.badge,.announcement-status.active{background:#e4f1e8;color:#286e43}.state.warn,.announcement-status.draft{background:#fff1d8;color:#895711}.state.down,.state.critical{background:#fbe6e2;color:#a13e35}.traffic-usage{color:#52645a;font-size:13px;font-variant-numeric:tabular-nums}.quota-card .quota-timeline{margin-top:12px}.quota-heading{color:#52645a}.quota-heading .warn{color:#985d16}.quota-heading .critical{color:#a5443b}.quota-pace{color:#28764a}.quota-pace.ahead{color:#a87326}.quota-track{border-color:#cfdbd2;background:#e9f0eb}.quota-fill{background:#68b582}.quota-timeline.ahead .quota-fill,.quota-timeline.warn .quota-fill{background:#d39b4c}.quota-now{background:#52645a;box-shadow:0 0 0 1px #fff}.quota-now:before{background:#52645a}.quota-period{color:#77857d}
20
+ .field label{color:#3b5142}input,textarea,select,.announcement-controls select{background:#fff;border-color:#c6d4cb;color:#26332b}input::placeholder,textarea::placeholder{color:#89988d}.date{color:#77857d}.announcement-row{background:#fff;border-color:#d5ddd8}.announcement-row.active{border-color:#a9c6b3;box-shadow:inset 3px 0 #68b582}.announcement-row p{color:#607168}.announcement-status{background:#edf1ee;color:#52645a}.announcement-empty{color:#607168}dialog{background:#fff;color:#26332b;border-color:#c6d4cb;box-shadow:0 24px 70px #263a2d33}dialog::backdrop{background:#172c20a8}.status{color:#28764a}.status.error{color:#a5443b}.relay-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}.overview-sample{display:flex;justify-content:flex-end}.throughput{margin-top:12px}.throughput .metric{padding:10px 12px}.throughput .metric strong{font-size:20px}@media(max-width:600px){header.page-head{gap:8px}header.page-head h1{font-size:18px}.page-head .nav{gap:2px}.page-head .nav button{padding:7px 6px;font-size:12px}main .throughput{margin-top:8px}main .throughput .metric{padding:7px 8px}main .throughput .metric strong{font-size:14px}}
21
+ </style><style>
22
+ #metrics{grid-template-columns:repeat(5,minmax(0,1fr))}.relay-heading{margin-bottom:14px}.resource-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.resource-head .hint{white-space:nowrap}.resource-card .throughput{border-top:1px solid #dce5df;padding-top:10px;margin-top:12px}.resource-card .throughput .metric{display:flex;flex-direction:row-reverse;align-items:center;justify-content:space-between;gap:8px;padding:8px 10px}.resource-card .throughput .metric strong{font-size:16px;margin:0;white-space:nowrap}
23
+ @media(max-width:800px){#metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}
24
+ @media(max-width:600px){.resource-card .throughput .metric strong{font-size:14px}}
25
+ </style><style>main:not(.authenticated)>:not(#adminLogin){display:none!important}#adminLogin{max-width:430px;margin:10vh auto}#adminLogin[hidden]{display:none}.usage-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:16px}.usage-views,.usage-pages{display:flex;align-items:center;gap:8px}.usage-views button[aria-pressed=true]{background:#286e43;color:#fff}.usage-rows{display:grid;gap:8px}.usage-row{display:grid;gap:8px;padding:12px 14px;border:1px solid var(--admin-row-border);border-radius:var(--admin-control-radius);background:var(--admin-row)}.usage-row-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;min-width:0}.usage-row-head span{min-width:0;overflow-wrap:anywhere}.usage-row-head small{flex-shrink:0;white-space:nowrap}.usage-row strong{font-variant-numeric:tabular-nums}.usage-row small{text-align:right}.usage-pages{margin-top:16px}</style></head><body><main class="authenticated">
26
+ <style>${adminAuthStyles}${adminWorkstationStyles}</style><header class="page-head"><h1>中继管理</h1><nav class="nav" aria-label="管理页面"><button type="button" data-page="overview" aria-current="page">运行概览</button><button type="button" data-page="workstations">工作站</button><button type="button" data-page="announcements">系统公告</button><button type="button" data-page="settings">容量设置</button><button type="button" data-page="usage">使用统计</button></nav><button id="logout" class="secondary admin-logout" type="button">退出登录</button></header>
27
+ ${adminWorkstationsMarkup}
28
+ <section id="overview" class="page stack"><section class="card" aria-label="中继连接与转发"><div class="relay-heading"><h2>中继连接与转发</h2><span id="healthText" class="state">读取中</span></div><div id="metrics" class="metrics"></div></section><div class="grid"><section class="card resource-card" aria-label="服务器资源"><div class="card-head resource-head"><h2>服务器资源</h2><span id="sampleTime" class="hint">等待采样</span></div><div id="serverMetrics" class="metrics"></div><div class="throughput" id="throughput"></div></section><section class="card quota-card"><div class="card-head"><h2>流量包</h2><span id="trafficNote" class="hint">等待更新</span></div><p id="trafficUsage" class="traffic-usage">—</p><div id="trafficTimeline" class="quota-timeline" hidden><div class="quota-heading"><strong id="trafficRemaining">流量剩余 —</strong><span id="trafficPace" class="quota-pace">—×</span><span id="timeRemaining">时间剩余 —</span></div><div id="quotaTrack" class="quota-track" role="img"><span id="quotaFill" class="quota-fill"></span><span id="quotaNow" class="quota-now"></span></div><div class="quota-period"><span id="trafficEnd">到期 —</span><span id="trafficStart">本期开始 —</span></div></div></section></div></section>
29
+ <section id="announcements" class="page" hidden><section class="card"><div class="announcement-toolbar"><div><h2>公告管理</h2><p id="announcementCount" class="hint">正在读取</p></div><button id="newAnnouncement" type="button">新建公告</button></div><div class="announcement-controls"><input id="announcementSearch" type="search" aria-label="搜索公告" placeholder="搜索标题或正文"><select id="announcementFilter" aria-label="筛选状态"><option value="all">全部状态</option><option value="active">发布中</option><option value="draft">草稿</option><option value="withdrawn">已撤下</option></select></div><div id="announcementList" class="announcement-list" style="margin-top:14px"></div></section></section>
30
+ <dialog id="announcementEditor" aria-labelledby="editorHeading"><div class="editor-head"><h2 id="editorHeading">新建公告</h2><button id="closeEditor" class="secondary" type="button" aria-label="关闭">关闭</button></div><div class="field"><label for="title">标题</label><input id="title" maxlength="80" placeholder="输入公告标题"></div><div class="field"><label for="message">正文</label><textarea id="message" maxlength="2000" placeholder="输入公告内容"></textarea></div><p id="editorNote" class="hint"></p><p id="editorError" class="status error" role="alert" hidden></p><div id="editorActions" class="editor-actions"><button id="saveDraft" class="secondary" type="button">保存草稿</button><button id="publish" type="button">发布公告</button></div></dialog>
31
+ <section id="settings" class="page stack" hidden><section class="card"><div class="card-head"><h2>服务器转发容量</h2><p class="hint">只限制经 WSS 转发的普通会话,WebRTC 直连与白名单转发不占用此额度</p></div><div class="field"><label for="limit">最大并发会话</label><input id="limit" type="number" min="1" max="256"></div><button id="saveLimit" type="button">保存设置</button><p class="note">当前上限是人工配置值,不随 CPU 或带宽自动改变。</p></section></section>
32
+ <section id="usage" class="page" hidden><section class="card"><div class="usage-toolbar"><div><h2>今日使用统计</h2><p id="usageCount" class="hint">正在读取</p></div><div class="usage-views"><button id="usageUsers" type="button" class="secondary" aria-pressed="true">用户</button><button id="usageComputers" type="button" class="secondary" aria-pressed="false">工作站</button><button id="usageRefresh" type="button" class="secondary">刷新</button></div></div><div id="usageRows" class="usage-rows"></div><div class="usage-pages"><button id="usagePrev" type="button" class="secondary">上一页</button><span id="usagePageText" class="hint"></span><button id="usageNext" type="button" class="secondary">下一页</button></div><p id="usageError" class="status error" role="alert"></p></section></section><p id="result" class="status" role="status" aria-live="polite"></p>
33
+ </main><footer class="admin-footer">网站备案号:京ICP备2026027643号-2</footer><script>
34
+ const el=id=>document.getElementById(id);let snapshot=null;
35
+ const request=async(path,options={})=>{const response=await fetch(path,{...options,referrerPolicy:'no-referrer'});if(response.status===401){location.replace('/');throw new Error('登录已过期,请重新登录')}const body=await response.json();if(!response.ok)throw new Error(body.error||('HTTP '+response.status));return body};
36
+ el('logout').addEventListener('click',async()=>{el('logout').disabled=true;try{await request('/admin/logout',{method:'POST',headers:{'X-Code-Relax-Admin':'1'}});location.replace('/')}catch(error){setResult(error.message,true)}finally{el('logout').disabled=false}});
37
+ function setResult(message,error=false){el('result').textContent=message;el('result').classList.toggle('error',error)}
38
+ function node(tag,className,value){const item=document.createElement(tag);if(className)item.className=className;if(value!==undefined)item.textContent=value;return item}
39
+ function metric(value,label,extra,tone){const item=node('div','metric'+(tone?' '+tone:''));item.append(node('strong','',value),node('span','',label));if(extra)item.append(node('small','',extra));return item}
40
+ function toneAbove(value,warn,critical){return typeof value==='number'&&Number.isFinite(value)?value>=critical?'critical':value>=warn?'warn':'':''}
41
+ function toneBelow(value,warn,critical){return typeof value==='number'&&Number.isFinite(value)?value<=critical?'critical':value<=warn?'warn':'':''}
42
+ function number(value,digits=0){return typeof value==='number'&&Number.isFinite(value)?value.toFixed(digits):'—'}
43
+ function date(value){return new Date(value).toLocaleString('zh-CN')}
44
+ function renderTrafficTimeline(cloud,available){const start=Date.parse(cloud.trafficStartAt);const end=Date.parse(cloud.trafficEndAt);const total=Number(cloud.trafficTotalBytes);const remaining=Number(cloud.trafficRemainingBytes);const valid=available&&Number.isFinite(start)&&Number.isFinite(end)&&end>start&&cloud.trafficTotalBytes!=null&&cloud.trafficRemainingBytes!=null&&total>0&&Number.isFinite(remaining);const timeline=el('trafficTimeline');timeline.hidden=!valid;if(!valid)return;
45
+ const clamp=value=>Math.max(0,Math.min(100,value));const quotaLeft=clamp(remaining/total*100);const timeLeft=clamp((end-Date.now())/(end-start)*100);const used=100-quotaLeft;const elapsed=100-timeLeft;const pace=elapsed>0?used/elapsed:null;
46
+ timeline.classList.toggle('ahead',pace!==null&&pace>1);timeline.classList.toggle('warn',quotaLeft<=20);timeline.classList.toggle('critical',quotaLeft<=10);el('trafficRemaining').textContent='流量剩余 '+number(quotaLeft,1)+'%';el('trafficRemaining').className=toneBelow(quotaLeft,20,10);el('timeRemaining').textContent='时间剩余 '+number(timeLeft,1)+'%';el('trafficPace').textContent=pace===null?'—×':number(pace,2)+'×';el('trafficPace').classList.toggle('ahead',pace!==null&&pace>1);
47
+ el('quotaFill').style.width=quotaLeft+'%';el('quotaNow').style.left=timeLeft+'%';el('quotaTrack').setAttribute('aria-label','流量剩余 '+number(quotaLeft,1)+'%,时间剩余 '+number(timeLeft,1)+'%');el('trafficStart').textContent='本期开始 '+date(cloud.trafficStartAt);el('trafficEnd').textContent='到期 '+date(cloud.trafficEndAt)}
48
+ function renderOverview(state){const cloud=state.cloud||{};const relay=state.relayAvailable!==false;const cloudFresh=cloud.metricAvailable&&Date.now()-Date.parse(cloud.metricSampledAt)<180000;const trafficFresh=cloud.trafficAvailable&&Date.now()-Date.parse(cloud.trafficSampledAt)<900000;const occupancy=state.maxRelaySessions?state.activeRelaySessions/state.maxRelaySessions:0;const high=occupancy>=.9||(cloudFresh&&(cloud.cpuPercent>=85||cloud.memoryPercent>=90||cloud.diskPercent>=90||cloud.outRatioPercent>=90));const caution=occupancy>=.75||(cloudFresh&&(cloud.cpuPercent>=65||cloud.memoryPercent>=80||cloud.diskPercent>=80||cloud.outRatioPercent>=70));const label=!relay?'中继不可用':high?'压力较高':!cloudFresh?'云监控数据过期':caution?'需要关注':'压力正常';
49
+ el('healthText').textContent=label;el('healthText').className='state'+(!relay||!cloudFresh&&!high?' down':high?' critical':caution?' warn':'');el('sampleTime').textContent=cloudFresh?'采样时间 '+new Date(cloud.metricSampledAt).toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'}):'等待采样';
50
+ el('throughput').replaceChildren(metric(cloudFresh?number(cloud.outMbps,2)+' Mbps':'—','公网发送'),metric(cloudFresh?number(cloud.inMbps,2)+' Mbps':'—','公网接收'));
51
+ el('serverMetrics').replaceChildren(metric(cloudFresh?number(cloud.cpuPercent,1)+'%':'—','CPU 使用率',null,cloudFresh?toneAbove(cloud.cpuPercent,65,85):''),metric(cloudFresh?number(cloud.memoryPercent,1)+'%':'—','内存使用率',null,cloudFresh?toneAbove(cloud.memoryPercent,80,90):''),metric(cloudFresh?number(cloud.diskPercent,1)+'%':'—','系统盘使用率',null,cloudFresh?toneAbove(cloud.diskPercent,80,90):''),metric(cloudFresh?number(cloud.cpuLoad1,2):'—','1 分钟负载'),metric(cloudFresh?number(cloud.outRatioPercent,1)+'%':'—','出口带宽使用率',null,cloudFresh?toneAbove(cloud.outRatioPercent,70,90):''));
52
+ const gb=value=>value==null?'—':number(Number(value)/1073741824,2)+' GB';
53
+ el('trafficUsage').textContent=trafficFresh?'已用 '+gb(cloud.trafficUsedBytes)+' / '+gb(cloud.trafficTotalBytes):'流量暂不可用';
54
+ el('trafficNote').textContent=trafficFresh?'更新时间 '+new Date(cloud.trafficSampledAt).toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'}):'暂不可用';
55
+ renderTrafficTimeline(cloud,trafficFresh);
56
+ el('metrics').replaceChildren(metric(number(state.activeRelaySessions)+' / '+number(state.maxRelaySessions),'受限转发',null,state.maxRelaySessions?toneAbove(state.activeRelaySessions/state.maxRelaySessions,.75,.9):''),metric(number(state.activeExemptRelaySessions),'白名单转发'),metric(number(state.websocketConnections),'WebSocket 连接'),metric(number(state.onlineComputers),'在线工作站'),metric(number(state.activePhones),'活动移动端'))}
57
+ let editorItem=null,editorMode='new',writing=false;
58
+ function openEditor(item=null,duplicate=false){editorItem=duplicate?null:item;editorMode=duplicate?'new':item?.status==='draft'?'draft':item?'view':'new';el('editorHeading').textContent=duplicate?'复制为新公告':editorMode==='draft'?'编辑草稿':editorMode==='view'?'查看公告':'新建公告';el('title').value=item?.title||'';el('message').value=item?.message||'';el('title').disabled=editorMode==='view';el('message').disabled=editorMode==='view';el('editorActions').hidden=editorMode==='view';el('editorNote').textContent=editorMode==='view'?'已发布内容只读;可复制为新公告再修改。':'发布后会立即替换当前公告并通知在线设备。';el('editorError').hidden=true;el('announcementEditor').showModal();if(editorMode!=='view')el('title').focus()}
59
+ function action(label,className,handler,disabled=false){const button=node('button',className,label);button.type='button';button.disabled=disabled;button.addEventListener('click',handler);return button}
60
+ function renderAnnouncements(state){const available=state.relayAvailable!==false;const items=state.announcements||[];const filter=el('announcementFilter').value;const query=el('announcementSearch').value.trim().toLocaleLowerCase();const visible=items.filter(item=>(filter==='all'||item.status===filter)&&(!query||(item.title+' '+item.message).toLocaleLowerCase().includes(query)));el('newAnnouncement').disabled=!available;el('announcementCount').textContent=available?'共 '+items.length+' 条 · 发布中 '+items.filter(item=>item.status==='active').length+' 条':'中继暂不可用';const list=el('announcementList');list.replaceChildren();if(!visible.length){list.append(node('div','announcement-empty',available?items.length?'没有符合条件的公告':'暂无公告,点击“新建公告”开始':'暂时无法读取公告'));return}
61
+ for(const item of visible){const row=node('article','announcement-row'+(item.status==='active'?' active':''));const summary=node('div');summary.append(node('h3','',item.title),node('p','',item.message));const status=node('span','announcement-status '+item.status,{active:'发布中',draft:'草稿',withdrawn:'已撤下'}[item.status]||'未知');const when=node('span','date',(item.status==='draft'?'修改 ':'发布 ')+date(item.status==='draft'?item.updatedAt:item.publishedAt));const actions=node('div','actions');const path='/admin/api/announcements/'+encodeURIComponent(item.id);
62
+ if(item.status==='draft'){actions.append(action('编辑','secondary',()=>openEditor(item)),action('发布','',()=>{if(!snapshot.announcement||confirm('发布后将替换当前公告,确定发布?'))write('PUT',path+'/publish',{},'公告已发布')},!available),action('删除','danger',()=>{if(confirm('确定删除这条草稿?'))write('DELETE',path,{},'草稿已删除')},!available))}
63
+ else if(item.status==='active'){actions.append(action('查看','secondary',()=>openEditor(item)),action('撤下','danger',()=>{if(confirm('确定撤下当前公告?'))write('PUT',path+'/withdraw',{},'公告已撤下')},!available))}
64
+ else{actions.append(action('查看','secondary',()=>openEditor(item)),action('复制','secondary',()=>openEditor(item,true),!available),action('删除','danger',()=>{if(confirm('确定删除这条已撤下的公告?'))write('DELETE',path,{},'公告记录已删除')},!available))}
65
+ row.append(summary,status,when,actions);list.append(row)}}
66
+ function render(state){snapshot=state;renderOverview(state);renderAnnouncements(state);el('saveLimit').disabled=state.relayAvailable===false;if(document.activeElement!==el('limit'))el('limit').value=state.maxRelaySessions??''}
67
+ async function load(){try{render(await request('/admin/api/status'));setResult('')}catch(error){snapshot=null;el('healthText').textContent='状态暂不可用';el('healthText').className='state down';for(const id of ['metrics','serverMetrics','throughput','announcementList'])el(id).replaceChildren();el('trafficTimeline').hidden=true;el('trafficUsage').textContent='—';el('announcementCount').textContent='暂时无法读取';el('saveLimit').disabled=true;setResult(error.message,true)}}
68
+ async function write(method,path,body,message){if(writing)return false;writing=true;try{await request(path,{method,headers:{'Content-Type':'application/json','X-Code-Relax-Admin':'1'},body:JSON.stringify(body)});await load();setResult(message);return true}catch(error){setResult(error.message,true);if(el('announcementEditor').open){el('editorError').textContent=error.message;el('editorError').hidden=false}return false}finally{writing=false}}
69
+ let usageView='user',usagePage=1,usageTotal=0;
70
+ function usageDuration(value){const seconds=Math.floor(Number(value)||0),hours=Math.floor(seconds/3600),minutes=Math.floor(seconds%3600/60),rest=seconds%60;return hours?hours+' 小时 '+minutes+' 分钟':minutes?minutes+' 分钟 '+rest+' 秒':rest+' 秒'}
71
+ function usageTime(value){return value?new Date(value.replace(' ','T')+'Z').toLocaleTimeString('zh-CN',{timeZone:'Asia/Shanghai',hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false}):'—'}
72
+ async function loadUsage(){const view=usageView,page=usagePage;el('usageError').textContent='';el('usageCount').textContent='正在读取';try{const result=await request('/admin/api/usage?view='+view+'&page='+page);if(view!==usageView||page!==usagePage||el('usage').hidden)return;usageTotal=result.total;el('usageCount').textContent='今日有记录:'+usageTotal+(view==='user'?' 个用户':' 台工作站')+' · 北京时间';el('usagePageText').textContent='第 '+page+' / '+Math.max(1,Math.ceil(usageTotal/30))+' 页';el('usagePrev').disabled=page<=1;el('usageNext').disabled=page*30>=usageTotal;const rows=el('usageRows');rows.replaceChildren();if(!result.rows.length){rows.append(node('div','empty-note','今日暂无计时记录'));return}for(const item of result.rows){const row=node('div','usage-row');const counts=' · 消息 '+Number(item.message_count||0)+' · 轮次 '+Number(item.turn_count||0);const heading=node('div','usage-row-head');const title=node('span','',view==='user'?item.email:'工作站 …'+item.id.slice(-8));title.title=view==='user'?item.email:item.id;heading.append(title,node('small','hint','最后使用 '+usageTime(item.last_used_at)));row.append(heading,node('strong','',view==='user'?'任务 '+usageDuration(item.today_task_seconds)+counts:'任务 '+usageDuration(item.today_task_seconds)+' · 免费账号 '+usageDuration(item.today_free_task_seconds)+counts));rows.append(row)}}catch(error){if(view===usageView&&page===usagePage){usageTotal=0;el('usageCount').textContent='统计暂不可用';el('usageRows').replaceChildren();el('usagePageText').textContent='';el('usagePrev').disabled=true;el('usageNext').disabled=true;el('usageError').textContent=error.message}}}
73
+ function setUsageView(view){if(usageView===view)return;usageView=view;usagePage=1;el('usageUsers').setAttribute('aria-pressed',String(view==='user'));el('usageComputers').setAttribute('aria-pressed',String(view==='computer'));void loadUsage()}
74
+ el('usageUsers').onclick=()=>setUsageView('user');el('usageComputers').onclick=()=>setUsageView('computer');el('usageRefresh').onclick=()=>loadUsage();el('usagePrev').onclick=()=>{if(usagePage>1){usagePage--;void loadUsage()}};el('usageNext').onclick=()=>{if(usagePage*30<usageTotal){usagePage++;void loadUsage()}};
75
+ function showPage(route){cancelWorkstations();const detail=route.startsWith('workstations/')&&/^[a-zA-Z0-9_-]{16,128}$/.test(route.slice(13))?[route,route.slice(13)]:null;const page=detail?'workstations':['overview','announcements','settings','usage','workstations'].includes(route)?route:'overview';workstationId=detail?detail[1]:null;for(const button of document.querySelectorAll('[data-page]'))button.setAttribute('aria-current',button.dataset.page===page?'page':'false');for(const section of document.querySelectorAll('.page'))section.hidden=section.id!==page;if(page==='workstations'){el('workstationBack').hidden=!workstationId;el('workstationHeading').textContent=workstationId?'工作站详情':'工作站';el('workstationCount').textContent='正在读取';el('workstationContent').replaceChildren();el('workstationError').textContent='';void loadWorkstations()}else if(page==='usage')void loadUsage();else void load()}
76
+ function navigatePage(page){history.pushState(null,'','#'+page);showPage(page)}
77
+ for(const button of document.querySelectorAll('[data-page]'))button.addEventListener('click',()=>navigatePage(button.dataset.page));
78
+ window.addEventListener('popstate',()=>showPage(location.hash.slice(1)));
79
+ el('saveLimit').addEventListener('click',()=>write('PUT','/admin/api/settings',{maxRelaySessions:Number(el('limit').value)},'转发容量已保存'));
80
+ el('newAnnouncement').addEventListener('click',()=>openEditor());el('closeEditor').addEventListener('click',()=>el('announcementEditor').close());el('announcementSearch').addEventListener('input',()=>{if(snapshot)renderAnnouncements(snapshot)});el('announcementFilter').addEventListener('change',()=>{if(snapshot)renderAnnouncements(snapshot)});
81
+ async function saveAnnouncement(publish){const title=el('title').value.trim(),message=el('message').value.trim();if(!title||!message){el('editorError').textContent='请填写标题和正文';el('editorError').hidden=false;return}if(publish&&snapshot?.announcement&&snapshot.announcement.id!==editorItem?.id&&!confirm('发布后将替换当前公告,确定发布?'))return;const path=editorMode==='draft'?'/admin/api/announcements/'+encodeURIComponent(editorItem.id):'/admin/api/announcements';const target=publish&&editorMode==='draft'?path+'/publish':path;const body={title,message};if(publish&&editorMode==='new')body.publish=true;const ok=await write('PUT',target,body,publish?'公告已发布':'草稿已保存');if(ok)el('announcementEditor').close()}
82
+ el('saveDraft').addEventListener('click',()=>saveAnnouncement(false));el('publish').addEventListener('click',()=>saveAnnouncement(true));
83
+ ${adminWorkstationsScript}
84
+ showPage(location.hash.slice(1));setInterval(()=>{if(document.hidden)return;if(!el('overview').hidden)void load();else if(!el('workstations').hidden)void loadWorkstations()},5000);
85
+ document.addEventListener('visibilitychange',()=>{if(document.hidden)cancelWorkstations();else if(!el('workstations').hidden)void loadWorkstations()});
86
+ </script></body></html>`;
87
+ }
@@ -0,0 +1,105 @@
1
+ import http from 'node:http';
2
+ import fs from 'node:fs';
3
+ import { adminPage } from './admin-page.mjs';
4
+ import { startAdminCloud } from './admin-cloud.mjs';
5
+ import { MAX_ADMIN_REQUEST_BYTES } from './admin-state.mjs';
6
+ import { createAdminAuth } from './admin-auth.mjs';
7
+ import { adminLoginPage } from './admin-login-page.mjs';
8
+ import endpoints from '../../shared/service-endpoints.cjs';
9
+
10
+ function reply(response, status, body, type = 'application/json; charset=utf-8') {
11
+ const bytes = typeof body === 'string' ? body : JSON.stringify(body);
12
+ response.writeHead(status, { 'Content-Type': type, 'Content-Length': Buffer.byteLength(bytes),
13
+ 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff',
14
+ 'Referrer-Policy': 'no-referrer', 'X-Robots-Tag': 'noindex, nofollow', 'X-Frame-Options': 'DENY' });
15
+ response.end(bytes);
16
+ }
17
+
18
+ async function readBody(request) {
19
+ const chunks = [];
20
+ let size = 0;
21
+ for await (const chunk of request) {
22
+ size += chunk.length;
23
+ if (size > MAX_ADMIN_REQUEST_BYTES) throw Object.assign(new Error('Admin request too large'), { status: 413 });
24
+ chunks.push(chunk);
25
+ }
26
+ return Buffer.concat(chunks);
27
+ }
28
+
29
+ export async function startAdminServer({ port = 45851, relayUrl = 'http://127.0.0.1:45850', accessFile = '/etc/codex-relay/admin-access.json',
30
+ passwordFile = '/etc/codex-relay/admin-password.json', publicOrigin = endpoints.adminOrigin } = {}) {
31
+ const { key } = JSON.parse(fs.readFileSync(accessFile, 'utf8'));
32
+ if (!/^[A-Za-z0-9_-]{43}$/.test(key)) throw new Error('Invalid admin access key');
33
+ const auth = createAdminAuth(passwordFile);
34
+ const cloud = startAdminCloud();
35
+ const relayOrigin = new URL(relayUrl);
36
+ if (relayOrigin.hostname !== '127.0.0.1' || relayOrigin.protocol !== 'http:' || relayOrigin.pathname !== '/') {
37
+ cloud.close(); throw new Error('Admin relay endpoint must be loopback HTTP');
38
+ }
39
+ const server = http.createServer(async (request, response) => {
40
+ const target = new URL(request.url || '/', 'http://localhost');
41
+ if (request.method === 'GET' && target.pathname === '/admin/health') { reply(response, 200, { ok: true }); return; }
42
+ if (!['GET', 'HEAD'].includes(request.method) && (request.headers.origin !== publicOrigin
43
+ || request.headers['sec-fetch-site'] === 'cross-site' || request.headers['x-code-relax-admin'] !== '1')) {
44
+ reply(response, 403, { error: 'Cross-site request rejected' }); return;
45
+ }
46
+ if (request.method === 'GET' && target.pathname === '/') {
47
+ reply(response, 200, auth.authenticated(request) ? adminPage() : adminLoginPage(), 'text/html; charset=utf-8'); return;
48
+ }
49
+ if (request.method === 'GET' && ['/admin', '/admin/'].includes(target.pathname)) {
50
+ response.writeHead(303, { Location: '/', 'Cache-Control': 'no-store' }); response.end(); return;
51
+ }
52
+ if (request.method === 'POST' && target.pathname === '/admin/login') {
53
+ try {
54
+ if (request.headers['content-type']?.split(';')[0] !== 'application/json') {
55
+ reply(response, 415, { error: 'Expected application/json' }); return;
56
+ }
57
+ const { password } = JSON.parse((await readBody(request)).toString('utf8'));
58
+ const result = await auth.login(password, request, response);
59
+ reply(response, result.status, result.error ? { error: result.error } : { ok: true });
60
+ } catch (error) {
61
+ const status = error.status || (error instanceof SyntaxError || error instanceof TypeError ? 400 : 500);
62
+ if (status === 500) console.error('Admin login failed:', error.message);
63
+ reply(response, status, { error: status === 500 ? '登录服务暂不可用' : '登录请求无效' });
64
+ }
65
+ return;
66
+ }
67
+ if (request.method === 'POST' && target.pathname === '/admin/logout') {
68
+ auth.logout(request, response); reply(response, 200, { ok: true }); return;
69
+ }
70
+ if (!target.pathname.startsWith('/admin/api/')) { reply(response, 404, { error: 'Not found' }); return; }
71
+ if (!auth.authenticated(request)) { reply(response, 401, { error: '登录已过期,请重新登录' }); return; }
72
+ if (request.method === 'GET' && (['/admin/api/status', '/admin/api/usage', '/admin/api/usage-diagnostics',
73
+ '/admin/api/workstations'].includes(target.pathname) || /^\/admin\/api\/workstations\/[\w-]{16,128}$/.test(target.pathname))) {
74
+ try {
75
+ const upstream = await fetch(new URL(target.pathname + target.search, relayOrigin), {
76
+ signal: AbortSignal.timeout(3000), headers: { 'X-Code-Relax-Admin-Key': key } });
77
+ const state = await upstream.json();
78
+ if (!upstream.ok) { reply(response, upstream.status, state); return; }
79
+ reply(response, 200, target.pathname === '/admin/api/status'
80
+ ? { ...state, relayAvailable: true, cloud: cloud.read() } : state);
81
+ } catch {
82
+ reply(response, 503, { error: '中继服务暂不可用' });
83
+ }
84
+ return;
85
+ }
86
+ if (!target.pathname.startsWith('/admin/api/') || !['PUT', 'DELETE', 'POST'].includes(request.method || '')) {
87
+ reply(response, 404, { error: 'Not found' }); return;
88
+ }
89
+ try {
90
+ const body = await readBody(request);
91
+ const upstream = await fetch(new URL(target.pathname + target.search, relayOrigin), {
92
+ method: request.method, signal: AbortSignal.timeout(3000),
93
+ headers: { 'Content-Type': request.headers['content-type'] || '',
94
+ 'X-Code-Relax-Admin': request.headers['x-code-relax-admin'] || '',
95
+ 'X-Code-Relax-Admin-Key': key },
96
+ body,
97
+ });
98
+ reply(response, upstream.status, await upstream.text());
99
+ } catch (error) { reply(response, error.status || 503, { error: error.status === 413 ? error.message : '中继服务暂不可用' }); }
100
+ });
101
+ try {
102
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port, '127.0.0.1', resolve); });
103
+ } catch (error) { cloud.close(); throw error; }
104
+ return { port: server.address().port, async close() { cloud.close(); await new Promise(resolve => server.close(resolve)); } };
105
+ }
@@ -2,19 +2,23 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
 
5
- export function relayAdminState(file, initialMaxRelaySessions) {
6
- let value = { version: 1, maxRelaySessions: initialMaxRelaySessions, announcement: null };
5
+ // 80 title + 2000 body UTF-16 units fit even when every unit uses a six-byte JSON escape.
6
+ export const MAX_ADMIN_REQUEST_BYTES = 16 * 1024;
7
+
8
+ export function relayAdminState(file, initialMaxRelaySessions, { initial, persist } = {}) {
9
+ let value = initial ? validate(initial) : { version: 3, maxRelaySessions: initialMaxRelaySessions, announcement: null, announcements: [] };
7
10
  if (file && fs.existsSync(file)) value = validate(JSON.parse(fs.readFileSync(file, 'utf8')));
8
11
 
9
- function save(next) {
10
- value = validate(next);
11
- if (!file) return value;
12
+ async function save(next) {
13
+ const validated = validate(next);
14
+ if (persist) await persist(validated);
15
+ if (!file) { value = validated; return value; }
12
16
  fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
13
17
  const temporary = `${file}.${randomUUID()}.tmp`;
14
18
  let descriptor;
15
19
  try {
16
20
  descriptor = fs.openSync(temporary, 'wx', 0o600);
17
- fs.writeFileSync(descriptor, JSON.stringify(value));
21
+ fs.writeFileSync(descriptor, JSON.stringify(validated));
18
22
  fs.fsyncSync(descriptor); fs.closeSync(descriptor); descriptor = undefined;
19
23
  fs.renameSync(temporary, file);
20
24
  if (process.platform !== 'win32') {
@@ -25,32 +29,94 @@ export function relayAdminState(file, initialMaxRelaySessions) {
25
29
  if (descriptor !== undefined) fs.closeSync(descriptor);
26
30
  fs.rmSync(temporary, { force: true });
27
31
  }
32
+ value = validated;
28
33
  return value;
29
34
  }
30
35
 
31
36
  return {
32
37
  read: () => structuredClone(value),
33
38
  setMaxRelaySessions(maxRelaySessions) { return save({ ...value, maxRelaySessions }); },
34
- publish(title, message) {
35
- return save({ ...value, announcement: { id: randomUUID(), title: clean(title, 80), message: clean(message, 2000),
36
- publishedAt: new Date().toISOString() } });
39
+ createAnnouncement(title, message, publish = false) {
40
+ const now = new Date().toISOString();
41
+ const record = { id: randomUUID(), title: clean(title, 80), message: clean(message, 2000),
42
+ createdAt: now, updatedAt: now, publishedAt: publish ? now : null, status: publish ? 'active' : 'draft' };
43
+ const announcements = value.announcements.map(item => publish && item.status === 'active'
44
+ ? { ...item, status: 'withdrawn', updatedAt: now } : item);
45
+ return save({ ...value, announcement: publish ? publicAnnouncement(record) : value.announcement,
46
+ announcements: [record, ...announcements] });
47
+ },
48
+ updateDraft(id, title, message) {
49
+ const existing = value.announcements.find(item => item.id === id);
50
+ if (!existing || existing.status !== 'draft') throw new Error('Only drafts can be edited');
51
+ return save({ ...value, announcements: value.announcements.map(item => item.id === id
52
+ ? { ...item, title: clean(title, 80), message: clean(message, 2000), updatedAt: new Date().toISOString() } : item) });
53
+ },
54
+ publishDraft(id, title, message) {
55
+ const existing = value.announcements.find(item => item.id === id);
56
+ if (!existing || existing.status !== 'draft') throw new Error('Only drafts can be published');
57
+ const now = new Date().toISOString();
58
+ const published = { ...existing, title: title === undefined ? existing.title : clean(title, 80),
59
+ message: message === undefined ? existing.message : clean(message, 2000),
60
+ publishedAt: now, updatedAt: now, status: 'active' };
61
+ return save({ ...value, announcement: publicAnnouncement(published),
62
+ announcements: value.announcements.map(item => item.id === id ? published : item.status === 'active'
63
+ ? { ...item, status: 'withdrawn', updatedAt: now } : item) });
64
+ },
65
+ withdraw(id) {
66
+ if (!value.announcement || value.announcement.id !== id) throw new Error('Announcement is no longer active');
67
+ const now = new Date().toISOString();
68
+ return save({ ...value, announcement: null, announcements: value.announcements.map(item => item.status === 'active'
69
+ ? { ...item, status: 'withdrawn', updatedAt: now } : item) });
70
+ },
71
+ deleteAnnouncement(id) {
72
+ if (typeof id !== 'string' || !value.announcements.some(item => item.id === id)) throw new Error('Announcement not found');
73
+ if (value.announcement?.id === id) throw new Error('Withdraw the current announcement before deleting it');
74
+ return save({ ...value, announcements: value.announcements.filter(item => item.id !== id) });
37
75
  },
38
- withdraw() { return save({ ...value, announcement: null }); },
39
76
  };
40
77
  }
41
78
 
42
79
  function validate(source) {
43
- if (!source || source.version !== 1 || !Number.isSafeInteger(source.maxRelaySessions) ||
80
+ if (!source || ![1, 2, 3].includes(source.version) || !Number.isSafeInteger(source.maxRelaySessions) ||
44
81
  source.maxRelaySessions < 1 || source.maxRelaySessions > 256) throw new Error('Invalid relay admin state');
45
- let announcement = null;
46
- if (source.announcement !== null && source.announcement !== undefined) {
47
- const item = source.announcement;
48
- if (!item || !/^[\w-]{16,128}$/.test(item.id) || !Number.isFinite(Date.parse(item.publishedAt))) {
49
- throw new Error('Invalid relay announcement');
50
- }
51
- announcement = { id: item.id, title: clean(item.title, 80), message: clean(item.message, 2000), publishedAt: item.publishedAt };
82
+ const announcement = source.announcement == null ? null : validateAnnouncement(source.announcement);
83
+ const announcements = source.version === 1 ? (announcement ? [announcement] : []) : source.announcements;
84
+ if (!Array.isArray(announcements)) throw new Error('Invalid relay announcement history');
85
+ const validated = source.version === 3 ? announcements.map(validateRecord) : announcements.map(item => {
86
+ const old = validateAnnouncement(item);
87
+ return { ...old, createdAt: old.publishedAt, updatedAt: old.publishedAt,
88
+ status: old.id === announcement?.id ? 'active' : 'withdrawn' };
89
+ });
90
+ const active = validated.filter(item => item.status === 'active');
91
+ if (new Set(validated.map(item => item.id)).size !== validated.length || active.length > 1 ||
92
+ (active.length === 0) !== (announcement === null) ||
93
+ (announcement && !active.some(item => item.id === announcement.id &&
94
+ item.title === announcement.title && item.message === announcement.message && item.publishedAt === announcement.publishedAt))) {
95
+ throw new Error('Invalid relay announcement history');
52
96
  }
53
- return { version: 1, maxRelaySessions: source.maxRelaySessions, announcement };
97
+ return { version: 3, maxRelaySessions: source.maxRelaySessions, announcement, announcements: validated };
98
+ }
99
+
100
+ function validateAnnouncement(item) {
101
+ if (!item || !/^[\w-]{16,128}$/.test(item.id) || typeof item.publishedAt !== 'string' ||
102
+ !Number.isFinite(Date.parse(item.publishedAt))) throw new Error('Invalid relay announcement');
103
+ return { id: item.id, title: clean(item.title, 80), message: clean(item.message, 2000), publishedAt: item.publishedAt };
104
+ }
105
+
106
+ function validateRecord(item) {
107
+ if (!item || !/^[\w-]{16,128}$/.test(item.id) || !['draft', 'active', 'withdrawn'].includes(item.status) ||
108
+ typeof item.createdAt !== 'string' || !Number.isFinite(Date.parse(item.createdAt)) ||
109
+ typeof item.updatedAt !== 'string' || !Number.isFinite(Date.parse(item.updatedAt)) ||
110
+ (item.status === 'draft' ? item.publishedAt !== null :
111
+ typeof item.publishedAt !== 'string' || !Number.isFinite(Date.parse(item.publishedAt)))) {
112
+ throw new Error('Invalid relay announcement record');
113
+ }
114
+ return { id: item.id, title: clean(item.title, 80), message: clean(item.message, 2000),
115
+ createdAt: item.createdAt, updatedAt: item.updatedAt, publishedAt: item.publishedAt, status: item.status };
116
+ }
117
+
118
+ function publicAnnouncement(record) {
119
+ return { id: record.id, title: record.title, message: record.message, publishedAt: record.publishedAt };
54
120
  }
55
121
 
56
122
  function clean(value, limit) {