@blockrun/clawrouter 0.12.266 → 0.12.268

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/index.js CHANGED
@@ -35091,6 +35091,7 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
35091
35091
  const cached = !options?.skipPreAuth ? cache2.get(cacheKey2) : void 0;
35092
35092
  const preAuthCovers = cached !== void 0 && Date.now() - cached.cachedAt < ttlMs && cached.coverMicros !== void 0 && needMicros !== void 0 && needMicros <= cached.coverMicros;
35093
35093
  if (preAuthCovers) {
35094
+ let paymentInFlight = false;
35094
35095
  try {
35095
35096
  const payload2 = await client.createPaymentPayload(cached.paymentRequired);
35096
35097
  const headers = httpClient.encodePaymentSignatureHeader(payload2);
@@ -35098,16 +35099,21 @@ function createPayFetchWithPreAuth(baseFetch, client, ttlMs = DEFAULT_TTL_MS, op
35098
35099
  for (const [key2, value] of Object.entries(headers)) {
35099
35100
  preAuthRequest.headers.set(key2, value);
35100
35101
  }
35102
+ paymentInFlight = true;
35101
35103
  const response2 = await baseFetch(preAuthRequest);
35102
35104
  if (response2.status !== 402) {
35103
35105
  return response2;
35104
35106
  }
35107
+ paymentInFlight = false;
35105
35108
  cache2.delete(cacheKey2);
35106
35109
  } catch (err) {
35107
35110
  cache2.delete(cacheKey2);
35108
35111
  if (err instanceof SpendPolicyError) {
35109
35112
  throw err;
35110
35113
  }
35114
+ if (paymentInFlight) {
35115
+ throw err;
35116
+ }
35111
35117
  }
35112
35118
  }
35113
35119
  const clonedRequest = request2.clone();
@@ -40553,7 +40559,7 @@ var init_errors5 = __esm({
40553
40559
  });
40554
40560
 
40555
40561
  // src/balance.ts
40556
- var USDC_BASE, CACHE_TTL_MS, BALANCE_THRESHOLDS, BalanceMonitor;
40562
+ var USDC_BASE, CACHE_TTL_MS, BALANCE_THRESHOLDS, BalanceMonitor, ApiKeyBalanceMonitor;
40557
40563
  var init_balance = __esm({
40558
40564
  "src/balance.ts"() {
40559
40565
  "use strict";
@@ -40682,6 +40688,38 @@ var init_balance = __esm({
40682
40688
  };
40683
40689
  }
40684
40690
  };
40691
+ ApiKeyBalanceMonitor = class _ApiKeyBalanceMonitor {
40692
+ /** Large enough that every `balance >= estimatedCost` comparison passes. */
40693
+ static UNMETERED = BigInt(Number.MAX_SAFE_INTEGER);
40694
+ async checkBalance() {
40695
+ return {
40696
+ balance: _ApiKeyBalanceMonitor.UNMETERED,
40697
+ // Never a dollar figure: no caller may print this as if it were one.
40698
+ balanceUSD: "account credit",
40699
+ isLow: false,
40700
+ isEmpty: false,
40701
+ walletAddress: ""
40702
+ };
40703
+ }
40704
+ async checkSufficient() {
40705
+ return { sufficient: true, info: await this.checkBalance() };
40706
+ }
40707
+ /** No local cache to adjust — the server keeps the books. */
40708
+ deductEstimated() {
40709
+ }
40710
+ invalidate() {
40711
+ }
40712
+ async refresh() {
40713
+ return this.checkBalance();
40714
+ }
40715
+ formatUSDC(amountMicros) {
40716
+ return `$${(Number(amountMicros) / 1e6).toFixed(2)}`;
40717
+ }
40718
+ /** No wallet in this mode; callers render the account instead. */
40719
+ getWalletAddress() {
40720
+ return "";
40721
+ }
40722
+ };
40685
40723
  }
40686
40724
  });
40687
40725
 
@@ -59781,6 +59819,99 @@ var init_auth = __esm({
59781
59819
  }
59782
59820
  });
59783
59821
 
59822
+ // src/api-key.ts
59823
+ import { writeFile as writeFile2, mkdir as mkdir3, rm } from "fs/promises";
59824
+ import { join as join8 } from "path";
59825
+ import { homedir as homedir5 } from "os";
59826
+ function isValidApiKey(value) {
59827
+ if (typeof value !== "string") return false;
59828
+ return /^brk_[A-Za-z0-9_-]{8,}$/.test(value.trim());
59829
+ }
59830
+ function maskApiKey(key2) {
59831
+ const trimmed = key2.trim();
59832
+ if (trimmed.length <= 18) return `${trimmed.slice(0, 8)}\u2026`;
59833
+ return `${trimmed.slice(0, 14)}\u2026${trimmed.slice(-4)}`;
59834
+ }
59835
+ async function readOptional2(path5) {
59836
+ try {
59837
+ return (await readTextFile(path5)).trim() || void 0;
59838
+ } catch {
59839
+ return void 0;
59840
+ }
59841
+ }
59842
+ async function resolveApiKey() {
59843
+ const envKey = process["env"].BLOCKRUN_API_KEY?.trim();
59844
+ if (envKey) {
59845
+ if (isValidApiKey(envKey)) return { key: envKey, source: "env" };
59846
+ console.warn(
59847
+ `[ClawRouter] \u26A0 BLOCKRUN_API_KEY is set but does not look like a BlockRun key (expected brk_\u2026) \u2014 ignoring.`
59848
+ );
59849
+ }
59850
+ for (const [path5, source] of [
59851
+ [CORE_API_KEY_FILE, "core"],
59852
+ [API_KEY_FILE, "saved"]
59853
+ ]) {
59854
+ const stored = await readOptional2(path5);
59855
+ if (stored === void 0) continue;
59856
+ if (isValidApiKey(stored)) return { key: stored, source };
59857
+ console.warn(
59858
+ `[ClawRouter] \u26A0 ${path5} does not contain a BlockRun key (expected brk_\u2026) \u2014 ignoring.`
59859
+ );
59860
+ }
59861
+ return void 0;
59862
+ }
59863
+ function createApiKeyFetch(apiKey, baseFetch = fetch) {
59864
+ return async (input, init2) => {
59865
+ const headers = new Headers(init2?.headers);
59866
+ headers.set("authorization", `Bearer ${apiKey}`);
59867
+ headers.delete("x-payment");
59868
+ headers.delete("x-api-key");
59869
+ const response = await baseFetch(input, { ...init2, headers });
59870
+ return response.ok ? response : explainApiKeyFailure(response);
59871
+ };
59872
+ }
59873
+ async function explainApiKeyFailure(response) {
59874
+ const hint = HINTS[response.status];
59875
+ if (!hint) return response;
59876
+ if (!(response.headers.get("content-type") ?? "").includes("json")) return response;
59877
+ let parsed;
59878
+ try {
59879
+ parsed = await response.clone().json();
59880
+ } catch {
59881
+ return response;
59882
+ }
59883
+ if (!parsed?.error || typeof parsed.error.message !== "string") return response;
59884
+ if (response.status === 404 && !/unsupported endpoint/i.test(parsed.error.message)) {
59885
+ return response;
59886
+ }
59887
+ parsed.error.message = `${parsed.error.message} ${hint}`;
59888
+ const headers = new Headers(response.headers);
59889
+ headers.delete("content-length");
59890
+ return new Response(JSON.stringify(parsed), {
59891
+ status: response.status,
59892
+ statusText: response.statusText,
59893
+ headers
59894
+ });
59895
+ }
59896
+ var API_KEY_FILE, CORE_API_KEY_FILE, PORTAL_URL, PORTAL_KEYS_URL, PORTAL_CREDITS_URL, BLOCKRUN_API_KEY_API, HINTS;
59897
+ var init_api_key = __esm({
59898
+ "src/api-key.ts"() {
59899
+ "use strict";
59900
+ init_fs_read();
59901
+ API_KEY_FILE = join8(homedir5(), ".openclaw", "blockrun", "api-key");
59902
+ CORE_API_KEY_FILE = join8(homedir5(), ".blockrun", ".api-key");
59903
+ PORTAL_URL = "https://user.blockrun.ai";
59904
+ PORTAL_KEYS_URL = `${PORTAL_URL}/dashboard/keys`;
59905
+ PORTAL_CREDITS_URL = `${PORTAL_URL}/dashboard/credits`;
59906
+ BLOCKRUN_API_KEY_API = process["env"].BLOCKRUN_API_BASE_URL?.replace(/\/+$/, "") || "https://api.blockrun.ai";
59907
+ HINTS = {
59908
+ 401: `Check BLOCKRUN_API_KEY, or mint a new key at ${PORTAL_KEYS_URL}.`,
59909
+ 402: `Top up your BlockRun credit at ${PORTAL_CREDITS_URL}.`,
59910
+ 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").`
59911
+ };
59912
+ }
59913
+ });
59914
+
59784
59915
  // src/compression/types.ts
59785
59916
  var DEFAULT_COMPRESSION_CONFIG;
59786
59917
  var init_types2 = __esm({
@@ -60870,8 +61001,8 @@ var init_updater = __esm({
60870
61001
 
60871
61002
  // src/exclude-models.ts
60872
61003
  import { readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, statSync } from "fs";
60873
- import { join as join8, dirname as dirname2 } from "path";
60874
- import { homedir as homedir5 } from "os";
61004
+ import { join as join9, dirname as dirname2 } from "path";
61005
+ import { homedir as homedir6 } from "os";
60875
61006
  function loadExcludeList(filePath = DEFAULT_FILE_PATH) {
60876
61007
  try {
60877
61008
  const mtimeMs = statSync(filePath).mtimeMs;
@@ -60920,7 +61051,7 @@ var init_exclude_models = __esm({
60920
61051
  "src/exclude-models.ts"() {
60921
61052
  "use strict";
60922
61053
  init_models();
60923
- DEFAULT_FILE_PATH = join8(homedir5(), ".openclaw", "blockrun", "exclude-models.json");
61054
+ DEFAULT_FILE_PATH = join9(homedir6(), ".openclaw", "blockrun", "exclude-models.json");
60924
61055
  loadCache = /* @__PURE__ */ new Map();
60925
61056
  }
60926
61057
  });
@@ -75929,7 +76060,7 @@ var require_snapshot_utils = __commonJS({
75929
76060
  var require_snapshot_recorder = __commonJS({
75930
76061
  "node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
75931
76062
  "use strict";
75932
- var { writeFile: writeFile3, readFile: readFile3, mkdir: mkdir5 } = __require("fs/promises");
76063
+ var { writeFile: writeFile4, readFile: readFile3, mkdir: mkdir6 } = __require("fs/promises");
75933
76064
  var { dirname: dirname4, resolve } = __require("path");
75934
76065
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
75935
76066
  var { InvalidArgumentError, UndiciError } = require_errors();
@@ -76176,12 +76307,12 @@ var require_snapshot_recorder = __commonJS({
76176
76307
  throw new InvalidArgumentError("Snapshot path is required");
76177
76308
  }
76178
76309
  const resolvedPath = resolve(path5);
76179
- await mkdir5(dirname4(resolvedPath), { recursive: true });
76310
+ await mkdir6(dirname4(resolvedPath), { recursive: true });
76180
76311
  const data = Array.from(this.#snapshots.entries()).map(([hash5, snapshot]) => ({
76181
76312
  hash: hash5,
76182
76313
  snapshot
76183
76314
  }));
76184
- await writeFile3(resolvedPath, JSON.stringify(data, null, 2), { flush: true });
76315
+ await writeFile4(resolvedPath, JSON.stringify(data, null, 2), { flush: true });
76185
76316
  }
76186
76317
  /**
76187
76318
  * Clears all recorded snapshots
@@ -88510,9 +88641,9 @@ var init_textual_tool_calls = __esm({
88510
88641
  });
88511
88642
 
88512
88643
  // src/response-store.ts
88513
- import { appendFile as appendFile2, mkdir as mkdir3, readFile, readdir as readdir2 } from "fs/promises";
88514
- import { homedir as homedir6 } from "os";
88515
- import { join as join9 } from "path";
88644
+ import { appendFile as appendFile2, mkdir as mkdir4, readFile, readdir as readdir2 } from "fs/promises";
88645
+ import { homedir as homedir7 } from "os";
88646
+ import { join as join10 } from "path";
88516
88647
  import { randomBytes as randomBytes7 } from "crypto";
88517
88648
  function isEnabled() {
88518
88649
  const v = process.env.BLOCKRUN_RESPONSE_STORE;
@@ -88520,12 +88651,12 @@ function isEnabled() {
88520
88651
  }
88521
88652
  async function ensureDir2() {
88522
88653
  if (dirReady2) return;
88523
- await mkdir3(STORE_DIR, { recursive: true });
88654
+ await mkdir4(STORE_DIR, { recursive: true });
88524
88655
  dirReady2 = true;
88525
88656
  }
88526
88657
  function dailyFile(date = /* @__PURE__ */ new Date()) {
88527
88658
  const iso = date.toISOString().slice(0, 10);
88528
- return join9(STORE_DIR, `responses-${iso}.jsonl`);
88659
+ return join10(STORE_DIR, `responses-${iso}.jsonl`);
88529
88660
  }
88530
88661
  function genId() {
88531
88662
  return `resp_${Date.now()}_${randomBytes7(3).toString("hex")}`;
@@ -88575,7 +88706,7 @@ async function listRecent(limit = 20, daysBack = 7) {
88575
88706
  d.setUTCDate(d.getUTCDate() - i);
88576
88707
  const iso = d.toISOString().slice(0, 10);
88577
88708
  const name = `responses-${iso}.jsonl`;
88578
- if (files.includes(name)) candidateFiles.push(join9(STORE_DIR, name));
88709
+ if (files.includes(name)) candidateFiles.push(join10(STORE_DIR, name));
88579
88710
  }
88580
88711
  const all3 = [];
88581
88712
  for (const f of candidateFiles) {
@@ -88604,7 +88735,7 @@ var STORE_DIR, dirReady2;
88604
88735
  var init_response_store = __esm({
88605
88736
  "src/response-store.ts"() {
88606
88737
  "use strict";
88607
- STORE_DIR = join9(homedir6(), ".openclaw", "blockrun", "responses");
88738
+ STORE_DIR = join10(homedir7(), ".openclaw", "blockrun", "responses");
88608
88739
  dirReady2 = false;
88609
88740
  }
88610
88741
  });
@@ -90255,15 +90386,18 @@ import { AsyncLocalStorage } from "async_hooks";
90255
90386
  import { createHmac } from "crypto";
90256
90387
  import { createServer } from "http";
90257
90388
  import { finished } from "stream";
90258
- import { homedir as homedir7 } from "os";
90259
- import { join as join10 } from "path";
90260
- import { mkdir as mkdir4, writeFile as writeFile2, readFile as readFile2, stat as fsStat } from "fs/promises";
90389
+ import { homedir as homedir8 } from "os";
90390
+ import { join as join11 } from "path";
90391
+ import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile2, stat as fsStat } from "fs/promises";
90261
90392
  import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
90262
- async function loadGatewayCatalog(apiBase) {
90393
+ async function loadGatewayCatalog(apiBase, apiKey) {
90263
90394
  try {
90264
90395
  const controller = new AbortController();
90265
90396
  const timer2 = setTimeout(() => controller.abort(), GATEWAY_CATALOG_TIMEOUT_MS);
90266
- const res = await fetch(`${apiBase}/v1/models`, { signal: controller.signal });
90397
+ const res = await fetch(`${apiBase}/v1/models`, {
90398
+ signal: controller.signal,
90399
+ ...apiKey ? { headers: { authorization: `Bearer ${apiKey}` } } : {}
90400
+ });
90267
90401
  clearTimeout(timer2);
90268
90402
  if (!res.ok) return;
90269
90403
  const body = await res.json();
@@ -90562,8 +90696,9 @@ async function checkExistingProxy(port) {
90562
90696
  clearTimeout(timeoutId);
90563
90697
  if (response.ok) {
90564
90698
  const data = await response.json();
90565
- if (data.status === "ok" && data.wallet) {
90566
- return { wallet: data.wallet, paymentChain: data.paymentChain };
90699
+ const authMode = data.authMode === "api-key" ? "api-key" : "wallet";
90700
+ if (data.status === "ok" && (data.wallet || authMode === "api-key")) {
90701
+ return { wallet: data.wallet ?? "", paymentChain: data.paymentChain, authMode };
90567
90702
  }
90568
90703
  }
90569
90704
  return void 0;
@@ -91071,7 +91206,7 @@ async function proxyPaidApiRequest(req, res, apiBase, payFetch, getActualPayment
91071
91206
  });
91072
91207
  }
91073
91208
  function readImageFileAsDataUri(filePath) {
91074
- const resolved = filePath.startsWith("~/") ? join10(homedir7(), filePath.slice(2)) : filePath;
91209
+ const resolved = filePath.startsWith("~/") ? join11(homedir8(), filePath.slice(2)) : filePath;
91075
91210
  if (!existsSync2(resolved)) {
91076
91211
  throw new Error(`Image file not found: ${resolved}`);
91077
91212
  }
@@ -91114,16 +91249,48 @@ async function uploadDataUriToHost(dataUri) {
91114
91249
  clearTimeout(uploadTimeout);
91115
91250
  }
91116
91251
  }
91252
+ function warnIfSpendLimitsUnenforced(spendControl) {
91253
+ let configured;
91254
+ try {
91255
+ const limits = (spendControl ?? new SpendControl()).getStatus().limits;
91256
+ configured = ["perRequest", "hourly", "daily", "session"].filter((window2) => typeof limits[window2] === "number").map((window2) => `${window2}=$${limits[window2].toFixed(2)}`);
91257
+ } catch {
91258
+ return;
91259
+ }
91260
+ if (configured.length === 0) return;
91261
+ console.warn(
91262
+ `[ClawRouter] \u26A0 Spend limits (${configured.join(", ")}) are NOT enforced in API-key mode \u2014 they gate x402 signing, and nothing is signed here.`
91263
+ );
91264
+ console.warn(
91265
+ `[ClawRouter] Your BlockRun account balance is the cap instead. Use maxCostPerRun for a per-session limit that still applies.`
91266
+ );
91267
+ }
91117
91268
  async function startProxy(options) {
91118
91269
  const upstreamProxy = await applyUpstreamProxy(options.upstreamProxy);
91119
91270
  if (upstreamProxy) {
91120
91271
  console.log(`[ClawRouter] Upstream proxy: ${upstreamProxy}`);
91121
91272
  }
91122
- const walletKey = typeof options.wallet === "string" ? options.wallet : options.wallet.key;
91123
- const solanaPrivateKeyBytes = typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
91273
+ const apiKey = options.apiKey?.trim() || void 0;
91274
+ if (apiKey && !isValidApiKey(apiKey)) {
91275
+ throw new Error(
91276
+ `BlockRun API key is malformed (expected it to start with "brk_"). Mint one at https://user.blockrun.ai/dashboard/keys`
91277
+ );
91278
+ }
91279
+ const authMode = apiKey ? "api-key" : "wallet";
91280
+ if (!apiKey && !options.wallet) {
91281
+ throw new Error(
91282
+ `startProxy needs a credential: pass either a wallet (x402) or an apiKey (brk_\u2026, from https://user.blockrun.ai/dashboard/keys).`
91283
+ );
91284
+ }
91285
+ const walletKey = options.wallet === void 0 ? void 0 : typeof options.wallet === "string" ? options.wallet : options.wallet.key;
91286
+ const solanaPrivateKeyBytes = options.wallet === void 0 || typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
91124
91287
  const paymentChain = options.paymentChain ?? await resolvePaymentChain();
91125
- const apiBase = options.apiBase ?? (paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
91126
- if (paymentChain === "solana" && !solanaPrivateKeyBytes) {
91288
+ const apiBase = options.apiBase ?? (authMode === "api-key" ? BLOCKRUN_API_KEY_API : paymentChain === "solana" && solanaPrivateKeyBytes ? BLOCKRUN_SOLANA_API : BLOCKRUN_API);
91289
+ if (authMode === "api-key") {
91290
+ console.log(`[ClawRouter] Auth: BlockRun API key ${maskApiKey(apiKey)} (${apiBase})`);
91291
+ console.log(`[ClawRouter] Billing: account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
91292
+ warnIfSpendLimitsUnenforced(options.spendControl);
91293
+ } else if (paymentChain === "solana" && !solanaPrivateKeyBytes) {
91127
91294
  console.warn(
91128
91295
  `[ClawRouter] \u26A0 Payment chain is Solana but no mnemonic found \u2014 falling back to Base (EVM).`
91129
91296
  );
@@ -91134,12 +91301,31 @@ async function startProxy(options) {
91134
91301
  } else if (paymentChain === "solana") {
91135
91302
  console.log(`[ClawRouter] Payment chain: Solana (${BLOCKRUN_SOLANA_API})`);
91136
91303
  }
91137
- void loadGatewayCatalog(apiBase);
91304
+ void loadGatewayCatalog(apiBase, apiKey);
91138
91305
  const listenPort = options.port ?? getProxyPort();
91139
91306
  const existingProxy = options.allowExistingProxy === false ? void 0 : await checkExistingProxy(listenPort);
91140
91307
  if (existingProxy) {
91141
- const account2 = privateKeyToAccount(walletKey);
91142
91308
  const baseUrl2 = `http://127.0.0.1:${listenPort}`;
91309
+ if (existingProxy.authMode !== authMode) {
91310
+ throw new Error(
91311
+ `Existing proxy on port ${listenPort} is authenticating with ${existingProxy.authMode === "api-key" ? "a BlockRun API key" : "a wallet"} but ${authMode === "api-key" ? "an API key" : "a wallet"} was requested. Stop the existing proxy first or use a different port.`
91312
+ );
91313
+ }
91314
+ if (authMode === "api-key") {
91315
+ options.onReady?.(listenPort);
91316
+ return {
91317
+ port: listenPort,
91318
+ baseUrl: baseUrl2,
91319
+ walletAddress: "",
91320
+ authMode,
91321
+ apiKeyLabel: maskApiKey(apiKey),
91322
+ balanceMonitor: new ApiKeyBalanceMonitor(),
91323
+ // No-op: we didn't start this proxy, so we shouldn't close it
91324
+ close: async () => {
91325
+ }
91326
+ };
91327
+ }
91328
+ const account2 = privateKeyToAccount(walletKey);
91143
91329
  if (existingProxy.wallet !== account2.address) {
91144
91330
  console.warn(
91145
91331
  `[ClawRouter] Existing proxy on port ${listenPort} uses wallet ${existingProxy.wallet}, but current config uses ${account2.address}. Reusing existing proxy.`
@@ -91178,20 +91364,23 @@ async function startProxy(options) {
91178
91364
  baseUrl: baseUrl2,
91179
91365
  walletAddress: existingProxy.wallet,
91180
91366
  solanaAddress: reuseSolanaAddress,
91367
+ authMode,
91181
91368
  balanceMonitor: balanceMonitor2,
91182
91369
  close: async () => {
91183
91370
  }
91184
91371
  };
91185
91372
  }
91186
- const account = privateKeyToAccount(walletKey);
91187
- const evmPublicClient = createPublicClient({ chain: base, transport: http() });
91188
- const evmSigner = toClientEvmSigner(account, evmPublicClient);
91189
- const x402 = new x402Client();
91190
- const spendControl = options.spendControl ?? new SpendControl();
91191
- registerSpendPolicyHook(x402, spendControl);
91192
- registerExactEvmScheme(x402, { signer: evmSigner });
91373
+ const account = walletKey ? privateKeyToAccount(walletKey) : void 0;
91374
+ const x402 = authMode === "wallet" ? new x402Client() : void 0;
91375
+ if (x402 && account) {
91376
+ const evmPublicClient = createPublicClient({ chain: base, transport: http() });
91377
+ const evmSigner = toClientEvmSigner(account, evmPublicClient);
91378
+ const spendControl = options.spendControl ?? new SpendControl();
91379
+ registerSpendPolicyHook(x402, spendControl);
91380
+ registerExactEvmScheme(x402, { signer: evmSigner });
91381
+ }
91193
91382
  let solanaAddress;
91194
- if (solanaPrivateKeyBytes) {
91383
+ if (x402 && solanaPrivateKeyBytes) {
91195
91384
  const { registerExactSvmScheme: registerExactSvmScheme2 } = await Promise.resolve().then(() => (init_client3(), client_exports));
91196
91385
  const { createKeyPairSignerFromPrivateKeyBytes: createKeyPairSignerFromPrivateKeyBytes2 } = await Promise.resolve().then(() => (init_index_node37(), index_node_exports));
91197
91386
  const solanaSigner = await createKeyPairSignerFromPrivateKeyBytes2(solanaPrivateKeyBytes);
@@ -91199,12 +91388,12 @@ async function startProxy(options) {
91199
91388
  registerExactSvmScheme2(x402, { signer: solanaSigner });
91200
91389
  console.log(`[ClawRouter] Solana wallet: ${solanaAddress}`);
91201
91390
  }
91202
- x402.onAfterPaymentCreation(async (context) => {
91391
+ x402?.onAfterPaymentCreation(async (context) => {
91203
91392
  if (!context.selectedRequirements.network.startsWith("eip155")) return;
91204
91393
  const payload = context.paymentPayload;
91205
91394
  payload.extensions = withBuilderCodeServiceCode(payload.extensions);
91206
91395
  });
91207
- x402.onAfterPaymentCreation(async (context) => {
91396
+ x402?.onAfterPaymentCreation(async (context) => {
91208
91397
  const network = context.selectedRequirements.network;
91209
91398
  const chain3 = network.startsWith("eip155") ? "Base (EVM)" : network.startsWith("solana") ? "Solana" : network;
91210
91399
  const amountMicros = parseInt(context.selectedRequirements.amount || "0", 10);
@@ -91213,7 +91402,7 @@ async function startProxy(options) {
91213
91402
  if (store) store.amountUsd = amountUsd;
91214
91403
  console.log(`[ClawRouter] Payment signed on ${chain3} (${network}) \u2014 $${amountUsd.toFixed(6)}`);
91215
91404
  });
91216
- const payFetch = createPayFetchWithPreAuth(fetch, x402, void 0, {
91405
+ const payFetch = authMode === "api-key" ? createApiKeyFetch(apiKey) : createPayFetchWithPreAuth(fetch, x402, void 0, {
91217
91406
  skipPreAuth: paymentChain === "solana",
91218
91407
  // Per-request cost estimate so pre-auth is only reused when the cached
91219
91408
  // payment still covers the (possibly larger) request — BlockRun prices per
@@ -91223,6 +91412,8 @@ async function startProxy(options) {
91223
91412
  let balanceMonitor;
91224
91413
  if (options._balanceMonitorOverride) {
91225
91414
  balanceMonitor = options._balanceMonitorOverride;
91415
+ } else if (authMode === "api-key") {
91416
+ balanceMonitor = new ApiKeyBalanceMonitor();
91226
91417
  } else if (paymentChain === "solana" && solanaAddress) {
91227
91418
  const { SolanaBalanceMonitor: SolanaBalanceMonitor2 } = await Promise.resolve().then(() => (init_solana_balance(), solana_balance_exports));
91228
91419
  balanceMonitor = new SolanaBalanceMonitor2(solanaAddress);
@@ -91264,11 +91455,17 @@ async function startProxy(options) {
91264
91455
  const full = url2.searchParams.get("full") === "true";
91265
91456
  const response = {
91266
91457
  status: "ok",
91267
- wallet: account.address,
91268
- paymentChain
91458
+ authMode
91269
91459
  };
91270
- if (solanaAddress) {
91271
- response.solana = solanaAddress;
91460
+ if (authMode === "api-key") {
91461
+ response.apiKey = maskApiKey(apiKey);
91462
+ response.gateway = apiBase;
91463
+ } else {
91464
+ response.wallet = account.address;
91465
+ response.paymentChain = paymentChain;
91466
+ if (solanaAddress) {
91467
+ response.solana = solanaAddress;
91468
+ }
91272
91469
  }
91273
91470
  if (upstreamProxy) {
91274
91471
  response.upstreamProxy = upstreamProxy;
@@ -91284,9 +91481,15 @@ async function startProxy(options) {
91284
91481
  ).unref()
91285
91482
  )
91286
91483
  ]);
91287
- response.balance = balanceInfo.balanceUSD;
91288
- response.isLow = balanceInfo.isLow;
91289
- response.isEmpty = balanceInfo.isEmpty;
91484
+ if (authMode === "api-key") {
91485
+ response.balance = null;
91486
+ response.billing = "BlockRun account credit";
91487
+ response.topUpUrl = PORTAL_CREDITS_URL;
91488
+ } else {
91489
+ response.balance = balanceInfo.balanceUSD;
91490
+ response.isLow = balanceInfo.isLow;
91491
+ response.isEmpty = balanceInfo.isEmpty;
91492
+ }
91290
91493
  } catch {
91291
91494
  response.balanceError = "Could not fetch balance";
91292
91495
  }
@@ -91466,7 +91669,7 @@ async function startProxy(options) {
91466
91669
  res.end("Bad request");
91467
91670
  return;
91468
91671
  }
91469
- const filePath = join10(IMAGE_DIR, filename);
91672
+ const filePath = join11(IMAGE_DIR, filename);
91470
91673
  try {
91471
91674
  const s3 = await fsStat(filePath);
91472
91675
  if (!s3.isFile()) throw new Error("not a file");
@@ -91497,7 +91700,7 @@ async function startProxy(options) {
91497
91700
  res.end("Bad request");
91498
91701
  return;
91499
91702
  }
91500
- const filePath = join10(AUDIO_DIR, filename);
91703
+ const filePath = join11(AUDIO_DIR, filename);
91501
91704
  try {
91502
91705
  const s3 = await fsStat(filePath);
91503
91706
  if (!s3.isFile()) throw new Error("not a file");
@@ -91527,7 +91730,7 @@ async function startProxy(options) {
91527
91730
  res.end("Bad request");
91528
91731
  return;
91529
91732
  }
91530
- const filePath = join10(VIDEO_DIR, filename);
91733
+ const filePath = join11(VIDEO_DIR, filename);
91531
91734
  try {
91532
91735
  const s3 = await fsStat(filePath);
91533
91736
  if (!s3.isFile()) throw new Error("not a file");
@@ -91660,7 +91863,7 @@ async function startProxy(options) {
91660
91863
  }
91661
91864
  }
91662
91865
  if (result.data?.length) {
91663
- await mkdir4(IMAGE_DIR, { recursive: true });
91866
+ await mkdir5(IMAGE_DIR, { recursive: true });
91664
91867
  const port2 = server.address()?.port ?? 8402;
91665
91868
  for (const img of result.data) {
91666
91869
  const dataUriMatch = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
@@ -91668,7 +91871,7 @@ async function startProxy(options) {
91668
91871
  const [, mimeType, b64] = dataUriMatch;
91669
91872
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
91670
91873
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
91671
- await writeFile2(join10(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91874
+ await writeFile3(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91672
91875
  img.url = `http://localhost:${port2}/images/${filename}`;
91673
91876
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
91674
91877
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -91679,7 +91882,7 @@ async function startProxy(options) {
91679
91882
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
91680
91883
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
91681
91884
  const buf = Buffer.from(await imgResp.arrayBuffer());
91682
- await writeFile2(join10(IMAGE_DIR, filename), buf);
91885
+ await writeFile3(join11(IMAGE_DIR, filename), buf);
91683
91886
  img.url = `http://localhost:${port2}/images/${filename}`;
91684
91887
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
91685
91888
  }
@@ -91783,7 +91986,7 @@ async function startProxy(options) {
91783
91986
  return;
91784
91987
  }
91785
91988
  if (result.data?.length) {
91786
- await mkdir4(IMAGE_DIR, { recursive: true });
91989
+ await mkdir5(IMAGE_DIR, { recursive: true });
91787
91990
  const port2 = server.address()?.port ?? 8402;
91788
91991
  for (const img of result.data) {
91789
91992
  const dataUriMatch = img.url?.match(/^data:(image\/\w+);base64,(.+)$/);
@@ -91791,7 +91994,7 @@ async function startProxy(options) {
91791
91994
  const [, mimeType, b64] = dataUriMatch;
91792
91995
  const ext = mimeType === "image/jpeg" ? "jpg" : mimeType.split("/")[1] ?? "png";
91793
91996
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
91794
- await writeFile2(join10(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91997
+ await writeFile3(join11(IMAGE_DIR, filename), Buffer.from(b64, "base64"));
91795
91998
  img.url = `http://localhost:${port2}/images/${filename}`;
91796
91999
  console.log(`[ClawRouter] Image saved \u2192 ${img.url}`);
91797
92000
  } else if (img.url?.startsWith("https://") || img.url?.startsWith("http://")) {
@@ -91802,7 +92005,7 @@ async function startProxy(options) {
91802
92005
  const ext = contentType.includes("jpeg") || contentType.includes("jpg") ? "jpg" : contentType.includes("webp") ? "webp" : "png";
91803
92006
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
91804
92007
  const buf = Buffer.from(await imgResp.arrayBuffer());
91805
- await writeFile2(join10(IMAGE_DIR, filename), buf);
92008
+ await writeFile3(join11(IMAGE_DIR, filename), buf);
91806
92009
  img.url = `http://localhost:${port2}/images/${filename}`;
91807
92010
  console.log(`[ClawRouter] Image downloaded & saved \u2192 ${img.url}`);
91808
92011
  }
@@ -91877,7 +92080,7 @@ async function startProxy(options) {
91877
92080
  return;
91878
92081
  }
91879
92082
  if (result.data?.length) {
91880
- await mkdir4(AUDIO_DIR, { recursive: true });
92083
+ await mkdir5(AUDIO_DIR, { recursive: true });
91881
92084
  const port2 = server.address()?.port ?? 8402;
91882
92085
  for (const track of result.data) {
91883
92086
  if (track.url?.startsWith("https://") || track.url?.startsWith("http://")) {
@@ -91888,7 +92091,7 @@ async function startProxy(options) {
91888
92091
  const ext = contentType.includes("wav") ? "wav" : "mp3";
91889
92092
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
91890
92093
  const buf = Buffer.from(await audioResp.arrayBuffer());
91891
- await writeFile2(join10(AUDIO_DIR, filename), buf);
92094
+ await writeFile3(join11(AUDIO_DIR, filename), buf);
91892
92095
  track.url = `http://localhost:${port2}/audio/${filename}`;
91893
92096
  console.log(`[ClawRouter] Audio saved \u2192 ${track.url}`);
91894
92097
  }
@@ -92037,7 +92240,7 @@ async function startProxy(options) {
92037
92240
  }
92038
92241
  }
92039
92242
  if (finalResult.data?.length) {
92040
- await mkdir4(VIDEO_DIR, { recursive: true });
92243
+ await mkdir5(VIDEO_DIR, { recursive: true });
92041
92244
  const port2 = server.address()?.port ?? 8402;
92042
92245
  for (const clip of finalResult.data) {
92043
92246
  if (clip.url?.startsWith("https://") || clip.url?.startsWith("http://")) {
@@ -92048,7 +92251,7 @@ async function startProxy(options) {
92048
92251
  const ext = contentType.includes("webm") ? "webm" : contentType.includes("quicktime") ? "mov" : "mp4";
92049
92252
  const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}.${ext}`;
92050
92253
  const buf = Buffer.from(await videoResp.arrayBuffer());
92051
- await writeFile2(join10(VIDEO_DIR, filename), buf);
92254
+ await writeFile3(join11(VIDEO_DIR, filename), buf);
92052
92255
  clip.url = `http://localhost:${port2}/videos/${filename}`;
92053
92256
  console.log(`[ClawRouter] Video saved \u2192 ${clip.url}`);
92054
92257
  }
@@ -92231,6 +92434,8 @@ async function startProxy(options) {
92231
92434
  port: listenPort,
92232
92435
  baseUrl: baseUrl2,
92233
92436
  walletAddress: error.wallet,
92437
+ authMode,
92438
+ ...apiKey ? { apiKeyLabel: maskApiKey(apiKey) } : {},
92234
92439
  balanceMonitor,
92235
92440
  close: async () => {
92236
92441
  }
@@ -92281,8 +92486,10 @@ async function startProxy(options) {
92281
92486
  return {
92282
92487
  port,
92283
92488
  baseUrl,
92284
- walletAddress: account.address,
92489
+ walletAddress: account?.address ?? "",
92285
92490
  solanaAddress,
92491
+ authMode,
92492
+ ...apiKey ? { apiKeyLabel: maskApiKey(apiKey) } : {},
92286
92493
  balanceMonitor,
92287
92494
  close: () => new Promise((res, rej) => {
92288
92495
  const timeout = setTimeout(() => {
@@ -94605,6 +94812,7 @@ var init_proxy = __esm({
94605
94812
  init_response_cache();
94606
94813
  init_balance();
94607
94814
  init_auth();
94815
+ init_api_key();
94608
94816
  init_spend_control();
94609
94817
  init_compression();
94610
94818
  init_version4();
@@ -94621,10 +94829,10 @@ var init_proxy = __esm({
94621
94829
  paymentStore = new AsyncLocalStorage();
94622
94830
  BLOCKRUN_API = "https://blockrun.ai/api";
94623
94831
  BLOCKRUN_SOLANA_API = "https://sol.blockrun.ai/api";
94624
- DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ?? join10(homedir7(), ".clawrouter-desktop", "service-token");
94625
- IMAGE_DIR = join10(homedir7(), ".openclaw", "blockrun", "images");
94626
- AUDIO_DIR = join10(homedir7(), ".openclaw", "blockrun", "audio");
94627
- VIDEO_DIR = join10(homedir7(), ".openclaw", "blockrun", "videos");
94832
+ DESKTOP_SERVICE_TOKEN_FILE = process.env.CLAWROUTER_DESKTOP_TOKEN_FILE ?? join11(homedir8(), ".clawrouter-desktop", "service-token");
94833
+ IMAGE_DIR = join11(homedir8(), ".openclaw", "blockrun", "images");
94834
+ AUDIO_DIR = join11(homedir8(), ".openclaw", "blockrun", "audio");
94835
+ VIDEO_DIR = join11(homedir8(), ".openclaw", "blockrun", "videos");
94628
94836
  AUTO_MODEL = "blockrun/auto";
94629
94837
  ROUTING_PROFILES = /* @__PURE__ */ new Set([
94630
94838
  "blockrun/eco",
@@ -116816,8 +117024,8 @@ var init_constants3 = __esm({
116816
117024
 
116817
117025
  // src/polymarket/wallet-adapter.ts
116818
117026
  import fs2 from "fs";
116819
- import { homedir as homedir8 } from "os";
116820
- import { join as join11 } from "path";
117027
+ import { homedir as homedir9 } from "os";
117028
+ import { join as join12 } from "path";
116821
117029
  function getOrCreateWalletKey() {
116822
117030
  const envKey = process.env.BLOCKRUN_WALLET_KEY?.trim();
116823
117031
  if (envKey && /^0x[0-9a-fA-F]{64}$/.test(envKey)) return envKey;
@@ -116865,7 +117073,7 @@ var init_wallet_adapter = __esm({
116865
117073
  init_esm();
116866
117074
  init_chains();
116867
117075
  init_constants3();
116868
- WALLET_FILE2 = join11(homedir8(), ".openclaw", "blockrun", "wallet.key");
117076
+ WALLET_FILE2 = join12(homedir9(), ".openclaw", "blockrun", "wallet.key");
116869
117077
  BASE_RPC_URLS = [
116870
117078
  "https://mainnet.base.org",
116871
117079
  "https://base.llamarpc.com",
@@ -227381,9 +227589,9 @@ import {
227381
227589
  unlinkSync
227382
227590
  } from "fs";
227383
227591
  import { readFile as readFileAsync } from "fs/promises";
227384
- import { homedir as homedir13 } from "os";
227592
+ import { homedir as homedir14 } from "os";
227385
227593
  import { randomUUID } from "crypto";
227386
- import { join as join16 } from "path";
227594
+ import { join as join17 } from "path";
227387
227595
  async function waitForProxyHealth(port, timeoutMs = 3e3) {
227388
227596
  const start = Date.now();
227389
227597
  while (Date.now() - start < timeoutMs) {
@@ -227425,8 +227633,8 @@ function isBlockrunWebSearchDisabled(config) {
227425
227633
  return cfg.tools?.web?.search?.enabled === false;
227426
227634
  }
227427
227635
  function injectModelsConfig(logger48, options = {}) {
227428
- const configDir = join16(homedir13(), ".openclaw");
227429
- const configPath = join16(configDir, "openclaw.json");
227636
+ const configDir = join17(homedir14(), ".openclaw");
227637
+ const configPath = join17(configDir, "openclaw.json");
227430
227638
  let config = {};
227431
227639
  let needsWrite = false;
227432
227640
  if (!existsSync3(configDir)) {
@@ -227647,7 +227855,7 @@ function injectModelsConfig(logger48, options = {}) {
227647
227855
  }
227648
227856
  function syncAgentModelCache(logger48, options = {}) {
227649
227857
  if (!isGatewayMode() && !options.forceWrite) return;
227650
- const agentsDir = join16(homedir13(), ".openclaw", "agents");
227858
+ const agentsDir = join17(homedir14(), ".openclaw", "agents");
227651
227859
  if (!existsSync3(agentsDir)) return;
227652
227860
  let agentDirs;
227653
227861
  try {
@@ -227657,7 +227865,7 @@ function syncAgentModelCache(logger48, options = {}) {
227657
227865
  }
227658
227866
  const expectedIds = VISIBLE_OPENCLAW_MODELS.map((m) => m.id);
227659
227867
  for (const agent of agentDirs) {
227660
- const cachePath = join16(agentsDir, agent, "agent", "models.json");
227868
+ const cachePath = join17(agentsDir, agent, "agent", "models.json");
227661
227869
  if (!existsSync3(cachePath)) continue;
227662
227870
  try {
227663
227871
  const raw = readTextFileSync(cachePath).trim();
@@ -227686,7 +227894,7 @@ function syncAgentModelCache(logger48, options = {}) {
227686
227894
  }
227687
227895
  }
227688
227896
  function injectAuthProfile(logger48) {
227689
- const agentsDir = join16(homedir13(), ".openclaw", "agents");
227897
+ const agentsDir = join17(homedir14(), ".openclaw", "agents");
227690
227898
  if (!existsSync3(agentsDir)) {
227691
227899
  try {
227692
227900
  mkdirSync3(agentsDir, { recursive: true });
@@ -227703,9 +227911,9 @@ function injectAuthProfile(logger48) {
227703
227911
  agents = ["main", ...agents];
227704
227912
  }
227705
227913
  for (const agentId of agents) {
227706
- const authDir = join16(agentsDir, agentId, "agent");
227707
- const authPath = join16(authDir, "auth-profiles.json");
227708
- const sqlitePath = join16(authDir, "openclaw-agent.sqlite");
227914
+ const authDir = join17(agentsDir, agentId, "agent");
227915
+ const authPath = join17(authDir, "auth-profiles.json");
227916
+ const sqlitePath = join17(authDir, "openclaw-agent.sqlite");
227709
227917
  if (existsSync3(sqlitePath)) {
227710
227918
  removeInjectedAuthPlaceholder(authPath, logger48, agentId);
227711
227919
  continue;
@@ -227852,9 +228060,21 @@ async function startProxyInBackground(api, startupGeneration) {
227852
228060
  if (startupGeneration !== void 0 && isProxyStartupCurrent(startupGeneration, proc)) {
227853
228061
  proc.__clawrouterStartupPhase = "starting";
227854
228062
  }
228063
+ const pluginApiKey = api.pluginConfig?.apiKey;
228064
+ const resolvedApiKey = typeof pluginApiKey === "string" && isValidApiKey(pluginApiKey) ? { key: pluginApiKey.trim(), source: "config" } : await resolveApiKey();
228065
+ if (typeof pluginApiKey === "string" && !isValidApiKey(pluginApiKey)) {
228066
+ api.logger.warn(
228067
+ `pluginConfig.apiKey is set but invalid (expected brk_...) \u2014 ignoring it and looking elsewhere`
228068
+ );
228069
+ }
228070
+ const apiKey = resolvedApiKey?.key;
227855
228071
  const configKey = api.pluginConfig?.walletKey;
227856
228072
  let wallet;
227857
- if (typeof configKey === "string" && /^0x[0-9a-fA-F]{64}$/.test(configKey)) {
228073
+ if (apiKey) {
228074
+ api.logger.info(
228075
+ `Using BlockRun API key ${maskApiKey(apiKey)} (from ${resolvedApiKey.source}) \u2014 billing account credit, no wallet`
228076
+ );
228077
+ } else if (typeof configKey === "string" && /^0x[0-9a-fA-F]{64}$/.test(configKey)) {
227858
228078
  const account = privateKeyToAccount(configKey);
227859
228079
  wallet = { key: configKey, address: account.address, source: "config" };
227860
228080
  } else {
@@ -227865,7 +228085,8 @@ async function startProxyInBackground(api, startupGeneration) {
227865
228085
  }
227866
228086
  wallet = await resolveOrGenerateWalletKey();
227867
228087
  }
227868
- if (wallet.source === "generated") {
228088
+ if (!wallet) {
228089
+ } else if (wallet.source === "generated") {
227869
228090
  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`);
227870
228091
  api.logger.warn(` NEW WALLET GENERATED \u2014 BACK UP YOUR KEY NOW!`);
227871
228092
  api.logger.warn(` Address : ${wallet.address}`);
@@ -227890,13 +228111,13 @@ async function startProxyInBackground(api, startupGeneration) {
227890
228111
  );
227891
228112
  }
227892
228113
  const proxy = await startProxy({
227893
- wallet,
228114
+ ...apiKey ? { apiKey } : { wallet },
227894
228115
  routingConfig,
227895
228116
  maxCostPerRunUsd,
227896
228117
  maxCostPerRunMode,
227897
228118
  spendControl: liveSpendControl = new SpendControl(),
227898
228119
  onReady: (port) => {
227899
- api.logger.info(`BlockRun x402 proxy listening on port ${port}`);
228120
+ api.logger.info(`BlockRun ${apiKey ? "API-key" : "x402"} proxy listening on port ${port}`);
227900
228121
  },
227901
228122
  onError: (error) => {
227902
228123
  api.logger.error(`BlockRun proxy error: ${error.message}`);
@@ -227942,6 +228163,10 @@ async function startProxyInBackground(api, startupGeneration) {
227942
228163
  }
227943
228164
  api.logger.info(`ClawRouter ready \u2014 smart routing enabled`);
227944
228165
  api.logger.info(`Pricing: Simple ~$0.001 | Code ~$0.01 | Complex ~$0.05 | Free: $0`);
228166
+ if (apiKey) {
228167
+ api.logger.info(`Billing: BlockRun account credit \u2014 top up at ${PORTAL_CREDITS_URL}`);
228168
+ return true;
228169
+ }
227945
228170
  const currentChain = await resolvePaymentChain();
227946
228171
  const displayAddress = currentChain === "solana" && proxy.solanaAddress ? proxy.solanaAddress : wallet.address;
227947
228172
  const network = currentChain === "solana" ? "Solana" : "Base";
@@ -228185,7 +228410,7 @@ function buildImageGenerationProvider() {
228185
228410
  (result.data ?? []).map(async (img) => {
228186
228411
  const filename = img.url?.split("/images/").pop();
228187
228412
  if (!filename) throw new Error(`Unexpected image URL format: ${img.url}`);
228188
- const filePath = join16(IMAGE_DIR2, filename);
228413
+ const filePath = join17(IMAGE_DIR2, filename);
228189
228414
  const buffer2 = await readFileAsync(filePath);
228190
228415
  const ext = filename.split(".").pop()?.toLowerCase() ?? "png";
228191
228416
  const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
@@ -228237,7 +228462,7 @@ function buildMusicGenerationProvider() {
228237
228462
  (result.data ?? []).map(async (track) => {
228238
228463
  const filename = track.url?.split("/audio/").pop();
228239
228464
  if (!filename) throw new Error(`Unexpected audio URL format: ${track.url}`);
228240
- const filePath = join16(AUDIO_DIR2, filename);
228465
+ const filePath = join17(AUDIO_DIR2, filename);
228241
228466
  const buffer2 = await readFileAsync(filePath);
228242
228467
  const ext = filename.split(".").pop()?.toLowerCase() ?? "mp3";
228243
228468
  const mimeType = ext === "wav" ? "audio/wav" : "audio/mpeg";
@@ -228313,7 +228538,7 @@ function buildVideoGenerationProvider() {
228313
228538
  (result.data ?? []).map(async (clip) => {
228314
228539
  const filename = clip.url?.split("/videos/").pop();
228315
228540
  if (!filename) throw new Error(`Unexpected video URL format: ${clip.url}`);
228316
- const filePath = join16(VIDEO_DIR2, filename);
228541
+ const filePath = join17(VIDEO_DIR2, filename);
228317
228542
  const buffer2 = await readFileAsync(filePath);
228318
228543
  const ext = filename.split(".").pop()?.toLowerCase() ?? "mp4";
228319
228544
  const mimeType = ext === "webm" ? "video/webm" : ext === "mov" ? "video/quicktime" : "video/mp4";
@@ -228366,6 +228591,22 @@ function createWalletCommand(api) {
228366
228591
  requireAuth: true,
228367
228592
  handler: async (ctx) => {
228368
228593
  const subcommand = ctx.args?.trim().toLowerCase() || "status";
228594
+ const activeApiKey = (await resolveApiKey())?.key;
228595
+ if (activeApiKey) {
228596
+ return {
228597
+ text: [
228598
+ "**BlockRun account** (API key)",
228599
+ "",
228600
+ `**Key:** \`${maskApiKey(activeApiKey)}\``,
228601
+ "**Billing:** account credit \u2014 no wallet, no payment chain, no gas.",
228602
+ "",
228603
+ `**Add credit:** ${PORTAL_CREDITS_URL}`,
228604
+ `**Usage & keys:** ${PORTAL_KEYS_URL}`,
228605
+ "",
228606
+ "To pay with a USDC wallet over x402 instead, run `clawrouter logout` and restart."
228607
+ ].join("\n")
228608
+ };
228609
+ }
228369
228610
  let wallet;
228370
228611
  try {
228371
228612
  wallet = subcommand === "status" || subcommand === "export" ? await resolveExistingWalletKey() : await resolveOrGenerateWalletKey();
@@ -228622,6 +228863,7 @@ var init_index = __esm({
228622
228863
  "src/index.ts"() {
228623
228864
  init_provider();
228624
228865
  init_proxy();
228866
+ init_api_key();
228625
228867
  init_web_search_provider();
228626
228868
  init_auth();
228627
228869
  init_balance();
@@ -228661,9 +228903,9 @@ var init_index = __esm({
228661
228903
  activeProxyHandle = null;
228662
228904
  liveSpendControl = null;
228663
228905
  pendingConfiguredStartupApi = null;
228664
- IMAGE_DIR2 = join16(homedir13(), ".openclaw", "blockrun", "images");
228665
- AUDIO_DIR2 = join16(homedir13(), ".openclaw", "blockrun", "audio");
228666
- VIDEO_DIR2 = join16(homedir13(), ".openclaw", "blockrun", "videos");
228906
+ IMAGE_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "images");
228907
+ AUDIO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "audio");
228908
+ VIDEO_DIR2 = join17(homedir14(), ".openclaw", "blockrun", "videos");
228667
228909
  plugin = {
228668
228910
  // NOT "clawrouter". OpenClaw bundles its own plugin under that id since the
228669
228911
  // 2026.7.1 line (vendored at dist/extensions/clawrouter, provider `clawrouter/*`,
@@ -229080,7 +229322,7 @@ ${errText}`
229080
229322
  }
229081
229323
  resetProxyStartupState();
229082
229324
  try {
229083
- const configPath = join16(homedir13(), ".openclaw", "openclaw.json");
229325
+ const configPath = join17(homedir14(), ".openclaw", "openclaw.json");
229084
229326
  if (existsSync3(configPath)) {
229085
229327
  const config = JSON.parse(readTextFileSync(configPath));
229086
229328
  if (config.models?.providers?.blockrun) {
@@ -229122,11 +229364,11 @@ ${errText}`
229122
229364
  api.logger.warn(`Config cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
229123
229365
  }
229124
229366
  try {
229125
- const agentsDir = join16(homedir13(), ".openclaw", "agents");
229367
+ const agentsDir = join17(homedir14(), ".openclaw", "agents");
229126
229368
  if (existsSync3(agentsDir)) {
229127
229369
  for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
229128
229370
  if (!entry.isDirectory()) continue;
229129
- const authPath = join16(agentsDir, entry.name, "agent", "auth-profiles.json");
229371
+ const authPath = join17(agentsDir, entry.name, "agent", "auth-profiles.json");
229130
229372
  if (!existsSync3(authPath)) continue;
229131
229373
  try {
229132
229374
  const store = JSON.parse(readTextFileSync(authPath));