@blockrun/llm 2.5.0 → 2.8.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.cjs CHANGED
@@ -40,7 +40,11 @@ __export(index_exports, {
40
40
  LLMClient: () => LLMClient,
41
41
  MusicClient: () => MusicClient,
42
42
  OpenAI: () => OpenAI,
43
+ PHONE_PRICES: () => PHONE_PRICES,
44
+ PORTRAIT_ENROLLMENT_PRICE_USD: () => PORTRAIT_ENROLLMENT_PRICE_USD,
43
45
  PaymentError: () => PaymentError,
46
+ PhoneClient: () => PhoneClient,
47
+ PortraitClient: () => PortraitClient,
44
48
  PriceClient: () => PriceClient,
45
49
  SOLANA_NETWORK: () => SOLANA_NETWORK,
46
50
  SOLANA_WALLET_FILE_PATH: () => SOLANA_WALLET_FILE,
@@ -211,8 +215,8 @@ async function createSolanaPaymentPayload(secretKey, fromAddress, recipient, amo
211
215
  const { Connection, PublicKey, TransactionMessage, VersionedTransaction, ComputeBudgetProgram } = await import("@solana/web3.js");
212
216
  const { getAssociatedTokenAddress, createTransferCheckedInstruction, getMint } = await import("@solana/spl-token");
213
217
  const { Keypair } = await import("@solana/web3.js");
214
- const rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
215
- const connection = new Connection(rpcUrl);
218
+ const rpcUrl = options.rpcUrl || "https://sol.blockrun.ai/api/v1/solana/rpc";
219
+ const connection = options.rpcHeaders ? new Connection(rpcUrl, { httpHeaders: options.rpcHeaders }) : new Connection(rpcUrl);
216
220
  const keypair = Keypair.fromSecretKey(secretKey);
217
221
  const feePayerPubkey = new PublicKey(feePayer);
218
222
  const ownerPubkey = keypair.publicKey;
@@ -2409,11 +2413,22 @@ var VideoClient = class {
2409
2413
  * @param options - Optional generation parameters
2410
2414
  */
2411
2415
  async generate(prompt, options) {
2416
+ if (options?.imageUrl && options?.realFaceAssetId) {
2417
+ throw new Error(
2418
+ "imageUrl and realFaceAssetId are mutually exclusive; pass at most one."
2419
+ );
2420
+ }
2421
+ if (options?.realFaceAssetId && !/^ta_[A-Za-z0-9]+$/.test(options.realFaceAssetId)) {
2422
+ throw new Error(
2423
+ "realFaceAssetId must be a Token360 asset id matching 'ta_[A-Za-z0-9]+' (e.g. 'ta_abc123xyz')"
2424
+ );
2425
+ }
2412
2426
  const body = {
2413
2427
  model: options?.model || DEFAULT_MODEL3,
2414
2428
  prompt
2415
2429
  };
2416
2430
  if (options?.imageUrl) body.image_url = options.imageUrl;
2431
+ if (options?.realFaceAssetId) body.real_face_asset_id = options.realFaceAssetId;
2417
2432
  if (options?.durationSeconds !== void 0) body.duration_seconds = options.durationSeconds;
2418
2433
  if (options?.aspectRatio) body.aspect_ratio = options.aspectRatio;
2419
2434
  if (options?.resolution) body.resolution = options.resolution;
@@ -2767,15 +2782,37 @@ var VoiceClient = class {
2767
2782
  }
2768
2783
  };
2769
2784
 
2770
- // src/search.ts
2785
+ // src/phone.ts
2771
2786
  var import_accounts7 = require("viem/accounts");
2772
2787
  var DEFAULT_API_URL6 = "https://blockrun.ai/api";
2773
2788
  var DEFAULT_TIMEOUT6 = 6e4;
2774
- var SearchClient = class {
2789
+ var PHONE_PRICES = Object.freeze({
2790
+ lookup: 0.01,
2791
+ "lookup/fraud": 0.05,
2792
+ "numbers/buy": 5,
2793
+ "numbers/renew": 5,
2794
+ "numbers/list": 1e-3,
2795
+ "numbers/release": 0
2796
+ });
2797
+ function requireE164(value) {
2798
+ if (!value || typeof value !== "string") {
2799
+ throw new Error("phoneNumber is required (E.164 format, e.g. '+14155552671')");
2800
+ }
2801
+ const v = value.trim();
2802
+ if (!/^\+\d{7,15}$/.test(v)) {
2803
+ throw new Error(
2804
+ `phoneNumber must be E.164 (e.g. '+14155552671'), got ${JSON.stringify(value)}`
2805
+ );
2806
+ }
2807
+ return v;
2808
+ }
2809
+ var PhoneClient = class {
2775
2810
  account;
2776
2811
  privateKey;
2777
2812
  apiUrl;
2778
2813
  timeout;
2814
+ sessionTotalUsd = 0;
2815
+ sessionCalls = 0;
2779
2816
  constructor(options = {}) {
2780
2817
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
2781
2818
  const privateKey = options.privateKey || envKey;
@@ -2792,6 +2829,357 @@ var SearchClient = class {
2792
2829
  this.apiUrl = apiUrl.replace(/\/$/, "");
2793
2830
  this.timeout = options.timeout || DEFAULT_TIMEOUT6;
2794
2831
  }
2832
+ /** EVM wallet address used for payments. */
2833
+ getWalletAddress() {
2834
+ return this.account.address;
2835
+ }
2836
+ /** Session-cumulative USD spend + call count across this client instance. */
2837
+ getSpending() {
2838
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
2839
+ }
2840
+ // ─── Lookup ────────────────────────────────────────────────────────────
2841
+ /**
2842
+ * Carrier + line-type lookup. ~$0.01.
2843
+ *
2844
+ * @param phoneNumber E.164 number (e.g. "+14155552671").
2845
+ */
2846
+ async lookup(phoneNumber) {
2847
+ const num = requireE164(phoneNumber);
2848
+ return this.request("lookup", { phoneNumber: num });
2849
+ }
2850
+ /**
2851
+ * Lookup + fraud signals (SIM swap, call forwarding). ~$0.05.
2852
+ *
2853
+ * @param phoneNumber E.164 number.
2854
+ */
2855
+ async lookupFraud(phoneNumber) {
2856
+ const num = requireE164(phoneNumber);
2857
+ return this.request("lookup/fraud", { phoneNumber: num });
2858
+ }
2859
+ // ─── Provisioning ──────────────────────────────────────────────────────
2860
+ /**
2861
+ * Provision a dedicated phone number for 30 days. $5.00.
2862
+ *
2863
+ * Payment is settled only after Twilio confirms the purchase, so failed
2864
+ * buys never charge your wallet.
2865
+ *
2866
+ * @param options.country ISO country code, "US" or "CA" (default "US").
2867
+ * @param options.areaCode Optional 3-digit area-code hint. Availability is
2868
+ * not guaranteed — the backend falls back to any number in the country if
2869
+ * the area code can't be matched.
2870
+ */
2871
+ async buyNumber(options = {}) {
2872
+ const country = options.country ?? "US";
2873
+ if (country !== "US" && country !== "CA") {
2874
+ throw new Error("country must be 'US' or 'CA'");
2875
+ }
2876
+ const body = { country };
2877
+ if (options.areaCode !== void 0) {
2878
+ const ac = options.areaCode;
2879
+ if (typeof ac !== "string" || !/^\d{3}$/.test(ac)) {
2880
+ throw new Error("areaCode must be a 3-digit string, e.g. '415'");
2881
+ }
2882
+ body.areaCode = ac;
2883
+ }
2884
+ const data = await this.request("numbers/buy", body);
2885
+ return data;
2886
+ }
2887
+ /**
2888
+ * Extend an existing provisioned number by 30 days. $5.00.
2889
+ *
2890
+ * @throws {APIError} 403 when the wallet doesn't own the number or it has expired.
2891
+ */
2892
+ async renewNumber(phoneNumber) {
2893
+ const num = requireE164(phoneNumber);
2894
+ const data = await this.request("numbers/renew", {
2895
+ phoneNumber: num
2896
+ });
2897
+ return data;
2898
+ }
2899
+ /** List the wallet's active phone numbers. ~$0.001. */
2900
+ async listNumbers() {
2901
+ const data = await this.request("numbers/list", {});
2902
+ return data;
2903
+ }
2904
+ /**
2905
+ * Release a provisioned number back to the Twilio pool. Free, but still
2906
+ * flows through x402 so the backend can verify ownership.
2907
+ */
2908
+ async releaseNumber(phoneNumber) {
2909
+ const num = requireE164(phoneNumber);
2910
+ const data = await this.request("numbers/release", {
2911
+ phoneNumber: num
2912
+ });
2913
+ return data;
2914
+ }
2915
+ // ─── Internals ─────────────────────────────────────────────────────────
2916
+ async request(path5, body) {
2917
+ const url = `${this.apiUrl}/v1/phone/${path5}`;
2918
+ const response = await this.fetchWithTimeout(url, {
2919
+ method: "POST",
2920
+ headers: { "Content-Type": "application/json" },
2921
+ body: JSON.stringify(body)
2922
+ });
2923
+ if (response.status === 402) {
2924
+ return this.handlePaymentAndRetry(url, body, response);
2925
+ }
2926
+ return this.unwrap(response, false);
2927
+ }
2928
+ async handlePaymentAndRetry(url, body, response) {
2929
+ let paymentHeader = response.headers.get("payment-required");
2930
+ if (!paymentHeader) {
2931
+ try {
2932
+ const rb = await response.json();
2933
+ if (rb.x402 || rb.accepts) paymentHeader = btoa(JSON.stringify(rb));
2934
+ } catch {
2935
+ }
2936
+ }
2937
+ if (!paymentHeader) {
2938
+ throw new PaymentError("402 response but no payment requirements found");
2939
+ }
2940
+ const paymentRequired = parsePaymentRequired(paymentHeader);
2941
+ const details = extractPaymentDetails(paymentRequired);
2942
+ const paymentPayload = await createPaymentPayload(
2943
+ this.privateKey,
2944
+ this.account.address,
2945
+ details.recipient,
2946
+ details.amount,
2947
+ details.network || "eip155:8453",
2948
+ {
2949
+ resourceUrl: details.resource?.url || url,
2950
+ resourceDescription: details.resource?.description || "BlockRun Phone",
2951
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
2952
+ extra: details.extra
2953
+ }
2954
+ );
2955
+ const retry = await this.fetchWithTimeout(url, {
2956
+ method: "POST",
2957
+ headers: {
2958
+ "Content-Type": "application/json",
2959
+ "PAYMENT-SIGNATURE": paymentPayload
2960
+ },
2961
+ body: JSON.stringify(body)
2962
+ });
2963
+ if (retry.status === 402) {
2964
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
2965
+ }
2966
+ const data = await this.unwrap(retry, true);
2967
+ const costUsd = Number.parseFloat(details.amount) / 1e6;
2968
+ this.sessionCalls += 1;
2969
+ this.sessionTotalUsd += costUsd;
2970
+ const txHash = retry.headers.get("x-payment-receipt") || retry.headers.get("X-Payment-Receipt");
2971
+ if (txHash && data && typeof data === "object" && !("txHash" in data)) {
2972
+ data.txHash = txHash;
2973
+ }
2974
+ return data;
2975
+ }
2976
+ async unwrap(response, afterPayment) {
2977
+ if (response.ok) {
2978
+ return response.json();
2979
+ }
2980
+ let errorBody;
2981
+ try {
2982
+ errorBody = await response.json();
2983
+ } catch {
2984
+ errorBody = { error: "Request failed" };
2985
+ }
2986
+ const prefix = afterPayment ? "API error after payment" : "API error";
2987
+ throw new APIError(
2988
+ `${prefix}: ${response.status}`,
2989
+ response.status,
2990
+ sanitizeErrorResponse(errorBody)
2991
+ );
2992
+ }
2993
+ async fetchWithTimeout(url, init) {
2994
+ const controller = new AbortController();
2995
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2996
+ try {
2997
+ return await fetch(url, { ...init, signal: controller.signal });
2998
+ } finally {
2999
+ clearTimeout(timeoutId);
3000
+ }
3001
+ }
3002
+ };
3003
+
3004
+ // src/portrait.ts
3005
+ var import_accounts8 = require("viem/accounts");
3006
+ var DEFAULT_API_URL7 = "https://blockrun.ai/api";
3007
+ var DEFAULT_TIMEOUT7 = 6e4;
3008
+ var PORTRAIT_ENROLLMENT_PRICE_USD = 0.01;
3009
+ var PortraitClient = class {
3010
+ account;
3011
+ privateKey;
3012
+ apiUrl;
3013
+ timeout;
3014
+ sessionTotalUsd = 0;
3015
+ sessionCalls = 0;
3016
+ constructor(options = {}) {
3017
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3018
+ const privateKey = options.privateKey || envKey;
3019
+ if (!privateKey) {
3020
+ throw new Error(
3021
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
3022
+ );
3023
+ }
3024
+ validatePrivateKey(privateKey);
3025
+ this.privateKey = privateKey;
3026
+ this.account = (0, import_accounts8.privateKeyToAccount)(privateKey);
3027
+ const apiUrl = options.apiUrl || DEFAULT_API_URL7;
3028
+ validateApiUrl(apiUrl);
3029
+ this.apiUrl = apiUrl.replace(/\/$/, "");
3030
+ this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3031
+ }
3032
+ /** EVM wallet address used for payments. */
3033
+ getWalletAddress() {
3034
+ return this.account.address;
3035
+ }
3036
+ /** Session-cumulative USD spend + call count across this client instance. */
3037
+ getSpending() {
3038
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
3039
+ }
3040
+ /**
3041
+ * Enroll a Virtual Portrait from a face image URL. $0.01 (promo).
3042
+ *
3043
+ * Payment is settled only after Token360 confirms the enrollment, so failed
3044
+ * enrollments never charge your wallet.
3045
+ *
3046
+ * @param options.name Display name (1–64 chars).
3047
+ * @param options.imageUrl Public HTTPS URL to a JPG/PNG/WEBP image (≤10 MB).
3048
+ * @returns The enrollment record, including `asset_id` (`ta_xxxxxx`).
3049
+ */
3050
+ async enroll(options) {
3051
+ const name = typeof options?.name === "string" ? options.name.trim() : "";
3052
+ if (name.length < 1 || name.length > 64) {
3053
+ throw new Error("name is required and must be 1\u201364 characters");
3054
+ }
3055
+ const imageUrl = typeof options?.imageUrl === "string" ? options.imageUrl.trim() : "";
3056
+ if (!/^https?:\/\//i.test(imageUrl)) {
3057
+ throw new Error(
3058
+ "imageUrl must be a public http(s) URL to a JPG/PNG/WEBP image"
3059
+ );
3060
+ }
3061
+ const data = await this.request({
3062
+ name,
3063
+ image_url: imageUrl
3064
+ });
3065
+ return data;
3066
+ }
3067
+ // ─── Internals ─────────────────────────────────────────────────────────
3068
+ async request(body) {
3069
+ const url = `${this.apiUrl}/v1/portrait/enroll`;
3070
+ const response = await this.fetchWithTimeout(url, {
3071
+ method: "POST",
3072
+ headers: { "Content-Type": "application/json" },
3073
+ body: JSON.stringify(body)
3074
+ });
3075
+ if (response.status === 402) {
3076
+ return this.handlePaymentAndRetry(url, body, response);
3077
+ }
3078
+ return this.unwrap(response, false);
3079
+ }
3080
+ async handlePaymentAndRetry(url, body, response) {
3081
+ let paymentHeader = response.headers.get("payment-required");
3082
+ if (!paymentHeader) {
3083
+ try {
3084
+ const rb = await response.json();
3085
+ if (rb.x402 || rb.accepts) paymentHeader = btoa(JSON.stringify(rb));
3086
+ } catch {
3087
+ }
3088
+ }
3089
+ if (!paymentHeader) {
3090
+ throw new PaymentError("402 response but no payment requirements found");
3091
+ }
3092
+ const paymentRequired = parsePaymentRequired(paymentHeader);
3093
+ const details = extractPaymentDetails(paymentRequired);
3094
+ const paymentPayload = await createPaymentPayload(
3095
+ this.privateKey,
3096
+ this.account.address,
3097
+ details.recipient,
3098
+ details.amount,
3099
+ details.network || "eip155:8453",
3100
+ {
3101
+ resourceUrl: details.resource?.url || url,
3102
+ resourceDescription: details.resource?.description || "BlockRun Virtual Portrait",
3103
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
3104
+ extra: details.extra
3105
+ }
3106
+ );
3107
+ const retry = await this.fetchWithTimeout(url, {
3108
+ method: "POST",
3109
+ headers: {
3110
+ "Content-Type": "application/json",
3111
+ "PAYMENT-SIGNATURE": paymentPayload
3112
+ },
3113
+ body: JSON.stringify(body)
3114
+ });
3115
+ if (retry.status === 402) {
3116
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
3117
+ }
3118
+ const data = await this.unwrap(retry, true);
3119
+ const costUsd = Number.parseFloat(details.amount) / 1e6;
3120
+ this.sessionCalls += 1;
3121
+ this.sessionTotalUsd += costUsd;
3122
+ if (data && typeof data === "object" && !("txHash" in data)) {
3123
+ const settlement = data.settlement;
3124
+ if (settlement?.tx_hash) {
3125
+ data.txHash = settlement.tx_hash;
3126
+ }
3127
+ }
3128
+ return data;
3129
+ }
3130
+ async unwrap(response, afterPayment) {
3131
+ if (response.ok) {
3132
+ return response.json();
3133
+ }
3134
+ let errorBody;
3135
+ try {
3136
+ errorBody = await response.json();
3137
+ } catch {
3138
+ errorBody = { error: "Request failed" };
3139
+ }
3140
+ const prefix = afterPayment ? "API error after payment" : "API error";
3141
+ throw new APIError(
3142
+ `${prefix}: ${response.status}`,
3143
+ response.status,
3144
+ sanitizeErrorResponse(errorBody)
3145
+ );
3146
+ }
3147
+ async fetchWithTimeout(url, init) {
3148
+ const controller = new AbortController();
3149
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3150
+ try {
3151
+ return await fetch(url, { ...init, signal: controller.signal });
3152
+ } finally {
3153
+ clearTimeout(timeoutId);
3154
+ }
3155
+ }
3156
+ };
3157
+
3158
+ // src/search.ts
3159
+ var import_accounts9 = require("viem/accounts");
3160
+ var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3161
+ var DEFAULT_TIMEOUT8 = 6e4;
3162
+ var SearchClient = class {
3163
+ account;
3164
+ privateKey;
3165
+ apiUrl;
3166
+ timeout;
3167
+ constructor(options = {}) {
3168
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3169
+ const privateKey = options.privateKey || envKey;
3170
+ if (!privateKey) {
3171
+ throw new Error(
3172
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
3173
+ );
3174
+ }
3175
+ validatePrivateKey(privateKey);
3176
+ this.privateKey = privateKey;
3177
+ this.account = (0, import_accounts9.privateKeyToAccount)(privateKey);
3178
+ const apiUrl = options.apiUrl || DEFAULT_API_URL8;
3179
+ validateApiUrl(apiUrl);
3180
+ this.apiUrl = apiUrl.replace(/\/$/, "");
3181
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3182
+ }
2795
3183
  async search(query, options) {
2796
3184
  if (!query || query.length > 1e3) {
2797
3185
  throw new Error("query must be 1-1000 characters");
@@ -2904,9 +3292,9 @@ var SearchClient = class {
2904
3292
  };
2905
3293
 
2906
3294
  // src/x-client.ts
2907
- var import_accounts8 = require("viem/accounts");
2908
- var DEFAULT_API_URL7 = "https://blockrun.ai/api";
2909
- var DEFAULT_TIMEOUT7 = 6e4;
3295
+ var import_accounts10 = require("viem/accounts");
3296
+ var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3297
+ var DEFAULT_TIMEOUT9 = 6e4;
2910
3298
  var XClient = class _XClient {
2911
3299
  account;
2912
3300
  privateKey;
@@ -2929,11 +3317,11 @@ var XClient = class _XClient {
2929
3317
  }
2930
3318
  validatePrivateKey(privateKey);
2931
3319
  this.privateKey = privateKey;
2932
- this.account = (0, import_accounts8.privateKeyToAccount)(privateKey);
2933
- const apiUrl = options.apiUrl || DEFAULT_API_URL7;
3320
+ this.account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3321
+ const apiUrl = options.apiUrl || DEFAULT_API_URL9;
2934
3322
  validateApiUrl(apiUrl);
2935
3323
  this.apiUrl = apiUrl.replace(/\/$/, "");
2936
- this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3324
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
2937
3325
  }
2938
3326
  // ───────── User endpoints ─────────
2939
3327
  userLookup(usernames) {
@@ -3111,9 +3499,9 @@ var XClient = class _XClient {
3111
3499
  };
3112
3500
 
3113
3501
  // src/price.ts
3114
- var import_accounts9 = require("viem/accounts");
3115
- var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3116
- var DEFAULT_TIMEOUT8 = 3e4;
3502
+ var import_accounts11 = require("viem/accounts");
3503
+ var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3504
+ var DEFAULT_TIMEOUT10 = 3e4;
3117
3505
  var PriceClient = class {
3118
3506
  account = null;
3119
3507
  privateKey = null;
@@ -3131,12 +3519,12 @@ var PriceClient = class {
3131
3519
  if (privateKey) {
3132
3520
  validatePrivateKey(privateKey);
3133
3521
  this.privateKey = privateKey;
3134
- this.account = (0, import_accounts9.privateKeyToAccount)(privateKey);
3522
+ this.account = (0, import_accounts11.privateKeyToAccount)(privateKey);
3135
3523
  }
3136
- const apiUrl = options.apiUrl || DEFAULT_API_URL8;
3524
+ const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3137
3525
  validateApiUrl(apiUrl);
3138
3526
  this.apiUrl = apiUrl.replace(/\/$/, "");
3139
- this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3527
+ this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3140
3528
  }
3141
3529
  async price(category, symbol, options) {
3142
3530
  if (!symbol) throw new Error("symbol is required");
@@ -3315,9 +3703,9 @@ function buildUrl(base, query) {
3315
3703
  }
3316
3704
 
3317
3705
  // src/surf.ts
3318
- var import_accounts10 = require("viem/accounts");
3319
- var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3320
- var DEFAULT_TIMEOUT9 = 6e4;
3706
+ var import_accounts12 = require("viem/accounts");
3707
+ var DEFAULT_API_URL11 = "https://blockrun.ai/api";
3708
+ var DEFAULT_TIMEOUT11 = 6e4;
3321
3709
  var SURF_PREFIX = "/v1/surf";
3322
3710
  var SurfClient = class {
3323
3711
  account;
@@ -3334,11 +3722,11 @@ var SurfClient = class {
3334
3722
  }
3335
3723
  validatePrivateKey(privateKey);
3336
3724
  this.privateKey = privateKey;
3337
- this.account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3338
- const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3725
+ this.account = (0, import_accounts12.privateKeyToAccount)(privateKey);
3726
+ const apiUrl = options.apiUrl || DEFAULT_API_URL11;
3339
3727
  validateApiUrl(apiUrl);
3340
3728
  this.apiUrl = apiUrl.replace(/\/$/, "");
3341
- this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3729
+ this.timeout = options.timeout || DEFAULT_TIMEOUT11;
3342
3730
  }
3343
3731
  /**
3344
3732
  * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
@@ -3470,9 +3858,9 @@ var SurfClient = class {
3470
3858
  };
3471
3859
 
3472
3860
  // src/blockrun.ts
3473
- var import_accounts11 = require("viem/accounts");
3474
- var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3475
- var DEFAULT_TIMEOUT10 = 6e4;
3861
+ var import_accounts13 = require("viem/accounts");
3862
+ var DEFAULT_API_URL12 = "https://blockrun.ai/api";
3863
+ var DEFAULT_TIMEOUT12 = 6e4;
3476
3864
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
3477
3865
  var DEFAULT_POLL_BUDGET_MS = 3e5;
3478
3866
  var MAX_SIGNED_AUTH_SECONDS = 600;
@@ -3493,11 +3881,11 @@ var BlockrunClient = class {
3493
3881
  }
3494
3882
  validatePrivateKey(privateKey);
3495
3883
  this.privateKey = privateKey;
3496
- this.account = (0, import_accounts11.privateKeyToAccount)(privateKey);
3497
- const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3884
+ this.account = (0, import_accounts13.privateKeyToAccount)(privateKey);
3885
+ const apiUrl = options.apiUrl || DEFAULT_API_URL12;
3498
3886
  validateApiUrl(apiUrl);
3499
3887
  this.apiUrl = apiUrl.replace(/\/$/, "");
3500
- this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3888
+ this.timeout = options.timeout || DEFAULT_TIMEOUT12;
3501
3889
  }
3502
3890
  /**
3503
3891
  * GET a BlockRun endpoint. `path` is everything after `/api` (a leading
@@ -3848,7 +4236,7 @@ function sleep(ms) {
3848
4236
  }
3849
4237
 
3850
4238
  // src/wallet.ts
3851
- var import_accounts12 = require("viem/accounts");
4239
+ var import_accounts14 = require("viem/accounts");
3852
4240
  var fs2 = __toESM(require("fs"), 1);
3853
4241
  var path2 = __toESM(require("path"), 1);
3854
4242
  var os2 = __toESM(require("os"), 1);
@@ -3857,8 +4245,8 @@ var BASE_CHAIN_ID2 = "8453";
3857
4245
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
3858
4246
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
3859
4247
  function createWallet() {
3860
- const privateKey = (0, import_accounts12.generatePrivateKey)();
3861
- const account = (0, import_accounts12.privateKeyToAccount)(privateKey);
4248
+ const privateKey = (0, import_accounts14.generatePrivateKey)();
4249
+ const account = (0, import_accounts14.privateKeyToAccount)(privateKey);
3862
4250
  return {
3863
4251
  address: account.address,
3864
4252
  privateKey
@@ -3914,12 +4302,12 @@ function loadWallet() {
3914
4302
  function getOrCreateWallet() {
3915
4303
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3916
4304
  if (envKey) {
3917
- const account = (0, import_accounts12.privateKeyToAccount)(envKey);
4305
+ const account = (0, import_accounts14.privateKeyToAccount)(envKey);
3918
4306
  return { address: account.address, privateKey: envKey, isNew: false };
3919
4307
  }
3920
4308
  const fileKey = loadWallet();
3921
4309
  if (fileKey) {
3922
- const account = (0, import_accounts12.privateKeyToAccount)(fileKey);
4310
+ const account = (0, import_accounts14.privateKeyToAccount)(fileKey);
3923
4311
  return { address: account.address, privateKey: fileKey, isNew: false };
3924
4312
  }
3925
4313
  const { address, privateKey } = createWallet();
@@ -3929,11 +4317,11 @@ function getOrCreateWallet() {
3929
4317
  function getWalletAddress() {
3930
4318
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3931
4319
  if (envKey) {
3932
- return (0, import_accounts12.privateKeyToAccount)(envKey).address;
4320
+ return (0, import_accounts14.privateKeyToAccount)(envKey).address;
3933
4321
  }
3934
4322
  const fileKey = loadWallet();
3935
4323
  if (fileKey) {
3936
- return (0, import_accounts12.privateKeyToAccount)(fileKey).address;
4324
+ return (0, import_accounts14.privateKeyToAccount)(fileKey).address;
3937
4325
  }
3938
4326
  return null;
3939
4327
  }
@@ -4113,14 +4501,37 @@ async function getOrCreateSolanaWallet() {
4113
4501
  // src/solana-client.ts
4114
4502
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
4115
4503
  var DEFAULT_MAX_TOKENS2 = 1024;
4116
- var DEFAULT_TIMEOUT11 = 6e4;
4504
+ var DEFAULT_TIMEOUT13 = 6e4;
4117
4505
  var SDK_VERSION2 = "0.3.0";
4118
4506
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
4507
+ var DEFAULT_SOLANA_RPC_URL = "https://sol.blockrun.ai/api/v1/solana/rpc";
4508
+ function resolveRpcConfig(rpcUrl, rpcHeaders) {
4509
+ const env = typeof process !== "undefined" && process.env ? process.env : {};
4510
+ const url = rpcUrl || env.SOLANA_RPC_URL || DEFAULT_SOLANA_RPC_URL;
4511
+ let headers;
4512
+ if (rpcHeaders) {
4513
+ headers = { ...rpcHeaders };
4514
+ } else if (env.SOLANA_RPC_HEADERS) {
4515
+ try {
4516
+ const parsed = JSON.parse(env.SOLANA_RPC_HEADERS);
4517
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4518
+ headers = Object.fromEntries(
4519
+ Object.entries(parsed).map(([k, v]) => [String(k), String(v)])
4520
+ );
4521
+ }
4522
+ } catch {
4523
+ }
4524
+ } else if (env.SOLANA_RPC_API_KEY) {
4525
+ headers = { "x-api-key": env.SOLANA_RPC_API_KEY };
4526
+ }
4527
+ return headers ? { url, headers } : { url };
4528
+ }
4119
4529
  var SolanaLLMClient = class {
4120
4530
  static SOLANA_API_URL = SOLANA_API_URL;
4121
4531
  privateKey;
4122
4532
  apiUrl;
4123
4533
  rpcUrl;
4534
+ rpcHeaders;
4124
4535
  timeout;
4125
4536
  sessionTotalUsd = 0;
4126
4537
  sessionCalls = 0;
@@ -4137,8 +4548,10 @@ var SolanaLLMClient = class {
4137
4548
  const apiUrl = options.apiUrl || SOLANA_API_URL;
4138
4549
  validateApiUrl(apiUrl);
4139
4550
  this.apiUrl = apiUrl.replace(/\/$/, "");
4140
- this.rpcUrl = options.rpcUrl || "https://api.mainnet-beta.solana.com";
4141
- this.timeout = options.timeout || DEFAULT_TIMEOUT11;
4551
+ const rpc = resolveRpcConfig(options.rpcUrl, options.rpcHeaders);
4552
+ this.rpcUrl = rpc.url;
4553
+ this.rpcHeaders = rpc.headers;
4554
+ this.timeout = options.timeout || DEFAULT_TIMEOUT13;
4142
4555
  }
4143
4556
  /** Get Solana wallet address (public key in base58). */
4144
4557
  async getWalletAddress() {
@@ -4196,7 +4609,7 @@ var SolanaLLMClient = class {
4196
4609
  try {
4197
4610
  const response = await fetch(this.rpcUrl, {
4198
4611
  method: "POST",
4199
- headers: { "Content-Type": "application/json" },
4612
+ headers: { "Content-Type": "application/json", ...this.rpcHeaders ?? {} },
4200
4613
  body: JSON.stringify({
4201
4614
  jsonrpc: "2.0",
4202
4615
  id: 1,
@@ -4447,7 +4860,8 @@ var SolanaLLMClient = class {
4447
4860
  maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
4448
4861
  extra: details.extra,
4449
4862
  extensions,
4450
- rpcUrl: this.rpcUrl
4863
+ rpcUrl: this.rpcUrl,
4864
+ rpcHeaders: this.rpcHeaders
4451
4865
  }
4452
4866
  );
4453
4867
  const retryResponse = await this.fetchWithTimeout(url, {
@@ -4538,7 +4952,8 @@ var SolanaLLMClient = class {
4538
4952
  maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
4539
4953
  extra: details.extra,
4540
4954
  extensions,
4541
- rpcUrl: this.rpcUrl
4955
+ rpcUrl: this.rpcUrl,
4956
+ rpcHeaders: this.rpcHeaders
4542
4957
  }
4543
4958
  );
4544
4959
  const retryResponse = await this.fetchWithTimeout(url, {
@@ -4629,7 +5044,8 @@ var SolanaLLMClient = class {
4629
5044
  maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
4630
5045
  extra: details.extra,
4631
5046
  extensions,
4632
- rpcUrl: this.rpcUrl
5047
+ rpcUrl: this.rpcUrl,
5048
+ rpcHeaders: this.rpcHeaders
4633
5049
  }
4634
5050
  );
4635
5051
  const query = params ? "?" + new URLSearchParams(params).toString() : "";
@@ -5045,7 +5461,7 @@ var OpenAI = class {
5045
5461
  };
5046
5462
 
5047
5463
  // src/anthropic-compat.ts
5048
- var import_accounts13 = require("viem/accounts");
5464
+ var import_accounts15 = require("viem/accounts");
5049
5465
  var AnthropicClient = class {
5050
5466
  _client = null;
5051
5467
  _clientPromise = null;
@@ -5058,7 +5474,7 @@ var AnthropicClient = class {
5058
5474
  const key = options.privateKey ?? wallet.privateKey;
5059
5475
  validatePrivateKey(key);
5060
5476
  this._privateKey = key;
5061
- this._account = (0, import_accounts13.privateKeyToAccount)(this._privateKey);
5477
+ this._account = (0, import_accounts15.privateKeyToAccount)(this._privateKey);
5062
5478
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
5063
5479
  validateApiUrl(apiUrl);
5064
5480
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -5164,7 +5580,11 @@ var AnthropicClient = class {
5164
5580
  LLMClient,
5165
5581
  MusicClient,
5166
5582
  OpenAI,
5583
+ PHONE_PRICES,
5584
+ PORTRAIT_ENROLLMENT_PRICE_USD,
5167
5585
  PaymentError,
5586
+ PhoneClient,
5587
+ PortraitClient,
5168
5588
  PriceClient,
5169
5589
  SOLANA_NETWORK,
5170
5590
  SOLANA_WALLET_FILE_PATH,