@alchemy/cli 0.2.1 → 0.2.2

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.
@@ -326,8 +326,16 @@ function errNetwork(detail) {
326
326
  "Check your internet connection and try again."
327
327
  );
328
328
  }
329
+ var RPC_ERROR_HINTS = {
330
+ [-32700]: "Parse error. The request JSON is malformed.",
331
+ [-32600]: "Invalid request. Check the JSON-RPC request format.",
332
+ [-32601]: "Method not supported. Check the method name and ensure your plan supports it.",
333
+ [-32602]: "Invalid parameters. Check argument types and format.",
334
+ [-32603]: "Internal JSON-RPC error."
335
+ };
329
336
  function errRPC(code, message) {
330
- return new CLIError(ErrorCode.RPC_ERROR, `RPC error ${code}: ${message}`);
337
+ const hint = RPC_ERROR_HINTS[code];
338
+ return new CLIError(ErrorCode.RPC_ERROR, `RPC error ${code}: ${message}`, hint);
331
339
  }
332
340
  function errInvalidArgs(detail) {
333
341
  return new CLIError(ErrorCode.INVALID_ARGS, detail);
@@ -349,6 +357,14 @@ function errInvalidAccessKey() {
349
357
  "Get an access key: https://www.alchemy.com/docs/reference/admin-api/overview"
350
358
  );
351
359
  }
360
+ function errAccessDenied(detail) {
361
+ const message = detail ? `Access denied: ${detail}` : "Access denied. Your access key may not have permission for this operation.";
362
+ return new CLIError(
363
+ ErrorCode.INVALID_ACCESS_KEY,
364
+ message,
365
+ "Check your account tier and feature access at https://dashboard.alchemy.com/"
366
+ );
367
+ }
352
368
  function errAppRequired() {
353
369
  return new CLIError(
354
370
  ErrorCode.APP_REQUIRED,
@@ -1093,7 +1109,7 @@ function semverLT(a, b) {
1093
1109
  return false;
1094
1110
  }
1095
1111
  function currentVersion() {
1096
- return true ? "0.2.1" : "0.0.0";
1112
+ return true ? "0.2.2" : "0.0.0";
1097
1113
  }
1098
1114
  function toUpdateStatus(latestVersion, checkedAt) {
1099
1115
  const current = currentVersion();
@@ -1178,6 +1194,7 @@ export {
1178
1194
  errNotFound,
1179
1195
  errRateLimited,
1180
1196
  errInvalidAccessKey,
1197
+ errAccessDenied,
1181
1198
  errAppRequired,
1182
1199
  errWalletKeyRequired,
1183
1200
  errAdminAPI,
@@ -7,6 +7,7 @@ import {
7
7
  configDir,
8
8
  debug,
9
9
  dim,
10
+ errAccessDenied,
10
11
  errAccessKeyRequired,
11
12
  errAdminAPI,
12
13
  errAppRequired,
@@ -40,7 +41,7 @@ import {
40
41
  toMap,
41
42
  withSpinner,
42
43
  yellow
43
- } from "./chunk-SDUCDSCZ.js";
44
+ } from "./chunk-2WI4JODY.js";
44
45
 
45
46
  // src/lib/client-utils.ts
46
47
  function isLocalhost(hostname) {
@@ -82,7 +83,24 @@ async function fetchWithTimeout(url, init) {
82
83
  if (err instanceof DOMException && err.name === "TimeoutError") {
83
84
  throw errNetwork(`Request timed out after ${timeout}ms`);
84
85
  }
85
- throw errNetwork(err.message);
86
+ const message = err.message ?? String(err);
87
+ const causeMessage = err.cause?.message ?? "";
88
+ const causeCode = err.cause?.code ?? "";
89
+ const fullErrorText = `${message} ${causeMessage} ${causeCode}`;
90
+ if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(fullErrorText)) {
91
+ try {
92
+ const hostname = new URL(url).hostname;
93
+ const networkSlug = hostname.replace(/\.g\.alchemy\.com$/, "");
94
+ if (networkSlug !== hostname) {
95
+ throw errInvalidArgs(
96
+ `Unknown network '${networkSlug}'. Run 'alchemy network list' to see available networks.`
97
+ );
98
+ }
99
+ } catch (innerErr) {
100
+ if (innerErr instanceof CLIError) throw innerErr;
101
+ }
102
+ }
103
+ throw errNetwork(message);
86
104
  }
87
105
  }
88
106
 
@@ -145,7 +163,18 @@ var AdminClient = class _AdminClient {
145
163
  },
146
164
  ...body !== void 0 && { body: JSON.stringify(body) }
147
165
  });
148
- if (resp.status === 401 || resp.status === 403) throw errInvalidAccessKey();
166
+ if (resp.status === 401) throw errInvalidAccessKey();
167
+ if (resp.status === 403) {
168
+ const detail = await resp.text().catch(() => "");
169
+ let reason;
170
+ try {
171
+ const parsed = JSON.parse(detail);
172
+ reason = parsed?.message || parsed?.error?.message || parsed?.error || void 0;
173
+ } catch {
174
+ reason = detail || void 0;
175
+ }
176
+ throw errAccessDenied(typeof reason === "string" ? reason : void 0);
177
+ }
149
178
  if (resp.status === 404) {
150
179
  const text = await resp.text().catch(() => "");
151
180
  throw errNotFound(text || path);
@@ -686,10 +715,14 @@ var Client = class _Client {
686
715
  try {
687
716
  parsed = new URL(`https://${hostname}`);
688
717
  } catch {
689
- throw errInvalidArgs(`Invalid network: ${network}`);
718
+ throw errInvalidArgs(
719
+ `Unknown network '${network}'. Run 'alchemy network list' to see available networks.`
720
+ );
690
721
  }
691
722
  if (!parsed.hostname.endsWith(".g.alchemy.com")) {
692
- throw errInvalidArgs(`Invalid network: ${network} \u2014 hostname must end with .g.alchemy.com`);
723
+ throw errInvalidArgs(
724
+ `Unknown network '${network}'. Run 'alchemy network list' to see available networks.`
725
+ );
693
726
  }
694
727
  }
695
728
  rpcBaseURLOverride() {
@@ -718,6 +751,16 @@ var Client = class _Client {
718
751
  if (networkNotEnabled) return networkNotEnabled;
719
752
  return errInvalidAPIKey(detail || void 0);
720
753
  }
754
+ tryParseRPCError(text) {
755
+ try {
756
+ const parsed = JSON.parse(text);
757
+ if (parsed?.error?.code !== void 0 && parsed?.error?.message !== void 0) {
758
+ return errRPC(parsed.error.code, parsed.error.message);
759
+ }
760
+ } catch {
761
+ }
762
+ return null;
763
+ }
721
764
  async doFetch(url, init) {
722
765
  return fetchWithTimeout(url, init);
723
766
  }
@@ -743,6 +786,8 @@ var Client = class _Client {
743
786
  }
744
787
  if (!resp.ok) {
745
788
  const text = await resp.text().catch(() => "");
789
+ const rpcError = this.tryParseRPCError(text);
790
+ if (rpcError) throw rpcError;
746
791
  throw errNetwork(`HTTP ${resp.status}: ${text}`);
747
792
  }
748
793
  const rpcResp = await resp.json();
@@ -766,6 +811,8 @@ var Client = class _Client {
766
811
  }
767
812
  if (!resp.ok) {
768
813
  const text = await resp.text().catch(() => "");
814
+ const rpcError = this.tryParseRPCError(text);
815
+ if (rpcError) throw rpcError;
769
816
  throw errNetwork(`HTTP ${resp.status}: ${text}`);
770
817
  }
771
818
  return resp.json();
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  isInteractiveAllowed
5
- } from "./chunk-SDUCDSCZ.js";
5
+ } from "./chunk-2WI4JODY.js";
6
6
 
7
7
  // src/lib/networks.ts
8
8
  var TESTNET_TOKEN_RE = /(testnet|sepolia|holesky|hoodi|devnet|minato|amoy|fuji|saigon|cardona|aeneid|curtis|chiado|cassiopeia|blaze|ropsten|signet|mocha|fam|bepolia)$/i;
package/dist/index.js CHANGED
@@ -14,13 +14,13 @@ import {
14
14
  splitCommaList,
15
15
  validateAddress,
16
16
  validateTxHash
17
- } from "./chunk-XP5KF4W2.js";
17
+ } from "./chunk-6KOJKH6N.js";
18
18
  import {
19
19
  getRPCNetworks,
20
20
  getSetupStatus,
21
21
  isSetupComplete,
22
22
  shouldRunOnboarding
23
- } from "./chunk-HBRTTBCY.js";
23
+ } from "./chunk-NA7MQB7X.js";
24
24
  import {
25
25
  EXIT_CODES,
26
26
  ErrorCode,
@@ -65,7 +65,7 @@ import {
65
65
  verbose,
66
66
  weiToEth,
67
67
  withSpinner
68
- } from "./chunk-SDUCDSCZ.js";
68
+ } from "./chunk-2WI4JODY.js";
69
69
 
70
70
  // src/index.ts
71
71
  import { Command, Help } from "commander";
@@ -277,6 +277,9 @@ Examples:
277
277
  "block must be a number, hex, or 'latest'"
278
278
  );
279
279
  }
280
+ if (num < 0) {
281
+ throw errInvalidArgs("Block number must be non-negative.");
282
+ }
280
283
  blockParam = `0x${num.toString(16)}`;
281
284
  }
282
285
  const client = clientFromFlags(program2);
@@ -1912,10 +1915,10 @@ function resetUpdateNoticeState() {
1912
1915
  }
1913
1916
  program.name("alchemy").description(
1914
1917
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
1915
- ).version("0.2.1", "-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(
1918
+ ).version("0.2.2", "-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(
1916
1919
  "-n, --network <network>",
1917
1920
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
1918
- ).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").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 (TTY only)").option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).exitOverride((err) => {
1921
+ ).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").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 (TTY only)").option("--timeout <ms>", "Request timeout in milliseconds", parseInt).option("--debug", "Enable debug diagnostics").option("--no-interactive", "Disable REPL and prompt-driven interactions").addHelpCommand(false).allowExcessArguments(true).exitOverride((err) => {
1919
1922
  if (err.code === "commander.help" || err.code === "commander.helpDisplayed" || err.code === "commander.version") {
1920
1923
  process.exit(0);
1921
1924
  }
@@ -2067,7 +2070,15 @@ ${styledLine}`;
2067
2070
  }
2068
2071
  }
2069
2072
  resetUpdateNoticeState();
2070
- }).action(async () => {
2073
+ }).action(async (_opts, cmd) => {
2074
+ const excessArgs = cmd.args;
2075
+ if (excessArgs.length > 0) {
2076
+ exitWithError(
2077
+ errInvalidArgs(
2078
+ `Unknown command '${excessArgs[0]}'. Run 'alchemy help' for available commands.`
2079
+ )
2080
+ );
2081
+ }
2071
2082
  const cfg = load();
2072
2083
  if (!isSetupComplete(cfg) && !isInteractiveAllowed(program)) {
2073
2084
  throw errSetupRequired(getSetupStatus(cfg));
@@ -2075,7 +2086,7 @@ ${styledLine}`;
2075
2086
  if (isInteractiveAllowed(program)) {
2076
2087
  let latestForInteractiveStartup = null;
2077
2088
  if (shouldRunOnboarding(program, cfg)) {
2078
- const { runOnboarding } = await import("./onboarding-YA32OJOT.js");
2089
+ const { runOnboarding } = await import("./onboarding-SJ2BRK5I.js");
2079
2090
  const latest = getAvailableUpdateOnce();
2080
2091
  const completed = await runOnboarding(program, latest);
2081
2092
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -2087,7 +2098,7 @@ ${styledLine}`;
2087
2098
  latestForInteractiveStartup = getAvailableUpdateOnce();
2088
2099
  updateShownDuringInteractiveStartup = Boolean(latestForInteractiveStartup);
2089
2100
  }
2090
- const { startREPL } = await import("./interactive-L7N4HI7K.js");
2101
+ const { startREPL } = await import("./interactive-EAUEVR63.js");
2091
2102
  program.exitOverride();
2092
2103
  program.configureOutput({
2093
2104
  writeErr: () => {
@@ -3,7 +3,7 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  getRPCNetworkIds,
5
5
  getSetupMethod
6
- } from "./chunk-HBRTTBCY.js";
6
+ } from "./chunk-NA7MQB7X.js";
7
7
  import {
8
8
  bgRgb,
9
9
  bold,
@@ -19,7 +19,7 @@ import {
19
19
  rgb,
20
20
  setBrandedHelpSuppressed,
21
21
  setReplMode
22
- } from "./chunk-SDUCDSCZ.js";
22
+ } from "./chunk-2WI4JODY.js";
23
23
 
24
24
  // src/commands/interactive.ts
25
25
  import * as readline from "readline";
@@ -5,7 +5,7 @@ import {
5
5
  generateAndPersistWallet,
6
6
  importAndPersistWallet,
7
7
  selectOrCreateApp
8
- } from "./chunk-XP5KF4W2.js";
8
+ } from "./chunk-6KOJKH6N.js";
9
9
  import {
10
10
  bold,
11
11
  brand,
@@ -19,7 +19,7 @@ import {
19
19
  promptSelect,
20
20
  promptText,
21
21
  save
22
- } from "./chunk-SDUCDSCZ.js";
22
+ } from "./chunk-2WI4JODY.js";
23
23
 
24
24
  // src/commands/onboarding.ts
25
25
  function printNextSteps(method) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {