@walkhi/code-relax 0.1.0-beta.10 → 0.1.0-beta.101

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 (851) hide show
  1. package/README.md +67 -44
  2. package/dist/bin/cleanup-accounts.mjs +63 -0
  3. package/dist/bin/codex-remote.mjs +387 -265
  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-demo.mjs +1 -1
  10. package/dist/bin/self-relay-server.mjs +43 -30
  11. package/dist/bin/set-relay-admin-password.mjs +11 -0
  12. package/dist/build-platform.json +1 -0
  13. package/dist/get-shared-service-url.ps1 +12 -4
  14. package/dist/shared/app-server-events.cjs +452 -235
  15. package/dist/shared/code-browser.cjs +159 -0
  16. package/dist/shared/file-changes.cjs +31 -0
  17. package/dist/shared/file-citations.cjs +117 -0
  18. package/dist/shared/file-mentions.cjs +92 -0
  19. package/dist/shared/message-delivery.cjs +115 -0
  20. package/dist/shared/p2p-data-channel.cjs +22 -0
  21. package/dist/shared/p2p-diagnostics.cjs +1 -1
  22. package/dist/shared/p2p-file-channel.cjs +87 -0
  23. package/dist/shared/p2p-ice-policy.cjs +22 -0
  24. package/dist/shared/plan-pricing.cjs +32 -0
  25. package/dist/shared/question-attention.cjs +100 -0
  26. package/dist/shared/release-version.cjs +31 -0
  27. package/dist/shared/server-requests.cjs +347 -67
  28. package/dist/shared/service-endpoints.cjs +21 -0
  29. package/dist/shared/timeline-sync.cjs +173 -67
  30. package/dist/skills/code-relax/SKILL.md +22 -17
  31. package/dist/src/archived-threads.mjs +22 -0
  32. package/dist/src/bounded-process-log.mjs +63 -0
  33. package/dist/src/browser-auth-operations.mjs +46 -0
  34. package/dist/src/celsius-client.mjs +107 -0
  35. package/dist/src/code-browser.mjs +99 -0
  36. package/dist/src/desktop-launcher.mjs +58 -36
  37. package/dist/src/desktop-operation.mjs +46 -0
  38. package/dist/src/dots-approval-timeline.mjs +99 -0
  39. package/dist/src/dots-avatar.mjs +42 -0
  40. package/dist/src/dots-chat-projection.mjs +50 -0
  41. package/dist/src/dots-chat-server.mjs +623 -0
  42. package/dist/src/dots-message-images.mjs +318 -0
  43. package/dist/src/dots-profile.mjs +77 -0
  44. package/dist/src/dots-room-activity.mjs +199 -0
  45. package/dist/src/dots-room-client.mjs +163 -0
  46. package/dist/src/dots-room-projection.mjs +94 -0
  47. package/dist/src/dots-schedules.mjs +66 -0
  48. package/dist/src/dots-summary-tasks.mjs +32 -0
  49. package/dist/src/dots-task-catalog.mjs +433 -0
  50. package/dist/src/dots-task-files.mjs +52 -0
  51. package/dist/src/file-transfer-range.mjs +22 -0
  52. package/dist/src/file-upload-session.mjs +151 -0
  53. package/dist/src/hosted-history-app-server.mjs +103 -0
  54. package/dist/src/hosted-history-cache.mjs +113 -0
  55. package/dist/src/lan-socket-server.mjs +271 -38
  56. package/dist/src/managed-app-server.mjs +398 -227
  57. package/dist/src/managed-endpoint.mjs +25 -0
  58. package/dist/src/message-upload-files.mjs +111 -0
  59. package/dist/src/onboarding.mjs +9 -26
  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 +31 -0
  82. package/dist/src/platform/macos/codex-runtime.mjs +121 -0
  83. package/dist/src/platform/macos/desktop-host.mjs +114 -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 +31 -0
  92. package/dist/src/platform/managed-user-environment.mjs +78 -0
  93. package/dist/src/platform/native-package.mjs +69 -0
  94. package/dist/src/platform/network-info.mjs +3 -0
  95. package/dist/src/platform/path-identity.mjs +5 -0
  96. package/dist/src/platform/posix/network-candidates.mjs +23 -0
  97. package/dist/src/platform/posix/runtime-paths.mjs +25 -0
  98. package/dist/src/platform/power-inhibitor.mjs +22 -0
  99. package/dist/src/platform/project-locations.mjs +3 -0
  100. package/dist/src/platform/runtime-paths.mjs +8 -0
  101. package/dist/src/platform/select.mjs +5 -0
  102. package/dist/src/platform/service-host.mjs +3 -0
  103. package/dist/src/platform/user-environment.mjs +3 -0
  104. package/dist/src/platform/windows/activate-desktop.ps1 +52 -0
  105. package/dist/src/platform/windows/codex-runtime.mjs +215 -107
  106. package/dist/src/platform/windows/desktop-browser-environment.mjs +38 -0
  107. package/dist/src/platform/windows/desktop-helper.mjs +272 -0
  108. package/dist/src/platform/windows/desktop-host.mjs +47 -33
  109. package/dist/src/platform/windows/desktop-info.ps1 +27 -3
  110. package/dist/src/platform/windows/desktop-project-registration.mjs +4 -0
  111. package/dist/src/platform/windows/desktop-runtime.ps1 +10 -0
  112. package/dist/src/platform/windows/desktop-shortcuts.ps1 +6 -13
  113. package/dist/src/platform/windows/dots-profile.mjs +49 -0
  114. package/dist/src/platform/windows/keep-awake.ps1 +20 -0
  115. package/dist/src/platform/windows/launch-worker.mjs +41 -42
  116. package/dist/src/platform/windows/project-locations.mjs +16 -0
  117. package/dist/src/platform/windows/resident-start.js +20 -0
  118. package/dist/src/platform/windows/resident-startup.ps1 +27 -11
  119. package/dist/src/platform/windows/rustdesk-video-probe.mjs +168 -0
  120. package/dist/src/platform/windows/service-host.mjs +17 -22
  121. package/dist/src/platform/windows/start-hidden-console.ps1 +11 -2
  122. package/dist/src/platform/windows/user-environment.mjs +56 -50
  123. package/dist/src/platform/windows/user-environment.ps1 +4 -3
  124. package/dist/src/project-create.mjs +121 -0
  125. package/dist/src/project-management.mjs +59 -0
  126. package/dist/src/project-remove.mjs +55 -0
  127. package/dist/src/project-update.mjs +59 -0
  128. package/dist/src/relay-config-import.mjs +45 -0
  129. package/dist/src/remote-control.mjs +51 -0
  130. package/dist/src/runtime-discovery.mjs +65 -41
  131. package/dist/src/self-relay/account-cleanup.mjs +30 -0
  132. package/dist/src/self-relay/account-connections.mjs +63 -0
  133. package/dist/src/self-relay/admin-auth.mjs +59 -0
  134. package/dist/src/self-relay/admin-login-page.mjs +7 -0
  135. package/dist/src/self-relay/admin-page.mjs +87 -71
  136. package/dist/src/self-relay/admin-server.mjs +68 -24
  137. package/dist/src/self-relay/admin-state.mjs +11 -6
  138. package/dist/src/self-relay/admin-style.mjs +58 -0
  139. package/dist/src/self-relay/admin-workstations-page.mjs +95 -0
  140. package/dist/src/self-relay/admin-workstations.mjs +145 -0
  141. package/dist/src/self-relay/android-releases.mjs +61 -0
  142. package/dist/src/self-relay/client.mjs +45 -45
  143. package/dist/src/self-relay/connection-reporting.mjs +38 -0
  144. package/dist/src/self-relay/connector.mjs +456 -145
  145. package/dist/src/self-relay/demo.mjs +1 -1
  146. package/dist/src/self-relay/device-info.mjs +27 -0
  147. package/dist/src/self-relay/device-store.mjs +10 -2
  148. package/dist/src/self-relay/diagnostics.mjs +54 -0
  149. package/dist/src/self-relay/email-accounts.mjs +195 -0
  150. package/dist/src/self-relay/file-wire.mjs +76 -0
  151. package/dist/src/self-relay/lan-authorizations.mjs +44 -2
  152. package/dist/src/self-relay/lifecycle.mjs +252 -259
  153. package/dist/src/self-relay/long-pairing.mjs +40 -0
  154. package/dist/src/self-relay/mysql-state.mjs +118 -0
  155. package/dist/src/self-relay/p2p-probe.mjs +168 -123
  156. package/dist/src/self-relay/phone-connections.mjs +55 -0
  157. package/dist/src/self-relay/privacy-page.mjs +26 -0
  158. package/dist/src/self-relay/public-releases.mjs +67 -0
  159. package/dist/src/self-relay/rtc-file-bridge.mjs +46 -0
  160. package/dist/src/self-relay/server.mjs +1078 -441
  161. package/dist/src/self-relay/usage-daily.mjs +55 -0
  162. package/dist/src/self-relay/usage-meter.mjs +141 -0
  163. package/dist/src/server.mjs +1442 -716
  164. package/dist/src/service-control.mjs +17 -7
  165. package/dist/src/service-doctor.mjs +150 -51
  166. package/dist/src/service-health.mjs +2 -2
  167. package/dist/src/service-install.mjs +16 -10
  168. package/dist/src/service-lifecycle.mjs +52 -42
  169. package/dist/src/service-power.mjs +58 -0
  170. package/dist/src/service-restart.mjs +45 -12
  171. package/dist/src/service-update-worker.mjs +42 -0
  172. package/dist/src/service-update.mjs +80 -0
  173. package/dist/src/shared-app-server.mjs +511 -307
  174. package/dist/src/shared-catalog.mjs +132 -50
  175. package/dist/src/shared-recovery.mjs +21 -13
  176. package/dist/src/shared-thread-stream.mjs +143 -51
  177. package/dist/src/skill-install.mjs +0 -13
  178. package/dist/src/thread-goals.mjs +40 -0
  179. package/dist/src/turn-model-history.mjs +47 -0
  180. package/dist/src/usage-reset.mjs +17 -0
  181. package/dist/src/workstation-downloads.mjs +120 -0
  182. package/package.json +48 -48
  183. package/tools/postinstall.mjs +186 -146
  184. package/dist/src/debug-ui.mjs +0 -131
  185. package/dist/src/platform/windows/credential-store.mjs +0 -27
  186. package/dist/src/platform/windows/credential-store.ps1 +0 -49
  187. package/dist/src/platform/windows/desktop-shortcut-run.ps1 +0 -5
  188. package/dist/web/activity-view.js +0 -283
  189. package/dist/web/approval-ui.js +0 -188
  190. package/dist/web/capabilities.js +0 -50
  191. package/dist/web/chat-transport.js +0 -196
  192. package/dist/web/chat.css +0 -778
  193. package/dist/web/chat.js +0 -3815
  194. package/dist/web/client-platform.js +0 -11
  195. package/dist/web/community.css +0 -82
  196. package/dist/web/community.html +0 -36
  197. package/dist/web/composer-controller.js +0 -97
  198. package/dist/web/conversation-controller.js +0 -99
  199. package/dist/web/disclosure-state-controller.js +0 -95
  200. package/dist/web/draft-controller.js +0 -103
  201. package/dist/web/harmony-platform.js +0 -52
  202. package/dist/web/highlight-powershell.min.js +0 -39
  203. package/dist/web/highlight.min.js +0 -1262
  204. package/dist/web/history-cache.js +0 -240
  205. package/dist/web/history-controller.js +0 -167
  206. package/dist/web/index.html +0 -288
  207. package/dist/web/lan-session.js +0 -122
  208. package/dist/web/link-action-controller.js +0 -212
  209. package/dist/web/message-send-controller.js +0 -177
  210. package/dist/web/message-view.js +0 -98
  211. package/dist/web/p2p-data-channel.js +0 -50
  212. package/dist/web/p2p-diagnostics.js +0 -28
  213. package/dist/web/p2p-probe.js +0 -123
  214. package/dist/web/page-resume.js +0 -28
  215. package/dist/web/pending-message-store.js +0 -108
  216. package/dist/web/queue-controller.js +0 -82
  217. package/dist/web/resources.json +0 -1
  218. package/dist/web/self-relay-session.js +0 -129
  219. package/dist/web/server-requests.js +0 -67
  220. package/dist/web/station-connection-controller.js +0 -75
  221. package/dist/web/task-list-view.js +0 -289
  222. package/dist/web/thread-attention-controller.js +0 -124
  223. package/dist/web/thread-context-controller.js +0 -30
  224. package/dist/web/thread-list-controller.js +0 -61
  225. package/dist/web/thread-list-sync.js +0 -86
  226. package/dist/web/thread-title-controller.js +0 -57
  227. package/dist/web/timeline-formatters.js +0 -249
  228. package/dist/web/timeline-reducer.js +0 -77
  229. package/dist/web/timeline-renderer.js +0 -161
  230. package/dist/web/timeline-scroll-controller.js +0 -50
  231. package/dist/web/timeline-sync.js +0 -67
  232. package/dist/web/usage-controller.js +0 -258
  233. package/dist/web/vendor/README.md +0 -14
  234. package/dist/web/vendor/highlight-powershell.min.js +0 -39
  235. package/dist/web/vendor/highlight.LICENSE.txt +0 -29
  236. package/dist/web/vendor/highlight.min.js +0 -1262
  237. package/dist/web/vendor/lucide.LICENSE.txt +0 -17
  238. package/node_modules/@img/colour/LICENSE.md +0 -82
  239. package/node_modules/@img/colour/README.md +0 -15
  240. package/node_modules/@img/colour/color.cjs +0 -1596
  241. package/node_modules/@img/colour/index.cjs +0 -1
  242. package/node_modules/@img/colour/index.d.ts +0 -929
  243. package/node_modules/@img/colour/package.json +0 -58
  244. package/node_modules/@img/sharp-win32-x64/LICENSE +0 -191
  245. package/node_modules/@img/sharp-win32-x64/README.md +0 -59
  246. package/node_modules/@img/sharp-win32-x64/index.cjs +0 -1
  247. package/node_modules/@img/sharp-win32-x64/lib/libvips-42.dll +0 -0
  248. package/node_modules/@img/sharp-win32-x64/lib/libvips-cpp-8.18.6.dll +0 -0
  249. package/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64-0.35.4.node +0 -0
  250. package/node_modules/@img/sharp-win32-x64/package.json +0 -40
  251. package/node_modules/@img/sharp-win32-x64/versions.json +0 -30
  252. package/node_modules/@node-datachannel/win32-x64-msvc/node_datachannel.node +0 -0
  253. package/node_modules/@node-datachannel/win32-x64-msvc/package.json +0 -20
  254. package/node_modules/ansi-regex/index.d.ts +0 -37
  255. package/node_modules/ansi-regex/index.js +0 -10
  256. package/node_modules/ansi-regex/license +0 -9
  257. package/node_modules/ansi-regex/package.json +0 -55
  258. package/node_modules/ansi-regex/readme.md +0 -78
  259. package/node_modules/ansi-styles/index.d.ts +0 -345
  260. package/node_modules/ansi-styles/index.js +0 -163
  261. package/node_modules/ansi-styles/license +0 -9
  262. package/node_modules/ansi-styles/package.json +0 -56
  263. package/node_modules/ansi-styles/readme.md +0 -152
  264. package/node_modules/camelcase/index.d.ts +0 -63
  265. package/node_modules/camelcase/index.js +0 -76
  266. package/node_modules/camelcase/license +0 -9
  267. package/node_modules/camelcase/package.json +0 -43
  268. package/node_modules/camelcase/readme.md +0 -99
  269. package/node_modules/cliui/CHANGELOG.md +0 -76
  270. package/node_modules/cliui/LICENSE.txt +0 -14
  271. package/node_modules/cliui/README.md +0 -115
  272. package/node_modules/cliui/index.js +0 -354
  273. package/node_modules/cliui/package.json +0 -65
  274. package/node_modules/color-convert/CHANGELOG.md +0 -54
  275. package/node_modules/color-convert/LICENSE +0 -21
  276. package/node_modules/color-convert/README.md +0 -68
  277. package/node_modules/color-convert/conversions.js +0 -839
  278. package/node_modules/color-convert/index.js +0 -81
  279. package/node_modules/color-convert/package.json +0 -48
  280. package/node_modules/color-convert/route.js +0 -97
  281. package/node_modules/color-name/LICENSE +0 -8
  282. package/node_modules/color-name/README.md +0 -11
  283. package/node_modules/color-name/index.js +0 -152
  284. package/node_modules/color-name/package.json +0 -28
  285. package/node_modules/decamelize/index.js +0 -13
  286. package/node_modules/decamelize/license +0 -21
  287. package/node_modules/decamelize/package.json +0 -38
  288. package/node_modules/decamelize/readme.md +0 -48
  289. package/node_modules/detect-libc/LICENSE +0 -201
  290. package/node_modules/detect-libc/README.md +0 -163
  291. package/node_modules/detect-libc/index.d.ts +0 -14
  292. package/node_modules/detect-libc/lib/detect-libc.js +0 -313
  293. package/node_modules/detect-libc/lib/elf.js +0 -39
  294. package/node_modules/detect-libc/lib/filesystem.js +0 -51
  295. package/node_modules/detect-libc/lib/process.js +0 -24
  296. package/node_modules/detect-libc/package.json +0 -44
  297. package/node_modules/dijkstrajs/.travis.yml +0 -4
  298. package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
  299. package/node_modules/dijkstrajs/LICENSE.md +0 -19
  300. package/node_modules/dijkstrajs/README.md +0 -18
  301. package/node_modules/dijkstrajs/dijkstra.js +0 -165
  302. package/node_modules/dijkstrajs/package.json +0 -60
  303. package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
  304. package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  305. package/node_modules/emoji-regex/README.md +0 -73
  306. package/node_modules/emoji-regex/es2015/index.js +0 -6
  307. package/node_modules/emoji-regex/es2015/text.js +0 -6
  308. package/node_modules/emoji-regex/index.d.ts +0 -23
  309. package/node_modules/emoji-regex/index.js +0 -6
  310. package/node_modules/emoji-regex/package.json +0 -50
  311. package/node_modules/emoji-regex/text.js +0 -6
  312. package/node_modules/find-up/index.d.ts +0 -137
  313. package/node_modules/find-up/index.js +0 -89
  314. package/node_modules/find-up/license +0 -9
  315. package/node_modules/find-up/package.json +0 -53
  316. package/node_modules/find-up/readme.md +0 -156
  317. package/node_modules/get-caller-file/LICENSE.md +0 -6
  318. package/node_modules/get-caller-file/README.md +0 -41
  319. package/node_modules/get-caller-file/index.d.ts +0 -2
  320. package/node_modules/get-caller-file/index.js +0 -22
  321. package/node_modules/get-caller-file/index.js.map +0 -1
  322. package/node_modules/get-caller-file/package.json +0 -42
  323. package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
  324. package/node_modules/is-fullwidth-code-point/index.js +0 -50
  325. package/node_modules/is-fullwidth-code-point/license +0 -9
  326. package/node_modules/is-fullwidth-code-point/package.json +0 -42
  327. package/node_modules/is-fullwidth-code-point/readme.md +0 -39
  328. package/node_modules/locate-path/index.d.ts +0 -83
  329. package/node_modules/locate-path/index.js +0 -65
  330. package/node_modules/locate-path/license +0 -9
  331. package/node_modules/locate-path/package.json +0 -45
  332. package/node_modules/locate-path/readme.md +0 -122
  333. package/node_modules/node-datachannel/.clang-format +0 -17
  334. package/node_modules/node-datachannel/.editorconfig +0 -12
  335. package/node_modules/node-datachannel/.eslintignore +0 -8
  336. package/node_modules/node-datachannel/.eslintrc.json +0 -27
  337. package/node_modules/node-datachannel/.gitmodules +0 -3
  338. package/node_modules/node-datachannel/.prettierignore +0 -7
  339. package/node_modules/node-datachannel/.prettierrc +0 -13
  340. package/node_modules/node-datachannel/API.md +0 -247
  341. package/node_modules/node-datachannel/BULDING.md +0 -26
  342. package/node_modules/node-datachannel/CMakeLists.txt +0 -153
  343. package/node_modules/node-datachannel/LICENSE +0 -373
  344. package/node_modules/node-datachannel/README.md +0 -129
  345. package/node_modules/node-datachannel/dist/cjs/index.cjs +0 -11
  346. package/node_modules/node-datachannel/dist/cjs/index.cjs.map +0 -1
  347. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs +0 -101
  348. package/node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs.map +0 -1
  349. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs +0 -91
  350. package/node_modules/node-datachannel/dist/cjs/lib/index.cjs.map +0 -1
  351. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs +0 -94
  352. package/node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs.map +0 -1
  353. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs +0 -45
  354. package/node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs.map +0 -1
  355. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs +0 -8
  356. package/node_modules/node-datachannel/dist/cjs/lib/websocket.cjs.map +0 -1
  357. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs +0 -86
  358. package/node_modules/node-datachannel/dist/cjs/polyfill/Events.cjs.map +0 -1
  359. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs +0 -34
  360. package/node_modules/node-datachannel/dist/cjs/polyfill/Exception.cjs.map +0 -1
  361. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs +0 -34
  362. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCCertificate.cjs.map +0 -1
  363. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs +0 -214
  364. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDataChannel.cjs.map +0 -1
  365. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs +0 -53
  366. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +0 -1
  367. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs +0 -83
  368. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCError.cjs.map +0 -1
  369. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs +0 -132
  370. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceCandidate.cjs.map +0 -1
  371. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs +0 -94
  372. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCIceTransport.cjs.map +0 -1
  373. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs +0 -434
  374. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCPeerConnection.cjs.map +0 -1
  375. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs +0 -59
  376. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSctpTransport.cjs.map +0 -1
  377. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs +0 -45
  378. package/node_modules/node-datachannel/dist/cjs/polyfill/RTCSessionDescription.cjs.map +0 -1
  379. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs +0 -42
  380. package/node_modules/node-datachannel/dist/cjs/polyfill/index.cjs.map +0 -1
  381. package/node_modules/node-datachannel/dist/esm/index.mjs +0 -8
  382. package/node_modules/node-datachannel/dist/esm/index.mjs.map +0 -1
  383. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs +0 -78
  384. package/node_modules/node-datachannel/dist/esm/lib/datachannel-stream.mjs.map +0 -1
  385. package/node_modules/node-datachannel/dist/esm/lib/index.mjs +0 -64
  386. package/node_modules/node-datachannel/dist/esm/lib/index.mjs.map +0 -1
  387. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs +0 -78
  388. package/node_modules/node-datachannel/dist/esm/lib/node-datachannel.mjs.map +0 -1
  389. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs +0 -43
  390. package/node_modules/node-datachannel/dist/esm/lib/websocket-server.mjs.map +0 -1
  391. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs +0 -6
  392. package/node_modules/node-datachannel/dist/esm/lib/websocket.mjs.map +0 -1
  393. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs +0 -82
  394. package/node_modules/node-datachannel/dist/esm/polyfill/Events.mjs.map +0 -1
  395. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs +0 -28
  396. package/node_modules/node-datachannel/dist/esm/polyfill/Exception.mjs.map +0 -1
  397. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs +0 -30
  398. package/node_modules/node-datachannel/dist/esm/polyfill/RTCCertificate.mjs.map +0 -1
  399. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs +0 -210
  400. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDataChannel.mjs.map +0 -1
  401. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs +0 -49
  402. package/node_modules/node-datachannel/dist/esm/polyfill/RTCDtlsTransport.mjs.map +0 -1
  403. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs +0 -79
  404. package/node_modules/node-datachannel/dist/esm/polyfill/RTCError.mjs.map +0 -1
  405. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs +0 -128
  406. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceCandidate.mjs.map +0 -1
  407. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs +0 -89
  408. package/node_modules/node-datachannel/dist/esm/polyfill/RTCIceTransport.mjs.map +0 -1
  409. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs +0 -430
  410. package/node_modules/node-datachannel/dist/esm/polyfill/RTCPeerConnection.mjs.map +0 -1
  411. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs +0 -55
  412. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSctpTransport.mjs.map +0 -1
  413. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs +0 -41
  414. package/node_modules/node-datachannel/dist/esm/polyfill/RTCSessionDescription.mjs.map +0 -1
  415. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs +0 -27
  416. package/node_modules/node-datachannel/dist/esm/polyfill/index.mjs.map +0 -1
  417. package/node_modules/node-datachannel/dist/types/lib/datachannel-stream.d.ts +0 -24
  418. package/node_modules/node-datachannel/dist/types/lib/index.d.ts +0 -241
  419. package/node_modules/node-datachannel/dist/types/lib/types.d.ts +0 -119
  420. package/node_modules/node-datachannel/dist/types/lib/websocket-server.d.ts +0 -13
  421. package/node_modules/node-datachannel/dist/types/lib/websocket.d.ts +0 -25
  422. package/node_modules/node-datachannel/dist/types/polyfill/Events.d.ts +0 -15
  423. package/node_modules/node-datachannel/dist/types/polyfill/RTCCertificate.d.ts +0 -9
  424. package/node_modules/node-datachannel/dist/types/polyfill/RTCDataChannel.d.ts +0 -29
  425. package/node_modules/node-datachannel/dist/types/polyfill/RTCDtlsTransport.d.ts +0 -15
  426. package/node_modules/node-datachannel/dist/types/polyfill/RTCError.d.ts +0 -17
  427. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceCandidate.d.ts +0 -21
  428. package/node_modules/node-datachannel/dist/types/polyfill/RTCIceTransport.d.ts +0 -30
  429. package/node_modules/node-datachannel/dist/types/polyfill/RTCPeerConnection.d.ts +0 -64
  430. package/node_modules/node-datachannel/dist/types/polyfill/RTCSctpTransport.d.ts +0 -15
  431. package/node_modules/node-datachannel/dist/types/polyfill/RTCSessionDescription.d.ts +0 -10
  432. package/node_modules/node-datachannel/dist/types/polyfill/index.d.ts +0 -26
  433. package/node_modules/node-datachannel/jest.config.ts +0 -14
  434. package/node_modules/node-datachannel/package.json +0 -133
  435. package/node_modules/node-datachannel/rollup.config.mjs +0 -67
  436. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.cpp +0 -530
  437. package/node_modules/node-datachannel/src/cpp/data-channel-wrapper.h +0 -63
  438. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.cpp +0 -172
  439. package/node_modules/node-datachannel/src/cpp/ice-udp-mux-listener-wrapper.h +0 -43
  440. package/node_modules/node-datachannel/src/cpp/main.cpp +0 -58
  441. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.cpp +0 -457
  442. package/node_modules/node-datachannel/src/cpp/media-audio-wrapper.h +0 -52
  443. package/node_modules/node-datachannel/src/cpp/media-av1packetization.cpp +0 -24
  444. package/node_modules/node-datachannel/src/cpp/media-av1packetization.h +0 -11
  445. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.cpp +0 -126
  446. package/node_modules/node-datachannel/src/cpp/media-av1rtppacketizer-wrapper.h +0 -29
  447. package/node_modules/node-datachannel/src/cpp/media-direction.cpp +0 -43
  448. package/node_modules/node-datachannel/src/cpp/media-direction.h +0 -10
  449. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.cpp +0 -126
  450. package/node_modules/node-datachannel/src/cpp/media-h264rtppacketizer-wrapper.h +0 -30
  451. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.cpp +0 -126
  452. package/node_modules/node-datachannel/src/cpp/media-h265rtppacketizer-wrapper.h +0 -30
  453. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.cpp +0 -32
  454. package/node_modules/node-datachannel/src/cpp/media-h26xseparator.h +0 -11
  455. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.cpp +0 -31
  456. package/node_modules/node-datachannel/src/cpp/media-mediahandler-helper.h +0 -11
  457. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.cpp +0 -79
  458. package/node_modules/node-datachannel/src/cpp/media-pacinghandler-wrapper.h +0 -28
  459. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.cpp +0 -68
  460. package/node_modules/node-datachannel/src/cpp/media-rtcpnackresponder-wrapper.h +0 -28
  461. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.cpp +0 -57
  462. package/node_modules/node-datachannel/src/cpp/media-rtcpreceivingsession-wrapper.h +0 -28
  463. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.cpp +0 -93
  464. package/node_modules/node-datachannel/src/cpp/media-rtcpsrreporter-wrapper.h +0 -30
  465. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.cpp +0 -167
  466. package/node_modules/node-datachannel/src/cpp/media-rtppacketizationconfig-wrapper.h +0 -35
  467. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.cpp +0 -95
  468. package/node_modules/node-datachannel/src/cpp/media-rtppacketizer-wrapper.h +0 -30
  469. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.cpp +0 -458
  470. package/node_modules/node-datachannel/src/cpp/media-track-wrapper.h +0 -61
  471. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.cpp +0 -526
  472. package/node_modules/node-datachannel/src/cpp/media-video-wrapper.h +0 -56
  473. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.cpp +0 -1298
  474. package/node_modules/node-datachannel/src/cpp/peer-connection-wrapper.h +0 -89
  475. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.cpp +0 -205
  476. package/node_modules/node-datachannel/src/cpp/rtc-wrapper.h +0 -24
  477. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.cpp +0 -57
  478. package/node_modules/node-datachannel/src/cpp/thread-safe-callback.h +0 -47
  479. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.cpp +0 -275
  480. package/node_modules/node-datachannel/src/cpp/web-socket-server-wrapper.h +0 -41
  481. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.cpp +0 -796
  482. package/node_modules/node-datachannel/src/cpp/web-socket-wrapper.h +0 -63
  483. package/node_modules/node-datachannel/src/index.ts +0 -9
  484. package/node_modules/node-datachannel/src/lib/datachannel-stream.ts +0 -100
  485. package/node_modules/node-datachannel/src/lib/index.ts +0 -290
  486. package/node_modules/node-datachannel/src/lib/node-datachannel.ts +0 -84
  487. package/node_modules/node-datachannel/src/lib/types.ts +0 -169
  488. package/node_modules/node-datachannel/src/lib/websocket-server.ts +0 -37
  489. package/node_modules/node-datachannel/src/lib/websocket.ts +0 -26
  490. package/node_modules/node-datachannel/src/polyfill/Events.ts +0 -82
  491. package/node_modules/node-datachannel/src/polyfill/Exception.ts +0 -37
  492. package/node_modules/node-datachannel/src/polyfill/README.md +0 -41
  493. package/node_modules/node-datachannel/src/polyfill/RTCCertificate.ts +0 -21
  494. package/node_modules/node-datachannel/src/polyfill/RTCDataChannel.ts +0 -225
  495. package/node_modules/node-datachannel/src/polyfill/RTCDtlsTransport.ts +0 -46
  496. package/node_modules/node-datachannel/src/polyfill/RTCError.ts +0 -78
  497. package/node_modules/node-datachannel/src/polyfill/RTCIceCandidate.ts +0 -128
  498. package/node_modules/node-datachannel/src/polyfill/RTCIceTransport.ts +0 -90
  499. package/node_modules/node-datachannel/src/polyfill/RTCPeerConnection.ts +0 -527
  500. package/node_modules/node-datachannel/src/polyfill/RTCSctpTransport.ts +0 -51
  501. package/node_modules/node-datachannel/src/polyfill/RTCSessionDescription.ts +0 -41
  502. package/node_modules/node-datachannel/src/polyfill/index.ts +0 -38
  503. package/node_modules/node-datachannel/tsconfig.json +0 -21
  504. package/node_modules/p-limit/index.d.ts +0 -38
  505. package/node_modules/p-limit/index.js +0 -57
  506. package/node_modules/p-limit/license +0 -9
  507. package/node_modules/p-limit/package.json +0 -52
  508. package/node_modules/p-limit/readme.md +0 -101
  509. package/node_modules/p-locate/index.d.ts +0 -64
  510. package/node_modules/p-locate/index.js +0 -52
  511. package/node_modules/p-locate/license +0 -9
  512. package/node_modules/p-locate/package.json +0 -53
  513. package/node_modules/p-locate/readme.md +0 -90
  514. package/node_modules/p-try/index.d.ts +0 -39
  515. package/node_modules/p-try/index.js +0 -9
  516. package/node_modules/p-try/license +0 -9
  517. package/node_modules/p-try/package.json +0 -42
  518. package/node_modules/p-try/readme.md +0 -58
  519. package/node_modules/path-exists/index.d.ts +0 -28
  520. package/node_modules/path-exists/index.js +0 -23
  521. package/node_modules/path-exists/license +0 -9
  522. package/node_modules/path-exists/package.json +0 -39
  523. package/node_modules/path-exists/readme.md +0 -52
  524. package/node_modules/pngjs/.eslintignore +0 -2
  525. package/node_modules/pngjs/.eslintrc.json +0 -19
  526. package/node_modules/pngjs/.prettierignore +0 -3
  527. package/node_modules/pngjs/LICENSE +0 -20
  528. package/node_modules/pngjs/README.md +0 -429
  529. package/node_modules/pngjs/browser.js +0 -16096
  530. package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
  531. package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
  532. package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
  533. package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
  534. package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
  535. package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
  536. package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
  537. package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
  538. package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
  539. package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
  540. package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
  541. package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
  542. package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
  543. package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
  544. package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
  545. package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
  546. package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
  547. package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
  548. package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
  549. package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
  550. package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
  551. package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
  552. package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
  553. package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
  554. package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
  555. package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
  556. package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  557. package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
  558. package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
  559. package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
  560. package/node_modules/pngjs/coverage/lcov.info +0 -2177
  561. package/node_modules/pngjs/lib/bitmapper.js +0 -267
  562. package/node_modules/pngjs/lib/bitpacker.js +0 -158
  563. package/node_modules/pngjs/lib/chunkstream.js +0 -189
  564. package/node_modules/pngjs/lib/constants.js +0 -32
  565. package/node_modules/pngjs/lib/crc.js +0 -40
  566. package/node_modules/pngjs/lib/filter-pack.js +0 -171
  567. package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
  568. package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
  569. package/node_modules/pngjs/lib/filter-parse.js +0 -177
  570. package/node_modules/pngjs/lib/format-normaliser.js +0 -93
  571. package/node_modules/pngjs/lib/interlace.js +0 -95
  572. package/node_modules/pngjs/lib/packer-async.js +0 -50
  573. package/node_modules/pngjs/lib/packer-sync.js +0 -56
  574. package/node_modules/pngjs/lib/packer.js +0 -129
  575. package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
  576. package/node_modules/pngjs/lib/parser-async.js +0 -165
  577. package/node_modules/pngjs/lib/parser-sync.js +0 -108
  578. package/node_modules/pngjs/lib/parser.js +0 -290
  579. package/node_modules/pngjs/lib/png-sync.js +0 -12
  580. package/node_modules/pngjs/lib/png.js +0 -194
  581. package/node_modules/pngjs/lib/sync-inflate.js +0 -168
  582. package/node_modules/pngjs/lib/sync-reader.js +0 -45
  583. package/node_modules/pngjs/package.json +0 -73
  584. package/node_modules/qrcode/README.md +0 -770
  585. package/node_modules/qrcode/bin/qrcode +0 -159
  586. package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
  587. package/node_modules/qrcode/helper/to-sjis.js +0 -105
  588. package/node_modules/qrcode/lib/browser.js +0 -76
  589. package/node_modules/qrcode/lib/can-promise.js +0 -7
  590. package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
  591. package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
  592. package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
  593. package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
  594. package/node_modules/qrcode/lib/core/byte-data.js +0 -30
  595. package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
  596. package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
  597. package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
  598. package/node_modules/qrcode/lib/core/format-info.js +0 -29
  599. package/node_modules/qrcode/lib/core/galois-field.js +0 -69
  600. package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
  601. package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
  602. package/node_modules/qrcode/lib/core/mode.js +0 -167
  603. package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
  604. package/node_modules/qrcode/lib/core/polynomial.js +0 -62
  605. package/node_modules/qrcode/lib/core/qrcode.js +0 -495
  606. package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
  607. package/node_modules/qrcode/lib/core/regex.js +0 -31
  608. package/node_modules/qrcode/lib/core/segments.js +0 -330
  609. package/node_modules/qrcode/lib/core/utils.js +0 -63
  610. package/node_modules/qrcode/lib/core/version-check.js +0 -9
  611. package/node_modules/qrcode/lib/core/version.js +0 -163
  612. package/node_modules/qrcode/lib/index.js +0 -12
  613. package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
  614. package/node_modules/qrcode/lib/renderer/png.js +0 -78
  615. package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
  616. package/node_modules/qrcode/lib/renderer/svg.js +0 -19
  617. package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
  618. package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
  619. package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
  620. package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
  621. package/node_modules/qrcode/lib/renderer/utils.js +0 -99
  622. package/node_modules/qrcode/lib/server.js +0 -138
  623. package/node_modules/qrcode/license +0 -10
  624. package/node_modules/qrcode/package.json +0 -77
  625. package/node_modules/require-directory/.jshintrc +0 -67
  626. package/node_modules/require-directory/.npmignore +0 -1
  627. package/node_modules/require-directory/.travis.yml +0 -3
  628. package/node_modules/require-directory/LICENSE +0 -22
  629. package/node_modules/require-directory/README.markdown +0 -184
  630. package/node_modules/require-directory/index.js +0 -86
  631. package/node_modules/require-directory/package.json +0 -40
  632. package/node_modules/require-main-filename/CHANGELOG.md +0 -26
  633. package/node_modules/require-main-filename/LICENSE.txt +0 -14
  634. package/node_modules/require-main-filename/README.md +0 -26
  635. package/node_modules/require-main-filename/index.js +0 -18
  636. package/node_modules/require-main-filename/package.json +0 -35
  637. package/node_modules/semver/LICENSE +0 -15
  638. package/node_modules/semver/README.md +0 -680
  639. package/node_modules/semver/bin/semver.js +0 -195
  640. package/node_modules/semver/classes/comparator.js +0 -143
  641. package/node_modules/semver/classes/index.js +0 -7
  642. package/node_modules/semver/classes/range.js +0 -577
  643. package/node_modules/semver/classes/semver.js +0 -350
  644. package/node_modules/semver/functions/clean.js +0 -8
  645. package/node_modules/semver/functions/cmp.js +0 -54
  646. package/node_modules/semver/functions/coerce.js +0 -62
  647. package/node_modules/semver/functions/compare-build.js +0 -9
  648. package/node_modules/semver/functions/compare-loose.js +0 -5
  649. package/node_modules/semver/functions/compare.js +0 -7
  650. package/node_modules/semver/functions/diff.js +0 -60
  651. package/node_modules/semver/functions/eq.js +0 -5
  652. package/node_modules/semver/functions/gt.js +0 -5
  653. package/node_modules/semver/functions/gte.js +0 -5
  654. package/node_modules/semver/functions/inc.js +0 -21
  655. package/node_modules/semver/functions/lt.js +0 -5
  656. package/node_modules/semver/functions/lte.js +0 -5
  657. package/node_modules/semver/functions/major.js +0 -5
  658. package/node_modules/semver/functions/minor.js +0 -5
  659. package/node_modules/semver/functions/neq.js +0 -5
  660. package/node_modules/semver/functions/parse.js +0 -18
  661. package/node_modules/semver/functions/patch.js +0 -5
  662. package/node_modules/semver/functions/prerelease.js +0 -8
  663. package/node_modules/semver/functions/rcompare.js +0 -5
  664. package/node_modules/semver/functions/rsort.js +0 -5
  665. package/node_modules/semver/functions/satisfies.js +0 -12
  666. package/node_modules/semver/functions/sort.js +0 -5
  667. package/node_modules/semver/functions/truncate.js +0 -48
  668. package/node_modules/semver/functions/valid.js +0 -8
  669. package/node_modules/semver/index.js +0 -93
  670. package/node_modules/semver/internal/constants.js +0 -37
  671. package/node_modules/semver/internal/debug.js +0 -11
  672. package/node_modules/semver/internal/identifiers.js +0 -29
  673. package/node_modules/semver/internal/lrucache.js +0 -42
  674. package/node_modules/semver/internal/parse-options.js +0 -17
  675. package/node_modules/semver/internal/re.js +0 -223
  676. package/node_modules/semver/package.json +0 -78
  677. package/node_modules/semver/preload.js +0 -4
  678. package/node_modules/semver/range.bnf +0 -17
  679. package/node_modules/semver/ranges/gtr.js +0 -6
  680. package/node_modules/semver/ranges/intersects.js +0 -9
  681. package/node_modules/semver/ranges/ltr.js +0 -6
  682. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  683. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  684. package/node_modules/semver/ranges/min-version.js +0 -63
  685. package/node_modules/semver/ranges/outside.js +0 -82
  686. package/node_modules/semver/ranges/simplify.js +0 -49
  687. package/node_modules/semver/ranges/subset.js +0 -249
  688. package/node_modules/semver/ranges/to-comparators.js +0 -10
  689. package/node_modules/semver/ranges/valid.js +0 -13
  690. package/node_modules/set-blocking/CHANGELOG.md +0 -26
  691. package/node_modules/set-blocking/LICENSE.txt +0 -14
  692. package/node_modules/set-blocking/README.md +0 -31
  693. package/node_modules/set-blocking/index.js +0 -7
  694. package/node_modules/set-blocking/package.json +0 -42
  695. package/node_modules/sharp/LICENSE +0 -191
  696. package/node_modules/sharp/README.md +0 -112
  697. package/node_modules/sharp/dist/channel.cjs +0 -177
  698. package/node_modules/sharp/dist/channel.mjs +0 -177
  699. package/node_modules/sharp/dist/colour.cjs +0 -199
  700. package/node_modules/sharp/dist/colour.mjs +0 -199
  701. package/node_modules/sharp/dist/composite.cjs +0 -213
  702. package/node_modules/sharp/dist/composite.mjs +0 -213
  703. package/node_modules/sharp/dist/constructor.cjs +0 -511
  704. package/node_modules/sharp/dist/constructor.mjs +0 -511
  705. package/node_modules/sharp/dist/index.cjs +0 -25
  706. package/node_modules/sharp/dist/index.d.cts +0 -1999
  707. package/node_modules/sharp/dist/index.d.mts +0 -2046
  708. package/node_modules/sharp/dist/index.mjs +0 -25
  709. package/node_modules/sharp/dist/input.cjs +0 -819
  710. package/node_modules/sharp/dist/input.mjs +0 -819
  711. package/node_modules/sharp/dist/is.cjs +0 -143
  712. package/node_modules/sharp/dist/is.mjs +0 -143
  713. package/node_modules/sharp/dist/libvips.cjs +0 -212
  714. package/node_modules/sharp/dist/libvips.mjs +0 -212
  715. package/node_modules/sharp/dist/operation.cjs +0 -1002
  716. package/node_modules/sharp/dist/operation.mjs +0 -1002
  717. package/node_modules/sharp/dist/output.cjs +0 -1785
  718. package/node_modules/sharp/dist/output.mjs +0 -1785
  719. package/node_modules/sharp/dist/resize.cjs +0 -617
  720. package/node_modules/sharp/dist/resize.mjs +0 -617
  721. package/node_modules/sharp/dist/sharp.cjs +0 -174
  722. package/node_modules/sharp/dist/sharp.mjs +0 -174
  723. package/node_modules/sharp/dist/utility.cjs +0 -301
  724. package/node_modules/sharp/dist/utility.mjs +0 -301
  725. package/node_modules/sharp/install/build.js +0 -38
  726. package/node_modules/sharp/lib/index.d.ts +0 -1999
  727. package/node_modules/sharp/package.json +0 -227
  728. package/node_modules/sharp/src/binding.gyp +0 -303
  729. package/node_modules/sharp/src/common.cc +0 -1186
  730. package/node_modules/sharp/src/common.h +0 -421
  731. package/node_modules/sharp/src/metadata.cc +0 -404
  732. package/node_modules/sharp/src/metadata.h +0 -95
  733. package/node_modules/sharp/src/operations.cc +0 -516
  734. package/node_modules/sharp/src/operations.h +0 -137
  735. package/node_modules/sharp/src/pipeline.cc +0 -1948
  736. package/node_modules/sharp/src/pipeline.h +0 -422
  737. package/node_modules/sharp/src/sharp.cc +0 -43
  738. package/node_modules/sharp/src/stats.cc +0 -186
  739. package/node_modules/sharp/src/stats.h +0 -62
  740. package/node_modules/sharp/src/utilities.cc +0 -289
  741. package/node_modules/sharp/src/utilities.h +0 -22
  742. package/node_modules/string-width/index.d.ts +0 -29
  743. package/node_modules/string-width/index.js +0 -47
  744. package/node_modules/string-width/license +0 -9
  745. package/node_modules/string-width/package.json +0 -56
  746. package/node_modules/string-width/readme.md +0 -50
  747. package/node_modules/strip-ansi/index.d.ts +0 -17
  748. package/node_modules/strip-ansi/index.js +0 -4
  749. package/node_modules/strip-ansi/license +0 -9
  750. package/node_modules/strip-ansi/package.json +0 -54
  751. package/node_modules/strip-ansi/readme.md +0 -46
  752. package/node_modules/which-module/LICENSE +0 -13
  753. package/node_modules/which-module/README.md +0 -58
  754. package/node_modules/which-module/index.js +0 -9
  755. package/node_modules/which-module/package.json +0 -41
  756. package/node_modules/wrap-ansi/index.js +0 -186
  757. package/node_modules/wrap-ansi/license +0 -9
  758. package/node_modules/wrap-ansi/package.json +0 -61
  759. package/node_modules/wrap-ansi/readme.md +0 -97
  760. package/node_modules/ws/LICENSE +0 -20
  761. package/node_modules/ws/README.md +0 -548
  762. package/node_modules/ws/browser.js +0 -8
  763. package/node_modules/ws/index.js +0 -22
  764. package/node_modules/ws/lib/buffer-util.js +0 -131
  765. package/node_modules/ws/lib/constants.js +0 -19
  766. package/node_modules/ws/lib/event-target.js +0 -292
  767. package/node_modules/ws/lib/extension.js +0 -203
  768. package/node_modules/ws/lib/limiter.js +0 -55
  769. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  770. package/node_modules/ws/lib/receiver.js +0 -743
  771. package/node_modules/ws/lib/sender.js +0 -607
  772. package/node_modules/ws/lib/stream.js +0 -161
  773. package/node_modules/ws/lib/subprotocol.js +0 -62
  774. package/node_modules/ws/lib/validation.js +0 -152
  775. package/node_modules/ws/lib/websocket-server.js +0 -562
  776. package/node_modules/ws/lib/websocket.js +0 -1407
  777. package/node_modules/ws/package.json +0 -74
  778. package/node_modules/ws/wrapper.mjs +0 -21
  779. package/node_modules/y18n/CHANGELOG.md +0 -35
  780. package/node_modules/y18n/LICENSE +0 -13
  781. package/node_modules/y18n/README.md +0 -109
  782. package/node_modules/y18n/index.js +0 -188
  783. package/node_modules/y18n/package.json +0 -39
  784. package/node_modules/yargs/CHANGELOG.md +0 -420
  785. package/node_modules/yargs/LICENSE +0 -21
  786. package/node_modules/yargs/README.md +0 -140
  787. package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
  788. package/node_modules/yargs/build/lib/apply-extends.js +0 -65
  789. package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
  790. package/node_modules/yargs/build/lib/argsert.js +0 -65
  791. package/node_modules/yargs/build/lib/command.d.ts +0 -64
  792. package/node_modules/yargs/build/lib/command.js +0 -416
  793. package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
  794. package/node_modules/yargs/build/lib/common-types.js +0 -25
  795. package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
  796. package/node_modules/yargs/build/lib/completion-templates.js +0 -50
  797. package/node_modules/yargs/build/lib/completion.d.ts +0 -21
  798. package/node_modules/yargs/build/lib/completion.js +0 -135
  799. package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
  800. package/node_modules/yargs/build/lib/is-promise.js +0 -9
  801. package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
  802. package/node_modules/yargs/build/lib/levenshtein.js +0 -58
  803. package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
  804. package/node_modules/yargs/build/lib/middleware.js +0 -57
  805. package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
  806. package/node_modules/yargs/build/lib/obj-filter.js +0 -14
  807. package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
  808. package/node_modules/yargs/build/lib/parse-command.js +0 -36
  809. package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
  810. package/node_modules/yargs/build/lib/process-argv.js +0 -31
  811. package/node_modules/yargs/build/lib/usage.d.ts +0 -49
  812. package/node_modules/yargs/build/lib/usage.js +0 -540
  813. package/node_modules/yargs/build/lib/validation.d.ts +0 -34
  814. package/node_modules/yargs/build/lib/validation.js +0 -330
  815. package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
  816. package/node_modules/yargs/build/lib/yargs.js +0 -1190
  817. package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
  818. package/node_modules/yargs/build/lib/yerror.js +0 -11
  819. package/node_modules/yargs/index.js +0 -40
  820. package/node_modules/yargs/locales/be.json +0 -46
  821. package/node_modules/yargs/locales/de.json +0 -46
  822. package/node_modules/yargs/locales/en.json +0 -51
  823. package/node_modules/yargs/locales/es.json +0 -46
  824. package/node_modules/yargs/locales/fi.json +0 -49
  825. package/node_modules/yargs/locales/fr.json +0 -53
  826. package/node_modules/yargs/locales/hi.json +0 -49
  827. package/node_modules/yargs/locales/hu.json +0 -46
  828. package/node_modules/yargs/locales/id.json +0 -50
  829. package/node_modules/yargs/locales/it.json +0 -46
  830. package/node_modules/yargs/locales/ja.json +0 -51
  831. package/node_modules/yargs/locales/ko.json +0 -49
  832. package/node_modules/yargs/locales/nb.json +0 -44
  833. package/node_modules/yargs/locales/nl.json +0 -49
  834. package/node_modules/yargs/locales/nn.json +0 -44
  835. package/node_modules/yargs/locales/pirate.json +0 -13
  836. package/node_modules/yargs/locales/pl.json +0 -49
  837. package/node_modules/yargs/locales/pt.json +0 -45
  838. package/node_modules/yargs/locales/pt_BR.json +0 -48
  839. package/node_modules/yargs/locales/ru.json +0 -46
  840. package/node_modules/yargs/locales/th.json +0 -46
  841. package/node_modules/yargs/locales/tr.json +0 -48
  842. package/node_modules/yargs/locales/zh_CN.json +0 -48
  843. package/node_modules/yargs/locales/zh_TW.json +0 -47
  844. package/node_modules/yargs/package.json +0 -92
  845. package/node_modules/yargs/yargs.js +0 -14
  846. package/node_modules/yargs-parser/CHANGELOG.md +0 -601
  847. package/node_modules/yargs-parser/LICENSE.txt +0 -14
  848. package/node_modules/yargs-parser/README.md +0 -449
  849. package/node_modules/yargs-parser/index.js +0 -1032
  850. package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
  851. package/node_modules/yargs-parser/package.json +0 -46
@@ -1,71 +1,87 @@
1
- export function adminPage() {
2
- 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>
3
- <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>
4
- <style>@media(max-width:600px){main .throughput .metric strong{font-size:17px}}</style>
5
- <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>
6
- <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>
7
- <style>
8
- :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}
9
- 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}}
10
- </style><style>
11
- .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}}
12
- </style><style>
13
- :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}
14
- 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}
15
- .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}
16
- .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}
17
- .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}}
18
- </style><style>
19
- #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}
20
- @media(max-width:800px){#metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}
21
- @media(max-width:600px){.resource-card .throughput .metric strong{font-size:14px}}
22
- </style></head><body><main>
23
- <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="announcements">系统公告</button><button type="button" data-page="settings">容量设置</button></nav></header>
24
- <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>
25
- <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>
26
- <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>
27
- <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><p id="result" class="status" role="status" aria-live="polite"></p>
28
- </main><script>
29
- const el=id=>document.getElementById(id);let snapshot=null;
30
- const request=async(path,options={})=>{const response=await fetch(path,options);const body=await response.json();if(!response.ok)throw new Error(body.error||('HTTP '+response.status));return body};
31
- function setResult(message,error=false){el('result').textContent=message;el('result').classList.toggle('error',error)}
32
- function node(tag,className,value){const item=document.createElement(tag);if(className)item.className=className;if(value!==undefined)item.textContent=value;return item}
33
- 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}
34
- function toneAbove(value,warn,critical){return typeof value==='number'&&Number.isFinite(value)?value>=critical?'critical':value>=warn?'warn':'':''}
35
- function toneBelow(value,warn,critical){return typeof value==='number'&&Number.isFinite(value)?value<=critical?'critical':value<=warn?'warn':'':''}
36
- function number(value,digits=0){return typeof value==='number'&&Number.isFinite(value)?value.toFixed(digits):'—'}
37
- function date(value){return new Date(value).toLocaleString('zh-CN')}
38
- 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;
39
- 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;
40
- 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);
41
- 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)}
42
- 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?'需要关注':'压力正常';
43
- 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'}):'等待采样';
44
- el('throughput').replaceChildren(metric(cloudFresh?number(cloud.outMbps,2)+' Mbps':'—','公网发送'),metric(cloudFresh?number(cloud.inMbps,2)+' Mbps':'—','公网接收'));
45
- 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):''));
46
- const gb=value=>value==null?'—':number(Number(value)/1073741824,2)+' GB';
47
- el('trafficUsage').textContent=trafficFresh?'已用 '+gb(cloud.trafficUsedBytes)+' / '+gb(cloud.trafficTotalBytes):'流量暂不可用';
48
- el('trafficNote').textContent=trafficFresh?'更新时间 '+new Date(cloud.trafficSampledAt).toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'}):'暂不可用';
49
- renderTrafficTimeline(cloud,trafficFresh);
50
- 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),'活动手机'))}
51
- let editorItem=null,editorMode='new',writing=false;
52
- 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()}
53
- 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}
54
- 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}
55
- 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);
56
- 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))}
57
- else if(item.status==='active'){actions.append(action('查看','secondary',()=>openEditor(item)),action('撤下','danger',()=>{if(confirm('确定撤下当前公告?'))write('PUT',path+'/withdraw',{},'公告已撤下')},!available))}
58
- else{actions.append(action('查看','secondary',()=>openEditor(item)),action('复制','secondary',()=>openEditor(item,true),!available),action('删除','danger',()=>{if(confirm('确定删除这条已撤下的公告?'))write('DELETE',path,{},'公告记录已删除')},!available))}
59
- row.append(summary,status,when,actions);list.append(row)}}
60
- 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??''}
61
- async function load(){try{render(await request('/admin/api/status'));setResult('')}catch(error){setResult(error.message,true)}}
62
- 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}}
63
- function showPage(page){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;history.replaceState(null,'','#'+page);load()}
64
- for(const button of document.querySelectorAll('[data-page]'))button.addEventListener('click',()=>showPage(button.dataset.page));
65
- el('saveLimit').addEventListener('click',()=>write('PUT','/admin/api/settings',{maxRelaySessions:Number(el('limit').value)},'转发容量已保存'));
66
- 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)});
67
- 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()}
68
- el('saveDraft').addEventListener('click',()=>saveAnnouncement(false));el('publish').addEventListener('click',()=>saveAnnouncement(true));
69
- showPage(['overview','announcements','settings'].includes(location.hash.slice(1))?location.hash.slice(1):'overview');setInterval(()=>{if(!document.hidden&&!el('overview').hidden)load()},5000);
70
- </script></body></html>`;
71
- }
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
+ }
@@ -1,15 +1,36 @@
1
1
  import http from 'node:http';
2
+ import fs from 'node:fs';
2
3
  import { adminPage } from './admin-page.mjs';
3
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';
4
9
 
5
10
  function reply(response, status, body, type = 'application/json; charset=utf-8') {
6
11
  const bytes = typeof body === 'string' ? body : JSON.stringify(body);
7
12
  response.writeHead(status, { 'Content-Type': type, 'Content-Length': Buffer.byteLength(bytes),
8
- 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff' });
13
+ 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff',
14
+ 'Referrer-Policy': 'no-referrer', 'X-Robots-Tag': 'noindex, nofollow', 'X-Frame-Options': 'DENY' });
9
15
  response.end(bytes);
10
16
  }
11
17
 
12
- export async function startAdminServer({ port = 45851, relayUrl = 'http://127.0.0.1:45850' } = {}) {
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);
13
34
  const cloud = startAdminCloud();
14
35
  const relayOrigin = new URL(relayUrl);
15
36
  if (relayOrigin.hostname !== '127.0.0.1' || relayOrigin.protocol !== 'http:' || relayOrigin.pathname !== '/') {
@@ -17,42 +38,65 @@ export async function startAdminServer({ port = 45851, relayUrl = 'http://127.0.
17
38
  }
18
39
  const server = http.createServer(async (request, response) => {
19
40
  const target = new URL(request.url || '/', 'http://localhost');
20
- if (!target.pathname.startsWith('/admin')) { reply(response, 404, { error: 'Not found' }); return; }
21
- if (request.method === 'GET' && target.pathname === '/admin') {
22
- reply(response, 200, adminPage(), 'text/html; charset=utf-8'); return;
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;
23
45
  }
24
- if (request.method === 'GET' && target.pathname === '/admin/api/status') {
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') {
25
53
  try {
26
- const upstream = await fetch(new URL(target.pathname, relayOrigin), { signal: AbortSignal.timeout(2000) });
27
- if (!upstream.ok) throw new Error('Relay status unavailable');
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 } });
28
77
  const state = await upstream.json();
29
- reply(response, 200, { ...state, relayAvailable: true, cloud: cloud.read() });
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);
30
81
  } catch {
31
- reply(response, 200, { relayAvailable: false, cloud: cloud.read(), maxRelaySessions: null,
32
- activeRelaySessions: null, activeExemptRelaySessions: null, websocketConnections: null,
33
- onlineComputers: null, activePhones: null, announcement: null, announcements: [] });
82
+ reply(response, 503, { error: '中继服务暂不可用' });
34
83
  }
35
84
  return;
36
85
  }
37
- if (!target.pathname.startsWith('/admin/api/') || !['PUT', 'DELETE'].includes(request.method || '')) {
86
+ if (!target.pathname.startsWith('/admin/api/') || !['PUT', 'DELETE', 'POST'].includes(request.method || '')) {
38
87
  reply(response, 404, { error: 'Not found' }); return;
39
88
  }
40
89
  try {
41
- const chunks = [];
42
- let size = 0;
43
- for await (const chunk of request) {
44
- size += chunk.length;
45
- if (size > 4096) { reply(response, 413, { error: 'Admin request too large' }); return; }
46
- chunks.push(chunk);
47
- }
48
- const upstream = await fetch(new URL(target.pathname, relayOrigin), {
90
+ const body = await readBody(request);
91
+ const upstream = await fetch(new URL(target.pathname + target.search, relayOrigin), {
49
92
  method: request.method, signal: AbortSignal.timeout(3000),
50
93
  headers: { 'Content-Type': request.headers['content-type'] || '',
51
- 'X-Code-Relax-Admin': request.headers['x-code-relax-admin'] || '' },
52
- body: Buffer.concat(chunks),
94
+ 'X-Code-Relax-Admin': request.headers['x-code-relax-admin'] || '',
95
+ 'X-Code-Relax-Admin-Key': key },
96
+ body,
53
97
  });
54
98
  reply(response, upstream.status, await upstream.text());
55
- } catch { reply(response, 503, { error: '中继服务暂不可用' }); }
99
+ } catch (error) { reply(response, error.status || 503, { error: error.status === 413 ? error.message : '中继服务暂不可用' }); }
56
100
  });
57
101
  try {
58
102
  await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port, '127.0.0.1', resolve); });
@@ -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: 3, maxRelaySessions: initialMaxRelaySessions, announcement: null, announcements: [] };
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,6 +29,7 @@ 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
 
@@ -0,0 +1,58 @@
1
+ // Shared semantic resources for the standalone relay management pages.
2
+ export const adminUiTokens = `:root{
3
+ --admin-page:#f5f7fa;--admin-text:#26332b;--admin-muted:#607168;
4
+ --admin-surface:#ffffffee;--admin-row:#f8faf8;
5
+ --admin-border:#d5ddd8;--admin-row-border:#dce5df;
6
+ --admin-accent:#286e43;--admin-accent-surface:#e0f0e5;
7
+ --admin-error:#a5443b;--admin-card-radius:16px;--admin-control-radius:9px;
8
+ --admin-card-padding:22px;
9
+ --admin-font:14px;--admin-heading:24px;--admin-title:18px;--admin-value:16px;--admin-label:12px;
10
+ --admin-space-sm:6px;--admin-space:12px;--admin-space-lg:18px;--admin-control-height:44px;
11
+ --admin-login-width:400px;--admin-status-height:42px;
12
+ --admin-footer-caption:12px;--admin-footer-opacity:0.75;--admin-footer-space:24px;
13
+ }`;
14
+
15
+ export const adminFooterStyles = `body{display:flex;flex-direction:column}
16
+ body>main{width:100%;flex:1;margin:0 auto}
17
+ .admin-footer{flex-shrink:0;padding:0 var(--admin-footer-space) var(--admin-footer-space);text-align:center;color:var(--admin-muted);font-size:var(--admin-footer-caption);opacity:var(--admin-footer-opacity)}`;
18
+
19
+ export const adminAuthStyles = `
20
+ *{box-sizing:border-box}.admin-login{margin:0;min-height:100vh;background:var(--admin-page);color:var(--admin-text);font:var(--admin-font)/1.5 system-ui,"Microsoft YaHei",sans-serif}
21
+ .admin-login main{display:grid;place-items:center;padding:var(--admin-card-padding)}
22
+ .login-card{width:100%;max-width:var(--admin-login-width);padding:var(--admin-card-padding);background:var(--admin-surface);border:1px solid var(--admin-border);border-radius:var(--admin-card-radius);display:grid;gap:var(--admin-space)}
23
+ .login-card h1{font-size:var(--admin-heading);margin:0}.login-card p{margin:0;color:var(--admin-muted)}
24
+ .login-card input,.login-card button{font:inherit;min-height:var(--admin-control-height);padding:var(--admin-space);border:1px solid var(--admin-border);border-radius:var(--admin-control-radius)}
25
+ .login-card input{width:100%;background:var(--admin-surface);color:var(--admin-text)}
26
+ .login-card button{background:var(--admin-accent-surface);color:var(--admin-accent);cursor:pointer;font-weight:600}
27
+ .login-card button:disabled{opacity:.6;cursor:wait}.login-card :focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
28
+ .login-card #result{min-height:var(--admin-status-height);color:var(--admin-error)}
29
+ .page-head .admin-logout{margin-left:auto;flex:none;white-space:nowrap}
30
+ @media(max-width:600px){.page-head{flex-wrap:wrap}.page-head .nav{order:3;flex-basis:100%}}`;
31
+
32
+ export const adminWorkstationStyles = `
33
+ .workstation-content{display:grid;gap:var(--admin-space-lg)}
34
+ .workstation-row{display:grid;gap:var(--admin-space-lg);width:100%;text-align:left;text-decoration:none;min-height:var(--admin-control-height);padding:var(--admin-card-padding);border:1px solid var(--admin-row-border);border-radius:var(--admin-card-radius);background:var(--admin-row);color:var(--admin-text);font-weight:400}
35
+ .workstation-row:hover{background:var(--admin-accent-surface);color:var(--admin-text)}
36
+ .workstation-row:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
37
+ .workstation-row-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--admin-space)}
38
+ .workstation-row-heading strong{min-width:0;overflow-wrap:anywhere;font-size:var(--admin-title)}
39
+ .workstation-state{display:inline-flex;flex:none;align-items:center;padding:var(--admin-space-sm) var(--admin-space);border-radius:var(--admin-card-radius);background:var(--admin-accent-surface);color:var(--admin-accent);font-size:var(--admin-label);font-weight:700}
40
+ .workstation-state.offline{background:var(--admin-row-border);color:var(--admin-muted)}
41
+ .workstation-key-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,2fr) minmax(0,1fr);gap:var(--admin-space-lg);margin:0}
42
+ .workstation-key{min-width:0}.workstation-key dt{color:var(--admin-muted);font-size:var(--admin-label);margin-bottom:var(--admin-space-sm)}
43
+ .workstation-key dd{margin:0;font-size:var(--admin-value);font-weight:600;overflow-wrap:anywhere;color:var(--admin-text)}
44
+ .workstation-key dd.working{color:var(--admin-accent)}
45
+ .workstation-account-note{display:block;font-size:var(--admin-label);font-weight:400;color:var(--admin-muted);margin-top:var(--admin-space-sm)}
46
+ .workstation-row-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--admin-space)}
47
+ .workstation-more{flex:none;color:var(--admin-accent);font-size:var(--admin-label)}
48
+ .workstation-overview{display:grid;gap:var(--admin-space-lg);padding:var(--admin-card-padding);background:var(--admin-row);border:1px solid var(--admin-row-border);border-radius:var(--admin-card-radius)}
49
+ .workstation-disclosure{min-width:0;border:1px solid var(--admin-border);border-radius:var(--admin-control-radius);background:var(--admin-surface)}
50
+ .workstation-disclosure>summary{cursor:pointer;padding:var(--admin-space);font-weight:600;min-height:var(--admin-control-height);color:var(--admin-text)}
51
+ .workstation-disclosure>summary:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
52
+ .workstation-disclosure-content{display:grid;gap:var(--admin-space);padding:0 var(--admin-space) var(--admin-space)}
53
+ .workstation-phone{display:grid;gap:var(--admin-space);padding:var(--admin-space);background:var(--admin-row);border-radius:var(--admin-control-radius)}
54
+ .workstation-block{display:grid;gap:var(--admin-space);min-width:0;padding:var(--admin-space);border:1px solid var(--admin-border);border-radius:var(--admin-control-radius)}
55
+ .workstation-fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--admin-space);margin:0}
56
+ .workstation-fields>div{min-width:0}.workstation-fields dd{margin:0;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
57
+ #workstationBack[hidden]{display:none}
58
+ @media(max-width:600px){.workstation-fields{grid-template-columns:minmax(0,1fr)}.workstation-row,.workstation-overview{padding:var(--admin-space)}.workstation-key-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.workstation-key-account{grid-column:1/-1;order:3}.workstation-row-heading{align-items:start}}`;