@blockrun/llm 2.11.1 → 2.13.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/dist/index.js CHANGED
@@ -2295,11 +2295,210 @@ var MusicClient = class {
2295
2295
  }
2296
2296
  };
2297
2297
 
2298
- // src/video.ts
2298
+ // src/speech.ts
2299
2299
  import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
2300
2300
  var DEFAULT_API_URL4 = "https://blockrun.ai/api";
2301
- var DEFAULT_MODEL3 = "xai/grok-imagine-video";
2301
+ var DEFAULT_MODEL3 = "elevenlabs/flash-v2.5";
2302
+ var DEFAULT_SOUNDFX_MODEL = "elevenlabs/sound-effects";
2302
2303
  var DEFAULT_TIMEOUT4 = 12e4;
2304
+ var SpeechClient = class {
2305
+ account;
2306
+ privateKey;
2307
+ apiUrl;
2308
+ timeout;
2309
+ sessionTotalUsd = 0;
2310
+ sessionCalls = 0;
2311
+ constructor(options = {}) {
2312
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2313
+ const privateKey = options.privateKey || envKey;
2314
+ if (!privateKey) {
2315
+ throw new Error(
2316
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
2317
+ );
2318
+ }
2319
+ validatePrivateKey(privateKey);
2320
+ this.privateKey = privateKey;
2321
+ this.account = privateKeyToAccount4(privateKey);
2322
+ const apiUrl = options.apiUrl || DEFAULT_API_URL4;
2323
+ validateApiUrl(apiUrl);
2324
+ this.apiUrl = apiUrl.replace(/\/$/, "");
2325
+ this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2326
+ }
2327
+ /**
2328
+ * Synthesize speech from text (OpenAI-compatible TTS).
2329
+ *
2330
+ * Price scales with character count: (chars / 1000) x model rate,
2331
+ * minimum $0.001/request. Synthesis is synchronous.
2332
+ *
2333
+ * @param input - Text to synthesize. Per-model character caps apply
2334
+ * (flash/turbo 40k, multilingual-v2 10k, v3 5k).
2335
+ * @param options - Optional model / voice / format / speed
2336
+ * @returns SpeechResponse with audio URL, format, and character count
2337
+ *
2338
+ * @example
2339
+ * const result = await client.generate('Welcome to BlockRun.', { voice: 'george' });
2340
+ * console.log(result.data[0].url); // audio URL (mp3 by default)
2341
+ */
2342
+ async generate(input, options) {
2343
+ const body = {
2344
+ model: options?.model || DEFAULT_MODEL3,
2345
+ input
2346
+ };
2347
+ if (options?.voice) body.voice = options.voice;
2348
+ if (options?.responseFormat) body.response_format = options.responseFormat;
2349
+ if (options?.speed !== void 0) body.speed = options.speed;
2350
+ return this.requestWithPayment("/v1/audio/speech", body);
2351
+ }
2352
+ /** OpenAI-style alias for {@link generate}. */
2353
+ speak(input, options) {
2354
+ return this.generate(input, options);
2355
+ }
2356
+ /**
2357
+ * Generate a cinematic sound effect from a text prompt.
2358
+ *
2359
+ * Flat $0.05/generation, up to 22 seconds of audio.
2360
+ *
2361
+ * @param text - Sound effect description (max 1000 chars).
2362
+ * E.g. 'rain on a tin roof', 'sci-fi door whoosh'
2363
+ * @param options - Optional duration / prompt influence / format
2364
+ * @returns SpeechResponse with audio URL and format
2365
+ *
2366
+ * @example
2367
+ * const fx = await client.soundEffect('crackling campfire at night');
2368
+ * console.log(fx.data[0].url);
2369
+ */
2370
+ async soundEffect(text, options) {
2371
+ const body = {
2372
+ model: options?.model || DEFAULT_SOUNDFX_MODEL,
2373
+ text
2374
+ };
2375
+ if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2376
+ if (options?.promptInfluence !== void 0) body.prompt_influence = options.promptInfluence;
2377
+ if (options?.responseFormat) body.response_format = options.responseFormat;
2378
+ return this.requestWithPayment("/v1/audio/sound-effects", body);
2379
+ }
2380
+ /**
2381
+ * List available voices for TTS (free, rate-limited 60 req/min/IP).
2382
+ *
2383
+ * Pass a voice's `alias` (if present) or `voice_id` as the `voice`
2384
+ * option to {@link generate}.
2385
+ */
2386
+ async listVoices() {
2387
+ const response = await this.fetchWithTimeout(`${this.apiUrl}/v1/audio/voices`, {
2388
+ method: "GET"
2389
+ });
2390
+ if (!response.ok) {
2391
+ let errorBody;
2392
+ try {
2393
+ errorBody = await response.json();
2394
+ } catch {
2395
+ errorBody = { error: "Request failed" };
2396
+ }
2397
+ throw new APIError(`API error: ${response.status}`, response.status, sanitizeErrorResponse(errorBody));
2398
+ }
2399
+ const payload = await response.json();
2400
+ return payload.data || [];
2401
+ }
2402
+ async requestWithPayment(endpoint, body) {
2403
+ const url = `${this.apiUrl}${endpoint}`;
2404
+ const response = await this.fetchWithTimeout(url, {
2405
+ method: "POST",
2406
+ headers: { "Content-Type": "application/json" },
2407
+ body: JSON.stringify(body)
2408
+ });
2409
+ if (response.status === 402) {
2410
+ return this.handlePaymentAndRetry(url, endpoint, body, response);
2411
+ }
2412
+ if (!response.ok) {
2413
+ let errorBody;
2414
+ try {
2415
+ errorBody = await response.json();
2416
+ } catch {
2417
+ errorBody = { error: "Request failed" };
2418
+ }
2419
+ throw new APIError(`API error: ${response.status}`, response.status, sanitizeErrorResponse(errorBody));
2420
+ }
2421
+ return response.json();
2422
+ }
2423
+ async handlePaymentAndRetry(url, endpoint, body, response) {
2424
+ let paymentHeader = response.headers.get("payment-required");
2425
+ if (!paymentHeader) {
2426
+ try {
2427
+ const respBody = await response.json();
2428
+ if (respBody.x402 || respBody.accepts) {
2429
+ paymentHeader = btoa(JSON.stringify(respBody));
2430
+ }
2431
+ } catch {
2432
+ }
2433
+ }
2434
+ if (!paymentHeader) {
2435
+ throw new PaymentError("402 response but no payment requirements found");
2436
+ }
2437
+ const paymentRequired = parsePaymentRequired(paymentHeader);
2438
+ const details = extractPaymentDetails(paymentRequired);
2439
+ const paymentPayload = await createPaymentPayload(
2440
+ this.privateKey,
2441
+ this.account.address,
2442
+ details.recipient,
2443
+ details.amount,
2444
+ details.network || "eip155:8453",
2445
+ {
2446
+ resourceUrl: details.resource?.url || `${this.apiUrl}${endpoint}`,
2447
+ resourceDescription: details.resource?.description || "BlockRun Voice",
2448
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
2449
+ extra: details.extra
2450
+ }
2451
+ );
2452
+ const retryResponse = await this.fetchWithTimeout(url, {
2453
+ method: "POST",
2454
+ headers: {
2455
+ "Content-Type": "application/json",
2456
+ "PAYMENT-SIGNATURE": paymentPayload
2457
+ },
2458
+ body: JSON.stringify(body)
2459
+ });
2460
+ if (retryResponse.status === 402) {
2461
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2462
+ }
2463
+ if (!retryResponse.ok) {
2464
+ let errorBody;
2465
+ try {
2466
+ errorBody = await retryResponse.json();
2467
+ } catch {
2468
+ errorBody = { error: "Request failed" };
2469
+ }
2470
+ throw new APIError(`API error after payment: ${retryResponse.status}`, retryResponse.status, sanitizeErrorResponse(errorBody));
2471
+ }
2472
+ const data = await retryResponse.json();
2473
+ this.sessionCalls++;
2474
+ const paidUsd = Number(details.amount) / 1e6;
2475
+ if (Number.isFinite(paidUsd)) this.sessionTotalUsd += paidUsd;
2476
+ const txHash = retryResponse.headers.get("x-payment-receipt") || retryResponse.headers.get("X-Payment-Receipt");
2477
+ if (txHash) data.txHash = txHash;
2478
+ return data;
2479
+ }
2480
+ async fetchWithTimeout(url, options) {
2481
+ const controller = new AbortController();
2482
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2483
+ try {
2484
+ return await fetch(url, { ...options, signal: controller.signal });
2485
+ } finally {
2486
+ clearTimeout(timeoutId);
2487
+ }
2488
+ }
2489
+ getWalletAddress() {
2490
+ return this.account.address;
2491
+ }
2492
+ getSpending() {
2493
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2494
+ }
2495
+ };
2496
+
2497
+ // src/video.ts
2498
+ import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
2499
+ var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2500
+ var DEFAULT_MODEL4 = "xai/grok-imagine-video";
2501
+ var DEFAULT_TIMEOUT5 = 12e4;
2303
2502
  var POLL_INTERVAL_MS2 = 5e3;
2304
2503
  var DEFAULT_GENERATE_BUDGET_MS = 3e5;
2305
2504
  var MAX_TIMEOUT_SECONDS = 600;
@@ -2320,11 +2519,11 @@ var VideoClient = class {
2320
2519
  }
2321
2520
  validatePrivateKey(privateKey);
2322
2521
  this.privateKey = privateKey;
2323
- this.account = privateKeyToAccount4(privateKey);
2324
- const apiUrl = options.apiUrl || DEFAULT_API_URL4;
2522
+ this.account = privateKeyToAccount5(privateKey);
2523
+ const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2325
2524
  validateApiUrl(apiUrl);
2326
2525
  this.apiUrl = apiUrl.replace(/\/$/, "");
2327
- this.timeout = options.timeout || DEFAULT_TIMEOUT4;
2526
+ this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2328
2527
  }
2329
2528
  /**
2330
2529
  * Generate a short video clip from a text prompt (or text + image).
@@ -2347,11 +2546,33 @@ var VideoClient = class {
2347
2546
  "realFaceAssetId must be a Token360 asset id matching 'ta_[A-Za-z0-9]+' (e.g. 'ta_abc123xyz')"
2348
2547
  );
2349
2548
  }
2549
+ if (options?.lastFrameUrl && !options?.imageUrl) {
2550
+ throw new Error(
2551
+ "lastFrameUrl requires imageUrl: imageUrl seeds the FIRST frame and lastFrameUrl the FINAL frame \u2014 send both."
2552
+ );
2553
+ }
2554
+ if (options?.lastFrameUrl && options?.realFaceAssetId) {
2555
+ throw new Error(
2556
+ "lastFrameUrl and realFaceAssetId are mutually exclusive; first-and-last-frame uses imageUrl + lastFrameUrl."
2557
+ );
2558
+ }
2559
+ if (options?.referenceImageUrls?.length) {
2560
+ if (options.imageUrl || options.lastFrameUrl || options.realFaceAssetId) {
2561
+ throw new Error(
2562
+ "referenceImageUrls is mutually exclusive with imageUrl, lastFrameUrl, and realFaceAssetId."
2563
+ );
2564
+ }
2565
+ if (options.referenceImageUrls.length > 9) {
2566
+ throw new Error("referenceImageUrls accepts at most 9 images.");
2567
+ }
2568
+ }
2350
2569
  const body = {
2351
- model: options?.model || DEFAULT_MODEL3,
2570
+ model: options?.model || DEFAULT_MODEL4,
2352
2571
  prompt
2353
2572
  };
2354
2573
  if (options?.imageUrl) body.image_url = options.imageUrl;
2574
+ if (options?.lastFrameUrl) body.last_frame_url = options.lastFrameUrl;
2575
+ if (options?.referenceImageUrls?.length) body.reference_image_urls = options.referenceImageUrls;
2355
2576
  if (options?.realFaceAssetId) body.real_face_asset_id = options.realFaceAssetId;
2356
2577
  if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2357
2578
  if (options?.aspectRatio) body.aspect_ratio = options.aspectRatio;
@@ -2506,9 +2727,9 @@ var VideoClient = class {
2506
2727
  };
2507
2728
 
2508
2729
  // src/voice.ts
2509
- import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
2510
- var DEFAULT_API_URL5 = "https://blockrun.ai/api";
2511
- var DEFAULT_TIMEOUT5 = 6e4;
2730
+ import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
2731
+ var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2732
+ var DEFAULT_TIMEOUT6 = 6e4;
2512
2733
  var CALL_PRICE_USD = 0.54;
2513
2734
  var VALID_MODELS = /* @__PURE__ */ new Set(["base", "enhanced", "turbo"]);
2514
2735
  var VoiceClient = class {
@@ -2528,11 +2749,11 @@ var VoiceClient = class {
2528
2749
  }
2529
2750
  validatePrivateKey(privateKey);
2530
2751
  this.privateKey = privateKey;
2531
- this.account = privateKeyToAccount5(privateKey);
2532
- const apiUrl = options.apiUrl || DEFAULT_API_URL5;
2752
+ this.account = privateKeyToAccount6(privateKey);
2753
+ const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2533
2754
  validateApiUrl(apiUrl);
2534
2755
  this.apiUrl = apiUrl.replace(/\/$/, "");
2535
- this.timeout = options.timeout || DEFAULT_TIMEOUT5;
2756
+ this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2536
2757
  }
2537
2758
  /**
2538
2759
  * Initiate an AI-powered outbound phone call.
@@ -2707,9 +2928,9 @@ var VoiceClient = class {
2707
2928
  };
2708
2929
 
2709
2930
  // src/phone.ts
2710
- import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
2711
- var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2712
- var DEFAULT_TIMEOUT6 = 6e4;
2931
+ import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
2932
+ var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2933
+ var DEFAULT_TIMEOUT7 = 6e4;
2713
2934
  var PHONE_PRICES = Object.freeze({
2714
2935
  lookup: 0.01,
2715
2936
  "lookup/fraud": 0.05,
@@ -2747,11 +2968,11 @@ var PhoneClient = class {
2747
2968
  }
2748
2969
  validatePrivateKey(privateKey);
2749
2970
  this.privateKey = privateKey;
2750
- this.account = privateKeyToAccount6(privateKey);
2751
- const apiUrl = options.apiUrl || DEFAULT_API_URL6;
2971
+ this.account = privateKeyToAccount7(privateKey);
2972
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
2752
2973
  validateApiUrl(apiUrl);
2753
2974
  this.apiUrl = apiUrl.replace(/\/$/, "");
2754
- this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2975
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
2755
2976
  }
2756
2977
  /** EVM wallet address used for payments. */
2757
2978
  getWalletAddress() {
@@ -2926,9 +3147,9 @@ var PhoneClient = class {
2926
3147
  };
2927
3148
 
2928
3149
  // src/portrait.ts
2929
- import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
2930
- var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2931
- var DEFAULT_TIMEOUT7 = 6e4;
3150
+ import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
3151
+ var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3152
+ var DEFAULT_TIMEOUT8 = 6e4;
2932
3153
  var PORTRAIT_ENROLLMENT_PRICE_USD = 0.01;
2933
3154
  var PortraitClient = class {
2934
3155
  account;
@@ -2947,11 +3168,11 @@ var PortraitClient = class {
2947
3168
  }
2948
3169
  validatePrivateKey(privateKey);
2949
3170
  this.privateKey = privateKey;
2950
- this.account = privateKeyToAccount7(privateKey);
2951
- const apiUrl = options.apiUrl || DEFAULT_API_URL7;
3171
+ this.account = privateKeyToAccount8(privateKey);
3172
+ const apiUrl = options.apiUrl || DEFAULT_API_URL8;
2952
3173
  validateApiUrl(apiUrl);
2953
3174
  this.apiUrl = apiUrl.replace(/\/$/, "");
2954
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3175
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
2955
3176
  }
2956
3177
  /** EVM wallet address used for payments. */
2957
3178
  getWalletAddress() {
@@ -3080,9 +3301,9 @@ var PortraitClient = class {
3080
3301
  };
3081
3302
 
3082
3303
  // src/search.ts
3083
- import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
3084
- var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3085
- var DEFAULT_TIMEOUT8 = 6e4;
3304
+ import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
3305
+ var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3306
+ var DEFAULT_TIMEOUT9 = 6e4;
3086
3307
  var SearchClient = class {
3087
3308
  account;
3088
3309
  privateKey;
@@ -3098,11 +3319,11 @@ var SearchClient = class {
3098
3319
  }
3099
3320
  validatePrivateKey(privateKey);
3100
3321
  this.privateKey = privateKey;
3101
- this.account = privateKeyToAccount8(privateKey);
3102
- const apiUrl = options.apiUrl || DEFAULT_API_URL8;
3322
+ this.account = privateKeyToAccount9(privateKey);
3323
+ const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3103
3324
  validateApiUrl(apiUrl);
3104
3325
  this.apiUrl = apiUrl.replace(/\/$/, "");
3105
- this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3326
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3106
3327
  }
3107
3328
  async search(query, options) {
3108
3329
  if (!query || query.length > 1e3) {
@@ -3216,9 +3437,9 @@ var SearchClient = class {
3216
3437
  };
3217
3438
 
3218
3439
  // src/x-client.ts
3219
- import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
3220
- var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3221
- var DEFAULT_TIMEOUT9 = 6e4;
3440
+ import { privateKeyToAccount as privateKeyToAccount10 } from "viem/accounts";
3441
+ var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3442
+ var DEFAULT_TIMEOUT10 = 6e4;
3222
3443
  var XClient = class _XClient {
3223
3444
  account;
3224
3445
  privateKey;
@@ -3241,11 +3462,11 @@ var XClient = class _XClient {
3241
3462
  }
3242
3463
  validatePrivateKey(privateKey);
3243
3464
  this.privateKey = privateKey;
3244
- this.account = privateKeyToAccount9(privateKey);
3245
- const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3465
+ this.account = privateKeyToAccount10(privateKey);
3466
+ const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3246
3467
  validateApiUrl(apiUrl);
3247
3468
  this.apiUrl = apiUrl.replace(/\/$/, "");
3248
- this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3469
+ this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3249
3470
  }
3250
3471
  // ───────── User endpoints ─────────
3251
3472
  userLookup(usernames) {
@@ -3423,9 +3644,9 @@ var XClient = class _XClient {
3423
3644
  };
3424
3645
 
3425
3646
  // src/price.ts
3426
- import { privateKeyToAccount as privateKeyToAccount10 } from "viem/accounts";
3427
- var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3428
- var DEFAULT_TIMEOUT10 = 3e4;
3647
+ import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
3648
+ var DEFAULT_API_URL11 = "https://blockrun.ai/api";
3649
+ var DEFAULT_TIMEOUT11 = 3e4;
3429
3650
  var PriceClient = class {
3430
3651
  account = null;
3431
3652
  privateKey = null;
@@ -3443,12 +3664,12 @@ var PriceClient = class {
3443
3664
  if (privateKey) {
3444
3665
  validatePrivateKey(privateKey);
3445
3666
  this.privateKey = privateKey;
3446
- this.account = privateKeyToAccount10(privateKey);
3667
+ this.account = privateKeyToAccount11(privateKey);
3447
3668
  }
3448
- const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3669
+ const apiUrl = options.apiUrl || DEFAULT_API_URL11;
3449
3670
  validateApiUrl(apiUrl);
3450
3671
  this.apiUrl = apiUrl.replace(/\/$/, "");
3451
- this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3672
+ this.timeout = options.timeout || DEFAULT_TIMEOUT11;
3452
3673
  }
3453
3674
  async price(category, symbol, options) {
3454
3675
  if (!symbol) throw new Error("symbol is required");
@@ -3627,9 +3848,9 @@ function buildUrl(base, query) {
3627
3848
  }
3628
3849
 
3629
3850
  // src/surf.ts
3630
- import { privateKeyToAccount as privateKeyToAccount11 } from "viem/accounts";
3631
- var DEFAULT_API_URL11 = "https://blockrun.ai/api";
3632
- var DEFAULT_TIMEOUT11 = 6e4;
3851
+ import { privateKeyToAccount as privateKeyToAccount12 } from "viem/accounts";
3852
+ var DEFAULT_API_URL12 = "https://blockrun.ai/api";
3853
+ var DEFAULT_TIMEOUT12 = 6e4;
3633
3854
  var SURF_PREFIX = "/v1/surf";
3634
3855
  var SurfClient = class {
3635
3856
  account;
@@ -3646,11 +3867,11 @@ var SurfClient = class {
3646
3867
  }
3647
3868
  validatePrivateKey(privateKey);
3648
3869
  this.privateKey = privateKey;
3649
- this.account = privateKeyToAccount11(privateKey);
3650
- const apiUrl = options.apiUrl || DEFAULT_API_URL11;
3870
+ this.account = privateKeyToAccount12(privateKey);
3871
+ const apiUrl = options.apiUrl || DEFAULT_API_URL12;
3651
3872
  validateApiUrl(apiUrl);
3652
3873
  this.apiUrl = apiUrl.replace(/\/$/, "");
3653
- this.timeout = options.timeout || DEFAULT_TIMEOUT11;
3874
+ this.timeout = options.timeout || DEFAULT_TIMEOUT12;
3654
3875
  }
3655
3876
  /**
3656
3877
  * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
@@ -3781,10 +4002,263 @@ var SurfClient = class {
3781
4002
  }
3782
4003
  };
3783
4004
 
4005
+ // src/rpc.ts
4006
+ import { privateKeyToAccount as privateKeyToAccount13 } from "viem/accounts";
4007
+ var DEFAULT_API_URL13 = "https://blockrun.ai/api";
4008
+ var DEFAULT_TIMEOUT13 = 6e4;
4009
+ var RPC_PRICE_USD = 2e-3;
4010
+ var SUPPORTED_NETWORKS = [
4011
+ // EVM
4012
+ "ethereum",
4013
+ "base",
4014
+ "arbitrum",
4015
+ "arbitrum-nova",
4016
+ "optimism",
4017
+ "polygon",
4018
+ "bsc",
4019
+ "avalanche",
4020
+ "fantom",
4021
+ "cronos",
4022
+ "celo",
4023
+ "gnosis",
4024
+ "zksync",
4025
+ "berachain",
4026
+ "unichain",
4027
+ "monad",
4028
+ "chiliz",
4029
+ "moonbeam",
4030
+ "aurora",
4031
+ "flare",
4032
+ "oasis",
4033
+ "kaia",
4034
+ "sonic",
4035
+ "xdc",
4036
+ "abstract",
4037
+ "hyperevm",
4038
+ "plume",
4039
+ "ronin",
4040
+ "rootstock",
4041
+ // Non-EVM (JSON-RPC-compatible)
4042
+ "solana",
4043
+ "bitcoin",
4044
+ "litecoin",
4045
+ "dogecoin",
4046
+ "bitcoin-cash",
4047
+ "near",
4048
+ "sui",
4049
+ "ripple",
4050
+ "polkadot",
4051
+ "kusama",
4052
+ "zcash"
4053
+ ];
4054
+ var NETWORK_ALIASES = {
4055
+ eth: "ethereum",
4056
+ arb: "arbitrum",
4057
+ "arbitrum-one": "arbitrum",
4058
+ "arb-one": "arbitrum",
4059
+ "arb-nova": "arbitrum-nova",
4060
+ op: "optimism",
4061
+ matic: "polygon",
4062
+ pol: "polygon",
4063
+ bnb: "bsc",
4064
+ binance: "bsc",
4065
+ "binance-smart-chain": "bsc",
4066
+ avax: "avalanche",
4067
+ ftm: "fantom",
4068
+ bera: "berachain",
4069
+ klaytn: "kaia",
4070
+ chz: "chiliz",
4071
+ hyperliquid: "hyperevm",
4072
+ rsk: "rootstock",
4073
+ sol: "solana",
4074
+ btc: "bitcoin",
4075
+ ltc: "litecoin",
4076
+ doge: "dogecoin",
4077
+ bch: "bitcoin-cash",
4078
+ xrp: "ripple",
4079
+ xrpl: "ripple",
4080
+ dot: "polkadot",
4081
+ zec: "zcash"
4082
+ };
4083
+ var RpcClient = class {
4084
+ account;
4085
+ privateKey;
4086
+ apiUrl;
4087
+ timeout;
4088
+ sessionTotalUsd = 0;
4089
+ sessionCalls = 0;
4090
+ constructor(options = {}) {
4091
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4092
+ const privateKey = options.privateKey || envKey;
4093
+ if (!privateKey) {
4094
+ throw new Error(
4095
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
4096
+ );
4097
+ }
4098
+ validatePrivateKey(privateKey);
4099
+ this.privateKey = privateKey;
4100
+ this.account = privateKeyToAccount13(privateKey);
4101
+ const apiUrl = options.apiUrl || DEFAULT_API_URL13;
4102
+ validateApiUrl(apiUrl);
4103
+ this.apiUrl = apiUrl.replace(/\/$/, "");
4104
+ this.timeout = options.timeout || DEFAULT_TIMEOUT13;
4105
+ }
4106
+ /**
4107
+ * Make a single JSON-RPC 2.0 call. Flat $0.002.
4108
+ *
4109
+ * @param network - Chain name (e.g. "ethereum", "base", "solana") or a
4110
+ * common alias ("eth", "sol", "matic", ...). See
4111
+ * SUPPORTED_NETWORKS / NETWORK_ALIASES.
4112
+ * @param method - Chain RPC method, e.g. "eth_blockNumber", "eth_call",
4113
+ * "eth_getBalance" (EVM) or "getSlot", "getAccountInfo"
4114
+ * (Solana).
4115
+ * @param params - Method-specific params array (optional).
4116
+ * @returns RpcResponse with `result` (or JSON-RPC `error`), plus
4117
+ * `network`, `cacheHit` and `txHash` metadata.
4118
+ *
4119
+ * @example
4120
+ * const block = await client.call('ethereum', 'eth_blockNumber');
4121
+ * console.log(parseInt(block.result as string, 16));
4122
+ */
4123
+ async call(network, method, params) {
4124
+ const body = { jsonrpc: "2.0", id: 1, method };
4125
+ if (params !== void 0) body.params = params;
4126
+ const { data, headers } = await this.requestWithPayment(network, body);
4127
+ return this.toResponse(data, headers);
4128
+ }
4129
+ /**
4130
+ * Make a JSON-RPC 2.0 batch call. Priced per element ($0.002 x N).
4131
+ *
4132
+ * @param network - Chain name or alias (see {@link call}).
4133
+ * @param requests - Requests, each with a `method` and optional
4134
+ * `params` / `id`. `jsonrpc` and missing ids are
4135
+ * filled in automatically.
4136
+ * @returns Array of RpcResponse, in upstream order.
4137
+ *
4138
+ * @example
4139
+ * const out = await client.batch('base', [
4140
+ * { method: 'eth_blockNumber' },
4141
+ * { method: 'eth_gasPrice' },
4142
+ * ]);
4143
+ */
4144
+ async batch(network, requests) {
4145
+ if (!requests.length) {
4146
+ throw new Error("batch requires at least one request");
4147
+ }
4148
+ const body = requests.map((req, i) => {
4149
+ if (!req.method) throw new Error(`batch request ${i} is missing 'method'`);
4150
+ return { jsonrpc: "2.0", id: i + 1, ...req };
4151
+ });
4152
+ const { data, headers } = await this.requestWithPayment(network, body);
4153
+ const items = Array.isArray(data) ? data : [data];
4154
+ return items.map((item) => this.toResponse(item, headers));
4155
+ }
4156
+ toResponse(data, headers) {
4157
+ const base = data !== null && typeof data === "object" && !Array.isArray(data) ? { ...data } : { result: data };
4158
+ const network = headers.get("x-network");
4159
+ if (network) base.network = network;
4160
+ base.cacheHit = (headers.get("x-cache") || "").toUpperCase() === "HIT";
4161
+ const txHash = headers.get("x-payment-receipt") || headers.get("X-Payment-Receipt");
4162
+ if (txHash) base.txHash = txHash;
4163
+ return base;
4164
+ }
4165
+ async requestWithPayment(network, body) {
4166
+ const endpoint = `/v1/rpc/${network}`;
4167
+ const url = `${this.apiUrl}${endpoint}`;
4168
+ const response = await this.fetchWithTimeout(url, {
4169
+ method: "POST",
4170
+ headers: { "Content-Type": "application/json" },
4171
+ body: JSON.stringify(body)
4172
+ });
4173
+ if (response.status === 402) {
4174
+ return this.handlePaymentAndRetry(url, endpoint, body, response);
4175
+ }
4176
+ if (!response.ok) {
4177
+ let errorBody;
4178
+ try {
4179
+ errorBody = await response.json();
4180
+ } catch {
4181
+ errorBody = { error: "Request failed" };
4182
+ }
4183
+ throw new APIError(`API error: ${response.status}`, response.status, sanitizeErrorResponse(errorBody));
4184
+ }
4185
+ return { data: await response.json(), headers: response.headers };
4186
+ }
4187
+ async handlePaymentAndRetry(url, endpoint, body, response) {
4188
+ let paymentHeader = response.headers.get("payment-required");
4189
+ if (!paymentHeader) {
4190
+ try {
4191
+ const respBody = await response.json();
4192
+ if (respBody.x402 || respBody.accepts) {
4193
+ paymentHeader = btoa(JSON.stringify(respBody));
4194
+ }
4195
+ } catch {
4196
+ }
4197
+ }
4198
+ if (!paymentHeader) {
4199
+ throw new PaymentError("402 response but no payment requirements found");
4200
+ }
4201
+ const paymentRequired = parsePaymentRequired(paymentHeader);
4202
+ const details = extractPaymentDetails(paymentRequired);
4203
+ const paymentPayload = await createPaymentPayload(
4204
+ this.privateKey,
4205
+ this.account.address,
4206
+ details.recipient,
4207
+ details.amount,
4208
+ details.network || "eip155:8453",
4209
+ {
4210
+ resourceUrl: details.resource?.url || `${this.apiUrl}${endpoint}`,
4211
+ resourceDescription: details.resource?.description || "BlockRun Multi-chain RPC",
4212
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
4213
+ extra: details.extra
4214
+ }
4215
+ );
4216
+ const retryResponse = await this.fetchWithTimeout(url, {
4217
+ method: "POST",
4218
+ headers: {
4219
+ "Content-Type": "application/json",
4220
+ "PAYMENT-SIGNATURE": paymentPayload
4221
+ },
4222
+ body: JSON.stringify(body)
4223
+ });
4224
+ if (retryResponse.status === 402) {
4225
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
4226
+ }
4227
+ if (!retryResponse.ok) {
4228
+ let errorBody;
4229
+ try {
4230
+ errorBody = await retryResponse.json();
4231
+ } catch {
4232
+ errorBody = { error: "Request failed" };
4233
+ }
4234
+ throw new APIError(`API error after payment: ${retryResponse.status}`, retryResponse.status, sanitizeErrorResponse(errorBody));
4235
+ }
4236
+ this.sessionCalls++;
4237
+ const paidUsd = Number(details.amount) / 1e6;
4238
+ if (Number.isFinite(paidUsd)) this.sessionTotalUsd += paidUsd;
4239
+ return { data: await retryResponse.json(), headers: retryResponse.headers };
4240
+ }
4241
+ async fetchWithTimeout(url, options) {
4242
+ const controller = new AbortController();
4243
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
4244
+ try {
4245
+ return await fetch(url, { ...options, signal: controller.signal });
4246
+ } finally {
4247
+ clearTimeout(timeoutId);
4248
+ }
4249
+ }
4250
+ getWalletAddress() {
4251
+ return this.account.address;
4252
+ }
4253
+ getSpending() {
4254
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
4255
+ }
4256
+ };
4257
+
3784
4258
  // src/blockrun.ts
3785
- import { privateKeyToAccount as privateKeyToAccount12 } from "viem/accounts";
3786
- var DEFAULT_API_URL12 = "https://blockrun.ai/api";
3787
- var DEFAULT_TIMEOUT12 = 6e4;
4259
+ import { privateKeyToAccount as privateKeyToAccount14 } from "viem/accounts";
4260
+ var DEFAULT_API_URL14 = "https://blockrun.ai/api";
4261
+ var DEFAULT_TIMEOUT14 = 6e4;
3788
4262
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
3789
4263
  var DEFAULT_POLL_BUDGET_MS = 3e5;
3790
4264
  var MAX_SIGNED_AUTH_SECONDS = 600;
@@ -3805,11 +4279,11 @@ var BlockrunClient = class {
3805
4279
  }
3806
4280
  validatePrivateKey(privateKey);
3807
4281
  this.privateKey = privateKey;
3808
- this.account = privateKeyToAccount12(privateKey);
3809
- const apiUrl = options.apiUrl || DEFAULT_API_URL12;
4282
+ this.account = privateKeyToAccount14(privateKey);
4283
+ const apiUrl = options.apiUrl || DEFAULT_API_URL14;
3810
4284
  validateApiUrl(apiUrl);
3811
4285
  this.apiUrl = apiUrl.replace(/\/$/, "");
3812
- this.timeout = options.timeout || DEFAULT_TIMEOUT12;
4286
+ this.timeout = options.timeout || DEFAULT_TIMEOUT14;
3813
4287
  }
3814
4288
  /**
3815
4289
  * GET a BlockRun endpoint. `path` is everything after `/api` (a leading
@@ -4160,7 +4634,7 @@ function sleep(ms) {
4160
4634
  }
4161
4635
 
4162
4636
  // src/wallet.ts
4163
- import { privateKeyToAccount as privateKeyToAccount13, generatePrivateKey } from "viem/accounts";
4637
+ import { privateKeyToAccount as privateKeyToAccount15, generatePrivateKey } from "viem/accounts";
4164
4638
  import * as fs2 from "fs";
4165
4639
  import * as path2 from "path";
4166
4640
  import * as os2 from "os";
@@ -4170,7 +4644,7 @@ var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
4170
4644
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
4171
4645
  function createWallet() {
4172
4646
  const privateKey = generatePrivateKey();
4173
- const account = privateKeyToAccount13(privateKey);
4647
+ const account = privateKeyToAccount15(privateKey);
4174
4648
  return {
4175
4649
  address: account.address,
4176
4650
  privateKey
@@ -4226,12 +4700,12 @@ function loadWallet() {
4226
4700
  function getOrCreateWallet() {
4227
4701
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4228
4702
  if (envKey) {
4229
- const account = privateKeyToAccount13(envKey);
4703
+ const account = privateKeyToAccount15(envKey);
4230
4704
  return { address: account.address, privateKey: envKey, isNew: false };
4231
4705
  }
4232
4706
  const fileKey = loadWallet();
4233
4707
  if (fileKey) {
4234
- const account = privateKeyToAccount13(fileKey);
4708
+ const account = privateKeyToAccount15(fileKey);
4235
4709
  return { address: account.address, privateKey: fileKey, isNew: false };
4236
4710
  }
4237
4711
  const { address, privateKey } = createWallet();
@@ -4241,11 +4715,11 @@ function getOrCreateWallet() {
4241
4715
  function getWalletAddress() {
4242
4716
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4243
4717
  if (envKey) {
4244
- return privateKeyToAccount13(envKey).address;
4718
+ return privateKeyToAccount15(envKey).address;
4245
4719
  }
4246
4720
  const fileKey = loadWallet();
4247
4721
  if (fileKey) {
4248
- return privateKeyToAccount13(fileKey).address;
4722
+ return privateKeyToAccount15(fileKey).address;
4249
4723
  }
4250
4724
  return null;
4251
4725
  }
@@ -4425,7 +4899,7 @@ async function getOrCreateSolanaWallet() {
4425
4899
  // src/solana-client.ts
4426
4900
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
4427
4901
  var DEFAULT_MAX_TOKENS2 = 1024;
4428
- var DEFAULT_TIMEOUT13 = 6e4;
4902
+ var DEFAULT_TIMEOUT15 = 6e4;
4429
4903
  var SDK_VERSION2 = "0.3.0";
4430
4904
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
4431
4905
  var DEFAULT_SOLANA_RPC_URL = "https://sol.blockrun.ai/api/v1/solana/rpc";
@@ -4475,7 +4949,7 @@ var SolanaLLMClient = class {
4475
4949
  const rpc = resolveRpcConfig(options.rpcUrl, options.rpcHeaders);
4476
4950
  this.rpcUrl = rpc.url;
4477
4951
  this.rpcHeaders = rpc.headers;
4478
- this.timeout = options.timeout || DEFAULT_TIMEOUT13;
4952
+ this.timeout = options.timeout || DEFAULT_TIMEOUT15;
4479
4953
  }
4480
4954
  /** Get Solana wallet address (public key in base58). */
4481
4955
  async getWalletAddress() {
@@ -5389,7 +5863,7 @@ var OpenAI = class {
5389
5863
  };
5390
5864
 
5391
5865
  // src/anthropic-compat.ts
5392
- import { privateKeyToAccount as privateKeyToAccount14 } from "viem/accounts";
5866
+ import { privateKeyToAccount as privateKeyToAccount16 } from "viem/accounts";
5393
5867
  var AnthropicClient = class {
5394
5868
  _client = null;
5395
5869
  _clientPromise = null;
@@ -5402,7 +5876,7 @@ var AnthropicClient = class {
5402
5876
  const key = options.privateKey ?? wallet.privateKey;
5403
5877
  validatePrivateKey(key);
5404
5878
  this._privateKey = key;
5405
- this._account = privateKeyToAccount14(this._privateKey);
5879
+ this._account = privateKeyToAccount16(this._privateKey);
5406
5880
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
5407
5881
  validateApiUrl(apiUrl);
5408
5882
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -5506,6 +5980,7 @@ export {
5506
5980
  KNOWN_PROVIDERS,
5507
5981
  LLMClient,
5508
5982
  MusicClient,
5983
+ NETWORK_ALIASES,
5509
5984
  OpenAI,
5510
5985
  PHONE_PRICES,
5511
5986
  PORTRAIT_ENROLLMENT_PRICE_USD,
@@ -5513,10 +5988,14 @@ export {
5513
5988
  PhoneClient,
5514
5989
  PortraitClient,
5515
5990
  PriceClient,
5991
+ RPC_PRICE_USD,
5992
+ RpcClient,
5516
5993
  SOLANA_NETWORK,
5517
5994
  SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH,
5995
+ SUPPORTED_NETWORKS,
5518
5996
  SearchClient,
5519
5997
  SolanaLLMClient,
5998
+ SpeechClient,
5520
5999
  SurfClient,
5521
6000
  USDC_BASE,
5522
6001
  USDC_BASE_CONTRACT,