sigrank 0.13.0 → 0.14.2
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/README.md +25 -24
- package/banner.svg +7 -0
- package/cli.mjs +18 -123
- package/index.mjs +1 -1
- package/package.json +19 -1
- package/submit.mjs +3 -0
- package/tools.mjs +24 -2
- package/tui.mjs +14 -34
package/README.md
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
███████ ██ ██████ ██████ █████ ███ ██ ██ ██
|
|
5
|
-
██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
|
|
6
|
-
███████ ██ ██ ███ ██████ ███████ ██ ██ ██ █████
|
|
7
|
-
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
8
|
-
███████ ██ ██████ ██ ██ ██ ██ ██ ████ ██ ██
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
◈ ─────────────────────────────────────────── ◈
|
|
12
|
-
|
|
13
|
-
# SigRank MCP
|
|
3
|
+
<p><img src="./.github/assets/og-card.png" alt="SigRank — the new standard in AI evaluation & benchmarks" width="800"></p>
|
|
14
4
|
|
|
15
5
|
**The yield cascade + live leaderboard as MCP tools any agent can call.**
|
|
16
6
|
|
|
@@ -25,21 +15,35 @@ For all builders, burners and 10xers.
|
|
|
25
15
|
|
|
26
16
|
</div>
|
|
27
17
|
|
|
28
|
-
|
|
18
|
+
| The board | Your operator profile |
|
|
19
|
+
|:---:|:---:|
|
|
20
|
+
| [](https://signalaf.com/board/all) | [](https://signalaf.com) |
|
|
21
|
+
| Every operator ranked by **Υ Yield** — the architecture of the cascade, not raw spend | Cascade layer, class, and fingerprint — derived from four token counts |
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
> **Run [`sigrank enroll`](#sign-in--submit) then [`sigrank submit`](#sign-in--submit) to get ranked and claim your public profile at [signalaf.com](https://signalaf.com).**
|
|
31
24
|
|
|
32
|
-
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quickstart — 3 steps to the board
|
|
33
28
|
|
|
34
29
|
```bash
|
|
30
|
+
# 1. Install (pulls ccusage + tokscale + tokendash automatically — no separate installs)
|
|
35
31
|
npm install -g sigrank
|
|
36
|
-
|
|
32
|
+
|
|
33
|
+
# 2. Sign in (paste a connect code from signalaf.com → Settings → New key)
|
|
34
|
+
sigrank enroll
|
|
35
|
+
|
|
36
|
+
# 3. Submit your cascade to the board
|
|
37
|
+
sigrank submit
|
|
37
38
|
```
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
That's it. The agent reads your local AI session logs on-device, derives your token cascade (Υ Yield, Leverage, Velocity, 10xDEV), and publishes to [signalaf.com](https://signalaf.com). No paste, no transcript content — only the four token counts leave your machine.
|
|
41
|
+
|
|
42
|
+
Or just explore without signing in:
|
|
40
43
|
|
|
41
44
|
```bash
|
|
42
|
-
|
|
45
|
+
sigrank # launches the full tabbed TUI (dashboard, compare, board, watch)
|
|
46
|
+
npx sigrank board --once # print the live leaderboard once
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
## Install from GitHub
|
|
@@ -67,7 +71,7 @@ sigrank
|
|
|
67
71
|
## Commands
|
|
68
72
|
|
|
69
73
|
```
|
|
70
|
-
⊙ SigRank CLI v0.
|
|
74
|
+
⊙ SigRank CLI v0.14.2
|
|
71
75
|
|
|
72
76
|
Default (no args)
|
|
73
77
|
sigrank unified dashboard: cascade + token pillars + board
|
|
@@ -78,8 +82,6 @@ Commands
|
|
|
78
82
|
board live leaderboard (refreshes every 30s)
|
|
79
83
|
board --window 7d board for a specific window (7d, 30d, 90d, all)
|
|
80
84
|
board --once print once and exit
|
|
81
|
-
me single-platform cascade (claude by default)
|
|
82
|
-
me --platform codex use a different platform adapter
|
|
83
85
|
compare raw pillar audit: tokenpull vs ccusage vs token-dash vs tokscale
|
|
84
86
|
compare --platform codex compare for a specific platform
|
|
85
87
|
tui full tabbed TUI: Dashboard / Trends / Compare / Board / Watch / Connect
|
|
@@ -103,7 +105,6 @@ Examples
|
|
|
103
105
|
sigrank board # live leaderboard
|
|
104
106
|
sigrank compare # pillar audit (claude)
|
|
105
107
|
sigrank compare --platform codex
|
|
106
|
-
sigrank me --platform codex
|
|
107
108
|
sigrank watch --window 7d --refresh 60
|
|
108
109
|
sigrank board --window all --once
|
|
109
110
|
```
|
|
@@ -202,9 +203,9 @@ The dashboard pulls from multiple sources and shows them side-by-side for verifi
|
|
|
202
203
|
| Source | What | Platform |
|
|
203
204
|
|---|---|---|
|
|
204
205
|
| `tokenpull` | On-device JSONL scanner (canon source) | claude, codex, amp, … |
|
|
205
|
-
| `ccusage` | `ccusage <platform> daily --json` CLI | claude, codex |
|
|
206
|
-
| `token-dashboard` | `~/.claude/token-dashboard.db` SQLite | claude only |
|
|
207
|
-
| `tokscale` | `~/tokscale_report.json`
|
|
206
|
+
| `ccusage` | `ccusage <platform> daily --json` CLI (bundled) | claude, codex |
|
|
207
|
+
| `token-dashboard` | `~/.claude/token-dashboard.db` SQLite (bundled) | claude only |
|
|
208
|
+
| `tokscale` | `tokscale models --json` CLI (bundled, falls back to `~/tokscale_report.json`) | claude, codex |
|
|
208
209
|
|
|
209
210
|
**Codex input is estimated** — Codex logs don't expose true input tokens directly. The formula:
|
|
210
211
|
```
|
package/banner.svg
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200" viewBox="0 0 800 200">
|
|
2
|
+
<rect width="800" height="200" rx="12" fill="#0a0805"/>
|
|
3
|
+
<rect x="0" y="0" width="800" height="3" rx="12" fill="#c4923a"/>
|
|
4
|
+
<text x="60" y="110" font-family="Georgia, serif" font-size="56" font-weight="700" fill="#e8b955">§ SigRank</text>
|
|
5
|
+
<text x="60" y="150" font-family="Helvetica, Arial, sans-serif" font-size="18" fill="#a89a7a">The AI operator leaderboard</text>
|
|
6
|
+
<text x="60" y="178" font-family="Helvetica, Arial, sans-serif" font-size="14" fill="#6b6354">signalaf.com</text>
|
|
7
|
+
</svg>
|
package/cli.mjs
CHANGED
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
* npx sigrank board live leaderboard, refreshes every 30s
|
|
10
10
|
* npx sigrank board --window 7d board for a specific window
|
|
11
11
|
* npx sigrank board --once print once and exit (no live refresh)
|
|
12
|
-
* npx sigrank
|
|
13
|
-
* npx sigrank me --platform amp use a different platform adapter
|
|
14
|
-
* npx sigrank me --compare raw pillar comparison: ccusage vs tokenpull vs token-dashboard
|
|
12
|
+
* npx sigrank compare raw pillar comparison: ccusage vs tokenpull vs token-dashboard
|
|
15
13
|
* npx sigrank watch RT tune meter — ALL active platforms × all windows
|
|
16
14
|
* npx sigrank watch --platform X watch one platform only (optional filter)
|
|
17
15
|
* npx sigrank watch --window 7d watch one window only (optional filter)
|
|
@@ -24,7 +22,7 @@
|
|
|
24
22
|
* green = positive movement
|
|
25
23
|
*/
|
|
26
24
|
|
|
27
|
-
import { callTool, DEFAULT_API_BASE } from './tools.mjs'
|
|
25
|
+
import { callTool, DEFAULT_API_BASE, pullActivePlatforms } from './tools.mjs'
|
|
28
26
|
import { classify } from './cascade.mjs'
|
|
29
27
|
import { ensureIdentity, keystorePath } from './keystore.mjs'
|
|
30
28
|
import { submitSignedWindow } from './submit.mjs'
|
|
@@ -314,107 +312,6 @@ async function runBoard({ window = '30d', once = false, refresh = 30 } = {}) {
|
|
|
314
312
|
}
|
|
315
313
|
}
|
|
316
314
|
|
|
317
|
-
// ── ME command ───────────────────────────────────────────────────────────────
|
|
318
|
-
|
|
319
|
-
async function runMe({ platform = 'claude', compare = false } = {}) {
|
|
320
|
-
if (compare) return runCompare({ platform })
|
|
321
|
-
|
|
322
|
-
write(HIDE_CURSOR)
|
|
323
|
-
writeln(` ${dim('reading local token logs…')}`)
|
|
324
|
-
|
|
325
|
-
let pulled
|
|
326
|
-
try {
|
|
327
|
-
pulled = await callTool('tokenpull', { platform })
|
|
328
|
-
} catch (e) {
|
|
329
|
-
write(SHOW_CURSOR)
|
|
330
|
-
write(CURSOR_UP(1) + ERASE_LINE)
|
|
331
|
-
writeln(red(` ✗ ${e.message}`))
|
|
332
|
-
process.exit(1)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// clear the "reading…" line
|
|
336
|
-
write(CURSOR_UP(1) + ERASE_LINE)
|
|
337
|
-
|
|
338
|
-
const w = termWidth()
|
|
339
|
-
writeln()
|
|
340
|
-
writeln(` ${gold('⊙ SigRank')} ${bold('Your Cascade')} ${dim(`platform: ${pulled.platform ?? platform}`)}`)
|
|
341
|
-
if (pulled.estimated) writeln(` ${dim('⚠ estimated values (cache data unavailable for this platform)')}`)
|
|
342
|
-
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
343
|
-
|
|
344
|
-
// column headers
|
|
345
|
-
const cols_h = [
|
|
346
|
-
padEnd(dim('Window'), 8),
|
|
347
|
-
padStart(dim('Υ Yield'), 10),
|
|
348
|
-
padStart(dim('SNR'), 7),
|
|
349
|
-
padStart(dim('Leverage'), 9),
|
|
350
|
-
padStart(dim('Velocity'), 9),
|
|
351
|
-
padStart(dim('10xDEV'), 8),
|
|
352
|
-
padStart(dim('Class'), 13),
|
|
353
|
-
padStart(dim('Tokens'), 8),
|
|
354
|
-
]
|
|
355
|
-
writeln(` ${cols_h.join(' ')}`)
|
|
356
|
-
writeln(` ${dim('·'.repeat(w - 4))}`)
|
|
357
|
-
|
|
358
|
-
const windows = pulled.windows ?? []
|
|
359
|
-
for (const win of windows) {
|
|
360
|
-
const cas = win.cascade
|
|
361
|
-
const isAll = win.window === 'all'
|
|
362
|
-
const wLabel = isAll ? bold('all-time') : win.window
|
|
363
|
-
const yVal = cas?.yield != null ? fmtYield(cas.yield) : '—'
|
|
364
|
-
const snrVal = cas?.snr != null ? fmtSNR(cas.snr) : '—'
|
|
365
|
-
const levVal = cas?.leverage != null ? `${fmtLev(cas.leverage)}×` : '—'
|
|
366
|
-
const velVal = cas?.velocity != null ? cas.velocity.toFixed(2) : '—'
|
|
367
|
-
const devVal = cas?.dev10x != null ? cas.dev10x.toFixed(2) : '—'
|
|
368
|
-
const cls = colorClass(cas?.class ?? '—')
|
|
369
|
-
const tok = fmtTokens(win.pillars?.total ?? (
|
|
370
|
-
(win.pillars?.input ?? 0) + (win.pillars?.output ?? 0) +
|
|
371
|
-
(win.pillars?.cacheCreate ?? 0) + (win.pillars?.cacheRead ?? 0)
|
|
372
|
-
))
|
|
373
|
-
|
|
374
|
-
const row = [
|
|
375
|
-
padEnd(wLabel, 8),
|
|
376
|
-
padStart(isAll ? gold(yVal) : yVal, 10),
|
|
377
|
-
padStart(snrVal, 7),
|
|
378
|
-
padStart(levVal, 9),
|
|
379
|
-
padStart(velVal, 9),
|
|
380
|
-
padStart(devVal, 8),
|
|
381
|
-
padEnd(cls, 13),
|
|
382
|
-
padStart(tok, 8),
|
|
383
|
-
]
|
|
384
|
-
writeln(` ${row.join(' ')}`)
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
writeln(` ${dim('─'.repeat(w - 4))}`)
|
|
388
|
-
|
|
389
|
-
// card for the best window (all-time if present, else first)
|
|
390
|
-
const best = windows.find(w => w.window === 'all') ?? windows[0]
|
|
391
|
-
if (best?.card) {
|
|
392
|
-
writeln()
|
|
393
|
-
writeln(` ${dim('cascade read:')}`)
|
|
394
|
-
// wrap card text at terminal width
|
|
395
|
-
const cardText = best.card
|
|
396
|
-
const maxW = w - 6
|
|
397
|
-
const words = cardText.split(' ')
|
|
398
|
-
let line = ''
|
|
399
|
-
for (const word of words) {
|
|
400
|
-
if (line.length + word.length + 1 > maxW) {
|
|
401
|
-
writeln(` ${line}`)
|
|
402
|
-
line = word
|
|
403
|
-
} else {
|
|
404
|
-
line = line ? `${line} ${word}` : word
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
if (line) writeln(` ${line}`)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
writeln()
|
|
411
|
-
|
|
412
|
-
// submit hint
|
|
413
|
-
writeln(` ${dim('to publish:')} ${cyan('npx sigrank submit')} ${dim('(sign in first:')} ${cyan('npx sigrank enroll')}${dim(' — get a code at signalaf.com → Settings)')}`)
|
|
414
|
-
writeln()
|
|
415
|
-
write(SHOW_CURSOR)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
315
|
// ── COMPARE command ───────────────────────────────────────────────────────────
|
|
419
316
|
// Side-by-side: ccusage (JSON) vs tokenpull vs token-dashboard (SQLite)
|
|
420
317
|
//
|
|
@@ -756,16 +653,8 @@ const WATCH_WINDOWS = ['7d', '30d', '90d', 'all']
|
|
|
756
653
|
// Detect which platforms have real local data (input+output > 0 in any window) via tokenpull.
|
|
757
654
|
// Returns an array of pull results (the same shape tokenpullAny yields), one per active platform.
|
|
758
655
|
async function detectActivePulls() {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const active = []
|
|
762
|
-
for (const r of settled) {
|
|
763
|
-
if (r.status !== 'fulfilled' || !r.value) continue
|
|
764
|
-
const d = r.value
|
|
765
|
-
const live = (d.windows || []).some((w) => (w.pillars.input + w.pillars.output) > 0)
|
|
766
|
-
if (live) active.push(d)
|
|
767
|
-
}
|
|
768
|
-
return active
|
|
656
|
+
// Unified: one loader for me / watch / the TUI Dashboard (was a separate tokenpullAny loop).
|
|
657
|
+
return pullActivePlatforms()
|
|
769
658
|
}
|
|
770
659
|
|
|
771
660
|
async function runWatch({ platform, window: win, refresh = 30, submit = false } = {}) {
|
|
@@ -1320,8 +1209,6 @@ async function showHelp() {
|
|
|
1320
1209
|
writeln(` ${cyan('board')} live leaderboard (refreshes every 30s)`)
|
|
1321
1210
|
writeln(` ${cyan('board --window 7d')} board for a specific window (7d, 30d, 90d, all)`)
|
|
1322
1211
|
writeln(` ${cyan('board --once')} print once and exit`)
|
|
1323
|
-
writeln(` ${cyan('me')} single-platform cascade (claude by default)`)
|
|
1324
|
-
writeln(` ${cyan('me --platform codex')} use a different platform adapter`)
|
|
1325
1212
|
writeln(` ${cyan('compare')} raw pillar audit: tokenpull vs ccusage vs token-dash vs tokscale`)
|
|
1326
1213
|
writeln(` ${cyan('compare --platform codex')} compare for a specific platform`)
|
|
1327
1214
|
writeln(` ${cyan('tui')} full tabbed TUI: Dashboard / Compare / Board / Watch`)
|
|
@@ -1346,7 +1233,6 @@ async function showHelp() {
|
|
|
1346
1233
|
writeln(` ${dim('sigrank board')} # live leaderboard`)
|
|
1347
1234
|
writeln(` ${dim('sigrank compare')} # pillar audit (claude)`)
|
|
1348
1235
|
writeln(` ${dim('sigrank compare --platform codex')}`)
|
|
1349
|
-
writeln(` ${dim('sigrank me --platform codex')}`)
|
|
1350
1236
|
writeln(` ${dim('sigrank watch --window 7d --refresh 60')}`)
|
|
1351
1237
|
writeln(` ${dim('sigrank board --window all --once')}`)
|
|
1352
1238
|
writeln()
|
|
@@ -1436,15 +1322,26 @@ async function runSubmit({ platform = 'claude', window } = {}) {
|
|
|
1436
1322
|
}
|
|
1437
1323
|
|
|
1438
1324
|
writeln()
|
|
1325
|
+
let anyRanked = false
|
|
1326
|
+
let anyReceivedNotRanked = false
|
|
1439
1327
|
for (const w of out.windows || []) {
|
|
1440
1328
|
const label = String(w.window).padEnd(8)
|
|
1441
|
-
if (w.
|
|
1442
|
-
|
|
1329
|
+
if (w.ranked) {
|
|
1330
|
+
anyRanked = true
|
|
1331
|
+
writeln(` ${green('✓')} ${bold(label)} ${green('RANKED')} ${dim('· live on the board')}`)
|
|
1332
|
+
} else if (w.status === 'received') {
|
|
1333
|
+
anyReceivedNotRanked = true
|
|
1334
|
+
writeln(` ${gold('⚠')} ${bold(label)} ${gold('received · NOT ranked')} ${dim(`(tier=${w.verification_tier || '—'})`)}`)
|
|
1443
1335
|
} else {
|
|
1444
1336
|
writeln(` ${red('✗')} ${bold(label)} ${dim(w.reason || w.status || 'failed')}`)
|
|
1445
1337
|
}
|
|
1446
1338
|
}
|
|
1447
|
-
writeln(
|
|
1339
|
+
writeln()
|
|
1340
|
+
if (anyRanked) {
|
|
1341
|
+
writeln(` ${dim('Your board row:')} ${cyan(`signalaf.com/user/${id.codename}`)}`)
|
|
1342
|
+
} else if (anyReceivedNotRanked) {
|
|
1343
|
+
writeln(` ${gold('Received, but NOT ranked')} ${dim('— this device is unenrolled or revoked. Re-enroll to rank:')} ${cyan('npx sigrank enroll')}`)
|
|
1344
|
+
}
|
|
1448
1345
|
}
|
|
1449
1346
|
|
|
1450
1347
|
export async function runCli(argv) {
|
|
@@ -1485,8 +1382,6 @@ export async function runCli(argv) {
|
|
|
1485
1382
|
once: flags.once === true || flags.once === 'true',
|
|
1486
1383
|
refresh: Number(flags.refresh) || 30,
|
|
1487
1384
|
})
|
|
1488
|
-
} else if (cmd === 'me') {
|
|
1489
|
-
await runMe({ platform: flags.platform ?? 'claude', compare: flags.compare === true || flags.compare === 'true' })
|
|
1490
1385
|
} else if (cmd === 'compare') {
|
|
1491
1386
|
await runCompare({ platform: flags.platform ?? 'claude' })
|
|
1492
1387
|
} else if (cmd === 'tui') {
|
package/index.mjs
CHANGED
|
@@ -70,7 +70,7 @@ async function startMcpServer() {
|
|
|
70
70
|
// no args + TTY stdout → full tabbed TUI (interactive use)
|
|
71
71
|
// no args + piped → MCP stdio server (AI client use)
|
|
72
72
|
const cliArgs = process.argv.slice(2)
|
|
73
|
-
const CLI_COMMANDS = new Set(['board', '
|
|
73
|
+
const CLI_COMMANDS = new Set(['board', 'compare', 'tui', 'watch', 'enroll', 'submit', 'help', '--help', '-h', '--version', '-v'])
|
|
74
74
|
if (cliArgs.length > 0 && (CLI_COMMANDS.has(cliArgs[0]) || cliArgs[0].startsWith('--'))) {
|
|
75
75
|
runCli(process.argv)
|
|
76
76
|
} else if (cliArgs.length === 0 && process.stdout.isTTY) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigrank",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,9 +37,27 @@
|
|
|
37
37
|
"keystore.mjs",
|
|
38
38
|
"submit.mjs",
|
|
39
39
|
"connect.mjs",
|
|
40
|
+
"banner.svg",
|
|
40
41
|
"README.md",
|
|
41
42
|
"LICENSE"
|
|
42
43
|
],
|
|
44
|
+
"keywords": [
|
|
45
|
+
"mcp",
|
|
46
|
+
"model-context-protocol",
|
|
47
|
+
"ai-agents",
|
|
48
|
+
"claude",
|
|
49
|
+
"anthropic",
|
|
50
|
+
"llm",
|
|
51
|
+
"token-telemetry",
|
|
52
|
+
"token-usage",
|
|
53
|
+
"leaderboard",
|
|
54
|
+
"cli",
|
|
55
|
+
"tui",
|
|
56
|
+
"yield-cascade",
|
|
57
|
+
"sigrank",
|
|
58
|
+
"agent-tools",
|
|
59
|
+
"on-device"
|
|
60
|
+
],
|
|
43
61
|
"dependencies": {
|
|
44
62
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
45
63
|
"@zhangferry-dev/tokendash": "^1.7.5",
|
package/submit.mjs
CHANGED
|
@@ -157,6 +157,9 @@ export async function submitSignedWindow(windowKey, pillars, messages, identity,
|
|
|
157
157
|
window: WINDOW_TYPE[windowKey] || windowKey,
|
|
158
158
|
verification_tier: ack.verification_tier ?? null,
|
|
159
159
|
persisted: ack.persisted ?? null,
|
|
160
|
+
// ranked = actually on the board (verified + written), not just "received".
|
|
161
|
+
// An unenrolled/revoked device gets HTTP 202 received but is NEVER ranked.
|
|
162
|
+
ranked: !!(res.ok && ack.verification_tier === 'verified' && ack.persisted === true),
|
|
160
163
|
snapshot_hash: payload.agent.snapshot_hash,
|
|
161
164
|
reason: res.ok ? null : ack.reason || ack.status || `http_${res.status}`,
|
|
162
165
|
detail: ack.detail ?? null,
|
package/tools.mjs
CHANGED
|
@@ -299,6 +299,24 @@ const WINDOW_TYPE = { '7d': '7d', '30d': '30d', '90d': '90d', all: 'all_time' }
|
|
|
299
299
|
const WATCH_SUBMIT_COOLDOWN_MS = 5 * 60 * 1000
|
|
300
300
|
const _lastWatchSubmitAt = new Map()
|
|
301
301
|
|
|
302
|
+
// ── Shared active-platform loader ───────────────────────────────────────────
|
|
303
|
+
// THE single data path for "show my cascade across platforms" — used by `me`,
|
|
304
|
+
// `watch`, and the TUI Dashboard so they can't drift apart again. Pulls each
|
|
305
|
+
// target platform via the tokenpull tool (enriched: pillars + cascade + card),
|
|
306
|
+
// keeps only platforms with real local data, and sorts claude → codex → rest.
|
|
307
|
+
// Pass `platforms` to scope it (e.g. ['claude'] for a fast first paint).
|
|
308
|
+
export async function pullActivePlatforms({ platforms } = {}, opts = {}) {
|
|
309
|
+
const targets = platforms && platforms.length ? platforms : ALL_PLATFORMS
|
|
310
|
+
const settled = await Promise.allSettled(targets.map((p) => callTool('tokenpull', { platform: p }, opts)))
|
|
311
|
+
const active = settled
|
|
312
|
+
.filter((r) => r.status === 'fulfilled' && r.value)
|
|
313
|
+
.map((r) => r.value)
|
|
314
|
+
.filter((d) => (d.windows || []).some((w) => ((w.pillars?.input ?? 0) + (w.pillars?.output ?? 0)) > 0))
|
|
315
|
+
const rank = (p) => (p === 'claude' ? -2 : p === 'codex' ? -1 : ALL_PLATFORMS.indexOf(p))
|
|
316
|
+
active.sort((a, b) => rank(a.platform) - rank(b.platform))
|
|
317
|
+
return active
|
|
318
|
+
}
|
|
319
|
+
|
|
302
320
|
export async function callTool(name, args, opts = {}) {
|
|
303
321
|
const apiBase = opts.apiBase || DEFAULT_API_BASE
|
|
304
322
|
const timeoutMs = opts.fetchTimeout ?? DEFAULT_FETCH_TIMEOUT
|
|
@@ -472,7 +490,8 @@ export async function callTool(name, args, opts = {}) {
|
|
|
472
490
|
})
|
|
473
491
|
let ack
|
|
474
492
|
try { ack = await res.json() } catch { ack = { status: 'error', detail: `HTTP ${res.status} (non-JSON response)` } }
|
|
475
|
-
|
|
493
|
+
const ranked = !!(res.ok && ack.verification_tier === 'verified' && ack.persisted === true)
|
|
494
|
+
return { ...c, card, ranked, submission: { ...stamp, httpStatus: res.status, ranked, ...ack } }
|
|
476
495
|
}
|
|
477
496
|
|
|
478
497
|
if (name === 'tokenpull') {
|
|
@@ -512,7 +531,10 @@ export async function callTool(name, args, opts = {}) {
|
|
|
512
531
|
})
|
|
513
532
|
let ack
|
|
514
533
|
try { ack = await res.json() } catch { ack = { status: 'error', detail: `HTTP ${res.status} (non-JSON)` } }
|
|
515
|
-
|
|
534
|
+
// ranked = actually on the board (verified + persisted), not just received — parity
|
|
535
|
+
// with submit_verified (submit.mjs). An unenrolled/revoked device gets 202 but is NEVER ranked.
|
|
536
|
+
const ranked = !!(res.ok && ack.verification_tier === 'verified' && ack.persisted === true)
|
|
537
|
+
out.push({ window: w.window, pillars: w.pillars, cascade: c, card, ranked, submission: { ...stamp, httpStatus: res.status, ranked, ...ack } })
|
|
516
538
|
}
|
|
517
539
|
return { platform: pulled.platform, codename: codename || null, generatedAt: pulled.generatedAt, windows: out }
|
|
518
540
|
}
|
package/tui.mjs
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* - Dashboard + Compare load once, refresh on [R].
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { callTool, DEFAULT_API_BASE } from './tools.mjs'
|
|
15
|
+
import { callTool, DEFAULT_API_BASE, pullActivePlatforms } from './tools.mjs'
|
|
16
|
+
import { ALL_PLATFORMS } from './adapters.mjs'
|
|
16
17
|
import { freshVerifierPillars } from './tokenpull.mjs'
|
|
17
18
|
import { isSignedIn, isCodeChar } from './connect.mjs'
|
|
18
19
|
import { loadIdentity, clearIdentity } from './keystore.mjs'
|
|
@@ -231,54 +232,33 @@ function sparkline(values) {
|
|
|
231
232
|
// still used by the `tokenpull_compare` MCP tool.)
|
|
232
233
|
|
|
233
234
|
async function loadDashboardData() {
|
|
234
|
-
const { tokenpullAny } = await import('./tokenpull.mjs')
|
|
235
|
-
const ALL_PLATFORMS = [
|
|
236
|
-
'claude','codex','amp','gemini','kimi','qwen','goose','kilo',
|
|
237
|
-
'hermes','droid','codebuff','copilot','openclaw','pi',
|
|
238
|
-
]
|
|
239
235
|
const boardPromise = Promise.race([
|
|
240
236
|
fetch(`${DEFAULT_API_BASE}/api/v1/leaderboard?window=30d&metric=yield_`, { headers: { accept: 'application/json' } })
|
|
241
237
|
.then(r => r.ok ? r.json() : null).catch(() => null),
|
|
242
238
|
new Promise(r => setTimeout(() => r(null), 5000)),
|
|
243
239
|
])
|
|
244
240
|
|
|
245
|
-
// FIX 0: progressive load
|
|
246
|
-
// then fill the
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
const
|
|
241
|
+
// FIX 0 + unify (0.14): progressive load via the SHARED loader (pullActivePlatforms) — paint the
|
|
242
|
+
// primary platform (claude) FAST, then fill the rest in fillDashboardRest. This is the SAME loader
|
|
243
|
+
// `me` and `watch` use, so the three views can't drift apart. (Verifier comparison still runs
|
|
244
|
+
// FRESH + on-demand in the Compare tab — see loadCompareData — never on this Dashboard path.)
|
|
245
|
+
const active = await pullActivePlatforms({ platforms: ['claude'] }).catch(() => [])
|
|
250
246
|
|
|
251
|
-
const active = []
|
|
252
|
-
if (claudeResult) {
|
|
253
|
-
const all = claudeResult.windows?.find(w => w.window === 'all')
|
|
254
|
-
if (all && (all.pillars.input ?? 0) + (all.pillars.output ?? 0) > 0) active.push(claudeResult)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// FIX A1 (2026-06-27): removed the 14× SYNCHRONOUS ccusage `verifierMap` build (measured 39,816 ms)
|
|
258
|
-
// that blocked the very first Dashboard paint — and the render never read `verifierMap` or `tdPillars`.
|
|
259
|
-
// (Also dropped the discarded second `tokenDashPillars()` call above.) Verifier comparison
|
|
260
|
-
// (ccusage / tokscale / token-dashboard) now runs FRESH + on-demand in the Compare tab, scoped to
|
|
261
|
-
// active platforms — see loadCompareData. Dashboard paints from tokenpull in ~2s.
|
|
262
247
|
const boardData = await boardPromise
|
|
263
|
-
return { active, verifierMap: {}, tdPillars: null, boardData, _loading: true, _remaining: ALL_PLATFORMS.
|
|
248
|
+
return { active, verifierMap: {}, tdPillars: null, boardData, _loading: true, _remaining: ALL_PLATFORMS.filter(p => p !== 'claude') }
|
|
264
249
|
}
|
|
265
250
|
|
|
266
251
|
/** FIX 0: fill the remaining platforms after the primary render. Mutates dashData.active. */
|
|
267
252
|
async function fillDashboardRest(dashData) {
|
|
268
253
|
if (!dashData?._remaining) return false
|
|
269
|
-
|
|
270
|
-
const rest = await
|
|
271
|
-
for (const
|
|
272
|
-
if (r.status !== 'fulfilled') continue
|
|
273
|
-
const d = r.value
|
|
274
|
-
const all = d.windows?.find(w => w.window === 'all')
|
|
275
|
-
if (!all) continue
|
|
276
|
-
if ((all.pillars.input ?? 0) + (all.pillars.output ?? 0) === 0) continue
|
|
254
|
+
// Unify: fill the remaining platforms through the SAME shared loader.
|
|
255
|
+
const rest = await pullActivePlatforms({ platforms: dashData._remaining }).catch(() => [])
|
|
256
|
+
for (const d of rest) {
|
|
277
257
|
if (!dashData.active.find(a => a.platform === d.platform)) dashData.active.push(d)
|
|
278
258
|
}
|
|
279
|
-
//
|
|
280
|
-
const
|
|
281
|
-
dashData.active.sort((a, b) =>
|
|
259
|
+
// stable display order: claude → codex → rest (matches the shared loader)
|
|
260
|
+
const rank = (p) => (p === 'claude' ? -2 : p === 'codex' ? -1 : ALL_PLATFORMS.indexOf(p))
|
|
261
|
+
dashData.active.sort((a, b) => rank(a.platform) - rank(b.platform))
|
|
282
262
|
dashData._loading = false
|
|
283
263
|
dashData._remaining = null
|
|
284
264
|
return true
|