@blockrun/llm 3.18.0 → 3.19.1

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.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  ARC_CHAIN_ID: () => ARC_CHAIN_ID,
36
36
  AnthropicClient: () => AnthropicClient,
37
37
  BASE_CHAIN_ID: () => BASE_CHAIN_ID,
38
+ BLOCKRUN_SOL_OPERATOR: () => BLOCKRUN_SOL_OPERATOR,
38
39
  BlockrunClient: () => BlockrunClient,
39
40
  BlockrunError: () => BlockrunError,
40
41
  EVM_NETWORKS: () => EVM_NETWORKS,
@@ -284,7 +285,7 @@ var TRANSIENT_RETRIES = 2;
284
285
  var TRANSIENT_BACKOFF_MS = 1e3;
285
286
  function retryDelay(ms, signal) {
286
287
  signal?.throwIfAborted();
287
- return new Promise((resolve, reject) => {
288
+ return new Promise((resolve2, reject) => {
288
289
  const onAbort = () => {
289
290
  clearTimeout(timer);
290
291
  signal?.removeEventListener("abort", onAbort);
@@ -292,7 +293,7 @@ function retryDelay(ms, signal) {
292
293
  };
293
294
  const timer = setTimeout(() => {
294
295
  signal?.removeEventListener("abort", onAbort);
295
- resolve();
296
+ resolve2();
296
297
  }, ms);
297
298
  signal?.addEventListener("abort", onAbort, { once: true });
298
299
  });
@@ -328,8 +329,8 @@ var ApiKeyAuth = class {
328
329
  this.#key = key;
329
330
  }
330
331
  /** Resolve gateway poll paths without forwarding credentials to another origin. */
331
- resolveUrl(path6) {
332
- const url = new URL(path6, this.apiUrl + "/");
332
+ resolveUrl(path7) {
333
+ const url = new URL(path7, this.apiUrl + "/");
333
334
  if (url.origin !== new URL(this.apiUrl).origin || url.username || url.password) {
334
335
  throw new Error("Refusing to send a BlockRun API key to a different origin.");
335
336
  }
@@ -395,7 +396,7 @@ var ApiKeyAuth = class {
395
396
  if (["failed", "cancelled", "canceled"].includes(String(data.status))) {
396
397
  throw new APIError("Account API job failed or was cancelled", 502, sanitizeErrorResponse(data));
397
398
  }
398
- await new Promise((resolve) => setTimeout(resolve, Math.min(interval, Math.max(0, deadline - Date.now()))));
399
+ await new Promise((resolve2) => setTimeout(resolve2, Math.min(interval, Math.max(0, deadline - Date.now()))));
399
400
  const remaining = deadline - Date.now();
400
401
  if (remaining <= 0) break;
401
402
  try {
@@ -4660,7 +4661,7 @@ function getCostSummary() {
4660
4661
  }
4661
4662
 
4662
4663
  // src/version.ts
4663
- var SDK_VERSION = "3.18.0";
4664
+ var SDK_VERSION = "3.19.1";
4664
4665
  var USER_AGENT = `blockrun-ts/${SDK_VERSION}`;
4665
4666
 
4666
4667
  // src/client.ts
@@ -5769,8 +5770,8 @@ var LLMClient = class _LLMClient {
5769
5770
  * @example
5770
5771
  * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
5771
5772
  */
5772
- async exa(path6, body) {
5773
- return this.requestWithPaymentRaw(`/v1/exa/${path6}`, body);
5773
+ async exa(path7, body) {
5774
+ return this.requestWithPaymentRaw(`/v1/exa/${path7}`, body);
5774
5775
  }
5775
5776
  /**
5776
5777
  * Neural web search via Exa. Returns semantically relevant URLs and metadata.
@@ -5918,8 +5919,8 @@ var LLMClient = class _LLMClient {
5918
5919
  * const market = await client.pm("kalshi/markets/KXBTC-25MAR14");
5919
5920
  * const results = await client.pm("polymarket/search", { q: "bitcoin" });
5920
5921
  */
5921
- async pm(path6, params) {
5922
- return this.getWithPaymentRaw(`/v1/pm/${path6}`, params);
5922
+ async pm(path7, params) {
5923
+ return this.getWithPaymentRaw(`/v1/pm/${path7}`, params);
5923
5924
  }
5924
5925
  /**
5925
5926
  * Structured query for Predexon prediction market data (POST endpoints).
@@ -5935,8 +5936,8 @@ var LLMClient = class _LLMClient {
5935
5936
  * addresses: ["0xabc...", "0xdef..."],
5936
5937
  * });
5937
5938
  */
5938
- async pmQuery(path6, query) {
5939
- return this.requestWithPaymentRaw(`/v1/pm/${path6}`, query);
5939
+ async pmQuery(path7, query) {
5940
+ return this.requestWithPaymentRaw(`/v1/pm/${path7}`, query);
5940
5941
  }
5941
5942
  // ── PM convenience helpers (Predexon v2) ─────────────────────────────────
5942
5943
  // Thin wrappers over pm() / pmQuery() for the most common v2 endpoints.
@@ -6024,8 +6025,8 @@ var LLMClient = class _LLMClient {
6024
6025
  * "prices/coingecko:bitcoin,base:0x..."
6025
6026
  * @param params - Query parameters passed through to DefiLlama
6026
6027
  */
6027
- async defi(path6, params) {
6028
- return this.getWithPaymentRaw(`/v1/defillama/${path6}`, params);
6028
+ async defi(path7, params) {
6029
+ return this.getWithPaymentRaw(`/v1/defillama/${path7}`, params);
6029
6030
  }
6030
6031
  /** All DeFi protocols with TVL. */
6031
6032
  async defiProtocols() {
@@ -6061,9 +6062,9 @@ var LLMClient = class _LLMClient {
6061
6062
  * @param params - Query params (chainId, sellToken, buyToken, sellAmount, taker, ...)
6062
6063
  * @param body - JSON body — pass to switch to POST (gasless/submit only)
6063
6064
  */
6064
- async dex(path6, params, body) {
6065
- if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path6}`, body);
6066
- return this.getWithPaymentRaw(`/v1/zerox/${path6}`, params);
6065
+ async dex(path7, params, body) {
6066
+ if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path7}`, body);
6067
+ return this.getWithPaymentRaw(`/v1/zerox/${path7}`, params);
6067
6068
  }
6068
6069
  /** Indicative Permit2 swap price — no commitment (free). */
6069
6070
  async dexPrice(params) {
@@ -6107,8 +6108,8 @@ var LLMClient = class _LLMClient {
6107
6108
  * "sandbox/status", "sandbox/terminate"
6108
6109
  * @param body - JSON body for the endpoint
6109
6110
  */
6110
- async modal(path6, body) {
6111
- return this.requestWithPaymentRaw(`/v1/modal/${path6}`, body ?? {});
6111
+ async modal(path7, body) {
6112
+ return this.requestWithPaymentRaw(`/v1/modal/${path7}`, body ?? {});
6112
6113
  }
6113
6114
  /** Create a sandboxed compute environment (CPU / GPU tiers). */
6114
6115
  async modalSandboxCreate(body) {
@@ -6416,7 +6417,7 @@ var ImageClient = class {
6416
6417
  const pollUrl = pollPath.startsWith("/api/") ? `${this.apiUrl.replace(/\/api$/, "")}${pollPath}` : `${this.apiUrl}${pollPath.startsWith("/") ? "" : "/"}${pollPath}`;
6417
6418
  const startedAt = Date.now();
6418
6419
  while (Date.now() - startedAt < POLL_MAX_DURATION_MS) {
6419
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
6420
+ await new Promise((resolve2) => setTimeout(resolve2, POLL_INTERVAL_MS));
6420
6421
  const pollResp = await this.fetchWithTimeout(pollUrl, {
6421
6422
  method: "GET",
6422
6423
  headers: {
@@ -7614,8 +7615,8 @@ var PhoneClient = class {
7614
7615
  return data;
7615
7616
  }
7616
7617
  // ─── Internals ─────────────────────────────────────────────────────────
7617
- async request(path6, body) {
7618
- const url = `${this.apiUrl}/v1/phone/${path6}`;
7618
+ async request(path7, body) {
7619
+ const url = `${this.apiUrl}/v1/phone/${path7}`;
7619
7620
  const response = await this.fetchWithTimeout(url, {
7620
7621
  method: "POST",
7621
7622
  headers: { "Content-Type": "application/json" },
@@ -8056,10 +8057,10 @@ var PriceClient = class {
8056
8057
  }
8057
8058
  async price(category, symbol, options) {
8058
8059
  if (!symbol) throw new Error("symbol is required");
8059
- const path6 = categoryPath(category, options?.market, "price", symbol);
8060
+ const path7 = categoryPath(category, options?.market, "price", symbol);
8060
8061
  const query = {};
8061
8062
  if (options?.session) query.session = options.session;
8062
- const data = await this.getWithPayment(path6, query);
8063
+ const data = await this.getWithPayment(path7, query);
8063
8064
  return {
8064
8065
  symbol: data.symbol ?? symbol.toUpperCase(),
8065
8066
  price: data.price,
@@ -8078,14 +8079,14 @@ var PriceClient = class {
8078
8079
  if (!options.from || options.from <= 0) {
8079
8080
  throw new Error("history requires options.from (unix seconds)");
8080
8081
  }
8081
- const path6 = categoryPath(category, options.market, "history", symbol);
8082
+ const path7 = categoryPath(category, options.market, "history", symbol);
8082
8083
  const query = {
8083
8084
  resolution: options.resolution ?? "D",
8084
8085
  from: String(options.from)
8085
8086
  };
8086
8087
  if (options.to) query.to = String(options.to);
8087
8088
  if (options.session) query.session = options.session;
8088
- const data = await this.getWithPayment(path6, query);
8089
+ const data = await this.getWithPayment(path7, query);
8089
8090
  return {
8090
8091
  symbol: data.symbol ?? symbol.toUpperCase(),
8091
8092
  resolution: data.resolution ?? (options.resolution ?? "D"),
@@ -8097,12 +8098,12 @@ var PriceClient = class {
8097
8098
  };
8098
8099
  }
8099
8100
  async listSymbols(category, options) {
8100
- const path6 = categoryPath(category, options?.market, "list");
8101
+ const path7 = categoryPath(category, options?.market, "list");
8101
8102
  const query = {
8102
8103
  limit: String(options?.limit && options.limit > 0 ? options.limit : 100)
8103
8104
  };
8104
8105
  if (options?.query) query.q = options.query;
8105
- const data = await this.getWithPayment(path6, query);
8106
+ const data = await this.getWithPayment(path7, query);
8106
8107
  if (Array.isArray(data)) {
8107
8108
  return { symbols: data, count: data.length };
8108
8109
  }
@@ -8275,19 +8276,19 @@ var SurfClient = class {
8275
8276
  * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
8276
8277
  * arrays become repeated keys (`?a=1&a=2`).
8277
8278
  */
8278
- async get(path6, params) {
8279
- const url = this.buildUrl(path6, params);
8279
+ async get(path7, params) {
8280
+ const url = this.buildUrl(path7, params);
8280
8281
  return this.requestWithPayment(url, "GET");
8281
8282
  }
8282
8283
  /**
8283
8284
  * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
8284
8285
  */
8285
- async post(path6, body) {
8286
- const url = this.buildUrl(path6);
8286
+ async post(path7, body) {
8287
+ const url = this.buildUrl(path7);
8287
8288
  return this.requestWithPayment(url, "POST", body);
8288
8289
  }
8289
- buildUrl(path6, params) {
8290
- let normalized = path6.startsWith("/") ? path6 : `/${path6}`;
8290
+ buildUrl(path7, params) {
8291
+ let normalized = path7.startsWith("/") ? path7 : `/${path7}`;
8291
8292
  if (!normalized.startsWith(SURF_PREFIX)) {
8292
8293
  normalized = `${SURF_PREFIX}${normalized}`;
8293
8294
  }
@@ -8745,15 +8746,15 @@ var BlockrunClient = class {
8745
8746
  * `/api` is tolerated and stripped). Query params are URL-encoded; arrays
8746
8747
  * become repeated keys (`?a=1&a=2`); undefined/null are dropped.
8747
8748
  */
8748
- async get(path6, params) {
8749
- const url = this.buildUrl(path6, params);
8749
+ async get(path7, params) {
8750
+ const url = this.buildUrl(path7, params);
8750
8751
  return this.requestWithPayment(url, "GET");
8751
8752
  }
8752
8753
  /**
8753
8754
  * POST a BlockRun endpoint with a JSON body.
8754
8755
  */
8755
- async post(path6, body) {
8756
- const url = this.buildUrl(path6);
8756
+ async post(path7, body) {
8757
+ const url = this.buildUrl(path7);
8757
8758
  return this.requestWithPayment(url, "POST", body);
8758
8759
  }
8759
8760
  /**
@@ -8777,8 +8778,8 @@ var BlockrunClient = class {
8777
8778
  * short-circuits and returns the body. Most long-running endpoints (image,
8778
8779
  * video, music, voice) return 202 with a poll_url.
8779
8780
  */
8780
- async poll(path6, body, options) {
8781
- const submitUrl = this.buildUrl(path6);
8781
+ async poll(path7, body, options) {
8782
+ const submitUrl = this.buildUrl(path7);
8782
8783
  const budgetMs = options?.budgetMs ?? DEFAULT_POLL_BUDGET_MS;
8783
8784
  const intervalMs = options?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
8784
8785
  const resp402 = await this.fetchWithTimeout(submitUrl, {
@@ -8878,8 +8879,8 @@ var BlockrunClient = class {
8878
8879
  * process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
8879
8880
  * }
8880
8881
  */
8881
- async *stream(path6, body) {
8882
- const url = this.buildUrl(path6);
8882
+ async *stream(path7, body) {
8883
+ const url = this.buildUrl(path7);
8883
8884
  const requestBody = JSON.stringify(body ?? {});
8884
8885
  const resp402 = await this.fetchWithTimeout(url, {
8885
8886
  method: "POST",
@@ -8922,8 +8923,8 @@ var BlockrunClient = class {
8922
8923
  // --------------------------------------------------------------------
8923
8924
  // Internal: shared infrastructure
8924
8925
  // --------------------------------------------------------------------
8925
- buildUrl(path6, params) {
8926
- let normalized = path6.startsWith("/") ? path6 : `/${path6}`;
8926
+ buildUrl(path7, params) {
8927
+ let normalized = path7.startsWith("/") ? path7 : `/${path7}`;
8927
8928
  if (normalized.startsWith("/api/")) {
8928
8929
  normalized = normalized.slice(4);
8929
8930
  }
@@ -9569,6 +9570,333 @@ async function getOrCreateSolanaWallet() {
9569
9570
  return { address, privateKey, isNew: true };
9570
9571
  }
9571
9572
 
9573
+ // src/solana-batch.ts
9574
+ var fs6 = __toESM(require("fs"), 1);
9575
+ var path5 = __toESM(require("path"), 1);
9576
+ var BATCH_SCHEME = "batch-settlement";
9577
+ var BLOCKRUN_SOL_OPERATOR = "5YKPQUFjw5WQqhSUkEGKNNfYYVqnRRNbpYyL71qQ1vm3";
9578
+ var INSTALL_HINT2 = "npm install @x402/core@~2.28.0 @x402/svm@~2.28.0 @solana/kit";
9579
+ async function load(pkg, importer) {
9580
+ try {
9581
+ return await importer();
9582
+ } catch (err) {
9583
+ throw new Error(
9584
+ `@blockrun/llm: Solana batch-settlement requires the optional peer dependency "${pkg}", which is not installed.
9585
+
9586
+ ${INSTALL_HINT2}
9587
+
9588
+ Original error: ${err instanceof Error ? err.message : String(err)}`,
9589
+ { cause: err }
9590
+ );
9591
+ }
9592
+ }
9593
+ var FileChannelStorage = class {
9594
+ constructor(file) {
9595
+ this.file = file;
9596
+ }
9597
+ file;
9598
+ read() {
9599
+ try {
9600
+ const parsed = JSON.parse(fs6.readFileSync(this.file, "utf8"));
9601
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
9602
+ } catch (err) {
9603
+ if (err.code === "ENOENT") return {};
9604
+ throw err;
9605
+ }
9606
+ }
9607
+ write(all) {
9608
+ fs6.mkdirSync(path5.dirname(this.file), { recursive: true, mode: 448 });
9609
+ const tmp = `${this.file}.${process.pid}.tmp`;
9610
+ fs6.writeFileSync(tmp, JSON.stringify(all, null, 2), { mode: 384 });
9611
+ fs6.renameSync(tmp, this.file);
9612
+ }
9613
+ async get(key) {
9614
+ return this.read()[key];
9615
+ }
9616
+ async set(key, record) {
9617
+ const all = this.read();
9618
+ all[key] = record;
9619
+ this.write(all);
9620
+ }
9621
+ async delete(key) {
9622
+ const all = this.read();
9623
+ if (!(key in all)) return;
9624
+ delete all[key];
9625
+ this.write(all);
9626
+ }
9627
+ /** Forget every channel in this file. */
9628
+ clear() {
9629
+ try {
9630
+ fs6.unlinkSync(this.file);
9631
+ } catch (err) {
9632
+ if (err.code !== "ENOENT") throw err;
9633
+ }
9634
+ }
9635
+ };
9636
+ function processAlive(pid) {
9637
+ try {
9638
+ process.kill(pid, 0);
9639
+ return true;
9640
+ } catch (err) {
9641
+ return err.code === "EPERM";
9642
+ }
9643
+ }
9644
+ var heldLocks = /* @__PURE__ */ new Set();
9645
+ var exitHookInstalled = false;
9646
+ function lockChannelFile(file) {
9647
+ if (heldLocks.has(file)) return void 0;
9648
+ const lock = `${file}.lock`;
9649
+ fs6.mkdirSync(path5.dirname(lock), { recursive: true, mode: 448 });
9650
+ for (let attempt = 0; attempt < 2; attempt += 1) {
9651
+ try {
9652
+ fs6.writeFileSync(lock, String(process.pid), { flag: "wx", mode: 384 });
9653
+ heldLocks.add(file);
9654
+ if (!exitHookInstalled) {
9655
+ exitHookInstalled = true;
9656
+ process.once("exit", () => {
9657
+ for (const held of heldLocks) {
9658
+ try {
9659
+ fs6.unlinkSync(`${held}.lock`);
9660
+ } catch {
9661
+ }
9662
+ }
9663
+ });
9664
+ }
9665
+ return void 0;
9666
+ } catch (err) {
9667
+ if (err.code !== "EEXIST") throw err;
9668
+ }
9669
+ const owner = Number(fs6.readFileSync(lock, "utf8").trim());
9670
+ if (Number.isInteger(owner) && owner > 0 && owner !== process.pid && processAlive(owner)) return owner;
9671
+ try {
9672
+ fs6.unlinkSync(lock);
9673
+ } catch {
9674
+ }
9675
+ }
9676
+ return -1;
9677
+ }
9678
+ function pendingDeposit(pending) {
9679
+ const entries = Array.isArray(pending) ? pending : [pending];
9680
+ return entries.some(
9681
+ (entry) => entry?.payment?.payload?.type === "deposit"
9682
+ );
9683
+ }
9684
+ function dropOrphanedPending(storage) {
9685
+ return {
9686
+ get: async (key) => {
9687
+ const record = await storage.get(key);
9688
+ if (!record?.pending || record.channelConfig?.voucherSigner !== "server") return record;
9689
+ if (!record.hasConfirmedState || pendingDeposit(record.pending)) {
9690
+ await storage.delete(key);
9691
+ return void 0;
9692
+ }
9693
+ const { pending: _pending, hasConfirmedState: _confirmed, ...confirmed } = record;
9694
+ await storage.set(key, confirmed);
9695
+ return confirmed;
9696
+ },
9697
+ set: (key, record) => storage.set(key, record),
9698
+ delete: (key) => storage.delete(key)
9699
+ };
9700
+ }
9701
+ function offersBatch(paymentRequired) {
9702
+ return (paymentRequired.accepts ?? []).some((accept) => accept.scheme === BATCH_SCHEME);
9703
+ }
9704
+ function atomicToUsd(amount) {
9705
+ return amount !== void 0 && /^\d+$/.test(amount) ? Number(amount) / 1e6 : void 0;
9706
+ }
9707
+ async function isBatchRefusal(response) {
9708
+ if (response.status === 402) return "payment_required";
9709
+ if (![400, 403, 409, 503].includes(response.status)) return void 0;
9710
+ let body;
9711
+ try {
9712
+ body = await response.clone().json();
9713
+ } catch {
9714
+ return void 0;
9715
+ }
9716
+ const { error, code } = body ?? {};
9717
+ if (code === "PAYMENT_VERIFICATION_UNAVAILABLE") return code;
9718
+ return typeof error === "string" && error.startsWith("batch_") ? error : void 0;
9719
+ }
9720
+ var wallets = /* @__PURE__ */ new Map();
9721
+ var SolanaBatchPayer = class {
9722
+ constructor(init) {
9723
+ this.init = init;
9724
+ const { operators } = init.options;
9725
+ if (!Array.isArray(operators) || operators.length === 0 || operators.some((o) => typeof o !== "string" || !o)) {
9726
+ throw new Error("batch.operators must list at least one base58 operator public key");
9727
+ }
9728
+ }
9729
+ init;
9730
+ state;
9731
+ async storeFile() {
9732
+ const { channelStore } = this.init.options;
9733
+ if (channelStore === false) return void 0;
9734
+ return path5.resolve(channelStore ?? path5.join(paths().dir, "solana-batch", `${await this.init.address()}.json`));
9735
+ }
9736
+ /**
9737
+ * This wallet's shared batch state, or why this client cannot use batch:
9738
+ * another client in this process holds the wallet with a different trust
9739
+ * configuration, or another live process owns its channel file.
9740
+ */
9741
+ wallet() {
9742
+ this.state ??= (async () => {
9743
+ const address = await this.init.address();
9744
+ const file = await this.storeFile();
9745
+ const { operators, maxDeposit } = this.init.options;
9746
+ const config = JSON.stringify([[...operators].sort(), String(maxDeposit ?? ""), file ?? null, this.init.rpcUrl]);
9747
+ const existing = wallets.get(address);
9748
+ if (existing) {
9749
+ return existing.config === config ? existing : "another client in this process uses this wallet with different batch options";
9750
+ }
9751
+ if (file) {
9752
+ const owner = lockChannelFile(file);
9753
+ if (owner !== void 0) return `channel store ${file} is in use by process ${owner}`;
9754
+ }
9755
+ const created = { config, busy: false, warned: /* @__PURE__ */ new Set() };
9756
+ wallets.set(address, created);
9757
+ return created;
9758
+ })();
9759
+ return this.state;
9760
+ }
9761
+ build(wallet) {
9762
+ wallet.client ??= (async () => {
9763
+ const [core, svm, kit] = await Promise.all([
9764
+ load("@x402/core", () => import("@x402/core/client")),
9765
+ load("@x402/svm", () => import("@x402/svm/batch-settlement/client")),
9766
+ load("@solana/kit", () => import("@solana/kit"))
9767
+ ]);
9768
+ const signer = await kit.createKeyPairSignerFromBytes(await this.init.secretKey());
9769
+ const file = await this.storeFile();
9770
+ const storage = file ? dropOrphanedPending(new FileChannelStorage(file)) : void 0;
9771
+ const scheme = new svm.BatchSvmScheme(signer, {
9772
+ rpcUrl: this.init.rpcUrl,
9773
+ serverSignedChannelsPolicy: {
9774
+ allowedOperators: this.init.options.operators,
9775
+ maxDeposit: this.init.options.maxDeposit ?? svm.DEFAULT_SERVER_SIGNED_MAX_DEPOSIT
9776
+ },
9777
+ ...storage ? { channelStorage: storage } : {}
9778
+ });
9779
+ const client = new core.x402Client().register("solana:*", scheme).registerPolicy(scheme.paymentPolicy);
9780
+ client.setSpendControls({ maxAmountPerPayment: false });
9781
+ return {
9782
+ http: new core.x402HTTPClient(client),
9783
+ refund: (url) => scheme.refund(url)
9784
+ };
9785
+ })();
9786
+ wallet.client.catch(() => {
9787
+ wallet.client = void 0;
9788
+ });
9789
+ return wallet.client;
9790
+ }
9791
+ /**
9792
+ * Drop everything this wallet knows about its channel, on disk and in
9793
+ * memory. The next call rebuilds the scheme, which reads the channel's real
9794
+ * state (deposit, open or closed) from the chain.
9795
+ */
9796
+ async forget(wallet) {
9797
+ wallet.client = void 0;
9798
+ const file = await this.storeFile();
9799
+ if (file) new FileChannelStorage(file).clear();
9800
+ }
9801
+ warn(wallet, reason) {
9802
+ if (wallet?.warned.has(reason)) return;
9803
+ wallet?.warned.add(reason);
9804
+ console.error(`[@blockrun/llm] batch-settlement unavailable (${reason}); paying with exact`);
9805
+ }
9806
+ /**
9807
+ * Pay one 402 with batch-settlement, or say why the caller should use exact.
9808
+ *
9809
+ * `send` replays the original request with the payment headers. The
9810
+ * returned response is 2xx; any other answer either becomes a fallback (the
9811
+ * gateway charged nothing) or is returned to the caller's own error path.
9812
+ */
9813
+ async pay(paymentRequired, send) {
9814
+ if (!offersBatch(paymentRequired)) return { kind: "fallback", reason: "not_offered" };
9815
+ const wallet = await this.wallet();
9816
+ if (typeof wallet === "string") {
9817
+ this.warn(void 0, wallet);
9818
+ return { kind: "fallback", reason: wallet };
9819
+ }
9820
+ if (wallet.busy) return { kind: "fallback", reason: "channel_busy" };
9821
+ wallet.busy = true;
9822
+ try {
9823
+ let http;
9824
+ let payload;
9825
+ try {
9826
+ http = (await this.build(wallet)).http;
9827
+ payload = await http.createPaymentPayload(paymentRequired);
9828
+ } catch (err) {
9829
+ const reason = err instanceof Error ? err.message : String(err);
9830
+ this.warn(wallet, reason);
9831
+ return { kind: "fallback", reason };
9832
+ }
9833
+ const deposit = payload.payload?.type === "deposit";
9834
+ let response;
9835
+ try {
9836
+ response = await send(http.encodePaymentSignatureHeader(payload));
9837
+ } catch (err) {
9838
+ if (deposit) await this.forget(wallet);
9839
+ else await this.settle(http, payload, () => null, 0);
9840
+ throw err;
9841
+ }
9842
+ const getHeader = (name) => response.headers.get(name);
9843
+ const settled = await this.settle(http, payload, getHeader, response.status);
9844
+ if (deposit && !(response.ok && settled?.success === true)) await this.forget(wallet);
9845
+ if (response.ok) {
9846
+ const charged = atomicToUsd(settled?.amount) ?? atomicToUsd(payload.accepted?.amount) ?? 0;
9847
+ return { kind: "paid", response, chargedUsd: charged };
9848
+ }
9849
+ const refusal = await isBatchRefusal(response);
9850
+ if (refusal) {
9851
+ this.warn(wallet, refusal);
9852
+ return { kind: "fallback", reason: refusal };
9853
+ }
9854
+ return { kind: "failed", response };
9855
+ } finally {
9856
+ wallet.busy = false;
9857
+ }
9858
+ }
9859
+ /**
9860
+ * Hand the gateway's answer to the scheme, which verifies the operator's
9861
+ * voucher and advances (or rolls back) the channel's local state.
9862
+ *
9863
+ * A malformed receipt must not lose a response the caller has been served,
9864
+ * so a scheme error is logged rather than thrown.
9865
+ */
9866
+ async settle(http, payload, getHeader, status2) {
9867
+ try {
9868
+ return (await http.processPaymentResult(payload, getHeader, status2)).settleResponse;
9869
+ } catch (err) {
9870
+ console.error(
9871
+ `[@blockrun/llm] batch-settlement receipt not reconciled: ${err instanceof Error ? err.message : String(err)}`
9872
+ );
9873
+ return void 0;
9874
+ }
9875
+ }
9876
+ /**
9877
+ * Close the channel and return its unused escrow to the wallet.
9878
+ *
9879
+ * The scheme keeps a closed channel in its records, and would go on paying
9880
+ * into it, so a successful close forgets the channel. The next batch call
9881
+ * finds no open channel on-chain and opens a new one.
9882
+ *
9883
+ * @param url - any batch-enabled route on the gateway the channel was opened with.
9884
+ */
9885
+ async close(url) {
9886
+ const wallet = await this.wallet();
9887
+ if (typeof wallet === "string") throw new Error(`batch-settlement unavailable: ${wallet}`);
9888
+ if (wallet.busy) throw new Error("batch-settlement channel has a request in flight; close it when the call returns");
9889
+ wallet.busy = true;
9890
+ try {
9891
+ const result = await (await this.build(wallet)).refund(url);
9892
+ await this.forget(wallet);
9893
+ return result;
9894
+ } finally {
9895
+ wallet.busy = false;
9896
+ }
9897
+ }
9898
+ };
9899
+
9572
9900
  // src/solana-client.ts
9573
9901
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
9574
9902
  var DEFAULT_MAX_TOKENS2 = 1024;
@@ -9632,7 +9960,7 @@ async function isSafeStaleBlockhashResponse(response) {
9632
9960
  }
9633
9961
  async function waitForStaleRetry(attempt) {
9634
9962
  await new Promise(
9635
- (resolve) => setTimeout(resolve, STALE_BLOCKHASH_RETRY_BACKOFFS_MS[attempt])
9963
+ (resolve2) => setTimeout(resolve2, STALE_BLOCKHASH_RETRY_BACKOFFS_MS[attempt])
9636
9964
  );
9637
9965
  }
9638
9966
  var DEFAULT_SOLANA_RPC_URL = "https://sol.blockrun.ai/api/v1/solana/rpc";
@@ -9676,6 +10004,7 @@ var SolanaLLMClient = class {
9676
10004
  addressCache = null;
9677
10005
  modelPricingCache = null;
9678
10006
  modelPricingPromise = null;
10007
+ batchPayer;
9679
10008
  constructor(options = {}) {
9680
10009
  this.apiAuth = resolveApiKeyAuth(options);
9681
10010
  if (!this.apiAuth) {
@@ -9695,6 +10024,25 @@ var SolanaLLMClient = class {
9695
10024
  this.rpcUrl = rpc.url;
9696
10025
  this.rpcHeaders = rpc.headers;
9697
10026
  this.timeout = options.timeout || DEFAULT_TIMEOUT14;
10027
+ if (options.batch && !this.apiAuth) {
10028
+ this.batchPayer = new SolanaBatchPayer({
10029
+ options: options.batch,
10030
+ secretKey: () => solanaKeyToBytes(this.privateKey),
10031
+ address: () => this.getWalletAddress(),
10032
+ rpcUrl: this.rpcUrl
10033
+ });
10034
+ }
10035
+ }
10036
+ /**
10037
+ * Close this wallet's batch-settlement channel and return its unused escrow.
10038
+ *
10039
+ * The gateway closes it cooperatively when it can; otherwise this starts a
10040
+ * payer-forced close and the escrow comes back after the channel's grace
10041
+ * period. Only meaningful with the `batch` option.
10042
+ */
10043
+ async closeBatchChannel() {
10044
+ if (!this.batchPayer) throw new Error("closeBatchChannel() requires the `batch` option in wallet mode");
10045
+ return this.batchPayer.close(`${this.apiUrl}/v1/chat/completions`);
9698
10046
  }
9699
10047
  /** Get Solana wallet address (public key in base58). */
9700
10048
  async getWalletAddress() {
@@ -9888,8 +10236,8 @@ var SolanaLLMClient = class {
9888
10236
  * how you end up debugging a request you did not send.
9889
10237
  * @returns each decoded SSE frame, in order.
9890
10238
  */
9891
- async *stream(path6, body) {
9892
- const url = this.buildUrl(path6);
10239
+ async *stream(path7, body) {
10240
+ const url = this.buildUrl(path7);
9893
10241
  const response = await this.openPaidStream(url, JSON.stringify(body ?? {}));
9894
10242
  yield* readSseFrames(
9895
10243
  response,
@@ -9961,8 +10309,8 @@ var SolanaLLMClient = class {
9961
10309
  * @param path - endpoint path, with or without a leading slash.
9962
10310
  * @returns the absolute URL to call.
9963
10311
  */
9964
- buildUrl(path6) {
9965
- let normalized = path6.startsWith("/") ? path6 : `/${path6}`;
10312
+ buildUrl(path7) {
10313
+ let normalized = path7.startsWith("/") ? path7 : `/${path7}`;
9966
10314
  if (normalized.startsWith("/api/")) normalized = normalized.slice(4);
9967
10315
  return `${this.apiUrl}${normalized}`;
9968
10316
  }
@@ -10041,11 +10389,11 @@ var SolanaLLMClient = class {
10041
10389
  return data;
10042
10390
  }
10043
10391
  // ── Prediction Markets (Powered by Predexon) ────────────────────────────
10044
- async pm(path6, params) {
10045
- return this.getWithPaymentRaw(`/v1/pm/${path6}`, params);
10392
+ async pm(path7, params) {
10393
+ return this.getWithPaymentRaw(`/v1/pm/${path7}`, params);
10046
10394
  }
10047
- async pmQuery(path6, query) {
10048
- return this.requestWithPaymentRaw(`/v1/pm/${path6}`, query);
10395
+ async pmQuery(path7, query) {
10396
+ return this.requestWithPaymentRaw(`/v1/pm/${path7}`, query);
10049
10397
  }
10050
10398
  // ── Exa Web Search (Powered by Exa) ──────────────────────────────────────
10051
10399
  /**
@@ -10057,8 +10405,8 @@ var SolanaLLMClient = class {
10057
10405
  * @example
10058
10406
  * const results = await client.exa("search", { query: "latest AI research", numResults: 5 });
10059
10407
  */
10060
- async exa(path6, body) {
10061
- return this.requestWithPaymentRaw(`/v1/exa/${path6}`, body);
10408
+ async exa(path7, body) {
10409
+ return this.requestWithPaymentRaw(`/v1/exa/${path7}`, body);
10062
10410
  }
10063
10411
  /**
10064
10412
  * Neural and keyword web search via Exa (Solana payment).
@@ -10108,8 +10456,8 @@ var SolanaLLMClient = class {
10108
10456
  * "prices/coingecko:bitcoin,base:0x..."
10109
10457
  * @param params - Query parameters passed through to DefiLlama
10110
10458
  */
10111
- async defi(path6, params) {
10112
- return this.getWithPaymentRaw(`/v1/defillama/${path6}`, params);
10459
+ async defi(path7, params) {
10460
+ return this.getWithPaymentRaw(`/v1/defillama/${path7}`, params);
10113
10461
  }
10114
10462
  /** All DeFi protocols with TVL. */
10115
10463
  async defiProtocols() {
@@ -10145,9 +10493,9 @@ var SolanaLLMClient = class {
10145
10493
  * @param params - Query params (chainId, sellToken, buyToken, sellAmount, taker, ...)
10146
10494
  * @param body - JSON body — pass to switch to POST (gasless/submit only)
10147
10495
  */
10148
- async dex(path6, params, body) {
10149
- if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path6}`, body);
10150
- return this.getWithPaymentRaw(`/v1/zerox/${path6}`, params);
10496
+ async dex(path7, params, body) {
10497
+ if (body) return this.requestWithPaymentRaw(`/v1/zerox/${path7}`, body);
10498
+ return this.getWithPaymentRaw(`/v1/zerox/${path7}`, params);
10151
10499
  }
10152
10500
  /** Indicative Permit2 swap price — no commitment (free). */
10153
10501
  async dexPrice(params) {
@@ -10191,8 +10539,8 @@ var SolanaLLMClient = class {
10191
10539
  * "sandbox/status", "sandbox/terminate"
10192
10540
  * @param body - JSON body for the endpoint
10193
10541
  */
10194
- async modal(path6, body) {
10195
- return this.requestWithPaymentRaw(`/v1/modal/${path6}`, body ?? {});
10542
+ async modal(path7, body) {
10543
+ return this.requestWithPaymentRaw(`/v1/modal/${path7}`, body ?? {});
10196
10544
  }
10197
10545
  /** Create a sandboxed compute environment (CPU / GPU tiers). */
10198
10546
  async modalSandboxCreate(body) {
@@ -10229,7 +10577,31 @@ var SolanaLLMClient = class {
10229
10577
  });
10230
10578
  if (response.status === 402) {
10231
10579
  try {
10232
- return await this.handlePaymentAndRetry(url, body, response, staleRetries > 0);
10580
+ const paymentRequired = await this.readPaymentRequired(response);
10581
+ if (this.batchPayer && staleRetries === 0) {
10582
+ const batch = await this.batchPayer.pay(
10583
+ paymentRequired,
10584
+ (paymentHeaders) => this.fetchWithTimeout(url, {
10585
+ method: "POST",
10586
+ headers: { "Content-Type": "application/json", "User-Agent": USER_AGENT, ...paymentHeaders },
10587
+ body: JSON.stringify(body)
10588
+ })
10589
+ );
10590
+ if (batch.kind === "paid") {
10591
+ this.recordSettlement(batch.chargedUsd);
10592
+ return batch.response.json();
10593
+ }
10594
+ if (batch.kind === "failed") {
10595
+ let errorBody;
10596
+ try {
10597
+ errorBody = await batch.response.json();
10598
+ } catch {
10599
+ errorBody = { error: "Request failed" };
10600
+ }
10601
+ throw new APIError(`API error after payment: ${batch.response.status}`, batch.response.status, sanitizeErrorResponse(errorBody));
10602
+ }
10603
+ }
10604
+ return await this.handlePaymentAndRetry(url, body, paymentRequired, staleRetries > 0);
10233
10605
  } catch (error) {
10234
10606
  if (!(error instanceof SafeStaleBlockhashError) || staleRetries >= STALE_BLOCKHASH_RETRY_BACKOFFS_MS.length) throw error;
10235
10607
  await waitForStaleRetry(staleRetries++);
@@ -10350,6 +10722,15 @@ var SolanaLLMClient = class {
10350
10722
  );
10351
10723
  }
10352
10724
  async signPaymentFrom402(url, response, forceFreshBlockhash, resourceFallback = url) {
10725
+ return this.signExactPayment(
10726
+ url,
10727
+ await this.readPaymentRequired(response),
10728
+ forceFreshBlockhash,
10729
+ resourceFallback
10730
+ );
10731
+ }
10732
+ /** Read a `402`'s payment requirements from its header, or its JSON body. */
10733
+ async readPaymentRequired(response) {
10353
10734
  let paymentHeader = response.headers.get("payment-required");
10354
10735
  if (!paymentHeader) {
10355
10736
  try {
@@ -10363,8 +10744,14 @@ var SolanaLLMClient = class {
10363
10744
  if (!paymentHeader) {
10364
10745
  throw new PaymentError("402 response but no payment requirements found");
10365
10746
  }
10366
- const paymentRequired = parsePaymentRequired(paymentHeader);
10367
- const details = extractPaymentDetails(paymentRequired, SOLANA_NETWORK);
10747
+ return parsePaymentRequired(paymentHeader);
10748
+ }
10749
+ /** Sign the `exact` accept of a 402 as an SPL TransferChecked payment. */
10750
+ async signExactPayment(url, paymentRequired, forceFreshBlockhash, resourceFallback = url) {
10751
+ const details = extractPaymentDetails(
10752
+ { ...paymentRequired, accepts: paymentRequired.accepts.filter((accept) => accept.scheme !== BATCH_SCHEME) },
10753
+ SOLANA_NETWORK
10754
+ );
10368
10755
  if (!details.network?.startsWith("solana:")) {
10369
10756
  throw new PaymentError(
10370
10757
  `Expected Solana payment network, got: ${details.network}. Use LLMClient for Base payments.`
@@ -10397,10 +10784,10 @@ var SolanaLLMClient = class {
10397
10784
  );
10398
10785
  return { paymentPayload, costUsd: parseFloat(details.amount) / 1e6 };
10399
10786
  }
10400
- async handlePaymentAndRetry(url, body, response, forceFreshBlockhash = false) {
10401
- const { paymentPayload, costUsd } = await this.signPaymentFrom402(
10787
+ async handlePaymentAndRetry(url, body, paymentRequired, forceFreshBlockhash = false) {
10788
+ const { paymentPayload, costUsd } = await this.signExactPayment(
10402
10789
  url,
10403
- response,
10790
+ paymentRequired,
10404
10791
  forceFreshBlockhash,
10405
10792
  `${this.apiUrl}/v1/chat/completions`
10406
10793
  );
@@ -10561,13 +10948,13 @@ function solanaClient(options = {}) {
10561
10948
  }
10562
10949
 
10563
10950
  // src/cache.ts
10564
- var fs6 = __toESM(require("fs"), 1);
10565
- var path5 = __toESM(require("path"), 1);
10951
+ var fs7 = __toESM(require("fs"), 1);
10952
+ var path6 = __toESM(require("path"), 1);
10566
10953
  var os4 = __toESM(require("os"), 1);
10567
10954
  var crypto2 = __toESM(require("crypto"), 1);
10568
- var CACHE_DIR = path5.join(os4.homedir(), ".blockrun", "cache");
10569
- var DATA_DIR = path5.join(os4.homedir(), ".blockrun", "data");
10570
- var COST_LOG_FILE2 = path5.join(os4.homedir(), ".blockrun", "cost_log.jsonl");
10955
+ var CACHE_DIR = path6.join(os4.homedir(), ".blockrun", "cache");
10956
+ var DATA_DIR = path6.join(os4.homedir(), ".blockrun", "data");
10957
+ var COST_LOG_FILE2 = path6.join(os4.homedir(), ".blockrun", "cost_log.jsonl");
10571
10958
  var DEFAULT_TTL = {
10572
10959
  "/v1/partner/": 3600 * 1e3,
10573
10960
  "/v1/pm/": 1800 * 1e3,
@@ -10587,19 +10974,19 @@ function cacheKey(endpoint, body) {
10587
10974
  return crypto2.createHash("sha256").update(keyData).digest("hex").slice(0, 16);
10588
10975
  }
10589
10976
  function cachePath(key) {
10590
- return path5.join(CACHE_DIR, `${key}.json`);
10977
+ return path6.join(CACHE_DIR, `${key}.json`);
10591
10978
  }
10592
10979
  function getCached(key) {
10593
10980
  const filePath = cachePath(key);
10594
- if (!fs6.existsSync(filePath)) return null;
10981
+ if (!fs7.existsSync(filePath)) return null;
10595
10982
  try {
10596
- const raw = fs6.readFileSync(filePath, "utf-8");
10983
+ const raw = fs7.readFileSync(filePath, "utf-8");
10597
10984
  const entry = JSON.parse(raw);
10598
10985
  const ttl = entry.ttlMs ?? getTtl(entry.endpoint ?? "");
10599
10986
  if (ttl <= 0) return null;
10600
10987
  if (Date.now() - entry.cachedAt > ttl) {
10601
10988
  try {
10602
- fs6.unlinkSync(filePath);
10989
+ fs7.unlinkSync(filePath);
10603
10990
  } catch {
10604
10991
  }
10605
10992
  return null;
@@ -10618,7 +11005,7 @@ function getCachedByRequest(endpoint, body) {
10618
11005
  function setCache(key, data, ttlMs) {
10619
11006
  if (ttlMs <= 0) return;
10620
11007
  try {
10621
- fs6.mkdirSync(CACHE_DIR, { recursive: true });
11008
+ fs7.mkdirSync(CACHE_DIR, { recursive: true });
10622
11009
  } catch {
10623
11010
  }
10624
11011
  const entry = {
@@ -10627,7 +11014,7 @@ function setCache(key, data, ttlMs) {
10627
11014
  ttlMs
10628
11015
  };
10629
11016
  try {
10630
- fs6.writeFileSync(cachePath(key), JSON.stringify(entry));
11017
+ fs7.writeFileSync(cachePath(key), JSON.stringify(entry));
10631
11018
  } catch {
10632
11019
  }
10633
11020
  }
@@ -10648,7 +11035,7 @@ function readableFilename(endpoint, body) {
10648
11035
  }
10649
11036
  function saveReadable(endpoint, body, response, costUsd) {
10650
11037
  try {
10651
- fs6.mkdirSync(DATA_DIR, { recursive: true });
11038
+ fs7.mkdirSync(DATA_DIR, { recursive: true });
10652
11039
  } catch {
10653
11040
  }
10654
11041
  const filename = readableFilename(endpoint, body);
@@ -10660,14 +11047,14 @@ function saveReadable(endpoint, body, response, costUsd) {
10660
11047
  response
10661
11048
  };
10662
11049
  try {
10663
- fs6.writeFileSync(path5.join(DATA_DIR, filename), JSON.stringify(entry, null, 2));
11050
+ fs7.writeFileSync(path6.join(DATA_DIR, filename), JSON.stringify(entry, null, 2));
10664
11051
  } catch {
10665
11052
  }
10666
11053
  }
10667
11054
  function appendCostLog(endpoint, costUsd) {
10668
11055
  if (costUsd <= 0) return;
10669
11056
  try {
10670
- fs6.mkdirSync(path5.dirname(COST_LOG_FILE2), { recursive: true });
11057
+ fs7.mkdirSync(path6.dirname(COST_LOG_FILE2), { recursive: true });
10671
11058
  } catch {
10672
11059
  }
10673
11060
  const entry = {
@@ -10676,7 +11063,7 @@ function appendCostLog(endpoint, costUsd) {
10676
11063
  cost_usd: costUsd
10677
11064
  };
10678
11065
  try {
10679
- fs6.appendFileSync(COST_LOG_FILE2, JSON.stringify(entry) + "\n");
11066
+ fs7.appendFileSync(COST_LOG_FILE2, JSON.stringify(entry) + "\n");
10680
11067
  } catch {
10681
11068
  }
10682
11069
  }
@@ -10684,7 +11071,7 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
10684
11071
  const ttl = getTtl(endpoint);
10685
11072
  if (ttl > 0) {
10686
11073
  try {
10687
- fs6.mkdirSync(CACHE_DIR, { recursive: true });
11074
+ fs7.mkdirSync(CACHE_DIR, { recursive: true });
10688
11075
  } catch {
10689
11076
  }
10690
11077
  const key = cacheKey(endpoint, body);
@@ -10696,7 +11083,7 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
10696
11083
  costUsd
10697
11084
  };
10698
11085
  try {
10699
- fs6.writeFileSync(cachePath(key), JSON.stringify(entry));
11086
+ fs7.writeFileSync(cachePath(key), JSON.stringify(entry));
10700
11087
  } catch {
10701
11088
  }
10702
11089
  }
@@ -10704,14 +11091,14 @@ function saveToCache(endpoint, body, response, costUsd = 0) {
10704
11091
  appendCostLog(endpoint, costUsd);
10705
11092
  }
10706
11093
  function clearCache() {
10707
- if (!fs6.existsSync(CACHE_DIR)) return 0;
11094
+ if (!fs7.existsSync(CACHE_DIR)) return 0;
10708
11095
  let count = 0;
10709
11096
  try {
10710
- const files = fs6.readdirSync(CACHE_DIR);
11097
+ const files = fs7.readdirSync(CACHE_DIR);
10711
11098
  for (const file of files) {
10712
11099
  if (file.endsWith(".json")) {
10713
11100
  try {
10714
- fs6.unlinkSync(path5.join(CACHE_DIR, file));
11101
+ fs7.unlinkSync(path6.join(CACHE_DIR, file));
10715
11102
  count++;
10716
11103
  } catch {
10717
11104
  }
@@ -10722,14 +11109,14 @@ function clearCache() {
10722
11109
  return count;
10723
11110
  }
10724
11111
  function getCostLogSummary() {
10725
- if (!fs6.existsSync(COST_LOG_FILE2)) {
11112
+ if (!fs7.existsSync(COST_LOG_FILE2)) {
10726
11113
  return { totalUsd: 0, calls: 0, byEndpoint: {} };
10727
11114
  }
10728
11115
  let totalUsd = 0;
10729
11116
  let calls = 0;
10730
11117
  const byEndpoint = {};
10731
11118
  try {
10732
- const content = fs6.readFileSync(COST_LOG_FILE2, "utf-8").trim();
11119
+ const content = fs7.readFileSync(COST_LOG_FILE2, "utf-8").trim();
10733
11120
  if (!content) return { totalUsd: 0, calls: 0, byEndpoint: {} };
10734
11121
  for (const line of content.split("\n")) {
10735
11122
  if (!line) continue;
@@ -11104,6 +11491,7 @@ var AnthropicClient = class {
11104
11491
  ARC_CHAIN_ID,
11105
11492
  AnthropicClient,
11106
11493
  BASE_CHAIN_ID,
11494
+ BLOCKRUN_SOL_OPERATOR,
11107
11495
  BlockrunClient,
11108
11496
  BlockrunError,
11109
11497
  EVM_NETWORKS,