@alchemy/cli 0.7.1 → 0.7.2-alpha.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,18 +2,20 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  registerAuth
5
- } from "./chunk-SYP6KKP6.js";
5
+ } from "./chunk-XSN4XA5Z.js";
6
6
  import {
7
7
  openBrowser
8
- } from "./chunk-HSKKIATB.js";
8
+ } from "./chunk-C5HNQOLB.js";
9
9
  import {
10
+ SETUP_CAPABILITY_LABELS,
11
+ SETUP_CAPABILITY_ORDER,
10
12
  getSetupStatus,
11
13
  isSetupComplete,
12
14
  shouldRunOnboarding
13
- } from "./chunk-V4IK4CJN.js";
15
+ } from "./chunk-R4W44A6E.js";
14
16
  import {
15
17
  isInteractiveAllowed
16
- } from "./chunk-HYCRHNPX.js";
18
+ } from "./chunk-64A5W4M2.js";
17
19
  import {
18
20
  adminClientFromFlags,
19
21
  clearSession,
@@ -41,12 +43,12 @@ import {
41
43
  resolveX402Client,
42
44
  saveSession,
43
45
  updateSession
44
- } from "./chunk-TOEVZMIP.js";
46
+ } from "./chunk-K6V3R7SH.js";
45
47
  import {
46
48
  getAvailableUpdate,
47
49
  getUpdateStatus,
48
50
  printUpdateNotice
49
- } from "./chunk-RS3DSL3X.js";
51
+ } from "./chunk-YQZLLSGS.js";
50
52
  import {
51
53
  bold,
52
54
  brand,
@@ -70,7 +72,7 @@ import {
70
72
  weiToEth,
71
73
  withSpinner,
72
74
  yellow
73
- } from "./chunk-5IFXLC2S.js";
75
+ } from "./chunk-5IL2PMZ6.js";
74
76
  import {
75
77
  KEY_MAP,
76
78
  configDir,
@@ -81,7 +83,7 @@ import {
81
83
  save,
82
84
  toMap,
83
85
  validKeys
84
- } from "./chunk-7WD3YLRK.js";
86
+ } from "./chunk-JUCUKTP3.js";
85
87
  import {
86
88
  CLIError,
87
89
  EXIT_CODES,
@@ -108,6 +110,7 @@ import {
108
110
  identity,
109
111
  isJSONMode,
110
112
  isLocalhost,
113
+ isReplMode,
111
114
  noColor,
112
115
  parseBaseURLOverride,
113
116
  printHuman,
@@ -116,7 +119,7 @@ import {
116
119
  setFlags,
117
120
  setNoColor,
118
121
  verbose
119
- } from "./chunk-QEDAULQ2.js";
122
+ } from "./chunk-2BALTY22.js";
120
123
 
121
124
  // src/index.ts
122
125
  import { Command, Help } from "commander";
@@ -623,7 +626,7 @@ function registerConfig(program2) {
623
626
  printJSON(toMap(cfg));
624
627
  return;
625
628
  }
626
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-R4JZZCCF.js");
629
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-WXT5ZCUK.js");
627
630
  const validToken = resolveAuthToken2(cfg);
628
631
  const authStatus = cfg.auth_token ? validToken ? `${green("\u2713")} authenticated${cfg.auth_token_expires_at ? ` ${dim(`(expires ${cfg.auth_token_expires_at})`)}` : ""}` : `${yellow("\u25C6")} expired${cfg.auth_token_expires_at ? ` ${dim(`(${cfg.auth_token_expires_at})`)}` : ""}` : dim("(not set) \u2014 run 'alchemy auth' to log in");
629
632
  const pairs = [
@@ -737,6 +740,15 @@ function registerConfig(program2) {
737
740
  console.log(` ${dim(`- ${command}`)}`);
738
741
  }
739
742
  }
743
+ console.log("");
744
+ console.log(` ${dim("Capabilities:")}`);
745
+ printKeyValue(
746
+ SETUP_CAPABILITY_ORDER.map((capability) => {
747
+ const capabilityStatus = status.capabilities[capability];
748
+ const value = capabilityStatus.complete ? `ready (${capabilityStatus.satisfiedBy})` : `missing ${capabilityStatus.missing.join(", ")}`;
749
+ return [SETUP_CAPABILITY_LABELS[capability], value];
750
+ })
751
+ );
740
752
  });
741
753
  }
742
754
 
@@ -2681,6 +2693,8 @@ function registerWallets(program2) {
2681
2693
  }
2682
2694
 
2683
2695
  // src/lib/rest.ts
2696
+ var DATA_API_BASE_URL_ENV = "ALCHEMY_DATA_API_BASE_URL";
2697
+ var PRICES_API_BASE_URL_ENV = "ALCHEMY_PRICES_API_BASE_URL";
2684
2698
  function withQuery(url, query) {
2685
2699
  if (!query) return url;
2686
2700
  for (const [key, value] of Object.entries(query)) {
@@ -2714,13 +2728,15 @@ async function requestJSON(url, options) {
2714
2728
  }
2715
2729
  async function callApiData(apiKey, path, options = {}) {
2716
2730
  if (!apiKey) throw errAuthRequired();
2717
- const base2 = new URL(`https://api.g.${getBaseDomain()}/data/v1/${apiKey}/`);
2731
+ const override = parseBaseURLOverride(DATA_API_BASE_URL_ENV);
2732
+ const base2 = override ? new URL(`/data/v1/${apiKey}/`, override) : new URL(`https://api.g.${getBaseDomain()}/data/v1/${apiKey}/`);
2718
2733
  const url = withQuery(new URL(path.replace(/^\//, ""), base2), options.query);
2719
2734
  return requestJSON(url, { ...options, path });
2720
2735
  }
2721
2736
  async function callApiPrices(apiKey, path, options = {}) {
2722
2737
  if (!apiKey) throw errAuthRequired();
2723
- const base2 = new URL(`https://api.g.${getBaseDomain()}/prices/v1/${apiKey}/`);
2738
+ const override = parseBaseURLOverride(PRICES_API_BASE_URL_ENV);
2739
+ const base2 = override ? new URL(`/prices/v1/${apiKey}/`, override) : new URL(`https://api.g.${getBaseDomain()}/prices/v1/${apiKey}/`);
2724
2740
  const url = withQuery(new URL(path.replace(/^\//, ""), base2), options.query);
2725
2741
  return requestJSON(url, { ...options, path });
2726
2742
  }
@@ -3631,7 +3647,7 @@ async function performSolanaSend(program2, toArg, amountArg, tokenAddress, opts
3631
3647
  }
3632
3648
 
3633
3649
  // src/lib/smart-wallet.ts
3634
- import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";
3650
+ import { createSmartWalletClient } from "@alchemy/wallet-apis";
3635
3651
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
3636
3652
 
3637
3653
  // src/lib/chains.ts
@@ -3970,6 +3986,17 @@ function createDelegatedAccount(args) {
3970
3986
  });
3971
3987
  }
3972
3988
 
3989
+ // src/lib/wallet-transport.ts
3990
+ import { alchemyWalletTransport } from "@alchemy/wallet-apis";
3991
+ var WALLET_RPC_BASE_URL_ENV = "ALCHEMY_WALLET_RPC_BASE_URL";
3992
+ function createAlchemyWalletTransport(apiKey) {
3993
+ const override = parseBaseURLOverride(WALLET_RPC_BASE_URL_ENV);
3994
+ return alchemyWalletTransport({
3995
+ apiKey,
3996
+ ...override ? { url: override.toString() } : {}
3997
+ });
3998
+ }
3999
+
3973
4000
  // src/lib/smart-wallet.ts
3974
4001
  function normalizeKey(key) {
3975
4002
  const trimmed = key.trim();
@@ -4068,7 +4095,7 @@ function buildWalletClient(program2, options = {}) {
4068
4095
  }
4069
4096
  const paymaster = gasSponsored && gasPolicyId ? { policyId: gasPolicyId } : void 0;
4070
4097
  const client = createSmartWalletClient({
4071
- transport: alchemyWalletTransport({ apiKey }),
4098
+ transport: createAlchemyWalletTransport(apiKey),
4072
4099
  chain,
4073
4100
  signer: signerConfig.signer,
4074
4101
  paymaster
@@ -4639,6 +4666,7 @@ var ERROR_RECOVERY = {
4639
4666
  RATE_LIMITED: "Wait and retry; consider upgrading your Alchemy plan",
4640
4667
  PAYMENT_REQUIRED: "Fund your x402 wallet or switch to API key auth",
4641
4668
  SETUP_REQUIRED: "Run preflight: alchemy --json --no-interactive config status, then follow nextCommands",
4669
+ QUOTE_FAILED: "Quote unavailable; check data.cause for the specific reason (INSUFFICIENT_BALANCE, INSUFFICIENT_LIQUIDITY, UNSUPPORTED_ROUTE, QUOTE_REVERTED, QUOTE_FAILED) and follow the hint",
4642
4670
  INTERNAL_ERROR: "Unexpected error; retry or report a bug"
4643
4671
  };
4644
4672
  function buildCommandSchema(cmd) {
@@ -4693,11 +4721,13 @@ function buildAgentPrompt(program2) {
4693
4721
  "For general capability and usage questions, prefer npx -y @alchemy/cli@latest --json --no-interactive agent-prompt so stale local installs or PATH shims do not hide new commands",
4694
4722
  "Use the user's installed alchemy binary only when executing commands against their local config or diagnosing their installed version",
4695
4723
  "If installed alchemy help contradicts latest npm help, compare alchemy --json --no-interactive version with npx -y @alchemy/cli@latest --json --no-interactive version and check command -v alchemy",
4696
- "Run alchemy --json --no-interactive update-check when you need to detect available CLI upgrades"
4724
+ "Run alchemy --json --no-interactive update-check when you need to detect available CLI upgrades",
4725
+ "For state-changing EVM commands, prefer preview flags such as --dry-run when available before asking the user to execute",
4726
+ "For large data reads, use output-size controls such as --limit or --summary when available"
4697
4727
  ],
4698
4728
  preflight: {
4699
4729
  command: "alchemy --json --no-interactive config status",
4700
- description: "Check auth readiness before first command. If complete is false, follow nextCommands in the response to configure auth."
4730
+ description: "Check auth readiness before first command. Use capabilities.rpc_data/admin_api/notify_webhooks/wallet_signing/x402 to scope setup to the intended command family."
4701
4731
  },
4702
4732
  runtimeDiscovery: {
4703
4733
  installed: {
@@ -4809,9 +4839,11 @@ function buildAgentPrompt(program2) {
4809
4839
  "ALCHEMY_ACCESS_KEY=ak_xxx alchemy --json --no-interactive app list",
4810
4840
  "alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY",
4811
4841
  "alchemy --json --no-interactive evm network list",
4812
- "alchemy --json --no-interactive evm send 0xRecipient 0.001 -n eth-sepolia",
4842
+ "alchemy --json --no-interactive evm send 0xRecipient 0.001 --dry-run -n eth-sepolia",
4813
4843
  `alchemy --json --no-interactive evm contract read 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "balanceOf(address)(uint256)" --args '["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]' -n eth-mainnet`,
4814
- "alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 -n eth-mainnet",
4844
+ "alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet",
4845
+ "alchemy --json --no-interactive evm logs --from-block latest --limit 25",
4846
+ "alchemy --json --no-interactive evm block latest --summary",
4815
4847
  "alchemy --json --no-interactive evm status 0xCallId -n eth-mainnet"
4816
4848
  ],
4817
4849
  docs: "https://www.alchemy.com/docs"
@@ -5153,15 +5185,26 @@ async function performApprove(program2, spenderArg, opts) {
5153
5185
  }
5154
5186
 
5155
5187
  // src/commands/block.ts
5188
+ function summarizeBlock(block) {
5189
+ return {
5190
+ number: block.number ?? null,
5191
+ hash: block.hash ?? null,
5192
+ timestamp: block.timestamp ?? null,
5193
+ transactionCount: Array.isArray(block.transactions) ? block.transactions.length : null,
5194
+ miner: block.miner ?? null,
5195
+ gasUsed: block.gasUsed ?? null,
5196
+ gasLimit: block.gasLimit ?? null
5197
+ };
5198
+ }
5156
5199
  function registerBlock(program2) {
5157
- program2.command("block").argument("<number>", "Block number, hex (0x...), or tag (latest, earliest, pending)").description("Get block details by number").addHelpText(
5200
+ program2.command("block").argument("<number>", "Block number, hex (0x...), or tag (latest, earliest, pending)").description("Get block details by number").option("--summary", "Return compact block summary in JSON mode").addHelpText(
5158
5201
  "after",
5159
5202
  `
5160
5203
  Examples:
5161
5204
  alchemy evm block latest
5162
5205
  alchemy evm block 17000000
5163
5206
  alchemy evm block 0x1`
5164
- ).action(async (blockId) => {
5207
+ ).action(async (blockId, opts) => {
5165
5208
  try {
5166
5209
  let blockParam;
5167
5210
  if (["latest", "earliest", "pending"].includes(blockId)) {
@@ -5188,7 +5231,7 @@ Examples:
5188
5231
  );
5189
5232
  if (!block) throw errNotFound(`block ${blockId}`);
5190
5233
  if (isJSONMode()) {
5191
- printJSON(block);
5234
+ printJSON(opts.summary ? summarizeBlock(block) : block);
5192
5235
  return;
5193
5236
  }
5194
5237
  const pairs = [];
@@ -6236,60 +6279,74 @@ async function runDataCall(program2, title, path, body) {
6236
6279
  () => x402 ? x402.callRest(`data/v1${path}`, { method: "POST", body }) : callApiData(resolveAPIKey(program2), path, { method: "POST", body })
6237
6280
  );
6238
6281
  }
6282
+ function limitPayload(value, limit) {
6283
+ if (limit === void 0) return value;
6284
+ if (Array.isArray(value)) {
6285
+ return value.slice(0, limit);
6286
+ }
6287
+ if (value && typeof value === "object") {
6288
+ return Object.fromEntries(
6289
+ Object.entries(value).map(([key, nested]) => [
6290
+ key,
6291
+ Array.isArray(nested) ? nested.slice(0, limit) : nested
6292
+ ])
6293
+ );
6294
+ }
6295
+ return value;
6296
+ }
6297
+ async function runPortfolioCommand(program2, title, path, opts) {
6298
+ const limit = parseOptionalInt(opts.limit, "--limit");
6299
+ const result = await runDataCall(program2, title, path, JSON.parse(opts.body));
6300
+ const output = limitPayload(result, limit);
6301
+ if (isJSONMode()) printJSON(output);
6302
+ else printSyntaxJSON(output);
6303
+ }
6239
6304
  function registerPortfolio(program2) {
6240
6305
  const cmd = program2.command("portfolio").description("Portfolio API wrappers");
6241
- cmd.command("tokens").description("Get token portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/by-address").action(async (opts) => {
6306
+ cmd.command("tokens").description("Get token portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/by-address").option("--limit <n>", "Limit arrays in output").action(async (opts) => {
6242
6307
  try {
6243
- const result = await runDataCall(
6308
+ await runPortfolioCommand(
6244
6309
  program2,
6245
6310
  "token portfolio",
6246
6311
  "/assets/tokens/by-address",
6247
- JSON.parse(opts.body)
6312
+ opts
6248
6313
  );
6249
- if (isJSONMode()) printJSON(result);
6250
- else printSyntaxJSON(result);
6251
6314
  } catch (err) {
6252
6315
  exitWithError(err);
6253
6316
  }
6254
6317
  });
6255
- cmd.command("token-balances").description("Get token balances by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/balances/by-address").action(async (opts) => {
6318
+ cmd.command("token-balances").description("Get token balances by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/balances/by-address").option("--limit <n>", "Limit arrays in output").action(async (opts) => {
6256
6319
  try {
6257
- const result = await runDataCall(
6320
+ await runPortfolioCommand(
6258
6321
  program2,
6259
6322
  "token balances",
6260
6323
  "/assets/tokens/balances/by-address",
6261
- JSON.parse(opts.body)
6324
+ opts
6262
6325
  );
6263
- if (isJSONMode()) printJSON(result);
6264
- else printSyntaxJSON(result);
6265
6326
  } catch (err) {
6266
6327
  exitWithError(err);
6267
6328
  }
6268
6329
  });
6269
- cmd.command("nfts").description("Get NFT portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/by-address").action(async (opts) => {
6330
+ cmd.command("nfts").description("Get NFT portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/by-address").option("--limit <n>", "Limit arrays in output").action(async (opts) => {
6270
6331
  try {
6271
- const result = await runDataCall(
6332
+ await runPortfolioCommand(
6272
6333
  program2,
6273
6334
  "NFT portfolio",
6274
6335
  "/assets/nfts/by-address",
6275
- JSON.parse(opts.body)
6336
+ opts
6276
6337
  );
6277
- if (isJSONMode()) printJSON(result);
6278
- else printSyntaxJSON(result);
6279
6338
  } catch (err) {
6280
6339
  exitWithError(err);
6281
6340
  }
6282
6341
  });
6283
- cmd.command("nft-contracts").description("Get NFT contracts by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/contracts/by-address").action(async (opts) => {
6342
+ cmd.command("nft-contracts").description("Get NFT contracts by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/contracts/by-address").option("--limit <n>", "Limit arrays in output").action(async (opts) => {
6284
6343
  try {
6285
- const result = await runDataCall(
6344
+ await runPortfolioCommand(
6286
6345
  program2,
6287
6346
  "NFT contracts",
6288
6347
  "/assets/nfts/contracts/by-address",
6289
- JSON.parse(opts.body)
6348
+ opts
6290
6349
  );
6291
- if (isJSONMode()) printJSON(result);
6292
- else printSyntaxJSON(result);
6293
6350
  } catch (err) {
6294
6351
  exitWithError(err);
6295
6352
  }
@@ -6453,8 +6510,9 @@ Examples:
6453
6510
  alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --from-block 18000000 --to-block 18000010
6454
6511
  alchemy evm logs --address 0xdAC17F958D2ee523a2206206994597C13D831ec7 --topic 0xddf252ad...
6455
6512
  alchemy evm logs --from-block latest --json`
6456
- ).option("--address <address>", "Contract address to filter logs").option("--topic <topic...>", "Event topic(s) to filter (topic0, topic1, ...)").option("--from-block <block>", "Start block (number, hex, or tag)", "latest").option("--to-block <block>", "End block (number, hex, or tag)", "latest").action(async (opts) => {
6513
+ ).option("--address <address>", "Contract address to filter logs").option("--topic <topic...>", "Event topic(s) to filter (topic0, topic1, ...)").option("--from-block <block>", "Start block (number, hex, or tag)", "latest").option("--to-block <block>", "End block (number, hex, or tag)", "latest").option("--limit <n>", "Limit returned logs in output").action(async (opts) => {
6457
6514
  try {
6515
+ const outputLimit = parseOptionalInt(opts.limit, "--limit");
6458
6516
  const filter = {
6459
6517
  fromBlock: normalizeBlockParam(opts.fromBlock),
6460
6518
  toBlock: normalizeBlockParam(opts.toBlock)
@@ -6472,20 +6530,41 @@ Examples:
6472
6530
  () => client.call("eth_getLogs", [filter])
6473
6531
  );
6474
6532
  const network = resolveNetwork(program2);
6533
+ const displayedLogs = outputLimit === void 0 ? logs : logs.slice(0, outputLimit);
6475
6534
  if (isJSONMode()) {
6476
- printJSON({ logs, count: logs.length, network });
6535
+ printJSON({
6536
+ logs: displayedLogs,
6537
+ count: displayedLogs.length,
6538
+ network,
6539
+ ...outputLimit !== void 0 ? {
6540
+ totalCount: logs.length,
6541
+ truncated: displayedLogs.length < logs.length
6542
+ } : {}
6543
+ });
6477
6544
  return;
6478
6545
  }
6479
6546
  if (logs.length === 0) {
6480
6547
  console.log(dim("No logs found for the given filter."));
6481
6548
  return;
6482
6549
  }
6550
+ if (displayedLogs.length === 0) {
6551
+ console.log(dim(`Found ${logs.length} log${logs.length === 1 ? "" : "s"}; --limit 0 hides output.`));
6552
+ return;
6553
+ }
6483
6554
  const total = logs.length;
6484
6555
  const interactive = isInteractiveAllowed(program2);
6556
+ const limitSummary = displayedLogs.length < total ? `Showing ${displayedLogs.length} of ${total} logs on ${network}
6557
+ ` : `Found ${total} log${total === 1 ? "" : "s"} on ${network}
6558
+ `;
6559
+ if (outputLimit !== void 0) {
6560
+ console.log(limitSummary);
6561
+ printTable(TABLE_HEADERS2, formatLogRows(displayedLogs));
6562
+ return;
6563
+ }
6485
6564
  if (!interactive) {
6486
6565
  console.log(`Found ${total} log${total === 1 ? "" : "s"} on ${network}
6487
6566
  `);
6488
- printTable(TABLE_HEADERS2, formatLogRows(logs));
6567
+ printTable(TABLE_HEADERS2, formatLogRows(displayedLogs));
6489
6568
  } else {
6490
6569
  let offset = 0;
6491
6570
  const firstPage = logs.slice(0, PAGE_SIZE);
@@ -6615,8 +6694,15 @@ Examples:
6615
6694
  // src/commands/send-evm.ts
6616
6695
  import { encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi2 } from "viem";
6617
6696
  var NATIVE_EVM_DECIMALS = 18;
6697
+ function formatCallPreview(call) {
6698
+ return {
6699
+ to: call.to,
6700
+ ...call.value !== void 0 ? { value: call.value.toString() } : {},
6701
+ ...call.data ? { data: call.data } : {}
6702
+ };
6703
+ }
6618
6704
  function registerEvmSend(program2) {
6619
- const sendCmd = program2.command("send").description("Send native tokens or ERC-20 tokens to an address").argument("<to>", "Recipient address (0x... or ENS name)").argument("<amount>", "Amount to send (human-readable, e.g. 1.5)").option("--token <address>", "ERC-20 token contract address (omit for native token)");
6705
+ const sendCmd = program2.command("send").description("Send native tokens or ERC-20 tokens to an address").argument("<to>", "Recipient address (0x... or ENS name)").argument("<amount>", "Amount to send (human-readable, e.g. 1.5)").option("--token <address>", "ERC-20 token contract address (omit for native token)").option("--dry-run", "Preview transaction without signing or sending");
6620
6706
  addSignerOption(sendCmd);
6621
6707
  sendCmd.option("--gas-sponsored", "Enable gas sponsorship (env: ALCHEMY_EVM_GAS_SPONSORED)").option("--gas-policy-id <id>", "Gas policy ID for sponsorship (env: ALCHEMY_EVM_GAS_POLICY_ID)").addHelpText(
6622
6708
  "after",
@@ -6625,16 +6711,18 @@ Examples:
6625
6711
  alchemy evm send 0xAbC...123 1.5 Send 1.5 ETH
6626
6712
  alchemy evm send vitalik.eth 0.1 -n base-mainnet Send 0.1 ETH on Base
6627
6713
  alchemy evm send 0xAbC...123 100 --token 0xA0b8...USDC Send 100 USDC
6714
+ alchemy evm send 0xAbC...123 1.5 --dry-run Preview without signing or sending
6628
6715
  alchemy evm send 0xAbC...123 1 --gas-sponsored --gas-policy-id <id>
6629
6716
  alchemy evm send 0xAbC...123 1.5 --signer local Force the local wallet`
6630
6717
  ).action(async (toArg, amountArg, _opts, cmd) => {
6631
6718
  try {
6632
6719
  const opts = cmd.opts();
6633
6720
  await performEvmSend(cmd, toArg, amountArg, opts.token, {
6634
- signer: parseSignerOpt(opts.signer)
6721
+ signer: parseSignerOpt(opts.signer),
6722
+ dryRun: opts.dryRun
6635
6723
  });
6636
6724
  } catch (err) {
6637
- const { exitWithError: exitWithError2 } = await import("./errors-3CNFGAXT.js");
6725
+ const { exitWithError: exitWithError2 } = await import("./errors-E2P6WHTX.js");
6638
6726
  exitWithError2(err);
6639
6727
  }
6640
6728
  });
@@ -6665,6 +6753,37 @@ async function performEvmSend(program2, toArg, amountArg, tokenAddress, opts = {
6665
6753
  args: [to, wei]
6666
6754
  })
6667
6755
  }] : [{ to, value: wei }];
6756
+ if (opts.dryRun) {
6757
+ const previewCalls = calls.map(formatCallPreview);
6758
+ if (isJSONMode()) {
6759
+ printJSON({
6760
+ dryRun: true,
6761
+ action: "evm-send",
6762
+ from,
6763
+ to,
6764
+ amount: amountArg,
6765
+ token: tokenAddress ?? symbol,
6766
+ tokenAddress: tokenAddress ?? null,
6767
+ network,
6768
+ sponsored: !!paymaster,
6769
+ calls: previewCalls
6770
+ });
6771
+ } else {
6772
+ const pairs = [
6773
+ ["Dry Run", "yes"],
6774
+ ["From", from],
6775
+ ["To", to],
6776
+ ["Amount", green(`${amountArg} ${symbol}`)],
6777
+ ["Network", network],
6778
+ ["Calls", String(previewCalls.length)]
6779
+ ];
6780
+ if (paymaster) {
6781
+ pairs.push(["Gas", green("Sponsored")]);
6782
+ }
6783
+ printKeyValue(pairs);
6784
+ }
6785
+ return;
6786
+ }
6668
6787
  const { id } = await withSpinner(
6669
6788
  "Sending transaction\u2026",
6670
6789
  "Transaction submitted",
@@ -6769,6 +6888,105 @@ function registerSimulate(program2) {
6769
6888
  import {
6770
6889
  swapActions
6771
6890
  } from "@alchemy/wallet-apis/experimental";
6891
+
6892
+ // src/lib/wallet-quote-client.ts
6893
+ import { createClient } from "viem";
6894
+ import { parseAccount } from "viem/accounts";
6895
+ function buildWalletQuoteClient(program2, address2) {
6896
+ const apiKey = resolveAPIKey(program2);
6897
+ if (!apiKey) throw errAuthRequired();
6898
+ const cfg = load();
6899
+ const network = resolveNetwork(program2, cfg);
6900
+ const chain = networkToChain(network);
6901
+ const client = createClient({
6902
+ account: parseAccount(address2),
6903
+ transport: createAlchemyWalletTransport(apiKey),
6904
+ chain,
6905
+ name: "alchemyQuoteClient"
6906
+ });
6907
+ if (typeof client.extend !== "function") {
6908
+ throw new Error("Quote client missing extend(); @alchemy/wallet-apis or viem may have changed.");
6909
+ }
6910
+ return {
6911
+ client,
6912
+ network,
6913
+ chain,
6914
+ address: address2,
6915
+ paymaster: void 0
6916
+ };
6917
+ }
6918
+
6919
+ // src/lib/quote-errors.ts
6920
+ function errorText(err) {
6921
+ if (err instanceof CLIError) {
6922
+ return [err.message, err.details].filter(Boolean).join(" ");
6923
+ }
6924
+ if (err instanceof Error) {
6925
+ return err.message;
6926
+ }
6927
+ return String(err);
6928
+ }
6929
+ function classifyQuoteFailure(text) {
6930
+ const lower = text.toLowerCase();
6931
+ if (/liquidity|no route|route not found|cannot find route|unable to find route|no quote/.test(lower)) {
6932
+ return "INSUFFICIENT_LIQUIDITY";
6933
+ }
6934
+ if (/insufficient (funds|balance)|exceeds balance|not enough (funds|balance)|balance too low/.test(lower)) {
6935
+ return "INSUFFICIENT_BALANCE";
6936
+ }
6937
+ if (/unsupported|not supported|unsupported route|unsupported token|unsupported chain|unsupported network/.test(lower)) {
6938
+ return "UNSUPPORTED_ROUTE";
6939
+ }
6940
+ if (/execution reverted|revert|call exception|internal json-rpc|rpc error|-32603/.test(lower)) {
6941
+ return "QUOTE_REVERTED";
6942
+ }
6943
+ return "QUOTE_FAILED";
6944
+ }
6945
+ function quoteMessage(flow, cause) {
6946
+ const label = flow === "swap" ? "Swap" : "Bridge";
6947
+ switch (cause) {
6948
+ case "INSUFFICIENT_BALANCE":
6949
+ return `${label} quote unavailable: insufficient balance for the requested amount.`;
6950
+ case "INSUFFICIENT_LIQUIDITY":
6951
+ return `${label} quote unavailable: no route or insufficient liquidity for this trade.`;
6952
+ case "UNSUPPORTED_ROUTE":
6953
+ return `${label} quote unavailable: unsupported token, chain, or route.`;
6954
+ case "QUOTE_REVERTED":
6955
+ return `${label} quote unavailable: quote simulation reverted.`;
6956
+ case "QUOTE_FAILED":
6957
+ return `${label} quote unavailable.`;
6958
+ }
6959
+ }
6960
+ function quoteHint(flow, cause) {
6961
+ switch (cause) {
6962
+ case "INSUFFICIENT_BALANCE":
6963
+ return "Lower --amount or quote from an address that holds enough source token.";
6964
+ case "INSUFFICIENT_LIQUIDITY":
6965
+ return flow === "swap" ? "Try a smaller --amount, different tokens, or a different mainnet." : "Try a smaller --amount, different tokens, or a different destination network.";
6966
+ case "UNSUPPORTED_ROUTE":
6967
+ return flow === "swap" ? "Check token addresses and use an EVM mainnet supported by swap." : "Check token addresses and source/destination EVM mainnets.";
6968
+ case "QUOTE_REVERTED":
6969
+ return "Try a smaller --amount or different route. Use --json for typed error details.";
6970
+ case "QUOTE_FAILED":
6971
+ return "Retry later or try a different amount, token, or network.";
6972
+ }
6973
+ }
6974
+ function normalizeQuoteError(err, flow) {
6975
+ if (err instanceof CLIError && err.code !== ErrorCode.RPC_ERROR && err.code !== ErrorCode.INTERNAL_ERROR) {
6976
+ return err;
6977
+ }
6978
+ const detail = errorText(err);
6979
+ const cause = classifyQuoteFailure(detail);
6980
+ return new CLIError(
6981
+ ErrorCode.QUOTE_FAILED,
6982
+ quoteMessage(flow, cause),
6983
+ quoteHint(flow, cause),
6984
+ detail || void 0,
6985
+ { cause }
6986
+ );
6987
+ }
6988
+
6989
+ // src/commands/swap.ts
6772
6990
  var NATIVE_TOKEN_ADDRESS2 = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
6773
6991
  var NATIVE_DECIMALS = 18;
6774
6992
  function isNativeToken2(address2) {
@@ -6791,11 +7009,12 @@ async function resolveTokenInfo(network, program2, tokenAddress) {
6791
7009
  throw errInvalidArgs(`Failed to resolve token info for ${tokenAddress}.${detail}`);
6792
7010
  }
6793
7011
  }
6794
- function createQuoteRequest(fromToken, toToken, fromAmount, slippagePercent, paymaster) {
7012
+ function createQuoteRequest(fromToken, toToken, fromAmount, slippagePercent, paymaster, account) {
6795
7013
  const request2 = {
6796
7014
  fromToken,
6797
7015
  toToken,
6798
7016
  fromAmount,
7017
+ ...account ? { account } : {},
6799
7018
  ...slippagePercent !== void 0 ? { slippage: slippagePercentToBasisPoints(slippagePercent) } : {},
6800
7019
  ...paymaster ? { capabilities: { paymaster } } : {}
6801
7020
  };
@@ -6842,7 +7061,7 @@ async function prepareQuoteForExecution(client, quote) {
6842
7061
  }
6843
7062
  function registerSwap(program2) {
6844
7063
  const cmd = program2.command("swap").description("Swap tokens on the same chain");
6845
- const quoteCmd = cmd.command("quote").description("Get a swap quote without executing").requiredOption("--from <token_address>", "Token address to swap from (use 0xEeee...EEeE for the native token)").requiredOption("--to <token_address>", "Token address to swap to (use 0xEeee...EEeE for the native token)").requiredOption("--amount <number>", "Amount to swap in decimal token units (for example, 1.5)").option("--slippage <percent>", "Max slippage percentage (omit to use the API default)");
7064
+ const quoteCmd = cmd.command("quote").description("Get a swap quote without executing").requiredOption("--from <token_address>", "Token address to swap from (use 0xEeee...EEeE for the native token)").requiredOption("--to <token_address>", "Token address to swap to (use 0xEeee...EEeE for the native token)").requiredOption("--amount <number>", "Amount to swap in decimal token units (for example, 1.5)").option("--from-address <address>", "Wallet/account address to quote from without changing signer selection").option("--slippage <percent>", "Max slippage percentage (omit to use the API default)");
6846
7065
  addSignerOption(quoteCmd);
6847
7066
  quoteCmd.addHelpText(
6848
7067
  "after",
@@ -6879,8 +7098,9 @@ async function performSwapQuote(program2, opts) {
6879
7098
  validateSwapNetwork(resolveNetwork(program2));
6880
7099
  validateAddress(opts.from);
6881
7100
  validateAddress(opts.to);
7101
+ if (opts.fromAddress) validateAddress(opts.fromAddress);
6882
7102
  const signer = parseSignerOpt(opts.signer);
6883
- const { client, network, paymaster } = buildWalletClient(program2, { signer });
7103
+ const { client, network, address: fromAddress, paymaster } = opts.fromAddress ? buildWalletQuoteClient(program2, opts.fromAddress) : buildWalletClient(program2, { signer });
6884
7104
  const swapClient = client.extend(swapActions);
6885
7105
  const fromInfo = await resolveTokenInfo(network, program2, opts.from);
6886
7106
  const rawAmount = parseAmount(opts.amount, fromInfo.decimals);
@@ -6888,17 +7108,23 @@ async function performSwapQuote(program2, opts) {
6888
7108
  if (slippage !== void 0 && (isNaN(slippage) || slippage < 0 || slippage > 100)) {
6889
7109
  throw errInvalidArgs("Slippage must be a number between 0 and 100.");
6890
7110
  }
6891
- const quote = await withSpinner(
6892
- "Fetching quote\u2026",
6893
- "Quote received",
6894
- () => swapClient.requestQuoteV0(createQuoteRequest(opts.from, opts.to, rawAmount, slippage, paymaster))
6895
- );
7111
+ let quote;
7112
+ try {
7113
+ quote = await withSpinner(
7114
+ "Fetching quote\u2026",
7115
+ "Quote received",
7116
+ () => swapClient.requestQuoteV0(createQuoteRequest(opts.from, opts.to, rawAmount, slippage, paymaster, fromAddress))
7117
+ );
7118
+ } catch (err) {
7119
+ throw normalizeQuoteError(err, "swap");
7120
+ }
6896
7121
  const toInfo = await resolveTokenInfo(network, program2, opts.to);
6897
7122
  const quoteData = extractQuoteData(quote);
6898
7123
  if (isJSONMode()) {
6899
7124
  printJSON({
6900
7125
  fromToken: opts.from,
6901
7126
  toToken: opts.to,
7127
+ fromAddress,
6902
7128
  fromAmount: opts.amount,
6903
7129
  fromSymbol: fromInfo.symbol,
6904
7130
  toSymbol: toInfo.symbol,
@@ -6917,6 +7143,7 @@ async function performSwapQuote(program2, opts) {
6917
7143
  pairs.push(["To", `${toInfo.symbol}`]);
6918
7144
  }
6919
7145
  pairs.push(
7146
+ ["Wallet", fromAddress],
6920
7147
  ["Slippage", slippage === void 0 ? "API default" : `${slippage}%`],
6921
7148
  ["Network", network]
6922
7149
  );
@@ -6936,11 +7163,16 @@ async function performSwapExecute(program2, opts) {
6936
7163
  if (slippage !== void 0 && (isNaN(slippage) || slippage < 0 || slippage > 100)) {
6937
7164
  throw errInvalidArgs("Slippage must be a number between 0 and 100.");
6938
7165
  }
6939
- const quote = await withSpinner(
6940
- "Fetching quote\u2026",
6941
- "Quote received",
6942
- () => swapClient.requestQuoteV0(createQuoteRequest(opts.from, opts.to, rawAmount, slippage, paymaster))
6943
- );
7166
+ let quote;
7167
+ try {
7168
+ quote = await withSpinner(
7169
+ "Fetching quote\u2026",
7170
+ "Quote received",
7171
+ () => swapClient.requestQuoteV0(createQuoteRequest(opts.from, opts.to, rawAmount, slippage, paymaster, from))
7172
+ );
7173
+ } catch (err) {
7174
+ throw normalizeQuoteError(err, "swap");
7175
+ }
6944
7176
  const preparedQuote = await prepareQuoteForExecution(client, quote);
6945
7177
  const { id } = await withSpinner(
6946
7178
  "Sending swap transaction\u2026",
@@ -7142,12 +7374,13 @@ async function resolveTokenInfo2(network, program2, tokenAddress) {
7142
7374
  throw errInvalidArgs(`Failed to resolve token info for ${tokenAddress}.${detail}`);
7143
7375
  }
7144
7376
  }
7145
- function createBridgeQuoteRequest(fromToken, toToken, fromAmount, toChainId, slippagePercent, paymaster) {
7377
+ function createBridgeQuoteRequest(fromToken, toToken, fromAmount, toChainId, slippagePercent, paymaster, account) {
7146
7378
  const request2 = {
7147
7379
  fromToken,
7148
7380
  toToken,
7149
7381
  fromAmount,
7150
7382
  toChainId,
7383
+ ...account ? { account } : {},
7151
7384
  ...slippagePercent !== void 0 ? { slippage: slippagePercentToBasisPoints2(slippagePercent) } : {},
7152
7385
  ...paymaster ? { capabilities: { paymaster } } : {}
7153
7386
  };
@@ -7213,7 +7446,7 @@ function extractQuoteData2(quote) {
7213
7446
  }
7214
7447
  function registerBridge(program2) {
7215
7448
  const cmd = program2.command("bridge").description("Bridge tokens from the source -n/--network to a destination --to-network");
7216
- const quoteCmd = cmd.command("quote").description("Get a bridge quote without executing").requiredOption("--from <token_address>", `Source token address (use ${NATIVE_TOKEN_ADDRESS3} for the native token)`).requiredOption("--to <token_address>", `Destination token address (use ${NATIVE_TOKEN_ADDRESS3} for the native token)`).requiredOption("--amount <number>", "Amount to bridge in decimal token units (for example, 1.5)").requiredOption("--to-network <network>", "Destination network (e.g. base-mainnet)").option("--slippage <percent>", "Max slippage percentage (omit to use the API default)");
7449
+ const quoteCmd = cmd.command("quote").description("Get a bridge quote without executing").requiredOption("--from <token_address>", `Source token address (use ${NATIVE_TOKEN_ADDRESS3} for the native token)`).requiredOption("--to <token_address>", `Destination token address (use ${NATIVE_TOKEN_ADDRESS3} for the native token)`).requiredOption("--amount <number>", "Amount to bridge in decimal token units (for example, 1.5)").requiredOption("--to-network <network>", "Destination network (e.g. base-mainnet)").option("--from-address <address>", "Wallet/account address to quote from without changing signer selection").option("--slippage <percent>", "Max slippage percentage (omit to use the API default)");
7217
7450
  addSignerOption(quoteCmd);
7218
7451
  quoteCmd.addHelpText(
7219
7452
  "after",
@@ -7255,8 +7488,9 @@ async function performBridgeQuote(program2, opts) {
7255
7488
  validateAddress(opts.from);
7256
7489
  const toChainId = bridgeDestinationChainId(opts.toNetwork);
7257
7490
  validateAddress(opts.to);
7491
+ if (opts.fromAddress) validateAddress(opts.fromAddress);
7258
7492
  const signer = parseSignerOpt(opts.signer);
7259
- const { client, network, paymaster } = buildWalletClient(program2, { signer });
7493
+ const { client, network, address: fromAddress, paymaster } = opts.fromAddress ? buildWalletQuoteClient(program2, opts.fromAddress) : buildWalletClient(program2, { signer });
7260
7494
  validateBridgeNetworks(network, opts.toNetwork);
7261
7495
  const swapClient = client.extend(swapActions2);
7262
7496
  const fromInfo = await resolveTokenInfo2(network, program2, opts.from);
@@ -7265,17 +7499,23 @@ async function performBridgeQuote(program2, opts) {
7265
7499
  if (slippage !== void 0 && (isNaN(slippage) || slippage < 0 || slippage > 100)) {
7266
7500
  throw errInvalidArgs("Slippage must be a number between 0 and 100.");
7267
7501
  }
7268
- const quote = await withSpinner(
7269
- "Fetching bridge quote\u2026",
7270
- "Quote received",
7271
- () => swapClient.requestQuoteV0(createBridgeQuoteRequest(opts.from, opts.to, rawAmount, toChainId, slippage, paymaster))
7272
- );
7502
+ let quote;
7503
+ try {
7504
+ quote = await withSpinner(
7505
+ "Fetching bridge quote\u2026",
7506
+ "Quote received",
7507
+ () => swapClient.requestQuoteV0(createBridgeQuoteRequest(opts.from, opts.to, rawAmount, toChainId, slippage, paymaster, fromAddress))
7508
+ );
7509
+ } catch (err) {
7510
+ throw normalizeQuoteError(err, "bridge");
7511
+ }
7273
7512
  const toInfo = await resolveTokenInfo2(opts.toNetwork, program2, opts.to);
7274
7513
  const quoteData = extractQuoteData2(quote);
7275
7514
  if (isJSONMode()) {
7276
7515
  printJSON({
7277
7516
  fromToken: opts.from,
7278
7517
  toToken: opts.to,
7518
+ fromAddress,
7279
7519
  fromAmount: opts.amount,
7280
7520
  fromSymbol: fromInfo.symbol,
7281
7521
  toSymbol: toInfo.symbol,
@@ -7295,6 +7535,7 @@ async function performBridgeQuote(program2, opts) {
7295
7535
  pairs.push(["To", toInfo.symbol]);
7296
7536
  }
7297
7537
  pairs.push(
7538
+ ["Wallet", fromAddress],
7298
7539
  ["Slippage", slippage === void 0 ? "API default" : `${slippage}%`],
7299
7540
  ["From Network", network],
7300
7541
  ["To Network", opts.toNetwork]
@@ -7316,11 +7557,16 @@ async function performBridgeExecute(program2, opts) {
7316
7557
  if (slippage !== void 0 && (isNaN(slippage) || slippage < 0 || slippage > 100)) {
7317
7558
  throw errInvalidArgs("Slippage must be a number between 0 and 100.");
7318
7559
  }
7319
- const quote = await withSpinner(
7320
- "Fetching bridge quote\u2026",
7321
- "Quote received",
7322
- () => swapClient.requestQuoteV0(createBridgeQuoteRequest(opts.from, opts.to, rawAmount, toChainId, slippage, paymaster))
7323
- );
7560
+ let quote;
7561
+ try {
7562
+ quote = await withSpinner(
7563
+ "Fetching bridge quote\u2026",
7564
+ "Quote received",
7565
+ () => swapClient.requestQuoteV0(createBridgeQuoteRequest(opts.from, opts.to, rawAmount, toChainId, slippage, paymaster, from))
7566
+ );
7567
+ } catch (err) {
7568
+ throw normalizeQuoteError(err, "bridge");
7569
+ }
7324
7570
  const preparedQuote = await prepareQuoteForExecution2(client, quote);
7325
7571
  const { id } = await withSpinner(
7326
7572
  "Sending bridge transaction\u2026",
@@ -7865,13 +8111,62 @@ function registerInstall(program2) {
7865
8111
  }
7866
8112
 
7867
8113
  // src/commands/doctor.ts
8114
+ var DOCTOR_SETUP_CAPABILITY_ORDER = SETUP_CAPABILITY_ORDER.filter(
8115
+ (capability) => capability !== "x402"
8116
+ );
8117
+ function removeX402SetupMissing(missing) {
8118
+ return missing.map(
8119
+ (item) => item.replace(" OR SIWx wallet", "").replace(" or x402 wallet", "")
8120
+ );
8121
+ }
8122
+ function removeX402NextCommands(commands) {
8123
+ return commands.filter((command) => !/x402|siwx/i.test(command));
8124
+ }
8125
+ function sanitizeCapabilityStatus(status) {
8126
+ if (status.satisfiedBy !== "x402_wallet") {
8127
+ return {
8128
+ ...status,
8129
+ missing: removeX402SetupMissing(status.missing),
8130
+ nextCommands: removeX402NextCommands(status.nextCommands)
8131
+ };
8132
+ }
8133
+ return {
8134
+ complete: false,
8135
+ satisfiedBy: null,
8136
+ missing: ["api-key"],
8137
+ nextCommands: ["alchemy config set app", "alchemy config set api-key <key>"]
8138
+ };
8139
+ }
8140
+ function doctorSetupStatus(setup) {
8141
+ const { x402: _x402, ...capabilities } = setup.capabilities;
8142
+ const sanitizedCapabilities = Object.fromEntries(
8143
+ Object.entries(capabilities).map(([capability, status]) => [
8144
+ capability,
8145
+ sanitizeCapabilityStatus(status)
8146
+ ])
8147
+ );
8148
+ const x402OnlySetup = setup.satisfiedBy === "x402_wallet";
8149
+ return {
8150
+ ...setup,
8151
+ complete: x402OnlySetup ? false : setup.complete,
8152
+ satisfiedBy: x402OnlySetup ? null : setup.satisfiedBy,
8153
+ missing: x402OnlySetup ? ["Provide one auth path: alchemy auth OR api-key OR ALCHEMY_ACCESS_KEY+app"] : removeX402SetupMissing(setup.missing),
8154
+ nextCommands: x402OnlySetup ? [
8155
+ "alchemy auth",
8156
+ "alchemy config set app",
8157
+ "alchemy config set access-key <key> && alchemy config set app <app-id>"
8158
+ ] : removeX402NextCommands(setup.nextCommands),
8159
+ capabilities: sanitizedCapabilities
8160
+ };
8161
+ }
7868
8162
  function registerDoctor(program2) {
7869
8163
  program2.command("doctor").description("Run readiness checks and print suggested fixes").action(() => {
7870
- const setup = getSetupStatus(load());
8164
+ const setup = doctorSetupStatus(getSetupStatus(load()));
7871
8165
  const payload = {
7872
8166
  ok: setup.complete,
7873
8167
  checks: {
7874
- setup
8168
+ setup,
8169
+ capabilities: setup.capabilities
7875
8170
  }
7876
8171
  };
7877
8172
  if (isJSONMode()) {
@@ -7890,6 +8185,15 @@ function registerDoctor(program2) {
7890
8185
  console.log(` ${command}`);
7891
8186
  }
7892
8187
  }
8188
+ console.log("");
8189
+ console.log(` ${dim("Capabilities:")}`);
8190
+ printKeyValue(
8191
+ DOCTOR_SETUP_CAPABILITY_ORDER.map((capability) => {
8192
+ const status = setup.capabilities[capability];
8193
+ const value = status.complete ? green(`ready${status.satisfiedBy ? ` (${status.satisfiedBy})` : ""}`) : dim(`missing ${status.missing.join(", ")}`);
8194
+ return [SETUP_CAPABILITY_LABELS[capability], value];
8195
+ })
8196
+ );
7893
8197
  });
7894
8198
  }
7895
8199
 
@@ -7986,9 +8290,15 @@ function resetUpdateNoticeState() {
7986
8290
  cachedAvailableUpdate = void 0;
7987
8291
  updateShownDuringInteractiveStartup = false;
7988
8292
  }
8293
+ async function flushProcessOutput() {
8294
+ await Promise.all([
8295
+ new Promise((resolve) => process.stdout.write("", () => resolve())),
8296
+ new Promise((resolve) => process.stderr.write("", () => resolve()))
8297
+ ]);
8298
+ }
7989
8299
  program.name("alchemy").description(
7990
8300
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
7991
- ).version("0.7.1", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
8301
+ ).version("0.7.2-alpha.26", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
7992
8302
  "-n, --network <network>",
7993
8303
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
7994
8304
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -8175,17 +8485,17 @@ ${styledLine}`;
8175
8485
  "wallet"
8176
8486
  ];
8177
8487
  if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
8178
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-R4JZZCCF.js");
8488
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-WXT5ZCUK.js");
8179
8489
  const authToken = resolveAuthToken2(cfg);
8180
8490
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
8181
8491
  if (authToken && !hasApiKey) {
8182
- const { selectAppAfterAuth } = await import("./auth-BNT5Z5GZ.js");
8492
+ const { selectAppAfterAuth } = await import("./auth-F2IXC6CM.js");
8183
8493
  console.log("");
8184
8494
  console.log(` No app selected. Please select an app to continue.`);
8185
8495
  await selectAppAfterAuth(authToken);
8186
8496
  }
8187
8497
  }
8188
- }).hook("postAction", () => {
8498
+ }).hook("postAction", async () => {
8189
8499
  if (!isJSONMode() && !quiet) {
8190
8500
  console.log("");
8191
8501
  if (!updateShownDuringInteractiveStartup) {
@@ -8194,6 +8504,10 @@ ${styledLine}`;
8194
8504
  }
8195
8505
  }
8196
8506
  resetUpdateNoticeState();
8507
+ if (!isReplMode()) {
8508
+ await flushProcessOutput();
8509
+ process.exit(0);
8510
+ }
8197
8511
  }).action(async (_opts, cmd) => {
8198
8512
  const excessArgs = cmd.args;
8199
8513
  if (excessArgs.length > 0) {
@@ -8210,7 +8524,7 @@ ${styledLine}`;
8210
8524
  if (isInteractiveAllowed(program)) {
8211
8525
  let latestForInteractiveStartup = null;
8212
8526
  if (shouldRunOnboarding(program, cfg)) {
8213
- const { runOnboarding } = await import("./onboarding-S6HKWOEA.js");
8527
+ const { runOnboarding } = await import("./onboarding-6FSQLTHP.js");
8214
8528
  const latest = getAvailableUpdateOnce();
8215
8529
  const completed = await runOnboarding(program, latest);
8216
8530
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -8224,7 +8538,7 @@ ${styledLine}`;
8224
8538
  latestForInteractiveStartup
8225
8539
  );
8226
8540
  }
8227
- const { startREPL } = await import("./interactive-N33RCX33.js");
8541
+ const { startREPL } = await import("./interactive-3L4IXWXJ.js");
8228
8542
  program.exitOverride();
8229
8543
  program.configureOutput({
8230
8544
  writeErr: () => {