@sleep2agi/agent-network-dashboard 0.5.1-preview.99 → 0.5.2-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/diagnostics/route-bundle-stats.json +32 -32
  4. package/.next/fallback-build-manifest.json +3 -3
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/server/app/_global-error.html +1 -1
  7. package/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_not-found.html +2 -2
  15. package/.next/server/app/_not-found.rsc +12 -12
  16. package/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
  17. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -7
  19. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/admin.html +2 -2
  24. package/.next/server/app/admin.rsc +14 -14
  25. package/.next/server/app/admin.segments/_full.segment.rsc +14 -14
  26. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  27. package/.next/server/app/admin.segments/_index.segment.rsc +7 -7
  28. package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
  29. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  30. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  31. package/.next/server/app/index.html +2 -2
  32. package/.next/server/app/index.rsc +14 -14
  33. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  34. package/.next/server/app/index.segments/_full.segment.rsc +14 -14
  35. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  36. package/.next/server/app/index.segments/_index.segment.rsc +7 -7
  37. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  38. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  39. package/.next/server/app/login.html +2 -2
  40. package/.next/server/app/login.rsc +14 -14
  41. package/.next/server/app/login.segments/_full.segment.rsc +14 -14
  42. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  43. package/.next/server/app/login.segments/_index.segment.rsc +7 -7
  44. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  45. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  46. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  47. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/logs.html +2 -2
  49. package/.next/server/app/logs.rsc +14 -14
  50. package/.next/server/app/logs.segments/_full.segment.rsc +14 -14
  51. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  52. package/.next/server/app/logs.segments/_index.segment.rsc +7 -7
  53. package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
  54. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  55. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  56. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  57. package/.next/server/app/messages.html +2 -2
  58. package/.next/server/app/messages.rsc +14 -14
  59. package/.next/server/app/messages.segments/_full.segment.rsc +14 -14
  60. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  61. package/.next/server/app/messages.segments/_index.segment.rsc +7 -7
  62. package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
  63. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  64. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  65. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  66. package/.next/server/app/node.html +2 -2
  67. package/.next/server/app/node.rsc +14 -14
  68. package/.next/server/app/node.segments/_full.segment.rsc +14 -14
  69. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  70. package/.next/server/app/node.segments/_index.segment.rsc +7 -7
  71. package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
  72. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  73. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  74. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/nodes.html +2 -2
  76. package/.next/server/app/nodes.rsc +14 -14
  77. package/.next/server/app/nodes.segments/_full.segment.rsc +14 -14
  78. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/nodes.segments/_index.segment.rsc +7 -7
  80. package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
  81. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  83. package/.next/server/app/page.js.nft.json +1 -1
  84. package/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  86. package/.next/server/app/server-logs.html +2 -2
  87. package/.next/server/app/server-logs.rsc +14 -14
  88. package/.next/server/app/server-logs.segments/_full.segment.rsc +14 -14
  89. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  90. package/.next/server/app/server-logs.segments/_index.segment.rsc +7 -7
  91. package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
  92. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  93. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  94. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  95. package/.next/server/app/settings/networks.html +2 -2
  96. package/.next/server/app/settings/networks.rsc +14 -14
  97. package/.next/server/app/settings/networks.segments/_full.segment.rsc +14 -14
  98. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  99. package/.next/server/app/settings/networks.segments/_index.segment.rsc +7 -7
  100. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
  101. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  102. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  103. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  104. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  105. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app/settings/tokens.html +2 -2
  107. package/.next/server/app/settings/tokens.rsc +14 -14
  108. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +14 -14
  109. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  110. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +7 -7
  111. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
  112. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  113. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  114. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  115. package/.next/server/app/settings.html +2 -2
  116. package/.next/server/app/settings.rsc +14 -14
  117. package/.next/server/app/settings.segments/_full.segment.rsc +14 -14
  118. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  119. package/.next/server/app/settings.segments/_index.segment.rsc +7 -7
  120. package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  121. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  122. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  123. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/tasks.html +2 -2
  126. package/.next/server/app/tasks.rsc +14 -14
  127. package/.next/server/app/tasks.segments/_full.segment.rsc +14 -14
  128. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  129. package/.next/server/app/tasks.segments/_index.segment.rsc +7 -7
  130. package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
  131. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  132. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  133. package/.next/server/chunks/ssr/{[root-of-the-server]__03b.f76._.js → [root-of-the-server]__0sv~g.o._.js} +2 -2
  134. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -0
  135. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
  136. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  141. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  142. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  143. package/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/server/pages/404.html +2 -2
  145. package/.next/server/pages/500.html +1 -1
  146. package/.next/server/server-reference-manifest.js +1 -1
  147. package/.next/server/server-reference-manifest.json +1 -1
  148. package/.next/static/chunks/{0l_~q07bhpkcx.js → 03a4--7ncekmk.js} +1 -1
  149. package/.next/static/chunks/0a4hmfvj-81x5.css +2 -0
  150. package/.next/static/chunks/0caxil0dw-oe9.js +4 -0
  151. package/.next/static/chunks/{03~~oirxz7~vc.js → 0p8xwrfjtykvn.js} +1 -1
  152. package/.next/static/chunks/0x.m3vy8e5iit.js +1 -0
  153. package/.next/static/chunks/12gq1w9k_7v06.js +1 -0
  154. package/.next/trace +2 -2
  155. package/.next/trace-build +1 -1
  156. package/app/components/ServersDrawer.tsx +17 -0
  157. package/app/components/TopoGraph.tsx +770 -76
  158. package/package.json +1 -1
  159. package/screenshots/v0.10.2-disk-verify/disk-render-full.png +0 -0
  160. package/screenshots/v0.10.2-disk-verify/disk-render.png +0 -0
  161. package/screenshots/v0.11.0-147/after.png +0 -0
  162. package/scripts/p0-147-screenshot.mjs +53 -0
  163. package/scripts/p0-servers-drawer-screenshot.mjs +2 -0
  164. package/scripts/topo-any-hover-attr-test.mjs +83 -0
  165. package/scripts/topo-any-pinned-attr-test.mjs +86 -0
  166. package/scripts/topo-chrome-fullscreen-icon-sw-test.mjs +92 -0
  167. package/scripts/topo-chrome-reset-icon-sw-test.mjs +80 -0
  168. package/scripts/topo-chrome-zoom-icon-sw-test.mjs +90 -0
  169. package/scripts/topo-dashboard-version-attr-test.mjs +69 -0
  170. package/scripts/topo-dense-alias-opacity-test.mjs +68 -0
  171. package/scripts/topo-edge-particle-hover-r-test.mjs +113 -0
  172. package/scripts/topo-endpoint-ring-r-hover-test.mjs +89 -0
  173. package/scripts/topo-fleet-count-attrs-test.mjs +87 -0
  174. package/scripts/topo-group-box-geom-transition-test.mjs +110 -0
  175. package/scripts/topo-group-box-rx-pin-test.mjs +103 -0
  176. package/scripts/topo-group-label-count-fw-test.mjs +100 -0
  177. package/scripts/topo-group-label-fw-pin-test.mjs +99 -0
  178. package/scripts/topo-group-label-tint-geom-test.mjs +94 -0
  179. package/scripts/topo-group-label-tint-transition-test.mjs +97 -0
  180. package/scripts/topo-group-pip-fontsize-test.mjs +106 -0
  181. package/scripts/topo-group-tier-attr-test.mjs +84 -0
  182. package/scripts/topo-group-tint-rx-pin-test.mjs +107 -0
  183. package/scripts/topo-hub-core-fill-hover-test.mjs +85 -0
  184. package/scripts/topo-hub-halo-r-hover-test.mjs +82 -0
  185. package/scripts/topo-legend-count-active-opacity-test.mjs +102 -0
  186. package/scripts/topo-legend-count-pin-fw-test.mjs +90 -0
  187. package/scripts/topo-minimap-viewport-opacity-test.mjs +96 -0
  188. package/scripts/topo-node-halo-hover-opacity-test.mjs +104 -0
  189. package/scripts/topo-node-halo-light-offline-test.mjs +80 -0
  190. package/scripts/topo-node-sub-text-fw-test.mjs +75 -0
  191. package/scripts/topo-overlap-stale-build-guard-test.mjs +66 -0
  192. package/scripts/topo-overlap-test.mjs +42 -1
  193. package/scripts/topo-recent-row-count-pin-fw-test.mjs +106 -0
  194. package/scripts/topo-recent-row-pip-hover-r-test.mjs +104 -0
  195. package/scripts/topo-runtime-icon-hover-test.mjs +96 -0
  196. package/.next/server/chunks/ssr/[root-of-the-server]__03b.f76._.js.map +0 -1
  197. package/.next/static/chunks/0.sf46gnv4wwm.js +0 -1
  198. package/.next/static/chunks/017hq2-5l~_98.css +0 -2
  199. package/.next/static/chunks/0_igeywsok2_-.js +0 -1
  200. package/.next/static/chunks/0igz0bww16uvc.js +0 -4
  201. /package/.next/static/{4rHxTzLwe2XC9M1rN-MpJ → 5IMzNtH5S5Oqe-FCw1CX4}/_buildManifest.js +0 -0
  202. /package/.next/static/{4rHxTzLwe2XC9M1rN-MpJ → 5IMzNtH5S5Oqe-FCw1CX4}/_clientMiddlewareManifest.js +0 -0
  203. /package/.next/static/{4rHxTzLwe2XC9M1rN-MpJ → 5IMzNtH5S5Oqe-FCw1CX4}/_ssgManifest.js +0 -0
@@ -0,0 +1,82 @@
1
+ /* Round 451 verification: hub center halo radius hover lift —
2
+ * r 20 → 22 on hoveredHub.
3
+ *
4
+ * Contract:
5
+ * - rest: hub halo reports data-topo-hub-halo-radius '20' + hovered='false'
6
+ * - hover the hub <g>: halo lifts to r '22' + hovered='true'
7
+ */
8
+ import { chromium } from 'playwright';
9
+ import { readFileSync } from 'node:fs';
10
+
11
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
12
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
13
+
14
+ const browser = await chromium.launch({ headless: true });
15
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
16
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
17
+ await ctx.addInitScript(() => {
18
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
19
+ });
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 mk = (alias, status) => ({
25
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
26
+ network_id: nid, project_dir: null,
27
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
28
+ });
29
+ await route.fulfill({ response: r, json: { ...b, sessions: [
30
+ mk('alpha', 'working'),
31
+ mk('beta', 'idle'),
32
+ ] } });
33
+ });
34
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
35
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
36
+
37
+ const page = await ctx.newPage();
38
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
39
+ await page.waitForSelector('[data-topo-hub-halo-radius]', { timeout: 15000 });
40
+ await page.waitForTimeout(400);
41
+
42
+ const readHalo = () => page.evaluate(() => {
43
+ const c = document.querySelector('[data-topo-hub-halo-radius]');
44
+ return c ? {
45
+ r_data: parseFloat(c.getAttribute('data-topo-hub-halo-radius') || '0'),
46
+ hovered: c.getAttribute('data-topo-hub-halo-hovered'),
47
+ } : null;
48
+ });
49
+
50
+ const rest = await readHalo();
51
+
52
+ let hover = null;
53
+ const hubBox = await page.evaluate(() => {
54
+ const hub = document.querySelector('[data-topo-hub]');
55
+ if (!hub) return null;
56
+ const b = hub.getBoundingClientRect();
57
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
58
+ });
59
+ if (hubBox) {
60
+ await page.mouse.move(hubBox.x, hubBox.y);
61
+ await page.waitForTimeout(300);
62
+ hover = await readHalo();
63
+ await page.mouse.move(0, 0);
64
+ }
65
+
66
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
67
+ const sourceWired = /const haloR = isHaloHovered \? 22 : 20/.test(fileText);
68
+
69
+ await browser.close();
70
+
71
+ const results = {
72
+ rest_mounted: !!rest,
73
+ rest_r_20: rest?.r_data === 20,
74
+ rest_hovered_false: rest?.hovered === 'false',
75
+ hover_r_22: hover?.r_data === 22,
76
+ hover_hovered_true: hover?.hovered === 'true',
77
+ source_wired: sourceWired,
78
+ };
79
+ const ok = Object.values(results).every(Boolean);
80
+ console.log(`${ok ? '✅' : '❌'} hub halo hover r:`, JSON.stringify(results),
81
+ '\n rest:', rest, '\n hover:', hover);
82
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,102 @@
1
+ /* Round 449 verification: legend-row count active-state opacity
2
+ * 0.95 → 1.0 on (hoveredStatus===row.key || isPinned). Closes the 5%
3
+ * alpha gap on populated active rows.
4
+ *
5
+ * Contract:
6
+ * - rest: every populated legend-count reports opacity '0.65' (R204)
7
+ * - click 'working' status: that count opacity '1'
8
+ * - empty (0-count) rows stay at R204 dim (0.28/0.30) unaffected
9
+ * - siblings stay at rest 0.65
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
16
+
17
+ const browser = await chromium.launch({ headless: true });
18
+ const ctx = await browser.newContext({ viewport: { width: 1500, 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
+ await ctx.route('**/api/hub/status*', async (route) => {
24
+ const r = await route.fetch();
25
+ const b = await r.json();
26
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
27
+ const mk = (alias, status) => ({
28
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
29
+ network_id: nid, project_dir: null,
30
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
31
+ });
32
+ await route.fulfill({ response: r, json: { ...b, sessions: [
33
+ mk('alpha', 'working'),
34
+ mk('beta', 'working'),
35
+ mk('gamma', 'idle'),
36
+ ] } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
39
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
40
+
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+ await page.waitForSelector('[data-legend-count]', { timeout: 15000 });
44
+ await page.waitForTimeout(400);
45
+
46
+ const readAll = () => page.evaluate(() => {
47
+ const ts = [...document.querySelectorAll('[data-legend-count]')];
48
+ return ts.map(t => ({
49
+ key: t.getAttribute('data-legend-count'),
50
+ opacity: t.getAttribute('opacity'),
51
+ empty: t.getAttribute('data-legend-count-empty'),
52
+ }));
53
+ });
54
+
55
+ const rest = await readAll();
56
+
57
+ // Click 'working' status row to pin
58
+ let pinned = null;
59
+ const workingGroup = await page.$(`[data-legend-status="working"]`);
60
+ if (workingGroup) {
61
+ await workingGroup.click();
62
+ await page.waitForTimeout(250);
63
+ pinned = await readAll();
64
+ }
65
+
66
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
67
+ const sourceWired = /:\s*\(hoveredStatus === row\.key \|\| isPinned \? 1 : 0\.65\)/.test(fileText);
68
+
69
+ await browser.close();
70
+
71
+ // rest: populated rows (working=2, idle=1) should be 0.65; offline (count=0) should be 0.28/0.30
72
+ const workingRest = rest.find(r => r.key === 'working');
73
+ const idleRest = rest.find(r => r.key === 'idle');
74
+ const offlineRest = rest.find(r => r.key === 'offline');
75
+
76
+ const restWorking_065 = workingRest?.opacity === '0.65';
77
+ const restIdle_065 = idleRest?.opacity === '0.65';
78
+ const restOfflineEmpty = offlineRest?.empty === 'true' && (offlineRest?.opacity === '0.3' || offlineRest?.opacity === '0.28');
79
+
80
+ // pinned: working should be 1
81
+ const pinnedWorking = pinned?.find(r => r.key === 'working');
82
+ const pinnedIdle = pinned?.find(r => r.key === 'idle');
83
+ const pinnedOffline = pinned?.find(r => r.key === 'offline');
84
+
85
+ const pinWorking_1 = pinnedWorking?.opacity === '1';
86
+ const pinIdleRest = pinnedIdle?.opacity === '0.65';
87
+ const pinOfflineEmpty = pinnedOffline?.empty === 'true';
88
+
89
+ const results = {
90
+ rest_working_065: restWorking_065,
91
+ rest_idle_065: restIdle_065,
92
+ rest_offline_empty_dim: restOfflineEmpty,
93
+ pin_working_opacity_1: pinWorking_1,
94
+ pin_idle_stays_rest_065: pinIdleRest,
95
+ pin_offline_stays_empty: pinOfflineEmpty,
96
+ source_wired: sourceWired,
97
+ };
98
+ const ok = Object.values(results).every(Boolean);
99
+ console.log(`${ok ? '✅' : '❌'} legend count active opacity:`, JSON.stringify(results),
100
+ '\n rest:', JSON.stringify(rest),
101
+ '\n pinned:', JSON.stringify(pinned));
102
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,90 @@
1
+ /* Round 446 verification: legend-row count fontWeight lift on isPinned.
2
+ * 600 → 700 when the status tier is pinned. Mirror of R444/R445 at
3
+ * legend-row scope.
4
+ *
5
+ * Contract:
6
+ * - rest: every legend-count reports font-weight '600' + pinned='false'
7
+ * - click a legend-status group: that row's count flips to '700' +
8
+ * pinned='true'; siblings stay rest
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status) => ({
27
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'working'),
34
+ mk('gamma', 'idle'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForSelector('[data-legend-count]', { timeout: 15000 });
43
+ await page.waitForTimeout(400);
44
+
45
+ const readAll = () => page.evaluate(() => {
46
+ const ts = [...document.querySelectorAll('[data-legend-count]')];
47
+ return ts.map(t => ({
48
+ key: t.getAttribute('data-legend-count'),
49
+ fw: t.getAttribute('font-weight'),
50
+ pinned: t.getAttribute('data-legend-count-pinned'),
51
+ }));
52
+ });
53
+
54
+ const rest = await readAll();
55
+
56
+ // Click the 'working' status row to pin it
57
+ let pinned = null;
58
+ const workingGroup = await page.$(`[data-legend-status="working"]`);
59
+ if (workingGroup) {
60
+ await workingGroup.click();
61
+ await page.waitForTimeout(250);
62
+ pinned = await readAll();
63
+ }
64
+
65
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
66
+ const sourceWired = /fontWeight=\{isPinned \? '700' : '600'\}/.test(fileText);
67
+
68
+ await browser.close();
69
+
70
+ const restAll600 = rest.every(r => r.fw === '600');
71
+ const restNoPin = rest.every(r => r.pinned === 'false');
72
+ const pinnedEntry = pinned?.find(r => r.key === 'working');
73
+ const pinFw_700 = pinnedEntry?.fw === '700';
74
+ const pinFlag = pinnedEntry?.pinned === 'true';
75
+ const othersStayRest = pinned ? pinned.filter(r => r.key !== 'working').every(r => r.fw === '600' && r.pinned === 'false') : false;
76
+
77
+ const results = {
78
+ rest_three_rows: rest.length === 3,
79
+ rest_all_fw_600: restAll600,
80
+ rest_no_pin: restNoPin,
81
+ pinned_target_fw_700: pinFw_700,
82
+ pinned_target_flag: pinFlag,
83
+ pinned_others_stay: othersStayRest,
84
+ source_wired: sourceWired,
85
+ };
86
+ const ok = Object.values(results).every(Boolean);
87
+ console.log(`${ok ? '✅' : '❌'} legend count fw pin:`, JSON.stringify(results),
88
+ '\n rest:', JSON.stringify(rest),
89
+ '\n pinned target:', JSON.stringify(pinnedEntry));
90
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,96 @@
1
+ /* Round 450 verification: minimap viewport rect rest opacity 0.9 →
2
+ * 0.95. Closes half the alpha gap on the wayfinding indicator.
3
+ *
4
+ * Contract:
5
+ * - rest: viewport rect opacity '0.95'
6
+ * - hover the minimap container: viewport opacity '1' (R346 preserved)
7
+ * - strokeWidth invariants preserved (rest=1.5, hover=1.75)
8
+ */
9
+ import { chromium } from 'playwright';
10
+ import { readFileSync } from 'node:fs';
11
+
12
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
13
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
14
+
15
+ const browser = await chromium.launch({ headless: true });
16
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
17
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
18
+ await ctx.addInitScript(() => {
19
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
20
+ });
21
+ await ctx.route('**/api/hub/status*', async (route) => {
22
+ const r = await route.fetch();
23
+ const b = await r.json();
24
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
25
+ const mk = (alias, status) => ({
26
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
27
+ network_id: nid, project_dir: null,
28
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
29
+ });
30
+ // Need enough nodes for minimap to render — and a non-default view
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'idle'),
34
+ mk('gamma', 'working'),
35
+ mk('delta', 'idle'),
36
+ ] } });
37
+ });
38
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
39
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
40
+
41
+ const page = await ctx.newPage();
42
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
43
+
44
+ // Trigger a zoom to mount the minimap (R30 gate: minimap only renders on non-default view)
45
+ await page.waitForSelector('[data-topo-chrome-zoom-in]', { timeout: 15000 });
46
+ await page.click('[data-topo-chrome-zoom-in]');
47
+ await page.waitForTimeout(300);
48
+
49
+ await page.waitForSelector('[data-topo-minimap-viewport]', { timeout: 5000 });
50
+ await page.waitForTimeout(300);
51
+
52
+ const rest = await page.evaluate(() => {
53
+ const v = document.querySelector('[data-topo-minimap-viewport]');
54
+ return v ? {
55
+ opacity: v.getAttribute('opacity'),
56
+ sw: v.getAttribute('stroke-width'),
57
+ hover: v.getAttribute('data-topo-minimap-viewport-hover'),
58
+ } : null;
59
+ });
60
+
61
+ // Hover the minimap container
62
+ let hover = null;
63
+ const minimap = await page.$('[data-topo-minimap]');
64
+ if (minimap) {
65
+ await minimap.hover({ force: true });
66
+ await page.waitForTimeout(250);
67
+ hover = await page.evaluate(() => {
68
+ const v = document.querySelector('[data-topo-minimap-viewport]');
69
+ return v ? {
70
+ opacity: v.getAttribute('opacity'),
71
+ sw: v.getAttribute('stroke-width'),
72
+ hover: v.getAttribute('data-topo-minimap-viewport-hover'),
73
+ } : null;
74
+ });
75
+ }
76
+
77
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
78
+ const sourceWired = /opacity=\{hoveredMinimap \? '1' : '0\.95'\}/.test(fileText);
79
+
80
+ await browser.close();
81
+
82
+ const results = {
83
+ rest_mounted: !!rest,
84
+ rest_opacity_0_95: rest?.opacity === '0.95',
85
+ rest_sw_1_5: rest?.sw === '1.5',
86
+ rest_hover_false: rest?.hover === 'false',
87
+ hover_opacity_1: hover?.opacity === '1',
88
+ hover_sw_1_75: hover?.sw === '1.75',
89
+ hover_hover_true: hover?.hover === 'true',
90
+ source_wired: sourceWired,
91
+ };
92
+ const ok = Object.values(results).every(Boolean);
93
+ console.log(`${ok ? '✅' : '❌'} minimap viewport rest opacity:`, JSON.stringify(results),
94
+ '\n rest:', JSON.stringify(rest),
95
+ '\n hover:', JSON.stringify(hover));
96
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,104 @@
1
+ /* Round 440 verification: node halo opacity hover lift —
2
+ * online cyber 0.65 → 0.80 on hoveredAlias match. Pure paint axis;
3
+ * no geometry change.
4
+ *
5
+ * Contract:
6
+ * - rest: every halo reports data-node-halo-hovered='false' and
7
+ * resolved-opacity matching its tier (online cyber 0.65)
8
+ * - hover one node: that halo flips to hovered='true' + resolved-
9
+ * opacity '0.8'
10
+ * - siblings stay rest
11
+ * - R51 sentinel intact (overlap test still finds nodes)
12
+ * - source-file probe confirms IIFE + conditional wired
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
19
+
20
+ const browser = await chromium.launch({ headless: true });
21
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
22
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
23
+ await ctx.addInitScript(() => {
24
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
25
+ });
26
+ await ctx.route('**/api/hub/status*', async (route) => {
27
+ const r = await route.fetch();
28
+ const b = await r.json();
29
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
30
+ const mk = (alias, status) => ({
31
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
32
+ network_id: nid, project_dir: null,
33
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
34
+ });
35
+ await route.fulfill({ response: r, json: { ...b, sessions: [
36
+ mk('alpha', 'working'),
37
+ mk('beta', 'idle'),
38
+ mk('gamma', 'working'),
39
+ ] } });
40
+ });
41
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
42
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
43
+
44
+ const page = await ctx.newPage();
45
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
46
+ await page.waitForSelector('[data-node-halo-hovered]', { timeout: 15000 });
47
+ await page.waitForTimeout(400);
48
+
49
+ const readAll = () => page.evaluate(() => {
50
+ const cs = [...document.querySelectorAll('[data-node-halo-hovered]')];
51
+ return cs.map(c => {
52
+ const node = c.closest('[data-node]');
53
+ return {
54
+ alias: node?.getAttribute('data-node') || null,
55
+ hovered: c.getAttribute('data-node-halo-hovered'),
56
+ opacity: parseFloat(c.getAttribute('data-node-halo-resolved-opacity') || '0'),
57
+ };
58
+ });
59
+ });
60
+
61
+ const rest = await readAll();
62
+
63
+ let hover = null;
64
+ const box = await page.evaluate(() => {
65
+ const t = document.querySelector('[data-node-alias-text="alpha"]');
66
+ if (!t) return null;
67
+ const node = t.closest('[data-node]');
68
+ const target = node || t;
69
+ const b = target.getBoundingClientRect();
70
+ return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
71
+ });
72
+ if (box) {
73
+ await page.mouse.move(box.x, box.y);
74
+ await page.waitForTimeout(300);
75
+ hover = await readAll();
76
+ await page.mouse.move(0, 0);
77
+ }
78
+
79
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
80
+ const sourceIIFE = /const isHaloHovered = !reducedMotion && hoveredAlias === session\.alias/.test(fileText);
81
+ const sourceCyberOnline = /isLight \? \(isHaloHovered \? 1\s*:\s*0\.85\) : \(isHaloHovered \? 0\.80 : 0\.65\)/.test(fileText);
82
+
83
+ await browser.close();
84
+
85
+ const restAllRest = rest.length > 0 && rest.every(r => r.hovered === 'false' && r.opacity === 0.65);
86
+ const hoveredEntry = hover?.find(r => r.alias === 'alpha');
87
+ const hoverTarget08 = hoveredEntry?.opacity === 0.80;
88
+ const hoverTargetFlag = hoveredEntry?.hovered === 'true';
89
+ const othersStayRest = hover ? hover.filter(r => r.alias !== 'alpha').every(r => r.hovered === 'false' && r.opacity === 0.65) : false;
90
+
91
+ const results = {
92
+ rest_halo_count_ge_3: rest.length >= 3,
93
+ rest_all_rest_state: restAllRest,
94
+ hover_target_opacity_0_8: hoverTarget08,
95
+ hover_target_attr_true: hoverTargetFlag,
96
+ hover_others_stay_rest: othersStayRest,
97
+ source_IIFE_wired: sourceIIFE,
98
+ source_cyber_online_wired: sourceCyberOnline,
99
+ };
100
+ const ok = Object.values(results).every(Boolean);
101
+ console.log(`${ok ? '✅' : '❌'} node-halo hover opacity:`, JSON.stringify(results),
102
+ '\n rest:', JSON.stringify(rest),
103
+ '\n hover target:', JSON.stringify(hoveredEntry));
104
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,80 @@
1
+ /* Round 456 verification: node halo light-theme offline rest opacity
2
+ * 0.45 → 0.50. Stale-state legibility lift family extension.
3
+ *
4
+ * Contract:
5
+ * - light theme, fixture has offline nodes
6
+ * - every offline halo reports data-node-halo-resolved-opacity '0.5'
7
+ * (was 0.45)
8
+ * - online halos still at light 0.85 (R407 + R440 preserved)
9
+ * - source-file probe confirms the conditional
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 fresh = new Date(Date.now() - 60 * 1000).toISOString();
16
+
17
+ const browser = await chromium.launch({ headless: true });
18
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
19
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
20
+ // Use LIGHT theme for this test
21
+ await ctx.addInitScript(() => {
22
+ try { localStorage.setItem('anet-theme', 'light'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
23
+ });
24
+ await ctx.route('**/api/hub/status*', async (route) => {
25
+ const r = await route.fetch();
26
+ const b = await r.json();
27
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
28
+ const mk = (alias, status) => ({
29
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
30
+ network_id: nid, project_dir: null,
31
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
32
+ });
33
+ await route.fulfill({ response: r, json: { ...b, sessions: [
34
+ mk('alpha', 'working'), // online
35
+ mk('beta', 'offline'), // offline
36
+ mk('gamma', 'offline'), // offline
37
+ ] } });
38
+ });
39
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
40
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
41
+
42
+ const page = await ctx.newPage();
43
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
44
+ await page.waitForSelector('[data-node-halo-hovered]', { timeout: 15000 });
45
+ await page.waitForTimeout(400);
46
+
47
+ const halos = await page.evaluate(() => {
48
+ const cs = [...document.querySelectorAll('[data-node-halo-hovered]')];
49
+ return cs.map(c => {
50
+ const node = c.closest('[data-node]');
51
+ const sub = node?.querySelector('[data-node-sub-text]')?.textContent || '';
52
+ return {
53
+ alias: node?.getAttribute('data-node') || null,
54
+ status: sub.trim(),
55
+ opacity: parseFloat(c.getAttribute('data-node-halo-resolved-opacity') || '0'),
56
+ offline_attr_alpha: c.getAttribute('data-node-halo-offline-opacity'),
57
+ };
58
+ });
59
+ });
60
+
61
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
62
+ const sourceWired = /isLight \? \(isHaloHovered \? 0\.60 : 0\.50\) : \(isHaloHovered \? 0\.45 : 0\.30\)/.test(fileText);
63
+
64
+ await browser.close();
65
+
66
+ const onlineHalo = halos.find(h => h.status.includes('working'));
67
+ const offlineHalos = halos.filter(h => h.status.includes('offline'));
68
+
69
+ const results = {
70
+ any_mounted: halos.length > 0,
71
+ online_opacity_0_85: onlineHalo?.opacity === 0.85,
72
+ offline_count_ge_2: offlineHalos.length >= 2,
73
+ offline_all_0_5: offlineHalos.every(h => h.opacity === 0.5),
74
+ offline_attr_0_45_preserved: offlineHalos.every(h => h.offline_attr_alpha === '0.45'), /* attr exposes the LEGACY R407 light value 0.45; the resolved-opacity is the live 0.50; this preserves R407 test compat */
75
+ source_wired: sourceWired,
76
+ };
77
+ const ok = Object.values(results).every(Boolean);
78
+ console.log(`${ok ? '✅' : '❌'} node halo light offline 0.5:`, JSON.stringify(results),
79
+ '\n halos:', JSON.stringify(halos));
80
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,75 @@
1
+ /* Round 448 verification: node sub-text fontWeight 400 → 500.
2
+ * Static legibility lift for small mono text at fontSize=9.
3
+ * Sibling to R363/R364 small-text fw lifts.
4
+ *
5
+ * Contract:
6
+ * - every node sub-text reports font-weight '500'
7
+ * - geometry invariants preserved (R428 hover ls + R211 fill
8
+ * transition still intact)
9
+ */
10
+ import { chromium } from 'playwright';
11
+ import { readFileSync } from 'node:fs';
12
+
13
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
14
+ const fresh = new Date(Date.now() - 60 * 1000).toISOString();
15
+
16
+ const browser = await chromium.launch({ headless: true });
17
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
18
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
19
+ await ctx.addInitScript(() => {
20
+ try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
21
+ });
22
+ await ctx.route('**/api/hub/status*', async (route) => {
23
+ const r = await route.fetch();
24
+ const b = await r.json();
25
+ const nid = (b.sessions || [])[0]?.network_id || 'default';
26
+ const mk = (alias, status) => ({
27
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
28
+ network_id: nid, project_dir: null,
29
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
30
+ });
31
+ await route.fulfill({ response: r, json: { ...b, sessions: [
32
+ mk('alpha', 'working'),
33
+ mk('beta', 'idle'),
34
+ mk('gamma', 'working'),
35
+ ] } });
36
+ });
37
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
38
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
39
+
40
+ const page = await ctx.newPage();
41
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
42
+ await page.waitForSelector('[data-node-sub-text]', { timeout: 15000 });
43
+ await page.waitForTimeout(400);
44
+
45
+ const subs = await page.evaluate(() => {
46
+ const ts = [...document.querySelectorAll('[data-node-sub-text]')];
47
+ return ts.map(t => ({
48
+ alias: t.getAttribute('data-node-sub-text'),
49
+ fw: t.getAttribute('font-weight'),
50
+ fw_data: t.getAttribute('data-node-sub-text-font-weight'),
51
+ text: t.textContent,
52
+ }));
53
+ });
54
+
55
+ const fileText = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
56
+ const sourceWired = /fontWeight="500"\s*\n\s*data-node-sub-text=\{session\.alias\}/.test(fileText);
57
+
58
+ await browser.close();
59
+
60
+ const restAllFw500 = subs.every(s => s.fw === '500');
61
+ const restAllAttr500 = subs.every(s => s.fw_data === '500');
62
+ const restCountGe3 = subs.length >= 3;
63
+ const restAllHaveText = subs.every(s => s.text && s.text.length > 0);
64
+
65
+ const results = {
66
+ sub_count_ge_3: restCountGe3,
67
+ all_fw_500_attr: restAllFw500,
68
+ all_fw_data_attr_500: restAllAttr500,
69
+ all_have_text: restAllHaveText,
70
+ source_wired: sourceWired,
71
+ };
72
+ const ok = Object.values(results).every(Boolean);
73
+ console.log(`${ok ? '✅' : '❌'} node sub-text fw 500:`, JSON.stringify(results),
74
+ '\n subs:', JSON.stringify(subs));
75
+ process.exit(ok ? 0 : 1);