sigrank 1.0.40 → 1.0.41

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.
@@ -42,13 +42,16 @@
42
42
  */
43
43
 
44
44
  import { readdir, readFile } from "node:fs/promises";
45
+ import { existsSync } from "node:fs";
45
46
  import { join, dirname } from "node:path";
46
47
  import { homedir } from "node:os";
47
48
  import { execFile as execFileCb } from "node:child_process";
48
49
  import { promisify } from "node:util";
50
+ import { fileURLToPath } from "node:url";
49
51
  import { cachedOmpScan, ompCacheEnabled } from "../omp-cache.mjs";
50
52
 
51
53
  const execFileP = promisify(execFileCb);
54
+ const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
52
55
  const DAY_MS = 86_400_000; // shared with tokenpull.mjs but kept local to avoid circular import
53
56
 
54
57
  // ── File-system helpers ───────────────────────────────────────────────────────
@@ -1075,6 +1078,96 @@ export const proxyAdapter = {
1075
1078
  },
1076
1079
  };
1077
1080
 
1081
+
1082
+ // ── ccusage-backed adapters ──────────────────────────────────────────────────
1083
+ // ccusage maintains source-specific readers for these platforms and normalizes
1084
+ // its daily JSON output into the same four token buckets SigRank requires.
1085
+ // Keep these as thin adapters instead of duplicating upstream parsers here.
1086
+ // The bundled dependency is preferred; PATH is a fallback for development.
1087
+ async function execCcusage(args) {
1088
+ const localEntrypoint = join(
1089
+ PACKAGE_ROOT,
1090
+ "node_modules",
1091
+ "ccusage",
1092
+ "src",
1093
+ "cli.js",
1094
+ );
1095
+ const options = {
1096
+ timeout: 30_000,
1097
+ maxBuffer: 64 * 1024 * 1024,
1098
+ };
1099
+ if (existsSync(localEntrypoint)) {
1100
+ return execFileP(process.execPath, [localEntrypoint, ...args], options);
1101
+ }
1102
+ return execFileP("ccusage", args, options);
1103
+ }
1104
+
1105
+ function makeCcusageAdapter(platform) {
1106
+ return {
1107
+ platform,
1108
+ defaultRoot: () => homedir(),
1109
+ async *messages() {
1110
+ let data;
1111
+ try {
1112
+ const { stdout } = await execCcusage([
1113
+ platform,
1114
+ "daily",
1115
+ "--json",
1116
+ "--no-cost",
1117
+ ]);
1118
+ data = JSON.parse(stdout || "{}");
1119
+ } catch {
1120
+ return;
1121
+ }
1122
+
1123
+ const rows = Array.isArray(data?.daily)
1124
+ ? data.daily
1125
+ : Array.isArray(data?.data)
1126
+ ? data.data
1127
+ : Array.isArray(data)
1128
+ ? data
1129
+ : [];
1130
+
1131
+ for (const row of rows) {
1132
+ if (!row || typeof row !== "object") continue;
1133
+ const date = row.date || row.day;
1134
+ if (!date) continue;
1135
+ const input = Number(row.inputTokens ?? row.input_tokens ?? 0);
1136
+ const output = Number(row.outputTokens ?? row.output_tokens ?? 0);
1137
+ const cacheCreate = Number(
1138
+ row.cacheCreationTokens ?? row.cache_create_tokens ?? 0,
1139
+ );
1140
+ const cacheRead = Number(
1141
+ row.cacheReadTokens ?? row.cache_read_tokens ?? 0,
1142
+ );
1143
+ if (
1144
+ ![input, output, cacheCreate, cacheRead].every(
1145
+ (n) => Number.isFinite(n) && n >= 0,
1146
+ )
1147
+ ) {
1148
+ continue;
1149
+ }
1150
+ if (input + output + cacheCreate + cacheRead === 0) continue;
1151
+
1152
+ yield {
1153
+ id: `${platform}:${date}`,
1154
+ sid: null,
1155
+ ts: `${date}T12:00:00.000Z`,
1156
+ input,
1157
+ output,
1158
+ cacheCreate,
1159
+ cacheRead,
1160
+ file: `ccusage:${platform}`,
1161
+ };
1162
+ }
1163
+ },
1164
+ };
1165
+ }
1166
+
1167
+ export const antigravityAdapter = makeCcusageAdapter("antigravity");
1168
+ export const grokAdapter = makeCcusageAdapter("grok");
1169
+ export const zcodeAdapter = makeCcusageAdapter("zcode");
1170
+
1078
1171
  // ── Registry ──────────────────────────────────────────────────────────────────
1079
1172
  /** All non-Claude, non-Codex adapters keyed by platform ID. */
1080
1173
  export const ADAPTERS = {
@@ -1095,6 +1188,9 @@ export const ADAPTERS = {
1095
1188
  other: otherAdapter,
1096
1189
  omp: ompAdapter,
1097
1190
  proxy: proxyAdapter,
1191
+ antigravity: antigravityAdapter,
1192
+ grok: grokAdapter,
1193
+ zcode: zcodeAdapter,
1098
1194
  };
1099
1195
 
1100
- export const ALL_PLATFORMS = Object.keys(ADAPTERS).concat(["claude", "codex", "grok"]);
1196
+ export const ALL_PLATFORMS = Object.keys(ADAPTERS).concat(["claude", "codex"]);
@@ -18,19 +18,20 @@
18
18
  import { readdir, readFile } from "node:fs/promises";
19
19
  import { existsSync } from "node:fs";
20
20
  import { execFile } from "node:child_process";
21
- import { join, dirname } from "node:path";
21
+ import { join, dirname, delimiter } from "node:path";
22
22
  import { homedir } from "node:os";
23
23
  import { fileURLToPath } from "node:url";
24
24
  import { ADAPTERS, walkFiles } from "./index.mjs";
25
+ import { TOKSCALE_CLIENT_MAP } from "../lib/constants.mjs";
25
26
 
26
27
  const DAY_MS = 86_400_000;
27
28
 
28
29
  // Resolve the package root for finding bundled binaries (ccusage, tokscale, etc.)
29
- const _pkgRoot = join(dirname(fileURLToPath(import.meta.url)));
30
+ const _pkgRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
30
31
  const _localBin = join(_pkgRoot, "node_modules", ".bin");
31
32
  // Prepend local node_modules/.bin to PATH so bundled deps are found even when
32
33
  // not globally installed (e.g., npx sigrank, local dev).
33
- const _envPath = `${_localBin}${process.env.PATH ? ":" + process.env.PATH : ""}`;
34
+ const _envPath = `${_localBin}${process.env.PATH ? delimiter + process.env.PATH : ""}`;
34
35
 
35
36
  // ASYNC FIX (2026-06-27): execFile wrapped in a Promise — replaces execSync in the
36
37
  // fresh verifier readers. execSync blocks the entire Node event loop (no key handling,
@@ -522,14 +523,17 @@ async function _freshTokscale(platform = "claude") {
522
523
  : Array.isArray(data)
523
524
  ? data
524
525
  : [];
525
- const rows = entries.filter(
526
- (e) =>
527
- e &&
528
- e.client === platform &&
529
- e.model !== "<synthetic>" &&
530
- e.model !== "unknown" &&
531
- ((Number(e.input) || 0) > 0 || (Number(e.output) || 0) > 0),
532
- );
526
+ const rows = entries.filter((e) => {
527
+ if (!e || e.model === "<synthetic>" || e.model === "unknown") return false;
528
+ const mapped = Object.prototype.hasOwnProperty.call(
529
+ TOKSCALE_CLIENT_MAP,
530
+ e.client,
531
+ )
532
+ ? TOKSCALE_CLIENT_MAP[e.client]
533
+ : e.client;
534
+ if (mapped !== platform) return false;
535
+ return (Number(e.input) || 0) > 0 || (Number(e.output) || 0) > 0;
536
+ });
533
537
  if (!rows.length) return null;
534
538
  const acc = rows.reduce(
535
539
  (a, e) => ({
package/lib/constants.mjs CHANGED
@@ -68,8 +68,9 @@ export const TOKSCALE_CLIENT_MAP = {
68
68
  roocode: "other",
69
69
  mux: "other",
70
70
  crush: "other",
71
- antigravity: "other",
72
- "antigravity-cli": "other",
71
+ antigravity: "antigravity",
72
+ "antigravity-cli": "antigravity",
73
+ "antigravity-extension": "antigravity",
73
74
  zed: "other",
74
75
  kiro: "other",
75
76
  trae: "other",
@@ -77,12 +78,12 @@ export const TOKSCALE_CLIENT_MAP = {
77
78
  cline: "other",
78
79
  "9router": "other",
79
80
  gjc: "other",
80
- grok: "other",
81
+ grok: "grok",
81
82
  jcode: "other",
82
83
  commandcode: "other",
83
84
  micode: "other",
84
85
  junie: "other",
85
- zcode: "other",
86
+ zcode: "zcode",
86
87
  opencodereview: "other",
87
88
  codebuddy: "other",
88
89
  workbuddy: "other",
@@ -92,25 +93,27 @@ export const TOKSCALE_CLIENT_MAP = {
92
93
  reasonix: "other",
93
94
  "prime-agent": "other",
94
95
  freebuff: "other",
96
+ cherrystudio: "other",
97
+ dsh: "other",
98
+ mcode: "other",
99
+ fx: "other",
100
+ lmstudio: "other",
101
+ unsloth: "other",
102
+ hindsight: "other",
103
+ "micode-desktop": "other",
104
+ muse: "other",
95
105
  synthetic: null,
96
106
  };
97
107
 
98
108
  /**
99
- * Platforms whose local data tokscale CANNOT see, so `tokscale models --json`
100
- * never lists them as an active client. pullActivePlatforms() auto-detect must
101
- * probe these directly or their cascade row can never appear, even with GBs of
102
- * native local telemetry (that was exactly the omp bug: tokscale reports
103
- * claude/codex/copilot/gemini/grok/kimi/kiro/opencode/pi and nothing else, so
104
- * a tokscale-detected machine silently dropped oh-my-pi entirely).
109
+ * Platforms whose local data the pinned tokscale release cannot discover.
105
110
  *
106
- * The adapter pull itself filters out platforms with no data, so probing a
107
- * platform the operator doesn't use costs one cheap missing-directory walk.
108
- *
109
- * As of 2026-08-08: oh-my-pi. Delete an entry once tokscale reports that client
110
- * (TOKSCALE_CLIENT_MAP already maps the `omp` / `oh-my-pi` client names for
111
- * when that happens).
111
+ * Tokscale 4.17.0 includes oh-my-pi (`omp`) directly, so there are currently
112
+ * no blind platforms that need forced probing. Keep the hook because future
113
+ * platform-specific discovery gaps can still be added here without changing
114
+ * pullActivePlatforms().
112
115
  */
113
- export const TOKSCALE_BLIND_PLATFORMS = ["omp"];
116
+ export const TOKSCALE_BLIND_PLATFORMS = [];
114
117
 
115
118
  /**
116
119
  * The "actually on the board" predicate. A submission is ranked only when the
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/anthropics/mcpb/main/schemas/mcpb-manifest-v0.4.schema.json",
3
3
  "manifest_version": "0.4",
4
4
  "name": "sigrank",
5
- "version": "1.0.39",
5
+ "version": "1.0.40",
6
6
  "description": "Ranks AI coding operators by token cascade efficiency (Yield = cache_read \u00d7 output / input\u00b2). 25 tools including SigRank Standard record, yield calculation, local token usage pulls, signed submission, and tokscale-powered analytics (market share, developer profiles, model trends, cost analysis, device profiles, MCP usage, competitive intelligence). Privacy-first: runs locally, only four token counts leave the machine.",
7
7
  "author": {
8
8
  "name": "SunrisesIllNeverSee",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigrank",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
5
5
  "description": "Upsilon measurement engine distributed through the sigrank CLI/MCP compatibility package, with SigRank public leaderboard tools.",
6
6
  "mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
@@ -29,7 +29,7 @@ import { cascade, classify, CLASS_TIERS, UNCLASSED, tierOf } from "../cascade.mj
29
29
  import { ALL_PLATFORMS } from "../adapters.mjs";
30
30
  import { ensureIdentity, keystorePath } from "../keystore.mjs";
31
31
  import { submitSignedWindow } from "../submit.mjs";
32
- import { LEADERBOARD_METRIC } from "../lib/constants.mjs";
32
+ import { LEADERBOARD_METRIC, TOKSCALE_CLIENT_MAP } from "../lib/constants.mjs";
33
33
  import { execFile } from "child_process";
34
34
  import { existsSync, readFileSync } from "fs";
35
35
  import os from "os";
@@ -37,9 +37,9 @@ import path from "path";
37
37
  import { fileURLToPath } from "url";
38
38
 
39
39
  // Resolve local node_modules/.bin for bundled deps (ccusage, tokscale)
40
- const _pkgRoot = path.dirname(fileURLToPath(import.meta.url));
40
+ const _pkgRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
41
41
  const _localBin = path.join(_pkgRoot, "node_modules", ".bin");
42
- const _envPath = `${_localBin}${process.env.PATH ? ":" + process.env.PATH : ""}`;
42
+ const _envPath = `${_localBin}${process.env.PATH ? path.delimiter + process.env.PATH : ""}`;
43
43
 
44
44
  // ASYNC FIX (2026-06-27): execFile wrapped in a Promise — replaces execSync for
45
45
  // defense-in-depth (shell injection prevention). execFile passes args as an
@@ -429,14 +429,17 @@ async function tokscalePillars(platform = "claude") {
429
429
  : Array.isArray(data)
430
430
  ? data
431
431
  : [];
432
- const rows = entries.filter(
433
- (e) =>
434
- e &&
435
- e.client === platform &&
436
- e.model !== "<synthetic>" &&
437
- e.model !== "unknown" &&
438
- ((Number(e.input) || 0) > 0 || (Number(e.output) || 0) > 0),
439
- );
432
+ const rows = entries.filter((e) => {
433
+ if (!e || e.model === "<synthetic>" || e.model === "unknown") return false;
434
+ const mapped = Object.prototype.hasOwnProperty.call(
435
+ TOKSCALE_CLIENT_MAP,
436
+ e.client,
437
+ )
438
+ ? TOKSCALE_CLIENT_MAP[e.client]
439
+ : e.client;
440
+ if (mapped !== platform) return false;
441
+ return (Number(e.input) || 0) > 0 || (Number(e.output) || 0) > 0;
442
+ });
440
443
  if (rows.length) {
441
444
  const p = rows.reduce(
442
445
  (acc, e) => ({
@@ -458,13 +461,16 @@ async function tokscalePillars(platform = "claude") {
458
461
  try {
459
462
  const data = JSON.parse(readFileSync(reportPath, "utf8"));
460
463
  const entries = data.entries ?? [];
461
- const rows = entries.filter(
462
- (e) =>
463
- e.client === platform &&
464
- e.model !== "<synthetic>" &&
465
- e.model !== "unknown" &&
466
- (e.input > 0 || e.output > 0),
467
- );
464
+ const rows = entries.filter((e) => {
465
+ if (!e || e.model === "<synthetic>" || e.model === "unknown") return false;
466
+ const mapped = Object.prototype.hasOwnProperty.call(
467
+ TOKSCALE_CLIENT_MAP,
468
+ e.client,
469
+ )
470
+ ? TOKSCALE_CLIENT_MAP[e.client]
471
+ : e.client;
472
+ return mapped === platform && (e.input > 0 || e.output > 0);
473
+ });
468
474
  if (rows.length === 0) return null;
469
475
  const p = rows.reduce(
470
476
  (acc, e) => ({
@@ -21,7 +21,7 @@ import {
21
21
  // Resolve local node_modules/.bin for bundled deps (ccusage, tokscale)
22
22
  const _pkgRoot = path.dirname(fileURLToPath(import.meta.url));
23
23
  const _localBin = path.join(_pkgRoot, "..", "node_modules", ".bin");
24
- const _envPath = `${_localBin}${process.env.PATH ? ":" + process.env.PATH : ""}`;
24
+ const _envPath = `${_localBin}${process.env.PATH ? path.delimiter + process.env.PATH : ""}`;
25
25
 
26
26
  export const DEFAULT_API_BASE =
27
27
  process.env.SIGRANK_API_BASE || "https://signalaf.com";