@blockrun/llm 2.0.0 → 2.1.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
@@ -48,6 +48,7 @@ __export(index_exports, {
48
48
  USDC_BASE: () => USDC_BASE,
49
49
  USDC_BASE_CONTRACT: () => USDC_BASE_CONTRACT,
50
50
  USDC_SOLANA: () => USDC_SOLANA,
51
+ VideoClient: () => VideoClient,
51
52
  WALLET_DIR_PATH: () => WALLET_DIR_PATH,
52
53
  WALLET_FILE_PATH: () => WALLET_FILE_PATH,
53
54
  XClient: () => XClient,
@@ -619,9 +620,6 @@ var LLMClient = class _LLMClient {
619
620
  *
620
621
  * @example With routing profile
621
622
  * ```ts
622
- * // Free tier only (zero cost)
623
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
624
- *
625
623
  * // Eco mode (budget optimized)
626
624
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
627
625
  *
@@ -781,6 +779,28 @@ var LLMClient = class _LLMClient {
781
779
  } catch {
782
780
  }
783
781
  }
782
+ /**
783
+ * Parse the chat response JSON and attach `fallback` metadata when the
784
+ * gateway signalled a transparent free-fallback substitution. The
785
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
786
+ * on the response when it served a paid request from a free model
787
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
788
+ * to the caller, the user gets a different model than requested with
789
+ * no visibility — silent quality drop and no clue why the on-chain
790
+ * balance didn't change.
791
+ */
792
+ async parseChatResponse(response) {
793
+ const body = await response.json();
794
+ const used = response.headers.get("X-Fallback-Used") === "true";
795
+ if (used) {
796
+ body.fallback = {
797
+ used: true,
798
+ model: response.headers.get("X-Fallback-Model") || void 0,
799
+ settlementSkipped: response.headers.get("X-Settlement-Skipped") === "free-fallback"
800
+ };
801
+ }
802
+ return body;
803
+ }
784
804
  /**
785
805
  * Make a request with automatic x402 payment handling.
786
806
  */
@@ -809,7 +829,7 @@ var LLMClient = class _LLMClient {
809
829
  }
810
830
  throw new APIError(`API error: ${retryResp.status}`, retryResp.status, sanitizeErrorResponse(errorBody));
811
831
  }
812
- return retryResp.json();
832
+ return this.parseChatResponse(retryResp);
813
833
  }
814
834
  }
815
835
  if (response.status === 402) {
@@ -828,7 +848,7 @@ var LLMClient = class _LLMClient {
828
848
  sanitizeErrorResponse(errorBody)
829
849
  );
830
850
  }
831
- return response.json();
851
+ return this.parseChatResponse(response);
832
852
  }
833
853
  /**
834
854
  * Handle 402 response: parse requirements, sign payment, retry.
@@ -903,7 +923,7 @@ var LLMClient = class _LLMClient {
903
923
  this.sessionCalls += 1;
904
924
  this.sessionTotalUsd += costUsd2;
905
925
  this.recordCost(url, costUsd2, { body, network: details.network });
906
- return retryResp2.json();
926
+ return this.parseChatResponse(retryResp2);
907
927
  }
908
928
  }
909
929
  if (retryResponse.status === 402) {
@@ -926,7 +946,7 @@ var LLMClient = class _LLMClient {
926
946
  this.sessionCalls += 1;
927
947
  this.sessionTotalUsd += costUsd;
928
948
  this.recordCost(url, costUsd, { body, network: details.network });
929
- return retryResponse.json();
949
+ return this.parseChatResponse(retryResponse);
930
950
  }
931
951
  /**
932
952
  * Sign a payment header and return the PAYMENT-SIGNATURE value.
@@ -2344,15 +2364,21 @@ var MusicClient = class {
2344
2364
  }
2345
2365
  };
2346
2366
 
2347
- // src/search.ts
2367
+ // src/video.ts
2348
2368
  var import_accounts5 = require("viem/accounts");
2349
2369
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2350
- var DEFAULT_TIMEOUT4 = 6e4;
2351
- var SearchClient = class {
2370
+ var DEFAULT_MODEL3 = "xai/grok-imagine-video";
2371
+ var DEFAULT_TIMEOUT4 = 12e4;
2372
+ var POLL_INTERVAL_MS2 = 5e3;
2373
+ var DEFAULT_GENERATE_BUDGET_MS = 3e5;
2374
+ var MAX_TIMEOUT_SECONDS = 600;
2375
+ var VideoClient = class {
2352
2376
  account;
2353
2377
  privateKey;
2354
2378
  apiUrl;
2355
2379
  timeout;
2380
+ sessionTotalUsd = 0;
2381
+ sessionCalls = 0;
2356
2382
  constructor(options = {}) {
2357
2383
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2358
2384
  const privateKey = options.privateKey || envKey;
@@ -2369,6 +2395,193 @@ var SearchClient = class {
2369
2395
  this.apiUrl = apiUrl.replace(/\/$/, "");
2370
2396
  this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2371
2397
  }
2398
+ /**
2399
+ * Generate a short video clip from a text prompt (or text + image).
2400
+ *
2401
+ * Submits an async job, then polls until the video is ready. Typical total
2402
+ * wall-time is 60-180s. If upstream runs past the budget (default 5min),
2403
+ * throws without charging.
2404
+ *
2405
+ * @param prompt - Text description of the video
2406
+ * @param options - Optional generation parameters
2407
+ */
2408
+ async generate(prompt, options) {
2409
+ const body = {
2410
+ model: options?.model || DEFAULT_MODEL3,
2411
+ prompt
2412
+ };
2413
+ if (options?.imageUrl) body.image_url = options.imageUrl;
2414
+ if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2415
+ const budgetMs = options?.budgetMs ?? DEFAULT_GENERATE_BUDGET_MS;
2416
+ return this.submitAndPoll(body, budgetMs);
2417
+ }
2418
+ // --------------------------------------------------------------------
2419
+ // Internal: async submit + poll
2420
+ // --------------------------------------------------------------------
2421
+ async submitAndPoll(body, budgetMs) {
2422
+ const submitUrl = `${this.apiUrl}/v1/videos/generations`;
2423
+ const resp402 = await this.fetchWithTimeout(submitUrl, {
2424
+ method: "POST",
2425
+ headers: { "Content-Type": "application/json" },
2426
+ body: JSON.stringify(body)
2427
+ });
2428
+ if (resp402.status !== 402) {
2429
+ await this.throwApiError(resp402, "Expected 402 on first POST");
2430
+ }
2431
+ const paymentRequired = await this.extractPaymentRequired(resp402);
2432
+ const details = extractPaymentDetails(paymentRequired);
2433
+ const paymentPayload = await createPaymentPayload(
2434
+ this.privateKey,
2435
+ this.account.address,
2436
+ details.recipient,
2437
+ details.amount,
2438
+ details.network || "eip155:8453",
2439
+ {
2440
+ resourceUrl: details.resource?.url || submitUrl,
2441
+ resourceDescription: details.resource?.description || "BlockRun Video Generation",
2442
+ // Ensure signed auth covers the entire polling window.
2443
+ maxTimeoutSeconds: Math.max(details.maxTimeoutSeconds || 0, MAX_TIMEOUT_SECONDS),
2444
+ extra: details.extra
2445
+ }
2446
+ );
2447
+ const submitResp = await this.fetchWithTimeout(submitUrl, {
2448
+ method: "POST",
2449
+ headers: {
2450
+ "Content-Type": "application/json",
2451
+ "PAYMENT-SIGNATURE": paymentPayload
2452
+ },
2453
+ body: JSON.stringify(body)
2454
+ });
2455
+ if (submitResp.status === 402) {
2456
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2457
+ }
2458
+ if (submitResp.status !== 200 && submitResp.status !== 202) {
2459
+ await this.throwApiError(submitResp, "Submit failed");
2460
+ }
2461
+ const submitData = await submitResp.json();
2462
+ if (!submitData.id || !submitData.poll_url) {
2463
+ throw new APIError(
2464
+ "Submit response missing id/poll_url",
2465
+ submitResp.status,
2466
+ { response: submitData }
2467
+ );
2468
+ }
2469
+ const pollUrl = this.absolute(submitData.poll_url);
2470
+ const deadline = Date.now() + budgetMs;
2471
+ let lastStatus = submitData.status || "queued";
2472
+ while (Date.now() < deadline) {
2473
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS2));
2474
+ const pollResp = await this.fetchWithTimeout(pollUrl, {
2475
+ method: "GET",
2476
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
2477
+ });
2478
+ let pollData = {};
2479
+ try {
2480
+ pollData = await pollResp.json();
2481
+ } catch {
2482
+ }
2483
+ lastStatus = pollData.status || lastStatus;
2484
+ if (pollResp.status === 202 && (lastStatus === "queued" || lastStatus === "in_progress")) {
2485
+ continue;
2486
+ }
2487
+ if (lastStatus === "failed") {
2488
+ throw new APIError(
2489
+ `Upstream generation failed: ${pollData.error || "unknown"}`,
2490
+ pollResp.status,
2491
+ sanitizeErrorResponse(pollData)
2492
+ );
2493
+ }
2494
+ if (pollResp.status === 200 && lastStatus === "completed") {
2495
+ const data = pollData;
2496
+ const billedSeconds = typeof body.duration_seconds === "number" ? body.duration_seconds : 8;
2497
+ this.sessionCalls++;
2498
+ this.sessionTotalUsd += 0.05 * billedSeconds * 1.05;
2499
+ const txHash = pollResp.headers.get("x-payment-receipt") || pollResp.headers.get("X-Payment-Receipt");
2500
+ if (txHash) data.txHash = txHash;
2501
+ return data;
2502
+ }
2503
+ if (pollResp.status !== 200 && pollResp.status !== 202 && pollResp.status !== 504) {
2504
+ await this.throwApiError(pollResp, "Poll failed");
2505
+ }
2506
+ }
2507
+ throw new APIError(
2508
+ `Video generation did not complete within ${Math.round(budgetMs / 1e3)}s (last status: ${lastStatus}). No payment was taken.`,
2509
+ 504,
2510
+ { id: submitData.id, last_status: lastStatus }
2511
+ );
2512
+ }
2513
+ absolute(url) {
2514
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
2515
+ const base = this.apiUrl.endsWith("/api") ? this.apiUrl.slice(0, -"/api".length) : this.apiUrl;
2516
+ return `${base}${url}`;
2517
+ }
2518
+ async extractPaymentRequired(resp) {
2519
+ const header = resp.headers.get("payment-required");
2520
+ if (header) return parsePaymentRequired(header);
2521
+ try {
2522
+ const body = await resp.json();
2523
+ if (body && (body.x402Version !== void 0 || body.accepts !== void 0)) {
2524
+ return body;
2525
+ }
2526
+ } catch {
2527
+ }
2528
+ throw new PaymentError("402 response but no payment requirements found");
2529
+ }
2530
+ async throwApiError(resp, prefix) {
2531
+ let errorBody;
2532
+ try {
2533
+ errorBody = await resp.json();
2534
+ } catch {
2535
+ errorBody = { error: "Request failed" };
2536
+ }
2537
+ throw new APIError(
2538
+ `${prefix}: HTTP ${resp.status}`,
2539
+ resp.status,
2540
+ sanitizeErrorResponse(errorBody)
2541
+ );
2542
+ }
2543
+ async fetchWithTimeout(url, options) {
2544
+ const controller = new AbortController();
2545
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2546
+ try {
2547
+ return await fetch(url, { ...options, signal: controller.signal });
2548
+ } finally {
2549
+ clearTimeout(timeoutId);
2550
+ }
2551
+ }
2552
+ getWalletAddress() {
2553
+ return this.account.address;
2554
+ }
2555
+ getSpending() {
2556
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2557
+ }
2558
+ };
2559
+
2560
+ // src/search.ts
2561
+ var import_accounts6 = require("viem/accounts");
2562
+ var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2563
+ var DEFAULT_TIMEOUT5 = 6e4;
2564
+ var SearchClient = class {
2565
+ account;
2566
+ privateKey;
2567
+ apiUrl;
2568
+ timeout;
2569
+ constructor(options = {}) {
2570
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2571
+ const privateKey = options.privateKey || envKey;
2572
+ if (!privateKey) {
2573
+ throw new Error(
2574
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
2575
+ );
2576
+ }
2577
+ validatePrivateKey(privateKey);
2578
+ this.privateKey = privateKey;
2579
+ this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
2580
+ const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2581
+ validateApiUrl(apiUrl);
2582
+ this.apiUrl = apiUrl.replace(/\/$/, "");
2583
+ this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2584
+ }
2372
2585
  async search(query, options) {
2373
2586
  if (!query || query.length > 1e3) {
2374
2587
  throw new Error("query must be 1-1000 characters");
@@ -2481,9 +2694,9 @@ var SearchClient = class {
2481
2694
  };
2482
2695
 
2483
2696
  // src/x-client.ts
2484
- var import_accounts6 = require("viem/accounts");
2485
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2486
- var DEFAULT_TIMEOUT5 = 6e4;
2697
+ var import_accounts7 = require("viem/accounts");
2698
+ var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2699
+ var DEFAULT_TIMEOUT6 = 6e4;
2487
2700
  var XClient = class _XClient {
2488
2701
  account;
2489
2702
  privateKey;
@@ -2506,11 +2719,11 @@ var XClient = class _XClient {
2506
2719
  }
2507
2720
  validatePrivateKey(privateKey);
2508
2721
  this.privateKey = privateKey;
2509
- this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
2510
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2722
+ this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
2723
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2511
2724
  validateApiUrl(apiUrl);
2512
2725
  this.apiUrl = apiUrl.replace(/\/$/, "");
2513
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2726
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2514
2727
  }
2515
2728
  // ───────── User endpoints ─────────
2516
2729
  userLookup(usernames) {
@@ -2688,9 +2901,9 @@ var XClient = class _XClient {
2688
2901
  };
2689
2902
 
2690
2903
  // src/price.ts
2691
- var import_accounts7 = require("viem/accounts");
2692
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2693
- var DEFAULT_TIMEOUT6 = 3e4;
2904
+ var import_accounts8 = require("viem/accounts");
2905
+ var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2906
+ var DEFAULT_TIMEOUT7 = 3e4;
2694
2907
  var PriceClient = class {
2695
2908
  account = null;
2696
2909
  privateKey = null;
@@ -2708,12 +2921,12 @@ var PriceClient = class {
2708
2921
  if (privateKey) {
2709
2922
  validatePrivateKey(privateKey);
2710
2923
  this.privateKey = privateKey;
2711
- this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
2924
+ this.account = (0, import_accounts8.privateKeyToAccount)(privateKey);
2712
2925
  }
2713
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2926
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2714
2927
  validateApiUrl(apiUrl);
2715
2928
  this.apiUrl = apiUrl.replace(/\/$/, "");
2716
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2929
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2717
2930
  }
2718
2931
  async price(category, symbol, options) {
2719
2932
  if (!symbol) throw new Error("symbol is required");
@@ -2892,7 +3105,7 @@ function buildUrl(base, query) {
2892
3105
  }
2893
3106
 
2894
3107
  // src/wallet.ts
2895
- var import_accounts8 = require("viem/accounts");
3108
+ var import_accounts9 = require("viem/accounts");
2896
3109
  var fs2 = __toESM(require("fs"), 1);
2897
3110
  var path2 = __toESM(require("path"), 1);
2898
3111
  var os2 = __toESM(require("os"), 1);
@@ -2901,8 +3114,8 @@ var BASE_CHAIN_ID2 = "8453";
2901
3114
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
2902
3115
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
2903
3116
  function createWallet() {
2904
- const privateKey = (0, import_accounts8.generatePrivateKey)();
2905
- const account = (0, import_accounts8.privateKeyToAccount)(privateKey);
3117
+ const privateKey = (0, import_accounts9.generatePrivateKey)();
3118
+ const account = (0, import_accounts9.privateKeyToAccount)(privateKey);
2906
3119
  return {
2907
3120
  address: account.address,
2908
3121
  privateKey
@@ -2958,12 +3171,12 @@ function loadWallet() {
2958
3171
  function getOrCreateWallet() {
2959
3172
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2960
3173
  if (envKey) {
2961
- const account = (0, import_accounts8.privateKeyToAccount)(envKey);
3174
+ const account = (0, import_accounts9.privateKeyToAccount)(envKey);
2962
3175
  return { address: account.address, privateKey: envKey, isNew: false };
2963
3176
  }
2964
3177
  const fileKey = loadWallet();
2965
3178
  if (fileKey) {
2966
- const account = (0, import_accounts8.privateKeyToAccount)(fileKey);
3179
+ const account = (0, import_accounts9.privateKeyToAccount)(fileKey);
2967
3180
  return { address: account.address, privateKey: fileKey, isNew: false };
2968
3181
  }
2969
3182
  const { address, privateKey } = createWallet();
@@ -2973,11 +3186,11 @@ function getOrCreateWallet() {
2973
3186
  function getWalletAddress() {
2974
3187
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2975
3188
  if (envKey) {
2976
- return (0, import_accounts8.privateKeyToAccount)(envKey).address;
3189
+ return (0, import_accounts9.privateKeyToAccount)(envKey).address;
2977
3190
  }
2978
3191
  const fileKey = loadWallet();
2979
3192
  if (fileKey) {
2980
- return (0, import_accounts8.privateKeyToAccount)(fileKey).address;
3193
+ return (0, import_accounts9.privateKeyToAccount)(fileKey).address;
2981
3194
  }
2982
3195
  return null;
2983
3196
  }
@@ -3157,7 +3370,7 @@ async function getOrCreateSolanaWallet() {
3157
3370
  // src/solana-client.ts
3158
3371
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3159
3372
  var DEFAULT_MAX_TOKENS2 = 1024;
3160
- var DEFAULT_TIMEOUT7 = 6e4;
3373
+ var DEFAULT_TIMEOUT8 = 6e4;
3161
3374
  var SDK_VERSION2 = "0.3.0";
3162
3375
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3163
3376
  var SolanaLLMClient = class {
@@ -3182,7 +3395,7 @@ var SolanaLLMClient = class {
3182
3395
  validateApiUrl(apiUrl);
3183
3396
  this.apiUrl = apiUrl.replace(/\/$/, "");
3184
3397
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3185
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3398
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3186
3399
  }
3187
3400
  /** Get Solana wallet address (public key in base58). */
3188
3401
  async getWalletAddress() {
@@ -4007,6 +4220,9 @@ var ChatCompletions = class {
4007
4220
  this.apiUrl = apiUrl;
4008
4221
  this.timeout = timeout;
4009
4222
  }
4223
+ client;
4224
+ apiUrl;
4225
+ timeout;
4010
4226
  async create(params) {
4011
4227
  if (params.stream) {
4012
4228
  return this.createStream(params);
@@ -4086,7 +4302,7 @@ var OpenAI = class {
4086
4302
  };
4087
4303
 
4088
4304
  // src/anthropic-compat.ts
4089
- var import_accounts9 = require("viem/accounts");
4305
+ var import_accounts10 = require("viem/accounts");
4090
4306
  var AnthropicClient = class {
4091
4307
  _client = null;
4092
4308
  _clientPromise = null;
@@ -4099,7 +4315,7 @@ var AnthropicClient = class {
4099
4315
  const key = options.privateKey ?? wallet.privateKey;
4100
4316
  validatePrivateKey(key);
4101
4317
  this._privateKey = key;
4102
- this._account = (0, import_accounts9.privateKeyToAccount)(this._privateKey);
4318
+ this._account = (0, import_accounts10.privateKeyToAccount)(this._privateKey);
4103
4319
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4104
4320
  validateApiUrl(apiUrl);
4105
4321
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4213,6 +4429,7 @@ var AnthropicClient = class {
4213
4429
  USDC_BASE,
4214
4430
  USDC_BASE_CONTRACT,
4215
4431
  USDC_SOLANA,
4432
+ VideoClient,
4216
4433
  WALLET_DIR_PATH,
4217
4434
  WALLET_FILE_PATH,
4218
4435
  XClient,
package/dist/index.d.cts CHANGED
@@ -58,6 +58,25 @@ interface ChatResponse {
58
58
  choices: ChatChoice[];
59
59
  usage?: ChatUsage;
60
60
  citations?: string[];
61
+ /**
62
+ * Populated when the gateway transparently substituted a different
63
+ * model for the one the caller asked for — typically because the
64
+ * requested model errored and the gateway routed to a free fallback
65
+ * to fulfil the request. When `used` is true:
66
+ * - `model` is the model that actually answered (vs `ChatResponse.model`
67
+ * which historically reflected the requested model id).
68
+ * - `settlementSkipped` is `true` when the gateway also skipped the
69
+ * on-chain settle — i.e. the user was not charged for this call
70
+ * because a free fallback served it.
71
+ * Surfaced from the gateway's `X-Fallback-Used / X-Fallback-Model /
72
+ * X-Settlement-Skipped` response headers. Absent when the headers
73
+ * aren't present (most calls).
74
+ */
75
+ fallback?: {
76
+ used: true;
77
+ model?: string;
78
+ settlementSkipped?: boolean;
79
+ };
61
80
  }
62
81
  interface Model {
63
82
  id: string;
@@ -272,7 +291,7 @@ interface ChatCompletionOptions {
272
291
  */
273
292
  fallbackModels?: string[];
274
293
  }
275
- type RoutingProfile = "free" | "eco" | "auto" | "premium";
294
+ type RoutingProfile = "eco" | "auto" | "premium";
276
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
277
296
  interface RoutingDecision {
278
297
  model: string;
@@ -283,6 +302,10 @@ interface RoutingDecision {
283
302
  costEstimate: number;
284
303
  baselineCost: number;
285
304
  savings: number;
305
+ /** Routing profile applied by clawrouter (may include "agentic" on gateway responses). */
306
+ profile?: RoutingProfile | "agentic";
307
+ /** Score used when agentic routing is active. */
308
+ agenticScore?: number;
286
309
  /**
287
310
  * Remaining tier models with known pricing, in fallback order. `chat()`
288
311
  * walks this list when the primary model hits a transient error
@@ -291,7 +314,7 @@ interface RoutingDecision {
291
314
  fallbacks?: string[];
292
315
  }
293
316
  interface SmartChatOptions extends ChatOptions {
294
- /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
317
+ /** Routing profile: eco (budget), auto (balanced), premium (best quality) */
295
318
  routingProfile?: RoutingProfile;
296
319
  /** Maximum output tokens (used for cost estimation) */
297
320
  maxOutputTokens?: number;
@@ -762,9 +785,6 @@ declare class LLMClient {
762
785
  *
763
786
  * @example With routing profile
764
787
  * ```ts
765
- * // Free tier only (zero cost)
766
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
767
- *
768
788
  * // Eco mode (budget optimized)
769
789
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
770
790
  *
@@ -811,6 +831,17 @@ declare class LLMClient {
811
831
  * cost_log.jsonl is a single source of truth regardless of caller.
812
832
  */
813
833
  private recordCost;
834
+ /**
835
+ * Parse the chat response JSON and attach `fallback` metadata when the
836
+ * gateway signalled a transparent free-fallback substitution. The
837
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
838
+ * on the response when it served a paid request from a free model
839
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
840
+ * to the caller, the user gets a different model than requested with
841
+ * no visibility — silent quality drop and no clue why the on-chain
842
+ * balance didn't change.
843
+ */
844
+ private parseChatResponse;
814
845
  /**
815
846
  * Make a request with automatic x402 payment handling.
816
847
  */
@@ -1323,6 +1354,68 @@ declare class MusicClient {
1323
1354
  getSpending(): Spending;
1324
1355
  }
1325
1356
 
1357
+ /**
1358
+ * BlockRun Video Client - Generate short AI videos via x402 micropayments.
1359
+ *
1360
+ * SECURITY NOTE - Private Key Handling:
1361
+ * Your private key NEVER leaves your machine. Here's what happens:
1362
+ * 1. Key stays local - only used to sign an EIP-712 typed data message
1363
+ * 2. Only the SIGNATURE is sent in the PAYMENT-SIGNATURE header
1364
+ * 3. BlockRun verifies the signature on-chain via Coinbase CDP facilitator
1365
+ *
1366
+ * Async flow (client-polled):
1367
+ * POST /v1/videos/generations -> 402 -> sign -> 202 { id, poll_url }
1368
+ * GET /v1/videos/generations/{id} -> loop until status=completed
1369
+ *
1370
+ * The client signs ONCE and replays the same PAYMENT-SIGNATURE on every poll.
1371
+ * Settlement happens only on the first completed poll, so upstream failure or
1372
+ * the caller giving up = zero charge.
1373
+ *
1374
+ * Usage:
1375
+ * import { VideoClient } from '@blockrun/llm';
1376
+ *
1377
+ * const client = new VideoClient({ privateKey: '0x...' });
1378
+ * const result = await client.generate('a red apple slowly spinning on a wooden table');
1379
+ * console.log(result.data[0].url); // permanent MP4 URL
1380
+ * console.log(result.data[0].duration_seconds);
1381
+ */
1382
+
1383
+ /**
1384
+ * BlockRun Video Generation Client.
1385
+ *
1386
+ * Supports xAI Grok Imagine Video and ByteDance Seedance (1.5 Pro /
1387
+ * 2.0 Fast / 2.0 Pro) with automatic x402 micropayments on Base.
1388
+ */
1389
+ declare class VideoClient {
1390
+ private account;
1391
+ private privateKey;
1392
+ private apiUrl;
1393
+ private timeout;
1394
+ private sessionTotalUsd;
1395
+ private sessionCalls;
1396
+ constructor(options?: VideoClientOptions);
1397
+ /**
1398
+ * Generate a short video clip from a text prompt (or text + image).
1399
+ *
1400
+ * Submits an async job, then polls until the video is ready. Typical total
1401
+ * wall-time is 60-180s. If upstream runs past the budget (default 5min),
1402
+ * throws without charging.
1403
+ *
1404
+ * @param prompt - Text description of the video
1405
+ * @param options - Optional generation parameters
1406
+ */
1407
+ generate(prompt: string, options?: VideoGenerateOptions & {
1408
+ budgetMs?: number;
1409
+ }): Promise<VideoResponse>;
1410
+ private submitAndPoll;
1411
+ private absolute;
1412
+ private extractPaymentRequired;
1413
+ private throwApiError;
1414
+ private fetchWithTimeout;
1415
+ getWalletAddress(): string;
1416
+ getSpending(): Spending;
1417
+ }
1418
+
1326
1419
  /**
1327
1420
  * BlockRun Search Client - Standalone Grok Live Search via x402 micropayments.
1328
1421
  *
@@ -2100,4 +2193,4 @@ declare function validateTemperature(temperature?: number): void;
2100
2193
  */
2101
2194
  declare function validateTopP(topP?: number): void;
2102
2195
 
2103
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
2196
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, VideoClient, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,25 @@ interface ChatResponse {
58
58
  choices: ChatChoice[];
59
59
  usage?: ChatUsage;
60
60
  citations?: string[];
61
+ /**
62
+ * Populated when the gateway transparently substituted a different
63
+ * model for the one the caller asked for — typically because the
64
+ * requested model errored and the gateway routed to a free fallback
65
+ * to fulfil the request. When `used` is true:
66
+ * - `model` is the model that actually answered (vs `ChatResponse.model`
67
+ * which historically reflected the requested model id).
68
+ * - `settlementSkipped` is `true` when the gateway also skipped the
69
+ * on-chain settle — i.e. the user was not charged for this call
70
+ * because a free fallback served it.
71
+ * Surfaced from the gateway's `X-Fallback-Used / X-Fallback-Model /
72
+ * X-Settlement-Skipped` response headers. Absent when the headers
73
+ * aren't present (most calls).
74
+ */
75
+ fallback?: {
76
+ used: true;
77
+ model?: string;
78
+ settlementSkipped?: boolean;
79
+ };
61
80
  }
62
81
  interface Model {
63
82
  id: string;
@@ -272,7 +291,7 @@ interface ChatCompletionOptions {
272
291
  */
273
292
  fallbackModels?: string[];
274
293
  }
275
- type RoutingProfile = "free" | "eco" | "auto" | "premium";
294
+ type RoutingProfile = "eco" | "auto" | "premium";
276
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
277
296
  interface RoutingDecision {
278
297
  model: string;
@@ -283,6 +302,10 @@ interface RoutingDecision {
283
302
  costEstimate: number;
284
303
  baselineCost: number;
285
304
  savings: number;
305
+ /** Routing profile applied by clawrouter (may include "agentic" on gateway responses). */
306
+ profile?: RoutingProfile | "agentic";
307
+ /** Score used when agentic routing is active. */
308
+ agenticScore?: number;
286
309
  /**
287
310
  * Remaining tier models with known pricing, in fallback order. `chat()`
288
311
  * walks this list when the primary model hits a transient error
@@ -291,7 +314,7 @@ interface RoutingDecision {
291
314
  fallbacks?: string[];
292
315
  }
293
316
  interface SmartChatOptions extends ChatOptions {
294
- /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
317
+ /** Routing profile: eco (budget), auto (balanced), premium (best quality) */
295
318
  routingProfile?: RoutingProfile;
296
319
  /** Maximum output tokens (used for cost estimation) */
297
320
  maxOutputTokens?: number;
@@ -762,9 +785,6 @@ declare class LLMClient {
762
785
  *
763
786
  * @example With routing profile
764
787
  * ```ts
765
- * // Free tier only (zero cost)
766
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
767
- *
768
788
  * // Eco mode (budget optimized)
769
789
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
770
790
  *
@@ -811,6 +831,17 @@ declare class LLMClient {
811
831
  * cost_log.jsonl is a single source of truth regardless of caller.
812
832
  */
813
833
  private recordCost;
834
+ /**
835
+ * Parse the chat response JSON and attach `fallback` metadata when the
836
+ * gateway signalled a transparent free-fallback substitution. The
837
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
838
+ * on the response when it served a paid request from a free model
839
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
840
+ * to the caller, the user gets a different model than requested with
841
+ * no visibility — silent quality drop and no clue why the on-chain
842
+ * balance didn't change.
843
+ */
844
+ private parseChatResponse;
814
845
  /**
815
846
  * Make a request with automatic x402 payment handling.
816
847
  */
@@ -1323,6 +1354,68 @@ declare class MusicClient {
1323
1354
  getSpending(): Spending;
1324
1355
  }
1325
1356
 
1357
+ /**
1358
+ * BlockRun Video Client - Generate short AI videos via x402 micropayments.
1359
+ *
1360
+ * SECURITY NOTE - Private Key Handling:
1361
+ * Your private key NEVER leaves your machine. Here's what happens:
1362
+ * 1. Key stays local - only used to sign an EIP-712 typed data message
1363
+ * 2. Only the SIGNATURE is sent in the PAYMENT-SIGNATURE header
1364
+ * 3. BlockRun verifies the signature on-chain via Coinbase CDP facilitator
1365
+ *
1366
+ * Async flow (client-polled):
1367
+ * POST /v1/videos/generations -> 402 -> sign -> 202 { id, poll_url }
1368
+ * GET /v1/videos/generations/{id} -> loop until status=completed
1369
+ *
1370
+ * The client signs ONCE and replays the same PAYMENT-SIGNATURE on every poll.
1371
+ * Settlement happens only on the first completed poll, so upstream failure or
1372
+ * the caller giving up = zero charge.
1373
+ *
1374
+ * Usage:
1375
+ * import { VideoClient } from '@blockrun/llm';
1376
+ *
1377
+ * const client = new VideoClient({ privateKey: '0x...' });
1378
+ * const result = await client.generate('a red apple slowly spinning on a wooden table');
1379
+ * console.log(result.data[0].url); // permanent MP4 URL
1380
+ * console.log(result.data[0].duration_seconds);
1381
+ */
1382
+
1383
+ /**
1384
+ * BlockRun Video Generation Client.
1385
+ *
1386
+ * Supports xAI Grok Imagine Video and ByteDance Seedance (1.5 Pro /
1387
+ * 2.0 Fast / 2.0 Pro) with automatic x402 micropayments on Base.
1388
+ */
1389
+ declare class VideoClient {
1390
+ private account;
1391
+ private privateKey;
1392
+ private apiUrl;
1393
+ private timeout;
1394
+ private sessionTotalUsd;
1395
+ private sessionCalls;
1396
+ constructor(options?: VideoClientOptions);
1397
+ /**
1398
+ * Generate a short video clip from a text prompt (or text + image).
1399
+ *
1400
+ * Submits an async job, then polls until the video is ready. Typical total
1401
+ * wall-time is 60-180s. If upstream runs past the budget (default 5min),
1402
+ * throws without charging.
1403
+ *
1404
+ * @param prompt - Text description of the video
1405
+ * @param options - Optional generation parameters
1406
+ */
1407
+ generate(prompt: string, options?: VideoGenerateOptions & {
1408
+ budgetMs?: number;
1409
+ }): Promise<VideoResponse>;
1410
+ private submitAndPoll;
1411
+ private absolute;
1412
+ private extractPaymentRequired;
1413
+ private throwApiError;
1414
+ private fetchWithTimeout;
1415
+ getWalletAddress(): string;
1416
+ getSpending(): Spending;
1417
+ }
1418
+
1326
1419
  /**
1327
1420
  * BlockRun Search Client - Standalone Grok Live Search via x402 micropayments.
1328
1421
  *
@@ -2100,4 +2193,4 @@ declare function validateTemperature(temperature?: number): void;
2100
2193
  */
2101
2194
  declare function validateTopP(topP?: number): void;
2102
2195
 
2103
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
2196
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type ChatChoice, type ChatCompletionOptions, type ChatMessage, type ChatOptions, type ChatResponse, type ChatResponseWithCost, type ChatUsage, type CostEntry, type CostEstimate, type CreatePaymentOptions, type FunctionCall, type FunctionDefinition, type HistoryOptions, ImageClient, type ImageClientOptions, type ImageData, type ImageEditOptions, type ImageGenerateOptions, type ImageModel, type ImageResponse, KNOWN_PROVIDERS, LLMClient, type LLMClientOptions, type ListOptions, type MarketSession, type Model, MusicClient, type MusicClientOptions, type MusicGenerateOptions, type MusicResponse, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PaymentError, type PaymentLinks, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, type RoutingDecision, type RoutingProfile, type RoutingTier, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type Spending, type SpendingReport, type StockMarket, type SymbolListResponse, type Tool, type ToolCall, type ToolChoice, USDC_BASE, USDC_BASE_CONTRACT, USDC_SOLANA, VideoClient, type VideoClientOptions, type VideoClip, type VideoGenerateOptions, type VideoModel, type VideoResponse, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XArticlesRisingResponse, type XAuthorAnalyticsResponse, XClient, type XClientOptions, type XCompareAuthorsResponse, type XFollower, type XFollowersResponse, type XFollowingsResponse, type XMentionsOptions, type XMentionsResponse, type XSearchOptions, type XSearchResponse, type XSearchSource, type XTrendingResponse, type XTweet, type XTweetLookupResponse, type XTweetRepliesOptions, type XTweetRepliesResponse, type XTweetThreadResponse, type XTweetsResponse, type XUser, type XUserInfoResponse, type XUserLookupResponse, type XUserTweetsOptions, type XVerifiedFollowersResponse, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatWalletCreatedMessage, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, loadSolanaWallet, loadWallet, logCost, parsePaymentRequired, saveSolanaWallet, saveToCache, saveWallet, scanSolanaWallets, scanWallets, setCache, setupAgentSolanaWallet, setupAgentWallet, solanaClient, solanaKeyToBytes, solanaPublicKey, status, validateMaxTokens, validateModel, validateTemperature, validateTopP };
package/dist/index.js CHANGED
@@ -524,9 +524,6 @@ var LLMClient = class _LLMClient {
524
524
  *
525
525
  * @example With routing profile
526
526
  * ```ts
527
- * // Free tier only (zero cost)
528
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
529
- *
530
527
  * // Eco mode (budget optimized)
531
528
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
532
529
  *
@@ -686,6 +683,28 @@ var LLMClient = class _LLMClient {
686
683
  } catch {
687
684
  }
688
685
  }
686
+ /**
687
+ * Parse the chat response JSON and attach `fallback` metadata when the
688
+ * gateway signalled a transparent free-fallback substitution. The
689
+ * gateway sets X-Fallback-Used / X-Fallback-Model / X-Settlement-Skipped
690
+ * on the response when it served a paid request from a free model
691
+ * (route.ts createPaymentResponseHeader path). Without surfacing these
692
+ * to the caller, the user gets a different model than requested with
693
+ * no visibility — silent quality drop and no clue why the on-chain
694
+ * balance didn't change.
695
+ */
696
+ async parseChatResponse(response) {
697
+ const body = await response.json();
698
+ const used = response.headers.get("X-Fallback-Used") === "true";
699
+ if (used) {
700
+ body.fallback = {
701
+ used: true,
702
+ model: response.headers.get("X-Fallback-Model") || void 0,
703
+ settlementSkipped: response.headers.get("X-Settlement-Skipped") === "free-fallback"
704
+ };
705
+ }
706
+ return body;
707
+ }
689
708
  /**
690
709
  * Make a request with automatic x402 payment handling.
691
710
  */
@@ -714,7 +733,7 @@ var LLMClient = class _LLMClient {
714
733
  }
715
734
  throw new APIError(`API error: ${retryResp.status}`, retryResp.status, sanitizeErrorResponse(errorBody));
716
735
  }
717
- return retryResp.json();
736
+ return this.parseChatResponse(retryResp);
718
737
  }
719
738
  }
720
739
  if (response.status === 402) {
@@ -733,7 +752,7 @@ var LLMClient = class _LLMClient {
733
752
  sanitizeErrorResponse(errorBody)
734
753
  );
735
754
  }
736
- return response.json();
755
+ return this.parseChatResponse(response);
737
756
  }
738
757
  /**
739
758
  * Handle 402 response: parse requirements, sign payment, retry.
@@ -808,7 +827,7 @@ var LLMClient = class _LLMClient {
808
827
  this.sessionCalls += 1;
809
828
  this.sessionTotalUsd += costUsd2;
810
829
  this.recordCost(url, costUsd2, { body, network: details.network });
811
- return retryResp2.json();
830
+ return this.parseChatResponse(retryResp2);
812
831
  }
813
832
  }
814
833
  if (retryResponse.status === 402) {
@@ -831,7 +850,7 @@ var LLMClient = class _LLMClient {
831
850
  this.sessionCalls += 1;
832
851
  this.sessionTotalUsd += costUsd;
833
852
  this.recordCost(url, costUsd, { body, network: details.network });
834
- return retryResponse.json();
853
+ return this.parseChatResponse(retryResponse);
835
854
  }
836
855
  /**
837
856
  * Sign a payment header and return the PAYMENT-SIGNATURE value.
@@ -2249,15 +2268,21 @@ var MusicClient = class {
2249
2268
  }
2250
2269
  };
2251
2270
 
2252
- // src/search.ts
2271
+ // src/video.ts
2253
2272
  import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
2254
2273
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2255
- var DEFAULT_TIMEOUT4 = 6e4;
2256
- var SearchClient = class {
2274
+ var DEFAULT_MODEL3 = "xai/grok-imagine-video";
2275
+ var DEFAULT_TIMEOUT4 = 12e4;
2276
+ var POLL_INTERVAL_MS2 = 5e3;
2277
+ var DEFAULT_GENERATE_BUDGET_MS = 3e5;
2278
+ var MAX_TIMEOUT_SECONDS = 600;
2279
+ var VideoClient = class {
2257
2280
  account;
2258
2281
  privateKey;
2259
2282
  apiUrl;
2260
2283
  timeout;
2284
+ sessionTotalUsd = 0;
2285
+ sessionCalls = 0;
2261
2286
  constructor(options = {}) {
2262
2287
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2263
2288
  const privateKey = options.privateKey || envKey;
@@ -2274,6 +2299,193 @@ var SearchClient = class {
2274
2299
  this.apiUrl = apiUrl.replace(/\/$/, "");
2275
2300
  this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2276
2301
  }
2302
+ /**
2303
+ * Generate a short video clip from a text prompt (or text + image).
2304
+ *
2305
+ * Submits an async job, then polls until the video is ready. Typical total
2306
+ * wall-time is 60-180s. If upstream runs past the budget (default 5min),
2307
+ * throws without charging.
2308
+ *
2309
+ * @param prompt - Text description of the video
2310
+ * @param options - Optional generation parameters
2311
+ */
2312
+ async generate(prompt, options) {
2313
+ const body = {
2314
+ model: options?.model || DEFAULT_MODEL3,
2315
+ prompt
2316
+ };
2317
+ if (options?.imageUrl) body.image_url = options.imageUrl;
2318
+ if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2319
+ const budgetMs = options?.budgetMs ?? DEFAULT_GENERATE_BUDGET_MS;
2320
+ return this.submitAndPoll(body, budgetMs);
2321
+ }
2322
+ // --------------------------------------------------------------------
2323
+ // Internal: async submit + poll
2324
+ // --------------------------------------------------------------------
2325
+ async submitAndPoll(body, budgetMs) {
2326
+ const submitUrl = `${this.apiUrl}/v1/videos/generations`;
2327
+ const resp402 = await this.fetchWithTimeout(submitUrl, {
2328
+ method: "POST",
2329
+ headers: { "Content-Type": "application/json" },
2330
+ body: JSON.stringify(body)
2331
+ });
2332
+ if (resp402.status !== 402) {
2333
+ await this.throwApiError(resp402, "Expected 402 on first POST");
2334
+ }
2335
+ const paymentRequired = await this.extractPaymentRequired(resp402);
2336
+ const details = extractPaymentDetails(paymentRequired);
2337
+ const paymentPayload = await createPaymentPayload(
2338
+ this.privateKey,
2339
+ this.account.address,
2340
+ details.recipient,
2341
+ details.amount,
2342
+ details.network || "eip155:8453",
2343
+ {
2344
+ resourceUrl: details.resource?.url || submitUrl,
2345
+ resourceDescription: details.resource?.description || "BlockRun Video Generation",
2346
+ // Ensure signed auth covers the entire polling window.
2347
+ maxTimeoutSeconds: Math.max(details.maxTimeoutSeconds || 0, MAX_TIMEOUT_SECONDS),
2348
+ extra: details.extra
2349
+ }
2350
+ );
2351
+ const submitResp = await this.fetchWithTimeout(submitUrl, {
2352
+ method: "POST",
2353
+ headers: {
2354
+ "Content-Type": "application/json",
2355
+ "PAYMENT-SIGNATURE": paymentPayload
2356
+ },
2357
+ body: JSON.stringify(body)
2358
+ });
2359
+ if (submitResp.status === 402) {
2360
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2361
+ }
2362
+ if (submitResp.status !== 200 && submitResp.status !== 202) {
2363
+ await this.throwApiError(submitResp, "Submit failed");
2364
+ }
2365
+ const submitData = await submitResp.json();
2366
+ if (!submitData.id || !submitData.poll_url) {
2367
+ throw new APIError(
2368
+ "Submit response missing id/poll_url",
2369
+ submitResp.status,
2370
+ { response: submitData }
2371
+ );
2372
+ }
2373
+ const pollUrl = this.absolute(submitData.poll_url);
2374
+ const deadline = Date.now() + budgetMs;
2375
+ let lastStatus = submitData.status || "queued";
2376
+ while (Date.now() < deadline) {
2377
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS2));
2378
+ const pollResp = await this.fetchWithTimeout(pollUrl, {
2379
+ method: "GET",
2380
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
2381
+ });
2382
+ let pollData = {};
2383
+ try {
2384
+ pollData = await pollResp.json();
2385
+ } catch {
2386
+ }
2387
+ lastStatus = pollData.status || lastStatus;
2388
+ if (pollResp.status === 202 && (lastStatus === "queued" || lastStatus === "in_progress")) {
2389
+ continue;
2390
+ }
2391
+ if (lastStatus === "failed") {
2392
+ throw new APIError(
2393
+ `Upstream generation failed: ${pollData.error || "unknown"}`,
2394
+ pollResp.status,
2395
+ sanitizeErrorResponse(pollData)
2396
+ );
2397
+ }
2398
+ if (pollResp.status === 200 && lastStatus === "completed") {
2399
+ const data = pollData;
2400
+ const billedSeconds = typeof body.duration_seconds === "number" ? body.duration_seconds : 8;
2401
+ this.sessionCalls++;
2402
+ this.sessionTotalUsd += 0.05 * billedSeconds * 1.05;
2403
+ const txHash = pollResp.headers.get("x-payment-receipt") || pollResp.headers.get("X-Payment-Receipt");
2404
+ if (txHash) data.txHash = txHash;
2405
+ return data;
2406
+ }
2407
+ if (pollResp.status !== 200 && pollResp.status !== 202 && pollResp.status !== 504) {
2408
+ await this.throwApiError(pollResp, "Poll failed");
2409
+ }
2410
+ }
2411
+ throw new APIError(
2412
+ `Video generation did not complete within ${Math.round(budgetMs / 1e3)}s (last status: ${lastStatus}). No payment was taken.`,
2413
+ 504,
2414
+ { id: submitData.id, last_status: lastStatus }
2415
+ );
2416
+ }
2417
+ absolute(url) {
2418
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
2419
+ const base = this.apiUrl.endsWith("/api") ? this.apiUrl.slice(0, -"/api".length) : this.apiUrl;
2420
+ return `${base}${url}`;
2421
+ }
2422
+ async extractPaymentRequired(resp) {
2423
+ const header = resp.headers.get("payment-required");
2424
+ if (header) return parsePaymentRequired(header);
2425
+ try {
2426
+ const body = await resp.json();
2427
+ if (body && (body.x402Version !== void 0 || body.accepts !== void 0)) {
2428
+ return body;
2429
+ }
2430
+ } catch {
2431
+ }
2432
+ throw new PaymentError("402 response but no payment requirements found");
2433
+ }
2434
+ async throwApiError(resp, prefix) {
2435
+ let errorBody;
2436
+ try {
2437
+ errorBody = await resp.json();
2438
+ } catch {
2439
+ errorBody = { error: "Request failed" };
2440
+ }
2441
+ throw new APIError(
2442
+ `${prefix}: HTTP ${resp.status}`,
2443
+ resp.status,
2444
+ sanitizeErrorResponse(errorBody)
2445
+ );
2446
+ }
2447
+ async fetchWithTimeout(url, options) {
2448
+ const controller = new AbortController();
2449
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2450
+ try {
2451
+ return await fetch(url, { ...options, signal: controller.signal });
2452
+ } finally {
2453
+ clearTimeout(timeoutId);
2454
+ }
2455
+ }
2456
+ getWalletAddress() {
2457
+ return this.account.address;
2458
+ }
2459
+ getSpending() {
2460
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2461
+ }
2462
+ };
2463
+
2464
+ // src/search.ts
2465
+ import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
2466
+ var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2467
+ var DEFAULT_TIMEOUT5 = 6e4;
2468
+ var SearchClient = class {
2469
+ account;
2470
+ privateKey;
2471
+ apiUrl;
2472
+ timeout;
2473
+ constructor(options = {}) {
2474
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2475
+ const privateKey = options.privateKey || envKey;
2476
+ if (!privateKey) {
2477
+ throw new Error(
2478
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
2479
+ );
2480
+ }
2481
+ validatePrivateKey(privateKey);
2482
+ this.privateKey = privateKey;
2483
+ this.account = privateKeyToAccount5(privateKey);
2484
+ const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2485
+ validateApiUrl(apiUrl);
2486
+ this.apiUrl = apiUrl.replace(/\/$/, "");
2487
+ this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2488
+ }
2277
2489
  async search(query, options) {
2278
2490
  if (!query || query.length > 1e3) {
2279
2491
  throw new Error("query must be 1-1000 characters");
@@ -2386,9 +2598,9 @@ var SearchClient = class {
2386
2598
  };
2387
2599
 
2388
2600
  // src/x-client.ts
2389
- import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
2390
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2391
- var DEFAULT_TIMEOUT5 = 6e4;
2601
+ import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
2602
+ var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2603
+ var DEFAULT_TIMEOUT6 = 6e4;
2392
2604
  var XClient = class _XClient {
2393
2605
  account;
2394
2606
  privateKey;
@@ -2411,11 +2623,11 @@ var XClient = class _XClient {
2411
2623
  }
2412
2624
  validatePrivateKey(privateKey);
2413
2625
  this.privateKey = privateKey;
2414
- this.account = privateKeyToAccount5(privateKey);
2415
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2626
+ this.account = privateKeyToAccount6(privateKey);
2627
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2416
2628
  validateApiUrl(apiUrl);
2417
2629
  this.apiUrl = apiUrl.replace(/\/$/, "");
2418
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2630
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2419
2631
  }
2420
2632
  // ───────── User endpoints ─────────
2421
2633
  userLookup(usernames) {
@@ -2593,9 +2805,9 @@ var XClient = class _XClient {
2593
2805
  };
2594
2806
 
2595
2807
  // src/price.ts
2596
- import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
2597
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2598
- var DEFAULT_TIMEOUT6 = 3e4;
2808
+ import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
2809
+ var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2810
+ var DEFAULT_TIMEOUT7 = 3e4;
2599
2811
  var PriceClient = class {
2600
2812
  account = null;
2601
2813
  privateKey = null;
@@ -2613,12 +2825,12 @@ var PriceClient = class {
2613
2825
  if (privateKey) {
2614
2826
  validatePrivateKey(privateKey);
2615
2827
  this.privateKey = privateKey;
2616
- this.account = privateKeyToAccount6(privateKey);
2828
+ this.account = privateKeyToAccount7(privateKey);
2617
2829
  }
2618
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2830
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2619
2831
  validateApiUrl(apiUrl);
2620
2832
  this.apiUrl = apiUrl.replace(/\/$/, "");
2621
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2833
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2622
2834
  }
2623
2835
  async price(category, symbol, options) {
2624
2836
  if (!symbol) throw new Error("symbol is required");
@@ -2797,7 +3009,7 @@ function buildUrl(base, query) {
2797
3009
  }
2798
3010
 
2799
3011
  // src/wallet.ts
2800
- import { privateKeyToAccount as privateKeyToAccount7, generatePrivateKey } from "viem/accounts";
3012
+ import { privateKeyToAccount as privateKeyToAccount8, generatePrivateKey } from "viem/accounts";
2801
3013
  import * as fs2 from "fs";
2802
3014
  import * as path2 from "path";
2803
3015
  import * as os2 from "os";
@@ -2807,7 +3019,7 @@ var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
2807
3019
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
2808
3020
  function createWallet() {
2809
3021
  const privateKey = generatePrivateKey();
2810
- const account = privateKeyToAccount7(privateKey);
3022
+ const account = privateKeyToAccount8(privateKey);
2811
3023
  return {
2812
3024
  address: account.address,
2813
3025
  privateKey
@@ -2863,12 +3075,12 @@ function loadWallet() {
2863
3075
  function getOrCreateWallet() {
2864
3076
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2865
3077
  if (envKey) {
2866
- const account = privateKeyToAccount7(envKey);
3078
+ const account = privateKeyToAccount8(envKey);
2867
3079
  return { address: account.address, privateKey: envKey, isNew: false };
2868
3080
  }
2869
3081
  const fileKey = loadWallet();
2870
3082
  if (fileKey) {
2871
- const account = privateKeyToAccount7(fileKey);
3083
+ const account = privateKeyToAccount8(fileKey);
2872
3084
  return { address: account.address, privateKey: fileKey, isNew: false };
2873
3085
  }
2874
3086
  const { address, privateKey } = createWallet();
@@ -2878,11 +3090,11 @@ function getOrCreateWallet() {
2878
3090
  function getWalletAddress() {
2879
3091
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2880
3092
  if (envKey) {
2881
- return privateKeyToAccount7(envKey).address;
3093
+ return privateKeyToAccount8(envKey).address;
2882
3094
  }
2883
3095
  const fileKey = loadWallet();
2884
3096
  if (fileKey) {
2885
- return privateKeyToAccount7(fileKey).address;
3097
+ return privateKeyToAccount8(fileKey).address;
2886
3098
  }
2887
3099
  return null;
2888
3100
  }
@@ -3062,7 +3274,7 @@ async function getOrCreateSolanaWallet() {
3062
3274
  // src/solana-client.ts
3063
3275
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3064
3276
  var DEFAULT_MAX_TOKENS2 = 1024;
3065
- var DEFAULT_TIMEOUT7 = 6e4;
3277
+ var DEFAULT_TIMEOUT8 = 6e4;
3066
3278
  var SDK_VERSION2 = "0.3.0";
3067
3279
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3068
3280
  var SolanaLLMClient = class {
@@ -3087,7 +3299,7 @@ var SolanaLLMClient = class {
3087
3299
  validateApiUrl(apiUrl);
3088
3300
  this.apiUrl = apiUrl.replace(/\/$/, "");
3089
3301
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3090
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3302
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3091
3303
  }
3092
3304
  /** Get Solana wallet address (public key in base58). */
3093
3305
  async getWalletAddress() {
@@ -3912,6 +4124,9 @@ var ChatCompletions = class {
3912
4124
  this.apiUrl = apiUrl;
3913
4125
  this.timeout = timeout;
3914
4126
  }
4127
+ client;
4128
+ apiUrl;
4129
+ timeout;
3915
4130
  async create(params) {
3916
4131
  if (params.stream) {
3917
4132
  return this.createStream(params);
@@ -3991,7 +4206,7 @@ var OpenAI = class {
3991
4206
  };
3992
4207
 
3993
4208
  // src/anthropic-compat.ts
3994
- import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
4209
+ import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
3995
4210
  var AnthropicClient = class {
3996
4211
  _client = null;
3997
4212
  _clientPromise = null;
@@ -4004,7 +4219,7 @@ var AnthropicClient = class {
4004
4219
  const key = options.privateKey ?? wallet.privateKey;
4005
4220
  validatePrivateKey(key);
4006
4221
  this._privateKey = key;
4007
- this._account = privateKeyToAccount8(this._privateKey);
4222
+ this._account = privateKeyToAccount9(this._privateKey);
4008
4223
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4009
4224
  validateApiUrl(apiUrl);
4010
4225
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4117,6 +4332,7 @@ export {
4117
4332
  USDC_BASE,
4118
4333
  USDC_BASE_CONTRACT,
4119
4334
  USDC_SOLANA,
4335
+ VideoClient,
4120
4336
  WALLET_DIR_PATH,
4121
4337
  WALLET_FILE_PATH,
4122
4338
  XClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "type": "module",
5
5
  "description": "BlockRun SDK - Pay-per-request AI (LLM, Image, Video, Music) via x402 on Base and Solana",
6
6
  "main": "dist/index.cjs",
@@ -48,23 +48,23 @@
48
48
  "url": "https://github.com/BlockRunAI/blockrun-llm-ts/issues"
49
49
  },
50
50
  "dependencies": {
51
- "@blockrun/clawrouter": "^0.12.71",
51
+ "@blockrun/clawrouter": "^0.12.190",
52
52
  "bs58": "^6.0.0",
53
- "viem": "^2.21.0"
53
+ "viem": "^2.49.0"
54
54
  },
55
55
  "optionalDependencies": {
56
56
  "@anthropic-ai/sdk": "^0.39.0",
57
- "@solana/spl-token": "^0.4.0",
58
- "@solana/web3.js": "^1.98.0"
57
+ "@solana/spl-token": "^0.4.14",
58
+ "@solana/web3.js": "^1.98.4"
59
59
  },
60
60
  "devDependencies": {
61
- "@eslint/js": "^9.0.0",
62
- "@types/node": "^20.0.0",
63
- "eslint": "^9.0.0",
64
- "tsup": "^8.0.0",
65
- "typescript": "^5.0.0",
66
- "typescript-eslint": "^8.0.0",
67
- "vitest": "^1.0.0"
61
+ "@eslint/js": "^9.39.4",
62
+ "@types/node": "^20.19.41",
63
+ "eslint": "^9.39.4",
64
+ "tsup": "^8.5.1",
65
+ "typescript": "^5.9.3",
66
+ "typescript-eslint": "^8.59.3",
67
+ "vitest": "^1.6.1"
68
68
  },
69
69
  "engines": {
70
70
  "node": ">=20"