@sleep2agi/agent-network-dashboard 0.6.3-preview.1 → 0.6.3-preview.10

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 (348) hide show
  1. package/.next/BUILD_COMMIT +1 -1
  2. package/.next/BUILD_ID +1 -1
  3. package/.next/app-path-routes-manifest.json +5 -0
  4. package/.next/build-manifest.json +3 -3
  5. package/.next/cache/.previewinfo +1 -0
  6. package/.next/cache/.rscinfo +1 -0
  7. package/.next/diagnostics/route-bundle-stats.json +56 -39
  8. package/.next/fallback-build-manifest.json +3 -3
  9. package/.next/prerender-manifest.json +27 -3
  10. package/.next/routes-manifest.json +30 -0
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/server/app/_not-found.html +2 -2
  20. package/.next/server/app/_not-found.rsc +13 -13
  21. package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
  22. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  23. package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -8
  24. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  25. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  26. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  27. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  28. package/.next/server/app/admin.html +2 -2
  29. package/.next/server/app/admin.rsc +15 -15
  30. package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
  31. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  32. package/.next/server/app/admin.segments/_index.segment.rsc +8 -8
  33. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  34. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  35. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  36. package/.next/server/app/api/anet/host-supervisors/route/app-paths-manifest.json +3 -0
  37. package/.next/server/app/api/anet/host-supervisors/route/build-manifest.json +9 -0
  38. package/.next/server/app/api/anet/host-supervisors/route/server-reference-manifest.json +4 -0
  39. package/.next/server/app/api/anet/host-supervisors/route.js +7 -0
  40. package/.next/server/app/api/anet/host-supervisors/route.js.map +5 -0
  41. package/.next/server/app/api/anet/host-supervisors/route.js.nft.json +1 -0
  42. package/.next/server/app/api/anet/host-supervisors/route_client-reference-manifest.js +3 -0
  43. package/.next/server/app/api/anet/providers/probe/route/app-paths-manifest.json +3 -0
  44. package/.next/server/app/api/anet/providers/probe/route/build-manifest.json +9 -0
  45. package/.next/server/app/api/anet/providers/probe/route/server-reference-manifest.json +4 -0
  46. package/.next/server/app/api/anet/providers/probe/route.js +7 -0
  47. package/.next/server/app/api/anet/providers/probe/route.js.map +5 -0
  48. package/.next/server/app/api/anet/providers/probe/route.js.nft.json +1 -0
  49. package/.next/server/app/api/anet/providers/probe/route_client-reference-manifest.js +3 -0
  50. package/.next/server/app/api/anet/providers/probe-results/route/app-paths-manifest.json +3 -0
  51. package/.next/server/app/api/anet/providers/probe-results/route/build-manifest.json +9 -0
  52. package/.next/server/app/api/anet/providers/probe-results/route/server-reference-manifest.json +4 -0
  53. package/.next/server/app/api/anet/providers/probe-results/route.js +7 -0
  54. package/.next/server/app/api/anet/providers/probe-results/route.js.map +5 -0
  55. package/.next/server/app/api/anet/providers/probe-results/route.js.nft.json +1 -0
  56. package/.next/server/app/api/anet/providers/probe-results/route_client-reference-manifest.js +3 -0
  57. package/.next/server/app/api/anet/providers/route/app-paths-manifest.json +3 -0
  58. package/.next/server/app/api/anet/providers/route/build-manifest.json +9 -0
  59. package/.next/server/app/api/anet/providers/route/server-reference-manifest.json +4 -0
  60. package/.next/server/app/api/anet/providers/route.js +8 -0
  61. package/.next/server/app/api/anet/providers/route.js.map +5 -0
  62. package/.next/server/app/api/anet/providers/route.js.nft.json +1 -0
  63. package/.next/server/app/api/anet/providers/route_client-reference-manifest.js +3 -0
  64. package/.next/server/app/index.html +2 -2
  65. package/.next/server/app/index.rsc +15 -15
  66. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  67. package/.next/server/app/index.segments/_full.segment.rsc +15 -15
  68. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  69. package/.next/server/app/index.segments/_index.segment.rsc +8 -8
  70. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  71. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  72. package/.next/server/app/login.html +2 -2
  73. package/.next/server/app/login.rsc +15 -15
  74. package/.next/server/app/login.segments/_full.segment.rsc +15 -15
  75. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  76. package/.next/server/app/login.segments/_index.segment.rsc +8 -8
  77. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  78. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  79. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  80. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app/logs.html +2 -2
  82. package/.next/server/app/logs.rsc +15 -15
  83. package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
  84. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  85. package/.next/server/app/logs.segments/_index.segment.rsc +8 -8
  86. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  87. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  88. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  89. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  90. package/.next/server/app/messages.html +2 -2
  91. package/.next/server/app/messages.rsc +15 -15
  92. package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
  93. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  94. package/.next/server/app/messages.segments/_index.segment.rsc +8 -8
  95. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  96. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  97. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  98. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  99. package/.next/server/app/node.html +2 -2
  100. package/.next/server/app/node.rsc +15 -15
  101. package/.next/server/app/node.segments/_full.segment.rsc +15 -15
  102. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  103. package/.next/server/app/node.segments/_index.segment.rsc +8 -8
  104. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  105. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  106. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  107. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  108. package/.next/server/app/nodes.html +2 -2
  109. package/.next/server/app/nodes.rsc +15 -15
  110. package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
  111. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  112. package/.next/server/app/nodes.segments/_index.segment.rsc +8 -8
  113. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  114. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  115. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  116. package/.next/server/app/page_client-reference-manifest.js +1 -1
  117. package/.next/server/app/providers/page/app-paths-manifest.json +3 -0
  118. package/.next/server/app/providers/page/build-manifest.json +17 -0
  119. package/.next/server/app/providers/page/next-font-manifest.json +11 -0
  120. package/.next/server/app/providers/page/react-loadable-manifest.json +1 -0
  121. package/.next/server/app/providers/page/server-reference-manifest.json +4 -0
  122. package/.next/server/app/providers/page.js +14 -0
  123. package/.next/server/app/providers/page.js.map +5 -0
  124. package/.next/server/app/providers/page.js.nft.json +1 -0
  125. package/.next/server/app/providers/page_client-reference-manifest.js +3 -0
  126. package/.next/server/app/providers.html +12 -0
  127. package/.next/server/app/providers.meta +15 -0
  128. package/.next/server/app/providers.rsc +27 -0
  129. package/.next/server/app/providers.segments/_full.segment.rsc +27 -0
  130. package/.next/server/app/providers.segments/_head.segment.rsc +6 -0
  131. package/.next/server/app/providers.segments/_index.segment.rsc +10 -0
  132. package/.next/server/app/providers.segments/_tree.segment.rsc +5 -0
  133. package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +9 -0
  134. package/.next/server/app/providers.segments/providers.segment.rsc +5 -0
  135. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  136. package/.next/server/app/server-logs.html +2 -2
  137. package/.next/server/app/server-logs.rsc +15 -15
  138. package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
  139. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  140. package/.next/server/app/server-logs.segments/_index.segment.rsc +8 -8
  141. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  142. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  143. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  144. package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
  145. package/.next/server/app/servers.html +2 -2
  146. package/.next/server/app/servers.rsc +15 -15
  147. package/.next/server/app/servers.segments/_full.segment.rsc +15 -15
  148. package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
  149. package/.next/server/app/servers.segments/_index.segment.rsc +8 -8
  150. package/.next/server/app/servers.segments/_tree.segment.rsc +2 -2
  151. package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
  152. package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
  153. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  154. package/.next/server/app/settings/networks.html +2 -2
  155. package/.next/server/app/settings/networks.rsc +15 -15
  156. package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
  157. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  158. package/.next/server/app/settings/networks.segments/_index.segment.rsc +8 -8
  159. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  160. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  161. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  162. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  163. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  164. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  165. package/.next/server/app/settings/tokens.html +2 -2
  166. package/.next/server/app/settings/tokens.rsc +15 -15
  167. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
  168. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  169. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +8 -8
  170. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  171. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  172. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  173. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  174. package/.next/server/app/settings.html +2 -2
  175. package/.next/server/app/settings.rsc +15 -15
  176. package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
  177. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  178. package/.next/server/app/settings.segments/_index.segment.rsc +8 -8
  179. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  180. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  181. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  182. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  183. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  184. package/.next/server/app/tasks.html +2 -2
  185. package/.next/server/app/tasks.rsc +15 -15
  186. package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
  187. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  188. package/.next/server/app/tasks.segments/_index.segment.rsc +8 -8
  189. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  190. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  191. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  192. package/.next/server/app-paths-manifest.json +5 -0
  193. package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_0bobkt3.js +3 -0
  194. package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_0bobkt3.js.map +1 -0
  195. package/.next/server/chunks/0dk5_next-internal_server_app_api_anet_providers_probe-results_route_actions_07.d.5a.js +3 -0
  196. package/.next/server/chunks/0dk5_next-internal_server_app_api_anet_providers_probe-results_route_actions_07.d.5a.js.map +1 -0
  197. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_host-supervisors_route_actions_0g3hek2.js +3 -0
  198. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_host-supervisors_route_actions_0g3hek2.js.map +1 -0
  199. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_probe_route_actions_05f000..js +3 -0
  200. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_probe_route_actions_05f000..js.map +1 -0
  201. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_route_actions_10htbhd.js +3 -0
  202. package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_route_actions_10htbhd.js.map +1 -0
  203. package/.next/server/chunks/[root-of-the-server]__0-29ynz._.js.map +1 -1
  204. package/.next/server/chunks/[root-of-the-server]__0-2le8-._.js.map +1 -1
  205. package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js.map +1 -1
  206. package/.next/server/chunks/[root-of-the-server]__02p2n3n._.js.map +1 -1
  207. package/.next/server/chunks/[root-of-the-server]__03nze3z._.js +3 -0
  208. package/.next/server/chunks/[root-of-the-server]__03nze3z._.js.map +1 -0
  209. package/.next/server/chunks/[root-of-the-server]__043tnx2._.js.map +1 -1
  210. package/.next/server/chunks/[root-of-the-server]__04z62zk._.js.map +1 -1
  211. package/.next/server/chunks/[root-of-the-server]__07ztet2._.js.map +1 -1
  212. package/.next/server/chunks/[root-of-the-server]__0aqb4n5._.js +1 -1
  213. package/.next/server/chunks/[root-of-the-server]__0aqb4n5._.js.map +1 -1
  214. package/.next/server/chunks/[root-of-the-server]__0cphmer._.js +3 -0
  215. package/.next/server/chunks/[root-of-the-server]__0cphmer._.js.map +1 -0
  216. package/.next/server/chunks/[root-of-the-server]__0e3ucl1._.js.map +1 -1
  217. package/.next/server/chunks/[root-of-the-server]__0e6yail._.js.map +1 -1
  218. package/.next/server/chunks/[root-of-the-server]__0gx2_6a._.js.map +1 -1
  219. package/.next/server/chunks/[root-of-the-server]__0hg3_8r._.js.map +1 -1
  220. package/.next/server/chunks/[root-of-the-server]__0lhq4ha._.js.map +1 -1
  221. package/.next/server/chunks/[root-of-the-server]__0mkwzz7._.js +1 -1
  222. package/.next/server/chunks/[root-of-the-server]__0mkwzz7._.js.map +1 -1
  223. package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js.map +1 -1
  224. package/.next/server/chunks/[root-of-the-server]__0mzopco._.js.map +1 -1
  225. package/.next/server/chunks/[root-of-the-server]__0o45a4q._.js.map +1 -1
  226. package/.next/server/chunks/[root-of-the-server]__0w1w_w0._.js +3 -0
  227. package/.next/server/chunks/[root-of-the-server]__0w1w_w0._.js.map +1 -0
  228. package/.next/server/chunks/[root-of-the-server]__0xtp-et._.js.map +1 -1
  229. package/.next/server/chunks/[root-of-the-server]__0yyo7k5._.js +1 -1
  230. package/.next/server/chunks/[root-of-the-server]__0yyo7k5._.js.map +1 -1
  231. package/.next/server/chunks/[root-of-the-server]__0~rwgo1._.js +3 -0
  232. package/.next/server/chunks/[root-of-the-server]__0~rwgo1._.js.map +1 -0
  233. package/.next/server/chunks/[root-of-the-server]__10pvabe._.js.map +1 -1
  234. package/.next/server/chunks/[root-of-the-server]__10t11zq._.js.map +1 -1
  235. package/.next/server/chunks/[root-of-the-server]__12r~omy._.js.map +1 -1
  236. package/.next/server/chunks/[root-of-the-server]__1389~cs._.js.map +1 -1
  237. package/.next/server/chunks/[root-of-the-server]__13n8t..._.js.map +1 -1
  238. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02lorbl.js +4 -0
  239. package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02lorbl.js.map +1 -0
  240. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
  241. package/.next/server/chunks/ssr/[root-of-the-server]__04mx~5g._.js +3 -0
  242. package/.next/server/chunks/ssr/[root-of-the-server]__04mx~5g._.js.map +1 -0
  243. package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +1 -1
  244. package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -1
  245. package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js.map +1 -1
  246. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  247. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  248. package/.next/server/chunks/ssr/[root-of-the-server]__0h5dae6._.js +1 -1
  249. package/.next/server/chunks/ssr/[root-of-the-server]__0h5dae6._.js.map +1 -1
  250. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
  251. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  252. package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_providers_page_actions_0ou6p43.js +3 -0
  253. package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_providers_page_actions_0ou6p43.js.map +1 -0
  254. package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js +1 -1
  255. package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js.map +1 -1
  256. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  257. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  258. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  259. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  260. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  261. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  262. package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js +1 -1
  263. package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js.map +1 -1
  264. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  265. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js +1 -1
  266. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js.map +1 -1
  267. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_EmptyState_tsx_0aol-el._.js +3 -0
  268. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_EmptyState_tsx_0aol-el._.js.map +1 -0
  269. package/.next/server/chunks/ssr/agent-network-dashboard_app_nodes_page_tsx_11haf_p._.js +2 -1
  270. package/.next/server/chunks/ssr/agent-network-dashboard_app_nodes_page_tsx_11haf_p._.js.map +1 -1
  271. package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js +3 -0
  272. package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js.map +1 -0
  273. package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -1
  274. package/.next/server/middleware-build-manifest.js +3 -3
  275. package/.next/server/next-font-manifest.js +1 -1
  276. package/.next/server/next-font-manifest.json +4 -0
  277. package/.next/server/pages/404.html +2 -2
  278. package/.next/server/pages/500.html +1 -1
  279. package/.next/server/server-reference-manifest.js +1 -1
  280. package/.next/server/server-reference-manifest.json +1 -1
  281. package/.next/static/chunks/{10kkhdde5ve~c.js → 0-z_kfck7-6_1.js} +3 -3
  282. package/.next/static/chunks/049vx3qljs1tt.js +1 -0
  283. package/.next/static/chunks/06.5euf239uvr.css +1 -0
  284. package/.next/static/chunks/{0vun~4ljcrj3p.js → 091hae.lhv9t1.js} +2 -2
  285. package/.next/static/chunks/0chpv7zsh-qrg.js +1 -0
  286. package/.next/static/chunks/0i-l67o_ae5hk.js +2 -0
  287. package/.next/static/chunks/{0vus9xpsz0s~i.js → 0l.gu-iqnmeja.js} +1 -1
  288. package/.next/static/chunks/{149a4l50_3vw-.js → 0mnmn-~ava1vy.js} +1 -1
  289. package/.next/static/chunks/0o-b62_a66.jr.js +1 -0
  290. package/.next/static/chunks/0pk6amtz5icbx.js +1 -0
  291. package/.next/static/chunks/0r4u1.sl.8p15.js +1 -0
  292. package/.next/static/chunks/0wz0122ym_gr3.js +1 -0
  293. package/.next/static/chunks/15aqdc5txzq9r.js +1 -0
  294. package/.next/trace +2 -2
  295. package/.next/trace-build +1 -1
  296. package/.next/types/routes.d.ts +7 -2
  297. package/.next/types/validator.ts +45 -0
  298. package/app/admin/page.tsx +3 -1
  299. package/app/api/anet/host-supervisors/route.ts +102 -0
  300. package/app/api/anet/node-config/route.ts +26 -2
  301. package/app/api/anet/node-create/route.ts +14 -5
  302. package/app/api/anet/node-lifecycle/route.ts +54 -5
  303. package/app/api/anet/providers/probe/route.ts +79 -0
  304. package/app/api/anet/providers/probe-results/route.ts +60 -0
  305. package/app/api/anet/providers/route.ts +191 -0
  306. package/app/api/hub/auth/route.ts +0 -12
  307. package/app/api/hub/networks/route.ts +1 -1
  308. package/app/api/hub/nodes/route.ts +0 -14
  309. package/app/api/hub/upload/route.ts +0 -9
  310. package/app/components/CommandCenter.tsx +3 -6
  311. package/app/components/CommandPalette.tsx +0 -5
  312. package/app/components/CreateNodeWizard.tsx +140 -47
  313. package/app/components/HostSupervisorPicker.tsx +283 -0
  314. package/app/components/NodeLifecycleMenu.tsx +436 -0
  315. package/app/components/NodeSettingsPanel.tsx +80 -39
  316. package/app/components/ProviderFormModal.tsx +303 -0
  317. package/app/components/ReachabilityMatrix.tsx +196 -0
  318. package/app/components/Sidebar.tsx +1 -0
  319. package/app/components/TaskDrawer.tsx +1 -0
  320. package/app/components/TopoGraph.tsx +4 -8
  321. package/app/components/UserBar.tsx +37 -0
  322. package/app/components/types.ts +11 -0
  323. package/app/lib/chat-unread.ts +0 -6
  324. package/app/lib/dashboard-auth.ts +0 -12
  325. package/app/lib/hooks.ts +22 -4
  326. package/app/lib/hub.ts +4 -0
  327. package/app/lib/network-context.tsx +0 -6
  328. package/app/lib/rate-limit.ts +2 -16
  329. package/app/node/page.tsx +12 -1
  330. package/app/nodes/page.tsx +42 -2
  331. package/app/page.tsx +1 -0
  332. package/app/providers/page.tsx +182 -0
  333. package/app/settings/networks/page.tsx +1 -1
  334. package/app/settings/page.tsx +5 -0
  335. package/app/tasks/[id]/page.tsx +1 -0
  336. package/app/tasks/page.tsx +1 -0
  337. package/package.json +1 -1
  338. package/.next/static/chunks/0-t2rbg9~-oh5.js +0 -1
  339. package/.next/static/chunks/0iv.0p9_5b36e.js +0 -1
  340. package/.next/static/chunks/0l_hz3xrjwumg.js +0 -1
  341. package/.next/static/chunks/0lmdw~i5qjqsg.js +0 -1
  342. package/.next/static/chunks/0vruzstkdr99w.js +0 -1
  343. package/.next/static/chunks/0xssg9sin~2xw.js +0 -1
  344. package/.next/static/chunks/124dtwo1k24ao.css +0 -1
  345. package/bun.lock +0 -1663
  346. /package/.next/static/{ZxdmFd_iOEELuA2Km4Q5t → tRPXkJdGLR6a0skqvMgQa}/_buildManifest.js +0 -0
  347. /package/.next/static/{ZxdmFd_iOEELuA2Km4Q5t → tRPXkJdGLR6a0skqvMgQa}/_clientMiddlewareManifest.js +0 -0
  348. /package/.next/static/{ZxdmFd_iOEELuA2Km4Q5t → tRPXkJdGLR6a0skqvMgQa}/_ssgManifest.js +0 -0
@@ -0,0 +1,436 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ // RFC-027 PR2 — per-node ⋮ lifecycle menu + stop/delete modals.
6
+ //
7
+ // State-aware menu items (RFC-027 §3.1 table):
8
+ // active → 重启 / 停止 / 删除
9
+ // stopping/deleting → loading (all disabled,菜单 surfaces "请稍候")
10
+ // stopped → 启动 / 删除 (停止 灰掉 — already stopped)
11
+ // stop_failed → 启动 / 删除 (warn — operator should investigate)
12
+ //
13
+ // Stop modal: low-friction confirm (RFC-027 §3.3)
14
+ // Delete modal: alias-input confirm + force-on-in-flight checkbox
15
+ // (RFC-027 §3.2; mirrors hub's confirm_alias gate + force semantics).
16
+ //
17
+ // Error propagation per 通信龙 PR2 requirement: structured payloads
18
+ // from hub (node_not_active / cannot_delete_daemon_via_delete_node /
19
+ // node_busy_in_flight / confirm_alias_mismatch / forbidden_cross_tenant)
20
+ // land in `lastError` with their real fields surfaced in the UI — no
21
+ // generic "操作失败".
22
+ //
23
+ // React rules-of-hooks: all hooks declared BEFORE early returns.
24
+
25
+ export type LifecycleState =
26
+ | 'active'
27
+ | 'stopping'
28
+ | 'stopped'
29
+ | 'stop_failed'
30
+ | 'deleting'
31
+ | string; // forward-compat for unknown server values
32
+
33
+ export interface NodeLifecycleMenuProps {
34
+ nodeId: string;
35
+ alias: string;
36
+ lifecycleState: LifecycleState | null | undefined;
37
+ networkId?: string | null;
38
+ /** Called when a lifecycle op completes successfully (caller refreshes nodes list). */
39
+ onCompleted?: () => void;
40
+ }
41
+
42
+ type LifecycleAction = 'restart' | 'stop' | 'delete';
43
+
44
+ type ApiReply = {
45
+ ok?: boolean;
46
+ error?: string;
47
+ message?: string;
48
+ in_flight_count?: number;
49
+ hint?: string;
50
+ lifecycle_state?: string;
51
+ unconfirmed?: boolean;
52
+ [k: string]: unknown;
53
+ };
54
+
55
+ const normalizeState = (s: LifecycleState | null | undefined): LifecycleState =>
56
+ (typeof s === 'string' && s.length > 0 ? s : 'active');
57
+
58
+ async function callLifecycle(
59
+ action: LifecycleAction,
60
+ body: Record<string, unknown>,
61
+ ): Promise<ApiReply & { httpStatus: number }> {
62
+ try {
63
+ const r = await fetch('/api/anet/node-lifecycle', {
64
+ method: 'POST',
65
+ headers: { 'Content-Type': 'application/json' },
66
+ body: JSON.stringify({ action, ...body }),
67
+ });
68
+ const data = (await r.json().catch(() => ({}))) as ApiReply;
69
+ return { ...data, httpStatus: r.status };
70
+ } catch (e: unknown) {
71
+ return {
72
+ ok: false,
73
+ error: 'network_error',
74
+ message: e instanceof Error ? e.message : String(e),
75
+ httpStatus: 0,
76
+ };
77
+ }
78
+ }
79
+
80
+ export function NodeLifecycleMenu({
81
+ nodeId,
82
+ alias,
83
+ lifecycleState,
84
+ networkId,
85
+ onCompleted,
86
+ }: NodeLifecycleMenuProps) {
87
+ // All hooks above any early return (avoid hooks-order regression that
88
+ // bit the mobile app at v0.1.29; 通信龙 explicit call-out for PR2).
89
+ const [menuOpen, setMenuOpen] = useState(false);
90
+ const [stopModal, setStopModal] = useState(false);
91
+ const [deleteModal, setDeleteModal] = useState(false);
92
+ const [restartBusy, setRestartBusy] = useState(false);
93
+ const [lastError, setLastError] = useState<ApiReply | null>(null);
94
+ const menuRef = useRef<HTMLDivElement | null>(null);
95
+
96
+ // Outside-click + Escape close the menu (focus management).
97
+ useEffect(() => {
98
+ if (!menuOpen) return;
99
+ const onDoc = (e: MouseEvent) => {
100
+ if (menuRef.current && !menuRef.current.contains(e.target as Node)) setMenuOpen(false);
101
+ };
102
+ const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMenuOpen(false); };
103
+ document.addEventListener('mousedown', onDoc);
104
+ document.addEventListener('keydown', onKey);
105
+ return () => {
106
+ document.removeEventListener('mousedown', onDoc);
107
+ document.removeEventListener('keydown', onKey);
108
+ };
109
+ }, [menuOpen]);
110
+
111
+ const onRestart = useCallback(async () => {
112
+ setMenuOpen(false);
113
+ setLastError(null);
114
+ setRestartBusy(true);
115
+ const r = await callLifecycle('restart', {
116
+ node_id: nodeId,
117
+ ...(networkId ? { network_id: networkId } : {}),
118
+ });
119
+ setRestartBusy(false);
120
+ if (r.ok) {
121
+ onCompleted?.();
122
+ return;
123
+ }
124
+ setLastError(r);
125
+ }, [nodeId, networkId, onCompleted]);
126
+
127
+ const state = normalizeState(lifecycleState);
128
+ const isLoading = state === 'stopping' || state === 'deleting';
129
+ const isActive = state === 'active';
130
+ const isStopped = state === 'stopped' || state === 'stop_failed';
131
+
132
+ // Menu items per RFC-027 §3.1 table.
133
+ const items: Array<{ label: string; key: string; danger?: boolean; onClick: () => void; disabled?: boolean }> = [];
134
+ if (isLoading) {
135
+ items.push({
136
+ label: state === 'stopping' ? '停止中…' : '删除中…',
137
+ key: 'loading', disabled: true, onClick: () => {},
138
+ });
139
+ } else if (isActive) {
140
+ items.push({ label: restartBusy ? '重启中…' : '重启节点', key: 'restart', disabled: restartBusy, onClick: onRestart });
141
+ items.push({ label: '停止节点', key: 'stop', onClick: () => { setMenuOpen(false); setStopModal(true); } });
142
+ items.push({ label: '删除节点', key: 'delete', danger: true, onClick: () => { setMenuOpen(false); setDeleteModal(true); } });
143
+ } else if (isStopped) {
144
+ // stopped + stop_failed: "启动" = restart_node (hub resets lifecycle_state→active on success per PR1.2a)
145
+ items.push({ label: restartBusy ? '启动中…' : '启动节点', key: 'restart', disabled: restartBusy, onClick: onRestart });
146
+ items.push({ label: '删除节点', key: 'delete', danger: true, onClick: () => { setMenuOpen(false); setDeleteModal(true); } });
147
+ } else {
148
+ // Unknown state — surface read-only badge, no destructive action.
149
+ items.push({
150
+ label: `状态 ${state} (不识别)`,
151
+ key: 'unknown', disabled: true, onClick: () => {},
152
+ });
153
+ }
154
+
155
+ return (
156
+ <div ref={menuRef} className="relative inline-block" onClick={e => e.stopPropagation()}>
157
+ <button
158
+ type="button"
159
+ aria-label={`节点 ${alias} 生命周期操作`}
160
+ aria-haspopup="menu"
161
+ aria-expanded={menuOpen}
162
+ onClick={() => setMenuOpen(v => !v)}
163
+ className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-400 hover:bg-white/5 hover:text-white focus:outline-none focus:ring-1 focus:ring-cyan-500/50"
164
+ >
165
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
166
+ <circle cx="12" cy="5" r="1.6" />
167
+ <circle cx="12" cy="12" r="1.6" />
168
+ <circle cx="12" cy="19" r="1.6" />
169
+ </svg>
170
+ </button>
171
+ {menuOpen && (
172
+ <div role="menu" className="absolute right-0 z-40 mt-1 w-44 overflow-hidden rounded-md border border-[#26262b] bg-[#161618] shadow-xl">
173
+ {items.map(item => (
174
+ <button
175
+ key={item.key}
176
+ type="button"
177
+ role="menuitem"
178
+ disabled={item.disabled}
179
+ onClick={item.onClick}
180
+ className={`block w-full px-3 py-2 text-left text-sm transition-colors ${
181
+ item.disabled
182
+ ? 'text-gray-600 cursor-not-allowed'
183
+ : item.danger
184
+ ? 'text-red-300 hover:bg-red-900/20'
185
+ : 'text-gray-200 hover:bg-white/5'
186
+ }`}
187
+ >
188
+ {item.label}
189
+ </button>
190
+ ))}
191
+ </div>
192
+ )}
193
+
194
+ {/* Last error inline (under the button) — pass-through hub's structured fields */}
195
+ {lastError && lastError.ok === false && !stopModal && !deleteModal && (
196
+ <div className="absolute right-0 z-40 mt-1 w-72 rounded-md border border-red-700/40 bg-red-900/20 px-3 py-2 text-xs text-red-200">
197
+ <div className="flex items-start justify-between gap-2">
198
+ <div className="min-w-0 flex-1">
199
+ <div className="font-semibold">{lastError.error || 'error'}</div>
200
+ {lastError.message ? <div className="mt-0.5 text-red-300/80">{lastError.message}</div> : null}
201
+ {typeof lastError.in_flight_count === 'number' && (
202
+ <div className="mt-0.5 text-red-300/80">in-flight: {lastError.in_flight_count}</div>
203
+ )}
204
+ {lastError.hint ? <div className="mt-1 text-red-300/60">{lastError.hint}</div> : null}
205
+ </div>
206
+ <button type="button" onClick={() => setLastError(null)} className="shrink-0 text-red-400 hover:text-red-200" aria-label="关闭">×</button>
207
+ </div>
208
+ </div>
209
+ )}
210
+
211
+ {stopModal && (
212
+ <StopNodeModal
213
+ alias={alias}
214
+ nodeId={nodeId}
215
+ networkId={networkId}
216
+ onClose={() => setStopModal(false)}
217
+ onCompleted={() => { setStopModal(false); onCompleted?.(); }}
218
+ />
219
+ )}
220
+ {deleteModal && (
221
+ <DeleteNodeModal
222
+ alias={alias}
223
+ nodeId={nodeId}
224
+ networkId={networkId}
225
+ onClose={() => setDeleteModal(false)}
226
+ onCompleted={() => { setDeleteModal(false); onCompleted?.(); }}
227
+ />
228
+ )}
229
+ </div>
230
+ );
231
+ }
232
+
233
+ // ──────────────────────────────────────────────────────────────────────
234
+
235
+ interface ModalProps {
236
+ alias: string;
237
+ nodeId: string;
238
+ networkId?: string | null;
239
+ onClose: () => void;
240
+ onCompleted: () => void;
241
+ }
242
+
243
+ function StopNodeModal({ alias, nodeId, networkId, onClose, onCompleted }: ModalProps) {
244
+ const [busy, setBusy] = useState(false);
245
+ const [force, setForce] = useState(false);
246
+ const [reply, setReply] = useState<ApiReply | null>(null);
247
+
248
+ useEffect(() => {
249
+ const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && !busy) onClose(); };
250
+ window.addEventListener('keydown', onKey);
251
+ return () => window.removeEventListener('keydown', onKey);
252
+ }, [onClose, busy]);
253
+
254
+ const inFlight = reply && typeof reply.in_flight_count === 'number' ? reply.in_flight_count : 0;
255
+
256
+ const onConfirm = async () => {
257
+ setBusy(true);
258
+ setReply(null);
259
+ const r = await callLifecycle('stop', {
260
+ node_id: nodeId,
261
+ ...(networkId ? { network_id: networkId } : {}),
262
+ ...(force ? { force: true } : {}),
263
+ });
264
+ setBusy(false);
265
+ if (r.ok) { onCompleted(); return; }
266
+ setReply(r);
267
+ };
268
+
269
+ return (
270
+ <>
271
+ <div className="fixed inset-0 z-40 bg-black/50" onClick={busy ? undefined : onClose} aria-hidden />
272
+ <div role="dialog" aria-label={`停止节点 ${alias}`} className="fixed left-1/2 top-1/2 z-50 w-[min(92vw,420px)] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl">
273
+ <div className="flex items-center justify-between border-b border-[#26262b] px-4 py-3">
274
+ <div className="text-sm font-semibold text-white">停止节点</div>
275
+ <button onClick={busy ? undefined : onClose} aria-label="关闭" className="text-gray-500 hover:text-white">×</button>
276
+ </div>
277
+ <div className="space-y-3 px-4 py-4">
278
+ <div className="text-sm text-gray-300">
279
+ 将停止 <span className="font-mono text-cyan-300">{alias}</span> 子进程(SIGTERM,~10s 内)。配置目录保留,可通过「启动」恢复。
280
+ </div>
281
+ {/* Surface hub's structured payload field-by-field; never collapse to generic */}
282
+ {reply && reply.ok === false && (
283
+ <div className="rounded-md border border-red-700/40 bg-red-900/20 px-3 py-2 text-xs text-red-200">
284
+ <div className="font-semibold">{reply.error}</div>
285
+ {reply.message ? <div className="mt-0.5 text-red-300/80">{reply.message}</div> : null}
286
+ {typeof reply.in_flight_count === 'number' && (
287
+ <div className="mt-0.5 text-red-300/80">
288
+ 当前 in-flight 任务: {reply.in_flight_count}
289
+ </div>
290
+ )}
291
+ {reply.hint ? <div className="mt-1 text-red-300/60">{reply.hint}</div> : null}
292
+ </div>
293
+ )}
294
+ {(inFlight > 0 || force) && (
295
+ <label className="flex items-start gap-2 rounded-md border border-amber-700/40 bg-amber-900/10 px-3 py-2 text-xs text-amber-200">
296
+ <input
297
+ type="checkbox"
298
+ checked={force}
299
+ onChange={e => setForce(e.target.checked)}
300
+ className="mt-0.5"
301
+ />
302
+ <span>
303
+ <span className="font-semibold text-amber-300">强制停止</span>{inFlight > 0 ? `(会丢弃 ${inFlight} 条待处理任务)` : '(覆盖 in-flight 拒绝;hub 会写 audit 行)'}
304
+ </span>
305
+ </label>
306
+ )}
307
+ </div>
308
+ <div className="flex items-center justify-end gap-2 border-t border-[#26262b] px-4 py-3">
309
+ <button type="button" disabled={busy} onClick={onClose} className="rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200 disabled:opacity-50">取消</button>
310
+ <button
311
+ type="button"
312
+ disabled={busy || (inFlight > 0 && !force)}
313
+ onClick={onConfirm}
314
+ className="rounded-md bg-cyan-600 px-4 py-1.5 text-sm font-medium text-white hover:bg-cyan-500 disabled:cursor-not-allowed disabled:opacity-50"
315
+ >
316
+ {busy ? '停止中…' : '确认停止'}
317
+ </button>
318
+ </div>
319
+ </div>
320
+ </>
321
+ );
322
+ }
323
+
324
+ function DeleteNodeModal({ alias, nodeId, networkId, onClose, onCompleted }: ModalProps) {
325
+ const [busy, setBusy] = useState(false);
326
+ const [confirmInput, setConfirmInput] = useState('');
327
+ const [force, setForce] = useState(false);
328
+ const [keepConfig, setKeepConfig] = useState(false);
329
+ const [reply, setReply] = useState<ApiReply | null>(null);
330
+
331
+ useEffect(() => {
332
+ const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && !busy) onClose(); };
333
+ window.addEventListener('keydown', onKey);
334
+ return () => window.removeEventListener('keydown', onKey);
335
+ }, [onClose, busy]);
336
+
337
+ const inFlight = reply && typeof reply.in_flight_count === 'number' ? reply.in_flight_count : 0;
338
+ const confirmValid = confirmInput === alias;
339
+
340
+ const onConfirm = async () => {
341
+ if (!confirmValid) return;
342
+ setBusy(true);
343
+ setReply(null);
344
+ const r = await callLifecycle('delete', {
345
+ node_id: nodeId,
346
+ confirm_alias: confirmInput,
347
+ ...(networkId ? { network_id: networkId } : {}),
348
+ ...(force ? { force: true } : {}),
349
+ ...(keepConfig ? { delete_config: false } : {}),
350
+ });
351
+ setBusy(false);
352
+ if (r.ok) { onCompleted(); return; }
353
+ setReply(r);
354
+ };
355
+
356
+ return (
357
+ <>
358
+ <div className="fixed inset-0 z-40 bg-black/50" onClick={busy ? undefined : onClose} aria-hidden />
359
+ <div role="dialog" aria-label={`删除节点 ${alias}`} className="fixed left-1/2 top-1/2 z-50 w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-red-700/40 bg-[#161618] shadow-2xl">
360
+ <div className="flex items-center justify-between border-b border-[#26262b] px-4 py-3">
361
+ <div className="text-sm font-semibold text-red-300">删除节点</div>
362
+ <button onClick={busy ? undefined : onClose} aria-label="关闭" className="text-gray-500 hover:text-white">×</button>
363
+ </div>
364
+ <div className="space-y-3 px-4 py-4 text-sm text-gray-300">
365
+ <div>这将:</div>
366
+ <ul className="list-inside list-disc space-y-1 pl-2 text-xs text-gray-400">
367
+ <li>停止 <span className="font-mono text-cyan-300">{alias}</span> 子进程(SIGTERM ~10s)</li>
368
+ <li>从 hub 注销该节点</li>
369
+ <li>撤销该节点的 ntok</li>
370
+ {!keepConfig && <li>配置文件备份到 <code className="text-amber-300">~/.anet/deleted/&lt;ts&gt;-{alias}/</code>(30 天后自动清除)</li>}
371
+ {keepConfig && <li className="text-amber-300">配置文件保留原地(不备份不清理 — 你勾选了「保留配置」)</li>}
372
+ </ul>
373
+
374
+ {/* Surface hub's structured payload field-by-field */}
375
+ {reply && reply.ok === false && (
376
+ <div className="rounded-md border border-red-700/40 bg-red-900/20 px-3 py-2 text-xs text-red-200">
377
+ <div className="font-semibold">{reply.error}</div>
378
+ {reply.message ? <div className="mt-0.5 text-red-300/80">{reply.message}</div> : null}
379
+ {typeof reply.in_flight_count === 'number' && (
380
+ <div className="mt-0.5 text-red-300/80">当前 in-flight 任务: {reply.in_flight_count}</div>
381
+ )}
382
+ {reply.hint ? <div className="mt-1 text-red-300/60">{reply.hint}</div> : null}
383
+ </div>
384
+ )}
385
+
386
+ {(inFlight > 0 || force) && (
387
+ <label className="flex items-start gap-2 rounded-md border border-amber-700/40 bg-amber-900/10 px-3 py-2 text-xs text-amber-200">
388
+ <input
389
+ type="checkbox"
390
+ checked={force}
391
+ onChange={e => setForce(e.target.checked)}
392
+ className="mt-0.5"
393
+ />
394
+ <span>
395
+ <span className="font-semibold text-amber-300">强制删除</span>{inFlight > 0 ? `(会丢弃 ${inFlight} 条待处理任务)` : '(覆盖 in-flight 拒绝)'}
396
+ </span>
397
+ </label>
398
+ )}
399
+
400
+ <label className="flex items-center gap-2 text-xs text-gray-400">
401
+ <input type="checkbox" checked={keepConfig} onChange={e => setKeepConfig(e.target.checked)} />
402
+ 保留配置目录(不备份;高级用法)
403
+ </label>
404
+
405
+ <label className="block space-y-1">
406
+ <span className="text-xs text-gray-400">输入节点名字 <span className="font-mono text-cyan-300">{alias}</span> 确认</span>
407
+ <input
408
+ autoFocus
409
+ value={confirmInput}
410
+ onChange={e => setConfirmInput(e.target.value)}
411
+ placeholder={alias}
412
+ className="w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-red-500 focus:outline-none"
413
+ autoCapitalize="none"
414
+ autoCorrect="off"
415
+ spellCheck={false}
416
+ />
417
+ {confirmInput && !confirmValid && (
418
+ <span className="text-[11px] text-red-400">名字不匹配(区分大小写)</span>
419
+ )}
420
+ </label>
421
+ </div>
422
+ <div className="flex items-center justify-end gap-2 border-t border-[#26262b] px-4 py-3">
423
+ <button type="button" disabled={busy} onClick={onClose} className="rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200 disabled:opacity-50">取消</button>
424
+ <button
425
+ type="button"
426
+ disabled={busy || !confirmValid || (inFlight > 0 && !force)}
427
+ onClick={onConfirm}
428
+ className="rounded-md bg-red-600 px-4 py-1.5 text-sm font-medium text-white hover:bg-red-500 disabled:cursor-not-allowed disabled:opacity-50"
429
+ >
430
+ {busy ? '删除中…' : `删除 ${alias}`}
431
+ </button>
432
+ </div>
433
+ </div>
434
+ </>
435
+ );
436
+ }
@@ -8,18 +8,22 @@ import { AliasAvatar } from './AliasAvatar';
8
8
  * Per-node settings panel (issue #260 → v0.11 flagship #262, Vincent tg
9
9
  * 2026-06-24, "must actually work").
10
10
  *
11
- * STATUS (F1/F2/F3, 通信龙 review round 2):
12
- * - **Wired (real)**: B Runtime·模型 model select + C 运行模式 flags
13
- * (permissionMode / dangerouslySkipPermissions / teammateMode / maxTurns /
14
- * budget / timeout). Controlled form state, loaded on mount from
15
- * `GET /api/anet/node-config` and persisted via `POST`. The six flags +
16
- * model are the editable contract 通信龙 specified.
11
+ * STATUS (F1/F2/F3 + A channel edit, 通信龙 review round 3):
12
+ * - **Wired (real)**: A 接入 Channel enable/disable + B Runtime·模型 model
13
+ * select + C 运行模式 flags (permissionMode / dangerouslySkipPermissions /
14
+ * teammateMode / maxTurns / budget / timeout). Controlled form state,
15
+ * loaded on mount from `GET /api/anet/node-config` and persisted via
16
+ * `POST`. The seven editable knobs are the contract 通信龙 specified.
17
17
  * - **F3 apply lifecycle**: after Save the panel runs a take-effect state
18
18
  * machine — saving → applying (node restarting) → applied | rejected |
19
19
  * timeout — polling `GET ?apply_id` for real backends and showing a colored
20
20
  * status strip ("toast") for the outcome.
21
- * - **Still P2 stub (disabled)**: A 接入 Channel (display-only) and D 运维
22
- * ops buttons (no-op), each with its own local "暂未接后端" note.
21
+ * - **Channel red-line**: only the on/off flip is on the wire. Per-channel
22
+ * bot token / app secret / allowFrom stay in the node's local config.json
23
+ * and are shown here as masked read-only (StubField). Editing a secret
24
+ * from the dashboard is a separate vault-backed feature (#393-adjacent).
25
+ * - **Still P2 stub**: D 运维 ops buttons other than restart (rename/stop/
26
+ * delete) — gated on RFC-024 rename + RFC-027 stop/delete backends.
23
27
  *
24
28
  * ⚠️ The hub-side tool is not deployed yet (工程马 WIP): the route mock-falls
25
29
  * back with `mock: true` + `status: pending`, so the lifecycle is *simulated*
@@ -41,7 +45,10 @@ const RUNTIME_MODELS: Record<string, string[]> = {
41
45
  // booleans; three numeric bounds. These six are the entire editable flag set.
42
46
  const PERMISSION_MODES = ['auto', 'default', 'bypassPermissions'];
43
47
 
44
- // A. Channel display (P2 read-only this round). `roadmap` = greyed "即将支持".
48
+ // A. Channel bindingsenable/disable is editable; the per-channel secret
49
+ // fields below stay read-only (`StubField`, masked). `roadmap` = greyed "即将
50
+ // 支持". Keep this list in sync with EDITABLE_CHANNELS in
51
+ // app/api/anet/node-config/route.ts (server-side whitelist).
45
52
  const CHANNELS: { key: string; label: string; roadmap?: boolean; fields: { label: string; value?: string }[] }[] = [
46
53
  {
47
54
  key: 'telegram', label: 'Telegram',
@@ -61,13 +68,6 @@ const CHANNELS: { key: string; label: string; roadmap?: boolean; fields: { label
61
68
  { label: '群触发策略', value: 'mention' },
62
69
  ],
63
70
  },
64
- {
65
- key: 'commhub', label: 'CommHub',
66
- fields: [
67
- { label: 'Hub 地址', value: '<hub-address>' },
68
- { label: '节点 token(自动)', value: '••••••••' },
69
- ],
70
- },
71
71
  { key: 'wechat', label: 'WeChat', roadmap: true, fields: [] },
72
72
  ];
73
73
 
@@ -223,7 +223,6 @@ function NumberField({ label, value, onChange, placeholder }: { label: string; v
223
223
  }
224
224
 
225
225
  export function NodeSettingsPanel({ session: s, onClose }: { session: Session; onClose: () => void }) {
226
- const active = channelSet(s.channels);
227
226
  const runtime = s.runtime || '—';
228
227
  const initialModel = s.model || s.agent || '';
229
228
  // imageCapable: read-only, auto from model (MiniMax-M3 / Claude = yes).
@@ -235,9 +234,13 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
235
234
 
236
235
  const nodeKey = s.node_id || s.alias;
237
236
 
238
- // ---- Editable form state (B model + C flags) ----
237
+ // ---- Editable form state (A channels + B model + C flags) ----
239
238
  const [model, setModel] = useState(initialModel);
240
239
  const [flags, setFlags] = useState<FlagsForm>(DEFAULT_FLAGS);
240
+ // Enabled channel keys — Set for O(1) toggle, serialized to array at save.
241
+ // Seeded from the session prop so the panel renders coherent state before
242
+ // the load effect resolves; hub snapshot overrides once available.
243
+ const [channels, setChannels] = useState<Set<string>>(() => channelSet(s.channels));
241
244
  const [dirty, setDirty] = useState(false);
242
245
  const [loading, setLoading] = useState(true);
243
246
  // ---- F3 apply lifecycle ----
@@ -260,9 +263,16 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
260
263
  const after = useCallback((ms: number, fn: () => void) => {
261
264
  timersRef.current.push(setTimeout(fn, ms));
262
265
  }, []);
263
- // Avoid setState after unmount (panel closes mid-request).
266
+ // Avoid setState after unmount (panel closes mid-request). Set the flag on
267
+ // each mount as well as clear on unmount — React strict-mode double-invokes
268
+ // effects in dev, which without the mount reset would leave mounted=false
269
+ // after the second attach and starve every subsequent state update (the
270
+ // loading spinner would never resolve, blocking the whole panel).
264
271
  const mounted = useRef(true);
265
- useEffect(() => () => { mounted.current = false; clearTimers(); }, [clearTimers]);
272
+ useEffect(() => {
273
+ mounted.current = true;
274
+ return () => { mounted.current = false; clearTimers(); };
275
+ }, [clearTimers]);
266
276
 
267
277
  // Monotonic run id, bumped on every save. Each async lifecycle callback
268
278
  // captures the run id it belongs to and bails if a newer save superseded it —
@@ -286,6 +296,16 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
286
296
  setDirty(true);
287
297
  }, []);
288
298
 
299
+ const toggleChannel = useCallback((key: string) => {
300
+ setChannels(prev => {
301
+ const next = new Set(prev);
302
+ if (next.has(key)) next.delete(key);
303
+ else next.add(key);
304
+ return next;
305
+ });
306
+ setDirty(true);
307
+ }, []);
308
+
289
309
  // Load current config on mount. Falls back to session-derived defaults when
290
310
  // the hub tool isn't deployed (route returns mock with empty flags).
291
311
  useEffect(() => {
@@ -305,6 +325,13 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
305
325
  budget: numStr(f.budget),
306
326
  timeout: numStr(f.timeout),
307
327
  });
328
+ // Channels: prefer hub snapshot when present (authoritative). If the
329
+ // field is absent (older hub or contract not deployed), keep the
330
+ // session-derived seed so users aren't shown an empty list on nodes
331
+ // that clearly have channels wired.
332
+ if (Array.isArray(data?.channels)) {
333
+ setChannels(new Set(data.channels.filter((c: unknown): c is string => typeof c === 'string').map((c: string) => c.trim().toLowerCase())));
334
+ }
308
335
  } catch {
309
336
  // keep defaults — Save still works (mock path) so the UI is reviewable
310
337
  } finally {
@@ -382,6 +409,7 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
382
409
  budget: numOrUndef(flags.budget),
383
410
  timeout: numOrUndef(flags.timeout),
384
411
  },
412
+ channels: Array.from(channels).sort(),
385
413
  };
386
414
  let data: { ok?: boolean; error?: string; mock?: boolean; applied?: boolean; status?: string; applyId?: string } = {};
387
415
  try {
@@ -560,7 +588,7 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
560
588
  const btnLabel = phase === 'saving' ? '保存中…'
561
589
  : phase === 'applying' ? '应用中…'
562
590
  : loading ? '加载中…'
563
- : dirty ? '保存模型 / flags'
591
+ : dirty ? '保存设置'
564
592
  : '无改动';
565
593
  return (
566
594
  <button
@@ -605,28 +633,41 @@ export function NodeSettingsPanel({ session: s, onClose }: { session: Session; o
605
633
  )}
606
634
  </section>
607
635
 
608
- {/* A. 接入 Channel (P2 display only) */}
636
+ {/* A. 接入 Channel — enable/disable editable; per-channel secrets
637
+ stay masked read-only (bot token / app secret / allowFrom live in
638
+ the node's local config.json, not on the wire from this UI). */}
609
639
  <section>
610
640
  <SectionTitle>接入 Channel</SectionTitle>
611
641
  <div className="space-y-2">
612
- {CHANNELS.map(ch => (
613
- <div key={ch.key} className={`rounded-lg border border-[#26262b] bg-[#161618] ${ch.roadmap ? 'opacity-50' : ''}`}>
614
- <label className="flex items-center justify-between px-3 py-2 text-sm text-gray-300 cursor-not-allowed">
615
- <span className="flex items-center gap-2">
616
- {ch.label}
617
- {ch.roadmap && <span className="text-[10px] px-1.5 py-0.5 rounded bg-[#2a2a30] text-gray-400">即将支持</span>}
618
- </span>
619
- <input type="checkbox" checked={!ch.roadmap && active.has(ch.key)} disabled readOnly className="h-4 w-4 accent-cyan-500 cursor-not-allowed" />
620
- </label>
621
- {!ch.roadmap && active.has(ch.key) && ch.fields.length > 0 && (
622
- <div className="px-3 pb-3 pt-1 space-y-2 border-t border-[#1c1c1f]">
623
- {ch.fields.map(f => <StubField key={f.label} label={f.label} value={f.value} />)}
624
- </div>
625
- )}
626
- </div>
627
- ))}
642
+ {CHANNELS.map(ch => {
643
+ const enabled = channels.has(ch.key);
644
+ const editable = !ch.roadmap && !loading;
645
+ return (
646
+ <div key={ch.key} className={`rounded-lg border border-[#26262b] bg-[#161618] ${ch.roadmap ? 'opacity-50' : ''}`}>
647
+ <label className={`flex items-center justify-between px-3 py-2 text-sm text-gray-300 ${editable ? 'cursor-pointer hover:bg-[#1a1a1d]' : 'cursor-not-allowed'}`}>
648
+ <span className="flex items-center gap-2">
649
+ {ch.label}
650
+ {ch.roadmap && <span className="text-[10px] px-1.5 py-0.5 rounded bg-[#2a2a30] text-gray-400">即将支持</span>}
651
+ </span>
652
+ <input
653
+ type="checkbox"
654
+ checked={!ch.roadmap && enabled}
655
+ onChange={editable ? () => toggleChannel(ch.key) : undefined}
656
+ disabled={!editable}
657
+ aria-label={`${editable ? '启用/关闭' : '(暂不可切换)'} ${ch.label}`}
658
+ className={`h-4 w-4 accent-cyan-500 ${editable ? '' : 'cursor-not-allowed'}`}
659
+ />
660
+ </label>
661
+ {!ch.roadmap && enabled && ch.fields.length > 0 && (
662
+ <div className="px-3 pb-3 pt-1 space-y-2 border-t border-[#1c1c1f]">
663
+ {ch.fields.map(f => <StubField key={f.label} label={f.label} value={f.value} />)}
664
+ </div>
665
+ )}
666
+ </div>
667
+ );
668
+ })}
628
669
  </div>
629
- <p className="mt-2 text-[11px] text-gray-600">Channel 绑定为下一阶段(P2)· 当前仅展示,暂未接后端</p>
670
+ <p className="mt-2 text-[11px] text-gray-600">勾选启用/关闭该 Channel;per-channel token / secret / allowFrom 仍保存在节点本地 config.json,不在此处编辑。</p>
630
671
  </section>
631
672
 
632
673
  {/* D. 节点操作 (M1 lifecycle — restart wired to RFC-024 restart_node;