@sleep2agi/agent-network-dashboard 0.5.1-preview.41 → 0.5.1-preview.43
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/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +4 -4
- package/.next/fallback-build-manifest.json +3 -3
- 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.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.html +1 -1
- package/.next/server/app/admin.rsc +1 -1
- package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +1 -1
- package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/logs.html +1 -1
- package/.next/server/app/logs.rsc +1 -1
- package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/messages.html +1 -1
- package/.next/server/app/messages.rsc +1 -1
- package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- package/.next/server/app/node.html +1 -1
- package/.next/server/app/node.rsc +1 -1
- package/.next/server/app/node.segments/_full.segment.rsc +1 -1
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +1 -1
- package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- package/.next/server/app/nodes.html +1 -1
- package/.next/server/app/nodes.rsc +1 -1
- package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +1 -1
- package/.next/server/app/server-logs.rsc +1 -1
- package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- package/.next/server/app/settings/networks.html +1 -1
- package/.next/server/app/settings/networks.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +1 -1
- package/.next/server/app/settings/tokens.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +2 -2
- package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/tasks.html +1 -1
- package/.next/server/app/tasks.rsc +1 -1
- package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +2 -2
- 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_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/{057c8hh9cri2y.js → 059vp-f16342f.js} +1 -1
- package/.next/static/chunks/{0ulf.39xk5_1v.js → 0m0cdeq4o.g_r.js} +1 -1
- package/.next/static/chunks/0th_n1zwgo9w_.js +4 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +35 -0
- package/package.json +1 -1
- package/scripts/topo-edge-visible-linecap-test.mjs +89 -0
- package/scripts/topo-hub-spoke-linecap-test.mjs +80 -0
- package/.next/static/chunks/03s0-hiyr832o.js +0 -4
- /package/.next/static/{4Mzmc_vft-1RJtomdAHL- → wXAwJtE5Oxerjy_QZ0cC8}/_buildManifest.js +0 -0
- /package/.next/static/{4Mzmc_vft-1RJtomdAHL- → wXAwJtE5Oxerjy_QZ0cC8}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{4Mzmc_vft-1RJtomdAHL- → wXAwJtE5Oxerjy_QZ0cC8}/_ssgManifest.js +0 -0
|
@@ -3869,6 +3869,20 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3869
3869
|
active surfaces the activity state for test probes
|
|
3870
3870
|
(active spokes don't carry the bucket/dur attrs so
|
|
3871
3871
|
they need their own data anchor). */
|
|
3872
|
+
// Round 382 / Loop: hub-spoke path picks up
|
|
3873
|
+
// strokeLinecap='round'. Sibling polish to R378 flow-
|
|
3874
|
+
// rail dashes + R380 group box dashes — three dashed-
|
|
3875
|
+
// stroke surfaces now share 'round' linecap:
|
|
3876
|
+
// R378 flow-rail '2 12' -> soft 3-px pills
|
|
3877
|
+
// R380 group box '6 6' -> soft 7.5-px pills
|
|
3878
|
+
// R382 hub spoke '6 14' -> soft 7-px pills (this round)
|
|
3879
|
+
// For idle spokes (dashed at sw=1), each 6-px dash gains
|
|
3880
|
+
// 0.5-px round caps and reads as a soft pill instead of
|
|
3881
|
+
// a sharp 6 x 1 rectangle. Active spokes (solid, no
|
|
3882
|
+
// dasharray) have caps mostly hidden by the hub center +
|
|
3883
|
+
// node radius. Geometry-safe; paint-only. R51 sentinel
|
|
3884
|
+
// strokeWidth 1.5/3 untouched (idle=1, active=2). data-
|
|
3885
|
+
// topo-hub-spoke-linecap attr exposes the value for tests.
|
|
3872
3886
|
return (
|
|
3873
3887
|
<path
|
|
3874
3888
|
key={`hub-${session.alias}`}
|
|
@@ -3877,11 +3891,13 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3877
3891
|
stroke={isActiveSpoke ? pal.spokeStroke.active : pal.spokeStroke.idle}
|
|
3878
3892
|
strokeWidth={isActiveSpoke ? 2 : 1}
|
|
3879
3893
|
strokeDasharray={isActiveSpoke ? 'none' : '6 14'}
|
|
3894
|
+
strokeLinecap="round"
|
|
3880
3895
|
opacity={isActiveSpoke ? 0.7 : 0.45}
|
|
3881
3896
|
className={isActiveSpoke ? undefined : 'anet-topo-spoke-flow'}
|
|
3882
3897
|
data-topo-spoke-bucket={isActiveSpoke ? undefined : busy}
|
|
3883
3898
|
data-topo-spoke-dur={isActiveSpoke ? undefined : spokeDur}
|
|
3884
3899
|
data-topo-hub-spoke-active={isActiveSpoke ? 'true' : 'false'}
|
|
3900
|
+
data-topo-hub-spoke-linecap="round"
|
|
3885
3901
|
style={{
|
|
3886
3902
|
transition: 'stroke 250ms ease-out, stroke-width 250ms ease-out, opacity 250ms ease-out',
|
|
3887
3903
|
...(isActiveSpoke ? {} : {
|
|
@@ -4515,15 +4531,34 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
4515
4531
|
online chips to splice in additional properties
|
|
4516
4532
|
beside Tailwind's). data-edge-flow-rail attr
|
|
4517
4533
|
surfaces the path for test introspection. */}
|
|
4534
|
+
{/* Round 381 / Loop: edge visible flow path picks up
|
|
4535
|
+
strokeLinecap='round'. Sibling polish to R378
|
|
4536
|
+
flow-rail dashed linecap — both flow-element paths
|
|
4537
|
+
(visible primary + dashed secondary rail) now share
|
|
4538
|
+
'round' linecap vocabulary. The visible path runs
|
|
4539
|
+
source-node → dest-node as one continuous line, so
|
|
4540
|
+
the dest-end is covered by the markerEnd arrow and
|
|
4541
|
+
the source-end usually sits inside the source-node
|
|
4542
|
+
circle. At certain alignments (post-zoom, post-
|
|
4543
|
+
layout-switch transitions), the source-end may peek
|
|
4544
|
+
out by a fraction of a px past the node edge —
|
|
4545
|
+
round caps render that overshoot as a smooth half-
|
|
4546
|
+
disc instead of a sharp rectangle. Pure paint
|
|
4547
|
+
refinement, geometry-safe (bbox of the stroke
|
|
4548
|
+
unchanged at the join with the arrow marker).
|
|
4549
|
+
data-edge-visible-linecap attr exposes the value
|
|
4550
|
+
for tests. */}
|
|
4518
4551
|
<path
|
|
4519
4552
|
d={path}
|
|
4520
4553
|
fill="none"
|
|
4521
4554
|
stroke={pal.flowEdge}
|
|
4522
4555
|
strokeWidth={renderWidth}
|
|
4556
|
+
strokeLinecap="round"
|
|
4523
4557
|
opacity={Math.min(1, (isLight ? 0.22 : 0.28) * fresh * edgeOpacityMul)}
|
|
4524
4558
|
filter={isLight ? undefined : 'url(#topo-glow)'}
|
|
4525
4559
|
markerEnd={`url(#${arrowId})`}
|
|
4526
4560
|
data-edge-visible={link.key}
|
|
4561
|
+
data-edge-visible-linecap="round"
|
|
4527
4562
|
style={{
|
|
4528
4563
|
pointerEvents: 'none',
|
|
4529
4564
|
transition: 'opacity 300ms ease-out, stroke-width 300ms ease-out, stroke 300ms ease-out',
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/* Round 381 verification: edge visible flow path strokeLinecap='round'.
|
|
2
|
+
* Sibling SVG stroke-softening polish to R378 flow-rail linecap — both
|
|
3
|
+
* flow-element paths now share 'round' linecap. R380 group box closed
|
|
4
|
+
* dash-cap + corner softening; R381 closes the flow-path family at the
|
|
5
|
+
* visible primary path.
|
|
6
|
+
*
|
|
7
|
+
* Stroke-softening family snapshot post-R381:
|
|
8
|
+
* R288 chrome icons strokeLinecap='round'
|
|
9
|
+
* R378 flow-rail dashes strokeLinecap='round'
|
|
10
|
+
* R380 group box dashes strokeLinecap='round'
|
|
11
|
+
* R381 flow visible path strokeLinecap='round' (this round)
|
|
12
|
+
* R379 viewport rect strokeLinejoin='round'
|
|
13
|
+
* R380 group box corners strokeLinejoin='round'
|
|
14
|
+
*
|
|
15
|
+
* Contract:
|
|
16
|
+
* - [data-edge-visible] element stroke-linecap attr === 'round'.
|
|
17
|
+
* - data-edge-visible-linecap === 'round'.
|
|
18
|
+
* - Pre-R381 invariants:
|
|
19
|
+
* * fill='none', stroke=pal.flowEdge
|
|
20
|
+
* * markerEnd referencing arrow id
|
|
21
|
+
* * opacity-based freshness preserved (any value in 0..1)
|
|
22
|
+
* * transition list contains opacity + stroke-width + stroke
|
|
23
|
+
*/
|
|
24
|
+
import { chromium } from 'playwright';
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
|
|
27
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
28
|
+
const browser = await chromium.launch({ headless: true });
|
|
29
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
30
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
31
|
+
await ctx.addInitScript(() => {
|
|
32
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
33
|
+
});
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
36
|
+
const r = await route.fetch();
|
|
37
|
+
const b = await r.json();
|
|
38
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
39
|
+
const mk = (alias) => ({
|
|
40
|
+
alias, status: 'working', 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: [ mk('a'), mk('b') ] } });
|
|
45
|
+
});
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
await ctx.route('**/api/hub/messages*', (r) => r.fulfill({ json: { messages: [
|
|
48
|
+
{ id: 'm1', from_alias: 'a', to_alias: 'b', content: 'ping',
|
|
49
|
+
network_id: 'default', created_at: new Date(now - 5000).toISOString() },
|
|
50
|
+
] } }));
|
|
51
|
+
await ctx.route('**/api/hub/tasks*', (r) => r.fulfill({ json: { tasks: [] } }));
|
|
52
|
+
|
|
53
|
+
const page = await ctx.newPage();
|
|
54
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
55
|
+
await page.waitForSelector('[data-edge-visible]', { timeout: 15000 });
|
|
56
|
+
await page.waitForTimeout(300);
|
|
57
|
+
|
|
58
|
+
const probe = await page.evaluate(() => {
|
|
59
|
+
const p = document.querySelector('[data-edge-visible]');
|
|
60
|
+
const cs = p ? getComputedStyle(p) : null;
|
|
61
|
+
return {
|
|
62
|
+
linecapAttr: p?.getAttribute('stroke-linecap') ?? null,
|
|
63
|
+
linecapData: p?.getAttribute('data-edge-visible-linecap') ?? null,
|
|
64
|
+
fillAttr: p?.getAttribute('fill') ?? null,
|
|
65
|
+
markerEndAttr: p?.getAttribute('marker-end') ?? null,
|
|
66
|
+
transition: cs?.transition ?? null,
|
|
67
|
+
linkKey: p?.getAttribute('data-edge-visible') ?? null,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await browser.close();
|
|
72
|
+
|
|
73
|
+
const hasTrans = (s, prop) =>
|
|
74
|
+
new RegExp(`${prop}\\s+\\d*\\.?\\d*s|${prop}\\s+\\d+ms`, 'i').test(s || '');
|
|
75
|
+
|
|
76
|
+
const results = {
|
|
77
|
+
linecap_round: probe.linecapAttr === 'round',
|
|
78
|
+
data_linecap_round: probe.linecapData === 'round',
|
|
79
|
+
fill_none: probe.fillAttr === 'none',
|
|
80
|
+
marker_end_arrow: /^url\(#topo-arrow-/.test(probe.markerEndAttr || ''),
|
|
81
|
+
trans_has_opacity: hasTrans(probe.transition, 'opacity'),
|
|
82
|
+
trans_has_stroke_w: hasTrans(probe.transition, 'stroke-width'),
|
|
83
|
+
trans_has_stroke: hasTrans(probe.transition, 'stroke'),
|
|
84
|
+
link_key_present: (probe.linkKey || '').length > 0,
|
|
85
|
+
};
|
|
86
|
+
const ok = Object.values(results).every(Boolean);
|
|
87
|
+
console.log(`${ok ? '✅' : '❌'} edge visible path strokeLinecap='round':`, JSON.stringify(results),
|
|
88
|
+
'\n probe:', probe);
|
|
89
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* Round 382 verification: hub-spoke path strokeLinecap='round'.
|
|
2
|
+
* Sibling SVG stroke-softening polish to R378 flow-rail dashes +
|
|
3
|
+
* R380 group box dashes — three dashed-stroke surfaces now share
|
|
4
|
+
* 'round' linecap.
|
|
5
|
+
*
|
|
6
|
+
* Dashed-stroke linecap family snapshot post-R382:
|
|
7
|
+
* R378 flow-rail '2 12' -> soft 3-px pills
|
|
8
|
+
* R380 group box '6 6' -> soft 7.5-px pills
|
|
9
|
+
* R382 hub spoke '6 14' -> soft 7-px pills (this round)
|
|
10
|
+
*
|
|
11
|
+
* Contract:
|
|
12
|
+
* - Every [data-topo-hub-spoke-active] path has stroke-linecap='round'.
|
|
13
|
+
* - data-topo-hub-spoke-linecap === 'round'.
|
|
14
|
+
* - Idle spokes still have strokeDasharray='6 14' (R382 doesn't touch
|
|
15
|
+
* the dasharray); active spokes still have 'none'.
|
|
16
|
+
* - Pre-R382 invariants preserved on each spoke: strokeWidth (1 idle /
|
|
17
|
+
* 2 active), R241 stroke + stroke-width + opacity 250ms transition.
|
|
18
|
+
*/
|
|
19
|
+
import { chromium } from 'playwright';
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
|
|
22
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
23
|
+
const browser = await chromium.launch({ headless: true });
|
|
24
|
+
const ctx = await browser.newContext({ viewport: { width: 1500, height: 1500 } });
|
|
25
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
26
|
+
await ctx.addInitScript(() => {
|
|
27
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
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) => ({
|
|
35
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'claude-code-cli',
|
|
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: [ mk('a'), mk('b'), mk('c') ] } });
|
|
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-topo-hub-spoke-active]', { timeout: 15000 });
|
|
47
|
+
await page.waitForTimeout(300);
|
|
48
|
+
|
|
49
|
+
const probe = await page.evaluate(() => {
|
|
50
|
+
const spokes = Array.from(document.querySelectorAll('[data-topo-hub-spoke-active]'));
|
|
51
|
+
return spokes.map(el => ({
|
|
52
|
+
activeAttr: el.getAttribute('data-topo-hub-spoke-active'),
|
|
53
|
+
linecapAttr: el.getAttribute('stroke-linecap'),
|
|
54
|
+
linecapData: el.getAttribute('data-topo-hub-spoke-linecap'),
|
|
55
|
+
strokeWidthAttr: el.getAttribute('stroke-width'),
|
|
56
|
+
dasharrayAttr: el.getAttribute('stroke-dasharray'),
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await browser.close();
|
|
61
|
+
|
|
62
|
+
const allOk = probe.length >= 1 && probe.every(s =>
|
|
63
|
+
s.linecapAttr === 'round'
|
|
64
|
+
&& s.linecapData === 'round'
|
|
65
|
+
&& (s.activeAttr === 'true'
|
|
66
|
+
? (s.strokeWidthAttr === '2' && s.dasharrayAttr === 'none')
|
|
67
|
+
: (s.strokeWidthAttr === '1' && s.dasharrayAttr === '6 14'))
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const results = {
|
|
71
|
+
spokes_rendered: probe.length >= 1,
|
|
72
|
+
all_linecap_round: probe.every(s => s.linecapAttr === 'round'),
|
|
73
|
+
all_data_linecap: probe.every(s => s.linecapData === 'round'),
|
|
74
|
+
per_state_invariants_intact: allOk,
|
|
75
|
+
};
|
|
76
|
+
const ok = Object.values(results).every(Boolean);
|
|
77
|
+
console.log(`${ok ? '✅' : '❌'} hub-spoke strokeLinecap='round':`, JSON.stringify(results),
|
|
78
|
+
'\n count:', probe.length,
|
|
79
|
+
'\n sample:', probe.slice(0, 3));
|
|
80
|
+
process.exit(ok ? 0 : 1);
|