@blockrun/llm 2.7.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -84,8 +84,8 @@ Four call shapes cover every endpoint type:
84
84
  - `stream<T>(path, body?)` — async iterator over SSE chunks (chat)
85
85
 
86
86
  The per-API client classes (`LLMClient`, `ImageClient`, `VideoClient`,
87
- `VoiceClient`, `MusicClient`, `SearchClient`, `XClient`, `PriceClient`,
88
- `SurfClient`) all remain — they will be soft-deprecated in 2.6 (rewritten as
87
+ `PortraitClient`, `VoiceClient`, `MusicClient`, `SearchClient`, `XClient`,
88
+ `PriceClient`, `SurfClient`) all remain — they will be soft-deprecated in 2.6 (rewritten as
89
89
  shims over `BlockrunClient`) and removed in 3.0.
90
90
 
91
91
  ### Try It Free (No USDC Required)
@@ -406,7 +406,17 @@ All models below have been tested end-to-end via the TypeScript SDK (Feb 2026):
406
406
  | `xai/grok-imagine-image-pro` | $0.07/image |
407
407
  | `zai/cogview-4` | $0.015/image |
408
408
 
409
- Image editing (`client.edit`): `openai/gpt-image-1` and `openai/gpt-image-2` both support the `/v1/images/image2image` endpoint.
409
+ Image editing (`client.edit`) via `/v1/images/image2image`: `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, and `google/nano-banana-pro`. Pass a single base64 `data:image/...` URI to edit one image, or an array of 2–4 URIs to **fuse** them (e.g. a subject + a brand logo). Fusion caps: `openai/*` up to 4 source images, `google/*` up to 3. A `mask` cannot be combined with multiple source images.
410
+
411
+ ```ts
412
+ // Multi-image fusion with Nano Banana
413
+ const fused = await client.edit(
414
+ "Place the logo on the t-shirt",
415
+ [subjectDataUri, logoDataUri],
416
+ { model: "google/nano-banana" }
417
+ );
418
+ console.log(fused.data[0].url);
419
+ ```
410
420
 
411
421
  ### Video Generation
412
422
  | Model | Price |
@@ -441,6 +451,35 @@ const r3 = await client.generate('aerial drone shot over a snowy mountain', {
441
451
  });
442
452
  ```
443
453
 
454
+ ### Virtual Portraits
455
+
456
+ `PortraitClient` wraps `POST /v1/portrait/enroll` (paid, flat **$0.01** promo,
457
+ no KYC). Enroll a face image by URL and get back a Token360 asset id (`ta_xxxxxx`).
458
+ Pass that id as `realFaceAssetId` on a Seedance 2.0 video generation to keep the
459
+ same AI character across clips. Payment settles only after Token360 confirms the
460
+ enrollment, so a failed enrollment never charges your wallet. The returned
461
+ `image_url` is a gateway-mirrored copy of your source image (see `mirrored` /
462
+ `source_image_url`). (Real-person likeness is not supported on BlockRun —
463
+ enrolled portraits are AI characters.)
464
+
465
+ ```ts
466
+ import { PortraitClient, VideoClient } from '@blockrun/llm';
467
+
468
+ const portraits = new PortraitClient();
469
+ const { asset_id } = await portraits.enroll({
470
+ name: 'Spokesperson',
471
+ imageUrl: 'https://example.com/face.jpg', // public https JPG/PNG/WEBP, ≤10 MB
472
+ });
473
+
474
+ // Reuse the same character across Seedance 2.0 clips
475
+ const video = new VideoClient();
476
+ const clip = await video.generate('she waves and smiles', {
477
+ model: 'bytedance/seedance-2.0-fast',
478
+ realFaceAssetId: asset_id,
479
+ });
480
+ console.log(clip.data[0].url);
481
+ ```
482
+
444
483
  ### Voice Calls
445
484
 
446
485
  `VoiceClient` wraps `POST /v1/voice/call` (paid, $0.54/call) and
package/dist/index.cjs CHANGED
@@ -41,8 +41,10 @@ __export(index_exports, {
41
41
  MusicClient: () => MusicClient,
42
42
  OpenAI: () => OpenAI,
43
43
  PHONE_PRICES: () => PHONE_PRICES,
44
+ PORTRAIT_ENROLLMENT_PRICE_USD: () => PORTRAIT_ENROLLMENT_PRICE_USD,
44
45
  PaymentError: () => PaymentError,
45
46
  PhoneClient: () => PhoneClient,
47
+ PortraitClient: () => PortraitClient,
46
48
  PriceClient: () => PriceClient,
47
49
  SOLANA_NETWORK: () => SOLANA_NETWORK,
48
50
  SOLANA_WALLET_FILE_PATH: () => SOLANA_WALLET_FILE,
@@ -1461,16 +1463,18 @@ var LLMClient = class _LLMClient {
1461
1463
  return data.data || [];
1462
1464
  }
1463
1465
  /**
1464
- * Edit an image using img2img.
1466
+ * Edit an image using img2img, optionally fusing multiple source images.
1465
1467
  *
1466
1468
  * @param prompt - Text description of the desired edit
1467
- * @param image - Base64-encoded image or URL of the source image
1469
+ * @param image - A base64 `data:image/...` URI, or an array of 2–4 such URIs
1470
+ * to fuse (image fusion). Caps: `openai/*` up to 4, `google/*` up to 3.
1471
+ * A `mask` cannot be combined with multiple source images.
1468
1472
  * @param options - Optional edit parameters
1469
1473
  * @returns ImageResponse with edited image URLs
1470
1474
  */
1471
1475
  async imageEdit(prompt, image, options) {
1472
1476
  const body = {
1473
- model: options?.model || "openai/gpt-image-1",
1477
+ model: options?.model || "openai/gpt-image-2",
1474
1478
  prompt,
1475
1479
  image,
1476
1480
  size: options?.size || "1024x1024",
@@ -1970,20 +1974,37 @@ var ImageClient = class {
1970
1974
  return this.requestWithPayment("/v1/images/generations", body);
1971
1975
  }
1972
1976
  /**
1973
- * Edit an image using img2img.
1977
+ * Edit an image using img2img, optionally fusing multiple source images.
1978
+ *
1979
+ * Pass a single `data:image/...` base64 data URI to edit one image, or an
1980
+ * array of 2–4 data URIs to fuse them (image fusion) — e.g. a reference photo
1981
+ * plus a brand logo. The server caps fusion per provider: `openai/*` accepts
1982
+ * up to 4 source images, `google/*` (Nano Banana) up to 3. A `mask` cannot be
1983
+ * combined with multiple source images.
1984
+ *
1985
+ * Edit-capable models: `openai/gpt-image-1`, `openai/gpt-image-2`,
1986
+ * `google/nano-banana`, `google/nano-banana-pro`.
1974
1987
  *
1975
1988
  * @param prompt - Text description of the desired edit
1976
- * @param image - Base64-encoded image or URL of the source image
1989
+ * @param image - A base64 `data:image/...` URI, or an array of 2–4 such URIs to fuse
1977
1990
  * @param options - Optional edit parameters
1978
1991
  * @returns ImageResponse with edited image URLs
1979
1992
  *
1980
1993
  * @example
1981
- * const result = await client.edit('Make it a painting', imageBase64);
1982
- * console.log(result.data[0].url);
1994
+ * // Single-image edit
1995
+ * const result = await client.edit('Make it a painting', imageDataUri);
1996
+ *
1997
+ * @example
1998
+ * // Multi-image fusion (Nano Banana): blend a subject with a brand logo
1999
+ * const fused = await client.edit(
2000
+ * 'Place the logo on the t-shirt',
2001
+ * [subjectDataUri, logoDataUri],
2002
+ * { model: 'google/nano-banana' }
2003
+ * );
1983
2004
  */
1984
2005
  async edit(prompt, image, options) {
1985
2006
  const body = {
1986
- model: options?.model || "openai/gpt-image-1",
2007
+ model: options?.model || "openai/gpt-image-2",
1987
2008
  prompt,
1988
2009
  image,
1989
2010
  size: options?.size || "1024x1024",
@@ -2999,15 +3020,18 @@ var PhoneClient = class {
2999
3020
  }
3000
3021
  };
3001
3022
 
3002
- // src/search.ts
3023
+ // src/portrait.ts
3003
3024
  var import_accounts8 = require("viem/accounts");
3004
3025
  var DEFAULT_API_URL7 = "https://blockrun.ai/api";
3005
3026
  var DEFAULT_TIMEOUT7 = 6e4;
3006
- var SearchClient = class {
3027
+ var PORTRAIT_ENROLLMENT_PRICE_USD = 0.01;
3028
+ var PortraitClient = class {
3007
3029
  account;
3008
3030
  privateKey;
3009
3031
  apiUrl;
3010
3032
  timeout;
3033
+ sessionTotalUsd = 0;
3034
+ sessionCalls = 0;
3011
3035
  constructor(options = {}) {
3012
3036
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3013
3037
  const privateKey = options.privateKey || envKey;
@@ -3024,6 +3048,157 @@ var SearchClient = class {
3024
3048
  this.apiUrl = apiUrl.replace(/\/$/, "");
3025
3049
  this.timeout = options.timeout || DEFAULT_TIMEOUT7;
3026
3050
  }
3051
+ /** EVM wallet address used for payments. */
3052
+ getWalletAddress() {
3053
+ return this.account.address;
3054
+ }
3055
+ /** Session-cumulative USD spend + call count across this client instance. */
3056
+ getSpending() {
3057
+ return { totalUsd: this.sessionTotalUsd, calls: this.sessionCalls };
3058
+ }
3059
+ /**
3060
+ * Enroll a Virtual Portrait from a face image URL. $0.01 (promo).
3061
+ *
3062
+ * Payment is settled only after Token360 confirms the enrollment, so failed
3063
+ * enrollments never charge your wallet.
3064
+ *
3065
+ * @param options.name Display name (1–64 chars).
3066
+ * @param options.imageUrl Public HTTPS URL to a JPG/PNG/WEBP image (≤10 MB).
3067
+ * @returns The enrollment record, including `asset_id` (`ta_xxxxxx`).
3068
+ */
3069
+ async enroll(options) {
3070
+ const name = typeof options?.name === "string" ? options.name.trim() : "";
3071
+ if (name.length < 1 || name.length > 64) {
3072
+ throw new Error("name is required and must be 1\u201364 characters");
3073
+ }
3074
+ const imageUrl = typeof options?.imageUrl === "string" ? options.imageUrl.trim() : "";
3075
+ if (!/^https?:\/\//i.test(imageUrl)) {
3076
+ throw new Error(
3077
+ "imageUrl must be a public http(s) URL to a JPG/PNG/WEBP image"
3078
+ );
3079
+ }
3080
+ const data = await this.request({
3081
+ name,
3082
+ image_url: imageUrl
3083
+ });
3084
+ return data;
3085
+ }
3086
+ // ─── Internals ─────────────────────────────────────────────────────────
3087
+ async request(body) {
3088
+ const url = `${this.apiUrl}/v1/portrait/enroll`;
3089
+ const response = await this.fetchWithTimeout(url, {
3090
+ method: "POST",
3091
+ headers: { "Content-Type": "application/json" },
3092
+ body: JSON.stringify(body)
3093
+ });
3094
+ if (response.status === 402) {
3095
+ return this.handlePaymentAndRetry(url, body, response);
3096
+ }
3097
+ return this.unwrap(response, false);
3098
+ }
3099
+ async handlePaymentAndRetry(url, body, response) {
3100
+ let paymentHeader = response.headers.get("payment-required");
3101
+ if (!paymentHeader) {
3102
+ try {
3103
+ const rb = await response.json();
3104
+ if (rb.x402 || rb.accepts) paymentHeader = btoa(JSON.stringify(rb));
3105
+ } catch {
3106
+ }
3107
+ }
3108
+ if (!paymentHeader) {
3109
+ throw new PaymentError("402 response but no payment requirements found");
3110
+ }
3111
+ const paymentRequired = parsePaymentRequired(paymentHeader);
3112
+ const details = extractPaymentDetails(paymentRequired);
3113
+ const paymentPayload = await createPaymentPayload(
3114
+ this.privateKey,
3115
+ this.account.address,
3116
+ details.recipient,
3117
+ details.amount,
3118
+ details.network || "eip155:8453",
3119
+ {
3120
+ resourceUrl: details.resource?.url || url,
3121
+ resourceDescription: details.resource?.description || "BlockRun Virtual Portrait",
3122
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
3123
+ extra: details.extra
3124
+ }
3125
+ );
3126
+ const retry = await this.fetchWithTimeout(url, {
3127
+ method: "POST",
3128
+ headers: {
3129
+ "Content-Type": "application/json",
3130
+ "PAYMENT-SIGNATURE": paymentPayload
3131
+ },
3132
+ body: JSON.stringify(body)
3133
+ });
3134
+ if (retry.status === 402) {
3135
+ throw new PaymentError("Payment was rejected. Check your wallet balance.");
3136
+ }
3137
+ const data = await this.unwrap(retry, true);
3138
+ const costUsd = Number.parseFloat(details.amount) / 1e6;
3139
+ this.sessionCalls += 1;
3140
+ this.sessionTotalUsd += costUsd;
3141
+ if (data && typeof data === "object" && !("txHash" in data)) {
3142
+ const settlement = data.settlement;
3143
+ if (settlement?.tx_hash) {
3144
+ data.txHash = settlement.tx_hash;
3145
+ }
3146
+ }
3147
+ return data;
3148
+ }
3149
+ async unwrap(response, afterPayment) {
3150
+ if (response.ok) {
3151
+ return response.json();
3152
+ }
3153
+ let errorBody;
3154
+ try {
3155
+ errorBody = await response.json();
3156
+ } catch {
3157
+ errorBody = { error: "Request failed" };
3158
+ }
3159
+ const prefix = afterPayment ? "API error after payment" : "API error";
3160
+ throw new APIError(
3161
+ `${prefix}: ${response.status}`,
3162
+ response.status,
3163
+ sanitizeErrorResponse(errorBody)
3164
+ );
3165
+ }
3166
+ async fetchWithTimeout(url, init) {
3167
+ const controller = new AbortController();
3168
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3169
+ try {
3170
+ return await fetch(url, { ...init, signal: controller.signal });
3171
+ } finally {
3172
+ clearTimeout(timeoutId);
3173
+ }
3174
+ }
3175
+ };
3176
+
3177
+ // src/search.ts
3178
+ var import_accounts9 = require("viem/accounts");
3179
+ var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3180
+ var DEFAULT_TIMEOUT8 = 6e4;
3181
+ var SearchClient = class {
3182
+ account;
3183
+ privateKey;
3184
+ apiUrl;
3185
+ timeout;
3186
+ constructor(options = {}) {
3187
+ const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
3188
+ const privateKey = options.privateKey || envKey;
3189
+ if (!privateKey) {
3190
+ throw new Error(
3191
+ "Private key required. Pass privateKey in options or set BLOCKRUN_WALLET_KEY environment variable."
3192
+ );
3193
+ }
3194
+ validatePrivateKey(privateKey);
3195
+ this.privateKey = privateKey;
3196
+ this.account = (0, import_accounts9.privateKeyToAccount)(privateKey);
3197
+ const apiUrl = options.apiUrl || DEFAULT_API_URL8;
3198
+ validateApiUrl(apiUrl);
3199
+ this.apiUrl = apiUrl.replace(/\/$/, "");
3200
+ this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3201
+ }
3027
3202
  async search(query, options) {
3028
3203
  if (!query || query.length > 1e3) {
3029
3204
  throw new Error("query must be 1-1000 characters");
@@ -3136,9 +3311,9 @@ var SearchClient = class {
3136
3311
  };
3137
3312
 
3138
3313
  // src/x-client.ts
3139
- var import_accounts9 = require("viem/accounts");
3140
- var DEFAULT_API_URL8 = "https://blockrun.ai/api";
3141
- var DEFAULT_TIMEOUT8 = 6e4;
3314
+ var import_accounts10 = require("viem/accounts");
3315
+ var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3316
+ var DEFAULT_TIMEOUT9 = 6e4;
3142
3317
  var XClient = class _XClient {
3143
3318
  account;
3144
3319
  privateKey;
@@ -3161,11 +3336,11 @@ var XClient = class _XClient {
3161
3336
  }
3162
3337
  validatePrivateKey(privateKey);
3163
3338
  this.privateKey = privateKey;
3164
- this.account = (0, import_accounts9.privateKeyToAccount)(privateKey);
3165
- const apiUrl = options.apiUrl || DEFAULT_API_URL8;
3339
+ this.account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3340
+ const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3166
3341
  validateApiUrl(apiUrl);
3167
3342
  this.apiUrl = apiUrl.replace(/\/$/, "");
3168
- this.timeout = options.timeout || DEFAULT_TIMEOUT8;
3343
+ this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3169
3344
  }
3170
3345
  // ───────── User endpoints ─────────
3171
3346
  userLookup(usernames) {
@@ -3343,9 +3518,9 @@ var XClient = class _XClient {
3343
3518
  };
3344
3519
 
3345
3520
  // src/price.ts
3346
- var import_accounts10 = require("viem/accounts");
3347
- var DEFAULT_API_URL9 = "https://blockrun.ai/api";
3348
- var DEFAULT_TIMEOUT9 = 3e4;
3521
+ var import_accounts11 = require("viem/accounts");
3522
+ var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3523
+ var DEFAULT_TIMEOUT10 = 3e4;
3349
3524
  var PriceClient = class {
3350
3525
  account = null;
3351
3526
  privateKey = null;
@@ -3363,12 +3538,12 @@ var PriceClient = class {
3363
3538
  if (privateKey) {
3364
3539
  validatePrivateKey(privateKey);
3365
3540
  this.privateKey = privateKey;
3366
- this.account = (0, import_accounts10.privateKeyToAccount)(privateKey);
3541
+ this.account = (0, import_accounts11.privateKeyToAccount)(privateKey);
3367
3542
  }
3368
- const apiUrl = options.apiUrl || DEFAULT_API_URL9;
3543
+ const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3369
3544
  validateApiUrl(apiUrl);
3370
3545
  this.apiUrl = apiUrl.replace(/\/$/, "");
3371
- this.timeout = options.timeout || DEFAULT_TIMEOUT9;
3546
+ this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3372
3547
  }
3373
3548
  async price(category, symbol, options) {
3374
3549
  if (!symbol) throw new Error("symbol is required");
@@ -3547,9 +3722,9 @@ function buildUrl(base, query) {
3547
3722
  }
3548
3723
 
3549
3724
  // src/surf.ts
3550
- var import_accounts11 = require("viem/accounts");
3551
- var DEFAULT_API_URL10 = "https://blockrun.ai/api";
3552
- var DEFAULT_TIMEOUT10 = 6e4;
3725
+ var import_accounts12 = require("viem/accounts");
3726
+ var DEFAULT_API_URL11 = "https://blockrun.ai/api";
3727
+ var DEFAULT_TIMEOUT11 = 6e4;
3553
3728
  var SURF_PREFIX = "/v1/surf";
3554
3729
  var SurfClient = class {
3555
3730
  account;
@@ -3566,11 +3741,11 @@ var SurfClient = class {
3566
3741
  }
3567
3742
  validatePrivateKey(privateKey);
3568
3743
  this.privateKey = privateKey;
3569
- this.account = (0, import_accounts11.privateKeyToAccount)(privateKey);
3570
- const apiUrl = options.apiUrl || DEFAULT_API_URL10;
3744
+ this.account = (0, import_accounts12.privateKeyToAccount)(privateKey);
3745
+ const apiUrl = options.apiUrl || DEFAULT_API_URL11;
3571
3746
  validateApiUrl(apiUrl);
3572
3747
  this.apiUrl = apiUrl.replace(/\/$/, "");
3573
- this.timeout = options.timeout || DEFAULT_TIMEOUT10;
3748
+ this.timeout = options.timeout || DEFAULT_TIMEOUT11;
3574
3749
  }
3575
3750
  /**
3576
3751
  * GET a Surf endpoint. `path` is everything after `/v1/surf` (a leading
@@ -3702,9 +3877,9 @@ var SurfClient = class {
3702
3877
  };
3703
3878
 
3704
3879
  // src/blockrun.ts
3705
- var import_accounts12 = require("viem/accounts");
3706
- var DEFAULT_API_URL11 = "https://blockrun.ai/api";
3707
- var DEFAULT_TIMEOUT11 = 6e4;
3880
+ var import_accounts13 = require("viem/accounts");
3881
+ var DEFAULT_API_URL12 = "https://blockrun.ai/api";
3882
+ var DEFAULT_TIMEOUT12 = 6e4;
3708
3883
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
3709
3884
  var DEFAULT_POLL_BUDGET_MS = 3e5;
3710
3885
  var MAX_SIGNED_AUTH_SECONDS = 600;
@@ -3725,11 +3900,11 @@ var BlockrunClient = class {
3725
3900
  }
3726
3901
  validatePrivateKey(privateKey);
3727
3902
  this.privateKey = privateKey;
3728
- this.account = (0, import_accounts12.privateKeyToAccount)(privateKey);
3729
- const apiUrl = options.apiUrl || DEFAULT_API_URL11;
3903
+ this.account = (0, import_accounts13.privateKeyToAccount)(privateKey);
3904
+ const apiUrl = options.apiUrl || DEFAULT_API_URL12;
3730
3905
  validateApiUrl(apiUrl);
3731
3906
  this.apiUrl = apiUrl.replace(/\/$/, "");
3732
- this.timeout = options.timeout || DEFAULT_TIMEOUT11;
3907
+ this.timeout = options.timeout || DEFAULT_TIMEOUT12;
3733
3908
  }
3734
3909
  /**
3735
3910
  * GET a BlockRun endpoint. `path` is everything after `/api` (a leading
@@ -4080,7 +4255,7 @@ function sleep(ms) {
4080
4255
  }
4081
4256
 
4082
4257
  // src/wallet.ts
4083
- var import_accounts13 = require("viem/accounts");
4258
+ var import_accounts14 = require("viem/accounts");
4084
4259
  var fs2 = __toESM(require("fs"), 1);
4085
4260
  var path2 = __toESM(require("path"), 1);
4086
4261
  var os2 = __toESM(require("os"), 1);
@@ -4089,8 +4264,8 @@ var BASE_CHAIN_ID2 = "8453";
4089
4264
  var WALLET_DIR = path2.join(os2.homedir(), ".blockrun");
4090
4265
  var WALLET_FILE = path2.join(WALLET_DIR, ".session");
4091
4266
  function createWallet() {
4092
- const privateKey = (0, import_accounts13.generatePrivateKey)();
4093
- const account = (0, import_accounts13.privateKeyToAccount)(privateKey);
4267
+ const privateKey = (0, import_accounts14.generatePrivateKey)();
4268
+ const account = (0, import_accounts14.privateKeyToAccount)(privateKey);
4094
4269
  return {
4095
4270
  address: account.address,
4096
4271
  privateKey
@@ -4146,12 +4321,12 @@ function loadWallet() {
4146
4321
  function getOrCreateWallet() {
4147
4322
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4148
4323
  if (envKey) {
4149
- const account = (0, import_accounts13.privateKeyToAccount)(envKey);
4324
+ const account = (0, import_accounts14.privateKeyToAccount)(envKey);
4150
4325
  return { address: account.address, privateKey: envKey, isNew: false };
4151
4326
  }
4152
4327
  const fileKey = loadWallet();
4153
4328
  if (fileKey) {
4154
- const account = (0, import_accounts13.privateKeyToAccount)(fileKey);
4329
+ const account = (0, import_accounts14.privateKeyToAccount)(fileKey);
4155
4330
  return { address: account.address, privateKey: fileKey, isNew: false };
4156
4331
  }
4157
4332
  const { address, privateKey } = createWallet();
@@ -4161,11 +4336,11 @@ function getOrCreateWallet() {
4161
4336
  function getWalletAddress() {
4162
4337
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4163
4338
  if (envKey) {
4164
- return (0, import_accounts13.privateKeyToAccount)(envKey).address;
4339
+ return (0, import_accounts14.privateKeyToAccount)(envKey).address;
4165
4340
  }
4166
4341
  const fileKey = loadWallet();
4167
4342
  if (fileKey) {
4168
- return (0, import_accounts13.privateKeyToAccount)(fileKey).address;
4343
+ return (0, import_accounts14.privateKeyToAccount)(fileKey).address;
4169
4344
  }
4170
4345
  return null;
4171
4346
  }
@@ -4345,7 +4520,7 @@ async function getOrCreateSolanaWallet() {
4345
4520
  // src/solana-client.ts
4346
4521
  var SOLANA_API_URL = "https://sol.blockrun.ai/api";
4347
4522
  var DEFAULT_MAX_TOKENS2 = 1024;
4348
- var DEFAULT_TIMEOUT12 = 6e4;
4523
+ var DEFAULT_TIMEOUT13 = 6e4;
4349
4524
  var SDK_VERSION2 = "0.3.0";
4350
4525
  var USER_AGENT2 = `blockrun-ts/${SDK_VERSION2}`;
4351
4526
  var DEFAULT_SOLANA_RPC_URL = "https://sol.blockrun.ai/api/v1/solana/rpc";
@@ -4395,7 +4570,7 @@ var SolanaLLMClient = class {
4395
4570
  const rpc = resolveRpcConfig(options.rpcUrl, options.rpcHeaders);
4396
4571
  this.rpcUrl = rpc.url;
4397
4572
  this.rpcHeaders = rpc.headers;
4398
- this.timeout = options.timeout || DEFAULT_TIMEOUT12;
4573
+ this.timeout = options.timeout || DEFAULT_TIMEOUT13;
4399
4574
  }
4400
4575
  /** Get Solana wallet address (public key in base58). */
4401
4576
  async getWalletAddress() {
@@ -4476,7 +4651,7 @@ var SolanaLLMClient = class {
4476
4651
  /** Edit an image using img2img (Solana payment). */
4477
4652
  async imageEdit(prompt, image, options) {
4478
4653
  const body = {
4479
- model: options?.model || "openai/gpt-image-1",
4654
+ model: options?.model || "openai/gpt-image-2",
4480
4655
  prompt,
4481
4656
  image,
4482
4657
  size: options?.size || "1024x1024",
@@ -5305,7 +5480,7 @@ var OpenAI = class {
5305
5480
  };
5306
5481
 
5307
5482
  // src/anthropic-compat.ts
5308
- var import_accounts14 = require("viem/accounts");
5483
+ var import_accounts15 = require("viem/accounts");
5309
5484
  var AnthropicClient = class {
5310
5485
  _client = null;
5311
5486
  _clientPromise = null;
@@ -5318,7 +5493,7 @@ var AnthropicClient = class {
5318
5493
  const key = options.privateKey ?? wallet.privateKey;
5319
5494
  validatePrivateKey(key);
5320
5495
  this._privateKey = key;
5321
- this._account = (0, import_accounts14.privateKeyToAccount)(this._privateKey);
5496
+ this._account = (0, import_accounts15.privateKeyToAccount)(this._privateKey);
5322
5497
  const apiUrl = options.apiUrl ?? "https://blockrun.ai/api";
5323
5498
  validateApiUrl(apiUrl);
5324
5499
  this._apiUrl = apiUrl.replace(/\/$/, "");
@@ -5425,8 +5600,10 @@ var AnthropicClient = class {
5425
5600
  MusicClient,
5426
5601
  OpenAI,
5427
5602
  PHONE_PRICES,
5603
+ PORTRAIT_ENROLLMENT_PRICE_USD,
5428
5604
  PaymentError,
5429
5605
  PhoneClient,
5606
+ PortraitClient,
5430
5607
  PriceClient,
5431
5608
  SOLANA_NETWORK,
5432
5609
  SOLANA_WALLET_FILE_PATH,