@sleep2agi/agent-network-dashboard 0.4.6-preview.134 → 0.4.6-preview.136
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/prerender-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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +2 -2
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- 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 +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 +2 -2
- package/.next/server/app/admin.segments/_full.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +2 -2
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- 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 +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- 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 +3 -3
- package/.next/server/app/login.segments/_full.segment.rsc +3 -3
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +2 -2
- package/.next/server/app/logs.rsc +2 -2
- package/.next/server/app/logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +2 -2
- package/.next/server/app/messages.rsc +2 -2
- package/.next/server/app/messages.segments/_full.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +2 -2
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +2 -2
- package/.next/server/app/node.rsc +2 -2
- package/.next/server/app/node.segments/_full.segment.rsc +2 -2
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +2 -2
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +2 -2
- package/.next/server/app/nodes.rsc +2 -2
- package/.next/server/app/nodes.segments/_full.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +2 -2
- package/.next/server/app/server-logs.rsc +2 -2
- package/.next/server/app/server-logs.segments/_full.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +2 -2
- package/.next/server/app/settings/networks.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +2 -2
- package/.next/server/app/settings/tokens.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- 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 +3 -3
- package/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- 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/[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 +2 -2
- package/.next/server/app/tasks.segments/_full.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- 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 +3 -3
- 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 +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_-fwen9-idz9.js → 080nt.fp4k4_-.js} +1 -1
- package/.next/static/chunks/0lquwnvbayt_v.css +2 -0
- package/.next/static/chunks/0sotj.mw.b5os.js +4 -0
- package/.next/static/chunks/{0h-cqsv5-90n7.js → 14hk6zvm4z7zb.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/TopoGraph.tsx +66 -2
- package/package.json +1 -1
- package/scripts/topo-recent-panel-hot-header-test.mjs +132 -0
- package/scripts/topo-recent-panel-more-test.mjs +106 -0
- package/.next/static/chunks/031l_9~y5x3g5.js +0 -4
- package/.next/static/chunks/1450fa_~v9qpc.css +0 -2
- /package/.next/static/{7shpN009bXFL74H8BtEvT → QEopPAN87BrHSdxfMANrJ}/_buildManifest.js +0 -0
- /package/.next/static/{7shpN009bXFL74H8BtEvT → QEopPAN87BrHSdxfMANrJ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{7shpN009bXFL74H8BtEvT → QEopPAN87BrHSdxfMANrJ}/_ssgManifest.js +0 -0
|
@@ -3310,8 +3310,46 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3310
3310
|
"X flows" mirrors flowLinks.length one-for-one. When
|
|
3311
3311
|
flows < msgs the chip-row's "N active links · last 2s"
|
|
3312
3312
|
already tells the operator about traffic volume — no
|
|
3313
|
-
duplicate metric needed here.
|
|
3314
|
-
|
|
3313
|
+
duplicate metric needed here.
|
|
3314
|
+
R129 / Loop: header gains an amber " · N hot" tail
|
|
3315
|
+
when ≥ 1 flowLink has count ≥ 10. The third surface
|
|
3316
|
+
of the hot-lane convention (R126 canvas badge / R127
|
|
3317
|
+
row count) lives at the panel header so a user
|
|
3318
|
+
scanning vertically — header → rows — gets a top-
|
|
3319
|
+
level summary before reading each row's amber digit.
|
|
3320
|
+
Restructured into a single <text> with <tspan>
|
|
3321
|
+
fragments so the amber portion can carry its own
|
|
3322
|
+
fill + weight without a sibling <text>. Switched
|
|
3323
|
+
anchor x=150 left-justified → x=217 right-justified
|
|
3324
|
+
so the count column unifies visually with the legend
|
|
3325
|
+
panel's right-justified header (line 3511 — also
|
|
3326
|
+
fontSize 10 monospace, also x≈215 textAnchor end).
|
|
3327
|
+
data-recent-panel-count stays on the flow tspan so
|
|
3328
|
+
the R96 / R128 tests still resolve. data-recent-
|
|
3329
|
+
panel-hot-count exposes the hot bucket count. */}
|
|
3330
|
+
{(() => {
|
|
3331
|
+
const hotFlowCount = flowLinks.filter(l => l.count >= 10).length;
|
|
3332
|
+
const hotStroke = isLight ? '#d97706' : '#fbbf24';
|
|
3333
|
+
return (
|
|
3334
|
+
<text
|
|
3335
|
+
x="217" y="21"
|
|
3336
|
+
textAnchor="end"
|
|
3337
|
+
fontSize="10"
|
|
3338
|
+
fontFamily="monospace"
|
|
3339
|
+
>
|
|
3340
|
+
<tspan fill={pal.legendAccent} data-recent-panel-count>{flowLinks.length} flows</tspan>
|
|
3341
|
+
{hotFlowCount > 0 && (
|
|
3342
|
+
<tspan
|
|
3343
|
+
fill={hotStroke}
|
|
3344
|
+
fontWeight="700"
|
|
3345
|
+
data-recent-panel-hot-count={hotFlowCount}
|
|
3346
|
+
>
|
|
3347
|
+
{' · '}{hotFlowCount} hot
|
|
3348
|
+
</tspan>
|
|
3349
|
+
)}
|
|
3350
|
+
</text>
|
|
3351
|
+
);
|
|
3352
|
+
})()}
|
|
3315
3353
|
{/* Round 45 / Loop: empty state. The panel used to render
|
|
3316
3354
|
"recent signal" + "0 msgs" with three blank slots below
|
|
3317
3355
|
when no flow yet — read as "broken" rather than "quiet".
|
|
@@ -3455,6 +3493,32 @@ export function TopoGraph({ sessions, sseSessions, renameSignal }: TopoGraphProp
|
|
|
3455
3493
|
);
|
|
3456
3494
|
})
|
|
3457
3495
|
)}
|
|
3496
|
+
{/* Round 128 / Loop: overflow hint. The recent-signal panel
|
|
3497
|
+
renders the top 3 flowLinks via .slice(0, 3) — but a
|
|
3498
|
+
fleet with 5 or 10 active flows silently truncates the
|
|
3499
|
+
rest. The R96 "X flows" header tells the total but
|
|
3500
|
+
doesn't say "you're seeing top-3". This hint fires
|
|
3501
|
+
only when flowLinks.length > 3 so quiet fleets stay
|
|
3502
|
+
clean. Footer y=82 sits in the 10-px gap between
|
|
3503
|
+
row 3 (baseline 70) and the panel bottom (84), so
|
|
3504
|
+
overlap-test geometry is unchanged. fontStyle=italic
|
|
3505
|
+
+ opacity 0.55 reads as muted metadata, not an
|
|
3506
|
+
actionable row — matches the R110 empty-state hint
|
|
3507
|
+
idiom. */}
|
|
3508
|
+
{flowLinks.length > 3 && (
|
|
3509
|
+
<text
|
|
3510
|
+
x="115" y="82"
|
|
3511
|
+
textAnchor="middle"
|
|
3512
|
+
fill={pal.legendText}
|
|
3513
|
+
fontSize="8"
|
|
3514
|
+
fontFamily="monospace"
|
|
3515
|
+
fontStyle="italic"
|
|
3516
|
+
opacity={0.55}
|
|
3517
|
+
data-recent-panel-more={flowLinks.length - 3}
|
|
3518
|
+
>
|
|
3519
|
+
+ {flowLinks.length - 3} more flow{flowLinks.length - 3 === 1 ? '' : 's'}
|
|
3520
|
+
</text>
|
|
3521
|
+
)}
|
|
3458
3522
|
</g>
|
|
3459
3523
|
|
|
3460
3524
|
{/* legend — Round 55 / Loop: each status row is now a hover
|
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* Round 129 verification: recent-signal panel header gains a
|
|
2
|
+
* " · N hot" amber tail when ≥ 1 flowLink has count ≥ 10.
|
|
3
|
+
*
|
|
4
|
+
* The hot-lane convention's surfaces:
|
|
5
|
+
* R126: canvas midpoint count badge → amber stroke
|
|
6
|
+
* R127: recent-signal row count digit → amber + bold
|
|
7
|
+
* R129: recent-signal panel HEADER → " · N hot" amber suffix
|
|
8
|
+
*
|
|
9
|
+
* Vertical scan path now reads: header → rows → canvas. Each layer
|
|
10
|
+
* answers a different scope: header = "how many hot?", rows =
|
|
11
|
+
* "which ones?", canvas = "where on the topology?".
|
|
12
|
+
*
|
|
13
|
+
* Three states:
|
|
14
|
+
* A. 3 pairs, all count < 10 → header "3 flows", no hot tail
|
|
15
|
+
* B. 5 pairs, 2 of them count ≥ 10 → header "5 flows · 2 hot", amber tail
|
|
16
|
+
* C. 5 pairs, 5 of them count ≥ 10 → header "5 flows · 5 hot"
|
|
17
|
+
*/
|
|
18
|
+
import { chromium } from 'playwright';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
|
|
21
|
+
const TOKEN = JSON.parse(readFileSync('/home/vansin/.anet/config.json', 'utf8')).token;
|
|
22
|
+
|
|
23
|
+
async function probe(seedPairs) {
|
|
24
|
+
// seedPairs is an array of [from, to, count] tuples — each pair gets
|
|
25
|
+
// `count` msgs to make link.count == count after dedup.
|
|
26
|
+
const browser = await chromium.launch({ headless: true });
|
|
27
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
28
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
29
|
+
await ctx.addInitScript(() => {
|
|
30
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
34
|
+
const aliasSet = new Set();
|
|
35
|
+
for (const [a, b] of seedPairs) { aliasSet.add(a); aliasSet.add(b); }
|
|
36
|
+
const aliases = [...aliasSet];
|
|
37
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
38
|
+
const r = await route.fetch();
|
|
39
|
+
const b = await r.json();
|
|
40
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
41
|
+
const mk = (alias) => ({
|
|
42
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
43
|
+
network_id: nid, project_dir: null,
|
|
44
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
45
|
+
});
|
|
46
|
+
await route.fulfill({ response: r, json: { ...b, sessions: aliases.map(mk) } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
const msgs = [];
|
|
51
|
+
let id = 0;
|
|
52
|
+
for (const [from, to, count] of seedPairs) {
|
|
53
|
+
for (let i = 0; i < count; i++) {
|
|
54
|
+
msgs.push({
|
|
55
|
+
id: `m${id++}`,
|
|
56
|
+
from_alias: from,
|
|
57
|
+
to_alias: to,
|
|
58
|
+
content: 'hi',
|
|
59
|
+
network_id: 'default',
|
|
60
|
+
created_at: new Date(now - (20000 + i * 500 + id * 13)).toISOString(),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
65
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
66
|
+
|
|
67
|
+
const page = await ctx.newPage();
|
|
68
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
69
|
+
await page.waitForFunction((n) => document.querySelectorAll('g[data-node]').length === n, aliases.length, { timeout: 30000 });
|
|
70
|
+
await page.waitForTimeout(500);
|
|
71
|
+
|
|
72
|
+
const out = await page.evaluate(() => {
|
|
73
|
+
const count = document.querySelector('[data-recent-panel-count]');
|
|
74
|
+
const hot = document.querySelector('[data-recent-panel-hot-count]');
|
|
75
|
+
return {
|
|
76
|
+
countText: count?.textContent,
|
|
77
|
+
hotText: hot?.textContent,
|
|
78
|
+
hotAttr: hot?.getAttribute('data-recent-panel-hot-count'),
|
|
79
|
+
hotFill: hot?.getAttribute('fill'),
|
|
80
|
+
hotWeight: hot?.getAttribute('font-weight'),
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await browser.close();
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// State A: all warm
|
|
89
|
+
const a = await probe([
|
|
90
|
+
['alpha', 'beta', 4],
|
|
91
|
+
['gamma', 'delta', 5],
|
|
92
|
+
['epsilon', 'zeta', 3],
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
// State B: 5 pairs, 2 hot (≥ 10)
|
|
96
|
+
const b = await probe([
|
|
97
|
+
['alpha', 'beta', 12], // hot
|
|
98
|
+
['gamma', 'delta', 15], // hot
|
|
99
|
+
['epsilon', 'zeta', 4],
|
|
100
|
+
['eta', 'theta', 6],
|
|
101
|
+
['iota', 'kappa', 8],
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
// State C: 5 pairs, all 5 hot
|
|
105
|
+
const c = await probe([
|
|
106
|
+
['alpha', 'beta', 12],
|
|
107
|
+
['gamma', 'delta', 11],
|
|
108
|
+
['epsilon', 'zeta', 14],
|
|
109
|
+
['eta', 'theta', 13],
|
|
110
|
+
['iota', 'kappa', 25],
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const amber = '#fbbf24';
|
|
114
|
+
|
|
115
|
+
const results = {
|
|
116
|
+
a_count_3flows: a.countText === '3 flows',
|
|
117
|
+
a_noHotTspan: a.hotText === undefined,
|
|
118
|
+
|
|
119
|
+
b_count_5flows: b.countText === '5 flows',
|
|
120
|
+
b_hot_text: b.hotText === ' · 2 hot',
|
|
121
|
+
b_hot_attr: b.hotAttr === '2',
|
|
122
|
+
b_hot_amberFill: (b.hotFill || '').toLowerCase() === amber,
|
|
123
|
+
b_hot_boldWeight: b.hotWeight === '700',
|
|
124
|
+
|
|
125
|
+
c_count_5flows: c.countText === '5 flows',
|
|
126
|
+
c_hot_text: c.hotText === ' · 5 hot',
|
|
127
|
+
c_hot_attr: c.hotAttr === '5',
|
|
128
|
+
};
|
|
129
|
+
const ok = Object.values(results).every(Boolean);
|
|
130
|
+
console.log(`${ok ? '✅' : '❌'} recent-panel hot header:`, JSON.stringify(results),
|
|
131
|
+
`\n A=`, a, `\n B=`, b, `\n C=`, c);
|
|
132
|
+
process.exit(ok ? 0 : 1);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* Round 128 verification: "+N more flows" overflow hint in the
|
|
2
|
+
* recent-signal panel.
|
|
3
|
+
*
|
|
4
|
+
* The panel renders top 3 flowLinks via .slice(0, 3). When the
|
|
5
|
+
* fleet has > 3 active flows, the rest silently disappear — the
|
|
6
|
+
* R96 "X flows" header reports the total but doesn't make
|
|
7
|
+
* truncation explicit. R128 adds a muted italic footer
|
|
8
|
+
* "+ N more flow(s)"
|
|
9
|
+
* inside the existing panel (y=82, between row-3 baseline and the
|
|
10
|
+
* panel bottom at y=84), only when flowLinks.length > 3.
|
|
11
|
+
*
|
|
12
|
+
* Three states tested:
|
|
13
|
+
* - 5 distinct flow pairs → footer shows "+ 2 more flows"
|
|
14
|
+
* - exactly 3 flow pairs → footer absent
|
|
15
|
+
* - 0 flows (empty panel) → footer absent (R45 placeholder wins)
|
|
16
|
+
*
|
|
17
|
+
* Note: the test seeds (count) messages per pair so each unique
|
|
18
|
+
* (from→to) pair becomes ONE flowLink (deduped). Pairs > 3 force
|
|
19
|
+
* the hint; pairs ≤ 3 don't.
|
|
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
|
+
async function probe(pairCount) {
|
|
27
|
+
const browser = await chromium.launch({ headless: true });
|
|
28
|
+
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1500 } });
|
|
29
|
+
await ctx.addCookies([{ name: 'anet_dashboard_session', value: `v3:${TOKEN}`, domain: '127.0.0.1', path: '/' }]);
|
|
30
|
+
await ctx.addInitScript(() => {
|
|
31
|
+
try { localStorage.setItem('anet-theme', 'cyber'); sessionStorage.setItem('anet_v3_auth', '1'); } catch {}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const fresh = new Date(Date.now() - 60 * 1000).toISOString();
|
|
35
|
+
const aliases = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta'];
|
|
36
|
+
await ctx.route('**/api/hub/status*', async (route) => {
|
|
37
|
+
const r = await route.fetch();
|
|
38
|
+
const b = await r.json();
|
|
39
|
+
const nid = (b.sessions || [])[0]?.network_id || 'default';
|
|
40
|
+
const mk = (alias) => ({
|
|
41
|
+
alias, status: 'working', model: 'claude-opus-4', runtime: 'cli-claude-code',
|
|
42
|
+
network_id: nid, project_dir: null,
|
|
43
|
+
created_at: fresh, updated_at: fresh, last_seen_at: fresh,
|
|
44
|
+
});
|
|
45
|
+
await route.fulfill({ response: r, json: { ...b, sessions: aliases.map(mk) } });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
const msgs = [];
|
|
50
|
+
// Make pairCount distinct (from→to) pairs, 1 msg each, ascending age
|
|
51
|
+
for (let i = 0; i < pairCount; i++) {
|
|
52
|
+
msgs.push({
|
|
53
|
+
id: `m${i}`,
|
|
54
|
+
from_alias: aliases[i],
|
|
55
|
+
to_alias: aliases[(i + 1) % aliases.length],
|
|
56
|
+
content: 'hi',
|
|
57
|
+
network_id: 'default',
|
|
58
|
+
created_at: new Date(now - (20000 + i * 1000)).toISOString(),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
await ctx.route('**/api/hub/messages*', (route) => route.fulfill({ json: { messages: msgs } }));
|
|
62
|
+
await ctx.route('**/api/hub/tasks*', (route) => route.fulfill({ json: { tasks: [] } }));
|
|
63
|
+
|
|
64
|
+
const page = await ctx.newPage();
|
|
65
|
+
await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' });
|
|
66
|
+
await page.waitForFunction(() => document.querySelectorAll('g[data-node]').length === 6, { timeout: 30000 });
|
|
67
|
+
await page.waitForTimeout(500);
|
|
68
|
+
|
|
69
|
+
const out = await page.evaluate(() => {
|
|
70
|
+
const el = document.querySelector('[data-recent-panel-more]');
|
|
71
|
+
const rows = document.querySelectorAll('[data-recent-row]').length;
|
|
72
|
+
const headerCount = document.querySelector('[data-recent-panel-count]')?.textContent;
|
|
73
|
+
return {
|
|
74
|
+
footerPresent: !!el,
|
|
75
|
+
footerText: el?.textContent?.trim(),
|
|
76
|
+
footerAttr: el?.getAttribute('data-recent-panel-more'),
|
|
77
|
+
rows,
|
|
78
|
+
headerCount,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await browser.close();
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const five = await probe(5);
|
|
87
|
+
const three = await probe(3);
|
|
88
|
+
const zero = await probe(0);
|
|
89
|
+
|
|
90
|
+
const results = {
|
|
91
|
+
five_footerPresent: five.footerPresent,
|
|
92
|
+
five_footerText: five.footerText === '+ 2 more flows',
|
|
93
|
+
five_footerAttr: five.footerAttr === '2',
|
|
94
|
+
five_rowsCapped3: five.rows === 3,
|
|
95
|
+
five_headerSaysFive: five.headerCount?.startsWith('5'),
|
|
96
|
+
|
|
97
|
+
three_footerAbsent: !three.footerPresent,
|
|
98
|
+
three_rowsExactly3: three.rows === 3,
|
|
99
|
+
|
|
100
|
+
zero_footerAbsent: !zero.footerPresent,
|
|
101
|
+
zero_rowsZero: zero.rows === 0,
|
|
102
|
+
};
|
|
103
|
+
const ok = Object.values(results).every(Boolean);
|
|
104
|
+
console.log(`${ok ? '✅' : '❌'} recent-panel more:`, JSON.stringify(results),
|
|
105
|
+
`\n five=`, five, `\n three=`, three, `\n zero=`, zero);
|
|
106
|
+
process.exit(ok ? 0 : 1);
|