@blockrun/clawrouter 0.10.0 → 0.10.1
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 +4 -4
- package/dist/cli.js +312 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -85,12 +85,12 @@ Choose your routing strategy with `/model <profile>`:
|
|
|
85
85
|
Request → Weighted Scorer (15 dimensions) → Tier → Cheapest Model → Response
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
| Tier | ECO Model
|
|
89
|
-
| --------- |
|
|
90
|
-
| SIMPLE | nvidia/gpt-oss-120b (FREE)
|
|
88
|
+
| Tier | ECO Model | AUTO Model | PREMIUM Model |
|
|
89
|
+
| --------- | ----------------------------------- | ---------------------------- | ---------------------------- |
|
|
90
|
+
| SIMPLE | nvidia/gpt-oss-120b (FREE) | kimi-k2.5 ($0.60/$3.00) | kimi-k2.5 |
|
|
91
91
|
| MEDIUM | gemini-2.5-flash-lite ($0.10/$0.40) | grok-code-fast ($0.20/$1.50) | gpt-5.2-codex ($1.75/$14.00) |
|
|
92
92
|
| COMPLEX | gemini-2.5-flash-lite ($0.10/$0.40) | gemini-3.1-pro ($2/$12) | claude-opus-4.6 ($5/$25) |
|
|
93
|
-
| REASONING | grok-4-fast ($0.20/$0.50)
|
|
93
|
+
| REASONING | grok-4-fast ($0.20/$0.50) | grok-4-fast ($0.20/$0.50) | claude-sonnet-4.6 ($3/$15) |
|
|
94
94
|
|
|
95
95
|
**Blended average: $2.05/M** vs $25/M for Claude Opus = **92% savings**
|
|
96
96
|
|
package/dist/cli.js
CHANGED
|
@@ -1277,7 +1277,11 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1277
1277
|
SIMPLE: {
|
|
1278
1278
|
primary: "moonshot/kimi-k2.5",
|
|
1279
1279
|
// $0.60/$3.00 - good for simple coding
|
|
1280
|
-
fallback: [
|
|
1280
|
+
fallback: [
|
|
1281
|
+
"anthropic/claude-haiku-4.5",
|
|
1282
|
+
"google/gemini-2.5-flash-lite",
|
|
1283
|
+
"xai/grok-code-fast-1"
|
|
1284
|
+
]
|
|
1281
1285
|
},
|
|
1282
1286
|
MEDIUM: {
|
|
1283
1287
|
primary: "openai/gpt-5.2-codex",
|
|
@@ -1320,7 +1324,11 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1320
1324
|
SIMPLE: {
|
|
1321
1325
|
primary: "moonshot/kimi-k2.5",
|
|
1322
1326
|
// Cheaper than Haiku ($0.5/$2.4 vs $1/$5), larger context
|
|
1323
|
-
fallback: [
|
|
1327
|
+
fallback: [
|
|
1328
|
+
"anthropic/claude-haiku-4.5",
|
|
1329
|
+
"xai/grok-4-1-fast-non-reasoning",
|
|
1330
|
+
"openai/gpt-4o-mini"
|
|
1331
|
+
]
|
|
1324
1332
|
},
|
|
1325
1333
|
MEDIUM: {
|
|
1326
1334
|
primary: "xai/grok-code-fast-1",
|
|
@@ -1342,7 +1350,11 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
1342
1350
|
REASONING: {
|
|
1343
1351
|
primary: "anthropic/claude-sonnet-4.6",
|
|
1344
1352
|
// Strong tool use + reasoning for agentic tasks
|
|
1345
|
-
fallback: [
|
|
1353
|
+
fallback: [
|
|
1354
|
+
"anthropic/claude-opus-4.6",
|
|
1355
|
+
"xai/grok-4-1-fast-reasoning",
|
|
1356
|
+
"deepseek/deepseek-reasoner"
|
|
1357
|
+
]
|
|
1346
1358
|
}
|
|
1347
1359
|
},
|
|
1348
1360
|
overrides: {
|
|
@@ -5231,6 +5243,282 @@ async function resolveOrGenerateWalletKey() {
|
|
|
5231
5243
|
return { key, address, source: "generated" };
|
|
5232
5244
|
}
|
|
5233
5245
|
|
|
5246
|
+
// src/doctor.ts
|
|
5247
|
+
import { platform, arch, freemem, totalmem } from "os";
|
|
5248
|
+
function formatBytes(bytes) {
|
|
5249
|
+
const gb = bytes / (1024 * 1024 * 1024);
|
|
5250
|
+
return `${gb.toFixed(1)}GB`;
|
|
5251
|
+
}
|
|
5252
|
+
function green(text) {
|
|
5253
|
+
return `\x1B[32m\u2713\x1B[0m ${text}`;
|
|
5254
|
+
}
|
|
5255
|
+
function red(text) {
|
|
5256
|
+
return `\x1B[31m\u2717\x1B[0m ${text}`;
|
|
5257
|
+
}
|
|
5258
|
+
function yellow(text) {
|
|
5259
|
+
return `\x1B[33m\u26A0\x1B[0m ${text}`;
|
|
5260
|
+
}
|
|
5261
|
+
function collectSystemInfo() {
|
|
5262
|
+
return {
|
|
5263
|
+
os: `${platform()} ${arch()}`,
|
|
5264
|
+
arch: arch(),
|
|
5265
|
+
nodeVersion: process.version,
|
|
5266
|
+
memoryFree: formatBytes(freemem()),
|
|
5267
|
+
memoryTotal: formatBytes(totalmem())
|
|
5268
|
+
};
|
|
5269
|
+
}
|
|
5270
|
+
async function collectWalletInfo() {
|
|
5271
|
+
try {
|
|
5272
|
+
const { key, address, source } = await resolveOrGenerateWalletKey();
|
|
5273
|
+
if (!key || !address) {
|
|
5274
|
+
return {
|
|
5275
|
+
exists: false,
|
|
5276
|
+
valid: false,
|
|
5277
|
+
address: null,
|
|
5278
|
+
balance: null,
|
|
5279
|
+
isLow: false,
|
|
5280
|
+
isEmpty: true,
|
|
5281
|
+
source: null
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
const monitor = new BalanceMonitor(address);
|
|
5285
|
+
try {
|
|
5286
|
+
const balanceInfo = await monitor.checkBalance();
|
|
5287
|
+
return {
|
|
5288
|
+
exists: true,
|
|
5289
|
+
valid: true,
|
|
5290
|
+
address,
|
|
5291
|
+
balance: balanceInfo.balanceUSD,
|
|
5292
|
+
isLow: balanceInfo.isLow,
|
|
5293
|
+
isEmpty: balanceInfo.isEmpty,
|
|
5294
|
+
source
|
|
5295
|
+
};
|
|
5296
|
+
} catch {
|
|
5297
|
+
return {
|
|
5298
|
+
exists: true,
|
|
5299
|
+
valid: true,
|
|
5300
|
+
address,
|
|
5301
|
+
balance: null,
|
|
5302
|
+
isLow: false,
|
|
5303
|
+
isEmpty: false,
|
|
5304
|
+
source
|
|
5305
|
+
};
|
|
5306
|
+
}
|
|
5307
|
+
} catch {
|
|
5308
|
+
return {
|
|
5309
|
+
exists: false,
|
|
5310
|
+
valid: false,
|
|
5311
|
+
address: null,
|
|
5312
|
+
balance: null,
|
|
5313
|
+
isLow: false,
|
|
5314
|
+
isEmpty: true,
|
|
5315
|
+
source: null
|
|
5316
|
+
};
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
async function collectNetworkInfo() {
|
|
5320
|
+
const port = getProxyPort();
|
|
5321
|
+
let blockrunReachable = false;
|
|
5322
|
+
let blockrunLatency = null;
|
|
5323
|
+
try {
|
|
5324
|
+
const start = Date.now();
|
|
5325
|
+
const response = await fetch("https://blockrun.ai/api/v1/models", {
|
|
5326
|
+
method: "GET",
|
|
5327
|
+
signal: AbortSignal.timeout(1e4)
|
|
5328
|
+
});
|
|
5329
|
+
blockrunLatency = Date.now() - start;
|
|
5330
|
+
blockrunReachable = response.ok || response.status === 402;
|
|
5331
|
+
} catch {
|
|
5332
|
+
blockrunReachable = false;
|
|
5333
|
+
}
|
|
5334
|
+
let proxyRunning = false;
|
|
5335
|
+
try {
|
|
5336
|
+
const response = await fetch(`http://127.0.0.1:${port}/health`, {
|
|
5337
|
+
method: "GET",
|
|
5338
|
+
signal: AbortSignal.timeout(3e3)
|
|
5339
|
+
});
|
|
5340
|
+
proxyRunning = response.ok;
|
|
5341
|
+
} catch {
|
|
5342
|
+
proxyRunning = false;
|
|
5343
|
+
}
|
|
5344
|
+
return {
|
|
5345
|
+
blockrunApi: { reachable: blockrunReachable, latencyMs: blockrunLatency },
|
|
5346
|
+
localProxy: { running: proxyRunning, port }
|
|
5347
|
+
};
|
|
5348
|
+
}
|
|
5349
|
+
async function collectLogInfo() {
|
|
5350
|
+
try {
|
|
5351
|
+
const stats = await getStats(1);
|
|
5352
|
+
return {
|
|
5353
|
+
requestsLast24h: stats.totalRequests,
|
|
5354
|
+
costLast24h: `$${stats.totalCost.toFixed(4)}`,
|
|
5355
|
+
errorsFound: 0
|
|
5356
|
+
// TODO: parse error logs
|
|
5357
|
+
};
|
|
5358
|
+
} catch {
|
|
5359
|
+
return {
|
|
5360
|
+
requestsLast24h: 0,
|
|
5361
|
+
costLast24h: "$0.00",
|
|
5362
|
+
errorsFound: 0
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
function identifyIssues(result) {
|
|
5367
|
+
const issues = [];
|
|
5368
|
+
if (!result.wallet.exists) {
|
|
5369
|
+
issues.push("No wallet found");
|
|
5370
|
+
}
|
|
5371
|
+
if (result.wallet.isEmpty) {
|
|
5372
|
+
issues.push("Wallet is empty - need to fund with USDC on Base");
|
|
5373
|
+
} else if (result.wallet.isLow) {
|
|
5374
|
+
issues.push("Wallet balance is low (< $1.00)");
|
|
5375
|
+
}
|
|
5376
|
+
if (!result.network.blockrunApi.reachable) {
|
|
5377
|
+
issues.push("Cannot reach BlockRun API - check internet connection");
|
|
5378
|
+
}
|
|
5379
|
+
if (!result.network.localProxy.running) {
|
|
5380
|
+
issues.push(`Local proxy not running on port ${result.network.localProxy.port}`);
|
|
5381
|
+
}
|
|
5382
|
+
return issues;
|
|
5383
|
+
}
|
|
5384
|
+
function printDiagnostics(result) {
|
|
5385
|
+
console.log("\n\u{1F50D} Collecting diagnostics...\n");
|
|
5386
|
+
console.log("System");
|
|
5387
|
+
console.log(` ${green(`OS: ${result.system.os}`)}`);
|
|
5388
|
+
console.log(` ${green(`Node: ${result.system.nodeVersion}`)}`);
|
|
5389
|
+
console.log(` ${green(`Memory: ${result.system.memoryFree} free / ${result.system.memoryTotal}`)}`);
|
|
5390
|
+
console.log("\nWallet");
|
|
5391
|
+
if (result.wallet.exists && result.wallet.valid) {
|
|
5392
|
+
console.log(` ${green(`Key: ${WALLET_FILE} (${result.wallet.source})`)}`);
|
|
5393
|
+
console.log(` ${green(`Address: ${result.wallet.address}`)}`);
|
|
5394
|
+
if (result.wallet.isEmpty) {
|
|
5395
|
+
console.log(` ${red(`Balance: $0.00 - NEED TO FUND!`)}`);
|
|
5396
|
+
} else if (result.wallet.isLow) {
|
|
5397
|
+
console.log(` ${yellow(`Balance: ${result.wallet.balance} (low)`)}`);
|
|
5398
|
+
} else if (result.wallet.balance) {
|
|
5399
|
+
console.log(` ${green(`Balance: ${result.wallet.balance}`)}`);
|
|
5400
|
+
} else {
|
|
5401
|
+
console.log(` ${yellow(`Balance: checking...`)}`);
|
|
5402
|
+
}
|
|
5403
|
+
} else {
|
|
5404
|
+
console.log(` ${red("No wallet found")}`);
|
|
5405
|
+
}
|
|
5406
|
+
console.log("\nNetwork");
|
|
5407
|
+
if (result.network.blockrunApi.reachable) {
|
|
5408
|
+
console.log(` ${green(`BlockRun API: reachable (${result.network.blockrunApi.latencyMs}ms)`)}`);
|
|
5409
|
+
} else {
|
|
5410
|
+
console.log(` ${red("BlockRun API: unreachable")}`);
|
|
5411
|
+
}
|
|
5412
|
+
if (result.network.localProxy.running) {
|
|
5413
|
+
console.log(` ${green(`Local proxy: running on :${result.network.localProxy.port}`)}`);
|
|
5414
|
+
} else {
|
|
5415
|
+
console.log(` ${red(`Local proxy: not running on :${result.network.localProxy.port}`)}`);
|
|
5416
|
+
}
|
|
5417
|
+
console.log("\nLogs");
|
|
5418
|
+
console.log(` ${green(`Last 24h: ${result.logs.requestsLast24h} requests, ${result.logs.costLast24h} spent`)}`);
|
|
5419
|
+
if (result.logs.errorsFound > 0) {
|
|
5420
|
+
console.log(` ${yellow(`${result.logs.errorsFound} errors found in logs`)}`);
|
|
5421
|
+
}
|
|
5422
|
+
if (result.issues.length > 0) {
|
|
5423
|
+
console.log("\n\u26A0\uFE0F Issues Found:");
|
|
5424
|
+
for (const issue of result.issues) {
|
|
5425
|
+
console.log(` \u2022 ${issue}`);
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
async function analyzeWithAI(diagnostics, userQuestion) {
|
|
5430
|
+
if (diagnostics.wallet.isEmpty) {
|
|
5431
|
+
console.log("\n\u{1F4B3} Wallet is empty - cannot call AI for analysis.");
|
|
5432
|
+
console.log(` Fund your wallet with USDC on Base: ${diagnostics.wallet.address}`);
|
|
5433
|
+
console.log(" Get USDC: https://www.coinbase.com/price/usd-coin");
|
|
5434
|
+
console.log(" Bridge to Base: https://bridge.base.org\n");
|
|
5435
|
+
return;
|
|
5436
|
+
}
|
|
5437
|
+
console.log("\n\u{1F4E4} Sending to Claude Opus 4.6...\n");
|
|
5438
|
+
try {
|
|
5439
|
+
const { key } = await resolveOrGenerateWalletKey();
|
|
5440
|
+
const { fetch: paymentFetch } = createPaymentFetch(key);
|
|
5441
|
+
const response = await paymentFetch(
|
|
5442
|
+
"https://blockrun.ai/api/v1/chat/completions",
|
|
5443
|
+
{
|
|
5444
|
+
method: "POST",
|
|
5445
|
+
headers: { "Content-Type": "application/json" },
|
|
5446
|
+
body: JSON.stringify({
|
|
5447
|
+
model: "anthropic/claude-opus-4.6",
|
|
5448
|
+
stream: false,
|
|
5449
|
+
messages: [
|
|
5450
|
+
{
|
|
5451
|
+
role: "system",
|
|
5452
|
+
content: `You are a technical support expert for BlockRun and ClawRouter.
|
|
5453
|
+
Analyze the diagnostics and:
|
|
5454
|
+
1. Identify the root cause of any issues
|
|
5455
|
+
2. Provide specific, actionable fix commands (bash)
|
|
5456
|
+
3. Explain why the issue occurred briefly
|
|
5457
|
+
4. Be concise but thorough
|
|
5458
|
+
5. Format commands in code blocks`
|
|
5459
|
+
},
|
|
5460
|
+
{
|
|
5461
|
+
role: "user",
|
|
5462
|
+
content: userQuestion ? `Here are my system diagnostics:
|
|
5463
|
+
|
|
5464
|
+
${JSON.stringify(diagnostics, null, 2)}
|
|
5465
|
+
|
|
5466
|
+
User's question: ${userQuestion}` : `Here are my system diagnostics:
|
|
5467
|
+
|
|
5468
|
+
${JSON.stringify(diagnostics, null, 2)}
|
|
5469
|
+
|
|
5470
|
+
Please analyze and help me fix any issues.`
|
|
5471
|
+
}
|
|
5472
|
+
],
|
|
5473
|
+
max_tokens: 1e3
|
|
5474
|
+
})
|
|
5475
|
+
},
|
|
5476
|
+
void 0
|
|
5477
|
+
);
|
|
5478
|
+
if (!response.ok) {
|
|
5479
|
+
const text = await response.text();
|
|
5480
|
+
console.log(`Error: ${response.status} - ${text}`);
|
|
5481
|
+
return;
|
|
5482
|
+
}
|
|
5483
|
+
const data = await response.json();
|
|
5484
|
+
const content = data.choices?.[0]?.message?.content;
|
|
5485
|
+
if (content) {
|
|
5486
|
+
console.log("\u{1F916} AI Analysis:\n");
|
|
5487
|
+
console.log(content);
|
|
5488
|
+
console.log();
|
|
5489
|
+
} else {
|
|
5490
|
+
console.log("Error: No response from AI");
|
|
5491
|
+
}
|
|
5492
|
+
} catch (err) {
|
|
5493
|
+
console.log(`
|
|
5494
|
+
Error calling AI: ${err instanceof Error ? err.message : String(err)}`);
|
|
5495
|
+
console.log("Try again or check your wallet balance.\n");
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
async function runDoctor(userQuestion) {
|
|
5499
|
+
console.log(`
|
|
5500
|
+
\u{1FA7A} BlockRun Doctor v${VERSION}
|
|
5501
|
+
`);
|
|
5502
|
+
const [system, wallet, network, logs] = await Promise.all([
|
|
5503
|
+
collectSystemInfo(),
|
|
5504
|
+
collectWalletInfo(),
|
|
5505
|
+
collectNetworkInfo(),
|
|
5506
|
+
collectLogInfo()
|
|
5507
|
+
]);
|
|
5508
|
+
const result = {
|
|
5509
|
+
version: VERSION,
|
|
5510
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5511
|
+
system,
|
|
5512
|
+
wallet,
|
|
5513
|
+
network,
|
|
5514
|
+
logs,
|
|
5515
|
+
issues: []
|
|
5516
|
+
};
|
|
5517
|
+
result.issues = identifyIssues(result);
|
|
5518
|
+
printDiagnostics(result);
|
|
5519
|
+
await analyzeWithAI(result, userQuestion);
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5234
5522
|
// src/cli.ts
|
|
5235
5523
|
function printHelp() {
|
|
5236
5524
|
console.log(`
|
|
@@ -5238,12 +5526,17 @@ ClawRouter v${VERSION} - Smart LLM Router
|
|
|
5238
5526
|
|
|
5239
5527
|
Usage:
|
|
5240
5528
|
clawrouter [options]
|
|
5529
|
+
clawrouter doctor Run AI-powered diagnostics
|
|
5241
5530
|
|
|
5242
5531
|
Options:
|
|
5243
5532
|
--version, -v Show version number
|
|
5244
5533
|
--help, -h Show this help message
|
|
5245
5534
|
--port <number> Port to listen on (default: ${getProxyPort()})
|
|
5246
5535
|
|
|
5536
|
+
Commands:
|
|
5537
|
+
doctor [question] Diagnose issues and get AI-powered fix suggestions
|
|
5538
|
+
Optional: Add your question for targeted help
|
|
5539
|
+
|
|
5247
5540
|
Examples:
|
|
5248
5541
|
# Start standalone proxy (survives gateway restarts)
|
|
5249
5542
|
npx @blockrun/clawrouter
|
|
@@ -5251,6 +5544,12 @@ Examples:
|
|
|
5251
5544
|
# Start on custom port
|
|
5252
5545
|
npx @blockrun/clawrouter --port 9000
|
|
5253
5546
|
|
|
5547
|
+
# Run diagnostics when something isn't working
|
|
5548
|
+
npx @blockrun/clawrouter doctor
|
|
5549
|
+
|
|
5550
|
+
# Ask a specific question
|
|
5551
|
+
npx @blockrun/clawrouter doctor "why is my request failing?"
|
|
5552
|
+
|
|
5254
5553
|
# Production deployment with PM2
|
|
5255
5554
|
pm2 start "npx @blockrun/clawrouter" --name clawrouter
|
|
5256
5555
|
|
|
@@ -5262,13 +5561,15 @@ For more info: https://github.com/BlockRunAI/ClawRouter
|
|
|
5262
5561
|
`);
|
|
5263
5562
|
}
|
|
5264
5563
|
function parseArgs(args) {
|
|
5265
|
-
const result = { version: false, help: false, port: void 0 };
|
|
5564
|
+
const result = { version: false, help: false, doctor: false, port: void 0 };
|
|
5266
5565
|
for (let i = 0; i < args.length; i++) {
|
|
5267
5566
|
const arg = args[i];
|
|
5268
5567
|
if (arg === "--version" || arg === "-v") {
|
|
5269
5568
|
result.version = true;
|
|
5270
5569
|
} else if (arg === "--help" || arg === "-h") {
|
|
5271
5570
|
result.help = true;
|
|
5571
|
+
} else if (arg === "doctor" || arg === "--doctor") {
|
|
5572
|
+
result.doctor = true;
|
|
5272
5573
|
} else if (arg === "--port" && args[i + 1]) {
|
|
5273
5574
|
result.port = parseInt(args[i + 1], 10);
|
|
5274
5575
|
i++;
|
|
@@ -5286,6 +5587,13 @@ async function main() {
|
|
|
5286
5587
|
printHelp();
|
|
5287
5588
|
process.exit(0);
|
|
5288
5589
|
}
|
|
5590
|
+
if (args.doctor) {
|
|
5591
|
+
const rawArgs = process.argv.slice(2);
|
|
5592
|
+
const doctorIndex = rawArgs.findIndex((a) => a === "doctor" || a === "--doctor");
|
|
5593
|
+
const userQuestion = rawArgs.slice(doctorIndex + 1).join(" ").trim() || void 0;
|
|
5594
|
+
await runDoctor(userQuestion);
|
|
5595
|
+
process.exit(0);
|
|
5596
|
+
}
|
|
5289
5597
|
const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();
|
|
5290
5598
|
if (source === "generated") {
|
|
5291
5599
|
console.log(`[ClawRouter] Generated new wallet: ${address}`);
|