@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.
- package/.next/BUILD_COMMIT +1 -1
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +5 -0
- package/.next/build-manifest.json +3 -3
- package/.next/cache/.previewinfo +1 -0
- package/.next/cache/.rscinfo +1 -0
- package/.next/diagnostics/route-bundle-stats.json +56 -39
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +27 -3
- package/.next/routes-manifest.json +30 -0
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +13 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -8
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +2 -2
- package/.next/server/app/admin.rsc +15 -15
- package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +8 -8
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/api/anet/host-supervisors/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/anet/host-supervisors/route/build-manifest.json +9 -0
- package/.next/server/app/api/anet/host-supervisors/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/anet/host-supervisors/route.js +7 -0
- package/.next/server/app/api/anet/host-supervisors/route.js.map +5 -0
- package/.next/server/app/api/anet/host-supervisors/route.js.nft.json +1 -0
- package/.next/server/app/api/anet/host-supervisors/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/anet/providers/probe/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/anet/providers/probe/route/build-manifest.json +9 -0
- package/.next/server/app/api/anet/providers/probe/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/anet/providers/probe/route.js +7 -0
- package/.next/server/app/api/anet/providers/probe/route.js.map +5 -0
- package/.next/server/app/api/anet/providers/probe/route.js.nft.json +1 -0
- package/.next/server/app/api/anet/providers/probe/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/anet/providers/probe-results/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/anet/providers/probe-results/route/build-manifest.json +9 -0
- package/.next/server/app/api/anet/providers/probe-results/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/anet/providers/probe-results/route.js +7 -0
- package/.next/server/app/api/anet/providers/probe-results/route.js.map +5 -0
- package/.next/server/app/api/anet/providers/probe-results/route.js.nft.json +1 -0
- package/.next/server/app/api/anet/providers/probe-results/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/anet/providers/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/anet/providers/route/build-manifest.json +9 -0
- package/.next/server/app/api/anet/providers/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/anet/providers/route.js +8 -0
- package/.next/server/app/api/anet/providers/route.js.map +5 -0
- package/.next/server/app/api/anet/providers/route.js.nft.json +1 -0
- package/.next/server/app/api/anet/providers/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +15 -15
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +8 -8
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +15 -15
- package/.next/server/app/login.segments/_full.segment.rsc +15 -15
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +8 -8
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +15 -15
- package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +15 -15
- package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +8 -8
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +15 -15
- package/.next/server/app/node.segments/_full.segment.rsc +15 -15
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +8 -8
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +15 -15
- package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +8 -8
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers/page/app-paths-manifest.json +3 -0
- package/.next/server/app/providers/page/build-manifest.json +17 -0
- package/.next/server/app/providers/page/next-font-manifest.json +11 -0
- package/.next/server/app/providers/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/providers/page/server-reference-manifest.json +4 -0
- package/.next/server/app/providers/page.js +14 -0
- package/.next/server/app/providers/page.js.map +5 -0
- package/.next/server/app/providers/page.js.nft.json +1 -0
- package/.next/server/app/providers/page_client-reference-manifest.js +3 -0
- package/.next/server/app/providers.html +12 -0
- package/.next/server/app/providers.meta +15 -0
- package/.next/server/app/providers.rsc +27 -0
- package/.next/server/app/providers.segments/_full.segment.rsc +27 -0
- package/.next/server/app/providers.segments/_head.segment.rsc +6 -0
- package/.next/server/app/providers.segments/_index.segment.rsc +10 -0
- package/.next/server/app/providers.segments/_tree.segment.rsc +5 -0
- package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +9 -0
- package/.next/server/app/providers.segments/providers.segment.rsc +5 -0
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +15 -15
- package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/servers.html +2 -2
- package/.next/server/app/servers.rsc +15 -15
- package/.next/server/app/servers.segments/_full.segment.rsc +15 -15
- package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/servers.segments/_index.segment.rsc +8 -8
- package/.next/server/app/servers.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +15 -15
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +15 -15
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +15 -15
- package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +2 -2
- package/.next/server/app/tasks.rsc +15 -15
- package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/app-paths-manifest.json +5 -0
- package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_0bobkt3.js +3 -0
- package/.next/server/chunks/00jm_next_dist_esm_build_templates_app-route_0bobkt3.js.map +1 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_anet_providers_probe-results_route_actions_07.d.5a.js +3 -0
- package/.next/server/chunks/0dk5_next-internal_server_app_api_anet_providers_probe-results_route_actions_07.d.5a.js.map +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_host-supervisors_route_actions_0g3hek2.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_host-supervisors_route_actions_0g3hek2.js.map +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_probe_route_actions_05f000..js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_probe_route_actions_05f000..js.map +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_route_actions_10htbhd.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_anet_providers_route_actions_10htbhd.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0-29ynz._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0-2le8-._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0-8_f0o._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__02p2n3n._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__03nze3z._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__03nze3z._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__043tnx2._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__04z62zk._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__07ztet2._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0aqb4n5._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0aqb4n5._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0cphmer._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0cphmer._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0e3ucl1._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0e6yail._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0gx2_6a._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0hg3_8r._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0lhq4ha._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mkwzz7._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mkwzz7._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mxw4vb._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mzopco._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0o45a4q._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0w1w_w0._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0w1w_w0._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0xtp-et._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0yyo7k5._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0yyo7k5._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__0~rwgo1._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0~rwgo1._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__10pvabe._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__10t11zq._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__12r~omy._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__1389~cs._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__13n8t..._.js.map +1 -1
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02lorbl.js +4 -0
- package/.next/server/chunks/ssr/00jm_next_dist_esm_build_templates_app-page_02lorbl.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__04mx~5g._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04mx~5g._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0h5dae6._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0h5dae6._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_providers_page_actions_0ou6p43.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard__next-internal_server_app_providers_page_actions_0ou6p43.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_EmptyState_tsx_0aol-el._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_EmptyState_tsx_0aol-el._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_nodes_page_tsx_11haf_p._.js +2 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_nodes_page_tsx_11haf_p._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js +3 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js.map +1 -0
- package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +4 -0
- package/.next/server/pages/404.html +2 -2
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{10kkhdde5ve~c.js → 0-z_kfck7-6_1.js} +3 -3
- package/.next/static/chunks/049vx3qljs1tt.js +1 -0
- package/.next/static/chunks/06.5euf239uvr.css +1 -0
- package/.next/static/chunks/{0vun~4ljcrj3p.js → 091hae.lhv9t1.js} +2 -2
- package/.next/static/chunks/0chpv7zsh-qrg.js +1 -0
- package/.next/static/chunks/0i-l67o_ae5hk.js +2 -0
- package/.next/static/chunks/{0vus9xpsz0s~i.js → 0l.gu-iqnmeja.js} +1 -1
- package/.next/static/chunks/{149a4l50_3vw-.js → 0mnmn-~ava1vy.js} +1 -1
- package/.next/static/chunks/0o-b62_a66.jr.js +1 -0
- package/.next/static/chunks/0pk6amtz5icbx.js +1 -0
- package/.next/static/chunks/0r4u1.sl.8p15.js +1 -0
- package/.next/static/chunks/0wz0122ym_gr3.js +1 -0
- package/.next/static/chunks/15aqdc5txzq9r.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +7 -2
- package/.next/types/validator.ts +45 -0
- package/app/admin/page.tsx +3 -1
- package/app/api/anet/host-supervisors/route.ts +102 -0
- package/app/api/anet/node-config/route.ts +26 -2
- package/app/api/anet/node-create/route.ts +14 -5
- package/app/api/anet/node-lifecycle/route.ts +54 -5
- package/app/api/anet/providers/probe/route.ts +79 -0
- package/app/api/anet/providers/probe-results/route.ts +60 -0
- package/app/api/anet/providers/route.ts +191 -0
- package/app/api/hub/auth/route.ts +0 -12
- package/app/api/hub/networks/route.ts +1 -1
- package/app/api/hub/nodes/route.ts +0 -14
- package/app/api/hub/upload/route.ts +0 -9
- package/app/components/CommandCenter.tsx +3 -6
- package/app/components/CommandPalette.tsx +0 -5
- package/app/components/CreateNodeWizard.tsx +140 -47
- package/app/components/HostSupervisorPicker.tsx +283 -0
- package/app/components/NodeLifecycleMenu.tsx +436 -0
- package/app/components/NodeSettingsPanel.tsx +80 -39
- package/app/components/ProviderFormModal.tsx +303 -0
- package/app/components/ReachabilityMatrix.tsx +196 -0
- package/app/components/Sidebar.tsx +1 -0
- package/app/components/TaskDrawer.tsx +1 -0
- package/app/components/TopoGraph.tsx +4 -8
- package/app/components/UserBar.tsx +37 -0
- package/app/components/types.ts +11 -0
- package/app/lib/chat-unread.ts +0 -6
- package/app/lib/dashboard-auth.ts +0 -12
- package/app/lib/hooks.ts +22 -4
- package/app/lib/hub.ts +4 -0
- package/app/lib/network-context.tsx +0 -6
- package/app/lib/rate-limit.ts +2 -16
- package/app/node/page.tsx +12 -1
- package/app/nodes/page.tsx +42 -2
- package/app/page.tsx +1 -0
- package/app/providers/page.tsx +182 -0
- package/app/settings/networks/page.tsx +1 -1
- package/app/settings/page.tsx +5 -0
- package/app/tasks/[id]/page.tsx +1 -0
- package/app/tasks/page.tsx +1 -0
- package/package.json +1 -1
- package/.next/static/chunks/0-t2rbg9~-oh5.js +0 -1
- package/.next/static/chunks/0iv.0p9_5b36e.js +0 -1
- package/.next/static/chunks/0l_hz3xrjwumg.js +0 -1
- package/.next/static/chunks/0lmdw~i5qjqsg.js +0 -1
- package/.next/static/chunks/0vruzstkdr99w.js +0 -1
- package/.next/static/chunks/0xssg9sin~2xw.js +0 -1
- package/.next/static/chunks/124dtwo1k24ao.css +0 -1
- package/bun.lock +0 -1663
- /package/.next/static/{ZxdmFd_iOEELuA2Km4Q5t → tRPXkJdGLR6a0skqvMgQa}/_buildManifest.js +0 -0
- /package/.next/static/{ZxdmFd_iOEELuA2Km4Q5t → tRPXkJdGLR6a0skqvMgQa}/_clientMiddlewareManifest.js +0 -0
- /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/<ts>-{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
|
|
12
|
-
* - **Wired (real)**:
|
|
13
|
-
*
|
|
14
|
-
* budget / timeout). Controlled form state,
|
|
15
|
-
* `GET /api/anet/node-config` and persisted via
|
|
16
|
-
*
|
|
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
|
-
* - **
|
|
22
|
-
*
|
|
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
|
|
48
|
+
// A. Channel bindings — enable/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(() =>
|
|
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 ? '
|
|
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
|
|
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
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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"
|
|
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;
|