@blockrun/llm 3.7.0 → 3.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/README.md CHANGED
@@ -1295,9 +1295,10 @@ await status();
1295
1295
  // Balance: $5.30 USDC
1296
1296
  ```
1297
1297
 
1298
- ## Wallet Scanning
1298
+ ## Wallet Discovery and Migration
1299
1299
 
1300
- The SDK auto-detects wallets from any provider on your system:
1300
+ The SDK can discover compatible wallets for an explicit, user-confirmed
1301
+ migration. It never automatically makes a discovered provider wallet active:
1301
1302
 
1302
1303
  ```typescript
1303
1304
  import { scanWallets, scanSolanaWallets } from '@blockrun/llm';
@@ -1309,7 +1310,50 @@ const baseWallets = scanWallets();
1309
1310
  const solWallets = scanSolanaWallets();
1310
1311
  ```
1311
1312
 
1312
- `getOrCreateWallet()` checks scanned wallets first, so if you already have a wallet from another BlockRun tool, it will be reused automatically.
1313
+ `getOrCreateWallet()` always uses `~/.blockrun/.session` (or an explicit
1314
+ wallet environment variable, or the legacy `~/.blockrun/wallet.key`). Review
1315
+ the discovered addresses and import one explicitly if you intend to switch
1316
+ wallets.
1317
+
1318
+ ### Upgrading from a provider wallet
1319
+
1320
+ Earlier versions adopted the most recently written provider wallet
1321
+ automatically. If you relied on that, the first run after upgrading creates a
1322
+ fresh BlockRun wallet and prints the addresses it found, so you can import the
1323
+ one you actually own:
1324
+
1325
+ ```
1326
+ NOTICE: BlockRun created a new wallet, but also found existing wallet(s)
1327
+ belonging to other applications on this system:
1328
+
1329
+ 0x88f9B82462f6C4bf4a0Fb15e5c3971559a316e7f
1330
+ ...
1331
+ ```
1332
+
1333
+ Adopt one deliberately:
1334
+
1335
+ ```typescript
1336
+ import { listDiscoveredWallets, importWallet } from '@blockrun/llm';
1337
+
1338
+ for (const w of listDiscoveredWallets()) {
1339
+ console.log(w.address, 'from', w.source);
1340
+ }
1341
+
1342
+ importWallet('0x88f9B82462f6C4bf4a0Fb15e5c3971559a316e7f');
1343
+ ```
1344
+
1345
+ `importWallet()` writes your current wallet to
1346
+ `~/.blockrun/.session.backup-<timestamp>` before switching, so adopting a wallet
1347
+ never strands funds in the old one. Solana: `listDiscoveredSolanaWallets()` and
1348
+ `importSolanaWallet()`.
1349
+
1350
+ Addresses shown are derived from the discovered key itself, and `importWallet()`
1351
+ matches on that derived address — so a wallet file cannot claim an address it
1352
+ cannot sign for, nor be adopted by one. `listDiscoveredWallets()` never returns
1353
+ private keys.
1354
+
1355
+ For a single run without changing anything, use
1356
+ `export BLOCKRUN_WALLET_KEY=<private-key>`.
1313
1357
 
1314
1358
  ## Response Caching
1315
1359
 
package/dist/index.cjs CHANGED
@@ -72,7 +72,9 @@ __export(index_exports, {
72
72
  extractPaymentDetails: () => extractPaymentDetails,
73
73
  formatFundingMessageCompact: () => formatFundingMessageCompact,
74
74
  formatNeedsFundingMessage: () => formatNeedsFundingMessage,
75
+ formatSolanaWalletMigrationNotice: () => formatSolanaWalletMigrationNotice,
75
76
  formatWalletCreatedMessage: () => formatWalletCreatedMessage,
77
+ formatWalletMigrationNotice: () => formatWalletMigrationNotice,
76
78
  getCached: () => getCached,
77
79
  getCachedByRequest: () => getCachedByRequest,
78
80
  getCostLogSummary: () => getCostLogSummary,
@@ -82,6 +84,10 @@ __export(index_exports, {
82
84
  getOrCreateWallet: () => getOrCreateWallet,
83
85
  getPaymentLinks: () => getPaymentLinks,
84
86
  getWalletAddress: () => getWalletAddress,
87
+ importSolanaWallet: () => importSolanaWallet,
88
+ importWallet: () => importWallet,
89
+ listDiscoveredSolanaWallets: () => listDiscoveredSolanaWallets,
90
+ listDiscoveredWallets: () => listDiscoveredWallets,
85
91
  loadSolanaWallet: () => loadSolanaWallet,
86
92
  loadWallet: () => loadWallet,
87
93
  logCost: () => logCost,
@@ -4629,7 +4635,7 @@ function scanWallets() {
4629
4635
  const addr = data.address || "";
4630
4636
  if (pk && addr) {
4631
4637
  const mtime = fs2.statSync(walletFile).mtimeMs;
4632
- results.push({ mtime, privateKey: pk, address: addr });
4638
+ results.push({ mtime, privateKey: pk, address: addr, source: walletFile });
4633
4639
  }
4634
4640
  } catch {
4635
4641
  continue;
@@ -4638,11 +4644,48 @@ function scanWallets() {
4638
4644
  } catch {
4639
4645
  }
4640
4646
  results.sort((a, b) => b.mtime - a.mtime);
4641
- return results.map(({ privateKey, address }) => ({ privateKey, address }));
4647
+ return results.map(({ privateKey, address, source }) => ({ privateKey, address, source }));
4648
+ }
4649
+ function listDiscoveredWallets() {
4650
+ const listed = [];
4651
+ for (const entry of scanWallets()) {
4652
+ try {
4653
+ listed.push({
4654
+ address: (0, import_accounts15.privateKeyToAccount)(entry.privateKey).address,
4655
+ source: entry.source
4656
+ });
4657
+ } catch {
4658
+ continue;
4659
+ }
4660
+ }
4661
+ return listed;
4662
+ }
4663
+ function importWallet(address) {
4664
+ const wanted = address.trim().toLowerCase();
4665
+ for (const entry of scanWallets()) {
4666
+ let derived;
4667
+ try {
4668
+ derived = (0, import_accounts15.privateKeyToAccount)(entry.privateKey).address;
4669
+ } catch {
4670
+ continue;
4671
+ }
4672
+ if (derived.toLowerCase() !== wanted) continue;
4673
+ if (fs2.existsSync(WALLET_FILE)) {
4674
+ const current = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4675
+ if (current && current !== entry.privateKey) {
4676
+ const backup = path2.join(WALLET_DIR, `.session.backup-${Math.floor(Date.now() / 1e3)}`);
4677
+ fs2.writeFileSync(backup, current, { mode: 384 });
4678
+ }
4679
+ }
4680
+ saveWallet(entry.privateKey);
4681
+ return derived;
4682
+ }
4683
+ const available = listDiscoveredWallets().map((w) => w.address);
4684
+ throw new Error(
4685
+ `No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`
4686
+ );
4642
4687
  }
4643
4688
  function loadWallet() {
4644
- const wallets = scanWallets();
4645
- if (wallets.length > 0) return wallets[0].privateKey;
4646
4689
  if (fs2.existsSync(WALLET_FILE)) {
4647
4690
  const key = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4648
4691
  if (key) return key;
@@ -4654,6 +4697,46 @@ function loadWallet() {
4654
4697
  }
4655
4698
  return null;
4656
4699
  }
4700
+ function formatWalletMigrationNotice(newAddress) {
4701
+ let discovered;
4702
+ try {
4703
+ discovered = scanWallets();
4704
+ } catch {
4705
+ return null;
4706
+ }
4707
+ const addresses = [];
4708
+ for (const entry of discovered) {
4709
+ try {
4710
+ addresses.push((0, import_accounts15.privateKeyToAccount)(entry.privateKey).address);
4711
+ } catch {
4712
+ continue;
4713
+ }
4714
+ }
4715
+ if (addresses.length === 0) return null;
4716
+ const found = addresses.map((addr) => ` ${addr}`).join("\n");
4717
+ return `
4718
+ NOTICE: BlockRun created a new wallet, but also found existing wallet(s)
4719
+ belonging to other applications on this system:
4720
+
4721
+ ${found}
4722
+
4723
+ BlockRun now uses only its own wallet:
4724
+
4725
+ ${newAddress}
4726
+
4727
+ Discovered wallets are never adopted automatically \u2014 one may belong to a
4728
+ different application, or have been planted to make you fund an address you
4729
+ do not control.
4730
+
4731
+ If an address above is yours and holds your USDC, adopt it deliberately:
4732
+
4733
+ import { importWallet } from '@blockrun/llm';
4734
+ importWallet("<address-from-the-list-above>");
4735
+
4736
+ Your current wallet is backed up first. You can also set
4737
+ BLOCKRUN_WALLET_KEY=<private-key> for a single run without changing anything.
4738
+ `;
4739
+ }
4657
4740
  function getOrCreateWallet() {
4658
4741
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4659
4742
  if (envKey) {
@@ -4796,7 +4879,7 @@ function scanSolanaWallets() {
4796
4879
  const addr = data.address || "";
4797
4880
  if (pk && addr) {
4798
4881
  const mtime = fs3.statSync(solanaWalletFile).mtimeMs;
4799
- results.push({ mtime, secretKey: pk, publicKey: addr });
4882
+ results.push({ mtime, secretKey: pk, publicKey: addr, source: solanaWalletFile });
4800
4883
  }
4801
4884
  } catch {
4802
4885
  }
@@ -4810,7 +4893,7 @@ function scanSolanaWallets() {
4810
4893
  const addr = data.address || "";
4811
4894
  if (pk && addr) {
4812
4895
  const mtime = fs3.statSync(brccWalletFile).mtimeMs;
4813
- results.push({ mtime, secretKey: pk, publicKey: addr });
4896
+ results.push({ mtime, secretKey: pk, publicKey: addr, source: brccWalletFile });
4814
4897
  }
4815
4898
  } catch {
4816
4899
  }
@@ -4820,27 +4903,100 @@ function scanSolanaWallets() {
4820
4903
  } catch {
4821
4904
  }
4822
4905
  results.sort((a, b) => b.mtime - a.mtime);
4823
- return results.map(({ secretKey, publicKey }) => ({ secretKey, publicKey }));
4906
+ return results.map(({ secretKey, publicKey, source }) => ({ secretKey, publicKey, source }));
4824
4907
  }
4825
4908
  function loadSolanaWallet() {
4826
- const wallets = scanSolanaWallets();
4827
- if (wallets.length > 0) return wallets[0].secretKey;
4828
4909
  if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4829
4910
  const key = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4830
4911
  if (key) return key;
4831
4912
  }
4832
4913
  return null;
4833
4914
  }
4915
+ async function listDiscoveredSolanaWallets() {
4916
+ const listed = [];
4917
+ for (const entry of scanSolanaWallets()) {
4918
+ try {
4919
+ listed.push({ address: await solanaPublicKey(entry.secretKey), source: entry.source });
4920
+ } catch {
4921
+ continue;
4922
+ }
4923
+ }
4924
+ return listed;
4925
+ }
4926
+ async function importSolanaWallet(address) {
4927
+ const wanted = address.trim();
4928
+ for (const entry of scanSolanaWallets()) {
4929
+ let derived;
4930
+ try {
4931
+ derived = await solanaPublicKey(entry.secretKey);
4932
+ } catch {
4933
+ continue;
4934
+ }
4935
+ if (derived !== wanted) continue;
4936
+ if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4937
+ const current = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4938
+ if (current && current !== entry.secretKey) {
4939
+ const backup = path3.join(
4940
+ WALLET_DIR2,
4941
+ `.solana-session.backup-${Math.floor(Date.now() / 1e3)}`
4942
+ );
4943
+ fs3.writeFileSync(backup, current, { mode: 384 });
4944
+ }
4945
+ }
4946
+ saveSolanaWallet(entry.secretKey);
4947
+ return derived;
4948
+ }
4949
+ const available = (await listDiscoveredSolanaWallets()).map((w) => w.address);
4950
+ throw new Error(
4951
+ `No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`
4952
+ );
4953
+ }
4954
+ async function formatSolanaWalletMigrationNotice(newAddress) {
4955
+ let discovered;
4956
+ try {
4957
+ discovered = scanSolanaWallets();
4958
+ } catch {
4959
+ return null;
4960
+ }
4961
+ const addresses = [];
4962
+ for (const entry of discovered) {
4963
+ try {
4964
+ addresses.push(await solanaPublicKey(entry.secretKey));
4965
+ } catch {
4966
+ continue;
4967
+ }
4968
+ }
4969
+ if (addresses.length === 0) return null;
4970
+ const found = addresses.map((addr) => ` ${addr}`).join("\n");
4971
+ return `
4972
+ NOTICE: BlockRun created a new Solana wallet, but also found existing
4973
+ wallet(s) belonging to other applications on this system:
4974
+
4975
+ ${found}
4976
+
4977
+ BlockRun now uses only its own wallet:
4978
+
4979
+ ${newAddress}
4980
+
4981
+ Discovered wallets are never adopted automatically \u2014 one may belong to a
4982
+ different application, or have been planted to make you fund an address you
4983
+ do not control.
4984
+
4985
+ If an address above is yours and holds your USDC, adopt it deliberately:
4986
+
4987
+ import { importSolanaWallet } from '@blockrun/llm';
4988
+ await importSolanaWallet("<address-from-the-list-above>");
4989
+
4990
+ Your current wallet is backed up first. You can also set
4991
+ SOLANA_WALLET_KEY=<private-key> for a single run without changing anything.
4992
+ `;
4993
+ }
4834
4994
  async function getOrCreateSolanaWallet() {
4835
4995
  const envKey = typeof process !== "undefined" && process.env ? process.env.SOLANA_WALLET_KEY : void 0;
4836
4996
  if (envKey) {
4837
4997
  const address2 = await solanaPublicKey(envKey);
4838
4998
  return { privateKey: envKey, address: address2, isNew: false };
4839
4999
  }
4840
- const wallets = scanSolanaWallets();
4841
- if (wallets.length > 0) {
4842
- return { privateKey: wallets[0].secretKey, address: wallets[0].publicKey, isNew: false };
4843
- }
4844
5000
  if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4845
5001
  const fileKey = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4846
5002
  if (fileKey) {
@@ -5665,27 +5821,35 @@ function getCostLogSummary() {
5665
5821
  // src/setup.ts
5666
5822
  function setupAgentWallet(options) {
5667
5823
  const { address, privateKey, isNew } = getOrCreateWallet();
5668
- if (isNew && !options?.silent) {
5669
- console.error(
5670
- `
5824
+ if (isNew) {
5825
+ const notice = formatWalletMigrationNotice(address);
5826
+ if (notice) console.error(notice);
5827
+ if (!options?.silent) {
5828
+ console.error(
5829
+ `
5671
5830
  BlockRun Agent Wallet Created!
5672
5831
  Address: ${address}
5673
5832
  Send USDC on Base to get started.
5674
5833
  `
5675
- );
5834
+ );
5835
+ }
5676
5836
  }
5677
5837
  return new LLMClient({ privateKey });
5678
5838
  }
5679
5839
  async function setupAgentSolanaWallet(options) {
5680
5840
  const result = await getOrCreateSolanaWallet();
5681
- if (result.isNew && !options?.silent) {
5682
- console.error(
5683
- `
5841
+ if (result.isNew) {
5842
+ const notice = await formatSolanaWalletMigrationNotice(result.address);
5843
+ if (notice) console.error(notice);
5844
+ if (!options?.silent) {
5845
+ console.error(
5846
+ `
5684
5847
  BlockRun Solana Agent Wallet Created!
5685
5848
  Address: ${result.address}
5686
5849
  Send USDC on Solana to get started.
5687
5850
  `
5688
- );
5851
+ );
5852
+ }
5689
5853
  }
5690
5854
  return new SolanaLLMClient({ privateKey: result.privateKey });
5691
5855
  }
@@ -5998,7 +6162,9 @@ var AnthropicClient = class {
5998
6162
  extractPaymentDetails,
5999
6163
  formatFundingMessageCompact,
6000
6164
  formatNeedsFundingMessage,
6165
+ formatSolanaWalletMigrationNotice,
6001
6166
  formatWalletCreatedMessage,
6167
+ formatWalletMigrationNotice,
6002
6168
  getCached,
6003
6169
  getCachedByRequest,
6004
6170
  getCostLogSummary,
@@ -6008,6 +6174,10 @@ var AnthropicClient = class {
6008
6174
  getOrCreateWallet,
6009
6175
  getPaymentLinks,
6010
6176
  getWalletAddress,
6177
+ importSolanaWallet,
6178
+ importWallet,
6179
+ listDiscoveredSolanaWallets,
6180
+ listDiscoveredWallets,
6011
6181
  loadSolanaWallet,
6012
6182
  loadWallet,
6013
6183
  logCost,
package/dist/index.d.cts CHANGED
@@ -2527,37 +2527,86 @@ declare function createWallet(): {
2527
2527
  */
2528
2528
  declare function saveWallet(privateKey: string): string;
2529
2529
  /**
2530
- * Scan ~/.<dir>/wallet.json files from any provider (agentcash, etc.).
2530
+ * Discover ~/.<dir>/wallet.json files from other providers.
2531
2531
  *
2532
2532
  * Each file should contain JSON with "privateKey" and "address" fields.
2533
- * Results are sorted by modification time (most recent first).
2533
+ * Results are sorted by modification time (most recent first). Discovery is
2534
+ * intentionally opt-in: a provider wallet must never replace the canonical
2535
+ * BlockRun wallet merely because it was written more recently.
2534
2536
  *
2535
2537
  * @returns Array of wallet objects with privateKey and address
2536
2538
  */
2537
2539
  declare function scanWallets(): Array<{
2538
2540
  privateKey: string;
2539
2541
  address: string;
2542
+ source: string;
2540
2543
  }>;
2544
+ /**
2545
+ * List wallets from other applications, safe to show to a user.
2546
+ *
2547
+ * Unlike `scanWallets()`, no private key is returned and the address is derived
2548
+ * from the key rather than read from the file, so a wallet file cannot claim an
2549
+ * address it has no key for.
2550
+ *
2551
+ * Nothing here is active. Adopt one deliberately with `importWallet()`.
2552
+ *
2553
+ * @returns Discovered wallets as `{ address, source }`, most recent first
2554
+ */
2555
+ declare function listDiscoveredWallets(): Array<{
2556
+ address: string;
2557
+ source: string;
2558
+ }>;
2559
+ /**
2560
+ * Adopt a discovered wallet by address, making it the active BlockRun wallet.
2561
+ *
2562
+ * This is the deliberate migration path: automatic selection never adopts a
2563
+ * discovered wallet, but you can choose one whose funds you want to spend.
2564
+ * Matching is done against the address *derived from each discovered key*, so a
2565
+ * wallet file claiming someone else's address can never be selected by it.
2566
+ *
2567
+ * The current `~/.blockrun/.session` is backed up beside itself before being
2568
+ * overwritten, so adopting a wallet can't strand the funds in the old one.
2569
+ *
2570
+ * @param address Address to adopt, as shown by `listDiscoveredWallets()`
2571
+ * @returns The adopted address
2572
+ * @throws If no discovered wallet derives to that address
2573
+ */
2574
+ declare function importWallet(address: string): string;
2541
2575
  /**
2542
2576
  * Load wallet private key from file.
2543
2577
  *
2544
2578
  * Priority:
2545
- * 1. Scan ~/.* /wallet.json (any provider)
2546
- * 2. Legacy ~/.blockrun/.session
2547
- * 3. Legacy ~/.blockrun/wallet.key
2579
+ * 1. ~/.blockrun/.session
2580
+ * 2. ~/.blockrun/wallet.key (legacy)
2548
2581
  *
2549
2582
  * @returns Private key string or null if not found
2550
2583
  */
2551
2584
  declare function loadWallet(): string | null;
2585
+ /**
2586
+ * Warn when a new wallet was created while other provider wallets exist.
2587
+ *
2588
+ * Automatic selection deliberately ignores wallets discovered in other
2589
+ * applications' directories, so a user who previously relied on that discovery
2590
+ * would otherwise land on an empty wallet with no explanation of where their
2591
+ * funds went. This notice names the discovered addresses and tells them how to
2592
+ * import one on purpose.
2593
+ *
2594
+ * Addresses are derived from the discovered private key rather than read from
2595
+ * the file's "address" field, so a file claiming an address it cannot sign for
2596
+ * cannot trick the user into importing it.
2597
+ *
2598
+ * @param newAddress Address of the wallet that was just created
2599
+ * @returns Formatted notice, or null if nothing was discovered
2600
+ */
2601
+ declare function formatWalletMigrationNotice(newAddress: string): string | null;
2552
2602
  /**
2553
2603
  * Get existing wallet or create new one.
2554
2604
  *
2555
2605
  * Priority:
2556
2606
  * 1. BLOCKRUN_WALLET_KEY environment variable
2557
- * 2. Scan wallet.json files from providers
2558
- * 3. ~/.blockrun/.session file
2559
- * 4. ~/.blockrun/wallet.key file - legacy
2560
- * 5. Create new wallet
2607
+ * 2. ~/.blockrun/.session file
2608
+ * 3. ~/.blockrun/wallet.key file - legacy
2609
+ * 4. Create new wallet
2561
2610
  *
2562
2611
  * @returns WalletInfo with address, privateKey, and isNew flag
2563
2612
  */
@@ -2821,19 +2870,57 @@ declare function solanaKeyToBytes(privateKey: string): Promise<Uint8Array>;
2821
2870
  declare function solanaPublicKey(privateKey: string): Promise<string>;
2822
2871
  declare function saveSolanaWallet(privateKey: string): string;
2823
2872
  /**
2824
- * Scan ~/.<dir>/solana-wallet.json files from any provider.
2873
+ * Discover ~/.<dir>/solana-wallet.json files from other providers.
2825
2874
  *
2826
2875
  * Each file should contain JSON with "privateKey" and "address" fields.
2827
2876
  * Also checks ~/.brcc/wallet.json for BRCC wallets.
2828
- * Results are sorted by modification time (most recent first).
2877
+ * Results are sorted by modification time (most recent first). Discovery is
2878
+ * opt-in and never changes the active BlockRun wallet automatically.
2829
2879
  *
2830
2880
  * @returns Array of wallet objects with secretKey and publicKey
2831
2881
  */
2832
2882
  declare function scanSolanaWallets(): Array<{
2833
2883
  secretKey: string;
2834
2884
  publicKey: string;
2885
+ source: string;
2835
2886
  }>;
2836
2887
  declare function loadSolanaWallet(): string | null;
2888
+ /**
2889
+ * List Solana wallets from other applications, safe to show to a user.
2890
+ *
2891
+ * Solana counterpart of `listDiscoveredWallets()`: no secret key is returned
2892
+ * and the address is derived from the key rather than trusted from the file.
2893
+ * Nothing here is active — adopt one with `importSolanaWallet()`.
2894
+ *
2895
+ * @returns Discovered wallets as `{ address, source }`, most recent first
2896
+ */
2897
+ declare function listDiscoveredSolanaWallets(): Promise<Array<{
2898
+ address: string;
2899
+ source: string;
2900
+ }>>;
2901
+ /**
2902
+ * Adopt a discovered Solana wallet, making it the active BlockRun wallet.
2903
+ *
2904
+ * Solana counterpart of `importWallet()`. Matching is done against the address
2905
+ * derived from each discovered key, and the current
2906
+ * `~/.blockrun/.solana-session` is backed up before being overwritten.
2907
+ *
2908
+ * @param address Address to adopt, as shown by `listDiscoveredSolanaWallets()`
2909
+ * @returns The adopted address
2910
+ * @throws If no discovered wallet derives to that address
2911
+ */
2912
+ declare function importSolanaWallet(address: string): Promise<string>;
2913
+ /**
2914
+ * Warn when a new Solana wallet was created while provider wallets exist.
2915
+ *
2916
+ * Solana counterpart of `formatWalletMigrationNotice`. Addresses are derived
2917
+ * from the discovered secret key rather than trusted from the file's "address"
2918
+ * field.
2919
+ *
2920
+ * @param newAddress Address of the wallet that was just created
2921
+ * @returns Formatted notice, or null if nothing was discovered
2922
+ */
2923
+ declare function formatSolanaWalletMigrationNotice(newAddress: string): Promise<string | null>;
2837
2924
  declare function getOrCreateSolanaWallet(): Promise<SolanaWalletInfo>;
2838
2925
 
2839
2926
  declare function getCached(key: string): unknown | null;
@@ -3104,4 +3191,4 @@ declare function validateTemperature(temperature?: number): void;
3104
3191
  */
3105
3192
  declare function validateTopP(topP?: number): void;
3106
3193
 
3107
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, 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, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, 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 VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, 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 };
3194
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, 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, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, 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 VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatSolanaWalletMigrationNotice, formatWalletCreatedMessage, formatWalletMigrationNotice, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, importSolanaWallet, importWallet, listDiscoveredSolanaWallets, listDiscoveredWallets, 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
@@ -2527,37 +2527,86 @@ declare function createWallet(): {
2527
2527
  */
2528
2528
  declare function saveWallet(privateKey: string): string;
2529
2529
  /**
2530
- * Scan ~/.<dir>/wallet.json files from any provider (agentcash, etc.).
2530
+ * Discover ~/.<dir>/wallet.json files from other providers.
2531
2531
  *
2532
2532
  * Each file should contain JSON with "privateKey" and "address" fields.
2533
- * Results are sorted by modification time (most recent first).
2533
+ * Results are sorted by modification time (most recent first). Discovery is
2534
+ * intentionally opt-in: a provider wallet must never replace the canonical
2535
+ * BlockRun wallet merely because it was written more recently.
2534
2536
  *
2535
2537
  * @returns Array of wallet objects with privateKey and address
2536
2538
  */
2537
2539
  declare function scanWallets(): Array<{
2538
2540
  privateKey: string;
2539
2541
  address: string;
2542
+ source: string;
2540
2543
  }>;
2544
+ /**
2545
+ * List wallets from other applications, safe to show to a user.
2546
+ *
2547
+ * Unlike `scanWallets()`, no private key is returned and the address is derived
2548
+ * from the key rather than read from the file, so a wallet file cannot claim an
2549
+ * address it has no key for.
2550
+ *
2551
+ * Nothing here is active. Adopt one deliberately with `importWallet()`.
2552
+ *
2553
+ * @returns Discovered wallets as `{ address, source }`, most recent first
2554
+ */
2555
+ declare function listDiscoveredWallets(): Array<{
2556
+ address: string;
2557
+ source: string;
2558
+ }>;
2559
+ /**
2560
+ * Adopt a discovered wallet by address, making it the active BlockRun wallet.
2561
+ *
2562
+ * This is the deliberate migration path: automatic selection never adopts a
2563
+ * discovered wallet, but you can choose one whose funds you want to spend.
2564
+ * Matching is done against the address *derived from each discovered key*, so a
2565
+ * wallet file claiming someone else's address can never be selected by it.
2566
+ *
2567
+ * The current `~/.blockrun/.session` is backed up beside itself before being
2568
+ * overwritten, so adopting a wallet can't strand the funds in the old one.
2569
+ *
2570
+ * @param address Address to adopt, as shown by `listDiscoveredWallets()`
2571
+ * @returns The adopted address
2572
+ * @throws If no discovered wallet derives to that address
2573
+ */
2574
+ declare function importWallet(address: string): string;
2541
2575
  /**
2542
2576
  * Load wallet private key from file.
2543
2577
  *
2544
2578
  * Priority:
2545
- * 1. Scan ~/.* /wallet.json (any provider)
2546
- * 2. Legacy ~/.blockrun/.session
2547
- * 3. Legacy ~/.blockrun/wallet.key
2579
+ * 1. ~/.blockrun/.session
2580
+ * 2. ~/.blockrun/wallet.key (legacy)
2548
2581
  *
2549
2582
  * @returns Private key string or null if not found
2550
2583
  */
2551
2584
  declare function loadWallet(): string | null;
2585
+ /**
2586
+ * Warn when a new wallet was created while other provider wallets exist.
2587
+ *
2588
+ * Automatic selection deliberately ignores wallets discovered in other
2589
+ * applications' directories, so a user who previously relied on that discovery
2590
+ * would otherwise land on an empty wallet with no explanation of where their
2591
+ * funds went. This notice names the discovered addresses and tells them how to
2592
+ * import one on purpose.
2593
+ *
2594
+ * Addresses are derived from the discovered private key rather than read from
2595
+ * the file's "address" field, so a file claiming an address it cannot sign for
2596
+ * cannot trick the user into importing it.
2597
+ *
2598
+ * @param newAddress Address of the wallet that was just created
2599
+ * @returns Formatted notice, or null if nothing was discovered
2600
+ */
2601
+ declare function formatWalletMigrationNotice(newAddress: string): string | null;
2552
2602
  /**
2553
2603
  * Get existing wallet or create new one.
2554
2604
  *
2555
2605
  * Priority:
2556
2606
  * 1. BLOCKRUN_WALLET_KEY environment variable
2557
- * 2. Scan wallet.json files from providers
2558
- * 3. ~/.blockrun/.session file
2559
- * 4. ~/.blockrun/wallet.key file - legacy
2560
- * 5. Create new wallet
2607
+ * 2. ~/.blockrun/.session file
2608
+ * 3. ~/.blockrun/wallet.key file - legacy
2609
+ * 4. Create new wallet
2561
2610
  *
2562
2611
  * @returns WalletInfo with address, privateKey, and isNew flag
2563
2612
  */
@@ -2821,19 +2870,57 @@ declare function solanaKeyToBytes(privateKey: string): Promise<Uint8Array>;
2821
2870
  declare function solanaPublicKey(privateKey: string): Promise<string>;
2822
2871
  declare function saveSolanaWallet(privateKey: string): string;
2823
2872
  /**
2824
- * Scan ~/.<dir>/solana-wallet.json files from any provider.
2873
+ * Discover ~/.<dir>/solana-wallet.json files from other providers.
2825
2874
  *
2826
2875
  * Each file should contain JSON with "privateKey" and "address" fields.
2827
2876
  * Also checks ~/.brcc/wallet.json for BRCC wallets.
2828
- * Results are sorted by modification time (most recent first).
2877
+ * Results are sorted by modification time (most recent first). Discovery is
2878
+ * opt-in and never changes the active BlockRun wallet automatically.
2829
2879
  *
2830
2880
  * @returns Array of wallet objects with secretKey and publicKey
2831
2881
  */
2832
2882
  declare function scanSolanaWallets(): Array<{
2833
2883
  secretKey: string;
2834
2884
  publicKey: string;
2885
+ source: string;
2835
2886
  }>;
2836
2887
  declare function loadSolanaWallet(): string | null;
2888
+ /**
2889
+ * List Solana wallets from other applications, safe to show to a user.
2890
+ *
2891
+ * Solana counterpart of `listDiscoveredWallets()`: no secret key is returned
2892
+ * and the address is derived from the key rather than trusted from the file.
2893
+ * Nothing here is active — adopt one with `importSolanaWallet()`.
2894
+ *
2895
+ * @returns Discovered wallets as `{ address, source }`, most recent first
2896
+ */
2897
+ declare function listDiscoveredSolanaWallets(): Promise<Array<{
2898
+ address: string;
2899
+ source: string;
2900
+ }>>;
2901
+ /**
2902
+ * Adopt a discovered Solana wallet, making it the active BlockRun wallet.
2903
+ *
2904
+ * Solana counterpart of `importWallet()`. Matching is done against the address
2905
+ * derived from each discovered key, and the current
2906
+ * `~/.blockrun/.solana-session` is backed up before being overwritten.
2907
+ *
2908
+ * @param address Address to adopt, as shown by `listDiscoveredSolanaWallets()`
2909
+ * @returns The adopted address
2910
+ * @throws If no discovered wallet derives to that address
2911
+ */
2912
+ declare function importSolanaWallet(address: string): Promise<string>;
2913
+ /**
2914
+ * Warn when a new Solana wallet was created while provider wallets exist.
2915
+ *
2916
+ * Solana counterpart of `formatWalletMigrationNotice`. Addresses are derived
2917
+ * from the discovered secret key rather than trusted from the file's "address"
2918
+ * field.
2919
+ *
2920
+ * @param newAddress Address of the wallet that was just created
2921
+ * @returns Formatted notice, or null if nothing was discovered
2922
+ */
2923
+ declare function formatSolanaWalletMigrationNotice(newAddress: string): Promise<string | null>;
2837
2924
  declare function getOrCreateSolanaWallet(): Promise<SolanaWalletInfo>;
2838
2925
 
2839
2926
  declare function getCached(key: string): unknown | null;
@@ -3104,4 +3191,4 @@ declare function validateTemperature(temperature?: number): void;
3104
3191
  */
3105
3192
  declare function validateTopP(topP?: number): void;
3106
3193
 
3107
- export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, 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, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, 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 VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, 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 };
3194
+ export { APIError, AnthropicClient, type AudioModel, type AudioTrack, BASE_CHAIN_ID, type BarResolution, type BlockRunAnthropicOptions, BlockrunClient, type BlockrunClientOptions, 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, NETWORK_ALIASES, type NewsSearchSource, OpenAI, type OpenAIChatCompletionChoice, type OpenAIChatCompletionChunk, type OpenAIChatCompletionParams, type OpenAIChatCompletionResponse, type OpenAIClientOptions, PHONE_PRICES, PORTRAIT_ENROLLMENT_PRICE_USD, PaymentError, type PaymentLinks, type PhoneBuyOptions, type PhoneBuyResponse, PhoneClient, type PhoneClientOptions, type PhoneListResponse, type PhoneLookupResponse, type PhoneNumberRecord, type PhoneReleaseResponse, type PhoneRenewResponse, type PollOptions, PortraitClient, type PortraitClientOptions, type PortraitEnrollOptions, type PortraitEnrollResponse, type PriceBar, type PriceCategory, PriceClient, type PriceClientOptions, type PriceHistoryResponse, type PriceOptions, type PricePoint, RPC_PRICE_USD, type ResponseFormat, type RoutingDecision, type RoutingProfile, type RoutingTier, type RpcBatchRequest, RpcClient, type RpcClientOptions, type RpcError, type RpcNetwork, type RpcResponse, type RssSearchSource, SOLANA_NETWORK, SOLANA_WALLET_FILE as SOLANA_WALLET_FILE_PATH, SUPPORTED_NETWORKS, SearchClient, type SearchClientOptions, type SearchOptions, type SearchParameters, type SearchResult, type SearchSource, type SearchUsage, type SmartChatOptions, type SmartChatResponse, SolanaLLMClient, type SolanaLLMClientOptions, type SolanaWalletInfo, type SoundEffectOptions, type SpeechAudio, SpeechClient, type SpeechClientOptions, type SpeechGenerateOptions, type SpeechModel, type SpeechResponse, type SpeechVoice, 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 VoiceInfo, type VoicePreset, WALLET_DIR_PATH, WALLET_FILE_PATH, type WalletInfo, type WebSearchSource, type XSearchSource, clearCache, createPaymentPayload, createSolanaPaymentPayload, createSolanaWallet, createWallet, LLMClient as default, extractPaymentDetails, formatFundingMessageCompact, formatNeedsFundingMessage, formatSolanaWalletMigrationNotice, formatWalletCreatedMessage, formatWalletMigrationNotice, getCached, getCachedByRequest, getCostLogSummary, getCostSummary, getEip681Uri, getOrCreateSolanaWallet, getOrCreateWallet, getPaymentLinks, getWalletAddress, importSolanaWallet, importWallet, listDiscoveredSolanaWallets, listDiscoveredWallets, 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
@@ -4522,7 +4522,7 @@ function scanWallets() {
4522
4522
  const addr = data.address || "";
4523
4523
  if (pk && addr) {
4524
4524
  const mtime = fs2.statSync(walletFile).mtimeMs;
4525
- results.push({ mtime, privateKey: pk, address: addr });
4525
+ results.push({ mtime, privateKey: pk, address: addr, source: walletFile });
4526
4526
  }
4527
4527
  } catch {
4528
4528
  continue;
@@ -4531,11 +4531,48 @@ function scanWallets() {
4531
4531
  } catch {
4532
4532
  }
4533
4533
  results.sort((a, b) => b.mtime - a.mtime);
4534
- return results.map(({ privateKey, address }) => ({ privateKey, address }));
4534
+ return results.map(({ privateKey, address, source }) => ({ privateKey, address, source }));
4535
+ }
4536
+ function listDiscoveredWallets() {
4537
+ const listed = [];
4538
+ for (const entry of scanWallets()) {
4539
+ try {
4540
+ listed.push({
4541
+ address: privateKeyToAccount14(entry.privateKey).address,
4542
+ source: entry.source
4543
+ });
4544
+ } catch {
4545
+ continue;
4546
+ }
4547
+ }
4548
+ return listed;
4549
+ }
4550
+ function importWallet(address) {
4551
+ const wanted = address.trim().toLowerCase();
4552
+ for (const entry of scanWallets()) {
4553
+ let derived;
4554
+ try {
4555
+ derived = privateKeyToAccount14(entry.privateKey).address;
4556
+ } catch {
4557
+ continue;
4558
+ }
4559
+ if (derived.toLowerCase() !== wanted) continue;
4560
+ if (fs2.existsSync(WALLET_FILE)) {
4561
+ const current = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4562
+ if (current && current !== entry.privateKey) {
4563
+ const backup = path2.join(WALLET_DIR, `.session.backup-${Math.floor(Date.now() / 1e3)}`);
4564
+ fs2.writeFileSync(backup, current, { mode: 384 });
4565
+ }
4566
+ }
4567
+ saveWallet(entry.privateKey);
4568
+ return derived;
4569
+ }
4570
+ const available = listDiscoveredWallets().map((w) => w.address);
4571
+ throw new Error(
4572
+ `No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`
4573
+ );
4535
4574
  }
4536
4575
  function loadWallet() {
4537
- const wallets = scanWallets();
4538
- if (wallets.length > 0) return wallets[0].privateKey;
4539
4576
  if (fs2.existsSync(WALLET_FILE)) {
4540
4577
  const key = fs2.readFileSync(WALLET_FILE, "utf-8").trim();
4541
4578
  if (key) return key;
@@ -4547,6 +4584,46 @@ function loadWallet() {
4547
4584
  }
4548
4585
  return null;
4549
4586
  }
4587
+ function formatWalletMigrationNotice(newAddress) {
4588
+ let discovered;
4589
+ try {
4590
+ discovered = scanWallets();
4591
+ } catch {
4592
+ return null;
4593
+ }
4594
+ const addresses = [];
4595
+ for (const entry of discovered) {
4596
+ try {
4597
+ addresses.push(privateKeyToAccount14(entry.privateKey).address);
4598
+ } catch {
4599
+ continue;
4600
+ }
4601
+ }
4602
+ if (addresses.length === 0) return null;
4603
+ const found = addresses.map((addr) => ` ${addr}`).join("\n");
4604
+ return `
4605
+ NOTICE: BlockRun created a new wallet, but also found existing wallet(s)
4606
+ belonging to other applications on this system:
4607
+
4608
+ ${found}
4609
+
4610
+ BlockRun now uses only its own wallet:
4611
+
4612
+ ${newAddress}
4613
+
4614
+ Discovered wallets are never adopted automatically \u2014 one may belong to a
4615
+ different application, or have been planted to make you fund an address you
4616
+ do not control.
4617
+
4618
+ If an address above is yours and holds your USDC, adopt it deliberately:
4619
+
4620
+ import { importWallet } from '@blockrun/llm';
4621
+ importWallet("<address-from-the-list-above>");
4622
+
4623
+ Your current wallet is backed up first. You can also set
4624
+ BLOCKRUN_WALLET_KEY=<private-key> for a single run without changing anything.
4625
+ `;
4626
+ }
4550
4627
  function getOrCreateWallet() {
4551
4628
  const envKey = typeof process !== "undefined" && process.env ? process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY : void 0;
4552
4629
  if (envKey) {
@@ -4689,7 +4766,7 @@ function scanSolanaWallets() {
4689
4766
  const addr = data.address || "";
4690
4767
  if (pk && addr) {
4691
4768
  const mtime = fs3.statSync(solanaWalletFile).mtimeMs;
4692
- results.push({ mtime, secretKey: pk, publicKey: addr });
4769
+ results.push({ mtime, secretKey: pk, publicKey: addr, source: solanaWalletFile });
4693
4770
  }
4694
4771
  } catch {
4695
4772
  }
@@ -4703,7 +4780,7 @@ function scanSolanaWallets() {
4703
4780
  const addr = data.address || "";
4704
4781
  if (pk && addr) {
4705
4782
  const mtime = fs3.statSync(brccWalletFile).mtimeMs;
4706
- results.push({ mtime, secretKey: pk, publicKey: addr });
4783
+ results.push({ mtime, secretKey: pk, publicKey: addr, source: brccWalletFile });
4707
4784
  }
4708
4785
  } catch {
4709
4786
  }
@@ -4713,27 +4790,100 @@ function scanSolanaWallets() {
4713
4790
  } catch {
4714
4791
  }
4715
4792
  results.sort((a, b) => b.mtime - a.mtime);
4716
- return results.map(({ secretKey, publicKey }) => ({ secretKey, publicKey }));
4793
+ return results.map(({ secretKey, publicKey, source }) => ({ secretKey, publicKey, source }));
4717
4794
  }
4718
4795
  function loadSolanaWallet() {
4719
- const wallets = scanSolanaWallets();
4720
- if (wallets.length > 0) return wallets[0].secretKey;
4721
4796
  if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4722
4797
  const key = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4723
4798
  if (key) return key;
4724
4799
  }
4725
4800
  return null;
4726
4801
  }
4802
+ async function listDiscoveredSolanaWallets() {
4803
+ const listed = [];
4804
+ for (const entry of scanSolanaWallets()) {
4805
+ try {
4806
+ listed.push({ address: await solanaPublicKey(entry.secretKey), source: entry.source });
4807
+ } catch {
4808
+ continue;
4809
+ }
4810
+ }
4811
+ return listed;
4812
+ }
4813
+ async function importSolanaWallet(address) {
4814
+ const wanted = address.trim();
4815
+ for (const entry of scanSolanaWallets()) {
4816
+ let derived;
4817
+ try {
4818
+ derived = await solanaPublicKey(entry.secretKey);
4819
+ } catch {
4820
+ continue;
4821
+ }
4822
+ if (derived !== wanted) continue;
4823
+ if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4824
+ const current = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4825
+ if (current && current !== entry.secretKey) {
4826
+ const backup = path3.join(
4827
+ WALLET_DIR2,
4828
+ `.solana-session.backup-${Math.floor(Date.now() / 1e3)}`
4829
+ );
4830
+ fs3.writeFileSync(backup, current, { mode: 384 });
4831
+ }
4832
+ }
4833
+ saveSolanaWallet(entry.secretKey);
4834
+ return derived;
4835
+ }
4836
+ const available = (await listDiscoveredSolanaWallets()).map((w) => w.address);
4837
+ throw new Error(
4838
+ `No discovered wallet controls ${address}. Available: ${available.length ? available.join(", ") : "none"}`
4839
+ );
4840
+ }
4841
+ async function formatSolanaWalletMigrationNotice(newAddress) {
4842
+ let discovered;
4843
+ try {
4844
+ discovered = scanSolanaWallets();
4845
+ } catch {
4846
+ return null;
4847
+ }
4848
+ const addresses = [];
4849
+ for (const entry of discovered) {
4850
+ try {
4851
+ addresses.push(await solanaPublicKey(entry.secretKey));
4852
+ } catch {
4853
+ continue;
4854
+ }
4855
+ }
4856
+ if (addresses.length === 0) return null;
4857
+ const found = addresses.map((addr) => ` ${addr}`).join("\n");
4858
+ return `
4859
+ NOTICE: BlockRun created a new Solana wallet, but also found existing
4860
+ wallet(s) belonging to other applications on this system:
4861
+
4862
+ ${found}
4863
+
4864
+ BlockRun now uses only its own wallet:
4865
+
4866
+ ${newAddress}
4867
+
4868
+ Discovered wallets are never adopted automatically \u2014 one may belong to a
4869
+ different application, or have been planted to make you fund an address you
4870
+ do not control.
4871
+
4872
+ If an address above is yours and holds your USDC, adopt it deliberately:
4873
+
4874
+ import { importSolanaWallet } from '@blockrun/llm';
4875
+ await importSolanaWallet("<address-from-the-list-above>");
4876
+
4877
+ Your current wallet is backed up first. You can also set
4878
+ SOLANA_WALLET_KEY=<private-key> for a single run without changing anything.
4879
+ `;
4880
+ }
4727
4881
  async function getOrCreateSolanaWallet() {
4728
4882
  const envKey = typeof process !== "undefined" && process.env ? process.env.SOLANA_WALLET_KEY : void 0;
4729
4883
  if (envKey) {
4730
4884
  const address2 = await solanaPublicKey(envKey);
4731
4885
  return { privateKey: envKey, address: address2, isNew: false };
4732
4886
  }
4733
- const wallets = scanSolanaWallets();
4734
- if (wallets.length > 0) {
4735
- return { privateKey: wallets[0].secretKey, address: wallets[0].publicKey, isNew: false };
4736
- }
4737
4887
  if (fs3.existsSync(SOLANA_WALLET_FILE)) {
4738
4888
  const fileKey = fs3.readFileSync(SOLANA_WALLET_FILE, "utf-8").trim();
4739
4889
  if (fileKey) {
@@ -5558,27 +5708,35 @@ function getCostLogSummary() {
5558
5708
  // src/setup.ts
5559
5709
  function setupAgentWallet(options) {
5560
5710
  const { address, privateKey, isNew } = getOrCreateWallet();
5561
- if (isNew && !options?.silent) {
5562
- console.error(
5563
- `
5711
+ if (isNew) {
5712
+ const notice = formatWalletMigrationNotice(address);
5713
+ if (notice) console.error(notice);
5714
+ if (!options?.silent) {
5715
+ console.error(
5716
+ `
5564
5717
  BlockRun Agent Wallet Created!
5565
5718
  Address: ${address}
5566
5719
  Send USDC on Base to get started.
5567
5720
  `
5568
- );
5721
+ );
5722
+ }
5569
5723
  }
5570
5724
  return new LLMClient({ privateKey });
5571
5725
  }
5572
5726
  async function setupAgentSolanaWallet(options) {
5573
5727
  const result = await getOrCreateSolanaWallet();
5574
- if (result.isNew && !options?.silent) {
5575
- console.error(
5576
- `
5728
+ if (result.isNew) {
5729
+ const notice = await formatSolanaWalletMigrationNotice(result.address);
5730
+ if (notice) console.error(notice);
5731
+ if (!options?.silent) {
5732
+ console.error(
5733
+ `
5577
5734
  BlockRun Solana Agent Wallet Created!
5578
5735
  Address: ${result.address}
5579
5736
  Send USDC on Solana to get started.
5580
5737
  `
5581
- );
5738
+ );
5739
+ }
5582
5740
  }
5583
5741
  return new SolanaLLMClient({ privateKey: result.privateKey });
5584
5742
  }
@@ -5891,7 +6049,9 @@ export {
5891
6049
  extractPaymentDetails,
5892
6050
  formatFundingMessageCompact,
5893
6051
  formatNeedsFundingMessage,
6052
+ formatSolanaWalletMigrationNotice,
5894
6053
  formatWalletCreatedMessage,
6054
+ formatWalletMigrationNotice,
5895
6055
  getCached,
5896
6056
  getCachedByRequest,
5897
6057
  getCostLogSummary,
@@ -5901,6 +6061,10 @@ export {
5901
6061
  getOrCreateWallet,
5902
6062
  getPaymentLinks,
5903
6063
  getWalletAddress,
6064
+ importSolanaWallet,
6065
+ importWallet,
6066
+ listDiscoveredSolanaWallets,
6067
+ listDiscoveredWallets,
5904
6068
  loadSolanaWallet,
5905
6069
  loadWallet,
5906
6070
  logCost,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/llm",
3
- "version": "3.7.0",
3
+ "version": "3.8.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",