@sleep2agi/agent-network-dashboard 0.4.6-preview.12 → 0.4.6-preview.121
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_ID +1 -1
- package/.next/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +69 -55
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +6 -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 +12 -12
- package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
- 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/hub/servers/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/hub/servers/route/build-manifest.json +9 -0
- package/.next/server/app/api/hub/servers/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/hub/servers/route.js +7 -0
- package/.next/server/app/api/hub/servers/route.js.map +5 -0
- package/.next/server/app/api/hub/servers/route.js.nft.json +1 -0
- package/.next/server/app/api/hub/servers/route_client-reference-manifest.js +3 -0
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +14 -14
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/login.segments/_full.segment.rsc +14 -14
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/node.segments/_full.segment.rsc +14 -14
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
- 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/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 +14 -14
- package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
- 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/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 +14 -14
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
- 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 +14 -14
- package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
- 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 +1 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js +3 -0
- package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_servers_route_actions_08-m-cs.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tx8s8i._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.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]__0nw~zhp._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +4 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.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_057q.ne._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.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_0i3759l._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +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_0mvyi-4._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- 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/0-mpa_947ipeq.js +1 -0
- package/.next/static/chunks/0.hnfdve7~422.js +7 -0
- package/.next/static/chunks/001t1axy5kvz8.css +2 -0
- package/.next/static/chunks/01lmbsd37fybu.js +1 -0
- package/.next/static/chunks/03o.h6kvmw4l_.js +1 -0
- package/.next/static/chunks/03~~oirxz7~vc.js +1 -0
- package/.next/static/chunks/05uk96gc~9mni.js +1 -0
- package/.next/static/chunks/08kf-.bgs10ze.js +1 -0
- package/.next/static/chunks/0a-6zp8m0ux5..js +1 -0
- package/.next/static/chunks/0ekib0be3t9i5.js +1 -0
- package/.next/static/chunks/0glmrdj9112tc.js +7 -0
- package/.next/static/chunks/0rxrez_cjn.eh.js +1 -0
- package/.next/static/chunks/0tvn2l1pc.h65.js +1 -0
- package/.next/static/chunks/0x8x.29hkju5a.js +1 -0
- package/.next/static/chunks/106~a5kttxze-.js +4 -0
- package/.next/static/chunks/174re5ar~kxne.js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/app/api/hub/servers/route.ts +39 -0
- package/app/components/AppShell.tsx +2 -0
- package/app/components/ChatPopover.tsx +38 -2
- package/app/components/CommandPalette.tsx +94 -0
- package/app/components/HelpOverlay.tsx +76 -0
- package/app/components/ServersDrawer.tsx +206 -0
- package/app/components/TopoGraph.tsx +2181 -163
- package/app/components/utils.ts +8 -7
- package/app/globals.css +19 -1
- package/app/lib/time.ts +36 -0
- package/package.json +1 -1
- package/scripts/help-overlay-topo-test.mjs +44 -0
- package/scripts/servers-drawer-live-test.mjs +96 -0
- package/scripts/topo-active-filter-pill-test.mjs +101 -0
- package/scripts/topo-active-links-chip-hover-test.mjs +103 -0
- package/scripts/topo-active-links-chip-test.mjs +73 -0
- package/scripts/topo-active-links-tooltip-test.mjs +79 -0
- package/scripts/topo-arrival-ping-test.mjs +87 -0
- package/scripts/topo-arrow-tier-test.mjs +61 -0
- package/scripts/topo-auto-fit-test.mjs +73 -0
- package/scripts/topo-chat-focus-ring-test.mjs +77 -0
- package/scripts/topo-chip-pin-mirror-test.mjs +113 -0
- package/scripts/topo-click-ripple-test.mjs +66 -0
- package/scripts/topo-cmdk-layout-view-test.mjs +108 -0
- package/scripts/topo-cmdk-pin-test.mjs +123 -0
- package/scripts/topo-cmdk-vendor-clear-test.mjs +133 -0
- package/scripts/topo-cmdk-vendor-pin-test.mjs +103 -0
- package/scripts/topo-cwd-tooltip-test.mjs +64 -0
- package/scripts/topo-dblclick-reset-test.mjs +74 -0
- package/scripts/topo-dispatch-pulse-test.mjs +83 -0
- package/scripts/topo-edge-count-badge-test.mjs +84 -0
- package/scripts/topo-edge-endpoint-highlight-test.mjs +111 -0
- package/scripts/topo-edge-focus-test.mjs +113 -0
- package/scripts/topo-edge-hover-test.mjs +93 -0
- package/scripts/topo-edge-tooltip-test.mjs +70 -0
- package/scripts/topo-endpoint-ring-test.mjs +93 -0
- package/scripts/topo-esc-clears-pin-test.mjs +98 -0
- package/scripts/topo-filter-pill-aliases-test.mjs +121 -0
- package/scripts/topo-filter-pill-body-click-test.mjs +112 -0
- package/scripts/topo-filter-pill-count-test.mjs +110 -0
- package/scripts/topo-filter-pill-mobile-test.mjs +90 -0
- package/scripts/topo-fit-key-test.mjs +106 -0
- package/scripts/topo-freshness-chip-test.mjs +74 -0
- package/scripts/topo-group-box-pinned-state-test.mjs +95 -0
- package/scripts/topo-group-count-test.mjs +68 -0
- package/scripts/topo-group-hover-test.mjs +82 -0
- package/scripts/topo-group-label-hover-test.mjs +119 -0
- package/scripts/topo-group-label-tint-test.mjs +99 -0
- package/scripts/topo-group-label-title-test.mjs +83 -0
- package/scripts/topo-group-pin-test.mjs +119 -0
- package/scripts/topo-group-status-pips-test.mjs +103 -0
- package/scripts/topo-groupbox-live-test.mjs +100 -0
- package/scripts/topo-groupbox-test.mjs +4 -2
- package/scripts/topo-help-overlay-r109-test.mjs +67 -0
- package/scripts/topo-help-overlay-r59-test.mjs +67 -0
- package/scripts/topo-help-overlay-r65-test.mjs +64 -0
- package/scripts/topo-help-overlay-r70-test.mjs +63 -0
- package/scripts/topo-help-overlay-r78-test.mjs +59 -0
- package/scripts/topo-help-overlay-r81-test.mjs +58 -0
- package/scripts/topo-help-overlay-r87-test.mjs +64 -0
- package/scripts/topo-help-overlay-r91-test.mjs +65 -0
- package/scripts/topo-hub-busyness-test.mjs +100 -0
- package/scripts/topo-hub-click-fit-test.mjs +137 -0
- package/scripts/topo-hub-title-test.mjs +93 -0
- package/scripts/topo-ingroup-edge-test.mjs +103 -0
- package/scripts/topo-label-lift-test.mjs +119 -0
- package/scripts/topo-label-scale-test.mjs +78 -0
- package/scripts/topo-lastseen-tooltip-test.mjs +59 -0
- package/scripts/topo-layout-key-test.mjs +91 -0
- package/scripts/topo-legend-count-test.mjs +88 -0
- package/scripts/topo-legend-hover-test.mjs +113 -0
- package/scripts/topo-legend-panel-header-test.mjs +83 -0
- package/scripts/topo-legend-pin-test.mjs +128 -0
- package/scripts/topo-legend-row-tint-test.mjs +95 -0
- package/scripts/topo-minimap-test.mjs +68 -0
- package/scripts/topo-mobile-chips-test.mjs +103 -0
- package/scripts/topo-node-hover-lift-test.mjs +85 -0
- package/scripts/topo-node-title-test.mjs +101 -0
- package/scripts/topo-offline-dim-test.mjs +70 -0
- package/scripts/topo-overlap-test.mjs +32 -5
- package/scripts/topo-pan-cursor-test.mjs +62 -0
- package/scripts/topo-panel-elevation-test.mjs +76 -0
- package/scripts/topo-particle-stagger-test.mjs +82 -0
- package/scripts/topo-pin-persistence-test.mjs +105 -0
- package/scripts/topo-popover-meta-test.mjs +78 -0
- package/scripts/topo-pressure-bar-test.mjs +69 -0
- package/scripts/topo-pressure-hover-test.mjs +123 -0
- package/scripts/topo-pressure-pin-test.mjs +119 -0
- package/scripts/topo-pressure-seg-tooltip-test.mjs +98 -0
- package/scripts/topo-pulse-rate-test.mjs +71 -0
- package/scripts/topo-recent-panel-count-test.mjs +72 -0
- package/scripts/topo-recent-row-hover-test.mjs +122 -0
- package/scripts/topo-recent-row-tint-test.mjs +91 -0
- package/scripts/topo-recent-row-ts-test.mjs +92 -0
- package/scripts/topo-recent-signal-empty-hint-test.mjs +73 -0
- package/scripts/topo-recent-signal-empty-test.mjs +63 -0
- package/scripts/topo-reduced-motion-test.mjs +77 -0
- package/scripts/topo-status-chip-hover-test.mjs +101 -0
- package/scripts/topo-status-chip-tooltip-test.mjs +95 -0
- package/scripts/topo-tier-ring-occupancy-test.mjs +87 -0
- package/scripts/topo-tier-ring-test.mjs +69 -0
- package/scripts/topo-tier-ring-tint-test.mjs +110 -0
- package/scripts/topo-tier-stagger-test.mjs +89 -0
- package/scripts/topo-tz-ghost-test.mjs +69 -0
- package/scripts/topo-vendor-chip-hover-test.mjs +111 -0
- package/scripts/topo-vendor-letter-tooltip-test.mjs +92 -0
- package/scripts/topo-vendor-pill-test.mjs +125 -0
- package/scripts/topo-vendor-pin-test.mjs +125 -0
- package/scripts/topo-wheel-zoom-test.mjs +79 -0
- package/scripts/topo-working-halo-breath-test.mjs +86 -0
- package/scripts/topo-zoom-keys-test.mjs +89 -0
- package/.next/static/chunks/0-7iowzu4.798.js +0 -1
- package/.next/static/chunks/02t5jr4shh.hy.js +0 -1
- package/.next/static/chunks/0_qfg5s5uzs7..js +0 -7
- package/.next/static/chunks/0cj5qr_a8dzky.js +0 -7
- package/.next/static/chunks/0dttftqqehpdj.js +0 -1
- package/.next/static/chunks/0elalb9n_.~di.js +0 -1
- package/.next/static/chunks/0j_q__o8p6g83.js +0 -1
- package/.next/static/chunks/0ja71r81z59di.js +0 -1
- package/.next/static/chunks/0jl6_w50zh4l5.js +0 -1
- package/.next/static/chunks/0r4q73.-1bkyl.js +0 -1
- package/.next/static/chunks/0r_c8eccg3z2o.js +0 -1
- package/.next/static/chunks/0z6mgaoe8c89-.js +0 -1
- package/.next/static/chunks/10oi7l-tycgl9.js +0 -1
- package/.next/static/chunks/11uk1izuv3~0t.js +0 -1
- package/.next/static/chunks/11y8xsp9mn.m8.css +0 -2
- /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_buildManifest.js +0 -0
- /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{irozqkI_mD2Ls10yQ_4yG → fh5pUqgTFNt162QarYePi}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/* Round 69 verification: Cmd+K palette can pin topology filters from
|
|
2
|
+
* anywhere. The palette dispatches a CustomEvent('anet:topo-pin') that
|
|
3
|
+
* TopoGraph listens for and translates into pinnedStatus / pinnedGroup
|
|
4
|
+
* state. sessionStorage is written in lockstep so a reload preserves it
|
|
5
|
+
* (R66 carries it through).
|
|
6
|
+
*
|
|
7
|
+
* - Dispatch {kind:'status', value:'working'} → pill appears + idle/
|
|
8
|
+
* offline nodes dim. sessionStorage holds 'working'.
|
|
9
|
+
* - Dispatch {kind:'clear'} → pill disappears + opacities restore.
|
|
10
|
+
* sessionStorage entries cleared.
|
|
11
|
+
* - Dispatch {kind:'group', value:'alpha'} → group pill + opacities
|
|
12
|
+
* composed; storage holds 'alpha'.
|
|
13
|
+
*/
|
|
14
|
+
import { chromium } from 'playwright';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
|
|
17
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
18
|
+
const browser = await chromium.launch({ headless: true });
|
|
19
|
+
|
|
20
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
21
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
22
|
+
await ctx.addInitScript(() => {
|
|
23
|
+
try {
|
|
24
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
25
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
26
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
27
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
28
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
29
|
+
} catch {}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
33
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
34
|
+
const r = await route.fetch();
|
|
35
|
+
const b = await r.json();
|
|
36
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
37
|
+
const sessions = [
|
|
38
|
+
{ alias: 'alpha1', status: 'working', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
39
|
+
{ alias: 'alpha2', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
40
|
+
{ alias: 'alpha3', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
41
|
+
{ alias: 'beta', status: 'idle', network_id: nid, project_dir: null, created_at: fresh, updated_at: fresh, last_seen_at: fresh },
|
|
42
|
+
];
|
|
43
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
50
|
+
await page.waitForTimeout(500);
|
|
51
|
+
|
|
52
|
+
const snapshot = () => page.evaluate(() => {
|
|
53
|
+
const pills = [...document.querySelectorAll('[data-active-filter]')].map(el => ({
|
|
54
|
+
kind: el.getAttribute('data-active-filter'),
|
|
55
|
+
text: (el.innerText || el.textContent || '').trim(),
|
|
56
|
+
}));
|
|
57
|
+
const opacities = {};
|
|
58
|
+
for (const a of ['alpha1', 'alpha2', 'beta']) {
|
|
59
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
60
|
+
opacities[a] = g ? +(g.style.opacity || '1') : null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
pills,
|
|
64
|
+
opacities,
|
|
65
|
+
storageStatus: sessionStorage.getItem('anet-topo-pinned-status'),
|
|
66
|
+
storageGroup: sessionStorage.getItem('anet-topo-pinned-group'),
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const before = await snapshot();
|
|
71
|
+
|
|
72
|
+
// Dispatch palette pin: status=working
|
|
73
|
+
await page.evaluate(() => {
|
|
74
|
+
// The Cmd+K commands write to storage AND dispatch the event in
|
|
75
|
+
// lockstep. We mirror both here so we exercise the same code path
|
|
76
|
+
// a palette `perform()` would take.
|
|
77
|
+
sessionStorage.setItem('anet-topo-pinned-status', 'working');
|
|
78
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'status', value: 'working' } }));
|
|
79
|
+
});
|
|
80
|
+
await page.waitForTimeout(250);
|
|
81
|
+
const afterStatus = await snapshot();
|
|
82
|
+
|
|
83
|
+
// Dispatch palette pin: group=alpha (additionally)
|
|
84
|
+
await page.evaluate(() => {
|
|
85
|
+
sessionStorage.setItem('anet-topo-pinned-group', 'alpha');
|
|
86
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'group', value: 'alpha' } }));
|
|
87
|
+
});
|
|
88
|
+
await page.waitForTimeout(250);
|
|
89
|
+
const afterGroup = await snapshot();
|
|
90
|
+
|
|
91
|
+
// Dispatch palette clear
|
|
92
|
+
await page.evaluate(() => {
|
|
93
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
94
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
95
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear' } }));
|
|
96
|
+
});
|
|
97
|
+
await page.waitForTimeout(250);
|
|
98
|
+
const afterClear = await snapshot();
|
|
99
|
+
|
|
100
|
+
await browser.close();
|
|
101
|
+
|
|
102
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
103
|
+
const dim = (v) => v != null && v < 0.4;
|
|
104
|
+
const results = {
|
|
105
|
+
before_noPills: before.pills.length === 0,
|
|
106
|
+
before_baselineBright: bright(before.opacities.alpha1) && bright(before.opacities.alpha2),
|
|
107
|
+
status_pillRendered: afterStatus.pills.some(p => p.kind === 'status' && /working/.test(p.text)),
|
|
108
|
+
status_dimsIdle: dim(afterStatus.opacities.alpha2) && dim(afterStatus.opacities.beta),
|
|
109
|
+
status_keepsWorking: bright(afterStatus.opacities.alpha1),
|
|
110
|
+
status_storageWritten: afterStatus.storageStatus === 'working',
|
|
111
|
+
groupAdded_bothPills: afterGroup.pills.length === 2,
|
|
112
|
+
groupAdded_storageBoth: afterGroup.storageStatus === 'working' && afterGroup.storageGroup === 'alpha',
|
|
113
|
+
clear_noPills: afterClear.pills.length === 0,
|
|
114
|
+
clear_restoredOpacities: bright(afterClear.opacities.alpha1) && bright(afterClear.opacities.alpha2) && bright(afterClear.opacities.beta),
|
|
115
|
+
clear_storageCleared: afterClear.storageStatus === null && afterClear.storageGroup === null,
|
|
116
|
+
};
|
|
117
|
+
const ok = Object.values(results).every(Boolean);
|
|
118
|
+
console.log(`${ok ? '✅' : '❌'} cmdk-pin:`, JSON.stringify(results),
|
|
119
|
+
`\n before=`, before,
|
|
120
|
+
`\n afterStatus=`, afterStatus,
|
|
121
|
+
`\n afterGroup=`, afterGroup,
|
|
122
|
+
`\n afterClear=`, afterClear);
|
|
123
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* Round 90 verification: Cmd+K palette "Clear topology filters" also
|
|
2
|
+
* releases the R88 pinnedVendor (the R69 implementation only knew
|
|
3
|
+
* about status + group). Plus the new granular "Clear topology vendor
|
|
4
|
+
* filter" command releases just vendor without disturbing status or
|
|
5
|
+
* group pins.
|
|
6
|
+
*
|
|
7
|
+
* Drive via direct CustomEvent dispatch (avoids fuzzy-match flake —
|
|
8
|
+
* same approach as the R82 chip-pin-mirror test). Verifies both the
|
|
9
|
+
* TopoGraph listener branch (state mutation) and the in-memory
|
|
10
|
+
* sessionStorage cleanup.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
|
|
18
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
19
|
+
await ctx.addInitScript(() => {
|
|
20
|
+
try {
|
|
21
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
22
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
23
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
24
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
25
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
26
|
+
sessionStorage.removeItem('anet-topo-pinned-vendor');
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
31
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
32
|
+
const r = await route.fetch();
|
|
33
|
+
const b = await r.json();
|
|
34
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
35
|
+
const mk = (alias, status, model) => ({
|
|
36
|
+
alias, status, model, runtime: 'cli-claude-code',
|
|
37
|
+
network_id: nid, project_dir: null,
|
|
38
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
39
|
+
});
|
|
40
|
+
const sessions = [
|
|
41
|
+
mk('alpha', 'working', 'claude-opus-4'),
|
|
42
|
+
mk('beta', 'idle', 'gpt-5'),
|
|
43
|
+
mk('gamma', 'idle', 'claude-opus-4'),
|
|
44
|
+
];
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
46
|
+
});
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
48
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
49
|
+
const page = await ctx.newPage();
|
|
50
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
51
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
52
|
+
await page.waitForTimeout(400);
|
|
53
|
+
|
|
54
|
+
const pills = () => page.evaluate(() => {
|
|
55
|
+
const all = document.querySelectorAll('[data-active-filter]');
|
|
56
|
+
return [...all].map(p => p.getAttribute('data-active-filter'));
|
|
57
|
+
});
|
|
58
|
+
const storage = () => page.evaluate(() => ({
|
|
59
|
+
status: sessionStorage.getItem('anet-topo-pinned-status'),
|
|
60
|
+
group: sessionStorage.getItem('anet-topo-pinned-group'),
|
|
61
|
+
vendor: sessionStorage.getItem('anet-topo-pinned-vendor'),
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
// Helper: dispatch a topo-pin event from the page side.
|
|
65
|
+
const pin = async (kind, value) => {
|
|
66
|
+
await page.evaluate(({ kind, value }) => {
|
|
67
|
+
if (kind === 'status') sessionStorage.setItem('anet-topo-pinned-status', value);
|
|
68
|
+
if (kind === 'group') sessionStorage.setItem('anet-topo-pinned-group', value);
|
|
69
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind, value } }));
|
|
70
|
+
}, { kind, value });
|
|
71
|
+
await page.waitForTimeout(200);
|
|
72
|
+
};
|
|
73
|
+
// Pin vendor via the actual UI letter click (no CustomEvent for that
|
|
74
|
+
// flow yet — palette doesn't surface per-vendor pin actions).
|
|
75
|
+
const pinVendor = async (initial) => {
|
|
76
|
+
await page.locator(`[data-vendor-letter="${initial}"]`).click();
|
|
77
|
+
await page.mouse.move(10, 10);
|
|
78
|
+
await page.waitForTimeout(200);
|
|
79
|
+
};
|
|
80
|
+
const clearAll = async () => {
|
|
81
|
+
await page.evaluate(() => {
|
|
82
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
83
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
84
|
+
sessionStorage.removeItem('anet-topo-pinned-vendor');
|
|
85
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear' } }));
|
|
86
|
+
});
|
|
87
|
+
await page.waitForTimeout(250);
|
|
88
|
+
};
|
|
89
|
+
const clearVendor = async () => {
|
|
90
|
+
await page.evaluate(() => {
|
|
91
|
+
sessionStorage.removeItem('anet-topo-pinned-vendor');
|
|
92
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'clear-vendor' } }));
|
|
93
|
+
});
|
|
94
|
+
await page.waitForTimeout(250);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// === Test 1: clear-all now also clears vendor (the R90 bug fix). ===
|
|
98
|
+
await pin('status', 'working');
|
|
99
|
+
await pinVendor('A');
|
|
100
|
+
const afterPinBoth = { pills: await pills(), storage: await storage() };
|
|
101
|
+
|
|
102
|
+
await clearAll();
|
|
103
|
+
const afterClearAll = { pills: await pills(), storage: await storage() };
|
|
104
|
+
|
|
105
|
+
// === Test 2: granular clear-vendor leaves status + group untouched. ===
|
|
106
|
+
await pin('status', 'working');
|
|
107
|
+
await pinVendor('A');
|
|
108
|
+
const beforeGranular = { pills: await pills(), storage: await storage() };
|
|
109
|
+
|
|
110
|
+
await clearVendor();
|
|
111
|
+
const afterClearVendor = { pills: await pills(), storage: await storage() };
|
|
112
|
+
|
|
113
|
+
await browser.close();
|
|
114
|
+
|
|
115
|
+
const sameSet = (a, b) => a.length === b.length && a.every(x => b.includes(x));
|
|
116
|
+
const results = {
|
|
117
|
+
pinBoth_threePills: sameSet(afterPinBoth.pills, ['status', 'vendor']),
|
|
118
|
+
pinBoth_storageBoth: afterPinBoth.storage.status === 'working' && afterPinBoth.storage.vendor === 'A',
|
|
119
|
+
clearAll_noPills: afterClearAll.pills.length === 0,
|
|
120
|
+
clearAll_storageAllNull: afterClearAll.storage.status === null && afterClearAll.storage.group === null && afterClearAll.storage.vendor === null,
|
|
121
|
+
granular_threePills: sameSet(beforeGranular.pills, ['status', 'vendor']),
|
|
122
|
+
clearVendor_keepsStatus: afterClearVendor.pills.includes('status'),
|
|
123
|
+
clearVendor_dropsVendor: !afterClearVendor.pills.includes('vendor'),
|
|
124
|
+
clearVendor_storageStatusKept: afterClearVendor.storage.status === 'working',
|
|
125
|
+
clearVendor_storageVendorCleared: afterClearVendor.storage.vendor === null,
|
|
126
|
+
};
|
|
127
|
+
const ok = Object.values(results).every(Boolean);
|
|
128
|
+
console.log(`${ok ? '✅' : '❌'} cmdk vendor clear:`, JSON.stringify(results),
|
|
129
|
+
`\n afterPinBoth=`, afterPinBoth,
|
|
130
|
+
`\n afterClearAll=`, afterClearAll,
|
|
131
|
+
`\n beforeGranular=`, beforeGranular,
|
|
132
|
+
`\n afterClearVendor=`, afterClearVendor);
|
|
133
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* Round 108 verification: Cmd+K palette can pin a vendor filter via
|
|
2
|
+
* three new static actions ("Pin topology filter: Anthropic" /
|
|
3
|
+
* OpenAI / 书生). TopoGraph's R69 listener handles kind='vendor' to
|
|
4
|
+
* setPinnedVendor. The R88 stale-purge cleans up if the vendor isn't
|
|
5
|
+
* in the fleet.
|
|
6
|
+
*
|
|
7
|
+
* Drives via direct CustomEvent dispatch (same deterministic path
|
|
8
|
+
* R82/R90 tests use — fuzzy-match-free). Verifies pin sticks, vendor
|
|
9
|
+
* pill renders, storage persists, and Esc / clear-all release.
|
|
10
|
+
*/
|
|
11
|
+
import { chromium } from 'playwright';
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
|
|
14
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
15
|
+
const browser = await chromium.launch({ headless: true });
|
|
16
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1200 } });
|
|
17
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
18
|
+
await ctx.addInitScript(() => {
|
|
19
|
+
try {
|
|
20
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
21
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
22
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
23
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
24
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
25
|
+
sessionStorage.removeItem('anet-topo-pinned-vendor');
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
30
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
31
|
+
const r = await route.fetch();
|
|
32
|
+
const b = await r.json();
|
|
33
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
34
|
+
const mk = (alias, model) => ({
|
|
35
|
+
alias, status: 'idle', model, runtime: 'cli-claude-code',
|
|
36
|
+
network_id: nid, project_dir: null,
|
|
37
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
38
|
+
});
|
|
39
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [
|
|
40
|
+
mk('alpha', 'claude-opus-4'), // A (Anthropic)
|
|
41
|
+
mk('beta', 'gpt-5'), // O (OpenAI)
|
|
42
|
+
mk('gamma', 'claude-opus-4'), // A
|
|
43
|
+
] } });
|
|
44
|
+
});
|
|
45
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [] } }));
|
|
46
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
47
|
+
const page = await ctx.newPage();
|
|
48
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
49
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
50
|
+
await page.waitForTimeout(400);
|
|
51
|
+
|
|
52
|
+
const pillCount = () => page.locator('[data-active-filter="vendor"]').count();
|
|
53
|
+
const storage = () => page.evaluate(() => sessionStorage.getItem('anet-topo-pinned-vendor'));
|
|
54
|
+
const opacities = () => page.evaluate(() => {
|
|
55
|
+
const o = {};
|
|
56
|
+
for (const a of ['alpha', 'beta', 'gamma']) {
|
|
57
|
+
const g = document.querySelector(`g[data-node="${a}"]`);
|
|
58
|
+
o[a] = g ? +(g.style.opacity || '1') : null;
|
|
59
|
+
}
|
|
60
|
+
return o;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Initial state — no pin.
|
|
64
|
+
const beforePill = await pillCount();
|
|
65
|
+
const beforeStorage = await storage();
|
|
66
|
+
|
|
67
|
+
// Pin A via R108 palette action's CustomEvent.
|
|
68
|
+
await page.evaluate(() => {
|
|
69
|
+
sessionStorage.setItem('anet-topo-pinned-vendor', 'A');
|
|
70
|
+
window.dispatchEvent(new CustomEvent('anet:topo-pin', { detail: { kind: 'vendor', value: 'A' } }));
|
|
71
|
+
});
|
|
72
|
+
await page.waitForTimeout(300);
|
|
73
|
+
const afterPinA_ops = await opacities();
|
|
74
|
+
const afterPinA_pill = await pillCount();
|
|
75
|
+
const afterPinA_storage = await storage();
|
|
76
|
+
|
|
77
|
+
// Esc → clear pin.
|
|
78
|
+
await page.keyboard.press('Escape');
|
|
79
|
+
await page.waitForTimeout(300);
|
|
80
|
+
const afterEsc_pill = await pillCount();
|
|
81
|
+
const afterEsc_storage = await storage();
|
|
82
|
+
|
|
83
|
+
await browser.close();
|
|
84
|
+
|
|
85
|
+
const bright = (v) => v != null && v >= 0.55;
|
|
86
|
+
const dim = (v) => v != null && v < 0.4;
|
|
87
|
+
const results = {
|
|
88
|
+
before_noPill: beforePill === 0,
|
|
89
|
+
before_noStorage: beforeStorage === null,
|
|
90
|
+
pinA_pillShown: afterPinA_pill === 1,
|
|
91
|
+
pinA_storageWritten: afterPinA_storage === 'A',
|
|
92
|
+
pinA_keepsClaudes: bright(afterPinA_ops.alpha) && bright(afterPinA_ops.gamma),
|
|
93
|
+
pinA_dimsGPT: dim(afterPinA_ops.beta),
|
|
94
|
+
esc_clearsPill: afterEsc_pill === 0,
|
|
95
|
+
esc_clearsStorage: afterEsc_storage === null,
|
|
96
|
+
};
|
|
97
|
+
const ok = Object.values(results).every(Boolean);
|
|
98
|
+
console.log(`${ok ? '✅' : '❌'} cmdk vendor pin:`, JSON.stringify(results),
|
|
99
|
+
`\n afterPinA_ops=`, afterPinA_ops,
|
|
100
|
+
`\n afterPinA_pill=`, afterPinA_pill,
|
|
101
|
+
`\n afterPinA_storage=`, afterPinA_storage,
|
|
102
|
+
`\n afterEsc_pill=`, afterEsc_pill);
|
|
103
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* Round 33 verification: hover tooltip carries `cwd: <project_dir>` on a
|
|
2
|
+
* second line when reported; falls back to the identity-only line
|
|
3
|
+
* otherwise; no `cwd:` line for nodes without project_dir. */
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
8
|
+
const browser = await chromium.launch({ headless: true });
|
|
9
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
10
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
11
|
+
await ctx.addInitScript(() => {
|
|
12
|
+
try {
|
|
13
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
14
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
15
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
16
|
+
} catch {}
|
|
17
|
+
});
|
|
18
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
19
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
20
|
+
const r = await route.fetch();
|
|
21
|
+
const b = await r.json();
|
|
22
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
23
|
+
const sessions = [
|
|
24
|
+
{
|
|
25
|
+
alias: 'full', status: 'idle', network_id: nid,
|
|
26
|
+
project_dir: '/home/vansin/agent-orchestra',
|
|
27
|
+
model: 'claude-opus-4-7', runtime: 'cli-claude-code',
|
|
28
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
alias: 'cwdOnly', status: 'idle', network_id: nid,
|
|
32
|
+
project_dir: '/home/vansin/some-project',
|
|
33
|
+
model: null, runtime: null,
|
|
34
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
alias: 'bare', status: 'idle', network_id: nid,
|
|
38
|
+
project_dir: null, model: null, runtime: null,
|
|
39
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
43
|
+
});
|
|
44
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
45
|
+
const page = await ctx.newPage();
|
|
46
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
47
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 3, { timeout: 30000 });
|
|
48
|
+
await page.waitForTimeout(400);
|
|
49
|
+
|
|
50
|
+
const titleOf = alias => page.$eval(`g[data-node="${alias}"] title`, el => el.textContent);
|
|
51
|
+
const full = await titleOf('full');
|
|
52
|
+
const cwdOnly = await titleOf('cwdOnly');
|
|
53
|
+
const bare = await titleOf('bare');
|
|
54
|
+
|
|
55
|
+
await browser.close();
|
|
56
|
+
const results = {
|
|
57
|
+
fullHasIdentityAndCwd: /claude-opus-4-7/.test(full) && /cwd: \/home\/vansin\/agent-orchestra/.test(full) && full.includes('\n'),
|
|
58
|
+
cwdOnlyShowsAliasAndCwd: cwdOnly.startsWith('cwdOnly') && /cwd: \/home\/vansin\/some-project/.test(cwdOnly),
|
|
59
|
+
bareShowsAliasOnly: bare === 'bare',
|
|
60
|
+
};
|
|
61
|
+
const ok = Object.values(results).every(Boolean);
|
|
62
|
+
console.log(`${ok ? '✅' : '❌'} cwd tooltip:`, JSON.stringify(results),
|
|
63
|
+
`\n full=${JSON.stringify(full)}\n cwdOnly=${JSON.stringify(cwdOnly)}\n bare=${JSON.stringify(bare)}`);
|
|
64
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* Round 41 verification: double-clicking empty canvas resets view to
|
|
2
|
+
* {zoom:1, x:0, y:0}. Double-clicking a node does NOT reset (the
|
|
3
|
+
* node's own onClick wins, and resetView should not fire). */
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
8
|
+
const browser = await chromium.launch({ headless: true });
|
|
9
|
+
const ctx = await browser.newContext({ viewport: { width: 1280, height: 1600 } });
|
|
10
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
11
|
+
await ctx.addInitScript(() => {
|
|
12
|
+
try {
|
|
13
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
14
|
+
localStorage.setItem('anet-topo-layout', 'grid');
|
|
15
|
+
localStorage.setItem('anet-topo-view', JSON.stringify({ zoom: 1, x: 0, y: 0 }));
|
|
16
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
17
|
+
} catch {}
|
|
18
|
+
});
|
|
19
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
20
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
21
|
+
const r = await route.fetch();
|
|
22
|
+
const b = await r.json();
|
|
23
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
24
|
+
const sessions = ['a', 'b'].map(a => ({
|
|
25
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
26
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
27
|
+
}));
|
|
28
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
29
|
+
});
|
|
30
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
31
|
+
const page = await ctx.newPage();
|
|
32
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
33
|
+
await page.waitForSelector('svg[viewBox="0 0 1000 680"]', { timeout: 30000 });
|
|
34
|
+
await page.waitForTimeout(500);
|
|
35
|
+
|
|
36
|
+
const readZoom = () => page.evaluate(() => {
|
|
37
|
+
const span = document.querySelector('button[aria-label="Zoom in"]').parentElement.querySelector('span[title*="zoom level"]');
|
|
38
|
+
return +span.textContent.replace('%', '');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Zoom in a couple times via keyboard, then dbl-click empty canvas → expect reset to 100%.
|
|
42
|
+
await page.keyboard.press('=');
|
|
43
|
+
await page.keyboard.press('=');
|
|
44
|
+
await page.waitForTimeout(120);
|
|
45
|
+
const zoomedIn = await readZoom();
|
|
46
|
+
|
|
47
|
+
// Find empty-canvas coords: top-left area of the SVG, well outside node positions.
|
|
48
|
+
const box = await page.$eval('svg[viewBox="0 0 1000 680"]', el => el.getBoundingClientRect());
|
|
49
|
+
// y=0.35 is below the top-overlay panels (which sit at y<100 inside the SVG)
|
|
50
|
+
const emptyX = box.x + box.width * 0.20;
|
|
51
|
+
const emptyY = box.y + box.height * 0.40;
|
|
52
|
+
await page.mouse.dblclick(emptyX, emptyY);
|
|
53
|
+
await page.waitForTimeout(200);
|
|
54
|
+
const afterEmptyDbl = await readZoom();
|
|
55
|
+
|
|
56
|
+
// Now zoom in again and dbl-click a node — view should NOT reset.
|
|
57
|
+
await page.keyboard.press('=');
|
|
58
|
+
await page.keyboard.press('=');
|
|
59
|
+
await page.waitForTimeout(120);
|
|
60
|
+
const zoomedIn2 = await readZoom();
|
|
61
|
+
await page.locator('g[data-node="a"]').dblclick().catch(() => {});
|
|
62
|
+
await page.waitForTimeout(200);
|
|
63
|
+
const afterNodeDbl = await readZoom();
|
|
64
|
+
|
|
65
|
+
await browser.close();
|
|
66
|
+
const results = {
|
|
67
|
+
zoomedInBeforeDbl: zoomedIn > 100,
|
|
68
|
+
emptyCanvasResets: afterEmptyDbl === 100,
|
|
69
|
+
nodeDoesNotReset: zoomedIn2 > 100 && afterNodeDbl === zoomedIn2,
|
|
70
|
+
};
|
|
71
|
+
const ok = Object.values(results).every(Boolean);
|
|
72
|
+
console.log(`${ok ? '✅' : '❌'} dblclick reset:`, JSON.stringify(results),
|
|
73
|
+
`\n zoomedIn=${zoomedIn} afterEmptyDbl=${afterEmptyDbl} zoomedIn2=${zoomedIn2} afterNodeDbl=${afterNodeDbl}`);
|
|
74
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* Round 76 verification: source-side dispatch pulse renders for high-
|
|
2
|
+
* traffic edges only (link.count >= 3), bookending the R75 destination
|
|
3
|
+
* ping. Quiet edges (count < 3) skip the source pulse to keep the
|
|
4
|
+
* canvas calm. Reduced-motion users see neither pulse nor ping.
|
|
5
|
+
*
|
|
6
|
+
* - Busy alpha→beta (5 messages, fresh) → dispatch pulse present at
|
|
7
|
+
* source AND arrival ping at destination.
|
|
8
|
+
* - Quiet gamma→delta (1 message, fresh) → arrival ping only, no
|
|
9
|
+
* dispatch pulse.
|
|
10
|
+
* - Reduced-motion: no dispatch pulses, no arrival pings.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
|
|
18
|
+
async function probe(reducedMotion) {
|
|
19
|
+
const ctx = await browser.newContext({
|
|
20
|
+
viewport: { width: 1280, height: 1500 },
|
|
21
|
+
reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
|
|
22
|
+
});
|
|
23
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
24
|
+
await ctx.addInitScript(() => {
|
|
25
|
+
try {
|
|
26
|
+
localStorage.setItem('anet-theme', 'cyber');
|
|
27
|
+
localStorage.setItem('anet-topo-layout', 'ring');
|
|
28
|
+
sessionStorage.setItem('anet_v3_auth', '1');
|
|
29
|
+
sessionStorage.removeItem('anet-topo-pinned-status');
|
|
30
|
+
sessionStorage.removeItem('anet-topo-pinned-group');
|
|
31
|
+
} catch {}
|
|
32
|
+
});
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
34
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
35
|
+
const r = await route.fetch();
|
|
36
|
+
const b = await r.json();
|
|
37
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
38
|
+
const sessions = ['alpha', 'beta', 'gamma', 'delta'].map(a => ({
|
|
39
|
+
alias: a, status: 'idle', network_id: nid, project_dir: null,
|
|
40
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
41
|
+
}));
|
|
42
|
+
await route.fulfill({ response: r, json: { ...b, sessions } });
|
|
43
|
+
});
|
|
44
|
+
// 5 busy messages alpha→beta + 1 quiet gamma→delta.
|
|
45
|
+
const now = new Date().toISOString();
|
|
46
|
+
const msgs = [
|
|
47
|
+
...Array.from({ length: 5 }).map(() => ({ from_alias: 'alpha', to_alias: 'beta', content: 'm', created_at: now })),
|
|
48
|
+
{ from_alias: 'gamma', to_alias: 'delta', content: 'm', created_at: now },
|
|
49
|
+
];
|
|
50
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
const page = await ctx.newPage();
|
|
53
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
54
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
55
|
+
await page.waitForTimeout(600);
|
|
56
|
+
const elements = await page.evaluate(() => {
|
|
57
|
+
return {
|
|
58
|
+
dispatchPulses: [...document.querySelectorAll('[data-dispatch-pulse]')].map(el => el.getAttribute('data-dispatch-pulse')),
|
|
59
|
+
arrivalPings: [...document.querySelectorAll('[data-arrival-ping]')].map(el => el.getAttribute('data-arrival-ping')),
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
await ctx.close();
|
|
63
|
+
return elements;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const normal = await probe(false);
|
|
67
|
+
const reduced = await probe(true);
|
|
68
|
+
await browser.close();
|
|
69
|
+
|
|
70
|
+
const has = (arr, key) => arr.includes(key);
|
|
71
|
+
const results = {
|
|
72
|
+
normal_busyHasDispatch: has(normal.dispatchPulses, 'alpha->beta'),
|
|
73
|
+
normal_quietHasNoDispatch: !has(normal.dispatchPulses, 'gamma->delta'),
|
|
74
|
+
normal_busyHasArrival: has(normal.arrivalPings, 'alpha->beta'),
|
|
75
|
+
normal_quietHasArrival: has(normal.arrivalPings, 'gamma->delta'),
|
|
76
|
+
reduced_noDispatchPulses: reduced.dispatchPulses.length === 0,
|
|
77
|
+
reduced_noArrivalPings: reduced.arrivalPings.length === 0,
|
|
78
|
+
};
|
|
79
|
+
const ok = Object.values(results).every(Boolean);
|
|
80
|
+
console.log(`${ok ? '✅' : '❌'} dispatch pulse:`, JSON.stringify(results),
|
|
81
|
+
`\n normal=`, normal,
|
|
82
|
+
`\n reduced=`, reduced);
|
|
83
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Round 100 verification: edges with count >= 3 render a midpoint
|
|
2
|
+
* count badge. The edge stroke width gauges intensity but at high
|
|
3
|
+
* volume "5 vs 12" is hard to read off the line. The badge spells
|
|
4
|
+
* out the integer at the bezier midpoint.
|
|
5
|
+
*
|
|
6
|
+
* Fleet: alpha → beta with 5 messages, gamma → delta with 1, all
|
|
7
|
+
* fresh. Expect one badge for the 5-msg flow, none for the 1-msg
|
|
8
|
+
* flow (count below threshold). Verify badge text matches the
|
|
9
|
+
* link.count and pointer-events:none so the existing R48 edge
|
|
10
|
+
* hitbox stays alive.
|
|
11
|
+
*/
|
|
12
|
+
import { chromium } from 'playwright';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
16
|
+
const browser = await chromium.launch({ headless: true });
|
|
17
|
+
|
|
18
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
19
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
20
|
+
await ctx.addInitScript(() => {
|
|
21
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const fresh = new Date(Date.now() - 30 * 1000).toISOString();
|
|
25
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
26
|
+
const r = await route.fetch();
|
|
27
|
+
const b = await r.json();
|
|
28
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
29
|
+
const mk = (alias) => ({
|
|
30
|
+
alias, status: 'idle', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
31
|
+
network_id: nid, project_dir: null,
|
|
32
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
33
|
+
});
|
|
34
|
+
await route.fulfill({ response: r, json: { ...b, sessions: [mk('alpha'), mk('beta'), mk('gamma'), mk('delta')] } });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const mkMsg = (id, from_alias, to_alias, ageMs) => ({
|
|
39
|
+
id, from_alias, to_alias, content: 'ping', network_id: 'default',
|
|
40
|
+
created_at: new Date(now - ageMs).toISOString(),
|
|
41
|
+
});
|
|
42
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: [
|
|
43
|
+
// 5 alpha → beta (count=5, badge expected)
|
|
44
|
+
mkMsg('m1', 'alpha', 'beta', 30 * 1000),
|
|
45
|
+
mkMsg('m2', 'alpha', 'beta', 40 * 1000),
|
|
46
|
+
mkMsg('m3', 'alpha', 'beta', 50 * 1000),
|
|
47
|
+
mkMsg('m4', 'alpha', 'beta', 60 * 1000),
|
|
48
|
+
mkMsg('m5', 'alpha', 'beta', 70 * 1000),
|
|
49
|
+
// 1 gamma → delta (count=1, NO badge)
|
|
50
|
+
mkMsg('m6', 'gamma', 'delta', 80 * 1000),
|
|
51
|
+
// 3 beta → alpha (count=3 — threshold, badge expected)
|
|
52
|
+
mkMsg('m7', 'beta', 'alpha', 35 * 1000),
|
|
53
|
+
mkMsg('m8', 'beta', 'alpha', 45 * 1000),
|
|
54
|
+
mkMsg('m9', 'beta', 'alpha', 55 * 1000),
|
|
55
|
+
] } }));
|
|
56
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
57
|
+
const page = await ctx.newPage();
|
|
58
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
59
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 4, { timeout: 30000 });
|
|
60
|
+
await page.waitForSelector('[data-edge-count-badge]', { timeout: 10000 });
|
|
61
|
+
await page.waitForTimeout(400);
|
|
62
|
+
|
|
63
|
+
const badges = await page.evaluate(() => {
|
|
64
|
+
return [...document.querySelectorAll('[data-edge-count-badge]')].map(g => ({
|
|
65
|
+
key: g.getAttribute('data-edge-count-badge'),
|
|
66
|
+
count: g.querySelector('text')?.textContent || '',
|
|
67
|
+
pe: getComputedStyle(g).pointerEvents,
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const byKey = Object.fromEntries(badges.map(b => [b.key, b]));
|
|
74
|
+
const results = {
|
|
75
|
+
twoBadges: badges.length === 2,
|
|
76
|
+
alphaBeta_badge: byKey['alpha->beta']?.count === '5',
|
|
77
|
+
betaAlpha_badge: byKey['beta->alpha']?.count === '3',
|
|
78
|
+
gammaDelta_noBadge: !byKey['gamma->delta'],
|
|
79
|
+
badges_nonInteractive: badges.every(b => b.pe === 'none'),
|
|
80
|
+
};
|
|
81
|
+
const ok = Object.values(results).every(Boolean);
|
|
82
|
+
console.log(`${ok ? '✅' : '❌'} edge-count badge:`, JSON.stringify(results),
|
|
83
|
+
`\n badges=`, badges);
|
|
84
|
+
process.exit(ok ? 0 : 1);
|