@alchemy/cli 0.14.0 → 0.15.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.
@@ -53,7 +53,7 @@ function semverLT(a, b) {
53
53
  return false;
54
54
  }
55
55
  function currentVersion() {
56
- return true ? "0.14.0" : "0.0.0";
56
+ return true ? "0.15.0" : "0.0.0";
57
57
  }
58
58
  function toUpdateStatus(latestVersion, checkedAt) {
59
59
  const current = currentVersion();
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ import {
68
68
  getAvailableUpdate,
69
69
  getUpdateStatus,
70
70
  printUpdateNotice
71
- } from "./chunk-22KBYYLI.js";
71
+ } from "./chunk-UL5ZSWTE.js";
72
72
  import {
73
73
  bold,
74
74
  brand,
@@ -3371,6 +3371,60 @@ function registerWebhooks(program2) {
3371
3371
  });
3372
3372
  }
3373
3373
 
3374
+ // src/lib/solana-rpc.ts
3375
+ var SOLANA_DAS_METHODS = /* @__PURE__ */ new Set([
3376
+ "getAsset",
3377
+ "getAssetsByOwner",
3378
+ "searchAssets",
3379
+ "getTokenAccounts",
3380
+ "getAssets",
3381
+ "getAssetProof",
3382
+ "getAssetsByAuthority",
3383
+ "getAssetsByGroup",
3384
+ "getAssetsByCreator",
3385
+ "getAssetSignatures",
3386
+ "getNftEditions"
3387
+ ]);
3388
+ function errorText(err) {
3389
+ if (err instanceof CLIError) {
3390
+ return [err.message, err.details].filter(Boolean).join(" ");
3391
+ }
3392
+ if (err instanceof Error) {
3393
+ return err.message;
3394
+ }
3395
+ return String(err);
3396
+ }
3397
+ function solanaRpcFamilyForMethod(method) {
3398
+ return SOLANA_DAS_METHODS.has(method) ? "das" : "rpc";
3399
+ }
3400
+ function normalizeSolanaDasPaywallError(err) {
3401
+ if (err instanceof CLIError && err.code === ErrorCode.PAYMENT_REQUIRED) {
3402
+ return err;
3403
+ }
3404
+ const detail = errorText(err);
3405
+ if (!/\b(free tier|payg|paid tier|paid plan|upgrade|plan does not|current plan|not available on your plan)\b/i.test(detail)) {
3406
+ return null;
3407
+ }
3408
+ return new CLIError(
3409
+ ErrorCode.PAYMENT_REQUIRED,
3410
+ "Solana DAS request requires a paid Alchemy plan.",
3411
+ "Upgrade your app to PAYG or use a plan with Solana DAS access at https://dashboard.alchemy.com/.",
3412
+ detail || void 0
3413
+ );
3414
+ }
3415
+ async function callSolana(client, family, method, params) {
3416
+ const startedAt = Date.now();
3417
+ debug(`solana ${family} ${method} params`, params);
3418
+ try {
3419
+ const result = await client.call(method, params);
3420
+ debug(`solana ${family} ${method} ok ${Date.now() - startedAt}ms`);
3421
+ return result;
3422
+ } catch (err) {
3423
+ debug(`solana ${family} ${method} failed ${Date.now() - startedAt}ms`, errorText(err));
3424
+ throw err;
3425
+ }
3426
+ }
3427
+
3374
3428
  // src/commands/network.ts
3375
3429
  function registerNetwork(program2) {
3376
3430
  const cmd = program2.command("network").description("Manage networks");
@@ -6020,7 +6074,7 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
6020
6074
  result = await withSpinner(
6021
6075
  `Calling ${method.name}...`,
6022
6076
  `Called ${method.name}`,
6023
- () => client.call(method.rpcMethod, params)
6077
+ () => callNetworkRpcMethod(client, method, params)
6024
6078
  );
6025
6079
  }
6026
6080
  printSyntaxJSON(result);
@@ -6029,6 +6083,20 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
6029
6083
  }
6030
6084
  });
6031
6085
  }
6086
+ async function callNetworkRpcMethod(client, method, params) {
6087
+ if (method.family !== "solana") {
6088
+ return await client.call(method.rpcMethod, params);
6089
+ }
6090
+ const solanaFamily = solanaRpcFamilyForMethod(method.rpcMethod);
6091
+ try {
6092
+ return await callSolana(client, solanaFamily, method.rpcMethod, params);
6093
+ } catch (err) {
6094
+ if (solanaFamily === "das") {
6095
+ throw normalizeSolanaDasPaywallError(err) ?? err;
6096
+ }
6097
+ throw err;
6098
+ }
6099
+ }
6032
6100
  function parseRpcParams(input) {
6033
6101
  const parsed = parseRequiredJSON(input, "--params");
6034
6102
  if (Array.isArray(parsed)) {
@@ -6141,7 +6209,7 @@ function registerSolana(program2) {
6141
6209
  const result = await withSpinner(
6142
6210
  `Calling ${method}\u2026`,
6143
6211
  `Called ${method}`,
6144
- () => client.call(method, parseCLIParams(params))
6212
+ () => callSolana(client, "rpc", method, parseCLIParams(params))
6145
6213
  );
6146
6214
  printSyntaxJSON(result);
6147
6215
  } catch (err) {
@@ -6158,7 +6226,9 @@ function registerSolana(program2) {
6158
6226
  const result = await withSpinner(
6159
6227
  `Calling ${method}\u2026`,
6160
6228
  `Called ${method}`,
6161
- () => client.call(method, rpcParams)
6229
+ () => callSolana(client, "das", method, rpcParams).catch((err) => {
6230
+ throw normalizeSolanaDasPaywallError(err) ?? err;
6231
+ })
6162
6232
  );
6163
6233
  printSyntaxJSON(result);
6164
6234
  } catch (err) {
@@ -6807,7 +6877,7 @@ function registrySymbolSuggestions(network) {
6807
6877
  }
6808
6878
 
6809
6879
  // src/lib/preflight-errors.ts
6810
- function errorText(err) {
6880
+ function errorText2(err) {
6811
6881
  if (err instanceof CLIError) {
6812
6882
  return [err.message, err.details].filter(Boolean).join(" ");
6813
6883
  }
@@ -6842,7 +6912,7 @@ function normalizePreflightRevertError(err) {
6842
6912
  if (err instanceof CLIError && err.code === ErrorCode.PREFLIGHT_REVERT) {
6843
6913
  return err;
6844
6914
  }
6845
- const detail = errorText(err);
6915
+ const detail = errorText2(err);
6846
6916
  const reason = extractRevertReason(detail);
6847
6917
  if (!reason) return null;
6848
6918
  return errPreflightRevert(reason, detail || void 0);
@@ -8180,6 +8250,13 @@ Examples:
8180
8250
  }
8181
8251
 
8182
8252
  // src/commands/portfolio.ts
8253
+ var BALANCE_FIELD_NAMES = /* @__PURE__ */ new Set([
8254
+ "balance",
8255
+ "tokenbalance",
8256
+ "rawbalance",
8257
+ "rawtokenbalance",
8258
+ "amount"
8259
+ ]);
8183
8260
  async function runDataCall(program2, title, path, body) {
8184
8261
  const x402 = resolveX402Client(program2);
8185
8262
  return withSpinner(
@@ -8191,22 +8268,82 @@ async function runDataCall(program2, title, path, body) {
8191
8268
  function limitPayload(value, limit) {
8192
8269
  if (limit === void 0) return value;
8193
8270
  if (Array.isArray(value)) {
8194
- return value.slice(0, limit);
8271
+ return value.slice(0, limit).map((item) => limitPayload(item, limit));
8195
8272
  }
8196
- if (value && typeof value === "object") {
8273
+ if (isRecord(value)) {
8197
8274
  return Object.fromEntries(
8198
8275
  Object.entries(value).map(([key, nested]) => [
8199
8276
  key,
8200
- Array.isArray(nested) ? nested.slice(0, limit) : nested
8277
+ limitPayload(nested, limit)
8201
8278
  ])
8202
8279
  );
8203
8280
  }
8204
8281
  return value;
8205
8282
  }
8283
+ function isRecord(value) {
8284
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8285
+ }
8286
+ function isHexQuantity(value) {
8287
+ return /^0x[0-9a-f]+$/i.test(value);
8288
+ }
8289
+ function parseDecimals2(value) {
8290
+ const decimals = typeof value === "number" ? value : typeof value === "string" && /^\d+$/.test(value) ? Number(value) : void 0;
8291
+ if (decimals === void 0 || !Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
8292
+ return void 0;
8293
+ }
8294
+ return decimals;
8295
+ }
8296
+ function decimalsForRecord(value) {
8297
+ const direct = parseDecimals2(value.decimals);
8298
+ if (direct !== void 0) return direct;
8299
+ for (const key of ["tokenMetadata", "metadata", "token"]) {
8300
+ const nested = value[key];
8301
+ if (isRecord(nested)) {
8302
+ const decimals = parseDecimals2(nested.decimals);
8303
+ if (decimals !== void 0) return decimals;
8304
+ }
8305
+ }
8306
+ return void 0;
8307
+ }
8308
+ function formatRawAmount(raw, decimals) {
8309
+ if (decimals === 0) return raw.toString();
8310
+ const divisor = 10n ** BigInt(decimals);
8311
+ const whole = raw / divisor;
8312
+ const fraction = raw % divisor;
8313
+ if (fraction === 0n) return whole.toString();
8314
+ const fractionText = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
8315
+ return `${whole}.${fractionText}`;
8316
+ }
8317
+ function shouldNormalizeBalanceField(key) {
8318
+ const normalized = key.toLowerCase();
8319
+ return BALANCE_FIELD_NAMES.has(normalized) || normalized.endsWith("balance");
8320
+ }
8321
+ function normalizePortfolioOutput(value) {
8322
+ if (Array.isArray(value)) {
8323
+ return value.map(normalizePortfolioOutput);
8324
+ }
8325
+ if (!isRecord(value)) {
8326
+ return value;
8327
+ }
8328
+ const decimals = decimalsForRecord(value);
8329
+ const output = {};
8330
+ for (const [key, nested] of Object.entries(value)) {
8331
+ output[key] = normalizePortfolioOutput(nested);
8332
+ if (typeof nested !== "string" || !shouldNormalizeBalanceField(key) || !isHexQuantity(nested)) {
8333
+ continue;
8334
+ }
8335
+ const raw = BigInt(nested);
8336
+ output[`${key}Decimal`] = raw.toString();
8337
+ if (decimals !== void 0) {
8338
+ output[`${key}Formatted`] = formatRawAmount(raw, decimals);
8339
+ }
8340
+ }
8341
+ return output;
8342
+ }
8206
8343
  async function runPortfolioCommand(program2, title, path, opts) {
8207
8344
  const limit = parseOptionalInt(opts.limit, "--limit");
8208
8345
  const result = await runDataCall(program2, title, path, JSON.parse(opts.body));
8209
- const output = limitPayload(result, limit);
8346
+ const output = normalizePortfolioOutput(limitPayload(result, limit));
8210
8347
  if (isJSONMode()) printJSON(output);
8211
8348
  else printSyntaxJSON(output);
8212
8349
  }
@@ -8864,7 +9001,7 @@ function buildWalletQuoteClient(program2, address3) {
8864
9001
  }
8865
9002
 
8866
9003
  // src/lib/quote-errors.ts
8867
- function errorText2(err) {
9004
+ function errorText3(err) {
8868
9005
  if (err instanceof CLIError) {
8869
9006
  return [err.message, err.details].filter(Boolean).join(" ");
8870
9007
  }
@@ -8922,7 +9059,7 @@ function normalizeQuoteError(err, flow) {
8922
9059
  if (err instanceof CLIError && err.code !== ErrorCode.RPC_ERROR && err.code !== ErrorCode.INTERNAL_ERROR) {
8923
9060
  return err;
8924
9061
  }
8925
- const detail = errorText2(err);
9062
+ const detail = errorText3(err);
8926
9063
  const cause = classifyQuoteFailure(detail);
8927
9064
  return new CLIError(
8928
9065
  ErrorCode.QUOTE_FAILED,
@@ -9962,7 +10099,7 @@ function displayPath(path) {
9962
10099
  const home = getHomeDir();
9963
10100
  return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
9964
10101
  }
9965
- function isRecord(value) {
10102
+ function isRecord2(value) {
9966
10103
  return typeof value === "object" && value !== null && !Array.isArray(value);
9967
10104
  }
9968
10105
  function isClientId(value) {
@@ -10149,7 +10286,7 @@ async function readJSONConfig(path) {
10149
10286
  const raw = await readFile(path, "utf8");
10150
10287
  if (raw.trim().length === 0) return {};
10151
10288
  const parsed = JSON.parse(raw);
10152
- if (!isRecord(parsed)) {
10289
+ if (!isRecord2(parsed)) {
10153
10290
  throw errInvalidArgs(`${displayPath(path)} must contain a JSON object.`);
10154
10291
  }
10155
10292
  return parsed;
@@ -10163,7 +10300,7 @@ async function readJSONConfig(path) {
10163
10300
  }
10164
10301
  function withMcpServerConfig(config) {
10165
10302
  const existing = config.mcpServers;
10166
- if (existing !== void 0 && !isRecord(existing)) {
10303
+ if (existing !== void 0 && !isRecord2(existing)) {
10167
10304
  throw errInvalidArgs("Cursor mcpServers must be a JSON object.");
10168
10305
  }
10169
10306
  return {
@@ -10581,7 +10718,7 @@ async function flushProcessOutput() {
10581
10718
  }
10582
10719
  program.name("alchemy").description(
10583
10720
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
10584
- ).version("0.14.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
10721
+ ).version("0.15.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
10585
10722
  "-n, --network <network>",
10586
10723
  "Target network for networked commands"
10587
10724
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -10807,7 +10944,7 @@ ${styledLine}`;
10807
10944
  if (isInteractiveAllowed(program)) {
10808
10945
  let latestForInteractiveStartup = null;
10809
10946
  if (shouldRunOnboarding(program, cfg)) {
10810
- const { runOnboarding } = await import("./onboarding-QE43IUEK.js");
10947
+ const { runOnboarding } = await import("./onboarding-L2FTZRYN.js");
10811
10948
  const latest = getAvailableUpdateOnce();
10812
10949
  const completed = await runOnboarding(program, latest);
10813
10950
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -10821,7 +10958,7 @@ ${styledLine}`;
10821
10958
  latestForInteractiveStartup
10822
10959
  );
10823
10960
  }
10824
- const { startREPL } = await import("./interactive-XNDXNMAJ.js");
10961
+ const { startREPL } = await import("./interactive-3ACOLHPG.js");
10825
10962
  program.exitOverride();
10826
10963
  program.configureOutput({
10827
10964
  writeErr: () => {
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-CXR7CCJ7.js";
10
10
  import {
11
11
  getUpdateNoticeLines
12
- } from "./chunk-22KBYYLI.js";
12
+ } from "./chunk-UL5ZSWTE.js";
13
13
  import {
14
14
  bold,
15
15
  brand,
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  getUpdateNoticeLines
5
- } from "./chunk-22KBYYLI.js";
5
+ } from "./chunk-UL5ZSWTE.js";
6
6
  import {
7
7
  bold,
8
8
  brand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {