sigrank 0.18.2 → 0.18.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tools.mjs +66 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigrank",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
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/tools.mjs CHANGED
@@ -1383,9 +1383,73 @@ const _lastWatchSubmitAt = new Map();
1383
1383
  // keeps only platforms with real local data, and sorts claude → codex → rest.
1384
1384
  // Pass `platforms` to scope it (e.g. ['claude'] for a fast first paint).
1385
1385
  export async function pullActivePlatforms({ platforms } = {}, opts = {}) {
1386
- const targets = platforms && platforms.length ? platforms : ALL_PLATFORMS;
1386
+ // If the caller specified explicit platforms, use those.
1387
+ if (platforms && platforms.length) {
1388
+ return _pullExplicit(platforms, opts);
1389
+ }
1390
+ // Auto-detect: run tokscale once to discover all active clients, then pull
1391
+ // only those. This is much faster than trying all 17 adapters (most fail
1392
+ // silently on a machine that doesn't have that tool installed).
1393
+ //
1394
+ // Flow: tokscale surfaces ALL clients → map to our platform names →
1395
+ // ccusage is primary for Claude (more accurate) → other platforms use
1396
+ // their adapters. Anything in tokscale NOT in ccusage gets included.
1397
+ const detected = await _tokscaleDetectClients().catch(() => null);
1398
+ if (detected && detected.length > 0) {
1399
+ return _pullExplicit(detected, opts);
1400
+ }
1401
+ // Fallback: tokscale not installed or failed → try all adapters (old behavior)
1402
+ return _pullExplicit(ALL_PLATFORMS, opts);
1403
+ }
1404
+
1405
+ /** Run `tokscale models --json` and return the list of our platform names. */
1406
+ async function _tokscaleDetectClients() {
1407
+ const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
1408
+ const data = JSON.parse(raw);
1409
+ const entries = Array.isArray(data?.entries)
1410
+ ? data.entries
1411
+ : Array.isArray(data)
1412
+ ? data
1413
+ : [];
1414
+ if (!entries.length) return [];
1415
+ // Map tokscale client names → our platform names
1416
+ const TOKSCALE_CLIENT_MAP = {
1417
+ claude: "claude",
1418
+ "devin-cli": "devin",
1419
+ codex: "codex",
1420
+ copilot: "copilot",
1421
+ gemini: "gemini",
1422
+ amp: "amp",
1423
+ kimi: "kimi",
1424
+ qwen: "qwen",
1425
+ goose: "goose",
1426
+ kilo: "kilo",
1427
+ hermes: "hermes",
1428
+ droid: "droid",
1429
+ codebuff: "codebuff",
1430
+ opencode: "opencode",
1431
+ openclaw: "openclaw",
1432
+ pi: "pi",
1433
+ cline: "other", // no native adapter yet
1434
+ "antigravity-cli": "other", // no native adapter yet
1435
+ };
1436
+ const clients = new Set();
1437
+ for (const e of entries) {
1438
+ if (!e || !e.client) continue;
1439
+ if (e.model === "<synthetic>" || e.model === "unknown") continue;
1440
+ const input = Number(e.input) || 0;
1441
+ const output = Number(e.output) || 0;
1442
+ if (input + output === 0) continue;
1443
+ const platform = TOKSCALE_CLIENT_MAP[e.client] || "other";
1444
+ clients.add(platform);
1445
+ }
1446
+ return [...clients];
1447
+ }
1448
+
1449
+ /** Pull a specific set of platforms in parallel, filter to active ones. */
1450
+ async function _pullExplicit(platforms, opts = {}) {
1387
1451
  const settled = await Promise.allSettled(
1388
- targets.map((p) => callTool("tokenpull", { platform: p }, opts)),
1452
+ platforms.map((p) => callTool("tokenpull", { platform: p }, opts)),
1389
1453
  );
1390
1454
  const active = settled
1391
1455
  .filter((r) => r.status === "fulfilled" && r.value)