sigrank 1.0.44 → 1.0.45

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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.44](https://github.com/SunrisesIllNeverSee/sigrank-mcp/releases/tag/v1.0.44) (2026-10-05)
4
+
5
+ ### Features
6
+
7
+ * MCP: explicit opt-in support for protocol revision 2026-07-28 via `serveStdio` — a `server/discover` probe with the modern `_meta` envelope pins the connection to the modern era; `initialize` openings keep the 2025-era protocol unchanged (dual-era serving, `legacy: 'serve'`)
8
+ * Docs: `docs/PROTOCOL-COMPATIBILITY.md` — served eras, negotiation rules, host/client matrix
9
+
10
+ ### Tests
11
+
12
+ * stdio characterization suite 15 → 24: 9 modern-era tests (discover negotiation, era parity, envelope-claim requirement, `-32022` late-initialize, probe→initialize fallback, modern stdout purity + EOF)
13
+
14
+ ## [1.0.43](https://github.com/SunrisesIllNeverSee/sigrank-mcp/releases/tag/v1.0.43) (2026-10-05)
15
+
16
+ ### Breaking Changes
17
+
18
+ * Node.js >= 20 required (was >= 18)
19
+
20
+ ### Features
21
+
22
+ * MCP: migrated `@modelcontextprotocol/sdk` v1 → official v2 packages (`@modelcontextprotocol/server` 2.3.0); zero v1 imports remain
23
+ * `sigrank standard --json`: `sigrank/0.1-draft` now correctly labeled `legacy_alias` under TTEOP authority (`tteop-spec@0.1.5-draft`)
24
+
25
+ ### Fixes
26
+
27
+ * Release workflow: explicit releases only — no auto-publish after CI; real npm OIDC trusted publishing (dead NPM_TOKEN path removed); `--dry-run` + collision pre-gates; `--provenance`
28
+
29
+ ### Tests
30
+
31
+ * 15-test SDK-agnostic stdio behavior freeze locked identical across v1 → v2 (tools/prompts/resources, error classes, stdout purity, routing, EOF) on Node 20/22/26
32
+
3
33
  ## [1.0.37](https://github.com/SunrisesIllNeverSee/sigrank-mcp/releases/tag/v1.0.37) (2026-09-08)
4
34
 
5
35
  ### Fixes
@@ -1,6 +1,6 @@
1
1
  # SigRank MCP Server
2
2
 
3
- > Operator leaderboard measuring users, not models — 24 tools, TUI, signed submit, privacy-first.
3
+ > Operator leaderboard measuring users, not models — 25 tools, TUI, signed submit, privacy-first.
4
4
 
5
5
  SigRank is the first leaderboard that ranks **AI operators** — the humans — by token cascade efficiency, not the models. The yield metric (Υ = cache_read × output / input²) turns raw token telemetry into a competitive ranking on [signalaf.com](https://signalaf.com).
6
6
 
@@ -64,7 +64,7 @@ npx sigrank diagnose # Diagnose cascade inefficiencies
64
64
  npx sigrank improve # Get improvement suggestions
65
65
  ```
66
66
 
67
- ## The 24 MCP Tools
67
+ ## The 25 MCP Tools
68
68
 
69
69
  ### Read-only — pure math (no auth, no filesystem)
70
70
 
@@ -81,39 +81,40 @@ npx sigrank improve # Get improvement suggestions
81
81
 
82
82
  | # | Tool | What it does |
83
83
  |---|------|-------------|
84
- | 7 | `get_leaderboard` | Fetch the live public leaderboard from signalaf.com. Returns all ranked operators sorted by yield, with codename, Υ, leverage, velocity, class, and rank position. |
85
- | 8 | `get_operator` | Get any operator's full profile by codename. Returns detailed metrics + per-window breakdowns (7d/30d/90d/all-time) with the four token pillars per window. |
86
- | 9 | `discover_peers` | Find mentors, peers, and complementary operators for your operator on the leaderboard. |
84
+ | 7 | `get_sigrank_standard_record` | Build the portable `sigrank/0.1-draft` compatibility record (legacy alias under TTEOP authority) from token telemetry. Computes the canonical cascade locally — no network. |
85
+ | 8 | `get_leaderboard` | Fetch the live public leaderboard from signalaf.com. Returns all ranked operators sorted by yield, with codename, Υ, leverage, velocity, class, and rank position. |
86
+ | 9 | `get_operator` | Get any operator's full profile by codename. Returns detailed metrics + per-window breakdowns (7d/30d/90d/all-time) with the four token pillars per window. |
87
+ | 10 | `discover_peers` | Find mentors, peers, and complementary operators for your operator on the leaderboard. |
87
88
 
88
89
  ### Read-only — local filesystem (no auth, no network)
89
90
 
90
91
  | # | Tool | What it does |
91
92
  |---|------|-------------|
92
- | 10 | `tokenpull` | Pull your LOCAL token usage from session logs and rank it across all four windows — zero paste. Reads token counts, not message content. Numbers stay on your machine. 17 platform adapters. |
93
- | 11 | `tokenpull_compare` | Pull token usage from ALL four local sources in parallel (JSONL canon, ccusage CLI, token-dashboard SQLite, tokscale) and compare side-by-side with delta % vs baseline. |
94
- | 12 | `watch_tokenpull` | One poll per call — pulls local token logs and returns current cascade for the watched window. Re-call at your desired cadence to monitor. With `submit:true`, signs + publishes (rate-limited to once per 5 min). |
93
+ | 11 | `tokenpull` | Pull your LOCAL token usage from session logs and rank it across all four windows — zero paste. Reads token counts, not message content. Numbers stay on your machine. 17 platform adapters. |
94
+ | 12 | `tokenpull_compare` | Pull token usage from ALL four local sources in parallel (JSONL canon, ccusage CLI, token-dashboard SQLite, tokscale) and compare side-by-side with delta % vs baseline. |
95
+ | 13 | `watch_tokenpull` | One poll per call — pulls local token logs and returns current cascade for the watched window. Re-call at your desired cadence to monitor. With `submit:true`, signs + publishes (rate-limited to once per 5 min). |
95
96
 
96
97
  ### Write — submit to board (needs enrolled identity)
97
98
 
98
99
  | # | Tool | What it does |
99
100
  |---|------|-------------|
100
- | 13 | `submit_paste` | Ranks a paste of token counts AND publishes to the live board in one call. Computes local preview, then submits to server for authoritative scoring. Returns both local + server results. |
101
- | 14 | `tokenpull_submit` | Pull your LOCAL token usage AND publish to the board in one call — the zero-paste flow. Reads pillars per window, computes cascade, submits each window server-side. Token-only, no prompt content. |
102
- | 15 | `submit_verified` | Publish your LOCAL token runs as a VERIFIED operator — the enrolled, signed path. Reads pillars, builds Schema 1.0 snapshot, ed25519-signs with your device key, POSTs to board. Requires `enroll` first. |
103
- | 16 | `enroll` | Bind THIS device to your SigRank operator. Paste the key from signalaf.com → Settings → "New key". Generates + stores a local ed25519 keypair. Only the PUBLIC key is ever sent. |
101
+ | 14 | `submit_paste` | Ranks a paste of token counts AND publishes to the live board in one call. Computes local preview, then submits to server for authoritative scoring. Returns both local + server results. |
102
+ | 15 | `tokenpull_submit` | Pull your LOCAL token usage AND publish to the board in one call — the zero-paste flow. Reads pillars per window, computes cascade, submits each window server-side. Token-only, no prompt content. |
103
+ | 16 | `submit_verified` | Publish your LOCAL token runs as a VERIFIED operator — the enrolled, signed path. Reads pillars, builds Schema 1.0 snapshot, ed25519-signs with your device key, POSTs to board. Requires `enroll` first. |
104
+ | 17 | `enroll` | Bind THIS device to your SigRank operator. Paste the key from signalaf.com → Settings → "New key". Generates + stores a local ed25519 keypair. Only the PUBLIC key is ever sent. |
104
105
 
105
106
  ### Tokscale analytics — local tokscale data (no auth, no network)
106
107
 
107
108
  | # | Tool | What it does |
108
109
  |---|------|-------------|
109
- | 17 | `tokscale_breakdown` | Per-model token breakdown across platforms (models under threshold → "other"). |
110
- | 18 | `tokscale_market_share` | AI tool market share: each tool's % of tokens/cost/messages, ranked. From local tokscale data. |
111
- | 19 | `tokscale_developer_profile` | Per-developer usage profile across all detected tools: model mix, pillars, sessions, workspaces. Paths redacted. |
112
- | 20 | `tokscale_model_trends` | Model adoption over time: per-model first/last seen, active days, month-by-month adoption curve. |
113
- | 21 | `tokscale_cost_analysis` | Cost per developer per model: cost_per_million_tokens, cost_per_message, share_cost, client rollup. |
114
- | 22 | `tokscale_device_profile` | Device fingerprinting: installed tools, session counts, active days, day-of-week distribution, concurrency. Paths redacted. |
115
- | 23 | `tokscale_mcp_usage` | MCP server usage: detected servers, detection window, active days. |
116
- | 24 | `tokscale_competitive_intel` | Competitive intelligence for any AI tool: rank, model mix, cost efficiency, share vs all competitors. |
110
+ | 18 | `tokscale_breakdown` | Per-model token breakdown across platforms (models under threshold → "other"). |
111
+ | 19 | `tokscale_market_share` | AI tool market share: each tool's % of tokens/cost/messages, ranked. From local tokscale data. |
112
+ | 20 | `tokscale_developer_profile` | Per-developer usage profile across all detected tools: model mix, pillars, sessions, workspaces. Paths redacted. |
113
+ | 21 | `tokscale_model_trends` | Model adoption over time: per-model first/last seen, active days, month-by-month adoption curve. |
114
+ | 22 | `tokscale_cost_analysis` | Cost per developer per model: cost_per_million_tokens, cost_per_message, share_cost, client rollup. |
115
+ | 23 | `tokscale_device_profile` | Device fingerprinting: installed tools, session counts, active days, day-of-week distribution, concurrency. Paths redacted. |
116
+ | 24 | `tokscale_mcp_usage` | MCP server usage: detected servers, detection window, active days. |
117
+ | 25 | `tokscale_competitive_intel` | Competitive intelligence for any AI tool: rank, model mix, cost efficiency, share vs all competitors. |
117
118
 
118
119
  ### Moved to bestuser-router-mcp (5 intent tools)
119
120
 
@@ -160,7 +161,7 @@ The intent-based tools (in bestuser-router-mcp) are designed to map natural-lang
160
161
  | Transport | stdio |
161
162
  | Platform | Cross-platform (Node.js) |
162
163
  | Language | JavaScript |
163
- | Tools | 24 |
164
+ | Tools | 25 |
164
165
  | Auth | None for reads; Supabase anon key for submits |
165
166
  | Category | Developer Tools |
166
167
 
package/README.md CHANGED
@@ -303,6 +303,8 @@ Or if installed globally:
303
303
  | `tokenpull_submit(codename, window?)` | `{codename?, window?}` | `tokenpull` → publish to board. Omit codename for preview |
304
304
  | `tokenpull_compare(platform?)` | `{platform?}` | All four sources side-by-side: tokenpull + ccusage + token-dash + tokscale. Returns pillars, cascade metrics, and delta % vs tokenpull per window |
305
305
  | `rank_windows` | `{platform?, window?}` | Multi-window cascade from local logs |
306
+ | `get_sigrank_standard_record` | `{input, output, cache_write?, cache_read?}` | Builds the portable `sigrank/0.1-draft` compatibility record (legacy alias under TTEOP authority) — canonical cascade, computed locally |
307
+ | `discover_peers` | `{codename?}` | Find mentors, peers, and complementary operators on the leaderboard |
306
308
  | `watch_tokenpull` | `{platform?, interval_s?}` | One cascade snapshot per call (interval_s advisory) |
307
309
  | `submit_verified` | `{window?, platform?, dry_run?}` | THE ranked path: builds + ed25519-signs Schema 1.0 snapshots and POSTs them. `platform:'multi'` sums all active platforms. `dry_run:true` returns the exact payload unsent |
308
310
  | `enroll` | `{code, device_label?}` | Bind this device with a connect code from signalaf.com → Settings |
@@ -310,11 +312,6 @@ Or if installed globally:
310
312
  | `simulate_change` | `{text?, changes}` | Prescriptive "what if" — test proposed pillar changes and see the exact Υ delta + class change before committing |
311
313
  | `suggest_improvements` | `{text?}` | Generates ranked, simulated improvement suggestions — tests strategies and returns them sorted by Υ yield impact |
312
314
  | `self_improve` | `{text?}` | One-click optimize: diagnoses, suggests, and simulates the best change in a single call |
313
- | `get_best_operator(n?)` | `{n?}` | Top N operators with behavioral framing in power-user language. Intent: "who is the best AI user?" |
314
- | `compare_self(codename? \| text?)` | `{codename?}` or `{text?}` | Your metrics vs board averages + power-user assessment + percentile + suggestion. Intent: "how do I measure up?" |
315
- | `compare_operators(a, b)` | `{codename_a, codename_b}` | Side-by-side comparison with behavioral verdict. Intent: "compare operator X vs Y" |
316
- | `describe_power_user()` | `{}` | Static explanation of AI power user archetype + metrics explained. Intent: "what is an AI power user?" |
317
- | `optimize_efficiency(codename? \| text?)` | `{codename?}` or `{text?}` | Ranked efficiency suggestions tied to your cascade shape. Intent: "how can I use AI more efficiently?" |
318
315
  | `tokscale_breakdown(threshold?)` | `{threshold?}` | Per-model token breakdown across platforms (models under threshold → "other") |
319
316
  | `tokscale_market_share()` | `{}` | AI tool market share: each tool's % of tokens/cost/messages, ranked. From local tokscale data |
320
317
  | `tokscale_developer_profile()` | `{}` | Per-developer usage profile across all detected tools: model mix, pillars, sessions, workspaces. Paths redacted |
@@ -324,6 +321,8 @@ Or if installed globally:
324
321
  | `tokscale_mcp_usage()` | `{}` | MCP server usage: detected servers, detection window, active days |
325
322
  | `tokscale_competitive_intel(target)` | `{target}` | Competitive intelligence for any AI tool: rank, model mix, cost efficiency, share vs all competitors |
326
323
 
324
+ Intent tools (`get_best_operator`, `compare_self`, `compare_operators`, `describe_power_user`, `optimize_efficiency`) moved to [bestuser-router-mcp](https://github.com/SunrisesIllNeverSee/bestuser-router-mcp) — `npx bestuser-router-mcp`.
325
+
327
326
  ---
328
327
 
329
328
  ## Cascade math
@@ -22,7 +22,10 @@ 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
+ import {
26
+ TOKSCALE_CLIENT_MAP,
27
+ TOKSCALE_SCAN_TIMEOUT_MS,
28
+ } from "../lib/constants.mjs";
26
29
 
27
30
  const DAY_MS = 86_400_000;
28
31
 
@@ -516,7 +519,11 @@ async function _freshTokendash(platform = "claude") {
516
519
  // Now uses execFile (async) so the TUI stays responsive.
517
520
  async function _freshTokscale(platform = "claude") {
518
521
  try {
519
- const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
522
+ const raw = await execFileAsync(
523
+ "tokscale",
524
+ ["models", "--json"],
525
+ TOKSCALE_SCAN_TIMEOUT_MS,
526
+ );
520
527
  const data = JSON.parse(raw);
521
528
  const entries = Array.isArray(data?.entries)
522
529
  ? data.entries
package/lib/constants.mjs CHANGED
@@ -115,6 +115,18 @@ export const TOKSCALE_CLIENT_MAP = {
115
115
  */
116
116
  export const TOKSCALE_BLIND_PLATFORMS = [];
117
117
 
118
+ /**
119
+ * Timeout for `tokscale` CLI invocations (models/clients/graph/report scans).
120
+ * The scan walks every session log on the machine; on heavy machines it runs
121
+ * ~55–60s and GROWS with log volume, so the old hardcoded 60s ceiling flaked —
122
+ * the subprocess was killed mid-scan and the caller silently got null
123
+ * (TUI Compare showed "no data from tokscale"). 180s gives 3x headroom and
124
+ * stays under tokscale's own nativeTimeoutMs default (300s).
125
+ * Override via SIGRANK_TOKSCALE_TIMEOUT_MS.
126
+ */
127
+ export const TOKSCALE_SCAN_TIMEOUT_MS =
128
+ Number(process.env.SIGRANK_TOKSCALE_TIMEOUT_MS) || 180_000;
129
+
118
130
  /**
119
131
  * The "actually on the board" predicate. A submission is ranked only when the
120
132
  * server returns HTTP ok AND verification_tier === "verified" AND persisted
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.43",
5
+ "version": "1.0.45",
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.44",
3
+ "version": "1.0.45",
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",
@@ -81,10 +81,10 @@
81
81
  "on-device"
82
82
  ],
83
83
  "dependencies": {
84
- "token-cascade": "^0.2.1",
84
+ "token-cascade": "0.2.1",
85
85
  "ccusage": "^20.0.19",
86
86
  "tokscale": "^4.7.0",
87
- "@modelcontextprotocol/server": "^2.3.0"
87
+ "@modelcontextprotocol/server": "2.3.0"
88
88
  },
89
89
  "devDependencies": {
90
90
  "@resvg/resvg-js": "^2.6.2",
@@ -29,7 +29,11 @@ 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, TOKSCALE_CLIENT_MAP } from "../lib/constants.mjs";
32
+ import {
33
+ LEADERBOARD_METRIC,
34
+ TOKSCALE_CLIENT_MAP,
35
+ TOKSCALE_SCAN_TIMEOUT_MS,
36
+ } from "../lib/constants.mjs";
33
37
  import { execFile } from "child_process";
34
38
  import { existsSync, readFileSync } from "fs";
35
39
  import os from "os";
@@ -422,7 +426,11 @@ async function ccusagePillars(platform = "claude") {
422
426
  async function tokscalePillars(platform = "claude") {
423
427
  // Try the bundled tokscale CLI first (fresh data), fall back to saved report file.
424
428
  try {
425
- const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
429
+ const raw = await execFileAsync(
430
+ "tokscale",
431
+ ["models", "--json"],
432
+ TOKSCALE_SCAN_TIMEOUT_MS,
433
+ );
426
434
  const data = JSON.parse(raw);
427
435
  const entries = Array.isArray(data?.entries)
428
436
  ? data.entries
@@ -1444,7 +1444,7 @@ function renderCompare(data) {
1444
1444
  const missing = [];
1445
1445
  if (!cc) missing.push("ccusage (npm i -g ccusage)");
1446
1446
  if (!td) missing.push("token-dashboard (~/.claude/token-dashboard.db)");
1447
- if (!ts) missing.push("tokscale (npm i -g tokscale)");
1447
+ if (!ts) missing.push("tokscale (not installed, or scan timed out)");
1448
1448
  if (missing.length)
1449
1449
  for (const ln of wrapDim(`no data from: ${missing.join(" · ")}`)) emit(ln);
1450
1450
  emit();
@@ -33,7 +33,10 @@ import { existsSync, readFileSync } from "node:fs";
33
33
  import os from "node:os";
34
34
  import path from "node:path";
35
35
  import { fileURLToPath } from "node:url";
36
- import { TOKSCALE_CLIENT_MAP } from "./lib/constants.mjs";
36
+ import {
37
+ TOKSCALE_CLIENT_MAP,
38
+ TOKSCALE_SCAN_TIMEOUT_MS,
39
+ } from "./lib/constants.mjs";
37
40
 
38
41
  // Resolve local node_modules/.bin for the bundled tokscale binary.
39
42
  const _pkgRoot = path.dirname(fileURLToPath(import.meta.url));
@@ -61,13 +64,14 @@ function execFileAsync(cmd, args, timeoutMs) {
61
64
  });
62
65
  }
63
66
 
64
- // tokscale subcommand timeouts. `models` is fast (~5s); `graph` and `report`
65
- // walk every session log and can take ~30s on a heavy machine. 60s ceiling.
66
- const T_MODELS = 60_000;
67
- const T_MONTHLY = 60_000;
68
- const T_CLIENTS = 30_000;
69
- const T_GRAPH = 60_000;
70
- const T_REPORT = 60_000;
67
+ // tokscale subcommand timeouts. `models`/`clients`/`graph`/`report` all walk the
68
+ // session logs — on a heavy machine the scan runs ~55-60s and grows with log
69
+ // volume, so they share TOKSCALE_SCAN_TIMEOUT_MS (was a flat 60s that flaked).
70
+ const T_MODELS = TOKSCALE_SCAN_TIMEOUT_MS;
71
+ const T_MONTHLY = TOKSCALE_SCAN_TIMEOUT_MS;
72
+ const T_CLIENTS = TOKSCALE_SCAN_TIMEOUT_MS;
73
+ const T_GRAPH = TOKSCALE_SCAN_TIMEOUT_MS;
74
+ const T_REPORT = TOKSCALE_SCAN_TIMEOUT_MS;
71
75
 
72
76
  const HOME = os.homedir();
73
77
 
@@ -10,6 +10,7 @@ import { existsSync, readFileSync } from "node:fs";
10
10
  import os from "node:os";
11
11
  import path from "node:path";
12
12
  import { execFileAsync } from "./_helpers.mjs";
13
+ import { TOKSCALE_SCAN_TIMEOUT_MS } from "../lib/constants.mjs";
13
14
 
14
15
  async function _ccusagePillars(platform = "claude") {
15
16
  try {
@@ -87,7 +88,11 @@ async function _tokenDashPillars() {
87
88
  async function _tokscalePillars(platform = "claude") {
88
89
  // Try the bundled tokscale CLI first (fresh data), fall back to saved report file.
89
90
  try {
90
- const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
91
+ const raw = await execFileAsync(
92
+ "tokscale",
93
+ ["models", "--json"],
94
+ TOKSCALE_SCAN_TIMEOUT_MS,
95
+ );
91
96
  const data = JSON.parse(raw);
92
97
  const entries = Array.isArray(data?.entries)
93
98
  ? data.entries
package/tools/index.mjs CHANGED
@@ -12,6 +12,7 @@ import { buildFetch, DEFAULT_API_BASE, DEFAULT_FETCH_TIMEOUT } from "./_helpers.
12
12
  import {
13
13
  TOKSCALE_CLIENT_MAP,
14
14
  TOKSCALE_BLIND_PLATFORMS,
15
+ TOKSCALE_SCAN_TIMEOUT_MS,
15
16
  } from "../lib/constants.mjs";
16
17
 
17
18
  import { TOOL_DEF as rankPasteTool, handleRankPaste } from "./rank-paste.mjs";
@@ -83,7 +84,11 @@ export const TOOLS = [
83
84
 
84
85
  /** Run `tokscale models --json` and return the list of our platform names. */
85
86
  async function _tokscaleDetectClients() {
86
- const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
87
+ const raw = await execFileAsync(
88
+ "tokscale",
89
+ ["models", "--json"],
90
+ TOKSCALE_SCAN_TIMEOUT_MS,
91
+ );
87
92
  const data = JSON.parse(raw);
88
93
  const entries = Array.isArray(data?.entries)
89
94
  ? data.entries
@@ -4,7 +4,10 @@
4
4
 
5
5
  import { execFileAsync } from "./_helpers.mjs";
6
6
  import { ANNOTATIONS } from "./_schemas.mjs";
7
- import { TOKSCALE_CLIENT_MAP } from "../lib/constants.mjs";
7
+ import {
8
+ TOKSCALE_CLIENT_MAP,
9
+ TOKSCALE_SCAN_TIMEOUT_MS,
10
+ } from "../lib/constants.mjs";
8
11
 
9
12
  export const TOOL_DEF = {
10
13
  name: "tokscale_breakdown",
@@ -47,7 +50,11 @@ export const TOOL_DEF = {
47
50
  * Returns: { platform: [{ model, input, output, cacheRead, cacheCreate, pct }] }
48
51
  */
49
52
  export async function tokscaleModelBreakdown(threshold = 0.01) {
50
- const raw = await execFileAsync("tokscale", ["models", "--json"], 60000);
53
+ const raw = await execFileAsync(
54
+ "tokscale",
55
+ ["models", "--json"],
56
+ TOKSCALE_SCAN_TIMEOUT_MS,
57
+ );
51
58
  const data = JSON.parse(raw);
52
59
  const entries = Array.isArray(data?.entries)
53
60
  ? data.entries