@sleep2agi/agent-network-dashboard 0.5.3-preview.13 → 0.5.3-preview.15

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 (158) 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/server/app/_global-error.html +1 -1
  6. package/.next/server/app/_global-error.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +2 -2
  14. package/.next/server/app/_not-found.rsc +11 -11
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
  16. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  17. package/.next/server/app/_not-found.segments/_index.segment.rsc +6 -6
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  22. package/.next/server/app/admin.html +2 -2
  23. package/.next/server/app/admin.rsc +13 -13
  24. package/.next/server/app/admin.segments/_full.segment.rsc +13 -13
  25. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  26. package/.next/server/app/admin.segments/_index.segment.rsc +6 -6
  27. package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
  28. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  29. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  30. package/.next/server/app/index.html +2 -2
  31. package/.next/server/app/index.rsc +13 -13
  32. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  33. package/.next/server/app/index.segments/_full.segment.rsc +13 -13
  34. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  35. package/.next/server/app/index.segments/_index.segment.rsc +6 -6
  36. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  37. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  38. package/.next/server/app/login.html +2 -2
  39. package/.next/server/app/login.rsc +13 -13
  40. package/.next/server/app/login.segments/_full.segment.rsc +13 -13
  41. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  42. package/.next/server/app/login.segments/_index.segment.rsc +6 -6
  43. package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
  44. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  45. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  46. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/logs.html +2 -2
  48. package/.next/server/app/logs.rsc +13 -13
  49. package/.next/server/app/logs.segments/_full.segment.rsc +13 -13
  50. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  51. package/.next/server/app/logs.segments/_index.segment.rsc +6 -6
  52. package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
  53. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  54. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  55. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/messages.html +2 -2
  57. package/.next/server/app/messages.rsc +13 -13
  58. package/.next/server/app/messages.segments/_full.segment.rsc +13 -13
  59. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  60. package/.next/server/app/messages.segments/_index.segment.rsc +6 -6
  61. package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
  62. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  63. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  64. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app/node.html +2 -2
  66. package/.next/server/app/node.rsc +13 -13
  67. package/.next/server/app/node.segments/_full.segment.rsc +13 -13
  68. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  69. package/.next/server/app/node.segments/_index.segment.rsc +6 -6
  70. package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
  71. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  72. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  73. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  74. package/.next/server/app/nodes.html +2 -2
  75. package/.next/server/app/nodes.rsc +13 -13
  76. package/.next/server/app/nodes.segments/_full.segment.rsc +13 -13
  77. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  78. package/.next/server/app/nodes.segments/_index.segment.rsc +6 -6
  79. package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
  80. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  81. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  82. package/.next/server/app/page_client-reference-manifest.js +1 -1
  83. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  84. package/.next/server/app/server-logs.html +2 -2
  85. package/.next/server/app/server-logs.rsc +13 -13
  86. package/.next/server/app/server-logs.segments/_full.segment.rsc +13 -13
  87. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  88. package/.next/server/app/server-logs.segments/_index.segment.rsc +6 -6
  89. package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
  90. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  91. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  92. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  93. package/.next/server/app/settings/networks.html +2 -2
  94. package/.next/server/app/settings/networks.rsc +13 -13
  95. package/.next/server/app/settings/networks.segments/_full.segment.rsc +13 -13
  96. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  97. package/.next/server/app/settings/networks.segments/_index.segment.rsc +6 -6
  98. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
  99. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  100. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  101. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  102. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  103. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  104. package/.next/server/app/settings/tokens.html +2 -2
  105. package/.next/server/app/settings/tokens.rsc +13 -13
  106. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +13 -13
  107. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  108. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +6 -6
  109. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
  110. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  111. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  112. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  113. package/.next/server/app/settings.html +2 -2
  114. package/.next/server/app/settings.rsc +13 -13
  115. package/.next/server/app/settings.segments/_full.segment.rsc +13 -13
  116. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  117. package/.next/server/app/settings.segments/_index.segment.rsc +6 -6
  118. package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  119. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  120. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  121. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  123. package/.next/server/app/tasks.html +2 -2
  124. package/.next/server/app/tasks.rsc +13 -13
  125. package/.next/server/app/tasks.segments/_full.segment.rsc +13 -13
  126. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  127. package/.next/server/app/tasks.segments/_index.segment.rsc +6 -6
  128. package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
  129. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  130. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  131. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +1 -1
  132. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +1 -1
  133. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +1 -1
  134. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  135. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  136. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  137. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  138. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  139. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js +1 -1
  140. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0mvyi-4._.js.map +1 -1
  141. package/.next/server/middleware-build-manifest.js +3 -3
  142. package/.next/server/pages/404.html +2 -2
  143. package/.next/server/pages/500.html +1 -1
  144. package/.next/static/chunks/{0zwxl-vr5q45i.js → 056n7oz45zp01.js} +1 -1
  145. package/.next/static/chunks/{0-eqn.ga3bcnl.js → 0bud6.ic~l84v.js} +1 -1
  146. package/.next/static/chunks/{11iqwh145jvo5.js → 0g21xro04lbjk.js} +1 -1
  147. package/.next/static/chunks/{0tl3h11hxa7oe.js → 0qf8ynb6zk1~~.js} +1 -1
  148. package/.next/static/chunks/{03a4--7ncekmk.js → 0v4-5tng.uh.7.js} +2 -2
  149. package/.next/trace +2 -2
  150. package/.next/trace-build +1 -1
  151. package/app/components/ServersDrawer.tsx +16 -3
  152. package/app/components/TopoGraph.tsx +30 -0
  153. package/package.json +1 -1
  154. package/scripts/p157-servers-copy-test.mjs +95 -0
  155. package/scripts/topo-avatar-brightness-test.mjs +116 -0
  156. /package/.next/static/{U6pY8Ja-T2ME4lRhun26w → gEFJytjHWAA9KfebCcNdi}/_buildManifest.js +0 -0
  157. /package/.next/static/{U6pY8Ja-T2ME4lRhun26w → gEFJytjHWAA9KfebCcNdi}/_clientMiddlewareManifest.js +0 -0
  158. /package/.next/static/{U6pY8Ja-T2ME4lRhun26w → gEFJytjHWAA9KfebCcNdi}/_ssgManifest.js +0 -0
@@ -0,0 +1,95 @@
1
+ /* #157 fix verification (v0.10.8 lean ship — Fix #1 only per 通信龙 5573).
2
+ *
3
+ * Pre-fix the Servers panel showed:
4
+ * "agent rollup pending hub ≥ 0.8.2-preview"
5
+ * "disk metric pending hub ≥ 0.8.2-preview"
6
+ * commhub-server@0.8.2 is LIVE on prod but still doesn't ship `agents[]`
7
+ * or `disk_*` — the version-pinned text was misleading.
8
+ *
9
+ * Post-fix:
10
+ * "agent rollup not reported by hub"
11
+ * "disk metric not reported by hub"
12
+ * + data-server-agents-missing + data-server-disk-missing test attrs.
13
+ *
14
+ * Test:
15
+ * 1. Open dashboard, expand Servers drawer (localStorage flag)
16
+ * 2. Expand first server card (localStorage flag)
17
+ * 3. Assert visible copy lacks "0.8.2-preview"
18
+ * 4. Assert test-surface attrs present
19
+ * 5. Source-side regex confirms new copy + attrs wired
20
+ */
21
+ import { chromium } from 'playwright';
22
+ import { readFileSync } from 'node:fs';
23
+
24
+ const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
25
+
26
+ const browser = await chromium.launch({ headless: true });
27
+ const ctx = await browser.newContext({ viewport: { width: 1500, height: 1200 } });
28
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
29
+ await ctx.addInitScript(() => {
30
+ try {
31
+ localStorage.setItem('anet-theme', 'cyber');
32
+ sessionStorage.setItem('anet_v3_auth', '1');
33
+ // Force servers drawer open
34
+ localStorage.setItem('anet-servers-drawer', '1');
35
+ } catch {}
36
+ });
37
+ const page = await ctx.newPage();
38
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
39
+ await page.waitForTimeout(3500); // wait for SWR fetch + servers to render
40
+
41
+ // Expand first server card by clicking it
42
+ const firstCard = await page.$('[data-server-host], [data-server-card], button:has-text("iZ")');
43
+ // Simpler: just probe the drawer body text after expansion attempt
44
+ await page.evaluate(() => {
45
+ const buttons = Array.from(document.querySelectorAll('button'));
46
+ // Find server card expand toggles by their host text
47
+ const candidates = buttons.filter(b => /iZ|elaine/.test(b.textContent || ''));
48
+ if (candidates[0]) candidates[0].click();
49
+ });
50
+ await page.waitForTimeout(800);
51
+
52
+ const probe = await page.evaluate(() => {
53
+ // Probe drawer body for the placeholder text
54
+ const drawer = document.querySelector('[data-servers-body]') || document.body;
55
+ const text = drawer.textContent || '';
56
+ const agentsMissing = document.querySelectorAll('[data-server-agents-missing="true"]');
57
+ const diskMissing = document.querySelectorAll('[data-server-disk-missing="true"]');
58
+ return {
59
+ body_text_excerpt: text.slice(0, 1500),
60
+ has_stale_copy: /0\.8\.2-preview/.test(text),
61
+ has_new_agents_copy: /agent rollup not reported by hub/.test(text),
62
+ has_new_disk_copy: /disk metric not reported by hub/.test(text),
63
+ agents_missing_count: agentsMissing.length,
64
+ disk_missing_count: diskMissing.length,
65
+ };
66
+ });
67
+
68
+ await browser.close();
69
+
70
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/ServersDrawer.tsx', 'utf8');
71
+ // The OLD strings should be gone from rendered text. The comment block keeps the
72
+ // historical "0.8.2-preview" reference for audit — that's OK because comments
73
+ // don't render. So source check: look for the NEW visible strings AND the test
74
+ // attrs, not for the absence of "0.8.2-preview".
75
+ const sourceNewAgentsCopy = /agent rollup not reported by hub/.test(src);
76
+ const sourceNewDiskCopy = /disk metric not reported by hub/.test(src);
77
+ const sourceAgentsAttr = /data-server-agents-missing="true"/.test(src);
78
+ const sourceDiskAttr = /data-server-disk-missing="true"/.test(src);
79
+
80
+ const results = {
81
+ dom_no_stale_copy: !probe.has_stale_copy,
82
+ dom_new_agents_copy: probe.has_new_agents_copy,
83
+ dom_new_disk_copy: probe.has_new_disk_copy,
84
+ agents_attr_present: probe.agents_missing_count > 0,
85
+ disk_attr_present: probe.disk_missing_count > 0,
86
+ source_new_agents: sourceNewAgentsCopy,
87
+ source_new_disk: sourceNewDiskCopy,
88
+ source_agents_attr: sourceAgentsAttr,
89
+ source_disk_attr: sourceDiskAttr,
90
+ };
91
+ const ok = Object.values(results).every(Boolean);
92
+ console.log(`${ok ? '✅' : '❌'} #157 servers copy fix:`, JSON.stringify(results),
93
+ '\n agents_missing_count:', probe.agents_missing_count, ' disk_missing_count:', probe.disk_missing_count,
94
+ '\n body_excerpt:', probe.body_text_excerpt.replace(/\s+/g, ' ').slice(0, 400));
95
+ process.exit(ok ? 0 : 1);
@@ -0,0 +1,116 @@
1
+ /* Round 501 verification: vendor avatar <image> inside node circles
2
+ * gains a hover-gated `filter: brightness(1.15)`. Closes the per-node
3
+ * hover-affordance arc — every per-node element type now has a hover
4
+ * treatment.
5
+ *
6
+ * Test fixture: claude-code-cli runtime → vendor.logo path renders the
7
+ * <image> branch (vs monogram fallback). Verifies:
8
+ * 1. rest: data-node-avatar-hovered='false', no brightness filter
9
+ * 2. hover (synthetic pointerenter): attr='true', computed filter
10
+ * contains brightness(1.15)
11
+ * 3. a11y: attr='false' even after hover (component gate)
12
+ * 4. source-side regex confirms wiring
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
+ async function probe({ reducedMotion }) {
21
+ const browser = await chromium.launch({ headless: true });
22
+ const ctx = await browser.newContext({
23
+ viewport: { width: 1500, height: 1200 },
24
+ reducedMotion: reducedMotion ? 'reduce' : 'no-preference',
25
+ });
26
+ await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
27
+ await ctx.addInitScript(() => {
28
+ try {
29
+ localStorage.setItem('anet-theme', 'cyber');
30
+ localStorage.setItem('anet-topo-layout', 'ring');
31
+ sessionStorage.setItem('anet_v3_auth', '1');
32
+ } catch {}
33
+ });
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 mk = (alias, status) => ({
39
+ // claude-code-cli renders the avatar <image> branch
40
+ alias, status, model: 'claude-opus-4', runtime: 'claude-code-cli',
41
+ network_id: nid, project_dir: null,
42
+ created_at: fresh, updated_at: fresh, last_seen_at: fresh,
43
+ });
44
+ await route.fulfill({ response: r, json: { ...b, sessions: [
45
+ mk('alpha·a1', 'working'),
46
+ mk('alpha·a2', 'idle'),
47
+ ] } });
48
+ });
49
+ await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [] } }));
50
+ await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
51
+ const page = await ctx.newPage();
52
+ await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' });
53
+ await page.waitForSelector('g[data-node]', { timeout: 15000 });
54
+ await page.waitForTimeout(1500);
55
+
56
+ const rest = await page.evaluate(() => {
57
+ const img = document.querySelector('[data-node-avatar]');
58
+ if (!img) return { found: false };
59
+ return {
60
+ found: true,
61
+ hovered_attr: img.getAttribute('data-node-avatar-hovered'),
62
+ computed_filter: window.getComputedStyle(img).filter,
63
+ };
64
+ });
65
+
66
+ // Synthetic hover via R488 banked recipe
67
+ await page.evaluate(() => {
68
+ const g = document.querySelector('g[data-node]');
69
+ if (!g) return;
70
+ const target = g.querySelector('circle, image, rect') || g;
71
+ ['pointerenter', 'pointerover', 'mouseenter', 'mouseover'].forEach((t) => {
72
+ target.dispatchEvent(new Event(t, { bubbles: true, cancelable: true }));
73
+ });
74
+ });
75
+ await page.waitForTimeout(400);
76
+
77
+ const hover = await page.evaluate(() => {
78
+ const img = document.querySelector('[data-node-avatar]');
79
+ if (!img) return { found: false };
80
+ return {
81
+ found: true,
82
+ hovered_attr: img.getAttribute('data-node-avatar-hovered'),
83
+ computed_filter: window.getComputedStyle(img).filter,
84
+ };
85
+ });
86
+
87
+ await browser.close();
88
+ return { rest, hover };
89
+ }
90
+
91
+ const motion = await probe({ reducedMotion: false });
92
+ const a11y = await probe({ reducedMotion: true });
93
+
94
+ const src = readFileSync('/home/vansin/agent-network-dashboard/app/components/TopoGraph.tsx', 'utf8');
95
+ const sourceGate = /const isAvatarHovered = !reducedMotion && hoveredAlias === session\.alias;/.test(src);
96
+ const sourceFilter = /filter: isAvatarHovered \? 'brightness\(1\.15\)' : undefined/.test(src);
97
+ const sourceAttr = /data-node-avatar-hovered=\{isAvatarHovered \? 'true' : 'false'\}/.test(src);
98
+
99
+ const results = {
100
+ motion_rest_found: motion.rest.found,
101
+ motion_rest_attr_false: motion.rest.found && motion.rest.hovered_attr === 'false',
102
+ motion_rest_filter_none: motion.rest.found && motion.rest.computed_filter === 'none',
103
+ motion_hover_attr_true: motion.hover.found && motion.hover.hovered_attr === 'true',
104
+ motion_hover_filter_bright: motion.hover.found && /brightness\(1\.15\)/.test(motion.hover.computed_filter || ''),
105
+ a11y_rest_attr_false: a11y.rest.found && a11y.rest.hovered_attr === 'false',
106
+ a11y_hover_attr_false: a11y.hover.found && a11y.hover.hovered_attr === 'false',
107
+ a11y_hover_filter_none: a11y.hover.found && a11y.hover.computed_filter === 'none',
108
+ source_gate_wired: sourceGate,
109
+ source_filter_wired: sourceFilter,
110
+ source_attr_wired: sourceAttr,
111
+ };
112
+ const ok = Object.values(results).every(Boolean);
113
+ console.log(`${ok ? '✅' : '❌'} R501 avatar brightness:`, JSON.stringify(results),
114
+ '\n motion:', JSON.stringify(motion),
115
+ '\n a11y :', JSON.stringify(a11y));
116
+ process.exit(ok ? 0 : 1);