@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,303 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useMemo, useState } from 'react';
4
+
5
+ /**
6
+ * Create/edit a model provider (RFC-028 P1 + #393 preset catalog). Single-step
7
+ * modal mirroring the CreateNodeWizard shell (escape-to-close, SVG ✕, cyan
8
+ * primary). The API key field is WRITE-ONLY: on edit of a provider that already
9
+ * has a key it shows a "•••• configured" placeholder and an empty value means
10
+ * "keep existing key"; the stored value is never fetched or displayed.
11
+ *
12
+ * #393 (Vincent UX): official providers (DeepSeek / MiniMax / GLM / Claude) are
13
+ * offered as PRESETS — picking one auto-fills the Anthropic-compatible base_url
14
+ * and turns the model field into a checkbox list of that provider's known
15
+ * models, so the operator only supplies an API key. "自定义" falls back to the
16
+ * manual base_url + comma-separated model entry for any other endpoint.
17
+ */
18
+
19
+ export interface ProviderDraft {
20
+ provider_id?: string;
21
+ name: string;
22
+ type: string;
23
+ base_url: string;
24
+ models: string[];
25
+ enabled: boolean;
26
+ hasKey?: boolean;
27
+ }
28
+
29
+ // Backend (#308) currently supports only the `anthropic` vendor — which means
30
+ // any Anthropic-COMPATIBLE endpoint (Claude api.anthropic.com, DeepSeek
31
+ // api.deepseek.com/anthropic, MiniMax api.minimax.chat/anthropic, …) via a
32
+ // custom base_url, not just Anthropic the company. Native OpenAI/Grok vendors
33
+ // aren't wired yet → shown disabled so users can't pick an erroring option.
34
+ const TYPES: { id: string; label: string; disabled?: boolean }[] = [
35
+ { id: 'anthropic', label: 'Anthropic 兼容端点(Claude / DeepSeek / MiniMax 等)' },
36
+ { id: 'openai-compatible', label: 'OpenAI-compatible(native,即将支持)', disabled: true },
37
+ { id: 'grok', label: 'Grok(native,即将支持)', disabled: true },
38
+ ];
39
+
40
+ // #393 preset catalog. Each base_url is an Anthropic-compatible endpoint
41
+ // (verified 401-reachable). `models` are sensible defaults the operator can
42
+ // tick on/off; extras can still be typed. Keep `custom` last as the escape
43
+ // hatch to the manual URL/model flow.
44
+ interface ProviderPreset {
45
+ id: string;
46
+ label: string;
47
+ type: string;
48
+ base_url: string;
49
+ models: string[];
50
+ keyPlaceholder?: string;
51
+ }
52
+ const PRESETS: ProviderPreset[] = [
53
+ { id: 'deepseek', label: 'DeepSeek', type: 'anthropic', base_url: 'https://api.deepseek.com/anthropic', models: ['deepseek-v4-pro', 'deepseek-v4-flash'], keyPlaceholder: 'sk-…' },
54
+ { id: 'minimax', label: 'MiniMax', type: 'anthropic', base_url: 'https://api.minimaxi.com/anthropic', models: ['MiniMax-M2.7'], keyPlaceholder: 'MiniMax API key' },
55
+ { id: 'glm', label: '智谱 GLM', type: 'anthropic', base_url: 'https://open.bigmodel.cn/api/anthropic', models: ['glm-4.7', 'glm-5.2', 'glm-4.6'], keyPlaceholder: '{id}.{secret}' },
56
+ { id: 'claude', label: 'Claude(Anthropic 官方)', type: 'anthropic', base_url: 'https://api.anthropic.com', models: ['claude-opus-4-8', 'claude-sonnet-5', 'claude-haiku-4-5'], keyPlaceholder: 'sk-ant-…' },
57
+ { id: 'custom', label: '自定义(手动填 URL / 模型)', type: 'anthropic', base_url: '', models: [] },
58
+ ];
59
+
60
+ function detectPreset(baseUrl?: string): string {
61
+ if (!baseUrl) return 'custom';
62
+ const hit = PRESETS.find(p => p.id !== 'custom' && p.base_url === baseUrl.trim());
63
+ return hit ? hit.id : 'custom';
64
+ }
65
+
66
+ type Phase = 'form' | 'saving' | 'done' | 'unconfirmed' | 'error';
67
+
68
+ const inputCls =
69
+ 'w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-cyan-600 focus:outline-none';
70
+
71
+ function parseModels(text: string): string[] {
72
+ return text.split(',').map(m => m.trim()).filter(Boolean);
73
+ }
74
+
75
+ export function ProviderFormModal({
76
+ initial,
77
+ onClose,
78
+ onSaved,
79
+ }: {
80
+ initial?: ProviderDraft | null;
81
+ onClose: () => void;
82
+ onSaved?: () => void;
83
+ }) {
84
+ const editing = !!initial?.provider_id;
85
+ const [presetId, setPresetId] = useState(() => detectPreset(initial?.base_url));
86
+ const [name, setName] = useState(initial?.name ?? '');
87
+ const [type, setType] = useState(initial?.type ?? TYPES[0].id);
88
+ const [baseUrl, setBaseUrl] = useState(initial?.base_url ?? '');
89
+ // Checked preset models (only used while a non-custom preset is selected).
90
+ const [checkedModels, setCheckedModels] = useState<string[]>(initial?.models ?? []);
91
+ // Free-text models — for `custom` it's the whole list, for a preset it's extras.
92
+ const [modelsText, setModelsText] = useState(() => {
93
+ const p = detectPreset(initial?.base_url);
94
+ if (p === 'custom') return (initial?.models ?? []).join(', ');
95
+ // extras = initial models not in the preset's default list
96
+ const preset = PRESETS.find(x => x.id === p);
97
+ const extras = (initial?.models ?? []).filter(m => !preset?.models.includes(m));
98
+ return extras.join(', ');
99
+ });
100
+ const [apiKey, setApiKey] = useState('');
101
+ const [enabled, setEnabled] = useState(initial?.enabled ?? true);
102
+ const [phase, setPhase] = useState<Phase>('form');
103
+ const [msg, setMsg] = useState('');
104
+
105
+ const preset = useMemo(() => PRESETS.find(p => p.id === presetId) ?? PRESETS[PRESETS.length - 1], [presetId]);
106
+ const isCustom = presetId === 'custom';
107
+
108
+ useEffect(() => {
109
+ const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && phase !== 'saving') onClose(); };
110
+ window.addEventListener('keydown', onKey);
111
+ return () => window.removeEventListener('keydown', onKey);
112
+ }, [onClose, phase]);
113
+
114
+ function selectPreset(id: string) {
115
+ setPresetId(id);
116
+ const p = PRESETS.find(x => x.id === id);
117
+ if (!p) return;
118
+ if (id !== 'custom') {
119
+ setType(p.type);
120
+ setBaseUrl(p.base_url);
121
+ // default-check all preset models; keep any prior extras in the text box
122
+ setCheckedModels(p.models);
123
+ if (!name.trim() || PRESETS.some(x => x.label === name.trim())) setName(p.label);
124
+ } else {
125
+ setBaseUrl('');
126
+ setCheckedModels([]);
127
+ }
128
+ }
129
+
130
+ function toggleModel(m: string) {
131
+ setCheckedModels(prev => (prev.includes(m) ? prev.filter(x => x !== m) : [...prev, m]));
132
+ }
133
+
134
+ const finalModels = useMemo(() => {
135
+ const extras = parseModels(modelsText);
136
+ if (isCustom) return extras;
137
+ return Array.from(new Set([...checkedModels, ...extras]));
138
+ }, [isCustom, checkedModels, modelsText]);
139
+
140
+ const nameValid = name.trim().length > 0;
141
+ const urlValid = baseUrl.trim().length > 0;
142
+ // Secret-first (RFC-028): a provider can't be created without a vault key,
143
+ // so the API key is required on CREATE. On edit the key + vendor are
144
+ // immutable (update_provider patches name/base_url/models only).
145
+ const keyValid = editing || apiKey.trim().length > 0;
146
+ const modelsValid = finalModels.length > 0;
147
+ const canSave = nameValid && urlValid && keyValid && modelsValid && phase !== 'saving';
148
+
149
+ async function handleSave() {
150
+ setPhase('saving'); setMsg('');
151
+ const payload = {
152
+ ...(initial?.provider_id ? { provider_id: initial.provider_id } : {}),
153
+ name: name.trim(), type, base_url: baseUrl.trim(), models: finalModels, enabled,
154
+ ...(apiKey ? { apiKey } : {}),
155
+ };
156
+ try {
157
+ const r = await fetch('/api/anet/providers', {
158
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
159
+ body: JSON.stringify(payload),
160
+ });
161
+ const data = await r.json().catch(() => ({}));
162
+ if (r.ok && data?.ok) {
163
+ setPhase('done'); onSaved?.();
164
+ // close shortly after success so the list refresh is visible
165
+ setTimeout(onClose, 350);
166
+ } else if (data?.unconfirmed) {
167
+ setPhase('unconfirmed');
168
+ setMsg(`后端 RFC-028 未就绪:${data?.error || '不可用'}(hub 需 #308 + 升级)`);
169
+ } else {
170
+ setPhase('error');
171
+ setMsg(data?.error ? `保存失败:${data.error}` : '保存失败');
172
+ }
173
+ } catch (e) {
174
+ setPhase('error');
175
+ setMsg(`保存失败:${e instanceof Error ? e.message : '网络错误'}`);
176
+ }
177
+ }
178
+
179
+ return (
180
+ <>
181
+ <div className="fixed inset-0 bg-black/50 z-40" onClick={() => phase !== 'saving' && onClose()} aria-hidden />
182
+ <div
183
+ role="dialog"
184
+ aria-label={editing ? '编辑供应商' : '新增供应商'}
185
+ className="fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl"
186
+ >
187
+ <div className="sticky top-0 flex items-center justify-between border-b border-[#26262b] bg-[#161618] px-4 py-3">
188
+ <div className="text-sm font-semibold text-white">{editing ? '编辑供应商' : '新增供应商'}</div>
189
+ <button onClick={onClose} aria-label="关闭" className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-white/5 hover:text-white">
190
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden>
191
+ <path d="M18 6 6 18M6 6l12 12" />
192
+ </svg>
193
+ </button>
194
+ </div>
195
+
196
+ <div className="space-y-3 px-4 py-4">
197
+ {phase === 'unconfirmed' || phase === 'error' ? (
198
+ <div className={`rounded-md border px-3 py-2 text-xs ${phase === 'unconfirmed' ? 'border-amber-500/30 bg-amber-500/5 text-amber-300' : 'border-red-500/30 bg-red-500/5 text-red-300'}`}>
199
+ {msg}
200
+ </div>
201
+ ) : null}
202
+
203
+ {/* #393 preset provider picker (hidden while editing — base_url/vendor are immutable) */}
204
+ {!editing && (
205
+ <label className="block space-y-1">
206
+ <span className="text-xs text-gray-400">供应商 <span className="text-red-400/70">*</span></span>
207
+ <select value={presetId} onChange={e => selectPreset(e.target.value)} className={inputCls}>
208
+ {PRESETS.map(p => <option key={p.id} value={p.id}>{p.label}</option>)}
209
+ </select>
210
+ {!isCustom && <span className="text-[11px] text-gray-600">已选官方供应商:Base URL 自动填好,勾选模型 + 填 API Key 即可。</span>}
211
+ </label>
212
+ )}
213
+
214
+ <label className="block space-y-1">
215
+ <span className="text-xs text-gray-400">名称 <span className="text-red-400/70">*</span></span>
216
+ <input autoFocus value={name} onChange={e => setName(e.target.value)} placeholder="例如 DeepSeek-V4-Pro" className={inputCls} />
217
+ </label>
218
+
219
+ {/* 类型 only surfaces in custom / edit — presets imply anthropic. */}
220
+ {(isCustom || editing) && (
221
+ <label className="block space-y-1">
222
+ <span className="text-xs text-gray-400">类型{editing ? '(不可修改)' : ''}</span>
223
+ <select value={type} onChange={e => setType(e.target.value)} disabled={editing} className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}>
224
+ {TYPES.map(t => <option key={t.id} value={t.id} disabled={t.disabled}>{t.label}</option>)}
225
+ </select>
226
+ </label>
227
+ )}
228
+
229
+ <label className="block space-y-1">
230
+ <span className="text-xs text-gray-400">Base URL <span className="text-red-400/70">*</span>{!isCustom && !editing && <span className="text-gray-600">(已自动填充)</span>}</span>
231
+ <input
232
+ value={baseUrl}
233
+ onChange={e => setBaseUrl(e.target.value)}
234
+ placeholder="https://api.anthropic.com"
235
+ disabled={!isCustom && !editing}
236
+ className={`${inputCls} ${(!isCustom && !editing) ? 'opacity-60 cursor-not-allowed' : ''}`}
237
+ />
238
+ {isCustom && !editing && <span className="text-[11px] text-gray-600">Anthropic 兼容端点:Claude <span className="font-mono">api.anthropic.com</span> · DeepSeek <span className="font-mono">api.deepseek.com/anthropic</span> · MiniMax <span className="font-mono">api.minimax.chat/anthropic</span>。</span>}
239
+ </label>
240
+
241
+ {/* Models: preset → checkbox list + extras; custom → comma text. */}
242
+ {!isCustom && !editing ? (
243
+ <div className="block space-y-1">
244
+ <span className="text-xs text-gray-400">模型 <span className="text-red-400/70">*</span><span className="text-gray-600">(勾选,可多选)</span></span>
245
+ <div className="flex flex-wrap gap-2">
246
+ {preset.models.map(m => {
247
+ const on = checkedModels.includes(m);
248
+ return (
249
+ <button
250
+ type="button"
251
+ key={m}
252
+ onClick={() => toggleModel(m)}
253
+ className={`rounded-md border px-2.5 py-1 text-xs font-mono transition-colors ${on ? 'border-cyan-600 bg-cyan-600/15 text-cyan-300' : 'border-[#26262b] bg-[#0e0e10] text-gray-400 hover:border-gray-600'}`}
254
+ >
255
+ {on ? '✓ ' : ''}{m}
256
+ </button>
257
+ );
258
+ })}
259
+ </div>
260
+ <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder="其他模型(逗号分隔,可留空)" className={`${inputCls} mt-1`} />
261
+ </div>
262
+ ) : (
263
+ <label className="block space-y-1">
264
+ <span className="text-xs text-gray-400">模型(逗号分隔){editing ? '' : <span className="text-red-400/70"> *</span>}</span>
265
+ <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder="claude-sonnet-4-6, deepseek-chat" className={inputCls} />
266
+ </label>
267
+ )}
268
+
269
+ <label className="block space-y-1">
270
+ <span className="text-xs text-gray-400">API Key {editing ? <span className="text-gray-600">(不可在此修改)</span> : <span className="text-red-400/70">*</span>}</span>
271
+ <input
272
+ type="password"
273
+ value={apiKey}
274
+ onChange={e => setApiKey(e.target.value)}
275
+ placeholder={editing ? '•••• configured(密钥库已存)' : (preset.keyPlaceholder ?? 'sk-…')}
276
+ className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}
277
+ autoComplete="off"
278
+ disabled={editing}
279
+ />
280
+ <span className="text-[11px] text-gray-600">{editing ? '密钥不可在此改;如需轮换 key 走单独的密钥库流程。' : '写入即存入密钥库,UI 不回显 key 值。'}</span>
281
+ </label>
282
+
283
+ <label className="flex items-center gap-2 pt-1">
284
+ <input type="checkbox" checked={enabled} onChange={e => setEnabled(e.target.checked)} className="accent-cyan-500" />
285
+ <span className="text-xs text-gray-300">启用</span>
286
+ </label>
287
+ </div>
288
+
289
+ <div className="sticky bottom-0 flex items-center justify-end gap-2 border-t border-[#26262b] bg-[#161618] px-4 py-3">
290
+ <button type="button" onClick={onClose} className="rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200">取消</button>
291
+ <button
292
+ type="button"
293
+ disabled={!canSave}
294
+ onClick={handleSave}
295
+ className={`rounded-md px-4 py-1.5 text-sm font-medium ${canSave ? 'bg-cyan-600 text-white hover:bg-cyan-500' : 'bg-[#1c1c1f] text-gray-600 cursor-not-allowed'}`}
296
+ >
297
+ {phase === 'saving' ? '保存中…' : phase === 'done' ? '✓ 已保存' : editing ? '保存' : '新增'}
298
+ </button>
299
+ </div>
300
+ </div>
301
+ </>
302
+ );
303
+ }
@@ -0,0 +1,196 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ /**
6
+ * Model × server reachability matrix (RFC-028 P2, aligned to real #308 schema).
7
+ * probe_provider_model is ASYNC: we DISPATCH a probe per (model × server) with
8
+ * bounded concurrency (~6), then POLL get_probe_results until every cell
9
+ * resolves (or times out). Cells: ok (green ✓ + latency) · auth_fail (amber) ·
10
+ * unreachable (red ✕) · incompatible (gray —) · pending (spinner).
11
+ * Rendered as a real <table> for screen-reader columns.
12
+ */
13
+
14
+ interface Server { daemon_node_id: string; hostname: string }
15
+ type CellStatus = 'pending' | 'ok' | 'auth_fail' | 'unreachable' | 'incompatible' | 'error';
16
+ interface Cell { status: CellStatus; latency_ms?: number; reason?: string }
17
+
18
+ // `tag` is the short inline label shown in the cell (visible without hover, so
19
+ // a screenshot/glance reads cleanly); `label` is the longer a11y/tooltip term.
20
+ const CELL_UI: Record<Exclude<CellStatus, 'pending'>, { glyph: string; cls: string; label: string; tag: string }> = {
21
+ ok: { glyph: '✓', cls: 'text-green-400 border-green-500/30 bg-green-500/5', label: 'ok', tag: 'ok' },
22
+ auth_fail: { glyph: '⚷', cls: 'text-amber-400 border-amber-500/30 bg-amber-500/5', label: 'auth', tag: '401' },
23
+ unreachable: { glyph: '✕', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'unreachable', tag: 'down' },
24
+ incompatible: { glyph: '—', cls: 'text-gray-500 border-gray-600/20', label: 'incompatible', tag: 'n/a' },
25
+ error: { glyph: '!', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'error', tag: 'err' },
26
+ };
27
+
28
+ // Legend entries shown under the matrix so cell glyphs are self-explanatory.
29
+ const LEGEND: Array<Exclude<CellStatus, 'pending'>> = ['ok', 'auth_fail', 'unreachable', 'incompatible'];
30
+
31
+ const CONCURRENCY = 6;
32
+ const POLL_MS = 1500;
33
+ const POLL_TIMEOUT_MS = 24000;
34
+
35
+ async function pool<T>(items: T[], worker: (t: T) => Promise<void>) {
36
+ let i = 0;
37
+ await Promise.all(Array.from({ length: Math.min(CONCURRENCY, items.length) }, async () => {
38
+ while (i < items.length) { const idx = i++; await worker(items[idx]); }
39
+ }));
40
+ }
41
+
42
+ const norm = (s: string): CellStatus => {
43
+ if (s === 'ok' || s === 'auth_fail' || s === 'unreachable' || s === 'incompatible' || s === 'pending') return s;
44
+ if (s === 'timeout' || s === 'dns_fail' || s === 'conn_fail') return 'unreachable';
45
+ return 'error';
46
+ };
47
+
48
+ export function ReachabilityMatrix({ providerId, models }: { providerId?: string; models: string[] }) {
49
+ const [servers, setServers] = useState<Server[]>([]);
50
+ const [cells, setCells] = useState<Record<string, Cell>>({});
51
+ const [loading, setLoading] = useState(true);
52
+ const [running, setRunning] = useState(false);
53
+ const [unconfirmed, setUnconfirmed] = useState<string | null>(null);
54
+ const aliveRef = useRef(true);
55
+ useEffect(() => () => { aliveRef.current = false; }, []);
56
+
57
+ const key = (m: string, s: string) => `${m}@@${s}`;
58
+
59
+ const run = useCallback(async (srv: Server[]) => {
60
+ if (!models.length || !srv.length || !providerId) return;
61
+ setRunning(true); setUnconfirmed(null);
62
+ const pairs = models.flatMap(m => srv.map(s => ({ m, s })));
63
+ setCells(Object.fromEntries(pairs.map(({ m, s }) => [key(m, s.daemon_node_id), { status: 'pending' as CellStatus }])));
64
+
65
+ // 1) dispatch all probes (async — backend mints pending rows + doorbells daemons)
66
+ await pool(pairs, async ({ m, s }) => {
67
+ try {
68
+ const r = await fetch('/api/anet/providers/probe', {
69
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
70
+ body: JSON.stringify({ provider_id: providerId, model_name: m, daemon_node_id: s.daemon_node_id }),
71
+ });
72
+ const d = await r.json().catch(() => ({}));
73
+ if (d?.unconfirmed && aliveRef.current) setUnconfirmed(d?.error || 'backend not available');
74
+ } catch { /* dispatch best-effort; poll surfaces the outcome */ }
75
+ });
76
+
77
+ // 2) poll get_probe_results until every cell resolves or we time out
78
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
79
+ const want = new Set(pairs.map(({ m, s }) => key(m, s.daemon_node_id)));
80
+ while (aliveRef.current && Date.now() < deadline) {
81
+ await new Promise(res => setTimeout(res, POLL_MS));
82
+ if (!aliveRef.current) break;
83
+ try {
84
+ const r = await fetch(`/api/anet/providers/probe-results?provider_id=${encodeURIComponent(providerId)}`, { cache: 'no-store' });
85
+ const d = await r.json().catch(() => ({}));
86
+ if (d?.unconfirmed) { if (aliveRef.current) setUnconfirmed(d?.error || 'backend not available'); break; }
87
+ const rows = Array.isArray(d?.results) ? d.results : [];
88
+ // latest row per (model,daemon)
89
+ const latest = new Map<string, { status: string; latency_ms?: number; error_label?: string; probed_at?: number }>();
90
+ for (const row of rows) {
91
+ const k = key(String(row.model_name), String(row.daemon_node_id));
92
+ if (!want.has(k)) continue;
93
+ const prev = latest.get(k);
94
+ if (!prev || (row.probed_at || 0) >= (prev.probed_at || 0)) latest.set(k, row);
95
+ }
96
+ if (aliveRef.current) {
97
+ setCells(prev => {
98
+ const next = { ...prev };
99
+ for (const [k, row] of latest) next[k] = { status: norm(String(row.status)), latency_ms: row.latency_ms, reason: row.error_label };
100
+ return next;
101
+ });
102
+ }
103
+ // done when no cell is still pending
104
+ const allResolved = [...want].every(k => latest.get(k) && latest.get(k)!.status !== 'pending');
105
+ if (allResolved) break;
106
+ } catch { /* transient — keep polling */ }
107
+ }
108
+ if (aliveRef.current) setRunning(false);
109
+ }, [models, providerId]);
110
+
111
+ useEffect(() => {
112
+ let alive = true;
113
+ (async () => {
114
+ try {
115
+ const r = await fetch('/api/anet/providers/probe', { cache: 'no-store' });
116
+ const d = await r.json().catch(() => ({}));
117
+ const srv: Server[] = Array.isArray(d?.servers) ? d.servers : [];
118
+ if (!alive) return;
119
+ setServers(srv); setLoading(false);
120
+ if (srv.length) run(srv);
121
+ } catch { if (alive) setLoading(false); }
122
+ })();
123
+ return () => { alive = false; };
124
+ }, [run]);
125
+
126
+ const total = models.length * servers.length;
127
+ const okCount = Object.values(cells).filter(c => c.status === 'ok').length;
128
+
129
+ if (loading) return <div className="px-3 py-3 text-[11px] text-gray-500">解析服务器集合…</div>;
130
+ if (unconfirmed) {
131
+ return <div className="mx-3 my-2 rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300">
132
+ 连通性 probe 后端尚未就绪:{unconfirmed}。矩阵 UI 已就位,待 hub 升级到含 RFC-028 接真 probe。
133
+ </div>;
134
+ }
135
+ if (!servers.length) return <div className="px-3 py-3 text-[11px] text-gray-500">未发现可达的 host_supervisor 服务器(probe 目标集为空)。</div>;
136
+
137
+ return (
138
+ <div className="border-t border-[#1c1c1f] px-3 py-3" data-reachability-matrix>
139
+ <div className="mb-2 flex items-center justify-between">
140
+ <span className="text-[11px] text-gray-400">
141
+ 可达矩阵 · <span className="tabular-nums text-gray-300">{okCount}/{total}</span> reachable
142
+ {running && <span className="ml-2 text-gray-600">probing…</span>}
143
+ </span>
144
+ <button type="button" onClick={() => run(servers)} disabled={running}
145
+ className="rounded border border-[#26262b] px-2 py-0.5 text-[11px] text-gray-400 hover:text-gray-200 disabled:opacity-50">重新测试</button>
146
+ </div>
147
+ <div className="overflow-x-auto">
148
+ <table className="border-collapse text-[11px]">
149
+ <thead>
150
+ <tr>
151
+ <th className="px-2 py-1 text-left font-medium text-gray-500">model \\ server</th>
152
+ {servers.map(s => <th key={s.daemon_node_id} className="px-2 py-1 text-center font-mono font-normal text-gray-400" title={s.daemon_node_id}>{s.hostname}</th>)}
153
+ </tr>
154
+ </thead>
155
+ <tbody>
156
+ {models.map(m => (
157
+ <tr key={m}>
158
+ <td className="px-2 py-1 font-mono text-gray-300 whitespace-nowrap">{m}</td>
159
+ {servers.map(s => {
160
+ const c = cells[key(m, s.daemon_node_id)];
161
+ if (!c || c.status === 'pending') return <td key={s.daemon_node_id} className="px-2 py-1 text-center">
162
+ <span className="inline-block h-3 w-3 animate-spin rounded-full border border-gray-600 border-t-transparent" aria-label="probing" />
163
+ </td>;
164
+ const ui = CELL_UI[c.status as Exclude<CellStatus, 'pending'>] || CELL_UI.error;
165
+ return (
166
+ <td key={s.daemon_node_id} className="px-2 py-1 text-center">
167
+ <span className={`inline-flex min-w-[2.75rem] items-center justify-center gap-1 rounded border px-1.5 py-0.5 ${ui.cls}`}
168
+ title={c.reason ? `${ui.label}: ${c.reason}` : ui.label} data-cell-status={c.status}>
169
+ <span aria-hidden>{ui.glyph}</span>
170
+ {c.status === 'ok'
171
+ ? (c.latency_ms != null && <span className="tabular-nums text-[10px] opacity-80">{c.latency_ms}ms</span>)
172
+ : <span className="text-[10px] opacity-80">{ui.tag}</span>}
173
+ </span>
174
+ </td>
175
+ );
176
+ })}
177
+ </tr>
178
+ ))}
179
+ </tbody>
180
+ </table>
181
+ </div>
182
+ <div className="mt-2.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-gray-500">
183
+ {LEGEND.map(s => {
184
+ const ui = CELL_UI[s];
185
+ return (
186
+ <span key={s} className="inline-flex items-center gap-1">
187
+ <span className={`inline-flex h-3.5 w-3.5 items-center justify-center rounded border ${ui.cls}`} aria-hidden>{ui.glyph}</span>
188
+ {ui.label}
189
+ </span>
190
+ );
191
+ })}
192
+ <span className="text-gray-600">· 实时探测(模型→服务器真实请求往返)</span>
193
+ </div>
194
+ </div>
195
+ );
196
+ }
@@ -18,6 +18,7 @@ const NAV_ITEMS = [
18
18
  { href: '/tasks', label: 'Tasks', icon: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4' },
19
19
  { href: '/nodes', label: 'Nodes', icon: 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01' },
20
20
  { href: '/servers', label: 'Servers', icon: 'M4 6.5A2.5 2.5 0 016.5 4h11A2.5 2.5 0 0120 6.5v1A2.5 2.5 0 0117.5 10h-11A2.5 2.5 0 014 7.5v-1zM4 16.5A2.5 2.5 0 016.5 14h11a2.5 2.5 0 012.5 2.5v1a2.5 2.5 0 01-2.5 2.5h-11A2.5 2.5 0 014 17.5v-1zM7 7h.01M7 17h.01' },
21
+ { href: '/providers', label: 'Providers', icon: 'M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z' },
21
22
  { href: '/admin', label: 'Admin', icon: 'M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z' },
22
23
  { href: '/settings', label: 'Settings', icon: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z' },
23
24
  ];
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useEffect, useState } from 'react';
4
4
  import { timeAgo } from './utils';
5
+ import Link from 'next/link';
5
6
  import { AliasAvatar } from './AliasAvatar';
6
7
 
7
8
  interface TaskDetail {
@@ -149,14 +149,6 @@ const offlineRadius = 325;
149
149
  * offline ring gets a half-step rotation so its nodes sit in the gaps
150
150
  * between online ones). */
151
151
  function polarPoint(index: number, total: number, radius: number, rotateBy = 0) {
152
- // Place node `index` of `total` evenly along an arc centred on 12 o'clock.
153
- // `spread` = arc width: a half-circle (π) for ≤2 nodes so a pair sits left
154
- // & right, otherwise ~320° (1.78π) which leaves a ~40° gap at the bottom
155
- // so the ring reads as an open horseshoe rather than a closed loop.
156
- // `start` backs off half the spread from straight-up (-π/2) so the arc is
157
- // symmetric about the top. A lone node (total ≤ 1) just sits at the top;
158
- // `rotateBy` is the caller's half-step offset (e.g. the offline ring nudges
159
- // its nodes into the gaps between the online ones).
160
152
  const spread = total <= 2 ? Math.PI : Math.PI * 1.78;
161
153
  const start = -Math.PI / 2 - spread / 2;
162
154
  const angle = total <= 1 ? -Math.PI / 2 : start + (spread * index) / (total - 1) + rotateBy;
@@ -1453,6 +1445,10 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
1453
1445
  // rather than confirmed. This state drives a subtle hint ring on
1454
1446
  // hover so users see the affordance before committing the click.
1455
1447
  const [hoveredHub, setHoveredHub] = useState(false);
1448
+ // R133: hover state for the recent-signal panel's "+N more flows"
1449
+ // navigation footer. Drives the on-hover opacity boost + underline
1450
+ // that signals interactivity, mirroring the hoveredHub idiom above.
1451
+ const [hoveredRecentMore, setHoveredRecentMore] = useState(false);
1456
1452
  // Round 346 / Loop: minimap-container hover affordance. The minimap
1457
1453
  // is a click-target (role=button at line ~7810, recenter-on-click +
1458
1454
  // Enter→resetView) but pre-R346 nothing visually changed on hover —
@@ -26,6 +26,11 @@ interface AuthState {
26
26
  export function UserBar() {
27
27
  const { setNetworkId } = useNetworkId();
28
28
  const [auth, setAuth] = useState<AuthState>({ user: null, networks: [], currentNetwork: '', token: '' });
29
+ const [showLogin, setShowLogin] = useState(false);
30
+ const [username, setUsername] = useState('');
31
+ const [password, setPassword] = useState('');
32
+ const [loginError, setLoginError] = useState('');
33
+ const [loginPending, setLoginPending] = useState(false);
29
34
 
30
35
  // Try to restore from sessionStorage
31
36
  useEffect(() => {
@@ -35,6 +40,38 @@ export function UserBar() {
35
40
  }
36
41
  }, []);
37
42
 
43
+ const doAuth = async (action: 'login' | 'register') => {
44
+ if (!username.trim() || !password.trim()) return;
45
+ setLoginPending(true);
46
+ setLoginError('');
47
+ try {
48
+ const res = await fetch('/api/hub/auth', {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({ action, username, password }),
52
+ });
53
+ const data = await res.json();
54
+ if (!data.ok) { setLoginError(data.error || 'Failed'); setLoginPending(false); return; }
55
+
56
+ // Fetch user details
57
+ const meRes = await fetch(`/api/hub/auth?token=${data.token}&endpoint=/api/auth/me`);
58
+ const meData = await meRes.json();
59
+
60
+ const newAuth: AuthState = {
61
+ user: data.user,
62
+ networks: meData.networks || [],
63
+ currentNetwork: meData.current_network?.network_id || meData.networks?.[0]?.network_id || '',
64
+ token: data.token,
65
+ };
66
+ setAuth(newAuth);
67
+ sessionStorage.setItem('anet_v3_auth', JSON.stringify(newAuth));
68
+ setShowLogin(false);
69
+ setUsername('');
70
+ setPassword('');
71
+ } catch { setLoginError('Connection failed'); }
72
+ setLoginPending(false);
73
+ };
74
+
38
75
  const logout = () => {
39
76
  setAuth({ user: null, networks: [], currentNetwork: '', token: '' });
40
77
  sessionStorage.removeItem('anet_v3_auth');
@@ -19,6 +19,10 @@ export interface Session {
19
19
  // commhub-server 0.8.1-preview.0; null on older nodes / older hubs.
20
20
  model?: string | null;
21
21
  runtime?: string | null;
22
+ // RFC-027 PR2 — lifecycle_state. Sourced from /api/nodes (preview.12+);
23
+ // merged into Session client-side via useNodeLifecycle hook. Absent →
24
+ // treated as 'active' so older sessions/hubs don't break the menu.
25
+ lifecycle_state?: string | null;
22
26
  }
23
27
 
24
28
  export interface Health {
@@ -31,6 +35,13 @@ export interface Health {
31
35
  auth?: string;
32
36
  }
33
37
 
38
+ export interface InboxMessage {
39
+ id: string;
40
+ content: string;
41
+ from_session: string;
42
+ created_at: string;
43
+ }
44
+
34
45
  export interface AnetConfig {
35
46
  path: string;
36
47
  exists: boolean;
@@ -95,12 +95,6 @@ export function useChatUnread() {
95
95
  );
96
96
  const [readVersion, setReadVersion] = useState(0);
97
97
 
98
- // Bump readVersion whenever the read-map changes so the readMap useMemo
99
- // below recomputes. Two listeners, by design — not redundant:
100
- // • UNREAD_EVENT (dispatched by writeStoredMap in THIS tab): the native
101
- // 'storage' event never fires in the tab that made the write, so this
102
- // is what refreshes the current tab's badges right after markChatRead.
103
- // • 'storage': covers the cross-tab case (another tab marked a chat read).
104
98
  useEffect(() => {
105
99
  const onStorage = (event: StorageEvent) => {
106
100
  if (event.key && event.key !== storageKey(networkId)) return;