@blockrun/clawrouter 0.12.269 → 0.12.271

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/dist/cli.js CHANGED
@@ -38541,8 +38541,8 @@ var init_models = __esm({
38541
38541
  id: "openai/gpt-5.6-sol",
38542
38542
  name: "GPT-5.6 Sol",
38543
38543
  version: "5.6",
38544
- inputPrice: 5,
38545
- outputPrice: 30,
38544
+ inputPrice: 4,
38545
+ outputPrice: 20,
38546
38546
  contextWindow: 105e4,
38547
38547
  maxOutput: 128e3,
38548
38548
  reasoning: true,
@@ -38583,8 +38583,8 @@ var init_models = __esm({
38583
38583
  id: "openai/gpt-5.6-sol-pro",
38584
38584
  name: "GPT-5.6 Sol Pro",
38585
38585
  version: "5.6",
38586
- inputPrice: 5,
38587
- outputPrice: 30,
38586
+ inputPrice: 4,
38587
+ outputPrice: 20,
38588
38588
  contextWindow: 105e4,
38589
38589
  maxOutput: 128e3,
38590
38590
  reasoning: true,
@@ -39022,8 +39022,8 @@ var init_models = __esm({
39022
39022
  id: "google/gemini-3.6-flash",
39023
39023
  name: "Gemini 3.6 Flash",
39024
39024
  version: "3.6",
39025
- inputPrice: 1.5,
39026
- outputPrice: 7.5,
39025
+ inputPrice: 0.75,
39026
+ outputPrice: 3.75,
39027
39027
  contextWindow: 1048576,
39028
39028
  maxOutput: 65536,
39029
39029
  reasoning: true,
@@ -39419,8 +39419,8 @@ var init_models = __esm({
39419
39419
  id: "xai/grok-4.5",
39420
39420
  name: "Grok 4.5",
39421
39421
  version: "4.5",
39422
- inputPrice: 2.5,
39423
- outputPrice: 9,
39422
+ inputPrice: 2,
39423
+ outputPrice: 6,
39424
39424
  contextWindow: 5e5,
39425
39425
  maxOutput: 16384,
39426
39426
  reasoning: true,
@@ -39434,8 +39434,8 @@ var init_models = __esm({
39434
39434
  id: "xai/grok-4.3",
39435
39435
  name: "Grok 4.3",
39436
39436
  version: "4.3",
39437
- inputPrice: 1.5,
39438
- outputPrice: 4,
39437
+ inputPrice: 1.25,
39438
+ outputPrice: 2.5,
39439
39439
  contextWindow: 1e6,
39440
39440
  maxOutput: 16384,
39441
39441
  reasoning: true,
@@ -39447,8 +39447,8 @@ var init_models = __esm({
39447
39447
  id: "xai/grok-build-0.1",
39448
39448
  name: "Grok Build 0.1",
39449
39449
  version: "0.1",
39450
- inputPrice: 1.5,
39451
- outputPrice: 3,
39450
+ inputPrice: 1,
39451
+ outputPrice: 2,
39452
39452
  contextWindow: 256e3,
39453
39453
  maxOutput: 16384,
39454
39454
  agentic: true,
@@ -40713,6 +40713,18 @@ function isBalanceError(error) {
40713
40713
  function isRpcError(error) {
40714
40714
  return error instanceof Error && error.code === "RPC_ERROR";
40715
40715
  }
40716
+ function describeFetchError(error) {
40717
+ const message = error instanceof Error ? error.message : String(error);
40718
+ const cause = error?.cause;
40719
+ if (!(cause instanceof Error)) return message;
40720
+ const detail = cause;
40721
+ const host = detail.hostname || detail.address;
40722
+ const where = host && detail.port ? `${host}:${detail.port}` : host;
40723
+ const parts = [detail.code, where, detail.code ? void 0 : detail.message].filter(
40724
+ (part) => typeof part === "string" && part !== ""
40725
+ );
40726
+ return parts.length > 0 ? `${message} (${parts.join(" ")})` : message;
40727
+ }
40716
40728
  var InsufficientFundsError2, EmptyWalletError, RpcError2;
40717
40729
  var init_errors5 = __esm({
40718
40730
  "src/errors.ts"() {
@@ -60054,13 +60066,18 @@ async function loadPaymentChain() {
60054
60066
  const core = await readOptional(CORE_CHAIN_FILE);
60055
60067
  if (core === "solana") return "solana";
60056
60068
  if (core === "base") return "base";
60057
- try {
60058
- const content = (await readTextFile(CHAIN_FILE)).trim();
60059
- if (content === "solana") return "solana";
60060
- return "base";
60061
- } catch {
60062
- return "base";
60063
- }
60069
+ const legacy = await readOptional(CHAIN_FILE);
60070
+ if (legacy === "solana") return "solana";
60071
+ if (legacy === "base") return "base";
60072
+ return await hasExistingWallet() ? "base" : "solana";
60073
+ }
60074
+ async function hasExistingWallet() {
60075
+ if (process["env"].BLOCKRUN_WALLET_KEY?.trim()) return true;
60076
+ const [core, legacy] = await Promise.all([
60077
+ readOptional(CORE_WALLET_FILE),
60078
+ readOptional(WALLET_FILE)
60079
+ ]);
60080
+ return Boolean(core || legacy);
60064
60081
  }
60065
60082
  async function resolvePaymentChain() {
60066
60083
  if (process["env"].CLAWROUTER_PAYMENT_CHAIN === "solana") return "solana";
@@ -60147,6 +60164,40 @@ async function clearApiKey() {
60147
60164
  }
60148
60165
  return { removed, envStillSet: Boolean(process["env"].BLOCKRUN_API_KEY?.trim()) };
60149
60166
  }
60167
+ async function fetchCreditBalance(apiKey, timeoutMs = 5e3) {
60168
+ try {
60169
+ const controller = new AbortController();
60170
+ const timer2 = setTimeout(() => controller.abort(), timeoutMs);
60171
+ const res = await fetch(`${BLOCKRUN_API_KEY_API}/v1/credits`, {
60172
+ headers: { authorization: `Bearer ${apiKey}` },
60173
+ signal: controller.signal
60174
+ });
60175
+ clearTimeout(timer2);
60176
+ if (!res.ok) return void 0;
60177
+ const b = await res.json();
60178
+ const num2 = (v) => typeof v === "number" ? v : null;
60179
+ return {
60180
+ accountId: typeof b.account_id === "string" ? b.account_id : void 0,
60181
+ billingMode: typeof b.billing_mode === "string" ? b.billing_mode : void 0,
60182
+ currency: typeof b.currency === "string" ? b.currency : "USD",
60183
+ grantedUsd: num2(b.granted_usd),
60184
+ spentUsd: num2(b.spent_usd),
60185
+ remainingUsd: num2(b.remaining_usd),
60186
+ blocked: b.blocked === true,
60187
+ blockedReason: typeof b.blocked_reason === "string" ? b.blocked_reason : void 0
60188
+ };
60189
+ } catch {
60190
+ return void 0;
60191
+ }
60192
+ }
60193
+ function formatCreditBalance(b) {
60194
+ const money = (v) => v === null ? void 0 : `$${v.toFixed(v < 0.01 && v > 0 ? 6 : 2)}`;
60195
+ const remaining = money(b.remainingUsd);
60196
+ const spent = money(b.spentUsd);
60197
+ if (remaining) return `${remaining} remaining${spent ? ` (spent ${spent})` : ""}`;
60198
+ const mode = b.billingMode === "ungated" ? "no prepaid limit" : b.billingMode ?? "unknown";
60199
+ return `${spent ? `spent ${spent}` : "unknown"} \u2014 ${mode}`;
60200
+ }
60150
60201
  function createApiKeyFetch(apiKey, baseFetch = fetch) {
60151
60202
  return async (input, init2) => {
60152
60203
  const headers = new Headers(init2?.headers);
@@ -60194,7 +60245,7 @@ var init_api_key = __esm({
60194
60245
  HINTS = {
60195
60246
  401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
60196
60247
  402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
60197
- 404: `api.blockrun.ai does not serve this endpoint yet \u2014 it currently carries chat and text completions. For media and partner APIs, run ClawRouter in wallet mode (unset BLOCKRUN_API_KEY and run "clawrouter logout").`
60248
+ 404: `api.blockrun.ai does not serve this endpoint. Chat, Anthropic-shaped messages, images, speech, video and the partner APIs (Surf, Exa, prediction markets, phone lookup) all work on an API key. The wallet-only exceptions are the routes that bind a lease or a position to a payer address \u2014 buying/renewing/releasing phone numbers, and Polymarket trading \u2014 which need a wallet to own the thing being bought. For those, unset BLOCKRUN_API_KEY and run "clawrouter logout".`
60198
60249
  };
60199
60250
  }
60200
60251
  });
@@ -90668,6 +90719,24 @@ var init_client3 = __esm({
90668
90719
  }
90669
90720
  });
90670
90721
 
90722
+ // node_modules/@x402/svm/dist/esm/v1/index.mjs
90723
+ var v1_exports = {};
90724
+ __export(v1_exports, {
90725
+ ExactSvmSchemeV1: () => ExactSvmSchemeV1,
90726
+ NETWORKS: () => NETWORKS2
90727
+ });
90728
+ var init_v1 = __esm({
90729
+ "node_modules/@x402/svm/dist/esm/v1/index.mjs"() {
90730
+ "use strict";
90731
+ init_chunk_WWACQNRQ();
90732
+ init_chunk_MNNIECS4();
90733
+ init_chunk_WIPN332D();
90734
+ init_chunk_FVTMOTG6();
90735
+ init_chunk_FJ6REVHH();
90736
+ init_chunk_QEBTC3LJ();
90737
+ }
90738
+ });
90739
+
90671
90740
  // src/proxy.ts
90672
90741
  import { AsyncLocalStorage } from "async_hooks";
90673
90742
  import { createHmac } from "crypto";
@@ -91422,6 +91491,23 @@ function estimateImageCost(model, size5, n = 1) {
91422
91491
  const pricePerImage = sizePrice ?? pricing.default;
91423
91492
  return pricePerImage * n * 1.05;
91424
91493
  }
91494
+ function blockrunRequestId(response) {
91495
+ if (!response) return void 0;
91496
+ for (const name of ["x-blockrun-request-id", "x-request-id", "request-id"]) {
91497
+ const value = response.headers.get(name)?.trim();
91498
+ if (value) return value;
91499
+ }
91500
+ return void 0;
91501
+ }
91502
+ function gatewaySettledCostUsd(response) {
91503
+ const raw = response?.headers.get("x-blockrun-cost-usd");
91504
+ if (raw === null || raw === void 0) return void 0;
91505
+ const trimmed = raw.trim();
91506
+ if (trimmed === "") return void 0;
91507
+ const value = Number(trimmed);
91508
+ if (!Number.isFinite(value) || value < 0) return void 0;
91509
+ return value;
91510
+ }
91425
91511
  async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPaymentUsd) {
91426
91512
  const startTime = Date.now();
91427
91513
  const upstreamUrl = `${apiBase}${req.url}`;
@@ -91471,8 +91557,9 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
91471
91557
  res.end();
91472
91558
  const latencyMs = Date.now() - startTime;
91473
91559
  console.log(`[ClawRouter] ${requestLabel} response: ${upstream.status} (${latencyMs}ms)`);
91474
- const paidAmount = getActualPaymentUsd();
91475
- const requestCost = resolvePhoneTelemetryCost({
91560
+ const settledCostUsd = gatewaySettledCostUsd(upstream);
91561
+ const paidAmount = settledCostUsd ?? getActualPaymentUsd();
91562
+ const requestCost = settledCostUsd !== void 0 ? settledCostUsd : resolvePhoneTelemetryCost({
91476
91563
  paidAmount,
91477
91564
  isPhone,
91478
91565
  upstreamStatus: upstream.status,
@@ -91481,6 +91568,7 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
91481
91568
  });
91482
91569
  logUsage({
91483
91570
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
91571
+ ...blockrunRequestId(upstream) ? { requestId: blockrunRequestId(upstream) } : {},
91484
91572
  model: isBlockrunExa ? "blockrun-exa" : isModalSandbox ? "modal-sandbox" : isPhone ? (req.url ?? "").replace(/^\/v1\//, "").split("?")[0] : isSurf ? (req.url ?? "").replace(/^\/v1\//, "").split("?")[0] : "partner",
91485
91573
  tier: isPhone ? "PHONE" : isSurf ? "SURF" : "PARTNER",
91486
91574
  cost: requestCost,
@@ -91672,8 +91760,20 @@ async function startProxy(options) {
91672
91760
  const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await Promise.resolve().then(() => (init_index_node37(), index_node_exports));
91673
91761
  const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
91674
91762
  solanaAddress = solanaSigner.address;
91763
+ const solanaRpcUrl = process["env"].CLAWROUTER_SOLANA_RPC_URL;
91675
91764
  registerExactSvmScheme2(x402, { signer: solanaSigner });
91765
+ if (solanaRpcUrl) {
91766
+ const { ExactSvmScheme: ExactSvmScheme2 } = await Promise.resolve().then(() => (init_client3(), client_exports));
91767
+ const { ExactSvmSchemeV1: ExactSvmSchemeV12, NETWORKS: SVM_V1_NETWORKS } = await Promise.resolve().then(() => (init_v1(), v1_exports));
91768
+ x402.register("solana:*", new ExactSvmScheme2(solanaSigner, { rpcUrl: solanaRpcUrl }));
91769
+ for (const network of SVM_V1_NETWORKS) {
91770
+ x402.registerV1(network, new ExactSvmSchemeV12(solanaSigner, { rpcUrl: solanaRpcUrl }));
91771
+ }
91772
+ }
91676
91773
  console.log(`[ClawRouter] Solana wallet: ${solanaAddress}`);
91774
+ if (solanaRpcUrl) {
91775
+ console.log(`[ClawRouter] Solana RPC (payment signing + balance): ${solanaRpcUrl}`);
91776
+ }
91677
91777
  }
91678
91778
  x402?.onAfterPaymentCreation(async (context) => {
91679
91779
  if (!context.selectedRequirements.network.startsWith("eip155")) return;
@@ -91773,7 +91873,15 @@ async function startProxy(options) {
91773
91873
  )
91774
91874
  ]);
91775
91875
  if (authMode === "api-key") {
91776
- response.balance = null;
91876
+ const credit = await fetchCreditBalance(apiKey, BALANCE_CHECK_TIMEOUT_MS);
91877
+ response.balance = credit?.remainingUsd ?? null;
91878
+ response.creditSpent = credit?.spentUsd ?? null;
91879
+ response.creditGranted = credit?.grantedUsd ?? null;
91880
+ response.billingMode = credit?.billingMode ?? null;
91881
+ response.creditSummary = credit ? formatCreditBalance(credit) : null;
91882
+ response.blocked = credit?.blocked ?? false;
91883
+ if (credit?.blockedReason) response.blockedReason = credit.blockedReason;
91884
+ response.isEmpty = credit?.remainingUsd !== null && (credit?.remainingUsd ?? 1) <= 0;
91777
91885
  response.billing = "BlockRun account credit";
91778
91886
  response.topUpUrl = PORTAL_CREDITS_URL;
91779
91887
  } else {
@@ -92578,7 +92686,8 @@ async function startProxy(options) {
92578
92686
  }
92579
92687
  return;
92580
92688
  }
92581
- if (req.url?.match(
92689
+ const isVerbatimGatewayPath = req.url === "/v1/messages" || (req.url?.startsWith("/v1/messages?") ?? false) || req.url === "/v1/audio/speech" || (req.url?.startsWith("/v1/audio/speech?") ?? false);
92690
+ if (isVerbatimGatewayPath || req.url?.match(
92582
92691
  /^\/v1\/(?:partner|pm|exa|modal|stocks|usstock|crypto|fx|commodity|phone|voice|surf)\//
92583
92692
  )) {
92584
92693
  try {
@@ -92610,6 +92719,19 @@ async function startProxy(options) {
92610
92719
  res.end(JSON.stringify({ error: "Not found" }));
92611
92720
  return;
92612
92721
  }
92722
+ if (!req.url.includes("/chat/completions")) {
92723
+ res.writeHead(404, { "Content-Type": "application/json" });
92724
+ res.end(
92725
+ JSON.stringify({
92726
+ error: {
92727
+ message: `ClawRouter does not proxy ${req.url}. It serves /v1/chat/completions, /v1/messages, /v1/models, /v1/images/generations, /v1/images/image2image, /v1/videos/generations, /v1/audio/generations, /v1/audio/speech, and the partner prefixes /v1/{partner,pm,exa,modal,stocks,usstock,crypto,fx,commodity,phone,voice,surf}/. If BlockRun serves this endpoint, call it directly against the gateway.`,
92728
+ type: "invalid_request_error",
92729
+ code: "unsupported_endpoint"
92730
+ }
92731
+ })
92732
+ );
92733
+ return;
92734
+ }
92613
92735
  try {
92614
92736
  await proxyRequest(
92615
92737
  req,
@@ -92864,7 +92986,8 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
92864
92986
  errorBody,
92865
92987
  errorStatus: response.status,
92866
92988
  isProviderError: category !== null,
92867
- errorCategory: category ?? void 0
92989
+ errorCategory: category ?? void 0,
92990
+ upstreamRequestId: blockrunRequestId(response)
92868
92991
  };
92869
92992
  }
92870
92993
  const contentType = response.headers.get("content-type") || "";
@@ -92901,9 +93024,12 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
92901
93024
  errorCategory: "payment_error"
92902
93025
  };
92903
93026
  }
93027
+ const networkMsg = describeFetchError(err);
93028
+ const solanaSigningHint = upstreamUrl.startsWith(BLOCKRUN_SOLANA_API) && networkMsg.startsWith("fetch failed") ? " \u2014 on the Solana rail this may be the RPC that signs the payment rather than the gateway; if api.mainnet-beta.solana.com is unreachable from this host, set CLAWROUTER_SOLANA_RPC_URL to your own endpoint" : "";
93029
+ console.error(`[ClawRouter] ${modelId} network error: ${networkMsg}${solanaSigningHint}`);
92904
93030
  return {
92905
93031
  success: false,
92906
- errorBody: errorMsg,
93032
+ errorBody: `${networkMsg}${solanaSigningHint}`,
92907
93033
  errorStatus: 500,
92908
93034
  isProviderError: true
92909
93035
  // Network errors are retryable
@@ -92936,6 +93062,7 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
92936
93062
  let accumulatedContent = "";
92937
93063
  let responseInputTokens;
92938
93064
  let responseOutputTokens;
93065
+ let upstreamRequestId;
92939
93066
  let requestHadError = false;
92940
93067
  let requestSummaryForStore = "";
92941
93068
  const isChatCompletion = req.url?.includes("/chat/completions");
@@ -94342,7 +94469,8 @@ data: [DONE]
94342
94469
  }
94343
94470
  lastError = {
94344
94471
  body: result.errorBody || "Unknown error",
94345
- status: result.errorStatus || 500
94472
+ status: result.errorStatus || 500,
94473
+ requestId: result.upstreamRequestId
94346
94474
  };
94347
94475
  failedAttempts.push({
94348
94476
  model: tryModel,
@@ -94414,7 +94542,8 @@ data: [DONE]
94414
94542
  }
94415
94543
  lastError = {
94416
94544
  body: retryResult.errorBody || lastError?.body || "Unknown error",
94417
- status: retryResult.errorStatus || lastError?.status || 500
94545
+ status: retryResult.errorStatus || lastError?.status || 500,
94546
+ requestId: retryResult.upstreamRequestId ?? lastError?.requestId
94418
94547
  };
94419
94548
  failedAttempts.push({
94420
94549
  model: tryModel,
@@ -94556,10 +94685,13 @@ data: [DONE]
94556
94685
  }
94557
94686
  }
94558
94687
  }
94688
+ upstreamRequestId = blockrunRequestId(upstream);
94559
94689
  if (!upstream) {
94560
94690
  const attemptSummary = failedAttempts.length > 0 ? failedAttempts.map((a) => `${a.model} (${a.reason})`).join(", ") : "unknown";
94561
94691
  const structuredMessage = failedAttempts.length > 0 ? `All ${failedAttempts.length} models failed. Tried: ${attemptSummary}` : "All models in fallback chain failed";
94562
- console.log(`[ClawRouter] ${structuredMessage}`);
94692
+ console.log(
94693
+ `[ClawRouter] ${structuredMessage}${lastError?.requestId ? ` | gateway request id: ${lastError.requestId}` : ""}`
94694
+ );
94563
94695
  const rawErrBody = lastError?.body || structuredMessage;
94564
94696
  const errStatus = lastError?.status || 502;
94565
94697
  const transformedErr = transformPaymentError(rawErrBody);
@@ -94597,7 +94729,8 @@ data: [DONE]
94597
94729
  res.writeHead(errStatus, {
94598
94730
  "Content-Type": "application/json",
94599
94731
  "x-context-used-kb": String(originalContextSizeKB),
94600
- "x-context-limit-kb": String(CONTEXT_LIMIT_KB)
94732
+ "x-context-limit-kb": String(CONTEXT_LIMIT_KB),
94733
+ ...lastError?.requestId ? { "x-blockrun-request-id": lastError.requestId } : {}
94601
94734
  });
94602
94735
  res.end(transformedErr);
94603
94736
  deduplicator.complete(dedupKey, {
@@ -95061,12 +95194,24 @@ data: [DONE]
95061
95194
  maxTokens,
95062
95195
  routingProfile ?? void 0
95063
95196
  );
95064
- logCost = FREE_MODELS.has(logModel) ? 0 : costs.costEstimate;
95197
+ const apiKeyPricing = options.apiKey ? routerOpts.modelPricing.get(logModel) : void 0;
95198
+ if (FREE_MODELS.has(logModel)) {
95199
+ logCost = 0;
95200
+ logSavings = 1;
95201
+ } else if (apiKeyPricing) {
95202
+ const inTokens = responseInputTokens ?? chargedInputTokens;
95203
+ const outTokens = responseOutputTokens ?? 0;
95204
+ logCost = (inTokens * (apiKeyPricing.inputPrice ?? 0) + outTokens * (apiKeyPricing.outputPrice ?? 0)) / 1e6;
95205
+ logSavings = costs.baselineCost > 0 ? Math.max(0, (costs.baselineCost - logCost) / costs.baselineCost) : 0;
95206
+ } else {
95207
+ logCost = costs.costEstimate;
95208
+ logSavings = costs.savings;
95209
+ }
95065
95210
  logBaseline = costs.baselineCost;
95066
- logSavings = FREE_MODELS.has(logModel) ? 1 : costs.savings;
95067
95211
  }
95068
95212
  const entry = {
95069
95213
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
95214
+ ...upstreamRequestId ? { requestId: upstreamRequestId } : {},
95070
95215
  model: logModel,
95071
95216
  tier: routingDecision?.tier ?? "DIRECT",
95072
95217
  cost: logCost,
@@ -95106,6 +95251,7 @@ var init_proxy = __esm({
95106
95251
  init_api_key();
95107
95252
  init_spend_control();
95108
95253
  init_compression();
95254
+ init_errors5();
95109
95255
  init_version4();
95110
95256
  init_session();
95111
95257
  init_updater();
@@ -229633,7 +229779,15 @@ ${errText}`
229633
229779
  });
229634
229780
  if (!isGatewayMode()) {
229635
229781
  if (shouldLogRegistration) {
229636
- resolveOrGenerateWalletKey().then(({ address: address2, source }) => {
229782
+ void (async () => {
229783
+ const keyResolution = await resolveApiKey().catch(() => void 0);
229784
+ if (keyResolution) {
229785
+ api.logger.info(
229786
+ `Using BlockRun API key ${maskApiKey(keyResolution.key)} (from ${keyResolution.source}) \u2014 billing account credit, no wallet`
229787
+ );
229788
+ return;
229789
+ }
229790
+ const { address: address2, source } = await resolveOrGenerateWalletKey();
229637
229791
  if (source === "generated") {
229638
229792
  api.logger.warn(`\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`);
229639
229793
  api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
@@ -229650,7 +229804,7 @@ ${errText}`
229650
229804
  } else {
229651
229805
  api.logger.info(`Using wallet from BLOCKRUN_WALLET_KEY: ${address2}`);
229652
229806
  }
229653
- }).catch((err) => {
229807
+ })().catch((err) => {
229654
229808
  api.logger.warn(
229655
229809
  `Failed to initialize wallet: ${err instanceof Error ? err.message : String(err)}`
229656
229810
  );
@@ -230582,6 +230736,12 @@ ClawRouter Status (port ${port})
230582
230736
  console.log(` Auth: BlockRun API key ${data.apiKey}`);
230583
230737
  console.log(` Gateway: ${data.gateway}`);
230584
230738
  console.log(` Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
230739
+ if (data.creditSummary) console.log(` Credit: ${data.creditSummary}`);
230740
+ if (data.blocked) {
230741
+ console.log(` \u26A0 Account is blocked${data.blockedReason ? `: ${data.blockedReason}` : ""}`);
230742
+ } else if (data.isEmpty) {
230743
+ console.log(` \u26A0 Credit exhausted \u2014 top up at ${PORTAL_CREDITS_URL}`);
230744
+ }
230585
230745
  } else {
230586
230746
  console.log(` Auth: wallet (x402)`);
230587
230747
  console.log(` Payment Chain: ${data.paymentChain}`);
@@ -230609,6 +230769,7 @@ ClawRouter Account
230609
230769
  console.log(` Auth: BlockRun API key ${data.apiKey}`);
230610
230770
  console.log(` Gateway: ${data.gateway}`);
230611
230771
  console.log(` Billing: account credit (no wallet, no chain)`);
230772
+ if (data.creditSummary) console.log(` Credit: ${data.creditSummary}`);
230612
230773
  console.log(` Top up: ${PORTAL_CREDITS_URL}`);
230613
230774
  console.log(` Usage: ${PORTAL_URL}/dashboard`);
230614
230775
  console.log();
package/dist/index.d.ts CHANGED
@@ -1248,13 +1248,29 @@ declare function setupSolana(): Promise<{
1248
1248
  declare function savePaymentChain(chain: "base" | "solana"): Promise<void>;
1249
1249
  /**
1250
1250
  * Load the persisted payment chain selection from disk.
1251
- * Returns "base" if no file exists or the file is invalid.
1252
- * New installs persist "solana" at wallet generation, so an absent file
1253
- * means a pre-existing install whose funds live on Base.
1251
+ *
1252
+ * Solana is the preferred rail, and since v0.12.246 wallet generation persists
1253
+ * `solana` outright — so a fresh install answers "solana" from its own chain
1254
+ * file, not from a default. This function only decides the case where no chain
1255
+ * file exists at all, and there the answer turns on whether a wallet already
1256
+ * does:
1257
+ *
1258
+ * no chain file, no wallet → "solana" (nothing to strand; prefer Solana)
1259
+ * no chain file, wallet on disk → "base" (a pre-v0.12.246 install)
1260
+ *
1261
+ * That second line is the one that must not move. An install predating the
1262
+ * Solana default has USDC sitting in its Base wallet and no Solana balance;
1263
+ * flipping it would point every request at a gateway its money is not on and
1264
+ * fail them all with an empty balance. The same reasoning covers a wallet
1265
+ * supplied through BLOCKRUN_WALLET_KEY, and a generated wallet whose Solana
1266
+ * derivation failed (no chain file is written in that case, on purpose — see
1267
+ * resolveOrGenerateWalletKey — and the wallet it did write keeps us on Base,
1268
+ * which is the only chain that proxy can actually sign for).
1254
1269
  */
1255
1270
  declare function loadPaymentChain(): Promise<"base" | "solana">;
1256
1271
  /**
1257
- * Resolve payment chain: env var first → persisted file second → default "base".
1272
+ * Resolve payment chain: env var first → persisted file second → the
1273
+ * fresh-install preference (Solana) last. See loadPaymentChain.
1258
1274
  */
1259
1275
  declare function resolvePaymentChain(): Promise<"base" | "solana">;
1260
1276
 
@@ -1400,6 +1416,23 @@ type UsageEntry = {
1400
1416
  partnerId?: string;
1401
1417
  /** Partner service name (e.g., "BlockRun") — only set for partner API calls */
1402
1418
  service?: string;
1419
+ /**
1420
+ * The gateway's own id for this request, from the `x-blockrun-request-id`
1421
+ * response header. Absent for requests that never reached the gateway (a
1422
+ * cache hit, a local refusal, an aborted call).
1423
+ *
1424
+ * This is the JOIN KEY for billing reconciliation. Every `cost` in this file
1425
+ * is a LOCAL estimate computed from our copy of the price table, so it drifts
1426
+ * whenever that copy goes stale — six prices were wrong until v0.12.270, and
1427
+ * `/stats` had been reporting the wrong money the whole time. Recording the
1428
+ * gateway's id is what will let us diff this journal line-by-line against a
1429
+ * server-side ledger and show what was actually charged instead of what we
1430
+ * guessed. It cannot be backfilled: a call whose id we did not record is
1431
+ * unreconcilable forever, which is why this is captured before the ledger API
1432
+ * that will consume it exists. It is also the id support needs to trace one
1433
+ * failed call.
1434
+ */
1435
+ requestId?: string;
1403
1436
  };
1404
1437
  /**
1405
1438
  * Log a usage entry as a JSON line.