@blockrun/clawrouter 0.12.245 → 0.12.247

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
@@ -34,7 +34,7 @@ Agents can only sign transactions.<br><br>
34
34
 
35
35
  </div>
36
36
 
37
- > **ClawRouter** is an open-source smart LLM router that reduces AI API costs by up to <!-- br:savings.autoVsBaselinePct -->88<!-- /br:savings.autoVsBaselinePct -->%. It analyzes each request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and routes to the cheapest capable model in under 1ms, entirely locally. ClawRouter is the only LLM router built for autonomous AI agents — it uses wallet signatures for authentication (no API keys) and USDC micropayments via the x402 protocol (no credit cards). <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models from OpenAI, Anthropic, Google, xAI, DeepSeek, and more. MIT licensed.
37
+ > **ClawRouter** is an open-source smart LLM router that reduces AI API costs by up to <!-- br:savings.autoVsBaselinePct -->88<!-- /br:savings.autoVsBaselinePct -->%. It analyzes each request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and routes to the cheapest capable model in under 1ms, entirely locally. ClawRouter is the only LLM router built for autonomous AI agents — it uses wallet signatures for authentication (no API keys) and USDC micropayments via the x402 protocol (no credit cards). <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models from OpenAI, Anthropic, Google, xAI, DeepSeek, and more. MIT licensed.
38
38
 
39
39
  ---
40
40
 
@@ -61,7 +61,7 @@ This is the stack that lets agents operate autonomously: **x402 + USDC + local r
61
61
 
62
62
  | | OpenRouter | LiteLLM | Martian | Portkey | **ClawRouter** |
63
63
  | ---------------- | ----------------- | ---------------- | ----------------- | ----------------- | ---------------------------------------------------------------------- |
64
- | **Models** | 200+ | 100+ | Smart routing | Gateway | **<!-- br:models.chatVisible -->70<!-- /br:models.chatVisible -->** |
64
+ | **Models** | 200+ | 100+ | Smart routing | Gateway | **<!-- br:models.chatVisible -->71<!-- /br:models.chatVisible -->** |
65
65
  | **Free tier** | Rate-limited | BYO keys | No | No | **<!-- br:models.free -->5<!-- /br:models.free --> models, no signup** |
66
66
  | **Routing** | Manual selection | Manual selection | Smart (closed) | Observability | **Smart (open source)** |
67
67
  | **Auth** | Account + API key | Your API keys | Account + API key | Account + API key | **Wallet signature** |
@@ -107,7 +107,7 @@ openclaw gateway restart
107
107
 
108
108
  > **Using Claude Code?** Check out [BRCC](https://blockrun.ai/brcc.md) — it's purpose-built for Claude Code with the same smart routing and x402 payments.
109
109
  >
110
- > **Using NousResearch Hermes?** See [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) — a Python plugin that wires Hermes into the ClawRouter proxy. Same wallet, same <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models, same x402 USDC settlement on Base & Solana.
110
+ > **Using NousResearch Hermes?** See [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) — a Python plugin that wires Hermes into the ClawRouter proxy. Same wallet, same <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models, same x402 USDC settlement on Base & Solana.
111
111
 
112
112
  No OpenClaw required. ClawRouter runs as a local proxy on port 8402.
113
113
 
@@ -213,7 +213,7 @@ Request → Weighted Scorer → Tier → Best Model → Response
213
213
 
214
214
  | Tier | ECO Model | AUTO Model | PREMIUM Model |
215
215
  | --------- | --------------------------------------- | --------------------------------------- | ---------------------------- |
216
- | SIMPLE | free/gpt-oss-120b † (**FREE**) | gemini-2.5-flash ($0.30/$2.50) | kimi-k2.7 † ($0.95/$4.00) |
216
+ | SIMPLE | step-3.7-flash (**FREE**) | gemini-2.5-flash ($0.30/$2.50) | kimi-k2.7 † ($0.95/$4.00) |
217
217
  | MEDIUM | gemini-3.1-flash-lite ($0.25/$1.50) | kimi-k2.7 ($0.95/$4.00) | gpt-5.3-codex ($1.75/$14.00) |
218
218
  | COMPLEX | gemini-3.1-flash-lite ($0.25/$1.50) | gemini-3.1-pro ($2/$12) | claude-fable-5 ($10/$50) |
219
219
  | REASONING | grok-4-1-fast-reasoning † ($0.20/$0.50) | grok-4-1-fast-reasoning † ($0.20/$0.50) | claude-sonnet-4.6 ($3/$15) |
@@ -244,6 +244,7 @@ Generate images directly from chat with `/cr-imagegen`:
244
244
  | Model | Provider | Price | Max Size |
245
245
  | ---------------------------- | -------------------- | ------------ | --------- |
246
246
  | `nano-banana` | Google Gemini Flash | $0.05/image | 1024x1024 |
247
+ | `banana-2` | Google Nano Banana 2 | $0.09/image | 1024x1024 |
247
248
  | `banana-pro` | Google Gemini Pro | $0.10/image | 4096x4096 |
248
249
  | `gpt-image` | OpenAI GPT Image 1 | $0.02/image | 1536x1024 |
249
250
  | `gpt-image-2` | OpenAI GPT Image 2 | $0.06/image | 1536x1024 |
@@ -393,7 +394,7 @@ No Surf account, no API key — settles directly to Surf's Base treasury in USDC
393
394
 
394
395
  ## Models & Pricing
395
396
 
396
- <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models across 9 providers, one wallet. **<!-- br:models.free -->5<!-- /br:models.free --> models are $0 — paid models start at fractions of a cent.**
397
+ <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models across 9 providers, one wallet. **<!-- br:models.free -->5<!-- /br:models.free --> models are $0 — paid models start at fractions of a cent.**
397
398
 
398
399
  > **💡 "Cost per request"** = estimated cost for a typical chat message (~500 input + 500 output tokens). Paid requests also carry a flat **$0.001/tx settlement fee** (covers on-chain gas; already included in the price the gateway quotes). Free models never pay it.
399
400
 
@@ -498,15 +499,14 @@ Request → 402 (price: $0.003) → wallet signs USDC → retry → response
498
499
 
499
500
  USDC stays in your wallet until spent — non-custodial. Price is visible in the 402 header before signing.
500
501
 
501
- **Dual-chain support:** Pay with **USDC** on **Base (EVM)** or **USDC on Solana**. Both wallets are derived from a single BIP-39 mnemonic on first run.
502
+ **Dual-chain support:** Pay with **USDC** on **Base (EVM)** or **USDC on Solana**. Both wallets are derived from a single BIP-39 mnemonic on first run. New installs default to **Solana**; existing installs stay on **Base** unless switched with `/wallet solana`.
502
503
 
503
504
  ```bash
504
505
  /wallet # Check balance and address (both chains)
505
506
  /wallet export # Export mnemonic + keys for backup
506
507
  /wallet recover # Restore wallet from mnemonic on a new machine
507
508
  /wallet solana # Switch to Solana USDC payments
508
- /wallet base # Switch back to Base (EVM) USDC payments
509
- /chain solana # Alias for /wallet solana
509
+ /wallet base # Switch to Base (EVM) USDC payments
510
510
  /stats # View usage and savings
511
511
  /stats clear # Reset usage statistics
512
512
  /exclude # Show excluded models
@@ -654,7 +654,7 @@ npm test
654
654
 
655
655
  **The LLM router built for autonomous agents**
656
656
 
657
- You're here. <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models, local smart routing, x402 USDC payments — the only stack that lets agents operate independently.
657
+ You're here. <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models, local smart routing, x402 USDC payments — the only stack that lets agents operate independently.
658
658
 
659
659
  `curl -fsSL https://blockrun.ai/ClawRouter-update | bash`
660
660
 
@@ -665,7 +665,7 @@ You're here. <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> mod
665
665
 
666
666
  **BlockRun for Claude Code**
667
667
 
668
- Run Claude Code with <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models, no rate limits, no Anthropic account, no phone verification. Pay per request with USDC — your wallet is your identity.
668
+ Run Claude Code with <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models, no rate limits, no Anthropic account, no phone verification. Pay per request with USDC — your wallet is your identity.
669
669
 
670
670
  `curl -fsSL https://blockrun.ai/brcc-install | bash`
671
671
 
@@ -678,7 +678,7 @@ Run Claude Code with <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible
678
678
 
679
679
  **ClawRouter for NousResearch Hermes**
680
680
 
681
- Python plugin that wraps the ClawRouter proxy for `hermes-agent`. Same <!-- br:models.chatVisible -->70<!-- /br:models.chatVisible --> models, same x402 USDC payments on Base & Solana, native Hermes ergonomics.
681
+ Python plugin that wraps the ClawRouter proxy for `hermes-agent`. Same <!-- br:models.chatVisible -->71<!-- /br:models.chatVisible --> models, same x402 USDC payments on Base & Solana, native Hermes ergonomics.
682
682
 
683
683
  `pip install hermes-plugin-clawrouter`
684
684
 
package/dist/cli.js CHANGED
@@ -33594,6 +33594,21 @@ function applyPromotions(tierConfigs, promotions, profile, now2 = /* @__PURE__ *
33594
33594
  }
33595
33595
  return result;
33596
33596
  }
33597
+ function applyUnavailableModels(tierConfigs, unavailableModels) {
33598
+ if (!unavailableModels || unavailableModels.length === 0) return tierConfigs;
33599
+ const dead = new Set(unavailableModels);
33600
+ let result = tierConfigs;
33601
+ for (const tier of Object.keys(tierConfigs)) {
33602
+ const config = tierConfigs[tier];
33603
+ const alive = [config.primary, ...config.fallback].filter((model) => !dead.has(model));
33604
+ if (alive.length === 0 || alive[0] === config.primary && alive.length === config.fallback.length + 1) {
33605
+ continue;
33606
+ }
33607
+ if (result === tierConfigs) result = { ...tierConfigs };
33608
+ result[tier] = { primary: alive[0], fallback: alive.slice(1) };
33609
+ }
33610
+ return result;
33611
+ }
33597
33612
  function getStrategy(name) {
33598
33613
  const strategy = registry.get(name);
33599
33614
  if (!strategy) {
@@ -34291,6 +34306,7 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34291
34306
  profile = useAgenticTiers ? "agentic" : "auto";
34292
34307
  }
34293
34308
  tierConfigs = applyPromotions(tierConfigs, config.promotions, profile, options.now);
34309
+ tierConfigs = applyUnavailableModels(tierConfigs, options.unavailableModels);
34294
34310
  const agenticScoreValue = ruleResult.agenticScore;
34295
34311
  if (estimatedTokens > config.overrides.maxTokensForceComplex) {
34296
34312
  const decision2 = selectModel(
@@ -34419,18 +34435,6 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34419
34435
  supportsTools: false,
34420
34436
  supportsVision: false
34421
34437
  },
34422
- "free/gpt-oss-120b": {
34423
- contextWindow: 128e3,
34424
- maxOutputTokens: 16384,
34425
- supportsTools: false,
34426
- supportsVision: false
34427
- },
34428
- "free/gpt-oss-20b": {
34429
- contextWindow: 128e3,
34430
- maxOutputTokens: 16384,
34431
- supportsTools: false,
34432
- supportsVision: false
34433
- },
34434
34438
  "free/seed-oss-36b": {
34435
34439
  contextWindow: 131072,
34436
34440
  maxOutputTokens: 16384,
@@ -34503,6 +34507,18 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34503
34507
  supportsTools: true,
34504
34508
  supportsVision: true
34505
34509
  },
34510
+ "nvidia/nemotron-nano-9b-v2": {
34511
+ contextWindow: 131072,
34512
+ maxOutputTokens: 16384,
34513
+ supportsTools: false,
34514
+ supportsVision: false
34515
+ },
34516
+ "nvidia/step-3.7-flash": {
34517
+ contextWindow: 131072,
34518
+ maxOutputTokens: 16384,
34519
+ supportsTools: false,
34520
+ supportsVision: false
34521
+ },
34506
34522
  "openai/gpt-4.1": {
34507
34523
  contextWindow: 128e3,
34508
34524
  maxOutputTokens: 16384,
@@ -34976,9 +34992,12 @@ ${value.slice(-(scanLimit - prefixLength))}`;
34976
34992
  const targetTier = (features.taskType === "reasoning_mcq" || features.taskType === "reasoning_math") && (base3.tier === "SIMPLE" || base3.tier === "MEDIUM") ? "REASONING" : base3.tier;
34977
34993
  const tierConfig = tierConfigs[targetTier];
34978
34994
  const configuredCandidates = tierConfig ? getFallbackChain(targetTier, tierConfigs) : [];
34995
+ const unavailable = new Set(options.unavailableModels ?? []);
34979
34996
  const chain3 = [
34980
34997
  .../* @__PURE__ */ new Set([...configuredCandidates, ...evidenceCandidates(features.taskType)])
34981
- ].filter((model2) => typeof model2 === "string" && model2.length > 0);
34998
+ ].filter(
34999
+ (model2) => typeof model2 === "string" && model2.length > 0 && !unavailable.has(model2)
35000
+ );
34982
35001
  const eligible = chain3.filter(
34983
35002
  (model2) => isEligible(model2, features, maxOutputTokens, options)
34984
35003
  );
@@ -35055,13 +35074,7 @@ ${value.slice(-(scanLimit - prefixLength))}`;
35055
35074
  ...eligibleCandidates.filter(
35056
35075
  (model2) => !scoredModels.includes(model2) && !webResearchFallbackOrder.includes(model2)
35057
35076
  )
35058
- ] : features.taskType === "tool_agent" || features.taskType === "tool_agent_parallel" && features.agentDomain !== "other" ? [
35059
- ...scoredModels,
35060
- ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
35061
- ] : [
35062
- ...scoredModels,
35063
- ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))
35064
- ];
35077
+ ] : [...scoredModels, ...eligibleCandidates.filter((model2) => !scoredModels.includes(model2))];
35065
35078
  const model = ranked[0] ?? base3.model;
35066
35079
  const selectedTierConfigs = {
35067
35080
  ...tierConfigs,
@@ -36173,8 +36186,8 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36173
36186
  // $0.20/$1.25, 1M context
36174
36187
  "xai/grok-4-fast-non-reasoning",
36175
36188
  // 1,143ms, $0.20/$0.50 — fast fallback
36176
- "free/gpt-oss-120b"
36177
- // 1,252ms, FREE fallback (hidden from /v1/models but direct calls work)
36189
+ "nvidia/step-3.7-flash"
36190
+ // FREE backstop — new NVIDIA free tier (gpt-oss-120b now 400s; probed 2026-08-21)
36178
36191
  ]
36179
36192
  },
36180
36193
  MEDIUM: {
@@ -36247,14 +36260,15 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36247
36260
  // Eco tier configs - absolute cheapest (blockrun/eco)
36248
36261
  ecoTiers: {
36249
36262
  SIMPLE: {
36250
- primary: "free/gpt-oss-120b",
36251
- // FREE! $0.00/$0.00 — heavy user default
36263
+ primary: "nvidia/step-3.7-flash",
36264
+ // FREE! $0.00/$0.00 — new NVIDIA free tier flagship
36252
36265
  fallback: [
36253
- "free/gpt-oss-20b",
36254
- // FREE — smaller, faster
36255
- // deepseek-v4-flash and seed-oss-36b sat here until NVIDIA EOL'd them
36256
- // (410; 2026-08-12 and 2026-08-03 respectively). gpt-oss-120b/20b already
36257
- // head this chain, so the rungs are dropped, not retargeted.
36266
+ "nvidia/nemotron-nano-9b-v2",
36267
+ // FREE — compact + fast (~0.7s), high-volume light tasks
36268
+ // This head keeps rotting with NVIDIA's free hosting: deepseek-v4-flash
36269
+ // (410, 2026-08-12), seed-oss-36b (410, 2026-08-03), then gpt-oss-120b/20b
36270
+ // (400 Unknown model, probed 2026-08-21). Each retirement retargets the
36271
+ // two free rungs to the current free tier; the paid rungs below never move.
36258
36272
  "google/gemini-3.1-flash-lite",
36259
36273
  // $0.25/$1.50 — newest flash-lite
36260
36274
  "openai/gpt-5.4-nano",
@@ -36371,8 +36385,8 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36371
36385
  "openai/gpt-5.3-codex",
36372
36386
  "deepseek/deepseek-chat",
36373
36387
  // Cheap, reliable
36374
- "free/gpt-oss-120b"
36375
- // NVIDIA free ultimate backstop (was seed-oss-36b; EOL'd 2026-08-03)
36388
+ "nvidia/step-3.7-flash"
36389
+ // NVIDIA free ultimate backstop (was gpt-oss-120b; 400s since ~2026-08)
36376
36390
  ]
36377
36391
  },
36378
36392
  REASONING: {
@@ -36461,8 +36475,8 @@ ${value.slice(-(scanLimit - prefixLength))}`;
36461
36475
  // Previous flagship — 6,213ms, reliable
36462
36476
  "deepseek/deepseek-chat",
36463
36477
  // 1,431ms — cheap, reliable
36464
- "free/gpt-oss-120b"
36465
- // NVIDIA free ultimate backstop (was seed-oss-36b; EOL'd 2026-08-03)
36478
+ "nvidia/step-3.7-flash"
36479
+ // NVIDIA free ultimate backstop (was gpt-oss-120b; 400s since ~2026-08)
36466
36480
  ]
36467
36481
  },
36468
36482
  REASONING: {
@@ -57934,6 +57948,14 @@ Then run: npx @blockrun/clawrouter`
57934
57948
  solanaAddress = await getSolanaAddress(derived.solanaPrivateKeyBytes);
57935
57949
  } catch {
57936
57950
  }
57951
+ let solanaDefaultSaved = false;
57952
+ if (solanaAddress) {
57953
+ try {
57954
+ await savePaymentChain("solana");
57955
+ solanaDefaultSaved = true;
57956
+ } catch {
57957
+ }
57958
+ }
57937
57959
  console.log(`[ClawRouter]`);
57938
57960
  console.log(`[ClawRouter] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
57939
57961
  console.log(`[ClawRouter] NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW`);
@@ -57946,6 +57968,12 @@ Then run: npx @blockrun/clawrouter`
57946
57968
  console.log(`[ClawRouter] Mnemonic : ${MNEMONIC_FILE}`);
57947
57969
  console.log(`[ClawRouter]`);
57948
57970
  console.log(`[ClawRouter] Both EVM (Base) and Solana wallets are ready.`);
57971
+ if (solanaDefaultSaved) {
57972
+ console.log(
57973
+ `[ClawRouter] Default payment chain: Solana \u2014 fund the Solana address above with USDC.`
57974
+ );
57975
+ console.log(`[ClawRouter] To switch to Base, run in OpenClaw: /wallet base`);
57976
+ }
57949
57977
  console.log(`[ClawRouter] To back up, run in OpenClaw:`);
57950
57978
  console.log(`[ClawRouter] /wallet export`);
57951
57979
  console.log(`[ClawRouter]`);
@@ -90788,25 +90816,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90788
90816
  const modelMatch = imageArgs.match(/--model\s+(\S+)/);
90789
90817
  if (modelMatch) {
90790
90818
  const raw = modelMatch[1];
90791
- const IMAGE_MODEL_ALIASES = {
90792
- // dall-e-3 delisted upstream 2026-05-25 — legacy aliases route to
90793
- // the OpenAI successor instead of a guaranteed 400.
90794
- "dall-e-3": "openai/gpt-image-2",
90795
- dalle3: "openai/gpt-image-2",
90796
- dalle: "openai/gpt-image-2",
90797
- "gpt-image": "openai/gpt-image-1",
90798
- "gpt-image-1": "openai/gpt-image-1",
90799
- "gpt-image-2": "openai/gpt-image-2",
90800
- seedream: "bytedance/seedream-5-pro",
90801
- banana: "google/nano-banana",
90802
- "nano-banana": "google/nano-banana",
90803
- "banana-pro": "google/nano-banana-pro",
90804
- "nano-banana-pro": "google/nano-banana-pro",
90805
- "grok-imagine": "xai/grok-imagine-image",
90806
- "grok-imagine-pro": "xai/grok-imagine-image-pro",
90807
- cogview: "zai/cogview-4"
90808
- };
90809
- imageModel = IMAGE_MODEL_ALIASES[raw] ?? raw;
90819
+ imageModel = Object.prototype.hasOwnProperty.call(IMAGE_MODEL_ALIASES, raw) ? IMAGE_MODEL_ALIASES[raw] : raw;
90810
90820
  imagePrompt = imagePrompt.replace(/--model\s+\S+/, "").trim();
90811
90821
  }
90812
90822
  const sizeMatch = imageArgs.match(/--size\s+(\d+x\d+)/);
@@ -90824,11 +90834,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90824
90834
  "",
90825
90835
  "Models:",
90826
90836
  " nano-banana Google Gemini Flash \u2014 $0.05/image",
90837
+ " banana-2 Google Nano Banana 2 \u2014 $0.09/image",
90827
90838
  " banana-pro Google Gemini Pro \u2014 $0.10/image (up to 4K)",
90828
90839
  " gpt-image OpenAI GPT Image 1 \u2014 $0.02/image",
90829
90840
  " gpt-image-2 OpenAI GPT Image 2 \u2014 $0.06/image",
90830
90841
  " seedream ByteDance Seedream 5 Pro \u2014 $0.045/image",
90831
90842
  " grok-imagine xAI Grok Imagine \u2014 $0.02/image",
90843
+ " grok-imagine-pro xAI Grok Imagine Pro \u2014 $0.07/image",
90832
90844
  " cogview Zhipu CogView-4 \u2014 $0.015/image",
90833
90845
  "",
90834
90846
  "Examples:",
@@ -90891,14 +90903,68 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90891
90903
  size: imageSize,
90892
90904
  n: 1
90893
90905
  });
90906
+ const imagegenAbort = new AbortController();
90907
+ res.on("close", () => {
90908
+ if (!res.writableEnded) imagegenAbort.abort();
90909
+ });
90894
90910
  const imageResponse = await payFetch(imageUpstreamUrl, {
90895
90911
  method: "POST",
90896
90912
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
90897
- body: imageBody
90913
+ body: imageBody,
90914
+ signal: imagegenAbort.signal
90898
90915
  });
90899
- const imageResult = await imageResponse.json();
90916
+ let imageResult = await imageResponse.json();
90917
+ let imageFailure;
90918
+ if (imageResponse.ok && imageResult.poll_url && imageResult.id && !imageResult.data?.length) {
90919
+ const apiOrigin = new URL(apiBase).origin;
90920
+ const pollUrl = imageResult.poll_url.startsWith("http") ? imageResult.poll_url : `${apiOrigin}${imageResult.poll_url}`;
90921
+ console.log(
90922
+ `[ClawRouter] /imagegen job submitted (id=${imageResult.id}), polling upstream \u2014 typical 30\u2013120s...`
90923
+ );
90924
+ const pollDeadline = Date.now() + 3e5;
90925
+ await new Promise((r2) => setTimeout(r2, 2e3));
90926
+ let completed2 = false;
90927
+ while (Date.now() < pollDeadline) {
90928
+ if (imagegenAbort.signal.aborted) {
90929
+ console.log(
90930
+ `[ClawRouter] Chat client disconnected \u2014 abandoning image poll (id=${imageResult.id})`
90931
+ );
90932
+ return;
90933
+ }
90934
+ const pollResp = await payFetch(pollUrl, {
90935
+ method: "GET",
90936
+ headers: { "user-agent": USER_AGENT },
90937
+ signal: imagegenAbort.signal
90938
+ });
90939
+ const pollText = await pollResp.text();
90940
+ let pollBody = {};
90941
+ try {
90942
+ pollBody = JSON.parse(pollText);
90943
+ } catch {
90944
+ imageFailure = `Non-JSON poll response (${pollResp.status}): ${pollText.slice(0, 200)}`;
90945
+ break;
90946
+ }
90947
+ if (pollResp.status === 202 || pollBody.status === "queued" || pollBody.status === "in_progress") {
90948
+ await new Promise((r2) => setTimeout(r2, 3e3));
90949
+ continue;
90950
+ }
90951
+ if (pollBody.status === "failed" || !pollResp.ok) {
90952
+ imageFailure = typeof pollBody.error === "string" ? pollBody.error : pollBody.error?.message ?? `Upstream image generation failed (HTTP ${pollResp.status})`;
90953
+ break;
90954
+ }
90955
+ imageResult = pollBody;
90956
+ completed2 = true;
90957
+ break;
90958
+ }
90959
+ if (!completed2 && !imageFailure) {
90960
+ imageFailure = "Image generation timed out after 5 minutes \u2014 the job may still complete upstream.";
90961
+ }
90962
+ }
90900
90963
  let responseText;
90901
- if (!imageResponse.ok || imageResult.error) {
90964
+ if (imageFailure) {
90965
+ responseText = `Image generation failed: ${imageFailure}`;
90966
+ console.log(`[ClawRouter] /imagegen error: ${imageFailure}`);
90967
+ } else if (!imageResponse.ok || imageResult.error) {
90902
90968
  const errMsg = typeof imageResult.error === "string" ? imageResult.error : imageResult.error?.message ?? `HTTP ${imageResponse.status}`;
90903
90969
  responseText = `Image generation failed: ${errMsg}`;
90904
90970
  console.log(`[ClawRouter] /imagegen error: ${errMsg}`);
@@ -90918,9 +90984,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90918
90984
  console.error(
90919
90985
  `[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
90920
90986
  );
90921
- lines.push(
90922
- "Image generated but upload failed. Try again or use --model dall-e-3."
90923
- );
90987
+ lines.push("Image generated but upload failed. Try again.");
90924
90988
  }
90925
90989
  } else {
90926
90990
  lines.push(img.url);
@@ -92704,7 +92768,7 @@ data: [DONE]
92704
92768
  });
92705
92769
  }
92706
92770
  }
92707
- var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, FREE_MODEL, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, VIDEO_PRICING, PHONE_PRICING;
92771
+ var paymentStore, BLOCKRUN_API, BLOCKRUN_SOLANA_API, IMAGE_DIR, AUDIO_DIR, VIDEO_DIR, AUTO_MODEL, ROUTING_PROFILES, FREE_MODELS, FREE_MODEL, MAX_MESSAGES, CONTEXT_LIMIT_KB, HEARTBEAT_INTERVAL_MS, BALANCE_CHECK_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, PER_MODEL_TIMEOUT_MS, REASONING_MODEL_TIMEOUT_MS, REASONING_MODEL_IDS, MAX_FALLBACK_ATTEMPTS, HEALTH_CHECK_TIMEOUT_MS, RATE_LIMIT_COOLDOWN_MS, OVERLOAD_COOLDOWN_MS, PORT_RETRY_ATTEMPTS, PORT_RETRY_DELAY_MS, MODEL_BODY_READ_TIMEOUT_MS, ERROR_BODY_READ_TIMEOUT_MS, rateLimitedModels, overloadedModels, perProviderErrors, BALANCE_CHECK_BUFFER, BALANCE_PREFLIGHT_OUTPUT_TOKEN_CAP, PROVIDER_ERROR_PATTERNS, DEGRADED_RESPONSE_PATTERNS, DEGRADED_LOOP_PATTERNS, VALID_ROLES, ROLE_MAPPINGS, VALID_TOOL_ID_PATTERN, KIMI_BLOCK_RE, KIMI_TOKEN_RE, THINKING_TAG_RE, THINKING_BLOCK_RE, BLOCKRUN_MODEL_BY_ID, IMAGE_PRICING, IMAGE_MODEL_IDS, IMAGE_MODEL_SIZES, IMAGE_MODEL_ALIASES, VIDEO_PRICING, PHONE_PRICING;
92708
92772
  var init_proxy = __esm({
92709
92773
  "src/proxy.ts"() {
92710
92774
  "use strict";
@@ -92849,9 +92913,20 @@ var init_proxy = __esm({
92849
92913
  },
92850
92914
  "bytedance/seedream-5-pro": {
92851
92915
  default: 0.045,
92852
- sizes: { "1024x1024": 0.045, "2048x1024": 0.045, "2048x2048": 0.09 }
92916
+ // Full size list live-probed 2026-08-23 (gateway 402 quotes, base price):
92917
+ // the 2K portrait/landscape tiers bill at the 2048x2048 rate.
92918
+ sizes: {
92919
+ "1024x1024": 0.045,
92920
+ "1280x720": 0.045,
92921
+ "2048x1024": 0.045,
92922
+ "2048x2048": 0.09,
92923
+ "2304x1728": 0.09,
92924
+ "1728x2304": 0.09,
92925
+ "2848x1600": 0.09,
92926
+ "1600x2848": 0.09
92927
+ }
92853
92928
  },
92854
- "google/nano-banana": { default: 0.05 },
92929
+ "google/nano-banana": { default: 0.05, sizes: { "1024x1024": 0.05 } },
92855
92930
  // Nano Banana 2 (Gemini 3.1 Flash imagegen, blockrun #329): $0.09/image at 1K.
92856
92931
  "google/nano-banana-2": { default: 0.09, sizes: { "1024x1024": 0.09 } },
92857
92932
  "google/nano-banana-pro": {
@@ -92872,6 +92947,30 @@ var init_proxy = __esm({
92872
92947
  }
92873
92948
  }
92874
92949
  };
92950
+ IMAGE_MODEL_IDS = Object.freeze(Object.keys(IMAGE_PRICING));
92951
+ IMAGE_MODEL_SIZES = Object.freeze(
92952
+ [...new Set(Object.values(IMAGE_PRICING).flatMap((p) => Object.keys(p.sizes ?? {})))].sort()
92953
+ );
92954
+ IMAGE_MODEL_ALIASES = Object.freeze({
92955
+ // dall-e-3 delisted upstream 2026-05-25 — legacy aliases route to
92956
+ // the OpenAI successor instead of a guaranteed 400.
92957
+ "dall-e-3": "openai/gpt-image-2",
92958
+ dalle3: "openai/gpt-image-2",
92959
+ dalle: "openai/gpt-image-2",
92960
+ "gpt-image": "openai/gpt-image-1",
92961
+ "gpt-image-1": "openai/gpt-image-1",
92962
+ "gpt-image-2": "openai/gpt-image-2",
92963
+ seedream: "bytedance/seedream-5-pro",
92964
+ banana: "google/nano-banana",
92965
+ "nano-banana": "google/nano-banana",
92966
+ "banana-2": "google/nano-banana-2",
92967
+ "nano-banana-2": "google/nano-banana-2",
92968
+ "banana-pro": "google/nano-banana-pro",
92969
+ "nano-banana-pro": "google/nano-banana-pro",
92970
+ "grok-imagine": "xai/grok-imagine-image",
92971
+ "grok-imagine-pro": "xai/grok-imagine-image-pro",
92972
+ cogview: "zai/cogview-4"
92973
+ });
92875
92974
  VIDEO_PRICING = {
92876
92975
  "xai/grok-imagine-video": { pricePerSecond: 0.05, defaultDurationSeconds: 8 },
92877
92976
  // Seedance rates re-synced 2026-08-08 from blockrun's models.ts after the
@@ -93374,8 +93473,8 @@ var init_registry = __esm({
93374
93473
  name: "Image Generation",
93375
93474
  partner: "BlockRun",
93376
93475
  category: "Image & Video",
93377
- shortDescription: "8 image models (DALL-E, Flux, Grok, ...)",
93378
- description: "Generate an image from a text prompt. Models available: google/nano-banana (default), google/nano-banana-pro (up to 4K), openai/gpt-image-1, openai/dall-e-3, black-forest/flux-1.1-pro, xai/grok-imagine-image, xai/grok-imagine-image-pro, zai/cogview-4. Returns a local http://localhost:8402/images/<file>.png URL.",
93476
+ shortDescription: "9 image models (Nano Banana, GPT Image, ...)",
93477
+ description: "Generate an image from a text prompt. Models available: google/nano-banana (default), google/nano-banana-2, google/nano-banana-pro (up to 4K), openai/gpt-image-1, openai/gpt-image-2, bytedance/seedream-5-pro, xai/grok-imagine-image, xai/grok-imagine-image-pro, zai/cogview-4. Returns a local http://localhost:8402/images/<file>.png URL.",
93379
93478
  proxyPath: "/images/generations",
93380
93479
  method: "POST",
93381
93480
  params: [
@@ -93388,7 +93487,7 @@ var init_registry = __esm({
93388
93487
  {
93389
93488
  name: "model",
93390
93489
  type: "string",
93391
- description: "Full model ID (e.g. 'google/nano-banana', 'openai/dall-e-3'). Default: google/nano-banana.",
93490
+ description: "Full model ID (e.g. 'google/nano-banana', 'openai/gpt-image-2'). Default: google/nano-banana.",
93392
93491
  required: false
93393
93492
  },
93394
93493
  {
@@ -225331,6 +225430,7 @@ __export(index_exports, {
225331
225430
  SpendControl: () => SpendControl,
225332
225431
  VISIBLE_OPENCLAW_MODELS: () => VISIBLE_OPENCLAW_MODELS,
225333
225432
  blockrunProvider: () => blockrunProvider,
225433
+ buildImageGenerationProvider: () => buildImageGenerationProvider,
225334
225434
  buildPartnerTools: () => buildPartnerTools,
225335
225435
  buildProviderModels: () => buildProviderModels,
225336
225436
  calculateModelCost: () => calculateModelCost,
@@ -226094,12 +226194,23 @@ function buildImageGenerationProvider() {
226094
226194
  id: "blockrun",
226095
226195
  label: "BlockRun",
226096
226196
  defaultModel: "google/nano-banana",
226197
+ // Must stay in sync with IMAGE_PRICING (proxy.ts). OpenClaw sends the
226198
+ // picked id straight to /v1/images/generations, which forwards the body
226199
+ // verbatim with no alias resolution, so a retired id here is a guaranteed
226200
+ // upstream 400. dall-e-3 (delisted 2026-05-25) and flux-1.1-pro (no
226201
+ // gateway entry) were dropped in v0.12.227 and had lingered here.
226202
+ // Kept as a hand-written array (not spread from IMAGE_MODEL_IDS) because
226203
+ // this is the picker's curated display order — default model first, which
226204
+ // IMAGE_PRICING declaration order does not give — and because the
226205
+ // lifecycle tests fully mock ./proxy.js. Set-equality is enforced by
226206
+ // src/index.image-provider.test.ts, which pins the two lists together.
226097
226207
  models: [
226098
226208
  "google/nano-banana",
226209
+ "google/nano-banana-2",
226099
226210
  "google/nano-banana-pro",
226100
226211
  "openai/gpt-image-1",
226101
- "openai/dall-e-3",
226102
- "black-forest/flux-1.1-pro",
226212
+ "openai/gpt-image-2",
226213
+ "bytedance/seedream-5-pro",
226103
226214
  "xai/grok-imagine-image",
226104
226215
  "xai/grok-imagine-image-pro",
226105
226216
  "zai/cogview-4"
@@ -226114,20 +226225,28 @@ function buildImageGenerationProvider() {
226114
226225
  // Only openai/gpt-image-1 supports edit server-side; OpenClaw's UI picks a
226115
226226
  // compatible model at edit time via /v1/images/image2image.
226116
226227
  edit: { enabled: true },
226228
+ // Union of every size the gateway accepts across the models above,
226229
+ // live-probed 2026-08-23 (the gateway validates size per-model BEFORE
226230
+ // payment and 400s unknown ones — 1216x832 / 1792x1024 / 1024x1792 were
226231
+ // accepted by no model and are gone with dall-e-3). Pinned against
226232
+ // IMAGE_MODEL_SIZES (proxy.ts) by src/index.image-provider.test.ts.
226117
226233
  geometry: {
226118
226234
  sizes: [
226119
226235
  "512x512",
226120
226236
  "768x768",
226121
226237
  "768x1344",
226122
226238
  "1024x1024",
226123
- "1216x832",
226239
+ "1024x1536",
226240
+ "1280x720",
226124
226241
  "1344x768",
226125
226242
  "1440x1440",
226126
226243
  "1536x1024",
226127
- "1024x1536",
226128
- "1792x1024",
226129
- "1024x1792",
226244
+ "1600x2848",
226245
+ "1728x2304",
226246
+ "2048x1024",
226130
226247
  "2048x2048",
226248
+ "2304x1728",
226249
+ "2848x1600",
226131
226250
  "4096x4096"
226132
226251
  ]
226133
226252
  }
package/dist/index.d.ts CHANGED
@@ -767,7 +767,11 @@ type PaymentChain = "base" | "solana";
767
767
  type ProxyOptions = {
768
768
  wallet: WalletConfig;
769
769
  apiBase?: string;
770
- /** Payment chain: "base" (default) or "solana". Can also be set via CLAWROUTER_PAYMENT_CHAIN env var. */
770
+ /**
771
+ * Payment chain: "base" or "solana". New installs persist "solana" at wallet
772
+ * generation; absent config resolves to "base" for pre-existing installs.
773
+ * Can also be set via CLAWROUTER_PAYMENT_CHAIN env var.
774
+ */
771
775
  paymentChain?: PaymentChain;
772
776
  /** Port to listen on (default: 8402) */
773
777
  port?: number;
@@ -895,6 +899,8 @@ declare function savePaymentChain(chain: "base" | "solana"): Promise<void>;
895
899
  /**
896
900
  * Load the persisted payment chain selection from disk.
897
901
  * Returns "base" if no file exists or the file is invalid.
902
+ * New installs persist "solana" at wallet generation, so an absent file
903
+ * means a pre-existing install whose funds live on Base.
898
904
  */
899
905
  declare function loadPaymentChain(): Promise<"base" | "solana">;
900
906
  /**
@@ -1488,7 +1494,8 @@ declare function buildPartnerTools(proxyBaseUrl: string): PartnerToolDefinition[
1488
1494
  * # Install the plugin
1489
1495
  * openclaw plugins install @blockrun/clawrouter
1490
1496
  *
1491
- * # Fund your wallet with USDC on Base (address printed on install)
1497
+ * # Fund your wallet with USDC (Solana for new installs, Base for existing ones;
1498
+ * # the funding address is printed on install)
1492
1499
  *
1493
1500
  * # Use smart routing (auto-picks cheapest model)
1494
1501
  * openclaw models set blockrun/auto
@@ -1587,6 +1594,12 @@ declare function parseCallArgs(raw: string): {
1587
1594
  from?: string;
1588
1595
  language?: string;
1589
1596
  };
1597
+ /**
1598
+ * Build the ImageGenerationProvider that registers BlockRun image models
1599
+ * with OpenClaw's native image generation UI.
1600
+ * Delegates to the local proxy (which handles x402 payment).
1601
+ */
1602
+ declare function buildImageGenerationProvider(): ImageGenerationProviderPlugin;
1590
1603
  declare const plugin: OpenClawPluginDefinition;
1591
1604
 
1592
- export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedLLMResponse, type CachedResponse, type CheckResult, DEFAULT_RETRY_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, type DerivedKeys, EmptyWalletError, FileSpendControlStorage, InMemorySpendControlStorage, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PARTNER_SERVICES, type PartnerServiceDefinition, type PartnerToolDefinition, type PaymentChain, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SolanaBalanceInfo, SolanaBalanceMonitor, SpendControl, type SpendControlOptions, type SpendControlStorage, type SpendLimits, type SpendRecord, type SpendWindow, type SpendingStatus, type SufficiencyResult, type UsageEntry, VISIBLE_OPENCLAW_MODELS, type WalletConfig, type WalletResolution, blockrunProvider, buildPartnerTools, buildProviderModels, clearStats, plugin as default, deriveAllKeys, deriveEvmKey, deriveSolanaKeyBytes, fetchWithRetry, formatDuration, formatStatsAscii, generateWalletMnemonic, getAgenticModels, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, hashRequestContent, injectAuthProfile, injectModelsConfig, isAgenticModel, isBalanceError, isBlockrunWebSearchDisabled, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, isValidMnemonic, loadPaymentChain, logUsage, parseCallArgs, resolveModelAlias, resolvePaymentChain, savePaymentChain, setupSolana, startProxy, syncAgentModelCache };
1605
+ export { type AggregatedStats, BALANCE_THRESHOLDS, BLOCKRUN_MODELS, type BalanceInfo, BalanceMonitor, type CachedLLMResponse, type CachedResponse, type CheckResult, DEFAULT_RETRY_CONFIG, DEFAULT_SESSION_CONFIG, type DailyStats, type DerivedKeys, EmptyWalletError, FileSpendControlStorage, InMemorySpendControlStorage, InsufficientFundsError, type InsufficientFundsInfo, type LowBalanceInfo, MODEL_ALIASES, OPENCLAW_MODELS, PARTNER_SERVICES, type PartnerServiceDefinition, type PartnerToolDefinition, type PaymentChain, type ProxyHandle, type ProxyOptions, RequestDeduplicator, ResponseCache, type ResponseCacheConfig, type RetryConfig, RpcError, type SessionConfig, type SessionEntry, SessionStore, type SolanaBalanceInfo, SolanaBalanceMonitor, SpendControl, type SpendControlOptions, type SpendControlStorage, type SpendLimits, type SpendRecord, type SpendWindow, type SpendingStatus, type SufficiencyResult, type UsageEntry, VISIBLE_OPENCLAW_MODELS, type WalletConfig, type WalletResolution, blockrunProvider, buildImageGenerationProvider, buildPartnerTools, buildProviderModels, clearStats, plugin as default, deriveAllKeys, deriveEvmKey, deriveSolanaKeyBytes, fetchWithRetry, formatDuration, formatStatsAscii, generateWalletMnemonic, getAgenticModels, getModelContextWindow, getPartnerService, getProxyPort, getSessionId, getStats, hashRequestContent, injectAuthProfile, injectModelsConfig, isAgenticModel, isBalanceError, isBlockrunWebSearchDisabled, isEmptyWalletError, isInsufficientFundsError, isRetryable, isRpcError, isValidMnemonic, loadPaymentChain, logUsage, parseCallArgs, resolveModelAlias, resolvePaymentChain, savePaymentChain, setupSolana, startProxy, syncAgentModelCache };