@blockrun/clawrouter 0.9.37 → 0.9.39
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/dist/cli.js +60 -49
- package/dist/cli.js.map +1 -1
- package/dist/index.js +73 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1683,8 +1683,6 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1683
1683
|
primary: "moonshot/kimi-k2.5",
|
|
1684
1684
|
// $0.50/$2.40 - best quality/price for simple tasks
|
|
1685
1685
|
fallback: [
|
|
1686
|
-
"minimax/minimax-m2.5",
|
|
1687
|
-
// $0.30/$1.20 - cheap with reasoning
|
|
1688
1686
|
"google/gemini-2.5-flash",
|
|
1689
1687
|
// 1M context, cost-effective
|
|
1690
1688
|
"nvidia/gpt-oss-120b",
|
|
@@ -1696,8 +1694,6 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1696
1694
|
primary: "xai/grok-code-fast-1",
|
|
1697
1695
|
// Code specialist, $0.20/$1.50
|
|
1698
1696
|
fallback: [
|
|
1699
|
-
"minimax/minimax-m2.5",
|
|
1700
|
-
// $0.30/$1.20 - cheap with reasoning
|
|
1701
1697
|
"google/gemini-2.5-flash",
|
|
1702
1698
|
// 1M context, cost-effective
|
|
1703
1699
|
"deepseek/deepseek-chat",
|
|
@@ -1712,10 +1708,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1712
1708
|
"google/gemini-2.5-flash",
|
|
1713
1709
|
// CRITICAL: 1M context, cheap failsafe before expensive models
|
|
1714
1710
|
"google/gemini-2.5-pro",
|
|
1715
|
-
"minimax/minimax-m2.5",
|
|
1716
|
-
// $0.30/$1.20 - cheap with reasoning
|
|
1717
1711
|
"deepseek/deepseek-chat",
|
|
1718
|
-
// Another cheap option
|
|
1719
1712
|
"xai/grok-4-0709",
|
|
1720
1713
|
"openai/gpt-5.2",
|
|
1721
1714
|
// Newer and cheaper input than gpt-4o
|
|
@@ -1727,8 +1720,6 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1727
1720
|
primary: "xai/grok-4-1-fast-reasoning",
|
|
1728
1721
|
// Upgraded Grok 4.1 reasoning $0.20/$0.50
|
|
1729
1722
|
fallback: [
|
|
1730
|
-
"minimax/minimax-m2.5",
|
|
1731
|
-
// $0.30/$1.20 - reasoning capable
|
|
1732
1723
|
"deepseek/deepseek-reasoner",
|
|
1733
1724
|
// Cheap reasoning model
|
|
1734
1725
|
"openai/o4-mini",
|
|
@@ -1742,22 +1733,22 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1742
1733
|
SIMPLE: {
|
|
1743
1734
|
primary: "nvidia/gpt-oss-120b",
|
|
1744
1735
|
// FREE! $0.00/$0.00
|
|
1745
|
-
fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat"
|
|
1736
|
+
fallback: ["google/gemini-2.5-flash", "deepseek/deepseek-chat"]
|
|
1746
1737
|
},
|
|
1747
1738
|
MEDIUM: {
|
|
1748
1739
|
primary: "google/gemini-2.5-flash",
|
|
1749
1740
|
// $0.15/$0.60 - cheapest capable
|
|
1750
|
-
fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b"
|
|
1741
|
+
fallback: ["deepseek/deepseek-chat", "nvidia/gpt-oss-120b"]
|
|
1751
1742
|
},
|
|
1752
1743
|
COMPLEX: {
|
|
1753
1744
|
primary: "google/gemini-2.5-flash",
|
|
1754
1745
|
// $0.15/$0.60 - 1M context handles complexity
|
|
1755
|
-
fallback: ["deepseek/deepseek-chat", "xai/grok-4-0709"
|
|
1746
|
+
fallback: ["deepseek/deepseek-chat", "xai/grok-4-0709"]
|
|
1756
1747
|
},
|
|
1757
1748
|
REASONING: {
|
|
1758
1749
|
primary: "xai/grok-4-1-fast-reasoning",
|
|
1759
|
-
// $0.20/$0.50
|
|
1760
|
-
fallback: ["deepseek/deepseek-reasoner"
|
|
1750
|
+
// $0.20/$0.50
|
|
1751
|
+
fallback: ["deepseek/deepseek-reasoner"]
|
|
1761
1752
|
}
|
|
1762
1753
|
},
|
|
1763
1754
|
// Premium tier configs - best quality (blockrun/premium)
|
|
@@ -1807,32 +1798,18 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1807
1798
|
SIMPLE: {
|
|
1808
1799
|
primary: "moonshot/kimi-k2.5",
|
|
1809
1800
|
// Cheaper than Haiku ($0.5/$2.4 vs $1/$5), larger context
|
|
1810
|
-
fallback: [
|
|
1811
|
-
"minimax/minimax-m2.5",
|
|
1812
|
-
// $0.30/$1.20 - agentic capable, cheaper than kimi
|
|
1813
|
-
"claude-haiku-4.5",
|
|
1814
|
-
"xai/grok-4-1-fast-non-reasoning",
|
|
1815
|
-
"openai/gpt-4o-mini"
|
|
1816
|
-
]
|
|
1801
|
+
fallback: ["claude-haiku-4.5", "xai/grok-4-1-fast-non-reasoning", "openai/gpt-4o-mini"]
|
|
1817
1802
|
},
|
|
1818
1803
|
MEDIUM: {
|
|
1819
1804
|
primary: "xai/grok-code-fast-1",
|
|
1820
1805
|
// Code specialist for agentic coding
|
|
1821
|
-
fallback: [
|
|
1822
|
-
"minimax/minimax-m2.5",
|
|
1823
|
-
// $0.30/$1.20 - agentic capable
|
|
1824
|
-
"moonshot/kimi-k2.5",
|
|
1825
|
-
"claude-haiku-4.5",
|
|
1826
|
-
"claude-sonnet-4"
|
|
1827
|
-
]
|
|
1806
|
+
fallback: ["moonshot/kimi-k2.5", "claude-haiku-4.5", "claude-sonnet-4"]
|
|
1828
1807
|
},
|
|
1829
1808
|
COMPLEX: {
|
|
1830
1809
|
primary: "claude-sonnet-4",
|
|
1831
1810
|
fallback: [
|
|
1832
1811
|
"claude-opus-4",
|
|
1833
1812
|
// Latest Opus - best agentic
|
|
1834
|
-
"minimax/minimax-m2.5",
|
|
1835
|
-
// $0.30/$1.20 - cheap agentic fallback
|
|
1836
1813
|
"openai/gpt-5.2",
|
|
1837
1814
|
"google/gemini-3-pro-preview",
|
|
1838
1815
|
"xai/grok-4-0709"
|
|
@@ -1841,13 +1818,7 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1841
1818
|
REASONING: {
|
|
1842
1819
|
primary: "claude-sonnet-4",
|
|
1843
1820
|
// Strong tool use + reasoning for agentic tasks
|
|
1844
|
-
fallback: [
|
|
1845
|
-
"claude-opus-4",
|
|
1846
|
-
"minimax/minimax-m2.5",
|
|
1847
|
-
// $0.30/$1.20 - reasoning + agentic
|
|
1848
|
-
"xai/grok-4-1-fast-reasoning",
|
|
1849
|
-
"deepseek/deepseek-reasoner"
|
|
1850
|
-
]
|
|
1821
|
+
fallback: ["claude-opus-4", "xai/grok-4-1-fast-reasoning", "deepseek/deepseek-reasoner"]
|
|
1851
1822
|
}
|
|
1852
1823
|
},
|
|
1853
1824
|
overrides: {
|
|
@@ -1951,7 +1922,33 @@ async function logUsage(entry) {
|
|
|
1951
1922
|
}
|
|
1952
1923
|
|
|
1953
1924
|
// src/stats.ts
|
|
1954
|
-
import {
|
|
1925
|
+
import { readdir } from "fs/promises";
|
|
1926
|
+
|
|
1927
|
+
// src/fs-read.ts
|
|
1928
|
+
import { open } from "fs/promises";
|
|
1929
|
+
import { openSync, readSync, closeSync, fstatSync } from "fs";
|
|
1930
|
+
async function readTextFile(filePath) {
|
|
1931
|
+
const fh = await open(filePath, "r");
|
|
1932
|
+
try {
|
|
1933
|
+
const buf = Buffer.alloc((await fh.stat()).size);
|
|
1934
|
+
await fh.read(buf, 0, buf.length, 0);
|
|
1935
|
+
return buf.toString("utf-8");
|
|
1936
|
+
} finally {
|
|
1937
|
+
await fh.close();
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
function readTextFileSync(filePath) {
|
|
1941
|
+
const fd = openSync(filePath, "r");
|
|
1942
|
+
try {
|
|
1943
|
+
const buf = Buffer.alloc(fstatSync(fd).size);
|
|
1944
|
+
readSync(fd, buf);
|
|
1945
|
+
return buf.toString("utf-8");
|
|
1946
|
+
} finally {
|
|
1947
|
+
closeSync(fd);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// src/stats.ts
|
|
1955
1952
|
import { join as join3 } from "path";
|
|
1956
1953
|
import { homedir as homedir2 } from "os";
|
|
1957
1954
|
|
|
@@ -1970,7 +1967,7 @@ var USER_AGENT = `clawrouter/${VERSION}`;
|
|
|
1970
1967
|
var LOG_DIR2 = join3(homedir2(), ".openclaw", "blockrun", "logs");
|
|
1971
1968
|
async function parseLogFile(filePath) {
|
|
1972
1969
|
try {
|
|
1973
|
-
const content = await
|
|
1970
|
+
const content = await readTextFile(filePath);
|
|
1974
1971
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
1975
1972
|
return lines.map((line) => {
|
|
1976
1973
|
const entry = JSON.parse(line);
|
|
@@ -3761,6 +3758,7 @@ var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
|
3761
3758
|
]);
|
|
3762
3759
|
var FREE_MODEL = "nvidia/gpt-oss-120b";
|
|
3763
3760
|
var MAX_MESSAGES = 200;
|
|
3761
|
+
var CONTEXT_LIMIT_KB = 5120;
|
|
3764
3762
|
var HEARTBEAT_INTERVAL_MS = 2e3;
|
|
3765
3763
|
var DEFAULT_REQUEST_TIMEOUT_MS = 18e4;
|
|
3766
3764
|
var MAX_FALLBACK_ATTEMPTS = 5;
|
|
@@ -4147,15 +4145,28 @@ function normalizeMessagesForThinking(messages) {
|
|
|
4147
4145
|
return hasChanges ? normalized : messages;
|
|
4148
4146
|
}
|
|
4149
4147
|
function truncateMessages(messages) {
|
|
4150
|
-
if (!messages || messages.length <= MAX_MESSAGES)
|
|
4148
|
+
if (!messages || messages.length <= MAX_MESSAGES) {
|
|
4149
|
+
return {
|
|
4150
|
+
messages,
|
|
4151
|
+
wasTruncated: false,
|
|
4152
|
+
originalCount: messages?.length ?? 0,
|
|
4153
|
+
truncatedCount: messages?.length ?? 0
|
|
4154
|
+
};
|
|
4155
|
+
}
|
|
4151
4156
|
const systemMsgs = messages.filter((m) => m.role === "system");
|
|
4152
4157
|
const conversationMsgs = messages.filter((m) => m.role !== "system");
|
|
4153
4158
|
const maxConversation = MAX_MESSAGES - systemMsgs.length;
|
|
4154
4159
|
const truncatedConversation = conversationMsgs.slice(-maxConversation);
|
|
4160
|
+
const result = [...systemMsgs, ...truncatedConversation];
|
|
4155
4161
|
console.log(
|
|
4156
|
-
`[ClawRouter] Truncated messages: ${messages.length} \u2192 ${
|
|
4162
|
+
`[ClawRouter] Truncated messages: ${messages.length} \u2192 ${result.length} (kept ${systemMsgs.length} system + ${truncatedConversation.length} recent)`
|
|
4157
4163
|
);
|
|
4158
|
-
return
|
|
4164
|
+
return {
|
|
4165
|
+
messages: result,
|
|
4166
|
+
wasTruncated: true,
|
|
4167
|
+
originalCount: messages.length,
|
|
4168
|
+
truncatedCount: result.length
|
|
4169
|
+
};
|
|
4159
4170
|
}
|
|
4160
4171
|
var KIMI_BLOCK_RE = /<[||][^<>]*begin[^<>]*[||]>[\s\S]*?<[||][^<>]*end[^<>]*[||]>/gi;
|
|
4161
4172
|
var KIMI_TOKEN_RE = /<[||][^<>]*[||]>/g;
|
|
@@ -4488,7 +4499,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
4488
4499
|
parsed.messages = normalizeMessageRoles(parsed.messages);
|
|
4489
4500
|
}
|
|
4490
4501
|
if (Array.isArray(parsed.messages)) {
|
|
4491
|
-
|
|
4502
|
+
const truncationResult = truncateMessages(parsed.messages);
|
|
4503
|
+
parsed.messages = truncationResult.messages;
|
|
4492
4504
|
}
|
|
4493
4505
|
if (Array.isArray(parsed.messages)) {
|
|
4494
4506
|
parsed.messages = sanitizeToolIds(parsed.messages);
|
|
@@ -4562,6 +4574,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4562
4574
|
bodyChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4563
4575
|
}
|
|
4564
4576
|
let body = Buffer.concat(bodyChunks);
|
|
4577
|
+
const originalContextSizeKB = Math.ceil(body.length / 1024);
|
|
4565
4578
|
let routingDecision;
|
|
4566
4579
|
let isStreaming = false;
|
|
4567
4580
|
let modelId = "";
|
|
@@ -4669,7 +4682,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4669
4682
|
const systemPrompt = typeof systemMsg?.content === "string" ? systemMsg.content : void 0;
|
|
4670
4683
|
const tools = parsed.tools;
|
|
4671
4684
|
const hasTools = Array.isArray(tools) && tools.length > 0;
|
|
4672
|
-
if (hasTools) {
|
|
4685
|
+
if (hasTools && tools) {
|
|
4673
4686
|
console.log(
|
|
4674
4687
|
`[ClawRouter] Tools detected (${tools.length}), agentic mode via keywords`
|
|
4675
4688
|
);
|
|
@@ -4814,7 +4827,9 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4814
4827
|
res.writeHead(200, {
|
|
4815
4828
|
"content-type": "text/event-stream",
|
|
4816
4829
|
"cache-control": "no-cache",
|
|
4817
|
-
connection: "keep-alive"
|
|
4830
|
+
connection: "keep-alive",
|
|
4831
|
+
"x-context-used-kb": String(originalContextSizeKB),
|
|
4832
|
+
"x-context-limit-kb": String(CONTEXT_LIMIT_KB)
|
|
4818
4833
|
});
|
|
4819
4834
|
headersSentEarly = true;
|
|
4820
4835
|
safeWrite(res, ": heartbeat\n\n");
|
|
@@ -4977,7 +4992,11 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4977
4992
|
completedAt: Date.now()
|
|
4978
4993
|
});
|
|
4979
4994
|
} else {
|
|
4980
|
-
res.writeHead(errStatus, {
|
|
4995
|
+
res.writeHead(errStatus, {
|
|
4996
|
+
"Content-Type": "application/json",
|
|
4997
|
+
"x-context-used-kb": String(originalContextSizeKB),
|
|
4998
|
+
"x-context-limit-kb": String(CONTEXT_LIMIT_KB)
|
|
4999
|
+
});
|
|
4981
5000
|
res.end(transformedErr);
|
|
4982
5001
|
deduplicator.complete(dedupKey, {
|
|
4983
5002
|
status: errStatus,
|
|
@@ -5103,6 +5122,8 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5103
5122
|
return;
|
|
5104
5123
|
responseHeaders[key] = value;
|
|
5105
5124
|
});
|
|
5125
|
+
responseHeaders["x-context-used-kb"] = String(originalContextSizeKB);
|
|
5126
|
+
responseHeaders["x-context-limit-kb"] = String(CONTEXT_LIMIT_KB);
|
|
5106
5127
|
res.writeHead(upstream.status, responseHeaders);
|
|
5107
5128
|
if (upstream.body) {
|
|
5108
5129
|
const reader = upstream.body.getReader();
|
|
@@ -5197,7 +5218,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
5197
5218
|
}
|
|
5198
5219
|
|
|
5199
5220
|
// src/auth.ts
|
|
5200
|
-
import { writeFile,
|
|
5221
|
+
import { writeFile, mkdir as mkdir2 } from "fs/promises";
|
|
5201
5222
|
import { join as join4 } from "path";
|
|
5202
5223
|
import { homedir as homedir3 } from "os";
|
|
5203
5224
|
import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
|
|
@@ -5205,7 +5226,7 @@ var WALLET_DIR = join4(homedir3(), ".openclaw", "blockrun");
|
|
|
5205
5226
|
var WALLET_FILE = join4(WALLET_DIR, "wallet.key");
|
|
5206
5227
|
async function loadSavedWallet() {
|
|
5207
5228
|
try {
|
|
5208
|
-
const key = (await
|
|
5229
|
+
const key = (await readTextFile(WALLET_FILE)).trim();
|
|
5209
5230
|
if (key.startsWith("0x") && key.length === 66) {
|
|
5210
5231
|
console.log(`[ClawRouter] \u2713 Loaded existing wallet from ${WALLET_FILE}`);
|
|
5211
5232
|
return key;
|
|
@@ -5226,7 +5247,7 @@ async function generateAndSaveWallet() {
|
|
|
5226
5247
|
await mkdir2(WALLET_DIR, { recursive: true });
|
|
5227
5248
|
await writeFile(WALLET_FILE, key + "\n", { mode: 384 });
|
|
5228
5249
|
try {
|
|
5229
|
-
const verification = (await
|
|
5250
|
+
const verification = (await readTextFile(WALLET_FILE)).trim();
|
|
5230
5251
|
if (verification !== key) {
|
|
5231
5252
|
throw new Error("Wallet file verification failed - content mismatch");
|
|
5232
5253
|
}
|
|
@@ -5255,7 +5276,6 @@ async function resolveOrGenerateWalletKey() {
|
|
|
5255
5276
|
|
|
5256
5277
|
// src/index.ts
|
|
5257
5278
|
import {
|
|
5258
|
-
readFileSync,
|
|
5259
5279
|
writeFileSync,
|
|
5260
5280
|
existsSync,
|
|
5261
5281
|
readdirSync,
|
|
@@ -5362,7 +5382,7 @@ function injectModelsConfig(logger) {
|
|
|
5362
5382
|
}
|
|
5363
5383
|
if (existsSync(configPath)) {
|
|
5364
5384
|
try {
|
|
5365
|
-
const content =
|
|
5385
|
+
const content = readTextFileSync(configPath).trim();
|
|
5366
5386
|
if (content) {
|
|
5367
5387
|
config = JSON.parse(content);
|
|
5368
5388
|
} else {
|
|
@@ -5554,7 +5574,7 @@ function injectAuthProfile(logger) {
|
|
|
5554
5574
|
};
|
|
5555
5575
|
if (existsSync(authPath)) {
|
|
5556
5576
|
try {
|
|
5557
|
-
const existing = JSON.parse(
|
|
5577
|
+
const existing = JSON.parse(readTextFileSync(authPath));
|
|
5558
5578
|
if (existing.version && existing.profiles) {
|
|
5559
5579
|
store = existing;
|
|
5560
5580
|
}
|
|
@@ -5673,7 +5693,7 @@ async function createWalletCommand() {
|
|
|
5673
5693
|
let address;
|
|
5674
5694
|
try {
|
|
5675
5695
|
if (existsSync(WALLET_FILE)) {
|
|
5676
|
-
walletKey =
|
|
5696
|
+
walletKey = readTextFileSync(WALLET_FILE).trim();
|
|
5677
5697
|
if (walletKey.startsWith("0x") && walletKey.length === 66) {
|
|
5678
5698
|
const account = privateKeyToAccount4(walletKey);
|
|
5679
5699
|
address = account.address;
|