@alchemy/cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,37 +1,65 @@
1
1
  #!/usr/bin/env node
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
- adminClientFromFlags,
5
- clientFromFlags,
6
- fetchWithTimeout,
4
+ registerAuth
5
+ } from "./chunk-LYUW7O6X.js";
6
+ import "./chunk-IGD4NIK7.js";
7
+ import {
7
8
  readStdinArg,
8
9
  readStdinLines,
9
10
  registerConfig,
10
11
  registerWallet,
11
- resolveAPIKey,
12
12
  resolveAddress,
13
- resolveAppId,
14
- resolveNetwork,
15
13
  splitCommaList,
16
14
  validateAddress,
17
15
  validateTxHash
18
- } from "./chunk-PIWNNNMZ.js";
16
+ } from "./chunk-44OGGLN4.js";
19
17
  import {
20
18
  getRPCNetworks,
21
19
  getSetupStatus,
22
20
  isSetupComplete,
23
21
  nativeTokenSymbol,
24
22
  shouldRunOnboarding
25
- } from "./chunk-Z3LXQFIY.js";
23
+ } from "./chunk-5X6YRTPU.js";
26
24
  import {
27
- EXIT_CODES,
28
- ErrorCode,
25
+ getAvailableUpdate,
26
+ getUpdateStatus,
27
+ printUpdateNotice
28
+ } from "./chunk-DUQFOLLZ.js";
29
+ import {
30
+ adminClientFromFlags,
29
31
  bold,
30
32
  brand,
31
33
  brandedHelp,
32
- debug,
34
+ clientFromFlags,
33
35
  dim,
34
36
  emptyState,
37
+ etherscanTxURL,
38
+ failBadge,
39
+ green,
40
+ isInteractiveAllowed,
41
+ load,
42
+ maskIf,
43
+ printKeyValueBox,
44
+ printSyntaxJSON,
45
+ printTable,
46
+ promptConfirm,
47
+ promptSelect,
48
+ red,
49
+ resolveAPIKey,
50
+ resolveAppId,
51
+ resolveNetwork,
52
+ resolveX402Client,
53
+ save,
54
+ successBadge,
55
+ timeAgo,
56
+ weiToEth,
57
+ withSpinner
58
+ } from "./chunk-T2XSNZE3.js";
59
+ import {
60
+ EXIT_CODES,
61
+ ErrorCode,
62
+ debug,
35
63
  errAppRequired,
36
64
  errAuthRequired,
37
65
  errInvalidAPIKey,
@@ -41,37 +69,20 @@ import {
41
69
  errRateLimited,
42
70
  errSetupRequired,
43
71
  esc,
44
- etherscanTxURL,
45
72
  exitWithError,
46
- failBadge,
73
+ fetchWithTimeout,
47
74
  formatCommanderError,
48
- getAvailableUpdate,
49
- getUpdateStatus,
50
- green,
75
+ getBaseDomain,
51
76
  identity,
52
- isInteractiveAllowed,
53
77
  isJSONMode,
54
- load,
55
- maskIf,
56
78
  noColor,
57
79
  printHuman,
58
80
  printJSON,
59
- printKeyValueBox,
60
- printSyntaxJSON,
61
- printTable,
62
- printUpdateNotice,
63
- promptConfirm,
64
- promptSelect,
65
81
  quiet,
66
- red,
67
82
  setFlags,
68
83
  setNoColor,
69
- successBadge,
70
- timeAgo,
71
- verbose,
72
- weiToEth,
73
- withSpinner
74
- } from "./chunk-TH75DFAY.js";
84
+ verbose
85
+ } from "./chunk-56ZVYB4G.js";
75
86
 
76
87
  // src/index.ts
77
88
  import { Command, Help } from "commander";
@@ -829,11 +840,11 @@ Examples:
829
840
  const result = await withSpinner(
830
841
  "Fetching token allowance\u2026",
831
842
  "Token allowance fetched",
832
- () => client.call("alchemy_getTokenAllowance", [
833
- opts.owner,
834
- opts.spender,
835
- opts.contract
836
- ])
843
+ () => client.call("alchemy_getTokenAllowance", [{
844
+ owner: opts.owner,
845
+ spender: opts.spender,
846
+ contract: opts.contract
847
+ }])
837
848
  );
838
849
  if (isJSONMode()) printJSON(result);
839
850
  else printSyntaxJSON(result);
@@ -1310,6 +1321,64 @@ function registerApps(program2) {
1310
1321
  exitWithError(err);
1311
1322
  }
1312
1323
  });
1324
+ cmd.command("select [id]").description("Select an app to use as the default").action(async (id) => {
1325
+ try {
1326
+ const admin = adminClientFromFlags(program2);
1327
+ let selected;
1328
+ if (id) {
1329
+ selected = await withSpinner(
1330
+ "Fetching app\u2026",
1331
+ "App fetched",
1332
+ () => admin.getApp(id)
1333
+ );
1334
+ } else {
1335
+ const result = await withSpinner(
1336
+ "Fetching apps\u2026",
1337
+ "Apps fetched",
1338
+ () => admin.listAllApps()
1339
+ );
1340
+ if (result.apps.length === 0) {
1341
+ emptyState("No apps found.");
1342
+ return;
1343
+ }
1344
+ if (isJSONMode()) {
1345
+ printJSON({ apps: result.apps.map(maskAppSecrets) });
1346
+ return;
1347
+ }
1348
+ const appId = await promptSelect({
1349
+ message: "Select an app",
1350
+ options: result.apps.map((app) => ({
1351
+ value: app.id,
1352
+ label: app.name,
1353
+ hint: `${app.chainNetworks.length} networks`
1354
+ })),
1355
+ cancelMessage: "Cancelled app selection."
1356
+ });
1357
+ if (!appId) return;
1358
+ const found = result.apps.find((a) => a.id === appId);
1359
+ if (!found) return;
1360
+ selected = found;
1361
+ }
1362
+ const cfg = load();
1363
+ save({
1364
+ ...cfg,
1365
+ app: {
1366
+ id: selected.id,
1367
+ name: selected.name,
1368
+ apiKey: selected.apiKey,
1369
+ webhookApiKey: selected.webhookApiKey
1370
+ }
1371
+ });
1372
+ if (isJSONMode()) {
1373
+ printJSON(maskAppSecrets(selected));
1374
+ return;
1375
+ }
1376
+ console.log(` ${green("\u2713")} Selected app: ${selected.name}`);
1377
+ console.log(` ${dim("Saved to config")}`);
1378
+ } catch (err) {
1379
+ exitWithError(err);
1380
+ }
1381
+ });
1313
1382
  cmd.command("chains").description("List Admin API chain identifiers for app configuration (e.g. ETH_MAINNET)").action(async () => {
1314
1383
  try {
1315
1384
  const admin = adminClientFromFlags(program2);
@@ -1599,13 +1668,13 @@ async function requestJSON(url, options) {
1599
1668
  }
1600
1669
  async function callApiData(apiKey, path, options = {}) {
1601
1670
  if (!apiKey) throw errAuthRequired();
1602
- const base = new URL(`https://api.g.alchemy.com/data/v1/${apiKey}/`);
1671
+ const base = new URL(`https://api.g.${getBaseDomain()}/data/v1/${apiKey}/`);
1603
1672
  const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
1604
1673
  return requestJSON(url, { ...options, path });
1605
1674
  }
1606
1675
  async function callApiPrices(apiKey, path, options = {}) {
1607
1676
  if (!apiKey) throw errAuthRequired();
1608
- const base = new URL(`https://api.g.alchemy.com/prices/v1/${apiKey}/`);
1677
+ const base = new URL(`https://api.g.${getBaseDomain()}/prices/v1/${apiKey}/`);
1609
1678
  const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
1610
1679
  return requestJSON(url, { ...options, path });
1611
1680
  }
@@ -1615,7 +1684,7 @@ async function callNotify(token, path, options = {}) {
1615
1684
  "Webhook API key required. Set ALCHEMY_WEBHOOK_API_KEY (or ALCHEMY_NOTIFY_AUTH_TOKEN) or pass --webhook-api-key."
1616
1685
  );
1617
1686
  }
1618
- const base = new URL("https://dashboard.alchemy.com/api/");
1687
+ const base = new URL(`https://dashboard.${getBaseDomain()}/api/`);
1619
1688
  const url = withQuery(new URL(path.replace(/^\//, ""), base), options.query);
1620
1689
  return requestJSON(url, {
1621
1690
  ...options,
@@ -1632,14 +1701,14 @@ function registerPrices(program2) {
1632
1701
  const cmd = program2.command("prices").description("Prices API wrappers");
1633
1702
  cmd.command("symbol <symbols>").description("Get spot prices by symbol (comma-separated)").action(async (symbols) => {
1634
1703
  try {
1635
- const apiKey = resolveAPIKey(program2);
1636
1704
  const values = splitCommaList(symbols);
1637
1705
  const query = new URLSearchParams();
1638
1706
  for (const symbol of values) query.append("symbols", symbol);
1707
+ const x402 = resolveX402Client(program2);
1639
1708
  const result = await withSpinner(
1640
1709
  "Fetching prices\u2026",
1641
1710
  "Prices fetched",
1642
- () => callApiPrices(apiKey, `/tokens/by-symbol?${query.toString()}`)
1711
+ () => x402 ? x402.callRest(`prices/v1/tokens/by-symbol?${query.toString()}`) : callApiPrices(resolveAPIKey(program2), `/tokens/by-symbol?${query.toString()}`)
1643
1712
  );
1644
1713
  if (isJSONMode()) printJSON(result);
1645
1714
  else printSyntaxJSON(result);
@@ -1649,12 +1718,12 @@ function registerPrices(program2) {
1649
1718
  });
1650
1719
  cmd.command("address").description("Get spot prices by token addresses").requiredOption("--addresses <json>", "JSON array of {network,address}").action(async (opts) => {
1651
1720
  try {
1652
- const apiKey = resolveAPIKey(program2);
1653
1721
  const body = { addresses: JSON.parse(opts.addresses) };
1722
+ const x402 = resolveX402Client(program2);
1654
1723
  const result = await withSpinner(
1655
1724
  "Fetching prices\u2026",
1656
1725
  "Prices fetched",
1657
- () => callApiPrices(apiKey, "/tokens/by-address", { method: "POST", body })
1726
+ () => x402 ? x402.callRest("prices/v1/tokens/by-address", { method: "POST", body }) : callApiPrices(resolveAPIKey(program2), "/tokens/by-address", { method: "POST", body })
1658
1727
  );
1659
1728
  if (isJSONMode()) printJSON(result);
1660
1729
  else printSyntaxJSON(result);
@@ -1670,12 +1739,12 @@ Examples:
1670
1739
  alchemy prices historical --body '{"address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","network":"eth-mainnet","startTime":"2024-06-01","endTime":"2024-06-07","interval":"1d"}'`
1671
1740
  ).action(async (opts) => {
1672
1741
  try {
1673
- const apiKey = resolveAPIKey(program2);
1674
1742
  const payload = JSON.parse(opts.body);
1743
+ const x402 = resolveX402Client(program2);
1675
1744
  const result = await withSpinner(
1676
1745
  "Fetching historical prices\u2026",
1677
1746
  "Historical prices fetched",
1678
- () => callApiPrices(apiKey, "/tokens/historical", { method: "POST", body: payload })
1747
+ () => x402 ? x402.callRest("prices/v1/tokens/historical", { method: "POST", body: payload }) : callApiPrices(resolveAPIKey(program2), "/tokens/historical", { method: "POST", body: payload })
1679
1748
  );
1680
1749
  if (isJSONMode()) printJSON(result);
1681
1750
  else printSyntaxJSON(result);
@@ -1686,20 +1755,20 @@ Examples:
1686
1755
  }
1687
1756
 
1688
1757
  // src/commands/portfolio.ts
1689
- async function runDataCall(apiKey, title, path, body) {
1758
+ async function runDataCall(program2, title, path, body) {
1759
+ const x402 = resolveX402Client(program2);
1690
1760
  return withSpinner(
1691
1761
  `Fetching ${title}\u2026`,
1692
1762
  `${title} fetched`,
1693
- () => callApiData(apiKey, path, { method: "POST", body })
1763
+ () => x402 ? x402.callRest(`data/v1${path}`, { method: "POST", body }) : callApiData(resolveAPIKey(program2), path, { method: "POST", body })
1694
1764
  );
1695
1765
  }
1696
1766
  function registerPortfolio(program2) {
1697
1767
  const cmd = program2.command("portfolio").description("Portfolio API wrappers");
1698
1768
  cmd.command("tokens").description("Get token portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/tokens/by-address").action(async (opts) => {
1699
1769
  try {
1700
- const apiKey = resolveAPIKey(program2);
1701
1770
  const result = await runDataCall(
1702
- apiKey,
1771
+ program2,
1703
1772
  "token portfolio",
1704
1773
  "/assets/tokens/by-address",
1705
1774
  JSON.parse(opts.body)
@@ -1712,9 +1781,8 @@ function registerPortfolio(program2) {
1712
1781
  });
1713
1782
  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) => {
1714
1783
  try {
1715
- const apiKey = resolveAPIKey(program2);
1716
1784
  const result = await runDataCall(
1717
- apiKey,
1785
+ program2,
1718
1786
  "token balances",
1719
1787
  "/assets/tokens/balances/by-address",
1720
1788
  JSON.parse(opts.body)
@@ -1727,9 +1795,8 @@ function registerPortfolio(program2) {
1727
1795
  });
1728
1796
  cmd.command("nfts").description("Get NFT portfolio by address/network pairs").requiredOption("--body <json>", "JSON body for /assets/nfts/by-address").action(async (opts) => {
1729
1797
  try {
1730
- const apiKey = resolveAPIKey(program2);
1731
1798
  const result = await runDataCall(
1732
- apiKey,
1799
+ program2,
1733
1800
  "NFT portfolio",
1734
1801
  "/assets/nfts/by-address",
1735
1802
  JSON.parse(opts.body)
@@ -1742,9 +1809,8 @@ function registerPortfolio(program2) {
1742
1809
  });
1743
1810
  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) => {
1744
1811
  try {
1745
- const apiKey = resolveAPIKey(program2);
1746
1812
  const result = await runDataCall(
1747
- apiKey,
1813
+ program2,
1748
1814
  "NFT contracts",
1749
1815
  "/assets/nfts/contracts/by-address",
1750
1816
  JSON.parse(opts.body)
@@ -2623,7 +2689,7 @@ var ROOT_COMMAND_PILLARS = [
2623
2689
  },
2624
2690
  {
2625
2691
  label: "Admin",
2626
- commands: ["apps", "config", "setup", "completions", "agent-prompt", "update-check", "version", "help"]
2692
+ commands: ["apps", "auth", "config", "setup", "completions", "agent-prompt", "update-check", "version", "help"]
2627
2693
  }
2628
2694
  ];
2629
2695
  function formatCommandSignature(sub) {
@@ -2672,7 +2738,7 @@ function resetUpdateNoticeState() {
2672
2738
  }
2673
2739
  program.name("alchemy").description(
2674
2740
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
2675
- ).version("0.4.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option("--access-key <key>", "Alchemy access key (env: ALCHEMY_ACCESS_KEY)").option(
2741
+ ).version("0.5.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option("--access-key <key>", "Alchemy access key (env: ALCHEMY_ACCESS_KEY)").option(
2676
2742
  "-n, --network <network>",
2677
2743
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
2678
2744
  ).option("--x402", "Use x402 wallet-based gateway auth").option("--wallet-key-file <path>", "Path to wallet private key file for x402").option("--json", "Force JSON output (auto-enabled when piped)").option("-q, --quiet", "Suppress non-essential output").option("--verbose", "Enable verbose output").option("--no-color", "Disable color output").option("--reveal", "Show secrets in plain text").option("--timeout <ms>", "Request timeout in milliseconds (default: none)", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).allowExcessArguments(true).exitOverride((err) => {
@@ -2852,7 +2918,7 @@ ${styledLine}`;
2852
2918
  if (isInteractiveAllowed(program)) {
2853
2919
  let latestForInteractiveStartup = null;
2854
2920
  if (shouldRunOnboarding(program, cfg)) {
2855
- const { runOnboarding } = await import("./onboarding-WQ2TWDM3.js");
2921
+ const { runOnboarding } = await import("./onboarding-F5PZMFZU.js");
2856
2922
  const latest = getAvailableUpdateOnce();
2857
2923
  const completed = await runOnboarding(program, latest);
2858
2924
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -2864,7 +2930,7 @@ ${styledLine}`;
2864
2930
  latestForInteractiveStartup = getAvailableUpdateOnce();
2865
2931
  updateShownDuringInteractiveStartup = Boolean(latestForInteractiveStartup);
2866
2932
  }
2867
- const { startREPL } = await import("./interactive-NASSNJHQ.js");
2933
+ const { startREPL } = await import("./interactive-K7XOS6U6.js");
2868
2934
  program.exitOverride();
2869
2935
  program.configureOutput({
2870
2936
  writeErr: () => {
@@ -2896,6 +2962,7 @@ registerGasManager(program);
2896
2962
  registerWebhooks(program);
2897
2963
  registerNetwork(program);
2898
2964
  registerApps(program);
2965
+ registerAuth(program);
2899
2966
  registerSetup(program);
2900
2967
  registerConfig(program);
2901
2968
  registerSolana(program);
@@ -3,23 +3,27 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  getRPCNetworkIds,
5
5
  getSetupMethod
6
- } from "./chunk-Z3LXQFIY.js";
6
+ } from "./chunk-5X6YRTPU.js";
7
+ import {
8
+ getUpdateNoticeLines
9
+ } from "./chunk-DUQFOLLZ.js";
7
10
  import {
8
- bgRgb,
9
11
  bold,
10
12
  brand,
11
13
  brandedHelp,
12
14
  configDir,
13
15
  dim,
14
- getUpdateNoticeLines,
15
16
  green,
16
- isJSONMode,
17
17
  load,
18
+ setBrandedHelpSuppressed
19
+ } from "./chunk-T2XSNZE3.js";
20
+ import {
21
+ bgRgb,
22
+ isJSONMode,
18
23
  noColor,
19
24
  rgb,
20
- setBrandedHelpSuppressed,
21
25
  setReplMode
22
- } from "./chunk-TH75DFAY.js";
26
+ } from "./chunk-56ZVYB4G.js";
23
27
 
24
28
  // src/commands/interactive.ts
25
29
  import * as readline from "readline";
@@ -118,7 +122,8 @@ function formatSetupMethodLabel() {
118
122
  const method = getSetupMethod(load());
119
123
  if (method === "api_key") return "API key";
120
124
  if (method === "access_key_app") return "Access key + app";
121
- if (method === "x402_wallet") return "x402 wallet";
125
+ if (method === "x402_wallet") return "SIWx wallet";
126
+ if (method === "auth_token") return "Auth token";
122
127
  return "Not configured";
123
128
  }
124
129
  function replHistoryPath() {
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
- AdminClient,
5
4
  generateAndPersistWallet,
6
5
  importAndPersistWallet,
7
6
  selectOrCreateApp
8
- } from "./chunk-PIWNNNMZ.js";
7
+ } from "./chunk-44OGGLN4.js";
8
+ import {
9
+ getUpdateNoticeLines
10
+ } from "./chunk-DUQFOLLZ.js";
9
11
  import {
12
+ AdminClient,
10
13
  bold,
11
14
  brand,
12
15
  brandedHelp,
13
16
  dim,
14
- getUpdateNoticeLines,
15
17
  green,
16
18
  load,
17
19
  maskIf,
@@ -19,17 +21,19 @@ import {
19
21
  promptSelect,
20
22
  promptText,
21
23
  save
22
- } from "./chunk-TH75DFAY.js";
24
+ } from "./chunk-T2XSNZE3.js";
25
+ import "./chunk-56ZVYB4G.js";
23
26
 
24
27
  // src/commands/onboarding.ts
25
28
  function printNextSteps(method) {
26
29
  const commandsByMethod = {
30
+ "browser-login": ["alchemy auth"],
27
31
  "api-key": ["alchemy config set api-key <key>"],
28
32
  "access-key": [
29
33
  "alchemy config set access-key <key>",
30
34
  "alchemy config set app <app-id>"
31
35
  ],
32
- x402: [
36
+ siwx: [
33
37
  "alchemy wallet generate",
34
38
  "alchemy config set wallet-key-file <path>",
35
39
  "alchemy config set x402 true"
@@ -83,15 +87,15 @@ async function runAccessKeyOnboarding() {
83
87
  console.log(` ${green("\u2713")} Saved access key`);
84
88
  await selectOrCreateApp(new AdminClient(key.trim()));
85
89
  }
86
- async function runX402Onboarding() {
90
+ async function runSiwxOnboarding() {
87
91
  const action = await promptSelect({
88
- message: "x402 wallet setup",
92
+ message: "SIWx wallet setup",
89
93
  options: [
90
94
  { label: "Generate a new wallet", value: "generate" },
91
95
  { label: "Import wallet from key file", value: "import" }
92
96
  ],
93
97
  initialValue: "generate",
94
- cancelMessage: "Skipped x402 setup."
98
+ cancelMessage: "Skipped SIWx setup."
95
99
  });
96
100
  if (!action) return;
97
101
  const wallet = action === "generate" ? generateAndPersistWallet() : await (async () => {
@@ -105,7 +109,18 @@ async function runX402Onboarding() {
105
109
  if (!wallet) return;
106
110
  const cfg = load();
107
111
  save({ ...cfg, x402: true });
108
- console.log(` ${green("\u2713")} x402 enabled with wallet ${wallet.address}`);
112
+ console.log(` ${green("\u2713")} SIWx enabled with wallet ${wallet.address}`);
113
+ try {
114
+ const { signSiwe } = await import("@alchemy/x402");
115
+ const { readFileSync } = await import("fs");
116
+ const keyPath = wallet.keyFile;
117
+ const privateKey = readFileSync(keyPath, "utf-8").trim();
118
+ const siweToken = await signSiwe({ privateKey, expiresAfter: "1h" });
119
+ const expiresAt = new Date(Date.now() + 60 * 60 * 1e3).toISOString();
120
+ save({ ...load(), siwe_token: siweToken, siwe_token_expires_at: expiresAt });
121
+ console.log(` ${green("\u2713")} Signed SIWE token (cached for 1h)`);
122
+ } catch {
123
+ }
109
124
  }
110
125
  async function runOnboarding(_program, latestUpdate = null) {
111
126
  process.stdout.write(brandedHelp({ force: true }));
@@ -125,6 +140,11 @@ async function runOnboarding(_program, latestUpdate = null) {
125
140
  const method = await promptSelect({
126
141
  message: "Choose an auth setup path",
127
142
  options: [
143
+ {
144
+ label: "Browser login",
145
+ hint: "Log in via browser (recommended)",
146
+ value: "browser-login"
147
+ },
128
148
  {
129
149
  label: "API key",
130
150
  hint: "Query Alchemy RPC nodes",
@@ -136,16 +156,16 @@ async function runOnboarding(_program, latestUpdate = null) {
136
156
  value: "access-key"
137
157
  },
138
158
  {
139
- label: "x402",
140
- hint: "Agentic API access and payment",
141
- value: "x402"
159
+ label: "SIWx",
160
+ hint: "Sign-In with Ethereum/Solana wallet",
161
+ value: "siwx"
142
162
  },
143
163
  {
144
164
  label: "exit",
145
165
  value: "exit"
146
166
  }
147
167
  ],
148
- initialValue: "api-key",
168
+ initialValue: "browser-login",
149
169
  cancelMessage: "Skipped onboarding."
150
170
  });
151
171
  if (!method) return false;
@@ -153,6 +173,28 @@ async function runOnboarding(_program, latestUpdate = null) {
153
173
  console.log(` ${dim("Exited onboarding.")}`);
154
174
  return false;
155
175
  }
176
+ if (method === "browser-login") {
177
+ const { performBrowserLogin, AUTH_PORT, getLoginUrl } = await import("./auth-E26YCAJV.js");
178
+ console.log(` Opening browser to log in...`);
179
+ console.log(` ${dim(getLoginUrl(AUTH_PORT))}`);
180
+ console.log(` ${dim("Waiting for authentication...")}`);
181
+ try {
182
+ const result = await performBrowserLogin();
183
+ const cfg2 = load();
184
+ save({
185
+ ...cfg2,
186
+ auth_token: result.token,
187
+ auth_token_expires_at: result.expiresAt
188
+ });
189
+ console.log(` ${green("\u2713")} Logged in successfully`);
190
+ const { selectAppAfterAuth } = await import("./auth-7E33EMAI.js");
191
+ await selectAppAfterAuth(result.token);
192
+ return true;
193
+ } catch (err) {
194
+ console.log(` ${dim(`Login failed: ${err instanceof Error ? err.message : String(err)}`)}`);
195
+ return false;
196
+ }
197
+ }
156
198
  if (method === "api-key") {
157
199
  await runAPIKeyOnboarding();
158
200
  const complete2 = Boolean(load().api_key?.trim());
@@ -172,11 +214,11 @@ async function runOnboarding(_program, latestUpdate = null) {
172
214
  }
173
215
  return complete2;
174
216
  }
175
- await runX402Onboarding();
217
+ await runSiwxOnboarding();
176
218
  const cfg = load();
177
219
  const complete = cfg.x402 === true && Boolean(cfg.wallet_key_file?.trim());
178
220
  if (!complete) {
179
- printNextSteps("x402");
221
+ printNextSteps("siwx");
180
222
  }
181
223
  return complete;
182
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {