@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 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 | AUTO Model | PREMIUM Model |
89
- | --------- | ---------------------------------- | ---------------------------- | ---------------------------- |
90
- | SIMPLE | nvidia/gpt-oss-120b (FREE) | kimi-k2.5 ($0.60/$3.00) | kimi-k2.5 |
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) | grok-4-fast ($0.20/$0.50) | claude-sonnet-4.6 ($3/$15) |
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: ["anthropic/claude-haiku-4.5", "google/gemini-2.5-flash-lite", "xai/grok-code-fast-1"]
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: ["anthropic/claude-haiku-4.5", "xai/grok-4-1-fast-non-reasoning", "openai/gpt-4o-mini"]
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: ["anthropic/claude-opus-4.6", "xai/grok-4-1-fast-reasoning", "deepseek/deepseek-reasoner"]
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}`);