sigrank 0.0.192 → 0.0.194
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 +1 -1
- package/adapters/index.mjs +16 -12
- package/adapters/tokenpull.mjs +6 -3
- package/package.json +1 -1
- package/presentation/tui.mjs +170 -19
package/README.md
CHANGED
|
@@ -339,7 +339,7 @@ All adapters are token-only (no message content, no cost fields, no credentials)
|
|
|
339
339
|
| ------------------ | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
340
340
|
| Claude Code | ✅ `~/.claude/projects` (recursive, incl. `subagents/`) | Native 4-pillar; dedup by `(session_id, message_id)` — final snapshot wins |
|
|
341
341
|
| Codex | ✅ `~/.codex/sessions` (+ `archived_sessions`) | `input_tokens` incl. cached → `input` + `cacheCreate` split window-level via `io_ratio`; `cacheRead` native (`cached_input_tokens`); `reasoning_output`→output; verified vs ccusage (~1%) |
|
|
342
|
-
| Devin CLI | ✅ `~/.local/share/devin/cli/sessions.db` | SQLite;
|
|
342
|
+
| Devin CLI | ✅ `~/.local/share/devin/cli/sessions.db` | SQLite; native 4-pillar from `metadata.metrics` (`input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens`); `input_tokens` is fresh (excludes cache) |
|
|
343
343
|
| Amp | ✅ `~/.local/share/amp/threads` | Native 4-pillar; per-message |
|
|
344
344
|
| Kimi | ✅ `~/.kimi/sessions` | Native 4-pillar; `StatusUpdate` lines only |
|
|
345
345
|
| pi-agent | ✅ `~/.pi/agent/sessions` | Native 4-pillar; per-message JSONL |
|
package/adapters/index.mjs
CHANGED
|
@@ -744,16 +744,15 @@ export const hermesAdapter = {
|
|
|
744
744
|
|
|
745
745
|
// ── 14. Devin CLI ────────────────────────────────────────────────────────────
|
|
746
746
|
// SQLite: ~/.local/share/devin/cli/sessions.db
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
// prior revision was wrong — tokenpullAny defaults ioRatio to 2.0 for both).
|
|
747
|
+
// Native 4-pillar: metrics.input_tokens is FRESH input (does NOT include cache
|
|
748
|
+
// read or cache creation — verified against live data where input=3 alongside
|
|
749
|
+
// cache_creation_tokens=23500). cache_read_tokens and cache_creation_tokens are
|
|
750
|
+
// separate fields (nullable → treat null as 0). output_tokens includes any
|
|
751
|
+
// reasoning tokens (no separate reasoning field in metrics). No estimation needed.
|
|
753
752
|
export const devinAdapter = {
|
|
754
753
|
platform: "devin",
|
|
755
754
|
defaultRoot: () => join(homedir(), ".local", "share", "devin", "cli"),
|
|
756
|
-
async *
|
|
755
|
+
async *messages(root) {
|
|
757
756
|
for (const r of roots("DEVIN_HOME", root)) {
|
|
758
757
|
const dbPath = join(r, "sessions.db");
|
|
759
758
|
const rows = await sqliteJson(
|
|
@@ -762,6 +761,7 @@ export const devinAdapter = {
|
|
|
762
761
|
json_extract(chat_message, '$.metadata.metrics.input_tokens') as input_tokens,
|
|
763
762
|
json_extract(chat_message, '$.metadata.metrics.output_tokens') as output_tokens,
|
|
764
763
|
json_extract(chat_message, '$.metadata.metrics.cache_read_tokens') as cache_read_tokens,
|
|
764
|
+
json_extract(chat_message, '$.metadata.metrics.cache_creation_tokens') as cache_creation_tokens,
|
|
765
765
|
json_extract(chat_message, '$.metadata.created_at') as created_at
|
|
766
766
|
FROM message_nodes
|
|
767
767
|
WHERE json_extract(chat_message, '$.role') = 'assistant'
|
|
@@ -770,15 +770,19 @@ export const devinAdapter = {
|
|
|
770
770
|
60_000,
|
|
771
771
|
);
|
|
772
772
|
for (const row of rows) {
|
|
773
|
-
const
|
|
774
|
-
const cached = Number(row.cache_read_tokens || 0);
|
|
773
|
+
const input = Number(row.input_tokens || 0);
|
|
775
774
|
const output = Number(row.output_tokens || 0);
|
|
776
|
-
|
|
775
|
+
const cacheRead = Number(row.cache_read_tokens || 0);
|
|
776
|
+
const cacheCreate = Number(row.cache_creation_tokens || 0);
|
|
777
|
+
if (input + output + cacheRead + cacheCreate === 0) continue;
|
|
777
778
|
yield {
|
|
779
|
+
id: `devin:${row.row_id}`,
|
|
780
|
+
sid: row.session_id || null,
|
|
778
781
|
ts: row.created_at || null,
|
|
782
|
+
input,
|
|
779
783
|
output,
|
|
780
|
-
|
|
781
|
-
|
|
784
|
+
cacheCreate,
|
|
785
|
+
cacheRead,
|
|
782
786
|
file: dbPath,
|
|
783
787
|
};
|
|
784
788
|
}
|
package/adapters/tokenpull.mjs
CHANGED
|
@@ -359,9 +359,12 @@ export async function tokenpullCodex({
|
|
|
359
359
|
export async function tokenpullAny(platform, opts = {}) {
|
|
360
360
|
if (!platform || platform === "claude")
|
|
361
361
|
return tokenpull({ adapter: claudeAdapter, ...opts });
|
|
362
|
-
// Codex
|
|
363
|
-
//
|
|
364
|
-
|
|
362
|
+
// Codex: input_tokens includes cache write, so it goes through tokenpullCodex()
|
|
363
|
+
// which splits via ioRatio (Beta from Claude, Alpha 2.0). Devin was previously
|
|
364
|
+
// routed here too, but its metrics object carries native cache_creation_tokens
|
|
365
|
+
// and cache_read_tokens as separate fields with input_tokens as fresh input —
|
|
366
|
+
// no estimation needed. Devin now goes through the standard tokenpull() path.
|
|
367
|
+
if (platform === "codex") {
|
|
365
368
|
let ioRatio = opts.ioRatio || 2.0;
|
|
366
369
|
if (!opts.ioRatio) {
|
|
367
370
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigrank",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.194",
|
|
4
4
|
"author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
|
|
5
5
|
"description": "SigRank MCP server — the yield cascade + live leaderboard as MCP tools any agent can call",
|
|
6
6
|
"mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
|
package/presentation/tui.mjs
CHANGED
|
@@ -29,6 +29,25 @@ import { LEADERBOARD_METRIC } from "../lib/constants.mjs";
|
|
|
29
29
|
// matches cli.mjs; never hardcode, that's what caused the version drift).
|
|
30
30
|
const VERSION = pkgVersion();
|
|
31
31
|
|
|
32
|
+
// Expensive platform readers that the Dashboard only runs on explicit user
|
|
33
|
+
// request. They remain in ALL_PLATFORMS and are still available to me, watch,
|
|
34
|
+
// Compare, and MCP tool calls when the caller explicitly selects the platform.
|
|
35
|
+
export const DASHBOARD_MANUAL_PLATFORMS = Object.freeze(["omp"]);
|
|
36
|
+
export function dashboardAutoPlatforms(platforms = ALL_PLATFORMS) {
|
|
37
|
+
return platforms.filter((p) => !DASHBOARD_MANUAL_PLATFORMS.includes(p));
|
|
38
|
+
}
|
|
39
|
+
export function dashboardEnabledPlatforms({
|
|
40
|
+
includeManual = false,
|
|
41
|
+
platforms = ALL_PLATFORMS,
|
|
42
|
+
} = {}) {
|
|
43
|
+
const automatic = dashboardAutoPlatforms(platforms);
|
|
44
|
+
if (!includeManual) return automatic;
|
|
45
|
+
return [
|
|
46
|
+
...automatic,
|
|
47
|
+
...DASHBOARD_MANUAL_PLATFORMS.filter((p) => platforms.includes(p)),
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
// ── ANSI ───────────────────────────────────────────────────────────────────
|
|
33
52
|
const ESC = "\x1b[";
|
|
34
53
|
const c = {
|
|
@@ -299,7 +318,7 @@ function sparkline(values) {
|
|
|
299
318
|
// (tools.mjs keeps its own `_ccusagePillars`/`_tokscalePillars`/`_tokenDashPillars` —
|
|
300
319
|
// still used by the `tokenpull_compare` MCP tool.)
|
|
301
320
|
|
|
302
|
-
async function loadDashboardData() {
|
|
321
|
+
async function loadDashboardData({ includeOmp = false } = {}) {
|
|
303
322
|
const boardPromise = Promise.race([
|
|
304
323
|
fetch(`${DEFAULT_API_BASE}/api/v1/leaderboard?window=30d&metric=${LEADERBOARD_METRIC}`, {
|
|
305
324
|
headers: { accept: "application/json" },
|
|
@@ -324,25 +343,55 @@ async function loadDashboardData() {
|
|
|
324
343
|
tdPillars: null,
|
|
325
344
|
boardData,
|
|
326
345
|
_loading: true,
|
|
327
|
-
_remaining:
|
|
346
|
+
_remaining: dashboardEnabledPlatforms({
|
|
347
|
+
includeManual: includeOmp,
|
|
348
|
+
}).filter((p) => p !== "claude"),
|
|
349
|
+
_ompAvailable: true,
|
|
350
|
+
_ompEnabled: includeOmp,
|
|
351
|
+
_ompLoading: false,
|
|
352
|
+
_ompScanned: false,
|
|
328
353
|
};
|
|
329
354
|
}
|
|
330
355
|
|
|
331
|
-
|
|
332
|
-
|
|
356
|
+
const dashboardPlatformRank = (p) =>
|
|
357
|
+
p === "claude" ? -2 : p === "codex" ? -1 : ALL_PLATFORMS.indexOf(p);
|
|
358
|
+
|
|
359
|
+
async function pullDashboardPlatform(dashData, platform) {
|
|
360
|
+
try {
|
|
361
|
+
const active = await pullActivePlatforms({ platforms: [platform] });
|
|
362
|
+
for (const d of active) {
|
|
363
|
+
if (!dashData.active.find((a) => a.platform === d.platform))
|
|
364
|
+
dashData.active.push(d);
|
|
365
|
+
}
|
|
366
|
+
dashData.active.sort(
|
|
367
|
+
(a, b) =>
|
|
368
|
+
dashboardPlatformRank(a.platform) - dashboardPlatformRank(b.platform),
|
|
369
|
+
);
|
|
370
|
+
return { ok: true, found: active.some((d) => d.platform === platform) };
|
|
371
|
+
} catch {
|
|
372
|
+
return { ok: false, found: false };
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* FIX 0: fill the remaining platforms after the primary render. Mutates
|
|
378
|
+
* dashData.active and calls onPlatformLoaded after each individual scan so the
|
|
379
|
+
* TUI can paint rows progressively.
|
|
380
|
+
*/
|
|
381
|
+
async function fillDashboardRest(
|
|
382
|
+
dashData,
|
|
383
|
+
{ shouldLoadPlatform, onPlatformStarted, onPlatformLoaded } = {},
|
|
384
|
+
) {
|
|
333
385
|
if (!dashData?._remaining) return false;
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
386
|
+
// Pull one platform at a time. Some adapters scan large session trees, so
|
|
387
|
+
// waiting for the entire batch would prevent the dashboard from updating
|
|
388
|
+
// while the slowest platform is still being read.
|
|
389
|
+
for (const platform of dashData._remaining) {
|
|
390
|
+
if (shouldLoadPlatform && !shouldLoadPlatform(platform)) continue;
|
|
391
|
+
await onPlatformStarted?.(platform);
|
|
392
|
+
const result = await pullDashboardPlatform(dashData, platform);
|
|
393
|
+
await onPlatformLoaded?.(platform, result);
|
|
341
394
|
}
|
|
342
|
-
// stable display order: claude → codex → rest (matches the shared loader)
|
|
343
|
-
const rank = (p) =>
|
|
344
|
-
p === "claude" ? -2 : p === "codex" ? -1 : ALL_PLATFORMS.indexOf(p);
|
|
345
|
-
dashData.active.sort((a, b) => rank(a.platform) - rank(b.platform));
|
|
346
395
|
dashData._loading = false;
|
|
347
396
|
dashData._remaining = null;
|
|
348
397
|
return true;
|
|
@@ -647,7 +696,7 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
|
|
|
647
696
|
// Count is derived from the registry — a hardcoded number drifts every time an
|
|
648
697
|
// adapter is added (it read "14 platforms" while ALL_PLATFORMS held 17).
|
|
649
698
|
emit(
|
|
650
|
-
` ${dim(` reading token logs… (${
|
|
699
|
+
` ${dim(` reading token logs… (${dashboardAutoPlatforms().length} automatic · omp is manual [O])`)}`,
|
|
651
700
|
);
|
|
652
701
|
}
|
|
653
702
|
|
|
@@ -1952,6 +2001,7 @@ export async function runTui({
|
|
|
1952
2001
|
let submitPreview = false; // FIX I1: [S] opens a preview grid before sending (see→confirm→send)
|
|
1953
2002
|
let boardYouOnly = false; // FIX I2: [Y] toggles the Board to "just me" (hybrid model)
|
|
1954
2003
|
let cascadeScroll = 0; // SCROLL-VIEW (2026-06-27): cascade-section scroll offset (Dashboard only)
|
|
2004
|
+
let ompEnabled = false; // Session-only Dashboard toggle; deliberately OFF on every launch.
|
|
1955
2005
|
|
|
1956
2006
|
// ── Redraw (buffered: renders into memory, then paints as a locked frame)
|
|
1957
2007
|
const redraw = async () => {
|
|
@@ -1993,7 +2043,12 @@ export async function runTui({
|
|
|
1993
2043
|
? ` ${dim("↑↓ scroll cascade")}`
|
|
1994
2044
|
: ""
|
|
1995
2045
|
: "";
|
|
1996
|
-
|
|
2046
|
+
const ompHint = !dashData?._ompAvailable
|
|
2047
|
+
? ""
|
|
2048
|
+
: dashData._ompLoading
|
|
2049
|
+
? ` ${dim(ompEnabled ? "omp: on · scanning…" : "omp: off · finishing scan…")}`
|
|
2050
|
+
: ` ${dim("[O]")} omp: ${ompEnabled ? "on" : "off"}`;
|
|
2051
|
+
setFooter(readFooter(`${hint}${scrollHint}${ompHint}${submitHint}`));
|
|
1997
2052
|
} else if (activeTab === 1) {
|
|
1998
2053
|
// Trends
|
|
1999
2054
|
if (!dashData) {
|
|
@@ -2072,7 +2127,7 @@ export async function runTui({
|
|
|
2072
2127
|
// FIX 0: progressive Dashboard load — claude first (fast), render it, THEN fill
|
|
2073
2128
|
// the remaining ALL_PLATFORMS entries in the background + redraw. The user sees their cascade
|
|
2074
2129
|
// within ~1 read instead of a 7s blank "loading dashboard…".
|
|
2075
|
-
dashData = await loadDashboardData().catch((e) => {
|
|
2130
|
+
dashData = await loadDashboardData({ includeOmp: ompEnabled }).catch((e) => {
|
|
2076
2131
|
status = `dashboard error: ${e.message}`;
|
|
2077
2132
|
return null;
|
|
2078
2133
|
});
|
|
@@ -2089,7 +2144,40 @@ export async function runTui({
|
|
|
2089
2144
|
}
|
|
2090
2145
|
[, boardData] = await Promise.all([
|
|
2091
2146
|
dashData?._remaining
|
|
2092
|
-
? fillDashboardRest(dashData
|
|
2147
|
+
? fillDashboardRest(dashData, {
|
|
2148
|
+
shouldLoadPlatform: (platform) =>
|
|
2149
|
+
platform !== "omp" || ompEnabled,
|
|
2150
|
+
onPlatformStarted: async (platform) => {
|
|
2151
|
+
if (platform !== "omp") return;
|
|
2152
|
+
dashData._ompLoading = true;
|
|
2153
|
+
status =
|
|
2154
|
+
"omp is on · scanning history… large histories may take tens of seconds";
|
|
2155
|
+
await redraw();
|
|
2156
|
+
},
|
|
2157
|
+
onPlatformLoaded: async (platform, result) => {
|
|
2158
|
+
if (platform === "omp") {
|
|
2159
|
+
dashData._ompLoading = false;
|
|
2160
|
+
dashData._ompScanned = result.ok && ompEnabled;
|
|
2161
|
+
if (!ompEnabled) {
|
|
2162
|
+
dashData.active = dashData.active.filter(
|
|
2163
|
+
(d) => d.platform !== "omp",
|
|
2164
|
+
);
|
|
2165
|
+
status = "omp: off · completed scan result discarded";
|
|
2166
|
+
} else if (!result.ok) {
|
|
2167
|
+
status = "omp scan failed · switch [O] off, then on to retry";
|
|
2168
|
+
} else if (!result.found) {
|
|
2169
|
+
status = "omp: on · scan complete · no usage data found";
|
|
2170
|
+
} else {
|
|
2171
|
+
status = "omp: on · scan complete";
|
|
2172
|
+
}
|
|
2173
|
+
} else {
|
|
2174
|
+
status = dashData._ompLoading
|
|
2175
|
+
? `${platform} ready · omp scanning…`
|
|
2176
|
+
: `claude ready · ${platform} ready · filling other platforms…`;
|
|
2177
|
+
}
|
|
2178
|
+
await redraw();
|
|
2179
|
+
},
|
|
2180
|
+
})
|
|
2093
2181
|
: Promise.resolve(false),
|
|
2094
2182
|
loadBoardData(boardWindow).catch(() => null),
|
|
2095
2183
|
]);
|
|
@@ -2349,6 +2437,64 @@ export async function runTui({
|
|
|
2349
2437
|
return; // swallow all other keys while the preview is open
|
|
2350
2438
|
}
|
|
2351
2439
|
|
|
2440
|
+
// Session-only omp toggle. OFF removes the row and excludes omp from later
|
|
2441
|
+
// refreshes; ON scans immediately and includes it until the TUI exits.
|
|
2442
|
+
if (activeTab === 0 && k === "o" && dashData?._ompAvailable) {
|
|
2443
|
+
// A filesystem traversal already in progress cannot be cancelled safely.
|
|
2444
|
+
// Switching OFF marks its result for discard; switching back ON waits
|
|
2445
|
+
// until that traversal finishes so two 10 GiB scans never overlap.
|
|
2446
|
+
if (dashData._ompLoading) {
|
|
2447
|
+
if (ompEnabled) {
|
|
2448
|
+
ompEnabled = false;
|
|
2449
|
+
dashData._ompEnabled = false;
|
|
2450
|
+
status = "omp: off · current scan will be discarded when it finishes";
|
|
2451
|
+
} else {
|
|
2452
|
+
status = "omp: off · waiting for the current scan to finish";
|
|
2453
|
+
}
|
|
2454
|
+
await redraw();
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
ompEnabled = !ompEnabled;
|
|
2459
|
+
dashData._ompEnabled = ompEnabled;
|
|
2460
|
+
if (!ompEnabled) {
|
|
2461
|
+
dashData.active = dashData.active.filter((d) => d.platform !== "omp");
|
|
2462
|
+
dashData._ompScanned = false;
|
|
2463
|
+
status = "omp: off · excluded from Dashboard refreshes";
|
|
2464
|
+
await redraw();
|
|
2465
|
+
return;
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
const targetDashData = dashData;
|
|
2469
|
+
targetDashData._ompLoading = true;
|
|
2470
|
+
status =
|
|
2471
|
+
"omp: on · scanning history… large histories may take tens of seconds";
|
|
2472
|
+
await redraw();
|
|
2473
|
+
|
|
2474
|
+
const result = await pullDashboardPlatform(targetDashData, "omp");
|
|
2475
|
+
targetDashData._ompLoading = false;
|
|
2476
|
+
targetDashData._ompScanned = result.ok && ompEnabled;
|
|
2477
|
+
|
|
2478
|
+
if (!ompEnabled) {
|
|
2479
|
+
targetDashData.active = targetDashData.active.filter(
|
|
2480
|
+
(d) => d.platform !== "omp",
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
// [R] may replace dashData while this scan is running. Do not let the
|
|
2485
|
+
// result or status from that stale Dashboard generation overwrite it.
|
|
2486
|
+
if (dashData !== targetDashData) return;
|
|
2487
|
+
status = !ompEnabled
|
|
2488
|
+
? "omp: off · completed scan result discarded"
|
|
2489
|
+
: !result.ok
|
|
2490
|
+
? "omp scan failed · switch [O] off, then on to retry"
|
|
2491
|
+
: result.found
|
|
2492
|
+
? `omp: on · loaded ${new Date().toLocaleTimeString("en-US", { hour12: false })}`
|
|
2493
|
+
: "omp: on · scan complete · no usage data found";
|
|
2494
|
+
await redraw();
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2352
2498
|
// ESC → go back to Dashboard from any tab
|
|
2353
2499
|
if (key === "\x1b" && activeTab !== 0) {
|
|
2354
2500
|
activeTab = 0;
|
|
@@ -2558,6 +2704,11 @@ export async function runTui({
|
|
|
2558
2704
|
}
|
|
2559
2705
|
|
|
2560
2706
|
if (k === "r") {
|
|
2707
|
+
if (dashData?._ompLoading) {
|
|
2708
|
+
status = "omp scan already in progress · refresh is available when it finishes";
|
|
2709
|
+
await redraw();
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2561
2712
|
status = "refreshing…";
|
|
2562
2713
|
cascadeScroll = 0; // SCROLL-VIEW: reset scroll on refresh
|
|
2563
2714
|
// #9: on the Compare tab, [R] re-runs the FRESH verifier pull for the current platform
|