@blockrun/clawrouter 0.12.246 → 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
@@ -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 |
package/dist/cli.js CHANGED
@@ -90816,25 +90816,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90816
90816
  const modelMatch = imageArgs.match(/--model\s+(\S+)/);
90817
90817
  if (modelMatch) {
90818
90818
  const raw = modelMatch[1];
90819
- const IMAGE_MODEL_ALIASES = {
90820
- // dall-e-3 delisted upstream 2026-05-25 — legacy aliases route to
90821
- // the OpenAI successor instead of a guaranteed 400.
90822
- "dall-e-3": "openai/gpt-image-2",
90823
- dalle3: "openai/gpt-image-2",
90824
- dalle: "openai/gpt-image-2",
90825
- "gpt-image": "openai/gpt-image-1",
90826
- "gpt-image-1": "openai/gpt-image-1",
90827
- "gpt-image-2": "openai/gpt-image-2",
90828
- seedream: "bytedance/seedream-5-pro",
90829
- banana: "google/nano-banana",
90830
- "nano-banana": "google/nano-banana",
90831
- "banana-pro": "google/nano-banana-pro",
90832
- "nano-banana-pro": "google/nano-banana-pro",
90833
- "grok-imagine": "xai/grok-imagine-image",
90834
- "grok-imagine-pro": "xai/grok-imagine-image-pro",
90835
- cogview: "zai/cogview-4"
90836
- };
90837
- imageModel = IMAGE_MODEL_ALIASES[raw] ?? raw;
90819
+ imageModel = Object.prototype.hasOwnProperty.call(IMAGE_MODEL_ALIASES, raw) ? IMAGE_MODEL_ALIASES[raw] : raw;
90838
90820
  imagePrompt = imagePrompt.replace(/--model\s+\S+/, "").trim();
90839
90821
  }
90840
90822
  const sizeMatch = imageArgs.match(/--size\s+(\d+x\d+)/);
@@ -90852,11 +90834,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90852
90834
  "",
90853
90835
  "Models:",
90854
90836
  " nano-banana Google Gemini Flash \u2014 $0.05/image",
90837
+ " banana-2 Google Nano Banana 2 \u2014 $0.09/image",
90855
90838
  " banana-pro Google Gemini Pro \u2014 $0.10/image (up to 4K)",
90856
90839
  " gpt-image OpenAI GPT Image 1 \u2014 $0.02/image",
90857
90840
  " gpt-image-2 OpenAI GPT Image 2 \u2014 $0.06/image",
90858
90841
  " seedream ByteDance Seedream 5 Pro \u2014 $0.045/image",
90859
90842
  " grok-imagine xAI Grok Imagine \u2014 $0.02/image",
90843
+ " grok-imagine-pro xAI Grok Imagine Pro \u2014 $0.07/image",
90860
90844
  " cogview Zhipu CogView-4 \u2014 $0.015/image",
90861
90845
  "",
90862
90846
  "Examples:",
@@ -90919,14 +90903,68 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90919
90903
  size: imageSize,
90920
90904
  n: 1
90921
90905
  });
90906
+ const imagegenAbort = new AbortController();
90907
+ res.on("close", () => {
90908
+ if (!res.writableEnded) imagegenAbort.abort();
90909
+ });
90922
90910
  const imageResponse = await payFetch(imageUpstreamUrl, {
90923
90911
  method: "POST",
90924
90912
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
90925
- body: imageBody
90913
+ body: imageBody,
90914
+ signal: imagegenAbort.signal
90926
90915
  });
90927
- 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
+ }
90928
90963
  let responseText;
90929
- 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) {
90930
90968
  const errMsg = typeof imageResult.error === "string" ? imageResult.error : imageResult.error?.message ?? `HTTP ${imageResponse.status}`;
90931
90969
  responseText = `Image generation failed: ${errMsg}`;
90932
90970
  console.log(`[ClawRouter] /imagegen error: ${errMsg}`);
@@ -90946,9 +90984,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90946
90984
  console.error(
90947
90985
  `[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
90948
90986
  );
90949
- lines.push(
90950
- "Image generated but upload failed. Try again or use --model dall-e-3."
90951
- );
90987
+ lines.push("Image generated but upload failed. Try again.");
90952
90988
  }
90953
90989
  } else {
90954
90990
  lines.push(img.url);
@@ -92732,7 +92768,7 @@ data: [DONE]
92732
92768
  });
92733
92769
  }
92734
92770
  }
92735
- 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;
92736
92772
  var init_proxy = __esm({
92737
92773
  "src/proxy.ts"() {
92738
92774
  "use strict";
@@ -92877,9 +92913,20 @@ var init_proxy = __esm({
92877
92913
  },
92878
92914
  "bytedance/seedream-5-pro": {
92879
92915
  default: 0.045,
92880
- 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
+ }
92881
92928
  },
92882
- "google/nano-banana": { default: 0.05 },
92929
+ "google/nano-banana": { default: 0.05, sizes: { "1024x1024": 0.05 } },
92883
92930
  // Nano Banana 2 (Gemini 3.1 Flash imagegen, blockrun #329): $0.09/image at 1K.
92884
92931
  "google/nano-banana-2": { default: 0.09, sizes: { "1024x1024": 0.09 } },
92885
92932
  "google/nano-banana-pro": {
@@ -92900,6 +92947,30 @@ var init_proxy = __esm({
92900
92947
  }
92901
92948
  }
92902
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
+ });
92903
92974
  VIDEO_PRICING = {
92904
92975
  "xai/grok-imagine-video": { pricePerSecond: 0.05, defaultDurationSeconds: 8 },
92905
92976
  // Seedance rates re-synced 2026-08-08 from blockrun's models.ts after the
@@ -93402,8 +93473,8 @@ var init_registry = __esm({
93402
93473
  name: "Image Generation",
93403
93474
  partner: "BlockRun",
93404
93475
  category: "Image & Video",
93405
- shortDescription: "8 image models (DALL-E, Flux, Grok, ...)",
93406
- 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.",
93407
93478
  proxyPath: "/images/generations",
93408
93479
  method: "POST",
93409
93480
  params: [
@@ -93416,7 +93487,7 @@ var init_registry = __esm({
93416
93487
  {
93417
93488
  name: "model",
93418
93489
  type: "string",
93419
- 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.",
93420
93491
  required: false
93421
93492
  },
93422
93493
  {
@@ -225359,6 +225430,7 @@ __export(index_exports, {
225359
225430
  SpendControl: () => SpendControl,
225360
225431
  VISIBLE_OPENCLAW_MODELS: () => VISIBLE_OPENCLAW_MODELS,
225361
225432
  blockrunProvider: () => blockrunProvider,
225433
+ buildImageGenerationProvider: () => buildImageGenerationProvider,
225362
225434
  buildPartnerTools: () => buildPartnerTools,
225363
225435
  buildProviderModels: () => buildProviderModels,
225364
225436
  calculateModelCost: () => calculateModelCost,
@@ -226122,12 +226194,23 @@ function buildImageGenerationProvider() {
226122
226194
  id: "blockrun",
226123
226195
  label: "BlockRun",
226124
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.
226125
226207
  models: [
226126
226208
  "google/nano-banana",
226209
+ "google/nano-banana-2",
226127
226210
  "google/nano-banana-pro",
226128
226211
  "openai/gpt-image-1",
226129
- "openai/dall-e-3",
226130
- "black-forest/flux-1.1-pro",
226212
+ "openai/gpt-image-2",
226213
+ "bytedance/seedream-5-pro",
226131
226214
  "xai/grok-imagine-image",
226132
226215
  "xai/grok-imagine-image-pro",
226133
226216
  "zai/cogview-4"
@@ -226142,20 +226225,28 @@ function buildImageGenerationProvider() {
226142
226225
  // Only openai/gpt-image-1 supports edit server-side; OpenClaw's UI picks a
226143
226226
  // compatible model at edit time via /v1/images/image2image.
226144
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.
226145
226233
  geometry: {
226146
226234
  sizes: [
226147
226235
  "512x512",
226148
226236
  "768x768",
226149
226237
  "768x1344",
226150
226238
  "1024x1024",
226151
- "1216x832",
226239
+ "1024x1536",
226240
+ "1280x720",
226152
226241
  "1344x768",
226153
226242
  "1440x1440",
226154
226243
  "1536x1024",
226155
- "1024x1536",
226156
- "1792x1024",
226157
- "1024x1792",
226244
+ "1600x2848",
226245
+ "1728x2304",
226246
+ "2048x1024",
226158
226247
  "2048x2048",
226248
+ "2304x1728",
226249
+ "2848x1600",
226159
226250
  "4096x4096"
226160
226251
  ]
226161
226252
  }
package/dist/index.d.ts CHANGED
@@ -1594,6 +1594,12 @@ declare function parseCallArgs(raw: string): {
1594
1594
  from?: string;
1595
1595
  language?: string;
1596
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;
1597
1603
  declare const plugin: OpenClawPluginDefinition;
1598
1604
 
1599
- 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 };
package/dist/index.js CHANGED
@@ -90636,25 +90636,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90636
90636
  const modelMatch = imageArgs.match(/--model\s+(\S+)/);
90637
90637
  if (modelMatch) {
90638
90638
  const raw = modelMatch[1];
90639
- const IMAGE_MODEL_ALIASES = {
90640
- // dall-e-3 delisted upstream 2026-05-25 — legacy aliases route to
90641
- // the OpenAI successor instead of a guaranteed 400.
90642
- "dall-e-3": "openai/gpt-image-2",
90643
- dalle3: "openai/gpt-image-2",
90644
- dalle: "openai/gpt-image-2",
90645
- "gpt-image": "openai/gpt-image-1",
90646
- "gpt-image-1": "openai/gpt-image-1",
90647
- "gpt-image-2": "openai/gpt-image-2",
90648
- seedream: "bytedance/seedream-5-pro",
90649
- banana: "google/nano-banana",
90650
- "nano-banana": "google/nano-banana",
90651
- "banana-pro": "google/nano-banana-pro",
90652
- "nano-banana-pro": "google/nano-banana-pro",
90653
- "grok-imagine": "xai/grok-imagine-image",
90654
- "grok-imagine-pro": "xai/grok-imagine-image-pro",
90655
- cogview: "zai/cogview-4"
90656
- };
90657
- imageModel = IMAGE_MODEL_ALIASES[raw] ?? raw;
90639
+ imageModel = Object.prototype.hasOwnProperty.call(IMAGE_MODEL_ALIASES, raw) ? IMAGE_MODEL_ALIASES[raw] : raw;
90658
90640
  imagePrompt = imagePrompt.replace(/--model\s+\S+/, "").trim();
90659
90641
  }
90660
90642
  const sizeMatch = imageArgs.match(/--size\s+(\d+x\d+)/);
@@ -90672,11 +90654,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90672
90654
  "",
90673
90655
  "Models:",
90674
90656
  " nano-banana Google Gemini Flash \u2014 $0.05/image",
90657
+ " banana-2 Google Nano Banana 2 \u2014 $0.09/image",
90675
90658
  " banana-pro Google Gemini Pro \u2014 $0.10/image (up to 4K)",
90676
90659
  " gpt-image OpenAI GPT Image 1 \u2014 $0.02/image",
90677
90660
  " gpt-image-2 OpenAI GPT Image 2 \u2014 $0.06/image",
90678
90661
  " seedream ByteDance Seedream 5 Pro \u2014 $0.045/image",
90679
90662
  " grok-imagine xAI Grok Imagine \u2014 $0.02/image",
90663
+ " grok-imagine-pro xAI Grok Imagine Pro \u2014 $0.07/image",
90680
90664
  " cogview Zhipu CogView-4 \u2014 $0.015/image",
90681
90665
  "",
90682
90666
  "Examples:",
@@ -90739,14 +90723,68 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90739
90723
  size: imageSize,
90740
90724
  n: 1
90741
90725
  });
90726
+ const imagegenAbort = new AbortController();
90727
+ res.on("close", () => {
90728
+ if (!res.writableEnded) imagegenAbort.abort();
90729
+ });
90742
90730
  const imageResponse = await payFetch(imageUpstreamUrl, {
90743
90731
  method: "POST",
90744
90732
  headers: { "content-type": "application/json", "user-agent": USER_AGENT },
90745
- body: imageBody
90733
+ body: imageBody,
90734
+ signal: imagegenAbort.signal
90746
90735
  });
90747
- const imageResult = await imageResponse.json();
90736
+ let imageResult = await imageResponse.json();
90737
+ let imageFailure;
90738
+ if (imageResponse.ok && imageResult.poll_url && imageResult.id && !imageResult.data?.length) {
90739
+ const apiOrigin = new URL(apiBase).origin;
90740
+ const pollUrl = imageResult.poll_url.startsWith("http") ? imageResult.poll_url : `${apiOrigin}${imageResult.poll_url}`;
90741
+ console.log(
90742
+ `[ClawRouter] /imagegen job submitted (id=${imageResult.id}), polling upstream \u2014 typical 30\u2013120s...`
90743
+ );
90744
+ const pollDeadline = Date.now() + 3e5;
90745
+ await new Promise((r2) => setTimeout(r2, 2e3));
90746
+ let completed2 = false;
90747
+ while (Date.now() < pollDeadline) {
90748
+ if (imagegenAbort.signal.aborted) {
90749
+ console.log(
90750
+ `[ClawRouter] Chat client disconnected \u2014 abandoning image poll (id=${imageResult.id})`
90751
+ );
90752
+ return;
90753
+ }
90754
+ const pollResp = await payFetch(pollUrl, {
90755
+ method: "GET",
90756
+ headers: { "user-agent": USER_AGENT },
90757
+ signal: imagegenAbort.signal
90758
+ });
90759
+ const pollText = await pollResp.text();
90760
+ let pollBody = {};
90761
+ try {
90762
+ pollBody = JSON.parse(pollText);
90763
+ } catch {
90764
+ imageFailure = `Non-JSON poll response (${pollResp.status}): ${pollText.slice(0, 200)}`;
90765
+ break;
90766
+ }
90767
+ if (pollResp.status === 202 || pollBody.status === "queued" || pollBody.status === "in_progress") {
90768
+ await new Promise((r2) => setTimeout(r2, 3e3));
90769
+ continue;
90770
+ }
90771
+ if (pollBody.status === "failed" || !pollResp.ok) {
90772
+ imageFailure = typeof pollBody.error === "string" ? pollBody.error : pollBody.error?.message ?? `Upstream image generation failed (HTTP ${pollResp.status})`;
90773
+ break;
90774
+ }
90775
+ imageResult = pollBody;
90776
+ completed2 = true;
90777
+ break;
90778
+ }
90779
+ if (!completed2 && !imageFailure) {
90780
+ imageFailure = "Image generation timed out after 5 minutes \u2014 the job may still complete upstream.";
90781
+ }
90782
+ }
90748
90783
  let responseText;
90749
- if (!imageResponse.ok || imageResult.error) {
90784
+ if (imageFailure) {
90785
+ responseText = `Image generation failed: ${imageFailure}`;
90786
+ console.log(`[ClawRouter] /imagegen error: ${imageFailure}`);
90787
+ } else if (!imageResponse.ok || imageResult.error) {
90750
90788
  const errMsg = typeof imageResult.error === "string" ? imageResult.error : imageResult.error?.message ?? `HTTP ${imageResponse.status}`;
90751
90789
  responseText = `Image generation failed: ${errMsg}`;
90752
90790
  console.log(`[ClawRouter] /imagegen error: ${errMsg}`);
@@ -90766,9 +90804,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
90766
90804
  console.error(
90767
90805
  `[ClawRouter] /imagegen: failed to upload data URI: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
90768
90806
  );
90769
- lines.push(
90770
- "Image generated but upload failed. Try again or use --model dall-e-3."
90771
- );
90807
+ lines.push("Image generated but upload failed. Try again.");
90772
90808
  }
90773
90809
  } else {
90774
90810
  lines.push(img.url);
@@ -92552,7 +92588,7 @@ data: [DONE]
92552
92588
  });
92553
92589
  }
92554
92590
  }
92555
- 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;
92591
+ 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;
92556
92592
  var init_proxy = __esm({
92557
92593
  "src/proxy.ts"() {
92558
92594
  "use strict";
@@ -92697,9 +92733,20 @@ var init_proxy = __esm({
92697
92733
  },
92698
92734
  "bytedance/seedream-5-pro": {
92699
92735
  default: 0.045,
92700
- sizes: { "1024x1024": 0.045, "2048x1024": 0.045, "2048x2048": 0.09 }
92736
+ // Full size list live-probed 2026-08-23 (gateway 402 quotes, base price):
92737
+ // the 2K portrait/landscape tiers bill at the 2048x2048 rate.
92738
+ sizes: {
92739
+ "1024x1024": 0.045,
92740
+ "1280x720": 0.045,
92741
+ "2048x1024": 0.045,
92742
+ "2048x2048": 0.09,
92743
+ "2304x1728": 0.09,
92744
+ "1728x2304": 0.09,
92745
+ "2848x1600": 0.09,
92746
+ "1600x2848": 0.09
92747
+ }
92701
92748
  },
92702
- "google/nano-banana": { default: 0.05 },
92749
+ "google/nano-banana": { default: 0.05, sizes: { "1024x1024": 0.05 } },
92703
92750
  // Nano Banana 2 (Gemini 3.1 Flash imagegen, blockrun #329): $0.09/image at 1K.
92704
92751
  "google/nano-banana-2": { default: 0.09, sizes: { "1024x1024": 0.09 } },
92705
92752
  "google/nano-banana-pro": {
@@ -92720,6 +92767,30 @@ var init_proxy = __esm({
92720
92767
  }
92721
92768
  }
92722
92769
  };
92770
+ IMAGE_MODEL_IDS = Object.freeze(Object.keys(IMAGE_PRICING));
92771
+ IMAGE_MODEL_SIZES = Object.freeze(
92772
+ [...new Set(Object.values(IMAGE_PRICING).flatMap((p) => Object.keys(p.sizes ?? {})))].sort()
92773
+ );
92774
+ IMAGE_MODEL_ALIASES = Object.freeze({
92775
+ // dall-e-3 delisted upstream 2026-05-25 — legacy aliases route to
92776
+ // the OpenAI successor instead of a guaranteed 400.
92777
+ "dall-e-3": "openai/gpt-image-2",
92778
+ dalle3: "openai/gpt-image-2",
92779
+ dalle: "openai/gpt-image-2",
92780
+ "gpt-image": "openai/gpt-image-1",
92781
+ "gpt-image-1": "openai/gpt-image-1",
92782
+ "gpt-image-2": "openai/gpt-image-2",
92783
+ seedream: "bytedance/seedream-5-pro",
92784
+ banana: "google/nano-banana",
92785
+ "nano-banana": "google/nano-banana",
92786
+ "banana-2": "google/nano-banana-2",
92787
+ "nano-banana-2": "google/nano-banana-2",
92788
+ "banana-pro": "google/nano-banana-pro",
92789
+ "nano-banana-pro": "google/nano-banana-pro",
92790
+ "grok-imagine": "xai/grok-imagine-image",
92791
+ "grok-imagine-pro": "xai/grok-imagine-image-pro",
92792
+ cogview: "zai/cogview-4"
92793
+ });
92723
92794
  VIDEO_PRICING = {
92724
92795
  "xai/grok-imagine-video": { pricePerSecond: 0.05, defaultDurationSeconds: 8 },
92725
92796
  // Seedance rates re-synced 2026-08-08 from blockrun's models.ts after the
@@ -93493,8 +93564,8 @@ var init_registry = __esm({
93493
93564
  name: "Image Generation",
93494
93565
  partner: "BlockRun",
93495
93566
  category: "Image & Video",
93496
- shortDescription: "8 image models (DALL-E, Flux, Grok, ...)",
93497
- 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.",
93567
+ shortDescription: "9 image models (Nano Banana, GPT Image, ...)",
93568
+ 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.",
93498
93569
  proxyPath: "/images/generations",
93499
93570
  method: "POST",
93500
93571
  params: [
@@ -93507,7 +93578,7 @@ var init_registry = __esm({
93507
93578
  {
93508
93579
  name: "model",
93509
93580
  type: "string",
93510
- description: "Full model ID (e.g. 'google/nano-banana', 'openai/dall-e-3'). Default: google/nano-banana.",
93581
+ description: "Full model ID (e.g. 'google/nano-banana', 'openai/gpt-image-2'). Default: google/nano-banana.",
93511
93582
  required: false
93512
93583
  },
93513
93584
  {
@@ -225875,12 +225946,23 @@ function buildImageGenerationProvider() {
225875
225946
  id: "blockrun",
225876
225947
  label: "BlockRun",
225877
225948
  defaultModel: "google/nano-banana",
225949
+ // Must stay in sync with IMAGE_PRICING (proxy.ts). OpenClaw sends the
225950
+ // picked id straight to /v1/images/generations, which forwards the body
225951
+ // verbatim with no alias resolution, so a retired id here is a guaranteed
225952
+ // upstream 400. dall-e-3 (delisted 2026-05-25) and flux-1.1-pro (no
225953
+ // gateway entry) were dropped in v0.12.227 and had lingered here.
225954
+ // Kept as a hand-written array (not spread from IMAGE_MODEL_IDS) because
225955
+ // this is the picker's curated display order — default model first, which
225956
+ // IMAGE_PRICING declaration order does not give — and because the
225957
+ // lifecycle tests fully mock ./proxy.js. Set-equality is enforced by
225958
+ // src/index.image-provider.test.ts, which pins the two lists together.
225878
225959
  models: [
225879
225960
  "google/nano-banana",
225961
+ "google/nano-banana-2",
225880
225962
  "google/nano-banana-pro",
225881
225963
  "openai/gpt-image-1",
225882
- "openai/dall-e-3",
225883
- "black-forest/flux-1.1-pro",
225964
+ "openai/gpt-image-2",
225965
+ "bytedance/seedream-5-pro",
225884
225966
  "xai/grok-imagine-image",
225885
225967
  "xai/grok-imagine-image-pro",
225886
225968
  "zai/cogview-4"
@@ -225895,20 +225977,28 @@ function buildImageGenerationProvider() {
225895
225977
  // Only openai/gpt-image-1 supports edit server-side; OpenClaw's UI picks a
225896
225978
  // compatible model at edit time via /v1/images/image2image.
225897
225979
  edit: { enabled: true },
225980
+ // Union of every size the gateway accepts across the models above,
225981
+ // live-probed 2026-08-23 (the gateway validates size per-model BEFORE
225982
+ // payment and 400s unknown ones — 1216x832 / 1792x1024 / 1024x1792 were
225983
+ // accepted by no model and are gone with dall-e-3). Pinned against
225984
+ // IMAGE_MODEL_SIZES (proxy.ts) by src/index.image-provider.test.ts.
225898
225985
  geometry: {
225899
225986
  sizes: [
225900
225987
  "512x512",
225901
225988
  "768x768",
225902
225989
  "768x1344",
225903
225990
  "1024x1024",
225904
- "1216x832",
225991
+ "1024x1536",
225992
+ "1280x720",
225905
225993
  "1344x768",
225906
225994
  "1440x1440",
225907
225995
  "1536x1024",
225908
- "1024x1536",
225909
- "1792x1024",
225910
- "1024x1792",
225996
+ "1600x2848",
225997
+ "1728x2304",
225998
+ "2048x1024",
225911
225999
  "2048x2048",
226000
+ "2304x1728",
226001
+ "2848x1600",
225912
226002
  "4096x4096"
225913
226003
  ]
225914
226004
  }
@@ -226874,6 +226964,7 @@ export {
226874
226964
  SpendControl,
226875
226965
  VISIBLE_OPENCLAW_MODELS,
226876
226966
  blockrunProvider,
226967
+ buildImageGenerationProvider,
226877
226968
  buildPartnerTools,
226878
226969
  buildProviderModels,
226879
226970
  calculateModelCost,
@@ -51,16 +51,22 @@ The returned URL is a publicly hosted image, ready to use in Telegram, Discord,
51
51
 
52
52
  ## Models & Pricing
53
53
 
54
- | Model ID | Shorthand | Price | Max Size | Provider |
55
- | --------------------------- | ------------- | ----------- | --------- | ------------------- |
56
- | `google/nano-banana` | `nano-banana` | $0.05/image | 1024×1024 | Google Gemini Flash |
57
- | `google/nano-banana-pro` | `banana-pro` | $0.10/image | 4096×4096 | Google Gemini Pro |
58
- | `openai/dall-e-3` | `dall-e-3` | $0.04/image | 1792×1024 | OpenAI DALL-E 3 |
59
- | `openai/gpt-image-1` | `gpt-image` | $0.02/image | 1536×1024 | OpenAI GPT Image |
60
- | `black-forest/flux-1.1-pro` | `flux` | $0.04/image | 1024×1024 | Black Forest Labs |
54
+ | Model ID | Shorthand | Price | Sizes | Provider |
55
+ | ---------------------------- | ------------------ | ------------------- | ---------------------------------------------------------------------------------- | --------------------- |
56
+ | `google/nano-banana` | `nano-banana` | $0.05/image | 1024×1024 | Google Gemini Flash |
57
+ | `google/nano-banana-2` | `banana-2` | $0.09/image | 1024×1024 | Google Nano Banana 2 |
58
+ | `google/nano-banana-pro` | `banana-pro` | $0.10–$0.15/image | 1024×1024, 2048×2048, 4096×4096 | Google Gemini Pro |
59
+ | `openai/gpt-image-1` | `gpt-image` | $0.02–$0.04/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 1 |
60
+ | `openai/gpt-image-2` | `gpt-image-2` | $0.06–$0.12/image | 1024×1024, 1536×1024, 1024×1536 | OpenAI GPT Image 2 |
61
+ | `bytedance/seedream-5-pro` | `seedream` | $0.045–$0.09/image | 1024×1024, 1280×720, 2048×1024, 2048×2048, 2304×1728, 1728×2304, 2848×1600, 1600×2848 | ByteDance Seedream 5 |
62
+ | `xai/grok-imagine-image` | `grok-imagine` | $0.02/image | 1024×1024 | xAI Grok Imagine |
63
+ | `xai/grok-imagine-image-pro` | `grok-imagine-pro` | $0.07/image | 1024×1024 | xAI Grok Imagine Pro |
64
+ | `zai/cogview-4` | `cogview` | $0.015–$0.02/image | 512×512, 768×768, 1024×1024, 768×1344, 1344×768, 1440×1440 | Zhipu CogView-4 |
61
65
 
62
66
  Default model: `google/nano-banana`.
63
67
 
68
+ > `openai/dall-e-3` was delisted upstream on 2026-05-25 and `black-forest/flux-1.1-pro` is no longer served; the legacy `dall-e-3` / `dalle` shorthands now route to `openai/gpt-image-2`. The gateway validates `size` per model **before** payment — sizes outside the lists above return an error and cost nothing. Slow models (notably `gpt-image-2`) can take longer than 30s; the proxy transparently polls the job for up to 5 minutes and still returns a single blocking response.
69
+
64
70
  ---
65
71
 
66
72
  ## API Reference
@@ -85,7 +91,7 @@ OpenAI-compatible endpoint. Route via ClawRouter proxy (`http://localhost:8402`)
85
91
  created: number; // Unix timestamp
86
92
  data: Array<{
87
93
  url: string; // Publicly hosted image URL
88
- revised_prompt?: string; // Model's rewritten prompt (dall-e-3 only)
94
+ revised_prompt?: string; // Model's rewritten prompt (when the model provides one)
89
95
  }>;
90
96
  }
91
97
  ```
@@ -146,13 +152,13 @@ curl -X POST http://localhost:8402/v1/images/generations \
146
152
  "n": 1
147
153
  }'
148
154
 
149
- # DALL-E 3 with landscape size ($0.04)
155
+ # GPT Image 2 with landscape size ($0.12 — slow model, the proxy polls for you)
150
156
  curl -X POST http://localhost:8402/v1/images/generations \
151
157
  -H "Content-Type: application/json" \
152
158
  -d '{
153
- "model": "openai/dall-e-3",
159
+ "model": "openai/gpt-image-2",
154
160
  "prompt": "a serene Japanese garden in autumn",
155
- "size": "1792x1024",
161
+ "size": "1536x1024",
156
162
  "n": 1
157
163
  }'
158
164
  ```
@@ -233,9 +239,9 @@ const response = await fetch(`${proxy.baseUrl}/v1/images/generations`, {
233
239
  method: "POST",
234
240
  headers: { "Content-Type": "application/json" },
235
241
  body: JSON.stringify({
236
- model: "openai/dall-e-3",
242
+ model: "bytedance/seedream-5-pro",
237
243
  prompt: "a serene Japanese garden in autumn",
238
- size: "1792x1024",
244
+ size: "2048x1024",
239
245
  n: 1,
240
246
  }),
241
247
  });
@@ -335,7 +341,7 @@ When using ClawRouter with OpenClaw, generate and edit images directly from any
335
341
 
336
342
  ```
337
343
  /cr-imagegen a dog dancing on the beach
338
- /cr-imagegen --model dall-e-3 a futuristic city at sunset
344
+ /cr-imagegen --model gpt-image-2 a futuristic city at sunset
339
345
  /cr-imagegen --model banana-pro --size 2048x2048 mountain landscape
340
346
  ```
341
347
 
@@ -363,13 +369,18 @@ When using ClawRouter with OpenClaw, generate and edit images directly from any
363
369
 
364
370
  ### Model shorthands
365
371
 
366
- | Shorthand | Full ID |
367
- | ------------- | --------------------------- |
368
- | `nano-banana` | `google/nano-banana` |
369
- | `banana-pro` | `google/nano-banana-pro` |
370
- | `dall-e-3` | `openai/dall-e-3` |
371
- | `gpt-image` | `openai/gpt-image-1` |
372
- | `flux` | `black-forest/flux-1.1-pro` |
372
+ | Shorthand | Full ID |
373
+ | ------------------------------------ | ---------------------------- |
374
+ | `nano-banana`, `banana` | `google/nano-banana` |
375
+ | `banana-2`, `nano-banana-2` | `google/nano-banana-2` |
376
+ | `banana-pro`, `nano-banana-pro` | `google/nano-banana-pro` |
377
+ | `gpt-image`, `gpt-image-1` | `openai/gpt-image-1` |
378
+ | `gpt-image-2` | `openai/gpt-image-2` |
379
+ | `seedream` | `bytedance/seedream-5-pro` |
380
+ | `grok-imagine` | `xai/grok-imagine-image` |
381
+ | `grok-imagine-pro` | `xai/grok-imagine-image-pro` |
382
+ | `cogview` | `zai/cogview-4` |
383
+ | `dall-e-3`, `dalle3`, `dalle` (legacy) | `openai/gpt-image-2` |
373
384
 
374
385
  ---
375
386
 
@@ -377,6 +388,6 @@ When using ClawRouter with OpenClaw, generate and edit images directly from any
377
388
 
378
389
  - **Local image caching** — All images (generated and edited) are cached locally at `~/.openclaw/blockrun/images/` and served via `http://localhost:8402/images/`. Both base64 data URIs and HTTP URLs from upstream are downloaded and replaced with localhost URLs.
379
390
  - **Payment** — Each image costs the listed price in USDC, deducted from your wallet via x402. Make sure your wallet is funded before generating or editing.
380
- - **No DALL-E content policy bypass** — DALL-E 3 and GPT Image 1 still apply OpenAI's content policy. Use `flux` or `nano-banana` for more flexibility with generation.
381
- - **Size limits** — Requesting a size larger than the model's max will return an error. Check the table above before setting `--size`.
391
+ - **Content policy** — OpenAI image models (GPT Image 1/2) apply OpenAI's content policy. Use `nano-banana` or `grok-imagine` for more flexibility with generation.
392
+ - **Size limits** — The gateway validates `size` per model before payment; a size outside the model's list returns an error and costs nothing. Check the table above before setting `--size`.
382
393
  - **Image editing** — The `/v1/images/image2image` endpoint currently supports `openai/gpt-image-1` (default). The `image` and `mask` fields accept local file paths (`~/photo.png`, `/abs/path.png`), HTTP/HTTPS URLs, or base64 data URIs. ClawRouter handles file reading and URL downloading automatically. Supported formats: PNG, JPG/JPEG, WebP.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/clawrouter",
3
- "version": "0.12.246",
3
+ "version": "0.12.247",
4
4
  "description": "Smart LLM router — save 88% on inference costs. 71 models (5 free), one wallet, x402 micropayments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -899,7 +899,7 @@ echo " /exclude add <model> → block a model from routing"
899
899
  echo ""
900
900
  echo "Image generation:"
901
901
  echo " /imagegen <prompt> # default: nano-banana"
902
- echo " /imagegen --model dall-e-3 <prompt> # DALL-E 3"
902
+ echo " /imagegen --model gpt-image-2 <prompt> # GPT Image 2"
903
903
  echo " /imagegen --model gpt-image <prompt> # GPT Image 1"
904
904
  echo ""
905
905
  echo "CLI commands:"
@@ -164,7 +164,7 @@ Realtime prices and historical OHLC across every asset class. The agent should c
164
164
 
165
165
  | Tool | Purpose | Price |
166
166
  | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
167
- | `blockrun_image_generation` | 8 image models — GPT Image 1/2, Nano Banana / Pro, Seedream 5 Pro, Grok Imagine / Pro, CogView-4 | $0.015–$0.15 / image |
167
+ | `blockrun_image_generation` | 9 image models — GPT Image 1/2, Nano Banana / 2 / Pro, Seedream 5 Pro, Grok Imagine / Pro, CogView-4 | $0.015–$0.15 / image |
168
168
  | `blockrun_image_edit` | Edit / inpaint existing image (openai/gpt-image-1) | $0.02–$0.04 / image |
169
169
  | `blockrun_video_generation` | Grok Imagine + ByteDance Seedance (1.5-pro / 2.0-fast / 2.0) at 720p with synced audio (t2v default), 5–10s, + OpenAI Sora 2 via Azure (`azure/sora-2`, 4/8/12s, t2v + i2v, rejects human faces in reference images). Seedance is token-priced upstream (~20,256 tokens/sec at the 720p+audio default — 2× the prior 480p rate); `image_url` (image-to-video) costs the same as t2v since 2026-06-01. Seedance 2.0 variants accept optional `real_face_asset_id` (`ta_…`) for BytePlus RealFace character-consistency — mutually exclusive with `image_url`. | $0.05/s (Grok); $0.10/s (Sora 2); Seedance ~$0.46–$1.49 per 5s clip |
170
170
 
@@ -57,8 +57,9 @@ Display inline: `![generated image](http://localhost:8402/images/abc123.png)`
57
57
 
58
58
  | Alias | Full ID | Price | Sizes | Best for |
59
59
  | ------------------ | ---------------------------- | ------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
60
- | `nano-banana` | `google/nano-banana` | $0.05 | 1024×1024, 1216×832, 1024×1792 | Default — fast, cheap, good quality |
61
- | `banana-pro` | `google/nano-banana-pro` | $0.10–$0.15 | up to 4096×4096 | High-res, large format |
60
+ | `nano-banana` | `google/nano-banana` | $0.05 | 1024×1024 | Default — fast, cheap, good quality |
61
+ | `banana-2` | `google/nano-banana-2` | $0.09 | 1024×1024 | Gemini 3.1 Flash imagegen — sharper than nano-banana at 1K |
62
+ | `banana-pro` | `google/nano-banana-pro` | $0.10–$0.15 | 1024×1024, 2048×2048, 4096×4096 | High-res, large format |
62
63
  | `gpt-image` | `openai/gpt-image-1` | $0.02–$0.04 | 1024×1024, 1536×1024, 1024×1536 | Budget option; supports editing |
63
64
  | `gpt-image-2` | `openai/gpt-image-2` | $0.06–$0.12 | 1024×1024, 1536×1024, 1024×1536 | Photorealistic, reasoning-driven, text rendering (slow — proxy polls up to 5min); legacy `dalle` alias routes here |
64
65
  | `seedream` | `bytedance/seedream-5-pro` | $0.045–$0.09 | up to 2848×1600 / 2304×1728 | Flagship quality, reference-image support |
@@ -79,10 +80,11 @@ Display inline: `![generated image](http://localhost:8402/images/abc123.png)`
79
80
 
80
81
  **Choosing a size:**
81
82
 
82
- - Default: `1024x1024`
83
- - Portrait: `1024x1792`
84
- - Landscape: `1536x1024` (gpt-image / gpt-image-2) or `1216x832` (nano-banana)
85
- - High-res: `2048x2048` or `4096x4096` with `banana-pro` only
83
+ - Default: `1024x1024` (the only size every model accepts)
84
+ - Portrait: `1024x1536` (gpt-image / gpt-image-2) or `1728x2304` (seedream)
85
+ - Landscape: `1536x1024` (gpt-image / gpt-image-2), `1344x768` (cogview), or `2048x1024` / `1280x720` (seedream)
86
+ - High-res: `2048x2048` / `4096x4096` with `banana-pro`; `2848x1600` with `seedream`
87
+ - The gateway validates size per model BEFORE payment and rejects unknown ones — do not invent sizes outside each model's list above
86
88
 
87
89
  ---
88
90
 
@@ -123,7 +125,7 @@ Response is identical to generation:
123
125
  → POST to `/v1/images/generations`, model `nano-banana`, prompt as given.
124
126
 
125
127
  **User:** Generate a high-res portrait of a samurai
126
- → POST to `/v1/images/generations`, model `banana-pro`, size `1024x1792`.
128
+ → POST to `/v1/images/generations`, model `seedream`, size `1728x2304`.
127
129
 
128
130
  **User:** Edit this photo to add a sunset background: https://example.com/portrait.jpg
129
131
  → POST to `/v1/images/image2image`, model `gpt-image`, image = the URL, prompt = "add a warm sunset background".