@blockrun/llm 2.1.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
  *
@@ -2366,15 +2364,21 @@ var MusicClient = class {
2366
2364
  }
2367
2365
  };
2368
2366
 
2369
- // src/search.ts
2367
+ // src/video.ts
2370
2368
  var import_accounts5 = require("viem/accounts");
2371
2369
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2372
- var DEFAULT_TIMEOUT4 = 6e4;
2373
- 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 {
2374
2376
  account;
2375
2377
  privateKey;
2376
2378
  apiUrl;
2377
2379
  timeout;
2380
+ sessionTotalUsd = 0;
2381
+ sessionCalls = 0;
2378
2382
  constructor(options = {}) {
2379
2383
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2380
2384
  const privateKey = options.privateKey || envKey;
@@ -2391,6 +2395,193 @@ var SearchClient = class {
2391
2395
  this.apiUrl = apiUrl.replace(/\/$/, "");
2392
2396
  this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2393
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
+ }
2394
2585
  async search(query, options) {
2395
2586
  if (!query || query.length > 1e3) {
2396
2587
  throw new Error("query must be 1-1000 characters");
@@ -2503,9 +2694,9 @@ var SearchClient = class {
2503
2694
  };
2504
2695
 
2505
2696
  // src/x-client.ts
2506
- var import_accounts6 = require("viem/accounts");
2507
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2508
- 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;
2509
2700
  var XClient = class _XClient {
2510
2701
  account;
2511
2702
  privateKey;
@@ -2528,11 +2719,11 @@ var XClient = class _XClient {
2528
2719
  }
2529
2720
  validatePrivateKey(privateKey);
2530
2721
  this.privateKey = privateKey;
2531
- this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
2532
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2722
+ this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
2723
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2533
2724
  validateApiUrl(apiUrl);
2534
2725
  this.apiUrl = apiUrl.replace(/\/$/, "");
2535
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2726
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2536
2727
  }
2537
2728
  // ───────── User endpoints ─────────
2538
2729
  userLookup(usernames) {
@@ -2710,9 +2901,9 @@ var XClient = class _XClient {
2710
2901
  };
2711
2902
 
2712
2903
  // src/price.ts
2713
- var import_accounts7 = require("viem/accounts");
2714
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2715
- 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;
2716
2907
  var PriceClient = class {
2717
2908
  account = null;
2718
2909
  privateKey = null;
@@ -2730,12 +2921,12 @@ var PriceClient = class {
2730
2921
  if (privateKey) {
2731
2922
  validatePrivateKey(privateKey);
2732
2923
  this.privateKey = privateKey;
2733
- this.account = (0, import_accounts7.privateKeyToAccount)(privateKey);
2924
+ this.account = (0, import_accounts8.privateKeyToAccount)(privateKey);
2734
2925
  }
2735
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2926
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2736
2927
  validateApiUrl(apiUrl);
2737
2928
  this.apiUrl = apiUrl.replace(/\/$/, "");
2738
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2929
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2739
2930
  }
2740
2931
  async price(category, symbol, options) {
2741
2932
  if (!symbol) throw new Error("symbol is required");
@@ -2914,7 +3105,7 @@ function buildUrl(base, query) {
2914
3105
  }
2915
3106
 
2916
3107
  // src/wallet.ts
2917
- var import_accounts8 = require("viem/accounts");
3108
+ var import_accounts9 = require("viem/accounts");
2918
3109
  var fs2 = __toESM(require("fs"), 1);
2919
3110
  var path2 = __toESM(require("path"), 1);
2920
3111
  var os2 = __toESM(require("os"), 1);
@@ -2923,8 +3114,8 @@ var BASE_CHAIN_ID2 = "8453";
2923
3114
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
2924
3115
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
2925
3116
  function createWallet() {
2926
- const privateKey = (0, import_accounts8.generatePrivateKey)();
2927
- const account = (0, import_accounts8.privateKeyToAccount)(privateKey);
3117
+ const privateKey = (0, import_accounts9.generatePrivateKey)();
3118
+ const account = (0, import_accounts9.privateKeyToAccount)(privateKey);
2928
3119
  return {
2929
3120
  address: account.address,
2930
3121
  privateKey
@@ -2980,12 +3171,12 @@ function loadWallet() {
2980
3171
  function getOrCreateWallet() {
2981
3172
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2982
3173
  if (envKey) {
2983
- const account = (0, import_accounts8.privateKeyToAccount)(envKey);
3174
+ const account = (0, import_accounts9.privateKeyToAccount)(envKey);
2984
3175
  return { address: account.address, privateKey: envKey, isNew: false };
2985
3176
  }
2986
3177
  const fileKey = loadWallet();
2987
3178
  if (fileKey) {
2988
- const account = (0, import_accounts8.privateKeyToAccount)(fileKey);
3179
+ const account = (0, import_accounts9.privateKeyToAccount)(fileKey);
2989
3180
  return { address: account.address, privateKey: fileKey, isNew: false };
2990
3181
  }
2991
3182
  const { address, privateKey } = createWallet();
@@ -2995,11 +3186,11 @@ function getOrCreateWallet() {
2995
3186
  function getWalletAddress() {
2996
3187
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2997
3188
  if (envKey) {
2998
- return (0, import_accounts8.privateKeyToAccount)(envKey).address;
3189
+ return (0, import_accounts9.privateKeyToAccount)(envKey).address;
2999
3190
  }
3000
3191
  const fileKey = loadWallet();
3001
3192
  if (fileKey) {
3002
- return (0, import_accounts8.privateKeyToAccount)(fileKey).address;
3193
+ return (0, import_accounts9.privateKeyToAccount)(fileKey).address;
3003
3194
  }
3004
3195
  return null;
3005
3196
  }
@@ -3179,7 +3370,7 @@ async function getOrCreateSolanaWallet() {
3179
3370
  // src/solana-client.ts
3180
3371
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3181
3372
  var DEFAULT_MAX_TOKENS2 = 1024;
3182
- var DEFAULT_TIMEOUT7 = 6e4;
3373
+ var DEFAULT_TIMEOUT8 = 6e4;
3183
3374
  var SDK_VERSION2 = "0.3.0";
3184
3375
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3185
3376
  var SolanaLLMClient = class {
@@ -3204,7 +3395,7 @@ var SolanaLLMClient = class {
3204
3395
  validateApiUrl(apiUrl);
3205
3396
  this.apiUrl = apiUrl.replace(/\/$/, "");
3206
3397
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3207
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3398
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3208
3399
  }
3209
3400
  /** Get Solana wallet address (public key in base58). */
3210
3401
  async getWalletAddress() {
@@ -4029,6 +4220,9 @@ var ChatCompletions = class {
4029
4220
  this.apiUrl = apiUrl;
4030
4221
  this.timeout = timeout;
4031
4222
  }
4223
+ client;
4224
+ apiUrl;
4225
+ timeout;
4032
4226
  async create(params) {
4033
4227
  if (params.stream) {
4034
4228
  return this.createStream(params);
@@ -4108,7 +4302,7 @@ var OpenAI = class {
4108
4302
  };
4109
4303
 
4110
4304
  // src/anthropic-compat.ts
4111
- var import_accounts9 = require("viem/accounts");
4305
+ var import_accounts10 = require("viem/accounts");
4112
4306
  var AnthropicClient = class {
4113
4307
  _client = null;
4114
4308
  _clientPromise = null;
@@ -4121,7 +4315,7 @@ var AnthropicClient = class {
4121
4315
  const key = options.privateKey ?? wallet.privateKey;
4122
4316
  validatePrivateKey(key);
4123
4317
  this._privateKey = key;
4124
- this._account = (0, import_accounts9.privateKeyToAccount)(this._privateKey);
4318
+ this._account = (0, import_accounts10.privateKeyToAccount)(this._privateKey);
4125
4319
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4126
4320
  validateApiUrl(apiUrl);
4127
4321
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4235,6 +4429,7 @@ var AnthropicClient = class {
4235
4429
  USDC_BASE,
4236
4430
  USDC_BASE_CONTRACT,
4237
4431
  USDC_SOLANA,
4432
+ VideoClient,
4238
4433
  WALLET_DIR_PATH,
4239
4434
  WALLET_FILE_PATH,
4240
4435
  XClient,
package/dist/index.d.cts CHANGED
@@ -291,7 +291,7 @@ interface ChatCompletionOptions {
291
291
  */
292
292
  fallbackModels?: string[];
293
293
  }
294
- type RoutingProfile = "free" | "eco" | "auto" | "premium";
294
+ type RoutingProfile = "eco" | "auto" | "premium";
295
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
296
296
  interface RoutingDecision {
297
297
  model: string;
@@ -302,6 +302,10 @@ interface RoutingDecision {
302
302
  costEstimate: number;
303
303
  baselineCost: number;
304
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;
305
309
  /**
306
310
  * Remaining tier models with known pricing, in fallback order. `chat()`
307
311
  * walks this list when the primary model hits a transient error
@@ -310,7 +314,7 @@ interface RoutingDecision {
310
314
  fallbacks?: string[];
311
315
  }
312
316
  interface SmartChatOptions extends ChatOptions {
313
- /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
317
+ /** Routing profile: eco (budget), auto (balanced), premium (best quality) */
314
318
  routingProfile?: RoutingProfile;
315
319
  /** Maximum output tokens (used for cost estimation) */
316
320
  maxOutputTokens?: number;
@@ -781,9 +785,6 @@ declare class LLMClient {
781
785
  *
782
786
  * @example With routing profile
783
787
  * ```ts
784
- * // Free tier only (zero cost)
785
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
786
- *
787
788
  * // Eco mode (budget optimized)
788
789
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
789
790
  *
@@ -1353,6 +1354,68 @@ declare class MusicClient {
1353
1354
  getSpending(): Spending;
1354
1355
  }
1355
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
+
1356
1419
  /**
1357
1420
  * BlockRun Search Client - Standalone Grok Live Search via x402 micropayments.
1358
1421
  *
@@ -2130,4 +2193,4 @@ declare function validateTemperature(temperature?: number): void;
2130
2193
  */
2131
2194
  declare function validateTopP(topP?: number): void;
2132
2195
 
2133
- 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
@@ -291,7 +291,7 @@ interface ChatCompletionOptions {
291
291
  */
292
292
  fallbackModels?: string[];
293
293
  }
294
- type RoutingProfile = "free" | "eco" | "auto" | "premium";
294
+ type RoutingProfile = "eco" | "auto" | "premium";
295
295
  type RoutingTier = "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING";
296
296
  interface RoutingDecision {
297
297
  model: string;
@@ -302,6 +302,10 @@ interface RoutingDecision {
302
302
  costEstimate: number;
303
303
  baselineCost: number;
304
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;
305
309
  /**
306
310
  * Remaining tier models with known pricing, in fallback order. `chat()`
307
311
  * walks this list when the primary model hits a transient error
@@ -310,7 +314,7 @@ interface RoutingDecision {
310
314
  fallbacks?: string[];
311
315
  }
312
316
  interface SmartChatOptions extends ChatOptions {
313
- /** Routing profile: free (zero cost), eco (budget), auto (balanced), premium (best quality) */
317
+ /** Routing profile: eco (budget), auto (balanced), premium (best quality) */
314
318
  routingProfile?: RoutingProfile;
315
319
  /** Maximum output tokens (used for cost estimation) */
316
320
  maxOutputTokens?: number;
@@ -781,9 +785,6 @@ declare class LLMClient {
781
785
  *
782
786
  * @example With routing profile
783
787
  * ```ts
784
- * // Free tier only (zero cost)
785
- * const result = await client.smartChat('Hello!', { routingProfile: 'free' });
786
- *
787
788
  * // Eco mode (budget optimized)
788
789
  * const result = await client.smartChat('Explain quantum computing', { routingProfile: 'eco' });
789
790
  *
@@ -1353,6 +1354,68 @@ declare class MusicClient {
1353
1354
  getSpending(): Spending;
1354
1355
  }
1355
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
+
1356
1419
  /**
1357
1420
  * BlockRun Search Client - Standalone Grok Live Search via x402 micropayments.
1358
1421
  *
@@ -2130,4 +2193,4 @@ declare function validateTemperature(temperature?: number): void;
2130
2193
  */
2131
2194
  declare function validateTopP(topP?: number): void;
2132
2195
 
2133
- 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
  *
@@ -2271,15 +2268,21 @@ var MusicClient = class {
2271
2268
  }
2272
2269
  };
2273
2270
 
2274
- // src/search.ts
2271
+ // src/video.ts
2275
2272
  import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
2276
2273
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2277
- var DEFAULT_TIMEOUT4 = 6e4;
2278
- 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 {
2279
2280
  account;
2280
2281
  privateKey;
2281
2282
  apiUrl;
2282
2283
  timeout;
2284
+ sessionTotalUsd = 0;
2285
+ sessionCalls = 0;
2283
2286
  constructor(options = {}) {
2284
2287
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2285
2288
  const privateKey = options.privateKey || envKey;
@@ -2296,6 +2299,193 @@ var SearchClient = class {
2296
2299
  this.apiUrl = apiUrl.replace(/\/$/, "");
2297
2300
  this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2298
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
+ }
2299
2489
  async search(query, options) {
2300
2490
  if (!query || query.length > 1e3) {
2301
2491
  throw new Error("query must be 1-1000 characters");
@@ -2408,9 +2598,9 @@ var SearchClient = class {
2408
2598
  };
2409
2599
 
2410
2600
  // src/x-client.ts
2411
- import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
2412
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2413
- 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;
2414
2604
  var XClient = class _XClient {
2415
2605
  account;
2416
2606
  privateKey;
@@ -2433,11 +2623,11 @@ var XClient = class _XClient {
2433
2623
  }
2434
2624
  validatePrivateKey(privateKey);
2435
2625
  this.privateKey = privateKey;
2436
- this.account = privateKeyToAccount5(privateKey);
2437
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2626
+ this.account = privateKeyToAccount6(privateKey);
2627
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2438
2628
  validateApiUrl(apiUrl);
2439
2629
  this.apiUrl = apiUrl.replace(/\/$/, "");
2440
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2630
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2441
2631
  }
2442
2632
  // ───────── User endpoints ─────────
2443
2633
  userLookup(usernames) {
@@ -2615,9 +2805,9 @@ var XClient = class _XClient {
2615
2805
  };
2616
2806
 
2617
2807
  // src/price.ts
2618
- import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
2619
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2620
- 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;
2621
2811
  var PriceClient = class {
2622
2812
  account = null;
2623
2813
  privateKey = null;
@@ -2635,12 +2825,12 @@ var PriceClient = class {
2635
2825
  if (privateKey) {
2636
2826
  validatePrivateKey(privateKey);
2637
2827
  this.privateKey = privateKey;
2638
- this.account = privateKeyToAccount6(privateKey);
2828
+ this.account = privateKeyToAccount7(privateKey);
2639
2829
  }
2640
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2830
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2641
2831
  validateApiUrl(apiUrl);
2642
2832
  this.apiUrl = apiUrl.replace(/\/$/, "");
2643
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2833
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2644
2834
  }
2645
2835
  async price(category, symbol, options) {
2646
2836
  if (!symbol) throw new Error("symbol is required");
@@ -2819,7 +3009,7 @@ function buildUrl(base, query) {
2819
3009
  }
2820
3010
 
2821
3011
  // src/wallet.ts
2822
- import { privateKeyToAccount as privateKeyToAccount7, generatePrivateKey } from "viem/accounts";
3012
+ import { privateKeyToAccount as privateKeyToAccount8, generatePrivateKey } from "viem/accounts";
2823
3013
  import * as fs2 from "fs";
2824
3014
  import * as path2 from "path";
2825
3015
  import * as os2 from "os";
@@ -2829,7 +3019,7 @@ var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
2829
3019
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
2830
3020
  function createWallet() {
2831
3021
  const privateKey = generatePrivateKey();
2832
- const account = privateKeyToAccount7(privateKey);
3022
+ const account = privateKeyToAccount8(privateKey);
2833
3023
  return {
2834
3024
  address: account.address,
2835
3025
  privateKey
@@ -2885,12 +3075,12 @@ function loadWallet() {
2885
3075
  function getOrCreateWallet() {
2886
3076
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2887
3077
  if (envKey) {
2888
- const account = privateKeyToAccount7(envKey);
3078
+ const account = privateKeyToAccount8(envKey);
2889
3079
  return { address: account.address, privateKey: envKey, isNew: false };
2890
3080
  }
2891
3081
  const fileKey = loadWallet();
2892
3082
  if (fileKey) {
2893
- const account = privateKeyToAccount7(fileKey);
3083
+ const account = privateKeyToAccount8(fileKey);
2894
3084
  return { address: account.address, privateKey: fileKey, isNew: false };
2895
3085
  }
2896
3086
  const { address, privateKey } = createWallet();
@@ -2900,11 +3090,11 @@ function getOrCreateWallet() {
2900
3090
  function getWalletAddress() {
2901
3091
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2902
3092
  if (envKey) {
2903
- return privateKeyToAccount7(envKey).address;
3093
+ return privateKeyToAccount8(envKey).address;
2904
3094
  }
2905
3095
  const fileKey = loadWallet();
2906
3096
  if (fileKey) {
2907
- return privateKeyToAccount7(fileKey).address;
3097
+ return privateKeyToAccount8(fileKey).address;
2908
3098
  }
2909
3099
  return null;
2910
3100
  }
@@ -3084,7 +3274,7 @@ async function getOrCreateSolanaWallet() {
3084
3274
  // src/solana-client.ts
3085
3275
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3086
3276
  var DEFAULT_MAX_TOKENS2 = 1024;
3087
- var DEFAULT_TIMEOUT7 = 6e4;
3277
+ var DEFAULT_TIMEOUT8 = 6e4;
3088
3278
  var SDK_VERSION2 = "0.3.0";
3089
3279
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3090
3280
  var SolanaLLMClient = class {
@@ -3109,7 +3299,7 @@ var SolanaLLMClient = class {
3109
3299
  validateApiUrl(apiUrl);
3110
3300
  this.apiUrl = apiUrl.replace(/\/$/, "");
3111
3301
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3112
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3302
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3113
3303
  }
3114
3304
  /** Get Solana wallet address (public key in base58). */
3115
3305
  async getWalletAddress() {
@@ -3934,6 +4124,9 @@ var ChatCompletions = class {
3934
4124
  this.apiUrl = apiUrl;
3935
4125
  this.timeout = timeout;
3936
4126
  }
4127
+ client;
4128
+ apiUrl;
4129
+ timeout;
3937
4130
  async create(params) {
3938
4131
  if (params.stream) {
3939
4132
  return this.createStream(params);
@@ -4013,7 +4206,7 @@ var OpenAI = class {
4013
4206
  };
4014
4207
 
4015
4208
  // src/anthropic-compat.ts
4016
- import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
4209
+ import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
4017
4210
  var AnthropicClient = class {
4018
4211
  _client = null;
4019
4212
  _clientPromise = null;
@@ -4026,7 +4219,7 @@ var AnthropicClient = class {
4026
4219
  const key = options.privateKey ?? wallet.privateKey;
4027
4220
  validatePrivateKey(key);
4028
4221
  this._privateKey = key;
4029
- this._account = privateKeyToAccount8(this._privateKey);
4222
+ this._account = privateKeyToAccount9(this._privateKey);
4030
4223
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4031
4224
  validateApiUrl(apiUrl);
4032
4225
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4139,6 +4332,7 @@ export {
4139
4332
  USDC_BASE,
4140
4333
  USDC_BASE_CONTRACT,
4141
4334
  USDC_SOLANA,
4335
+ VideoClient,
4142
4336
  WALLET_DIR_PATH,
4143
4337
  WALLET_FILE_PATH,
4144
4338
  XClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "2.1.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"