@blockrun/clawrouter 0.11.4 → 0.11.6

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
@@ -37,6 +37,7 @@ One wallet, 41+ models, zero API keys.
37
37
  | ----------------------------------------- | ------------------------------- |
38
38
  | [Quick Start](#-quick-start) | Install in 2 minutes |
39
39
  | [Routing Profiles](#-routing-profiles) | eco / auto / premium / free |
40
+ | [Image Generation](#-image-generation) | /imagegen with 5 models |
40
41
  | [How It Works](#-how-it-works) | 15-dimension local routing |
41
42
  | [Models & Pricing](#-models--pricing) | 41+ models, full price list |
42
43
  | [Screenshots](#-screenshots) | See it in action |
@@ -78,6 +79,28 @@ Choose your routing strategy with `/model <profile>`:
78
79
 
79
80
  ---
80
81
 
82
+ ## 🎨 Image Generation
83
+
84
+ Generate images directly from chat with `/imagegen`:
85
+
86
+ ```
87
+ /imagegen a dog dancing on the beach
88
+ /imagegen --model dall-e-3 a futuristic city at sunset
89
+ /imagegen --model banana-pro --size 2048x2048 mountain landscape
90
+ ```
91
+
92
+ | Model | Provider | Price | Max Size |
93
+ | ------------- | --------------------- | ----------- | --------- |
94
+ | `nano-banana` | Google Gemini Flash | $0.05/image | 1024x1024 |
95
+ | `banana-pro` | Google Gemini Pro | $0.10/image | 4096x4096 |
96
+ | `dall-e-3` | OpenAI DALL-E 3 | $0.04/image | 1792x1024 |
97
+ | `gpt-image` | OpenAI GPT Image 1 | $0.02/image | 1536x1024 |
98
+ | `flux` | Black Forest Flux 1.1 | $0.04/image | 1024x1024 |
99
+
100
+ Default model: `nano-banana`. Images are returned as hosted URLs for compatibility with Telegram, Discord, and other clients.
101
+
102
+ ---
103
+
81
104
  ## ⚡ How It Works
82
105
 
83
106
  **100% local routing. <1ms latency. Zero external API calls.**
@@ -188,6 +211,7 @@ USDC stays in your wallet until spent — non-custodial. Price is visible in the
188
211
  - **Coinbase:** Buy USDC, send to Base
189
212
  - **Bridge:** Move USDC from any chain to Base
190
213
  - **CEX:** Withdraw USDC to Base network
214
+ - **Credit card:** Don't have USDC? Reach out to [@bc1max on Telegram](https://t.me/bc1max) — we accept credit card payments
191
215
 
192
216
  ---
193
217
 
package/dist/cli.js CHANGED
@@ -5331,13 +5331,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5331
5331
  const raw = modelMatch[1];
5332
5332
  const IMAGE_MODEL_ALIASES = {
5333
5333
  "dall-e-3": "openai/dall-e-3",
5334
- "dalle3": "openai/dall-e-3",
5335
- "dalle": "openai/dall-e-3",
5334
+ dalle3: "openai/dall-e-3",
5335
+ dalle: "openai/dall-e-3",
5336
5336
  "gpt-image": "openai/gpt-image-1",
5337
5337
  "gpt-image-1": "openai/gpt-image-1",
5338
- "flux": "black-forest/flux-1.1-pro",
5338
+ flux: "black-forest/flux-1.1-pro",
5339
5339
  "flux-pro": "black-forest/flux-1.1-pro",
5340
- "banana": "google/nano-banana",
5340
+ banana: "google/nano-banana",
5341
5341
  "nano-banana": "google/nano-banana",
5342
5342
  "banana-pro": "google/nano-banana-pro",
5343
5343
  "nano-banana-pro": "google/nano-banana-pro"
@@ -5378,32 +5378,51 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5378
5378
  "Cache-Control": "no-cache",
5379
5379
  Connection: "keep-alive"
5380
5380
  });
5381
- res.write(`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: errorText }, finish_reason: null }] })}
5381
+ res.write(
5382
+ `data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: errorText }, finish_reason: null }] })}
5382
5383
 
5383
- `);
5384
- res.write(`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
5384
+ `
5385
+ );
5386
+ res.write(
5387
+ `data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
5385
5388
 
5386
- `);
5389
+ `
5390
+ );
5387
5391
  res.write("data: [DONE]\n\n");
5388
5392
  res.end();
5389
5393
  } else {
5390
5394
  res.writeHead(200, { "Content-Type": "application/json" });
5391
- res.end(JSON.stringify({
5392
- id: completionId,
5393
- object: "chat.completion",
5394
- created: timestamp,
5395
- model: "clawrouter/image",
5396
- choices: [{ index: 0, message: { role: "assistant", content: errorText }, finish_reason: "stop" }],
5397
- usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
5398
- }));
5395
+ res.end(
5396
+ JSON.stringify({
5397
+ id: completionId,
5398
+ object: "chat.completion",
5399
+ created: timestamp,
5400
+ model: "clawrouter/image",
5401
+ choices: [
5402
+ {
5403
+ index: 0,
5404
+ message: { role: "assistant", content: errorText },
5405
+ finish_reason: "stop"
5406
+ }
5407
+ ],
5408
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
5409
+ })
5410
+ );
5399
5411
  }
5400
5412
  console.log(`[ClawRouter] /imagegen command \u2192 showing usage help`);
5401
5413
  return;
5402
5414
  }
5403
- console.log(`[ClawRouter] /imagegen command \u2192 ${imageModel} (${imageSize}): ${imagePrompt.slice(0, 80)}...`);
5415
+ console.log(
5416
+ `[ClawRouter] /imagegen command \u2192 ${imageModel} (${imageSize}): ${imagePrompt.slice(0, 80)}...`
5417
+ );
5404
5418
  try {
5405
5419
  const imageUpstreamUrl = `${apiBase}/v1/images/generations`;
5406
- const imageBody = JSON.stringify({ model: imageModel, prompt: imagePrompt, size: imageSize, n: 1 });
5420
+ const imageBody = JSON.stringify({
5421
+ model: imageModel,
5422
+ prompt: imagePrompt,
5423
+ size: imageSize,
5424
+ n: 1
5425
+ });
5407
5426
  const imageResponse = await payFetch(imageUpstreamUrl, {
5408
5427
  method: "POST",
5409
5428
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
@@ -5428,8 +5447,12 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5428
5447
  const hostedUrl = await uploadDataUriToHost(img.url);
5429
5448
  lines.push(hostedUrl);
5430
5449
  } catch (uploadErr) {
5431
- console.error(`[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`);
5432
- lines.push("Image generated but upload failed. Try again or use --model dall-e-3.");
5450
+ console.error(
5451
+ `[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
5452
+ );
5453
+ lines.push(
5454
+ "Image generated but upload failed. Try again or use --model dall-e-3."
5455
+ );
5433
5456
  }
5434
5457
  } else {
5435
5458
  lines.push(img.url);
@@ -5450,33 +5473,47 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5450
5473
  "Cache-Control": "no-cache",
5451
5474
  Connection: "keep-alive"
5452
5475
  });
5453
- res.write(`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: responseText }, finish_reason: null }] })}
5476
+ res.write(
5477
+ `data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: { role: "assistant", content: responseText }, finish_reason: null }] })}
5454
5478
 
5455
- `);
5456
- res.write(`data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
5479
+ `
5480
+ );
5481
+ res.write(
5482
+ `data: ${JSON.stringify({ id: completionId, object: "chat.completion.chunk", created: timestamp, model: "clawrouter/image", choices: [{ index: 0, delta: {}, finish_reason: "stop" }] })}
5457
5483
 
5458
- `);
5484
+ `
5485
+ );
5459
5486
  res.write("data: [DONE]\n\n");
5460
5487
  res.end();
5461
5488
  } else {
5462
5489
  res.writeHead(200, { "Content-Type": "application/json" });
5463
- res.end(JSON.stringify({
5464
- id: completionId,
5465
- object: "chat.completion",
5466
- created: timestamp,
5467
- model: "clawrouter/image",
5468
- choices: [{ index: 0, message: { role: "assistant", content: responseText }, finish_reason: "stop" }],
5469
- usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
5470
- }));
5490
+ res.end(
5491
+ JSON.stringify({
5492
+ id: completionId,
5493
+ object: "chat.completion",
5494
+ created: timestamp,
5495
+ model: "clawrouter/image",
5496
+ choices: [
5497
+ {
5498
+ index: 0,
5499
+ message: { role: "assistant", content: responseText },
5500
+ finish_reason: "stop"
5501
+ }
5502
+ ],
5503
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
5504
+ })
5505
+ );
5471
5506
  }
5472
5507
  } catch (err) {
5473
5508
  const errMsg = err instanceof Error ? err.message : String(err);
5474
5509
  console.error(`[ClawRouter] /imagegen error: ${errMsg}`);
5475
5510
  if (!res.headersSent) {
5476
5511
  res.writeHead(500, { "Content-Type": "application/json" });
5477
- res.end(JSON.stringify({
5478
- error: { message: `Image generation failed: ${errMsg}`, type: "image_error" }
5479
- }));
5512
+ res.end(
5513
+ JSON.stringify({
5514
+ error: { message: `Image generation failed: ${errMsg}`, type: "image_error" }
5515
+ })
5516
+ );
5480
5517
  }
5481
5518
  }
5482
5519
  return;
@@ -5585,10 +5622,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
5585
5622
  const lastAssistantMsg = [...parsedMessages].reverse().find((m) => m.role === "assistant");
5586
5623
  const toolCallNames = Array.isArray(lastAssistantMsg?.tool_calls) ? lastAssistantMsg.tool_calls.map((tc) => tc.function?.name).filter(Boolean) : void 0;
5587
5624
  const contentHash = hashRequestContent(prompt, toolCallNames);
5588
- const shouldEscalate = sessionStore.recordRequestHash(
5589
- effectiveSessionId,
5590
- contentHash
5591
- );
5625
+ const shouldEscalate = sessionStore.recordRequestHash(effectiveSessionId, contentHash);
5592
5626
  if (shouldEscalate) {
5593
5627
  const activeTierConfigs = (() => {
5594
5628
  if (routingDecision.reasoning?.includes("agentic") && routerOpts.config.agenticTiers) {
@@ -6293,6 +6327,45 @@ async function resolveOrGenerateWalletKey() {
6293
6327
  return { key, address, source: "generated" };
6294
6328
  }
6295
6329
 
6330
+ // src/report.ts
6331
+ async function generateReport(period, json = false) {
6332
+ const days = period === "daily" ? 1 : period === "weekly" ? 7 : 30;
6333
+ const stats = await getStats(days);
6334
+ if (json) {
6335
+ return JSON.stringify(stats, null, 2);
6336
+ }
6337
+ return formatMarkdownReport(period, days, stats);
6338
+ }
6339
+ function formatMarkdownReport(period, days, stats) {
6340
+ const lines = [];
6341
+ lines.push(`# ClawRouter ${capitalize(period)} Report`);
6342
+ lines.push(`**Period:** Last ${days} day${days > 1 ? "s" : ""}`);
6343
+ lines.push(`**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}`);
6344
+ lines.push("");
6345
+ lines.push("## \u{1F4CA} Usage Summary");
6346
+ lines.push("");
6347
+ lines.push(`| Metric | Value |`);
6348
+ lines.push(`|--------|-------|`);
6349
+ lines.push(`| Total Requests | ${stats.totalRequests} |`);
6350
+ lines.push(`| Total Cost | $${stats.totalCost.toFixed(4)} |`);
6351
+ lines.push(`| Baseline Cost | $${stats.totalBaselineCost.toFixed(4)} |`);
6352
+ lines.push(`| **Savings** | **$${stats.totalSavings.toFixed(4)}** |`);
6353
+ lines.push(`| Savings % | ${stats.savingsPercentage.toFixed(1)}% |`);
6354
+ lines.push(`| Avg Latency | ${stats.avgLatencyMs.toFixed(0)}ms |`);
6355
+ lines.push("");
6356
+ lines.push("## \u{1F916} Model Distribution");
6357
+ lines.push("");
6358
+ const sortedModels = Object.entries(stats.byModel).sort((a, b) => b[1].count - a[1].count).slice(0, 10);
6359
+ for (const [model, data] of sortedModels) {
6360
+ lines.push(`- ${model}: ${data.count} reqs, $${data.cost.toFixed(4)}`);
6361
+ }
6362
+ lines.push("");
6363
+ return lines.join("\n");
6364
+ }
6365
+ function capitalize(str) {
6366
+ return str.charAt(0).toUpperCase() + str.slice(1);
6367
+ }
6368
+
6296
6369
  // src/doctor.ts
6297
6370
  import { platform, arch, freemem, totalmem } from "os";
6298
6371
  function formatBytes(bytes) {
@@ -6629,6 +6702,7 @@ Usage:
6629
6702
  clawrouter [options]
6630
6703
  clawrouter doctor [opus] [question]
6631
6704
  clawrouter partners [test]
6705
+ clawrouter report [daily|weekly|monthly] [--json]
6632
6706
 
6633
6707
  Options:
6634
6708
  --version, -v Show version number
@@ -6671,6 +6745,9 @@ function parseArgs(args) {
6671
6745
  doctor: false,
6672
6746
  partners: false,
6673
6747
  partnersTest: false,
6748
+ report: false,
6749
+ reportPeriod: "daily",
6750
+ reportJson: false,
6674
6751
  port: void 0
6675
6752
  };
6676
6753
  for (let i = 0; i < args.length; i++) {
@@ -6687,6 +6764,20 @@ function parseArgs(args) {
6687
6764
  result.partnersTest = true;
6688
6765
  i++;
6689
6766
  }
6767
+ } else if (arg === "report") {
6768
+ result.report = true;
6769
+ const next = args[i + 1];
6770
+ if (next && ["daily", "weekly", "monthly"].includes(next)) {
6771
+ result.reportPeriod = next;
6772
+ i++;
6773
+ if (args[i + 1] === "--json") {
6774
+ result.reportJson = true;
6775
+ i++;
6776
+ }
6777
+ } else if (next === "--json") {
6778
+ result.reportJson = true;
6779
+ i++;
6780
+ }
6690
6781
  } else if (arg === "--port" && args[i + 1]) {
6691
6782
  result.port = parseInt(args[i + 1], 10);
6692
6783
  i++;
@@ -6757,6 +6848,11 @@ ClawRouter Partner APIs (v${VERSION})
6757
6848
  }
6758
6849
  process.exit(0);
6759
6850
  }
6851
+ if (args.report) {
6852
+ const report = await generateReport(args.reportPeriod, args.reportJson);
6853
+ console.log(report);
6854
+ process.exit(0);
6855
+ }
6760
6856
  const { key: walletKey, address, source } = await resolveOrGenerateWalletKey();
6761
6857
  if (source === "generated") {
6762
6858
  console.log(`[ClawRouter] Generated new wallet: ${address}`);