@alchemy/cli 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -182,6 +182,16 @@ Per-command override: pass `--signer <session\|local>` to `evm send`, `evm appro
182
182
  | `agent-prompt` | Emits complete agent/automation usage instructions | `alchemy --json --no-interactive agent-prompt` |
183
183
  | `version` | Prints CLI version | `alchemy version` |
184
184
 
185
+ ### Usage API (alpha)
186
+
187
+ The `usage` commands are in **alpha** — the response shape may change, and
188
+ access is limited to teams enrolled in the Usage API alpha.
189
+
190
+ ```sh
191
+ alchemy --json usage summary
192
+ alchemy usage timeseries --start-date 2026-06-01 --granularity day
193
+ ```
194
+
185
195
  ## Flags
186
196
 
187
197
  ### Global flags
@@ -221,6 +231,7 @@ Additional env vars:
221
231
  | Env var | Description |
222
232
  |---|---|
223
233
  | `ALCHEMY_CONFIG` | Custom path to config file |
234
+ | `ALCHEMY_AUTH_TOKEN` | Admin API auth token override |
224
235
  | `ALCHEMY_WALLET_KEY` | EVM wallet private key for x402 auth and local signing |
225
236
  | `ALCHEMY_SOLANA_WALLET_KEY` | Solana wallet private key |
226
237
  | `ALCHEMY_ACTIVE_SIGNER` | Active EVM signer override (`session` or `local`) |
@@ -3,11 +3,11 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  registerAuth,
5
5
  selectAppAfterAuth
6
- } from "./chunk-WDGPT4OT.js";
6
+ } from "./chunk-LRCCQA3I.js";
7
7
  import "./chunk-CZWKHYTE.js";
8
8
  import "./chunk-EJB4WDTU.js";
9
- import "./chunk-CXR7CCJ7.js";
10
- import "./chunk-OIRERSZT.js";
9
+ import "./chunk-FFQ7XKW7.js";
10
+ import "./chunk-GJL52AFY.js";
11
11
  import "./chunk-PISUI34T.js";
12
12
  import "./chunk-CFIDLPKB.js";
13
13
  export {
@@ -953,6 +953,7 @@ var AdminClient = class _AdminClient {
953
953
  }
954
954
  // Test/debug only: used by mock E2E to route admin requests locally.
955
955
  static ADMIN_API_BASE_URL_ENV = "ALCHEMY_ADMIN_API_BASE_URL";
956
+ static USAGE_DEBUG_TEAM_ID_HEADER = "x-alchemy-cli-debug-team-id";
956
957
  credential;
957
958
  constructor(credential) {
958
959
  if (!credential.token.trim()) {
@@ -993,7 +994,7 @@ var AdminClient = class _AdminClient {
993
994
  throw errInvalidArgs("Refusing to send credentials over non-HTTPS connection.");
994
995
  }
995
996
  }
996
- async request(method, path, body) {
997
+ async request(method, path, body, options) {
997
998
  const url = `${this.baseURL()}${path}`;
998
999
  debug(`${method} ${url}`);
999
1000
  this.assertSafeRequestTarget(url);
@@ -1003,7 +1004,8 @@ var AdminClient = class _AdminClient {
1003
1004
  headers: {
1004
1005
  Authorization: `Bearer ${this.credential.token}`,
1005
1006
  "Content-Type": "application/json",
1006
- Accept: "application/json"
1007
+ Accept: "application/json",
1008
+ ...options?.headers
1007
1009
  },
1008
1010
  ...body !== void 0 && { body: JSON.stringify(body) }
1009
1011
  });
@@ -1070,6 +1072,31 @@ var AdminClient = class _AdminClient {
1070
1072
  } while (cursor);
1071
1073
  return { apps, pages };
1072
1074
  }
1075
+ usageDebugHeaders(options) {
1076
+ if (options?.teamId === void 0) return void 0;
1077
+ const baseUrl = new URL(this.baseURL());
1078
+ if (!isLocalhost(baseUrl.hostname)) {
1079
+ throw errInvalidArgs(
1080
+ "--team-id is a temporary debug option and only works with ALCHEMY_ADMIN_API_BASE_URL pointing at localhost."
1081
+ );
1082
+ }
1083
+ return {
1084
+ [_AdminClient.USAGE_DEBUG_TEAM_ID_HEADER]: String(options.teamId)
1085
+ };
1086
+ }
1087
+ async getUsageSummary(options) {
1088
+ return await this.request("GET", "/v1/usage/summary", void 0, {
1089
+ headers: this.usageDebugHeaders(options)
1090
+ });
1091
+ }
1092
+ async getUsageTimeseries(body, options) {
1093
+ return await this.request(
1094
+ "POST",
1095
+ "/v1/usage/time-series",
1096
+ body,
1097
+ { headers: this.usageDebugHeaders(options) }
1098
+ );
1099
+ }
1073
1100
  async getApp(id) {
1074
1101
  const resp = await this.request("GET", `/v1/apps/${id}`);
1075
1102
  return resp.data;
@@ -1621,6 +1648,9 @@ function resolveAppId(program, cfg) {
1621
1648
  return void 0;
1622
1649
  }
1623
1650
  function resolveAuthToken(cfg) {
1651
+ if (process.env.ALCHEMY_AUTH_TOKEN?.trim()) {
1652
+ return process.env.ALCHEMY_AUTH_TOKEN.trim();
1653
+ }
1624
1654
  const config = cfg ?? load();
1625
1655
  if (!config.auth_token?.trim()) return void 0;
1626
1656
  if (config.auth_token_expires_at) {
@@ -606,6 +606,7 @@ export {
606
606
  green,
607
607
  red,
608
608
  dim,
609
+ cyan,
609
610
  bold,
610
611
  yellow,
611
612
  brand,
@@ -3,7 +3,7 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  gasManagerClientFromFlags,
5
5
  toAdminNetworkId
6
- } from "./chunk-CXR7CCJ7.js";
6
+ } from "./chunk-FFQ7XKW7.js";
7
7
  import {
8
8
  dim,
9
9
  green,
@@ -11,7 +11,7 @@ import {
11
11
  promptConfirm,
12
12
  promptText,
13
13
  withSpinner
14
- } from "./chunk-OIRERSZT.js";
14
+ } from "./chunk-GJL52AFY.js";
15
15
  import {
16
16
  load,
17
17
  save
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  AdminClient,
13
13
  resolveAuthToken
14
- } from "./chunk-CXR7CCJ7.js";
14
+ } from "./chunk-FFQ7XKW7.js";
15
15
  import {
16
16
  bold,
17
17
  brand,
@@ -20,7 +20,7 @@ import {
20
20
  promptAutocomplete,
21
21
  promptText,
22
22
  withSpinner
23
- } from "./chunk-OIRERSZT.js";
23
+ } from "./chunk-GJL52AFY.js";
24
24
  import {
25
25
  configPath,
26
26
  load,
@@ -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.16.0" : "0.0.0";
57
57
  }
58
58
  function toUpdateStatus(latestVersion, checkedAt) {
59
59
  const current = currentVersion();
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  resolveAuthToken,
8
8
  resolveWalletSession
9
- } from "./chunk-CXR7CCJ7.js";
9
+ } from "./chunk-FFQ7XKW7.js";
10
10
 
11
11
  // src/lib/onboarding.ts
12
12
  var SETUP_CAPABILITY_ORDER = [
package/dist/index.js CHANGED
@@ -5,10 +5,10 @@ import {
5
5
  errNotLoggedInForPolicyLookup,
6
6
  errSponsorshipNeedsPolicy,
7
7
  selectOrCreatePolicy
8
- } from "./chunk-M7HFRKW6.js";
8
+ } from "./chunk-HPRJEGSP.js";
9
9
  import {
10
10
  registerAuth
11
- } from "./chunk-WDGPT4OT.js";
11
+ } from "./chunk-LRCCQA3I.js";
12
12
  import {
13
13
  openBrowser
14
14
  } from "./chunk-CZWKHYTE.js";
@@ -18,7 +18,7 @@ import {
18
18
  getSetupStatus,
19
19
  isSetupComplete,
20
20
  shouldRunOnboarding
21
- } from "./chunk-RQDWIB62.js";
21
+ } from "./chunk-Q2VRERYE.js";
22
22
  import {
23
23
  isInteractiveAllowed
24
24
  } from "./chunk-EJB4WDTU.js";
@@ -63,16 +63,17 @@ import {
63
63
  updateSession,
64
64
  validateNetwork,
65
65
  walletNetworkToChain
66
- } from "./chunk-CXR7CCJ7.js";
66
+ } from "./chunk-FFQ7XKW7.js";
67
67
  import {
68
68
  getAvailableUpdate,
69
69
  getUpdateStatus,
70
70
  printUpdateNotice
71
- } from "./chunk-22KBYYLI.js";
71
+ } from "./chunk-N422J6U3.js";
72
72
  import {
73
73
  bold,
74
74
  brand,
75
75
  brandedHelp,
76
+ cyan,
76
77
  dim,
77
78
  emptyState,
78
79
  etherscanTxURL,
@@ -92,7 +93,7 @@ import {
92
93
  weiToEth,
93
94
  withSpinner,
94
95
  yellow
95
- } from "./chunk-OIRERSZT.js";
96
+ } from "./chunk-GJL52AFY.js";
96
97
  import {
97
98
  KEY_MAP,
98
99
  configDir,
@@ -148,7 +149,7 @@ import {
148
149
  } from "./chunk-CFIDLPKB.js";
149
150
 
150
151
  // src/index.ts
151
- import { Command, Help } from "commander";
152
+ import { Command as Command2, Help } from "commander";
152
153
 
153
154
  // src/lib/ens.ts
154
155
  import { keccak_256 } from "@noble/hashes/sha3.js";
@@ -582,8 +583,8 @@ function registerConfig(program2) {
582
583
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set evm-gas-policy-id <id>`."
583
584
  );
584
585
  }
585
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-UNNIF63S.js");
586
- const { resolveNetwork: resolveNetwork2 } = await import("./resolve-IRTGQL4A.js");
586
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-DRO7Q5VI.js");
587
+ const { resolveNetwork: resolveNetwork2 } = await import("./resolve-B64J4LR6.js");
587
588
  const network = resolveNetwork2(program2);
588
589
  await selectOrCreatePolicy2({
589
590
  flavor: "sponsorship",
@@ -637,8 +638,8 @@ function registerConfig(program2) {
637
638
  "Interactive policy selection requires an interactive terminal. Pass an ID: `alchemy config set solana-fee-policy-id <id>`."
638
639
  );
639
640
  }
640
- const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-UNNIF63S.js");
641
- const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-IRTGQL4A.js");
641
+ const { selectOrCreatePolicy: selectOrCreatePolicy2 } = await import("./policy-prompt-DRO7Q5VI.js");
642
+ const { resolveSolanaNetwork: resolveSolanaNetwork2 } = await import("./resolve-B64J4LR6.js");
642
643
  const network = resolveSolanaNetwork2(program2);
643
644
  await selectOrCreatePolicy2({
644
645
  flavor: "solana",
@@ -695,7 +696,7 @@ function registerConfig(program2) {
695
696
  printJSON(toMap(cfg));
696
697
  return;
697
698
  }
698
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-IRTGQL4A.js");
699
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-B64J4LR6.js");
699
700
  const validToken = resolveAuthToken2(cfg);
700
701
  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");
701
702
  const pairs = [
@@ -1370,6 +1371,455 @@ function registerApps(program2) {
1370
1371
  });
1371
1372
  }
1372
1373
 
1374
+ // src/commands/usage.ts
1375
+ import { Option } from "commander";
1376
+
1377
+ // src/lib/usage-format.ts
1378
+ var BLOCKS = [" ", "\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
1379
+ var SPARKS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1380
+ var MAX_ROWS = 31;
1381
+ function asRecord(value) {
1382
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
1383
+ return void 0;
1384
+ }
1385
+ return value;
1386
+ }
1387
+ function asStringArray(value) {
1388
+ if (!Array.isArray(value)) return void 0;
1389
+ const strings = value.filter((item) => typeof item === "string");
1390
+ return strings.length > 0 ? strings : void 0;
1391
+ }
1392
+ function parsePoint(value) {
1393
+ const record = asRecord(value);
1394
+ if (!record) return void 0;
1395
+ const dimensions = asRecord(record.dimensions);
1396
+ return {
1397
+ startTime: typeof record.startTime === "string" ? record.startTime : void 0,
1398
+ endTime: typeof record.endTime === "string" ? record.endTime : void 0,
1399
+ isPartial: typeof record.isPartial === "boolean" ? record.isPartial : void 0,
1400
+ dimensions: dimensions ? Object.fromEntries(
1401
+ Object.entries(dimensions).filter((entry) => typeof entry[1] === "string")
1402
+ ) : void 0,
1403
+ amount: typeof record.amount === "string" ? record.amount : void 0,
1404
+ unit: typeof record.unit === "string" ? record.unit : void 0,
1405
+ usd: typeof record.usd === "string" ? record.usd : void 0
1406
+ };
1407
+ }
1408
+ function getUsageTimeSeriesData(result) {
1409
+ const root = asRecord(result);
1410
+ const envelope = asRecord(root?.data);
1411
+ const points = Array.isArray(envelope?.data) ? envelope.data.map(parsePoint).filter((point) => Boolean(point)) : void 0;
1412
+ if (!envelope || !points) return void 0;
1413
+ const query = asRecord(envelope.query);
1414
+ const freshness = asRecord(envelope.freshness);
1415
+ return {
1416
+ query: query ? {
1417
+ startTime: typeof query.startTime === "string" ? query.startTime : void 0,
1418
+ endTime: typeof query.endTime === "string" ? query.endTime : void 0,
1419
+ metrics: asStringArray(query.metrics),
1420
+ groupBy: asStringArray(query.groupBy)
1421
+ } : void 0,
1422
+ freshness: freshness ? {
1423
+ dataThrough: typeof freshness.dataThrough === "string" ? freshness.dataThrough : void 0,
1424
+ containsPartialToday: typeof freshness.containsPartialToday === "boolean" ? freshness.containsPartialToday : void 0
1425
+ } : void 0,
1426
+ data: points
1427
+ };
1428
+ }
1429
+ function finiteNumber(value) {
1430
+ if (!value) return void 0;
1431
+ const parsed = Number(value);
1432
+ return Number.isFinite(parsed) ? parsed : void 0;
1433
+ }
1434
+ function chooseValueField(data) {
1435
+ const metrics = data.query?.metrics ?? [];
1436
+ const hasAmount = data.data.some((point) => finiteNumber(point.amount) !== void 0);
1437
+ const hasUsd = data.data.some((point) => finiteNumber(point.usd) !== void 0);
1438
+ if (metrics.includes("usd") && !metrics.includes("amount") && hasUsd) return "usd";
1439
+ if (hasAmount) return "amount";
1440
+ if (hasUsd) return "usd";
1441
+ return void 0;
1442
+ }
1443
+ function dateLabel(value) {
1444
+ if (!value) return "unknown";
1445
+ return value.slice(0, 10);
1446
+ }
1447
+ function aggregateByDay(points, field) {
1448
+ const buckets = /* @__PURE__ */ new Map();
1449
+ for (const point of points) {
1450
+ const value = finiteNumber(point[field]);
1451
+ if (value === void 0) continue;
1452
+ const date = dateLabel(point.startTime);
1453
+ const existing = buckets.get(date);
1454
+ if (existing) {
1455
+ existing.value += value;
1456
+ existing.isPartial = existing.isPartial || point.isPartial === true;
1457
+ } else {
1458
+ buckets.set(date, {
1459
+ date,
1460
+ value,
1461
+ isPartial: point.isPartial === true
1462
+ });
1463
+ }
1464
+ }
1465
+ return [...buckets.values()].sort((a, b) => a.date.localeCompare(b.date));
1466
+ }
1467
+ function formatNumber(value, field) {
1468
+ return new Intl.NumberFormat("en-US", {
1469
+ maximumFractionDigits: field === "usd" ? 2 : 3
1470
+ }).format(value);
1471
+ }
1472
+ function metricLabel(args) {
1473
+ if (args.field === "usd") return "USD";
1474
+ return args.unit ?? "usage";
1475
+ }
1476
+ function bar(args) {
1477
+ if (args.max <= 0 || args.value <= 0) return dim(" ".repeat(args.width));
1478
+ const filled = args.value / args.max * args.width;
1479
+ const full = Math.floor(filled);
1480
+ const partial = Math.round((filled - full) * 8);
1481
+ const partialBlock = partial > 0 ? BLOCKS[partial] : "";
1482
+ const barText = "\u2588".repeat(full) + partialBlock;
1483
+ const padding = " ".repeat(Math.max(0, args.width - full - (partial > 0 ? 1 : 0)));
1484
+ return cyan(barText) + dim(padding);
1485
+ }
1486
+ function sparkline(values) {
1487
+ const max = Math.max(...values, 1);
1488
+ const line = values.map((value) => SPARKS[Math.round(value / max * 7)] ?? SPARKS[0]).join("");
1489
+ return green(line);
1490
+ }
1491
+ function labelWidth(labels) {
1492
+ return Math.min(Math.max(...labels.map((label) => label.length), 4), 18);
1493
+ }
1494
+ function formatRange(data, buckets) {
1495
+ const start = data.query?.startTime ? dateLabel(data.query.startTime) : buckets[0]?.date;
1496
+ const end = data.query?.endTime ? dateLabel(data.query.endTime) : buckets[buckets.length - 1]?.date;
1497
+ return start && end ? `${start} -> ${end}` : "unknown range";
1498
+ }
1499
+ function dimensionLabel(point) {
1500
+ const entries = Object.entries(point.dimensions ?? {}).filter(([, value]) => value).map(([key, value]) => `${key}=${value}`);
1501
+ return entries.length > 0 ? entries.join(", ") : void 0;
1502
+ }
1503
+ function aggregateDimensions(points, field) {
1504
+ const totals = /* @__PURE__ */ new Map();
1505
+ for (const point of points) {
1506
+ const label = dimensionLabel(point);
1507
+ const value = finiteNumber(point[field]);
1508
+ if (!label || value === void 0) continue;
1509
+ totals.set(label, (totals.get(label) ?? 0) + value);
1510
+ }
1511
+ return [...totals.entries()].map(([label, value]) => ({ label, value })).sort((a, b) => b.value - a.value).slice(0, 6);
1512
+ }
1513
+ function formatUsageTimeSeriesResult(result) {
1514
+ const data = getUsageTimeSeriesData(result);
1515
+ if (!data || data.data.length === 0) return null;
1516
+ const field = chooseValueField(data);
1517
+ if (!field) return null;
1518
+ const buckets = aggregateByDay(data.data, field);
1519
+ if (buckets.length === 0) return null;
1520
+ const unit = metricLabel({
1521
+ field,
1522
+ unit: data.data.find((point) => point.unit)?.unit
1523
+ });
1524
+ const values = buckets.map((bucket) => bucket.value);
1525
+ const total = values.reduce((sum, value) => sum + value, 0);
1526
+ const peak = buckets.reduce(
1527
+ (best, bucket) => bucket.value > best.value ? bucket : best
1528
+ );
1529
+ const max = Math.max(...values, 1);
1530
+ const shownBuckets = buckets.slice(0, MAX_ROWS);
1531
+ const width = labelWidth(shownBuckets.map((bucket) => bucket.date));
1532
+ const lines = [];
1533
+ lines.push("");
1534
+ lines.push(` ${brand(bold("Usage time series"))}`);
1535
+ lines.push(` ${dim(formatRange(data, buckets))} ${dim("metric")} ${field}`);
1536
+ lines.push("");
1537
+ lines.push(
1538
+ ` ${dim("total")} ${green(formatNumber(total, field))} ${dim(unit)} ${dim("peak")} ${yellow(`${peak.date} ${formatNumber(peak.value, field)}`)}`
1539
+ );
1540
+ if (data.freshness?.dataThrough) {
1541
+ const partial = data.freshness.containsPartialToday ? " partial today" : "";
1542
+ lines.push(` ${dim("fresh through")} ${dateLabel(data.freshness.dataThrough)}${dim(partial)}`);
1543
+ }
1544
+ lines.push("");
1545
+ lines.push(` ${sparkline(values)}`);
1546
+ lines.push("");
1547
+ for (const bucket of shownBuckets) {
1548
+ const date = bucket.date.padEnd(width);
1549
+ const partial = bucket.isPartial ? " *" : " ";
1550
+ const value = formatNumber(bucket.value, field).padStart(12);
1551
+ lines.push(
1552
+ ` ${dim(date)}${partial} ${value} ${dim(unit)} ${bar({
1553
+ value: bucket.value,
1554
+ max,
1555
+ width: 28
1556
+ })}`
1557
+ );
1558
+ }
1559
+ if (buckets.length > shownBuckets.length) {
1560
+ lines.push(
1561
+ ` ${dim(`showing ${shownBuckets.length} of ${buckets.length} days; use --json for full data`)}`
1562
+ );
1563
+ }
1564
+ if (buckets.some((bucket) => bucket.isPartial)) {
1565
+ lines.push(` ${dim("* partial bucket")}`);
1566
+ }
1567
+ const dimensions = aggregateDimensions(data.data, field);
1568
+ if (dimensions.length > 0) {
1569
+ const dimensionMax = Math.max(...dimensions.map((item) => item.value), 1);
1570
+ const dimensionWidth = labelWidth(dimensions.map((item) => item.label));
1571
+ lines.push("");
1572
+ lines.push(` ${brand(bold("Top dimensions"))}`);
1573
+ for (const item of dimensions) {
1574
+ const label = item.label.length > dimensionWidth ? `${item.label.slice(0, dimensionWidth - 1)}\u2026` : item.label.padEnd(dimensionWidth);
1575
+ lines.push(
1576
+ ` ${dim(label)} ${formatNumber(item.value, field).padStart(12)} ${dim(unit)} ${bar({
1577
+ value: item.value,
1578
+ max: dimensionMax,
1579
+ width: 20
1580
+ })}`
1581
+ );
1582
+ }
1583
+ }
1584
+ return `${lines.join("\n")}
1585
+ `;
1586
+ }
1587
+
1588
+ // src/commands/usage.ts
1589
+ var DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
1590
+ var DATETIME_WITH_TIMEZONE_RE = /^\d{4}-\d{2}-\d{2}T.+(Z|[+-]\d{2}:\d{2})$/;
1591
+ var TEAM_ID_RE = /^\d+$/;
1592
+ var VALID_METRICS = /* @__PURE__ */ new Set(["amount", "usd"]);
1593
+ var VALID_GROUP_BY = /* @__PURE__ */ new Set([
1594
+ "requestType",
1595
+ "app",
1596
+ "network",
1597
+ "method"
1598
+ ]);
1599
+ var VALID_REQUEST_TYPES = /* @__PURE__ */ new Set([
1600
+ "http",
1601
+ "websocket",
1602
+ "webhook",
1603
+ "grpc"
1604
+ ]);
1605
+ function parseBodyJSON(value) {
1606
+ let parsed;
1607
+ try {
1608
+ parsed = JSON.parse(value);
1609
+ } catch {
1610
+ throw errInvalidArgs("--body must be valid JSON.");
1611
+ }
1612
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
1613
+ throw errInvalidArgs("--body must be a JSON object.");
1614
+ }
1615
+ return parsed;
1616
+ }
1617
+ function splitCommaList2(value) {
1618
+ if (!value) return void 0;
1619
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
1620
+ return items.length > 0 ? items : void 0;
1621
+ }
1622
+ function assertAllowedValues(values, allowed, flagName) {
1623
+ for (const value of values ?? []) {
1624
+ if (!allowed.has(value)) {
1625
+ throw errInvalidArgs(
1626
+ `${flagName} contains unsupported value '${value}'.`
1627
+ );
1628
+ }
1629
+ }
1630
+ }
1631
+ function debugTeamIdOption() {
1632
+ return new Option(
1633
+ "--team-id <id>",
1634
+ "Temporary local usage API team override"
1635
+ ).hideHelp();
1636
+ }
1637
+ function parseTeamId(value) {
1638
+ if (value === void 0) return void 0;
1639
+ if (!TEAM_ID_RE.test(value)) {
1640
+ throw errInvalidArgs("--team-id must be a positive integer.");
1641
+ }
1642
+ const teamId = Number(value);
1643
+ if (!Number.isSafeInteger(teamId) || teamId <= 0) {
1644
+ throw errInvalidArgs("--team-id must be a positive integer.");
1645
+ }
1646
+ return teamId;
1647
+ }
1648
+ function buildUsageRequestOptions(opts) {
1649
+ const teamId = parseTeamId(opts.teamId);
1650
+ return teamId === void 0 ? void 0 : { teamId };
1651
+ }
1652
+ function normalizeDateTime(value, flagName) {
1653
+ if (!value) return void 0;
1654
+ if (DATE_ONLY_RE.test(value)) {
1655
+ const date2 = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
1656
+ if (Number.isNaN(date2.getTime()) || date2.toISOString().slice(0, 10) !== value) {
1657
+ throw errInvalidArgs(`${flagName} must be a valid date or datetime.`);
1658
+ }
1659
+ return `${value}T00:00:00.000Z`;
1660
+ }
1661
+ if (!DATETIME_WITH_TIMEZONE_RE.test(value)) {
1662
+ throw errInvalidArgs(
1663
+ `${flagName} must be YYYY-MM-DD or an ISO datetime with a timezone.`
1664
+ );
1665
+ }
1666
+ const date = new Date(value);
1667
+ if (Number.isNaN(date.getTime())) {
1668
+ throw errInvalidArgs(`${flagName} must be a valid date or datetime.`);
1669
+ }
1670
+ return date.toISOString();
1671
+ }
1672
+ function hasBuilderOptions(opts) {
1673
+ return Object.entries(opts).some(
1674
+ ([key, value]) => key !== "body" && key !== "teamId" && value !== void 0
1675
+ );
1676
+ }
1677
+ function buildTimeseriesRequest(opts) {
1678
+ if (opts.body) {
1679
+ if (hasBuilderOptions(opts)) {
1680
+ throw errInvalidArgs("--body cannot be combined with other time series flags.");
1681
+ }
1682
+ return parseBodyJSON(opts.body);
1683
+ }
1684
+ if (opts.startDate && opts.startTime) {
1685
+ throw errInvalidArgs("Pass either --start-date or --start-time, not both.");
1686
+ }
1687
+ if (opts.endDate && opts.endTime) {
1688
+ throw errInvalidArgs("Pass either --end-date or --end-time, not both.");
1689
+ }
1690
+ const startTime = normalizeDateTime(
1691
+ opts.startTime ?? opts.startDate,
1692
+ opts.startTime ? "--start-time" : "--start-date"
1693
+ );
1694
+ const endTime = normalizeDateTime(
1695
+ opts.endTime ?? opts.endDate,
1696
+ opts.endTime ? "--end-time" : "--end-date"
1697
+ ) ?? (/* @__PURE__ */ new Date()).toISOString();
1698
+ if (!startTime) {
1699
+ throw errInvalidArgs(
1700
+ "Usage time series requires --start-date or --start-time, or --body."
1701
+ );
1702
+ }
1703
+ const products = splitCommaList2(opts.products);
1704
+ const metrics = splitCommaList2(opts.metrics);
1705
+ const appIds = splitCommaList2(opts.appIds);
1706
+ const networks = splitCommaList2(opts.networks);
1707
+ const methods = splitCommaList2(opts.methods);
1708
+ const requestTypes = splitCommaList2(opts.requestTypes);
1709
+ const groupBy = splitCommaList2(opts.groupBy);
1710
+ assertAllowedValues(metrics, VALID_METRICS, "--metrics");
1711
+ assertAllowedValues(requestTypes, VALID_REQUEST_TYPES, "--request-types");
1712
+ assertAllowedValues(groupBy, VALID_GROUP_BY, "--group-by");
1713
+ if (groupBy && groupBy.length > 1) {
1714
+ throw errInvalidArgs("--group-by accepts at most one dimension.");
1715
+ }
1716
+ const granularity = opts.granularity ?? "day";
1717
+ if (granularity !== "day" && granularity !== "hour") {
1718
+ throw errInvalidArgs("--granularity must be 'hour' or 'day'.");
1719
+ }
1720
+ const filters = {
1721
+ ...appIds && { appIds },
1722
+ ...networks && { networks },
1723
+ ...methods && { methods },
1724
+ ...requestTypes && { requestTypes }
1725
+ };
1726
+ return {
1727
+ startTime,
1728
+ endTime,
1729
+ granularity,
1730
+ ...products && { products },
1731
+ ...metrics && { metrics },
1732
+ ...Object.keys(filters).length > 0 && { filters },
1733
+ ...groupBy && { groupBy }
1734
+ };
1735
+ }
1736
+ function printUsageResult(result) {
1737
+ if (isJSONMode()) {
1738
+ printJSON(result);
1739
+ return;
1740
+ }
1741
+ printSyntaxJSON(result);
1742
+ }
1743
+ function printUsageTimeseriesResult(result) {
1744
+ if (isJSONMode()) {
1745
+ printJSON(result);
1746
+ return;
1747
+ }
1748
+ const formatted = formatUsageTimeSeriesResult(result);
1749
+ if (formatted) {
1750
+ printHuman(formatted, result);
1751
+ return;
1752
+ }
1753
+ printSyntaxJSON(result);
1754
+ }
1755
+ function registerUsage(program2) {
1756
+ const cmd = program2.command("usage").description("View Alchemy usage data (alpha)").addHelpText(
1757
+ "after",
1758
+ () => (
1759
+ // Skip in JSON help mode so structured output stays valid JSON.
1760
+ isJSONMode() ? "" : [
1761
+ "",
1762
+ "Alpha: the Usage API is in early access. The response shape may",
1763
+ "change, and access is limited to teams enrolled in the alpha."
1764
+ ].join("\n")
1765
+ )
1766
+ );
1767
+ cmd.command("summary").description("Get account usage summary").addOption(debugTeamIdOption()).action(async (opts) => {
1768
+ try {
1769
+ const admin = adminClientFromFlags(program2);
1770
+ const usageOptions = buildUsageRequestOptions(opts);
1771
+ const result = await withSpinner(
1772
+ "Fetching usage summary...",
1773
+ "Usage summary fetched",
1774
+ () => usageOptions ? admin.getUsageSummary(usageOptions) : admin.getUsageSummary()
1775
+ );
1776
+ printUsageResult(result);
1777
+ } catch (err) {
1778
+ exitWithError(err);
1779
+ }
1780
+ });
1781
+ cmd.command("timeseries").alias("time-series").description("Get account usage time series data").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--end-date <date>", "End date (YYYY-MM-DD)").option("--start-time <datetime>", "Start time as an ISO datetime").option("--end-time <datetime>", "End time as an ISO datetime").option("--products <products>", "Comma-separated BillingProduct enum names").option("--metrics <metrics>", "Comma-separated metrics: amount,usd").option("--app-ids <ids>", "Filter by app IDs (comma-separated)").option(
1782
+ "--networks <networks>",
1783
+ "Filter by networks (comma-separated), e.g. eth-mainnet,base-mainnet"
1784
+ ).option("--methods <methods>", "Filter by methods (comma-separated), e.g. eth_getLogs").option(
1785
+ "--request-types <types>",
1786
+ "Filter by request types (comma-separated): http, websocket, webhook, grpc"
1787
+ ).option(
1788
+ "--group-by <dimension>",
1789
+ "Group by one dimension: requestType, app, network, method"
1790
+ ).option("--granularity <granularity>", "Granularity: hour or day (default day)").option("--body <json>", "Raw time series request JSON body").addOption(debugTeamIdOption()).addHelpText(
1791
+ "after",
1792
+ () => (
1793
+ // Skip in JSON help mode so the structured output stays valid JSON.
1794
+ isJSONMode() ? "" : [
1795
+ "",
1796
+ "Filters (optional, comma-separated, combined with AND):",
1797
+ " --app-ids one or more app IDs",
1798
+ " --networks network slugs, e.g. eth-mainnet,base-mainnet",
1799
+ " --methods RPC/REST methods, e.g. eth_getLogs",
1800
+ " --request-types http, websocket, webhook, grpc",
1801
+ "",
1802
+ "Group by (choose one dimension):",
1803
+ " requestType | app | network | method"
1804
+ ].join("\n")
1805
+ )
1806
+ ).action(async (opts) => {
1807
+ try {
1808
+ const admin = adminClientFromFlags(program2);
1809
+ const body = buildTimeseriesRequest(opts);
1810
+ const usageOptions = buildUsageRequestOptions(opts);
1811
+ const result = await withSpinner(
1812
+ "Fetching usage time series...",
1813
+ "Usage time series fetched",
1814
+ () => usageOptions ? admin.getUsageTimeseries(body, usageOptions) : admin.getUsageTimeseries(body)
1815
+ );
1816
+ printUsageTimeseriesResult(result);
1817
+ } catch (err) {
1818
+ exitWithError(err);
1819
+ }
1820
+ });
1821
+ }
1822
+
1373
1823
  // src/commands/wallet.ts
1374
1824
  import { readFileSync, writeFileSync, mkdirSync, rmSync } from "fs";
1375
1825
  import { join, dirname } from "path";
@@ -3371,6 +3821,87 @@ function registerWebhooks(program2) {
3371
3821
  });
3372
3822
  }
3373
3823
 
3824
+ // ../../packages/sdk/dist/solana/index.js
3825
+ var SOLANA_DAS_METHODS = /* @__PURE__ */ new Set([
3826
+ "getAsset",
3827
+ "getAssetsByOwner",
3828
+ "searchAssets",
3829
+ "getTokenAccounts",
3830
+ "getAssets",
3831
+ "getAssetProof",
3832
+ "getAssetsByAuthority",
3833
+ "getAssetsByGroup",
3834
+ "getAssetsByCreator",
3835
+ "getAssetSignatures",
3836
+ "getNftEditions"
3837
+ ]);
3838
+ var SOLANA_DAS_PAYWALL_PATTERN = /\b(free tier|payg|paid tier|paid plan|upgrade|plan does not|current plan|not available on your plan)\b/i;
3839
+ function isSolanaDasMethod(method) {
3840
+ return SOLANA_DAS_METHODS.has(method);
3841
+ }
3842
+ function solanaRpcFamilyForMethod(method) {
3843
+ return isSolanaDasMethod(method) ? "das" : "rpc";
3844
+ }
3845
+ function classifySolanaDasPaywallError(err) {
3846
+ const details = errorText(err);
3847
+ if (!SOLANA_DAS_PAYWALL_PATTERN.test(details)) {
3848
+ return void 0;
3849
+ }
3850
+ return {
3851
+ message: "Solana DAS request requires a paid Alchemy plan.",
3852
+ hint: "Upgrade your app to PAYG or use a plan with Solana DAS access at https://dashboard.alchemy.com/.",
3853
+ ...details && { details }
3854
+ };
3855
+ }
3856
+ function errorText(err) {
3857
+ if (err instanceof Error) {
3858
+ const details = stringValue(err.details);
3859
+ return [err.message, details].filter(Boolean).join(" ");
3860
+ }
3861
+ return String(err);
3862
+ }
3863
+ function stringValue(value) {
3864
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3865
+ }
3866
+
3867
+ // src/lib/solana-rpc.ts
3868
+ function errorText2(err) {
3869
+ if (err instanceof CLIError) {
3870
+ return [err.message, err.details].filter(Boolean).join(" ");
3871
+ }
3872
+ if (err instanceof Error) {
3873
+ return err.message;
3874
+ }
3875
+ return String(err);
3876
+ }
3877
+ function normalizeSolanaDasPaywallError(err) {
3878
+ if (err instanceof CLIError && err.code === ErrorCode.PAYMENT_REQUIRED) {
3879
+ return err;
3880
+ }
3881
+ const paywall = classifySolanaDasPaywallError(err);
3882
+ if (!paywall) {
3883
+ return null;
3884
+ }
3885
+ return new CLIError(
3886
+ ErrorCode.PAYMENT_REQUIRED,
3887
+ paywall.message,
3888
+ paywall.hint,
3889
+ paywall.details
3890
+ );
3891
+ }
3892
+ async function callSolana(client, family, method, params) {
3893
+ const startedAt = Date.now();
3894
+ debug(`solana ${family} ${method} params`, params);
3895
+ try {
3896
+ const result = await client.call(method, params);
3897
+ debug(`solana ${family} ${method} ok ${Date.now() - startedAt}ms`);
3898
+ return result;
3899
+ } catch (err) {
3900
+ debug(`solana ${family} ${method} failed ${Date.now() - startedAt}ms`, errorText2(err));
3901
+ throw err;
3902
+ }
3903
+ }
3904
+
3374
3905
  // src/commands/network.ts
3375
3906
  function registerNetwork(program2) {
3376
3907
  const cmd = program2.command("network").description("Manage networks");
@@ -5778,6 +6309,49 @@ var PortfolioApiClient = class {
5778
6309
  return dataPost(portfolioBaseUrl(this.options), this.apiKey, path, body, this.options);
5779
6310
  }
5780
6311
  };
6312
+ var PORTFOLIO_BALANCE_FIELD_NAMES = /* @__PURE__ */ new Set([
6313
+ "balance",
6314
+ "tokenbalance",
6315
+ "rawbalance",
6316
+ "rawtokenbalance",
6317
+ "amount"
6318
+ ]);
6319
+ function limitPortfolioPayload(value, limit) {
6320
+ if (limit === void 0)
6321
+ return value;
6322
+ if (Array.isArray(value)) {
6323
+ return value.slice(0, limit).map((item) => limitPortfolioPayload(item, limit));
6324
+ }
6325
+ if (isRecord(value)) {
6326
+ return Object.fromEntries(Object.entries(value).map(([key, nested]) => [
6327
+ key,
6328
+ limitPortfolioPayload(nested, limit)
6329
+ ]));
6330
+ }
6331
+ return value;
6332
+ }
6333
+ function normalizePortfolioOutput(value) {
6334
+ if (Array.isArray(value)) {
6335
+ return value.map(normalizePortfolioOutput);
6336
+ }
6337
+ if (!isRecord(value)) {
6338
+ return value;
6339
+ }
6340
+ const decimals = decimalsForRecord(value);
6341
+ const output = {};
6342
+ for (const [key, nested] of Object.entries(value)) {
6343
+ output[key] = normalizePortfolioOutput(nested);
6344
+ if (typeof nested !== "string" || !shouldNormalizeBalanceField(key) || !isHexQuantity(nested)) {
6345
+ continue;
6346
+ }
6347
+ const raw = BigInt(nested);
6348
+ output[`${key}Decimal`] = raw.toString();
6349
+ if (decimals !== void 0) {
6350
+ output[`${key}Formatted`] = formatRawAmount(raw, decimals);
6351
+ }
6352
+ }
6353
+ return output;
6354
+ }
5781
6355
  async function dataFetch(url, method, body, options) {
5782
6356
  const fetchFn = options.fetchFn ?? fetch;
5783
6357
  const resp = await fetchFn(url, {
@@ -5796,6 +6370,48 @@ async function dataFetch(url, method, body, options) {
5796
6370
  }
5797
6371
  return resp;
5798
6372
  }
6373
+ function isRecord(value) {
6374
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6375
+ }
6376
+ function isHexQuantity(value) {
6377
+ return /^0x[0-9a-f]+$/i.test(value);
6378
+ }
6379
+ function parseDecimals2(value) {
6380
+ const decimals = typeof value === "number" ? value : typeof value === "string" && /^\d+$/.test(value) ? Number(value) : void 0;
6381
+ if (decimals === void 0 || !Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
6382
+ return void 0;
6383
+ }
6384
+ return decimals;
6385
+ }
6386
+ function decimalsForRecord(value) {
6387
+ const direct = parseDecimals2(value.decimals);
6388
+ if (direct !== void 0)
6389
+ return direct;
6390
+ for (const key of ["tokenMetadata", "metadata", "token"]) {
6391
+ const nested = value[key];
6392
+ if (isRecord(nested)) {
6393
+ const decimals = parseDecimals2(nested.decimals);
6394
+ if (decimals !== void 0)
6395
+ return decimals;
6396
+ }
6397
+ }
6398
+ return void 0;
6399
+ }
6400
+ function formatRawAmount(raw, decimals) {
6401
+ if (decimals === 0)
6402
+ return raw.toString();
6403
+ const divisor = 10n ** BigInt(decimals);
6404
+ const whole = raw / divisor;
6405
+ const fraction = raw % divisor;
6406
+ if (fraction === 0n)
6407
+ return whole.toString();
6408
+ const fractionText = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
6409
+ return `${whole}.${fractionText}`;
6410
+ }
6411
+ function shouldNormalizeBalanceField(key) {
6412
+ const normalized = key.toLowerCase();
6413
+ return PORTFOLIO_BALANCE_FIELD_NAMES.has(normalized) || normalized.endsWith("balance");
6414
+ }
5799
6415
 
5800
6416
  // ../../packages/sdk/dist/surfaces/index.js
5801
6417
  var RPC_API_SURFACE = [
@@ -6020,7 +6636,7 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
6020
6636
  result = await withSpinner(
6021
6637
  `Calling ${method.name}...`,
6022
6638
  `Called ${method.name}`,
6023
- () => client.call(method.rpcMethod, params)
6639
+ () => callNetworkRpcMethod(client, method, params)
6024
6640
  );
6025
6641
  }
6026
6642
  printSyntaxJSON(result);
@@ -6029,6 +6645,20 @@ function registerRpcSurfaceCommand({ root, parent, method }) {
6029
6645
  }
6030
6646
  });
6031
6647
  }
6648
+ async function callNetworkRpcMethod(client, method, params) {
6649
+ if (method.family !== "solana") {
6650
+ return await client.call(method.rpcMethod, params);
6651
+ }
6652
+ const solanaFamily = solanaRpcFamilyForMethod(method.rpcMethod);
6653
+ try {
6654
+ return await callSolana(client, solanaFamily, method.rpcMethod, params);
6655
+ } catch (err) {
6656
+ if (solanaFamily === "das") {
6657
+ throw normalizeSolanaDasPaywallError(err) ?? err;
6658
+ }
6659
+ throw err;
6660
+ }
6661
+ }
6032
6662
  function parseRpcParams(input) {
6033
6663
  const parsed = parseRequiredJSON(input, "--params");
6034
6664
  if (Array.isArray(parsed)) {
@@ -6141,7 +6771,7 @@ function registerSolana(program2) {
6141
6771
  const result = await withSpinner(
6142
6772
  `Calling ${method}\u2026`,
6143
6773
  `Called ${method}`,
6144
- () => client.call(method, parseCLIParams(params))
6774
+ () => callSolana(client, "rpc", method, parseCLIParams(params))
6145
6775
  );
6146
6776
  printSyntaxJSON(result);
6147
6777
  } catch (err) {
@@ -6158,7 +6788,9 @@ function registerSolana(program2) {
6158
6788
  const result = await withSpinner(
6159
6789
  `Calling ${method}\u2026`,
6160
6790
  `Called ${method}`,
6161
- () => client.call(method, rpcParams)
6791
+ () => callSolana(client, "das", method, rpcParams).catch((err) => {
6792
+ throw normalizeSolanaDasPaywallError(err) ?? err;
6793
+ })
6162
6794
  );
6163
6795
  printSyntaxJSON(result);
6164
6796
  } catch (err) {
@@ -6308,7 +6940,7 @@ var AGENT_PROMPT_SCOPES = [
6308
6940
  "xchain"
6309
6941
  ];
6310
6942
  var SCOPE_COMMAND_PATHS = {
6311
- app: ["app", "auth", "config"],
6943
+ app: ["app", "usage", "auth", "config"],
6312
6944
  data: ["evm data", "evm logs", "evm block", "evm tx", "evm receipt"],
6313
6945
  evm: ["evm"],
6314
6946
  solana: ["solana"],
@@ -6325,7 +6957,7 @@ var SCOPE_COMMAND_PATHS = {
6325
6957
  xchain: ["xchain"]
6326
6958
  };
6327
6959
  var SCOPE_EXAMPLE_MATCHERS = {
6328
- app: [" app ", " auth ", " config "],
6960
+ app: [" app ", " usage ", " auth ", " config "],
6329
6961
  data: [" evm data ", " evm logs ", " evm block ", " evm tx ", " evm receipt "],
6330
6962
  evm: [" evm "],
6331
6963
  solana: [" solana "],
@@ -6371,7 +7003,7 @@ function buildCommandSchema(cmd) {
6371
7003
  required: a.required
6372
7004
  }));
6373
7005
  }
6374
- const opts = cmd.options;
7006
+ const opts = visibleOptions(cmd);
6375
7007
  if (opts.length > 0) {
6376
7008
  schema.options = opts.map((o) => ({
6377
7009
  flags: o.flags,
@@ -6386,6 +7018,11 @@ function buildCommandSchema(cmd) {
6386
7018
  }
6387
7019
  return schema;
6388
7020
  }
7021
+ function visibleOptions(cmd) {
7022
+ return cmd.options.filter((option) => {
7023
+ return !option.hidden;
7024
+ });
7025
+ }
6389
7026
  function commandMatchesPath(cmd, path) {
6390
7027
  if (path[0] !== cmd.name) return null;
6391
7028
  if (path.length === 1) return cmd;
@@ -6437,6 +7074,7 @@ function applyScope(payload, scope) {
6437
7074
  );
6438
7075
  }
6439
7076
  function buildAgentPrompt(program2) {
7077
+ const hasUsageCommand = program2.commands.some((cmd) => cmd.name() === "usage");
6440
7078
  const errors = {};
6441
7079
  for (const [code, exitCode] of Object.entries(EXIT_CODES)) {
6442
7080
  errors[code] = {
@@ -6446,6 +7084,24 @@ function buildAgentPrompt(program2) {
6446
7084
  };
6447
7085
  }
6448
7086
  const commands = program2.commands.filter((cmd) => cmd.name() !== "agent-prompt").map(buildCommandSchema);
7087
+ const examples = [
7088
+ "alchemy --json --no-interactive config status",
7089
+ "alchemy --json --no-interactive update-check",
7090
+ "alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY -n eth-mainnet",
7091
+ "alchemy --json --no-interactive app list",
7092
+ ...hasUsageCommand ? [
7093
+ "alchemy --json --no-interactive usage summary",
7094
+ "alchemy --json --no-interactive usage timeseries --start-date 2026-06-01 --end-date 2026-06-08"
7095
+ ] : [],
7096
+ "alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY -n eth-mainnet",
7097
+ "alchemy --json --no-interactive evm network list",
7098
+ "alchemy --json --no-interactive evm send 0xRecipient 0.001 --dry-run -n eth-sepolia",
7099
+ `alchemy --json --no-interactive evm contract read 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "balanceOf(address)(uint256)" --args '["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]' -n eth-mainnet`,
7100
+ "alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet",
7101
+ "alchemy --json --no-interactive evm logs --from-block latest --limit 25 -n eth-mainnet",
7102
+ "alchemy --json --no-interactive evm block latest --summary -n eth-mainnet",
7103
+ "alchemy --json --no-interactive evm status 0xCallId -n eth-mainnet"
7104
+ ];
6449
7105
  return {
6450
7106
  executionPolicy: [
6451
7107
  "Always pass --json --no-interactive",
@@ -6515,9 +7171,15 @@ function buildAgentPrompt(program2) {
6515
7171
  },
6516
7172
  {
6517
7173
  method: "Alchemy login",
7174
+ envVar: "ALCHEMY_AUTH_TOKEN",
6518
7175
  setup: "alchemy auth login",
6519
- commandFamilies: ["app", "evm network", "gas-manager"],
6520
- notes: "Admin surfaces use the browser login session stored by `alchemy auth login`."
7176
+ commandFamilies: [
7177
+ "app",
7178
+ ...hasUsageCommand ? ["usage"] : [],
7179
+ "evm network",
7180
+ "gas-manager"
7181
+ ],
7182
+ notes: "Admin surfaces use the browser login session stored by `alchemy auth login`, or `ALCHEMY_AUTH_TOKEN` when set."
6521
7183
  },
6522
7184
  {
6523
7185
  method: "Webhook API key",
@@ -6571,20 +7233,7 @@ function buildAgentPrompt(program2) {
6571
7233
  ],
6572
7234
  commands,
6573
7235
  errors,
6574
- examples: [
6575
- "alchemy --json --no-interactive config status",
6576
- "alchemy --json --no-interactive update-check",
6577
- "alchemy --json --no-interactive evm data balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY -n eth-mainnet",
6578
- "alchemy --json --no-interactive app list",
6579
- "alchemy --json --no-interactive evm rpc eth_blockNumber --api-key $ALCHEMY_API_KEY -n eth-mainnet",
6580
- "alchemy --json --no-interactive evm network list",
6581
- "alchemy --json --no-interactive evm send 0xRecipient 0.001 --dry-run -n eth-sepolia",
6582
- `alchemy --json --no-interactive evm contract read 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 "balanceOf(address)(uint256)" --args '["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"]' -n eth-mainnet`,
6583
- "alchemy --json --no-interactive evm swap quote --from 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE --to 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --amount 1.0 --from-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -n eth-mainnet",
6584
- "alchemy --json --no-interactive evm logs --from-block latest --limit 25 -n eth-mainnet",
6585
- "alchemy --json --no-interactive evm block latest --summary -n eth-mainnet",
6586
- "alchemy --json --no-interactive evm status 0xCallId -n eth-mainnet"
6587
- ],
7236
+ examples,
6588
7237
  docs: "https://www.alchemy.com/docs"
6589
7238
  };
6590
7239
  }
@@ -6807,7 +7456,7 @@ function registrySymbolSuggestions(network) {
6807
7456
  }
6808
7457
 
6809
7458
  // src/lib/preflight-errors.ts
6810
- function errorText(err) {
7459
+ function errorText3(err) {
6811
7460
  if (err instanceof CLIError) {
6812
7461
  return [err.message, err.details].filter(Boolean).join(" ");
6813
7462
  }
@@ -6842,7 +7491,7 @@ function normalizePreflightRevertError(err) {
6842
7491
  if (err instanceof CLIError && err.code === ErrorCode.PREFLIGHT_REVERT) {
6843
7492
  return err;
6844
7493
  }
6845
- const detail = errorText(err);
7494
+ const detail = errorText3(err);
6846
7495
  const reason = extractRevertReason(detail);
6847
7496
  if (!reason) return null;
6848
7497
  return errPreflightRevert(reason, detail || void 0);
@@ -8188,25 +8837,10 @@ async function runDataCall(program2, title, path, body) {
8188
8837
  () => x402 ? x402.callRest(`data/v1${path}`, { method: "POST", body }) : callApiData(resolveAPIKey(program2), path, { method: "POST", body })
8189
8838
  );
8190
8839
  }
8191
- function limitPayload(value, limit) {
8192
- if (limit === void 0) return value;
8193
- if (Array.isArray(value)) {
8194
- return value.slice(0, limit);
8195
- }
8196
- if (value && typeof value === "object") {
8197
- return Object.fromEntries(
8198
- Object.entries(value).map(([key, nested]) => [
8199
- key,
8200
- Array.isArray(nested) ? nested.slice(0, limit) : nested
8201
- ])
8202
- );
8203
- }
8204
- return value;
8205
- }
8206
8840
  async function runPortfolioCommand(program2, title, path, opts) {
8207
8841
  const limit = parseOptionalInt(opts.limit, "--limit");
8208
8842
  const result = await runDataCall(program2, title, path, JSON.parse(opts.body));
8209
- const output = limitPayload(result, limit);
8843
+ const output = normalizePortfolioOutput(limitPortfolioPayload(result, limit));
8210
8844
  if (isJSONMode()) printJSON(output);
8211
8845
  else printSyntaxJSON(output);
8212
8846
  }
@@ -8864,7 +9498,7 @@ function buildWalletQuoteClient(program2, address3) {
8864
9498
  }
8865
9499
 
8866
9500
  // src/lib/quote-errors.ts
8867
- function errorText2(err) {
9501
+ function errorText4(err) {
8868
9502
  if (err instanceof CLIError) {
8869
9503
  return [err.message, err.details].filter(Boolean).join(" ");
8870
9504
  }
@@ -8922,7 +9556,7 @@ function normalizeQuoteError(err, flow) {
8922
9556
  if (err instanceof CLIError && err.code !== ErrorCode.RPC_ERROR && err.code !== ErrorCode.INTERNAL_ERROR) {
8923
9557
  return err;
8924
9558
  }
8925
- const detail = errorText2(err);
9559
+ const detail = errorText4(err);
8926
9560
  const cause = classifyQuoteFailure(detail);
8927
9561
  return new CLIError(
8928
9562
  ErrorCode.QUOTE_FAILED,
@@ -9962,7 +10596,7 @@ function displayPath(path) {
9962
10596
  const home = getHomeDir();
9963
10597
  return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
9964
10598
  }
9965
- function isRecord(value) {
10599
+ function isRecord2(value) {
9966
10600
  return typeof value === "object" && value !== null && !Array.isArray(value);
9967
10601
  }
9968
10602
  function isClientId(value) {
@@ -10149,7 +10783,7 @@ async function readJSONConfig(path) {
10149
10783
  const raw = await readFile(path, "utf8");
10150
10784
  if (raw.trim().length === 0) return {};
10151
10785
  const parsed = JSON.parse(raw);
10152
- if (!isRecord(parsed)) {
10786
+ if (!isRecord2(parsed)) {
10153
10787
  throw errInvalidArgs(`${displayPath(path)} must contain a JSON object.`);
10154
10788
  }
10155
10789
  return parsed;
@@ -10163,7 +10797,7 @@ async function readJSONConfig(path) {
10163
10797
  }
10164
10798
  function withMcpServerConfig(config) {
10165
10799
  const existing = config.mcpServers;
10166
- if (existing !== void 0 && !isRecord(existing)) {
10800
+ if (existing !== void 0 && !isRecord2(existing)) {
10167
10801
  throw errInvalidArgs("Cursor mcpServers must be a JSON object.");
10168
10802
  }
10169
10803
  return {
@@ -10511,7 +11145,7 @@ var ROOT_COMMAND_PILLARS = [
10511
11145
  },
10512
11146
  {
10513
11147
  label: "Account & Platform",
10514
- commands: ["auth", "wallet", "app", "webhook", "config"]
11148
+ commands: ["auth", "wallet", "app", "usage", "webhook", "config"]
10515
11149
  },
10516
11150
  {
10517
11151
  label: "Utilities",
@@ -10545,7 +11179,12 @@ function rootOptionGroupLabel(flags) {
10545
11179
  }
10546
11180
  return "General";
10547
11181
  }
10548
- var program = new Command();
11182
+ function visibleOptions2(cmd) {
11183
+ return cmd.options.filter((option) => {
11184
+ return !option.hidden;
11185
+ });
11186
+ }
11187
+ var program = new Command2();
10549
11188
  var argvTokens = process.argv.slice(2);
10550
11189
  var isHelpInvocation = argvTokens.some(
10551
11190
  (token) => token === "help" || token === "--help" || token === "-h"
@@ -10561,6 +11200,16 @@ var findCommandByPath = (root, path) => {
10561
11200
  }
10562
11201
  return current;
10563
11202
  };
11203
+ var commandPathIncludes = (command, names) => {
11204
+ let current = command;
11205
+ while (current) {
11206
+ if (names.has(current.name())) {
11207
+ return true;
11208
+ }
11209
+ current = current.parent ?? null;
11210
+ }
11211
+ return false;
11212
+ };
10564
11213
  var cachedAvailableUpdate;
10565
11214
  var updateShownDuringInteractiveStartup = false;
10566
11215
  function getAvailableUpdateOnce() {
@@ -10581,7 +11230,7 @@ async function flushProcessOutput() {
10581
11230
  }
10582
11231
  program.name("alchemy").description(
10583
11232
  "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(
11233
+ ).version("0.16.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
10585
11234
  "-n, --network <network>",
10586
11235
  "Target network for networked commands"
10587
11236
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -10619,7 +11268,7 @@ program.name("alchemy").description(
10619
11268
  required: a.required
10620
11269
  }));
10621
11270
  }
10622
- const opts = cmd.options;
11271
+ const opts = visibleOptions2(cmd);
10623
11272
  if (opts.length > 0) {
10624
11273
  schema.options = opts.map((o) => ({
10625
11274
  flags: o.flags,
@@ -10754,11 +11403,11 @@ ${styledLine}`;
10754
11403
  reveal: Boolean(opts.reveal),
10755
11404
  timeout: opts.timeout
10756
11405
  });
10757
- const cmdName = actionCommand.name();
10758
- const skipAppPrompt = [
11406
+ const skipAppPrompt = /* @__PURE__ */ new Set([
10759
11407
  "auth",
10760
11408
  "config",
10761
11409
  "help",
11410
+ "usage",
10762
11411
  "version",
10763
11412
  "completions",
10764
11413
  "agent-prompt",
@@ -10766,13 +11415,13 @@ ${styledLine}`;
10766
11415
  "install",
10767
11416
  "update-check",
10768
11417
  "wallet"
10769
- ];
10770
- if (!skipAppPrompt.includes(cmdName) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
10771
- const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-IRTGQL4A.js");
11418
+ ]);
11419
+ if (!commandPathIncludes(actionCommand, skipAppPrompt) && isInteractiveAllowed(program) && !opts.apiKey && !process.env.ALCHEMY_API_KEY) {
11420
+ const { resolveAuthToken: resolveAuthToken2 } = await import("./resolve-B64J4LR6.js");
10772
11421
  const authToken = resolveAuthToken2(cfg);
10773
11422
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
10774
11423
  if (authToken && !hasApiKey) {
10775
- const { selectAppAfterAuth } = await import("./auth-ZBEPAFEP.js");
11424
+ const { selectAppAfterAuth } = await import("./auth-JHVDOG26.js");
10776
11425
  console.log("");
10777
11426
  console.log(` No app selected. Please select an app to continue.`);
10778
11427
  await selectAppAfterAuth(authToken);
@@ -10807,7 +11456,7 @@ ${styledLine}`;
10807
11456
  if (isInteractiveAllowed(program)) {
10808
11457
  let latestForInteractiveStartup = null;
10809
11458
  if (shouldRunOnboarding(program, cfg)) {
10810
- const { runOnboarding } = await import("./onboarding-QE43IUEK.js");
11459
+ const { runOnboarding } = await import("./onboarding-47TK4HNM.js");
10811
11460
  const latest = getAvailableUpdateOnce();
10812
11461
  const completed = await runOnboarding(program, latest);
10813
11462
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -10821,7 +11470,7 @@ ${styledLine}`;
10821
11470
  latestForInteractiveStartup
10822
11471
  );
10823
11472
  }
10824
- const { startREPL } = await import("./interactive-XNDXNMAJ.js");
11473
+ const { startREPL } = await import("./interactive-PQGRT27R.js");
10825
11474
  program.exitOverride();
10826
11475
  program.configureOutput({
10827
11476
  writeErr: () => {
@@ -10838,6 +11487,7 @@ registerXchain(program);
10838
11487
  registerWallets(program);
10839
11488
  registerGasManager(program);
10840
11489
  registerApps(program);
11490
+ registerUsage(program);
10841
11491
  registerWebhooks(program);
10842
11492
  registerAuth(program);
10843
11493
  registerConfig(program);
@@ -2,14 +2,14 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  getSetupMethod
5
- } from "./chunk-RQDWIB62.js";
5
+ } from "./chunk-Q2VRERYE.js";
6
6
  import "./chunk-EJB4WDTU.js";
7
7
  import {
8
8
  getRPCNetworkIds
9
- } from "./chunk-CXR7CCJ7.js";
9
+ } from "./chunk-FFQ7XKW7.js";
10
10
  import {
11
11
  getUpdateNoticeLines
12
- } from "./chunk-22KBYYLI.js";
12
+ } from "./chunk-N422J6U3.js";
13
13
  import {
14
14
  bold,
15
15
  brand,
@@ -17,7 +17,7 @@ import {
17
17
  dim,
18
18
  green,
19
19
  setBrandedHelpSuppressed
20
- } from "./chunk-OIRERSZT.js";
20
+ } from "./chunk-GJL52AFY.js";
21
21
  import {
22
22
  configDir,
23
23
  load
@@ -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-N422J6U3.js";
6
6
  import {
7
7
  bold,
8
8
  brand,
@@ -10,7 +10,7 @@ import {
10
10
  dim,
11
11
  green,
12
12
  promptText
13
- } from "./chunk-OIRERSZT.js";
13
+ } from "./chunk-GJL52AFY.js";
14
14
  import {
15
15
  load,
16
16
  save
@@ -51,7 +51,7 @@ async function runOnboarding(_program, latestUpdate = null) {
51
51
  auth_token_expires_at: result.expiresAt
52
52
  });
53
53
  console.log(` ${green("\u2713")} Logged in successfully`);
54
- const { selectAppAfterAuth } = await import("./auth-ZBEPAFEP.js");
54
+ const { selectAppAfterAuth } = await import("./auth-JHVDOG26.js");
55
55
  await selectAppAfterAuth(result.token);
56
56
  return true;
57
57
  } catch (err) {
@@ -5,9 +5,9 @@ import {
5
5
  errNotLoggedInForPolicyLookup,
6
6
  errSponsorshipNeedsPolicy,
7
7
  selectOrCreatePolicy
8
- } from "./chunk-M7HFRKW6.js";
9
- import "./chunk-CXR7CCJ7.js";
10
- import "./chunk-OIRERSZT.js";
8
+ } from "./chunk-HPRJEGSP.js";
9
+ import "./chunk-FFQ7XKW7.js";
10
+ import "./chunk-GJL52AFY.js";
11
11
  import "./chunk-PISUI34T.js";
12
12
  import "./chunk-CFIDLPKB.js";
13
13
  export {
@@ -26,7 +26,7 @@ import {
26
26
  resolveWalletSession,
27
27
  resolveX402,
28
28
  resolveX402Client
29
- } from "./chunk-CXR7CCJ7.js";
29
+ } from "./chunk-FFQ7XKW7.js";
30
30
  import "./chunk-PISUI34T.js";
31
31
  import "./chunk-CFIDLPKB.js";
32
32
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {