@blockrun/llm 2.2.0 → 2.4.0

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/README.md CHANGED
@@ -381,6 +381,17 @@ console.log(result.data[0].duration_seconds); // 8
381
381
  const r2 = await client.generate('the subject turns and smiles', {
382
382
  imageUrl: 'https://example.com/portrait.jpg',
383
383
  });
384
+
385
+ // Token360 / Seedance options (silently ignored by xAI Grok video)
386
+ const r3 = await client.generate('aerial drone shot over a snowy mountain', {
387
+ model: 'bytedance/seedance-2.0-fast',
388
+ aspectRatio: '21:9',
389
+ resolution: '1080p',
390
+ generateAudio: true, // omit to use the model's default
391
+ seed: 42,
392
+ watermark: false,
393
+ returnLastFrame: true, // useful for clip chaining
394
+ });
384
395
  ```
385
396
 
386
397
  ### Voice Calls
@@ -444,6 +455,50 @@ const results = await x.search('x402 micropayments', { queryType: 'Latest' });
444
455
  const tweets = await x.userTweets({ username: 'vitalikbuterin', includeReplies: false });
445
456
  ```
446
457
 
458
+ ### Surf Crypto Data
459
+
460
+ `SurfClient` exposes the full `/v1/surf/*` catalog — 84+ pay-per-call
461
+ endpoints across CEX/DEX market data, on-chain SQL, wallet intelligence,
462
+ prediction markets (Polymarket + Kalshi), social analytics, news, VC fund
463
+ data, and an OpenAI-compatible chat surface. Flat pricing per call:
464
+
465
+ | Tier | Price/call | Examples |
466
+ |------|-----------|----------|
467
+ | 1 | $0.001 | `/market/price`, `/market/ranking`, `/news/feed`, prediction-market reads, social tweets |
468
+ | 2 | $0.005 | `/exchange/depth`, `/exchange/klines`, `/wallet/detail`, `/search/*`, `/social/ranking` |
469
+ | 3 | $0.020 | `/onchain/sql`, `/onchain/query`, `/onchain/schema`, `/chat/completions` |
470
+
471
+ Because the catalog is broad and evolving, the client deliberately ships a
472
+ generic `get` / `post` pair instead of 84 typed wrappers. Pass the path
473
+ (with or without the `/v1/surf` prefix), query params, or a JSON body —
474
+ type the response via a generic if you want.
475
+
476
+ ```ts
477
+ import { SurfClient } from '@blockrun/llm';
478
+
479
+ const surf = new SurfClient();
480
+
481
+ // Tier 1 — token price ($0.001)
482
+ const btc = await surf.get('/market/price', { symbol: 'BTC' });
483
+
484
+ // Tier 2 — order book depth ($0.005)
485
+ const book = await surf.get('/exchange/depth', {
486
+ exchange: 'binance',
487
+ symbol: 'BTC-USDT',
488
+ });
489
+
490
+ // Tier 3 — raw on-chain SQL against 80+ ClickHouse tables ($0.020)
491
+ const rows = await surf.post('/onchain/sql', {
492
+ query: 'SELECT block_number FROM ethereum.blocks ORDER BY block_number DESC LIMIT 5',
493
+ });
494
+
495
+ // Typed response via generic
496
+ type Price = { symbol: string; price: number; timestamp: string };
497
+ const eth = await surf.get<Price>('/market/price', { symbol: 'ETH' });
498
+ ```
499
+
500
+ Full endpoint inventory: <https://blockrun.ai/marketplace/surf>.
501
+
447
502
  Methods: `userLookup`, `userInfo`, `followers`, `following`, `followings`,
448
503
  `verifiedFollowers`, `userTweets`, `mentions`, `tweetLookup`, `tweetReplies`,
449
504
  `tweetThread`, `search`, `trending`, `articlesRising`.
package/dist/index.cjs CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  SOLANA_WALLET_FILE_PATH: () => SOLANA_WALLET_FILE,
46
46
  SearchClient: () => SearchClient,
47
47
  SolanaLLMClient: () => SolanaLLMClient,
48
+ SurfClient: () => SurfClient,
48
49
  USDC_BASE: () => USDC_BASE,
49
50
  USDC_BASE_CONTRACT: () => USDC_BASE_CONTRACT,
50
51
  USDC_SOLANA: () => USDC_SOLANA,
@@ -2413,6 +2414,12 @@ var VideoClient = class {
2413
2414
  };
2414
2415
  if (options?.imageUrl) body.image_url = options.imageUrl;
2415
2416
  if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2417
+ if (options?.aspectRatio) body.aspect_ratio = options.aspectRatio;
2418
+ if (options?.resolution) body.resolution = options.resolution;
2419
+ if (options?.generateAudio !== void 0) body.generate_audio = options.generateAudio;
2420
+ if (options?.seed !== void 0) body.seed = options.seed;
2421
+ if (options?.watermark !== void 0) body.watermark = options.watermark;
2422
+ if (options?.returnLastFrame) body.return_last_frame = true;
2416
2423
  const budgetMs = options?.budgetMs ?? DEFAULT_GENERATE_BUDGET_MS;
2417
2424
  return this.submitAndPoll(body, budgetMs);
2418
2425
  }
@@ -3306,8 +3313,163 @@ function buildUrl(base, query) {
3306
3313
  return `${base}?${qs}`;
3307
3314
  }
3308
3315
 
3309
- // src/wallet.ts
3316
+ // src/surf.ts
3310
3317
  var import_accounts10 = require("viem/accounts");
3318
+ var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3319
+ var DEFAULT_TIMEOUT9 = 6e4;
3320
+ var SURF_PREFIX = "/v1/surf";
3321
+ var SurfClient = class {
3322
+ account;
3323
+ privateKey;
3324
+ apiUrl;
3325
+ timeout;
3326
+ constructor(options = {}) {
3327
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3328
+ const privateKey = options.privateKey || envKey;
3329
+ if (!privateKey) {
3330
+ throw new Error(
3331
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
3332
+ );
3333
+ }
3334
+ validatePrivateKey(privateKey);
3335
+ this.privateKey = privateKey;
3336
+ this.account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3337
+ const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3338
+ validateApiUrl(apiUrl);
3339
+ this.apiUrl = apiUrl.replace(/\/$/, "");
3340
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3341
+ }
3342
+ /**
3343
+ * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
3344
+ * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
3345
+ * arrays become repeated keys (`?a=1&a=2`).
3346
+ */
3347
+ async get(path5, params) {
3348
+ const url = this.buildUrl(path5, params);
3349
+ return this.requestWithPayment(url, "GET");
3350
+ }
3351
+ /**
3352
+ * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
3353
+ */
3354
+ async post(path5, body) {
3355
+ const url = this.buildUrl(path5);
3356
+ return this.requestWithPayment(url, "POST", body);
3357
+ }
3358
+ buildUrl(path5, params) {
3359
+ let normalized = path5.startsWith("/") ? path5 : `/${path5}`;
3360
+ if (!normalized.startsWith(SURF_PREFIX)) {
3361
+ normalized = `${SURF_PREFIX}${normalized}`;
3362
+ }
3363
+ const base = `${this.apiUrl}${normalized}`;
3364
+ if (!params) return base;
3365
+ const qs = new URLSearchParams();
3366
+ for (const [key, value] of Object.entries(params)) {
3367
+ if (value === void 0 || value === null) continue;
3368
+ if (Array.isArray(value)) {
3369
+ for (const v of value) {
3370
+ if (v === void 0 || v === null) continue;
3371
+ qs.append(key, String(v));
3372
+ }
3373
+ } else {
3374
+ qs.append(key, String(value));
3375
+ }
3376
+ }
3377
+ const query = qs.toString();
3378
+ return query ? `${base}?${query}` : base;
3379
+ }
3380
+ async requestWithPayment(url, method, body) {
3381
+ const init = { method };
3382
+ if (method === "POST") {
3383
+ init.headers = { "Content-Type": "application/json" };
3384
+ init.body = JSON.stringify(body ?? {});
3385
+ }
3386
+ const response = await this.fetchWithTimeout(url, init);
3387
+ if (response.status === 402) {
3388
+ return this.handlePaymentAndRetry(url, method, body, response);
3389
+ }
3390
+ if (!response.ok) {
3391
+ await this.throwApiError(response, `Surf request failed (${method} ${url})`);
3392
+ }
3393
+ return response.json();
3394
+ }
3395
+ async handlePaymentAndRetry(url, method, body, response) {
3396
+ let paymentHeader = response.headers.get("payment-required");
3397
+ if (!paymentHeader) {
3398
+ try {
3399
+ const respBody = await response.json();
3400
+ if (respBody.x402Version !== void 0 || respBody.accepts !== void 0) {
3401
+ paymentHeader = btoa(JSON.stringify(respBody));
3402
+ }
3403
+ } catch {
3404
+ }
3405
+ }
3406
+ if (!paymentHeader) {
3407
+ throw new PaymentError("402 response but no payment requirements found");
3408
+ }
3409
+ const paymentRequired = parsePaymentRequired(paymentHeader);
3410
+ const details = extractPaymentDetails(paymentRequired);
3411
+ const paymentPayload = await createPaymentPayload(
3412
+ this.privateKey,
3413
+ this.account.address,
3414
+ details.recipient,
3415
+ details.amount,
3416
+ details.network || "eip155:8453",
3417
+ {
3418
+ resourceUrl: details.resource?.url || url,
3419
+ resourceDescription: details.resource?.description || "BlockRun Surf",
3420
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
3421
+ extra: details.extra
3422
+ }
3423
+ );
3424
+ const retryInit = {
3425
+ method,
3426
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
3427
+ };
3428
+ if (method === "POST") {
3429
+ retryInit.headers = {
3430
+ ...retryInit.headers,
3431
+ "Content-Type": "application/json"
3432
+ };
3433
+ retryInit.body = JSON.stringify(body ?? {});
3434
+ }
3435
+ const retry = await this.fetchWithTimeout(url, retryInit);
3436
+ if (retry.status === 402) {
3437
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
3438
+ }
3439
+ if (!retry.ok) {
3440
+ await this.throwApiError(retry, `Surf request failed after payment (${method} ${url})`);
3441
+ }
3442
+ return retry.json();
3443
+ }
3444
+ async throwApiError(resp, prefix) {
3445
+ let errorBody;
3446
+ try {
3447
+ errorBody = await resp.json();
3448
+ } catch {
3449
+ errorBody = { error: "Request failed" };
3450
+ }
3451
+ throw new APIError(
3452
+ `${prefix}: HTTP ${resp.status}`,
3453
+ resp.status,
3454
+ sanitizeErrorResponse(errorBody)
3455
+ );
3456
+ }
3457
+ async fetchWithTimeout(url, init) {
3458
+ const controller = new AbortController();
3459
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3460
+ try {
3461
+ return await fetch(url, { ...init, signal: controller.signal });
3462
+ } finally {
3463
+ clearTimeout(timeoutId);
3464
+ }
3465
+ }
3466
+ getWalletAddress() {
3467
+ return this.account.address;
3468
+ }
3469
+ };
3470
+
3471
+ // src/wallet.ts
3472
+ var import_accounts11 = require("viem/accounts");
3311
3473
  var fs2 = __toESM(require("fs"), 1);
3312
3474
  var path2 = __toESM(require("path"), 1);
3313
3475
  var os2 = __toESM(require("os"), 1);
@@ -3316,8 +3478,8 @@ var BASE_CHAIN_ID2 = "8453";
3316
3478
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
3317
3479
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
3318
3480
  function createWallet() {
3319
- const privateKey = (0, import_accounts10.generatePrivateKey)();
3320
- const account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3481
+ const privateKey = (0, import_accounts11.generatePrivateKey)();
3482
+ const account = (0, import_accounts11.privateKeyToAccount)(privateKey);
3321
3483
  return {
3322
3484
  address: account.address,
3323
3485
  privateKey
@@ -3373,12 +3535,12 @@ function loadWallet() {
3373
3535
  function getOrCreateWallet() {
3374
3536
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3375
3537
  if (envKey) {
3376
- const account = (0, import_accounts10.privateKeyToAccount)(envKey);
3538
+ const account = (0, import_accounts11.privateKeyToAccount)(envKey);
3377
3539
  return { address: account.address, privateKey: envKey, isNew: false };
3378
3540
  }
3379
3541
  const fileKey = loadWallet();
3380
3542
  if (fileKey) {
3381
- const account = (0, import_accounts10.privateKeyToAccount)(fileKey);
3543
+ const account = (0, import_accounts11.privateKeyToAccount)(fileKey);
3382
3544
  return { address: account.address, privateKey: fileKey, isNew: false };
3383
3545
  }
3384
3546
  const { address, privateKey } = createWallet();
@@ -3388,11 +3550,11 @@ function getOrCreateWallet() {
3388
3550
  function getWalletAddress() {
3389
3551
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3390
3552
  if (envKey) {
3391
- return (0, import_accounts10.privateKeyToAccount)(envKey).address;
3553
+ return (0, import_accounts11.privateKeyToAccount)(envKey).address;
3392
3554
  }
3393
3555
  const fileKey = loadWallet();
3394
3556
  if (fileKey) {
3395
- return (0, import_accounts10.privateKeyToAccount)(fileKey).address;
3557
+ return (0, import_accounts11.privateKeyToAccount)(fileKey).address;
3396
3558
  }
3397
3559
  return null;
3398
3560
  }
@@ -3572,7 +3734,7 @@ async function getOrCreateSolanaWallet() {
3572
3734
  // src/solana-client.ts
3573
3735
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3574
3736
  var DEFAULT_MAX_TOKENS2 = 1024;
3575
- var DEFAULT_TIMEOUT9 = 6e4;
3737
+ var DEFAULT_TIMEOUT10 = 6e4;
3576
3738
  var SDK_VERSION2 = "0.3.0";
3577
3739
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3578
3740
  var SolanaLLMClient = class {
@@ -3597,7 +3759,7 @@ var SolanaLLMClient = class {
3597
3759
  validateApiUrl(apiUrl);
3598
3760
  this.apiUrl = apiUrl.replace(/\/$/, "");
3599
3761
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3600
- this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3762
+ this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3601
3763
  }
3602
3764
  /** Get Solana wallet address (public key in base58). */
3603
3765
  async getWalletAddress() {
@@ -4504,7 +4666,7 @@ var OpenAI = class {
4504
4666
  };
4505
4667
 
4506
4668
  // src/anthropic-compat.ts
4507
- var import_accounts11 = require("viem/accounts");
4669
+ var import_accounts12 = require("viem/accounts");
4508
4670
  var AnthropicClient = class {
4509
4671
  _client = null;
4510
4672
  _clientPromise = null;
@@ -4517,7 +4679,7 @@ var AnthropicClient = class {
4517
4679
  const key = options.privateKey ?? wallet.privateKey;
4518
4680
  validatePrivateKey(key);
4519
4681
  this._privateKey = key;
4520
- this._account = (0, import_accounts11.privateKeyToAccount)(this._privateKey);
4682
+ this._account = (0, import_accounts12.privateKeyToAccount)(this._privateKey);
4521
4683
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4522
4684
  validateApiUrl(apiUrl);
4523
4685
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4628,6 +4790,7 @@ var AnthropicClient = class {
4628
4790
  SOLANA_WALLET_FILE_PATH,
4629
4791
  SearchClient,
4630
4792
  SolanaLLMClient,
4793
+ SurfClient,
4631
4794
  USDC_BASE,
4632
4795
  USDC_BASE_CONTRACT,
4633
4796
  USDC_SOLANA,
package/dist/index.d.cts CHANGED
@@ -496,6 +496,24 @@ interface VideoGenerateOptions {
496
496
  imageUrl?: string;
497
497
  /** Duration to bill for (defaults to model's default duration) */
498
498
  durationSeconds?: number;
499
+ /** Output aspect ratio. Token360 / Seedance only — silently ignored by xAI Grok. */
500
+ aspectRatio?: "adaptive" | "16:9" | "9:16" | "1:1" | "4:3" | "3:4" | "21:9" | "9:21";
501
+ /** Output resolution. Token360 / Seedance only. */
502
+ resolution?: "360p" | "480p" | "540p" | "720p" | "1080p" | "1K" | "2K" | "4K";
503
+ /**
504
+ * Whether the model should produce an audio track. Token360 / Seedance only.
505
+ * When omitted, the gateway does not send the flag — Token360 then applies
506
+ * its own upstream default (typically off, occasionally on depending on
507
+ * model variant / prompt). Audio generation is usually a paid surcharge,
508
+ * so callers should expose this as a visible toggle to their users.
509
+ */
510
+ generateAudio?: boolean;
511
+ /** Reproducibility seed when supported by the model. */
512
+ seed?: number;
513
+ /** Embed the upstream watermark on the output. Defaults to false at the gateway. */
514
+ watermark?: boolean;
515
+ /** Return the last frame as an image alongside the clip — useful for chaining. */
516
+ returnLastFrame?: boolean;
499
517
  }
500
518
  interface SearchOptions {
501
519
  /** Source types to search (e.g. ["web", "x", "news"]) */
@@ -767,6 +785,11 @@ interface PriceClientOptions {
767
785
  /** If false, construction succeeds without a wallet (free endpoints only). */
768
786
  requireWallet?: boolean;
769
787
  }
788
+ interface SurfClientOptions {
789
+ privateKey?: `0x${string}` | string;
790
+ apiUrl?: string;
791
+ timeout?: number;
792
+ }
770
793
  declare class BlockrunError extends Error {
771
794
  constructor(message: string);
772
795
  }
@@ -1695,6 +1718,74 @@ declare class PriceClient {
1695
1718
  private fetchWithTimeout;
1696
1719
  }
1697
1720
 
1721
+ /**
1722
+ * BlockRun Surf Client — pay-per-call crypto data via x402 micropayments.
1723
+ *
1724
+ * Surf aggregates 84+ endpoints across CEX/DEX market data, on-chain SQL,
1725
+ * wallet intelligence, prediction markets, social analytics, and news under
1726
+ * a single OpenAPI surface mounted at `/api/v1/surf/*`.
1727
+ *
1728
+ * Pricing tiers (flat per-call, USDC on Base):
1729
+ * Tier 1 — $0.001/call (prices, rankings, lists, news, simple reads)
1730
+ * Tier 2 — $0.005/call (order books, candles, search, wallet details)
1731
+ * Tier 3 — $0.020/call (on-chain SQL, schema introspection, chat)
1732
+ *
1733
+ * Because the catalog is large and evolving, this client deliberately
1734
+ * exposes a thin `get` / `post` pair instead of 84 typed wrappers. Pass the
1735
+ * path (with or without the `/v1/surf` prefix) and either query params or a
1736
+ * JSON body. The full endpoint inventory lives at
1737
+ * https://blockrun.ai/marketplace/surf.
1738
+ *
1739
+ * Usage:
1740
+ * import { SurfClient } from "@blockrun/llm";
1741
+ *
1742
+ * const surf = new SurfClient({ privateKey: "0x..." });
1743
+ *
1744
+ * // Tier 1 — token price ($0.001)
1745
+ * const btc = await surf.get("/market/price", { symbol: "BTC" });
1746
+ *
1747
+ * // Tier 2 — order book ($0.005)
1748
+ * const book = await surf.get("/exchange/depth", {
1749
+ * exchange: "binance",
1750
+ * symbol: "BTC-USDT",
1751
+ * });
1752
+ *
1753
+ * // Tier 3 — raw on-chain SQL ($0.020)
1754
+ * const rows = await surf.post("/onchain/sql", {
1755
+ * query: "SELECT block_number FROM ethereum.blocks ORDER BY block_number DESC LIMIT 5",
1756
+ * });
1757
+ *
1758
+ * // Typed responses via generic
1759
+ * type Price = { symbol: string; price: number; timestamp: string };
1760
+ * const typed = await surf.get<Price>("/market/price", { symbol: "ETH" });
1761
+ */
1762
+
1763
+ type QueryValue = string | number | boolean | null | undefined;
1764
+ type QueryParams = Record<string, QueryValue | QueryValue[]>;
1765
+ declare class SurfClient {
1766
+ private account;
1767
+ private privateKey;
1768
+ private apiUrl;
1769
+ private timeout;
1770
+ constructor(options?: SurfClientOptions);
1771
+ /**
1772
+ * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
1773
+ * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
1774
+ * arrays become repeated keys (`?a=1&a=2`).
1775
+ */
1776
+ get<T = unknown>(path: string, params?: QueryParams): Promise<T>;
1777
+ /**
1778
+ * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
1779
+ */
1780
+ post<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T>;
1781
+ private buildUrl;
1782
+ private requestWithPayment;
1783
+ private handlePaymentAndRetry;
1784
+ private throwApiError;
1785
+ private fetchWithTimeout;
1786
+ getWalletAddress(): string;
1787
+ }
1788
+
1698
1789
  /**
1699
1790
  * x402 Payment Protocol v2 Implementation for BlockRun.
1700
1791
  *
@@ -2338,4 +2429,4 @@ declare function validateTemperature(temperature?: number): void;
2338
2429
  */
2339
2430
  declare function validateTopP(topP?: number): void;
2340
2431
 
2341
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, 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, VoiceClient, type VoiceClientOptions, type VoicePreset, 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 };
2432
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, 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, SurfClient, type SurfClientOptions, 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, VoiceClient, type VoiceClientOptions, type VoicePreset, 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
@@ -496,6 +496,24 @@ interface VideoGenerateOptions {
496
496
  imageUrl?: string;
497
497
  /** Duration to bill for (defaults to model's default duration) */
498
498
  durationSeconds?: number;
499
+ /** Output aspect ratio. Token360 / Seedance only — silently ignored by xAI Grok. */
500
+ aspectRatio?: "adaptive" | "16:9" | "9:16" | "1:1" | "4:3" | "3:4" | "21:9" | "9:21";
501
+ /** Output resolution. Token360 / Seedance only. */
502
+ resolution?: "360p" | "480p" | "540p" | "720p" | "1080p" | "1K" | "2K" | "4K";
503
+ /**
504
+ * Whether the model should produce an audio track. Token360 / Seedance only.
505
+ * When omitted, the gateway does not send the flag — Token360 then applies
506
+ * its own upstream default (typically off, occasionally on depending on
507
+ * model variant / prompt). Audio generation is usually a paid surcharge,
508
+ * so callers should expose this as a visible toggle to their users.
509
+ */
510
+ generateAudio?: boolean;
511
+ /** Reproducibility seed when supported by the model. */
512
+ seed?: number;
513
+ /** Embed the upstream watermark on the output. Defaults to false at the gateway. */
514
+ watermark?: boolean;
515
+ /** Return the last frame as an image alongside the clip — useful for chaining. */
516
+ returnLastFrame?: boolean;
499
517
  }
500
518
  interface SearchOptions {
501
519
  /** Source types to search (e.g. ["web", "x", "news"]) */
@@ -767,6 +785,11 @@ interface PriceClientOptions {
767
785
  /** If false, construction succeeds without a wallet (free endpoints only). */
768
786
  requireWallet?: boolean;
769
787
  }
788
+ interface SurfClientOptions {
789
+ privateKey?: `0x${string}` | string;
790
+ apiUrl?: string;
791
+ timeout?: number;
792
+ }
770
793
  declare class BlockrunError extends Error {
771
794
  constructor(message: string);
772
795
  }
@@ -1695,6 +1718,74 @@ declare class PriceClient {
1695
1718
  private fetchWithTimeout;
1696
1719
  }
1697
1720
 
1721
+ /**
1722
+ * BlockRun Surf Client — pay-per-call crypto data via x402 micropayments.
1723
+ *
1724
+ * Surf aggregates 84+ endpoints across CEX/DEX market data, on-chain SQL,
1725
+ * wallet intelligence, prediction markets, social analytics, and news under
1726
+ * a single OpenAPI surface mounted at `/api/v1/surf/*`.
1727
+ *
1728
+ * Pricing tiers (flat per-call, USDC on Base):
1729
+ * Tier 1 — $0.001/call (prices, rankings, lists, news, simple reads)
1730
+ * Tier 2 — $0.005/call (order books, candles, search, wallet details)
1731
+ * Tier 3 — $0.020/call (on-chain SQL, schema introspection, chat)
1732
+ *
1733
+ * Because the catalog is large and evolving, this client deliberately
1734
+ * exposes a thin `get` / `post` pair instead of 84 typed wrappers. Pass the
1735
+ * path (with or without the `/v1/surf` prefix) and either query params or a
1736
+ * JSON body. The full endpoint inventory lives at
1737
+ * https://blockrun.ai/marketplace/surf.
1738
+ *
1739
+ * Usage:
1740
+ * import { SurfClient } from "@blockrun/llm";
1741
+ *
1742
+ * const surf = new SurfClient({ privateKey: "0x..." });
1743
+ *
1744
+ * // Tier 1 — token price ($0.001)
1745
+ * const btc = await surf.get("/market/price", { symbol: "BTC" });
1746
+ *
1747
+ * // Tier 2 — order book ($0.005)
1748
+ * const book = await surf.get("/exchange/depth", {
1749
+ * exchange: "binance",
1750
+ * symbol: "BTC-USDT",
1751
+ * });
1752
+ *
1753
+ * // Tier 3 — raw on-chain SQL ($0.020)
1754
+ * const rows = await surf.post("/onchain/sql", {
1755
+ * query: "SELECT block_number FROM ethereum.blocks ORDER BY block_number DESC LIMIT 5",
1756
+ * });
1757
+ *
1758
+ * // Typed responses via generic
1759
+ * type Price = { symbol: string; price: number; timestamp: string };
1760
+ * const typed = await surf.get<Price>("/market/price", { symbol: "ETH" });
1761
+ */
1762
+
1763
+ type QueryValue = string | number | boolean | null | undefined;
1764
+ type QueryParams = Record<string, QueryValue | QueryValue[]>;
1765
+ declare class SurfClient {
1766
+ private account;
1767
+ private privateKey;
1768
+ private apiUrl;
1769
+ private timeout;
1770
+ constructor(options?: SurfClientOptions);
1771
+ /**
1772
+ * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
1773
+ * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
1774
+ * arrays become repeated keys (`?a=1&a=2`).
1775
+ */
1776
+ get<T = unknown>(path: string, params?: QueryParams): Promise<T>;
1777
+ /**
1778
+ * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
1779
+ */
1780
+ post<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T>;
1781
+ private buildUrl;
1782
+ private requestWithPayment;
1783
+ private handlePaymentAndRetry;
1784
+ private throwApiError;
1785
+ private fetchWithTimeout;
1786
+ getWalletAddress(): string;
1787
+ }
1788
+
1698
1789
  /**
1699
1790
  * x402 Payment Protocol v2 Implementation for BlockRun.
1700
1791
  *
@@ -2338,4 +2429,4 @@ declare function validateTemperature(temperature?: number): void;
2338
2429
  */
2339
2430
  declare function validateTopP(topP?: number): void;
2340
2431
 
2341
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, 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, VoiceClient, type VoiceClientOptions, type VoicePreset, 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 };
2432
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunError, type CallInitiatedResponse, type CallModel, type CallOptions, type CallStatusResponse, 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, SurfClient, type SurfClientOptions, 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, VoiceClient, type VoiceClientOptions, type VoicePreset, 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
@@ -2316,6 +2316,12 @@ var VideoClient = class {
2316
2316
  };
2317
2317
  if (options?.imageUrl) body.image_url = options.imageUrl;
2318
2318
  if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2319
+ if (options?.aspectRatio) body.aspect_ratio = options.aspectRatio;
2320
+ if (options?.resolution) body.resolution = options.resolution;
2321
+ if (options?.generateAudio !== void 0) body.generate_audio = options.generateAudio;
2322
+ if (options?.seed !== void 0) body.seed = options.seed;
2323
+ if (options?.watermark !== void 0) body.watermark = options.watermark;
2324
+ if (options?.returnLastFrame) body.return_last_frame = true;
2319
2325
  const budgetMs = options?.budgetMs ?? DEFAULT_GENERATE_BUDGET_MS;
2320
2326
  return this.submitAndPoll(body, budgetMs);
2321
2327
  }
@@ -3209,8 +3215,163 @@ function buildUrl(base, query) {
3209
3215
  return `${base}?${qs}`;
3210
3216
  }
3211
3217
 
3218
+ // src/surf.ts
3219
+ import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
3220
+ var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3221
+ var DEFAULT_TIMEOUT9 = 6e4;
3222
+ var SURF_PREFIX = "/v1/surf";
3223
+ var SurfClient = class {
3224
+ account;
3225
+ privateKey;
3226
+ apiUrl;
3227
+ timeout;
3228
+ constructor(options = {}) {
3229
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3230
+ const privateKey = options.privateKey || envKey;
3231
+ if (!privateKey) {
3232
+ throw new Error(
3233
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
3234
+ );
3235
+ }
3236
+ validatePrivateKey(privateKey);
3237
+ this.privateKey = privateKey;
3238
+ this.account = privateKeyToAccount9(privateKey);
3239
+ const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3240
+ validateApiUrl(apiUrl);
3241
+ this.apiUrl = apiUrl.replace(/\/$/, "");
3242
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3243
+ }
3244
+ /**
3245
+ * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
3246
+ * `/v1/surf` is tolerated and stripped). Query params are URL-encoded;
3247
+ * arrays become repeated keys (`?a=1&a=2`).
3248
+ */
3249
+ async get(path5, params) {
3250
+ const url = this.buildUrl(path5, params);
3251
+ return this.requestWithPayment(url, "GET");
3252
+ }
3253
+ /**
3254
+ * POST a Surf endpoint with a JSON body. Same path normalization as `get`.
3255
+ */
3256
+ async post(path5, body) {
3257
+ const url = this.buildUrl(path5);
3258
+ return this.requestWithPayment(url, "POST", body);
3259
+ }
3260
+ buildUrl(path5, params) {
3261
+ let normalized = path5.startsWith("/") ? path5 : `/${path5}`;
3262
+ if (!normalized.startsWith(SURF_PREFIX)) {
3263
+ normalized = `${SURF_PREFIX}${normalized}`;
3264
+ }
3265
+ const base = `${this.apiUrl}${normalized}`;
3266
+ if (!params) return base;
3267
+ const qs = new URLSearchParams();
3268
+ for (const [key, value] of Object.entries(params)) {
3269
+ if (value === void 0 || value === null) continue;
3270
+ if (Array.isArray(value)) {
3271
+ for (const v of value) {
3272
+ if (v === void 0 || v === null) continue;
3273
+ qs.append(key, String(v));
3274
+ }
3275
+ } else {
3276
+ qs.append(key, String(value));
3277
+ }
3278
+ }
3279
+ const query = qs.toString();
3280
+ return query ? `${base}?${query}` : base;
3281
+ }
3282
+ async requestWithPayment(url, method, body) {
3283
+ const init = { method };
3284
+ if (method === "POST") {
3285
+ init.headers = { "Content-Type": "application/json" };
3286
+ init.body = JSON.stringify(body ?? {});
3287
+ }
3288
+ const response = await this.fetchWithTimeout(url, init);
3289
+ if (response.status === 402) {
3290
+ return this.handlePaymentAndRetry(url, method, body, response);
3291
+ }
3292
+ if (!response.ok) {
3293
+ await this.throwApiError(response, `Surf request failed (${method} ${url})`);
3294
+ }
3295
+ return response.json();
3296
+ }
3297
+ async handlePaymentAndRetry(url, method, body, response) {
3298
+ let paymentHeader = response.headers.get("payment-required");
3299
+ if (!paymentHeader) {
3300
+ try {
3301
+ const respBody = await response.json();
3302
+ if (respBody.x402Version !== void 0 || respBody.accepts !== void 0) {
3303
+ paymentHeader = btoa(JSON.stringify(respBody));
3304
+ }
3305
+ } catch {
3306
+ }
3307
+ }
3308
+ if (!paymentHeader) {
3309
+ throw new PaymentError("402 response but no payment requirements found");
3310
+ }
3311
+ const paymentRequired = parsePaymentRequired(paymentHeader);
3312
+ const details = extractPaymentDetails(paymentRequired);
3313
+ const paymentPayload = await createPaymentPayload(
3314
+ this.privateKey,
3315
+ this.account.address,
3316
+ details.recipient,
3317
+ details.amount,
3318
+ details.network || "eip155:8453",
3319
+ {
3320
+ resourceUrl: details.resource?.url || url,
3321
+ resourceDescription: details.resource?.description || "BlockRun Surf",
3322
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
3323
+ extra: details.extra
3324
+ }
3325
+ );
3326
+ const retryInit = {
3327
+ method,
3328
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
3329
+ };
3330
+ if (method === "POST") {
3331
+ retryInit.headers = {
3332
+ ...retryInit.headers,
3333
+ "Content-Type": "application/json"
3334
+ };
3335
+ retryInit.body = JSON.stringify(body ?? {});
3336
+ }
3337
+ const retry = await this.fetchWithTimeout(url, retryInit);
3338
+ if (retry.status === 402) {
3339
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
3340
+ }
3341
+ if (!retry.ok) {
3342
+ await this.throwApiError(retry, `Surf request failed after payment (${method} ${url})`);
3343
+ }
3344
+ return retry.json();
3345
+ }
3346
+ async throwApiError(resp, prefix) {
3347
+ let errorBody;
3348
+ try {
3349
+ errorBody = await resp.json();
3350
+ } catch {
3351
+ errorBody = { error: "Request failed" };
3352
+ }
3353
+ throw new APIError(
3354
+ `${prefix}: HTTP ${resp.status}`,
3355
+ resp.status,
3356
+ sanitizeErrorResponse(errorBody)
3357
+ );
3358
+ }
3359
+ async fetchWithTimeout(url, init) {
3360
+ const controller = new AbortController();
3361
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3362
+ try {
3363
+ return await fetch(url, { ...init, signal: controller.signal });
3364
+ } finally {
3365
+ clearTimeout(timeoutId);
3366
+ }
3367
+ }
3368
+ getWalletAddress() {
3369
+ return this.account.address;
3370
+ }
3371
+ };
3372
+
3212
3373
  // src/wallet.ts
3213
- import { privateKeyToAccount as privateKeyToAccount9, generatePrivateKey } from "viem/accounts";
3374
+ import { privateKeyToAccount as privateKeyToAccount10, generatePrivateKey } from "viem/accounts";
3214
3375
  import * as fs2 from "fs";
3215
3376
  import * as path2 from "path";
3216
3377
  import * as os2 from "os";
@@ -3220,7 +3381,7 @@ var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
3220
3381
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
3221
3382
  function createWallet() {
3222
3383
  const privateKey = generatePrivateKey();
3223
- const account = privateKeyToAccount9(privateKey);
3384
+ const account = privateKeyToAccount10(privateKey);
3224
3385
  return {
3225
3386
  address: account.address,
3226
3387
  privateKey
@@ -3276,12 +3437,12 @@ function loadWallet() {
3276
3437
  function getOrCreateWallet() {
3277
3438
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3278
3439
  if (envKey) {
3279
- const account = privateKeyToAccount9(envKey);
3440
+ const account = privateKeyToAccount10(envKey);
3280
3441
  return { address: account.address, privateKey: envKey, isNew: false };
3281
3442
  }
3282
3443
  const fileKey = loadWallet();
3283
3444
  if (fileKey) {
3284
- const account = privateKeyToAccount9(fileKey);
3445
+ const account = privateKeyToAccount10(fileKey);
3285
3446
  return { address: account.address, privateKey: fileKey, isNew: false };
3286
3447
  }
3287
3448
  const { address, privateKey } = createWallet();
@@ -3291,11 +3452,11 @@ function getOrCreateWallet() {
3291
3452
  function getWalletAddress() {
3292
3453
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3293
3454
  if (envKey) {
3294
- return privateKeyToAccount9(envKey).address;
3455
+ return privateKeyToAccount10(envKey).address;
3295
3456
  }
3296
3457
  const fileKey = loadWallet();
3297
3458
  if (fileKey) {
3298
- return privateKeyToAccount9(fileKey).address;
3459
+ return privateKeyToAccount10(fileKey).address;
3299
3460
  }
3300
3461
  return null;
3301
3462
  }
@@ -3475,7 +3636,7 @@ async function getOrCreateSolanaWallet() {
3475
3636
  // src/solana-client.ts
3476
3637
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
3477
3638
  var DEFAULT_MAX_TOKENS2 = 1024;
3478
- var DEFAULT_TIMEOUT9 = 6e4;
3639
+ var DEFAULT_TIMEOUT10 = 6e4;
3479
3640
  var SDK_VERSION2 = "0.3.0";
3480
3641
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
3481
3642
  var SolanaLLMClient = class {
@@ -3500,7 +3661,7 @@ var SolanaLLMClient = class {
3500
3661
  validateApiUrl(apiUrl);
3501
3662
  this.apiUrl = apiUrl.replace(/\/$/, "");
3502
3663
  this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
3503
- this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3664
+ this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3504
3665
  }
3505
3666
  /** Get Solana wallet address (public key in base58). */
3506
3667
  async getWalletAddress() {
@@ -4407,7 +4568,7 @@ var OpenAI = class {
4407
4568
  };
4408
4569
 
4409
4570
  // src/anthropic-compat.ts
4410
- import { privateKeyToAccount as privateKeyToAccount10 } from "viem/accounts";
4571
+ import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
4411
4572
  var AnthropicClient = class {
4412
4573
  _client = null;
4413
4574
  _clientPromise = null;
@@ -4420,7 +4581,7 @@ var AnthropicClient = class {
4420
4581
  const key = options.privateKey ?? wallet.privateKey;
4421
4582
  validatePrivateKey(key);
4422
4583
  this._privateKey = key;
4423
- this._account = privateKeyToAccount10(this._privateKey);
4584
+ this._account = privateKeyToAccount11(this._privateKey);
4424
4585
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
4425
4586
  validateApiUrl(apiUrl);
4426
4587
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -4530,6 +4691,7 @@ export {
4530
4691
  SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH,
4531
4692
  SearchClient,
4532
4693
  SolanaLLMClient,
4694
+ SurfClient,
4533
4695
  USDC_BASE,
4534
4696
  USDC_BASE_CONTRACT,
4535
4697
  USDC_SOLANA,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "description": "BlockRun SDK - Pay-per-request AI (LLM, Image, Video, Music, Voice) via x402 on Base and Solana",
6
6
  "main": "dist/index.cjs",