@alchemy/cli 0.1.5 → 0.2.1

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
@@ -18,6 +18,63 @@ Or run without installing globally:
18
18
  npx @alchemy/cli <command>
19
19
  ```
20
20
 
21
+ ## Getting Started
22
+
23
+ ### Authentication Quick Start
24
+
25
+ Authentication is required before making requests. Configure auth first, then run commands.
26
+
27
+ If you are using the CLI as a human in an interactive terminal, the easiest path is:
28
+
29
+ ```bash
30
+ alchemy
31
+ ```
32
+
33
+ Then follow the setup flow in the terminal UI to configure auth.
34
+
35
+ Know which auth method does what:
36
+
37
+ - **API key** - direct auth for blockchain queries (`balance`, `tx`, `block`, `nfts`, `tokens`, `rpc`)
38
+ - **Access key** - Admin/API app management; app setup/selection can also provide API key auth for blockchain queries
39
+ - **x402 wallet auth** - wallet-authenticated, pay-per-request model for supported blockchain queries
40
+
41
+ If you use Notify webhooks, add webhook auth on top via `alchemy config set webhook-api-key <key>`, `--webhook-api-key`, or `ALCHEMY_WEBHOOK_API_KEY`.
42
+
43
+ For setup commands, env vars, and resolution order, see [Authentication Reference](#authentication-reference).
44
+
45
+ ### Usage By Workflow
46
+
47
+ After auth is configured, use the CLI differently depending on who is driving it:
48
+
49
+ - **Humans (interactive terminal):** start with `alchemy` and use the terminal UI/setup flow; this is the recommended path for human usage
50
+ - **Agents/scripts (automation):** always use `--json` and prefer non-interactive execution (`--no-interactive`)
51
+
52
+ Quick usage examples:
53
+
54
+ ```bash
55
+ # Human recommended entrypoint
56
+ alchemy
57
+
58
+ # Agent/script-friendly command
59
+ alchemy balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --json --no-interactive
60
+
61
+ # Agent checks whether a newer CLI version is available
62
+ alchemy update-check --json --no-interactive
63
+ ```
64
+
65
+ #### Agent bootstrap
66
+
67
+ Have your agent run `agent-prompt` as its first step to get a complete, machine-readable contract describing every command, auth method, error code, and execution rule:
68
+
69
+ ```bash
70
+ # Agent runs this once to learn everything the CLI can do
71
+ alchemy --json agent-prompt
72
+ ```
73
+
74
+ This returns a single JSON document with execution policy, preflight instructions, auth matrix, the full command tree with all arguments and options, error codes with recovery actions, and example invocations. No external docs required.
75
+
76
+ Agents can also call `alchemy --json --no-interactive update-check` to retrieve the current CLI version, latest known version, and install command for upgrades.
77
+
21
78
  ## Command Reference
22
79
 
23
80
  Run commands as `alchemy <command>`.
@@ -99,10 +156,12 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
99
156
  | `apps origin-allowlist <id>` | Updates app origin allowlist | `alchemy apps origin-allowlist <app-id> --origins https://example.com` |
100
157
  | `apps ip-allowlist <id>` | Updates app IP allowlist | `alchemy apps ip-allowlist <app-id> --ips 1.2.3.4,5.6.7.8` |
101
158
  | `setup status` | Shows setup status + next commands | `alchemy setup status` |
159
+ | `update-check` | Checks whether a newer CLI version is available | `alchemy update-check --json --no-interactive` |
102
160
  | `config set ...` | Sets config values | `alchemy config set api-key <key>` |
103
161
  | `config get <key>` | Gets one config value | `alchemy config get network` |
104
162
  | `config list` | Lists all config values | `alchemy config list` |
105
163
  | `config reset [key]` | Resets one or all config values | `alchemy config reset --yes` |
164
+ | `agent-prompt` | Emits complete agent/automation usage instructions | `alchemy --json agent-prompt` |
106
165
  | `version` | Prints CLI version | `alchemy version` |
107
166
 
108
167
  ## Flags
@@ -127,7 +186,7 @@ These apply to all commands.
127
186
  |---|---|---|
128
187
  | `--json` | — | Force JSON output |
129
188
  | `-q, --quiet` | — | Suppress non-essential output |
130
- | `-v, --verbose` | — | Enable verbose output |
189
+ | `--verbose` | — | Enable verbose output |
131
190
  | `--no-color` | `NO_COLOR` | Disable color output |
132
191
  | `--reveal` | — | Show secrets in plain text (TTY only) |
133
192
 
@@ -176,13 +235,13 @@ Additional env vars:
176
235
  | `network list` | `--configured`, `--app-id <id>` |
177
236
  | `config reset` | `-y, --yes` |
178
237
 
179
- ## Authentication
238
+ ## Authentication Reference
180
239
 
181
240
  The CLI supports three auth inputs:
182
241
 
183
242
  - API key for blockchain queries (`balance`, `tx`, `block`, `nfts`, `tokens`, `rpc`)
184
- - Access key for Admin API operations (`apps`, `chains`, configured network lookups)
185
- - x402 wallet key for wallet-authenticated blockchain queries
243
+ - Access key for Admin API operations (`apps`, `chains`, configured network lookups`) and app setup/selection, which can also supply the API key used by blockchain query commands
244
+ - x402 wallet key for wallet-authenticated blockchain queries in a pay-per-request model
186
245
 
187
246
  Notify/webhook commands use a webhook API key with resolution order:
188
247
  `--webhook-api-key` -> `ALCHEMY_WEBHOOK_API_KEY` -> `ALCHEMY_NOTIFY_AUTH_TOKEN` -> config `webhook-api-key` -> configured app webhook key.
@@ -221,6 +280,9 @@ Resolution order: `--access-key` -> `ALCHEMY_ACCESS_KEY` -> config file.
221
280
 
222
281
  #### x402 wallet auth
223
282
 
283
+ x402 is a wallet-authenticated, pay-per-request usage model for supported blockchain queries.
284
+ The CLI can generate or import the wallet key used for these requests.
285
+
224
286
  ```bash
225
287
  # Generate/import a wallet managed by CLI
226
288
  alchemy wallet generate
@@ -261,6 +323,7 @@ Use `--no-interactive` to disable REPL/prompts in automation.
261
323
 
262
324
  - TTY: formatted human output
263
325
  - Non-TTY: JSON output (script-friendly)
326
+ - `-v`, `--version`: prints the CLI version
264
327
  - `--json`: forces JSON output in any context
265
328
  - `--verbose` or `alchemy config set verbose true`: includes richer payload output on supported commands
266
329
 
@@ -277,116 +340,3 @@ Errors are structured JSON in JSON mode:
277
340
  }
278
341
  }
279
342
  ```
280
-
281
- ## Development
282
-
283
- Prerequisites:
284
-
285
- - [Node.js 22+](https://nodejs.org/)
286
- - [pnpm](https://pnpm.io/)
287
-
288
- ### Local development setup
289
-
290
- ```bash
291
- git clone https://github.com/alchemyplatform/alchemy-cli.git
292
- cd alchemy-cli
293
- pnpm install
294
- pnpm build
295
- pnpm link --global
296
- ```
297
-
298
- This makes the local `alchemy` build available globally for testing.
299
- To unlink later: `pnpm unlink --global`.
300
-
301
- Run during development:
302
-
303
- ```bash
304
- # Run without building
305
- npx tsx src/index.ts balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
306
-
307
- # Build in watch mode
308
- pnpm dev
309
- ```
310
-
311
- Build:
312
-
313
- ```bash
314
- pnpm build
315
- ```
316
-
317
- Test:
318
-
319
- ```bash
320
- pnpm test
321
- pnpm test:e2e
322
- ```
323
-
324
- Type check:
325
-
326
- ```bash
327
- pnpm lint
328
- ```
329
-
330
- Coverage:
331
-
332
- ```bash
333
- pnpm test:coverage
334
- ```
335
-
336
- ### Changesets & Releasing
337
-
338
- This project uses [Changesets](https://github.com/changesets/changesets) for versioning and release notes.
339
-
340
- **When to add a changeset:** Any PR with user-facing changes (new commands, bug fixes, flag changes, output format changes) needs a changeset. Internal changes (CI, refactors with no behavior change, docs) can skip by adding the `no-changeset` label.
341
-
342
- **How to add a changeset:**
343
-
344
- ```bash
345
- pnpm changeset
346
- ```
347
-
348
- You'll be prompted to pick the bump type:
349
- - **patch** — bug fixes, small tweaks (e.g. fixing `--json` output for a command)
350
- - **minor** — new commands, new flags, new capabilities
351
- - **major** — breaking changes (removed commands, changed flag behavior, output format changes)
352
-
353
- This creates a file like `.changeset/cool-dogs-fly.md`:
354
-
355
- ```markdown
356
- ---
357
- "@alchemy/cli": minor
358
- ---
359
-
360
- Add `alchemy portfolio transactions` command for portfolio transaction history.
361
- ```
362
-
363
- Write a 1-2 sentence summary of the change from a user's perspective. Commit this file with your PR.
364
-
365
- **How releases work:** When PRs with changesets merge to `main`, the publish workflow automatically:
366
- 1. Verifies the build (typecheck, build, test)
367
- 2. Applies version bumps and updates `CHANGELOG.md` via `changeset version`
368
- 3. Creates a signed release commit via the GitHub Git Database API (using a GitHub App token)
369
- 4. Publishes to npm using OIDC trusted publishing (no long-lived npm token)
370
- 5. Creates a GitHub release/tag with notes extracted from `CHANGELOG.md`
371
-
372
- If no changesets are pending, the workflow exits cleanly — no release is created.
373
-
374
- **Release infrastructure:**
375
- - Repository write operations use a GitHub App (`APP_ID` variable + `APP_PRIVATE_KEY` secret)
376
- - npm publish uses [trusted publishing](https://docs.npmjs.com/generating-provenance-statements) (OIDC) — no `NPM_TOKEN` secret required
377
- - Required GitHub repo settings: `APP_ID` (variable), `APP_PRIVATE_KEY` (secret)
378
- - Required npm-side: configure trusted publishing for this repo/workflow at npmjs.com package settings
379
-
380
- ### Endpoint Override Env Vars (Local Testing Only)
381
-
382
- These are for local/mock testing, not normal production usage:
383
-
384
- - `ALCHEMY_RPC_BASE_URL`
385
- - `ALCHEMY_ADMIN_API_BASE_URL`
386
- - `ALCHEMY_X402_BASE_URL`
387
-
388
- Safety constraints:
389
-
390
- - Only localhost targets are accepted (`localhost`, `127.0.0.1`, `::1`)
391
- - Non-HTTPS transport is allowed only for localhost
392
- - Production defaults are unchanged when unset
@@ -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-QKXQW4OF.js";
5
+ } from "./chunk-SDUCDSCZ.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;
@@ -1047,6 +1047,109 @@ function isInteractiveAllowed(program) {
1047
1047
  return true;
1048
1048
  }
1049
1049
 
1050
+ // src/lib/update-check.ts
1051
+ import { execFileSync } from "child_process";
1052
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1053
+ import { dirname as dirname2 } from "path";
1054
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
1055
+ var UPDATE_INSTALL_COMMAND = "npm i -g @alchemy/cli";
1056
+ function cachePath() {
1057
+ return configPath().replace(/config\.json$/, ".update-check");
1058
+ }
1059
+ function readCache() {
1060
+ try {
1061
+ return JSON.parse(readFileSync2(cachePath(), "utf-8"));
1062
+ } catch {
1063
+ return null;
1064
+ }
1065
+ }
1066
+ function writeCache(cache) {
1067
+ try {
1068
+ const p = cachePath();
1069
+ mkdirSync2(dirname2(p), { recursive: true });
1070
+ writeFileSync2(p, JSON.stringify(cache), { mode: 384 });
1071
+ } catch {
1072
+ }
1073
+ }
1074
+ function fetchLatestVersion() {
1075
+ try {
1076
+ const result = execFileSync("npm", ["view", "@alchemy/cli", "version"], {
1077
+ encoding: "utf-8",
1078
+ timeout: 5e3,
1079
+ stdio: ["pipe", "pipe", "pipe"]
1080
+ });
1081
+ return result.trim() || null;
1082
+ } catch {
1083
+ return null;
1084
+ }
1085
+ }
1086
+ function semverLT(a, b) {
1087
+ const pa = a.split(".").map(Number);
1088
+ const pb = b.split(".").map(Number);
1089
+ for (let i = 0; i < 3; i++) {
1090
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return true;
1091
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return false;
1092
+ }
1093
+ return false;
1094
+ }
1095
+ function currentVersion() {
1096
+ return true ? "0.2.1" : "0.0.0";
1097
+ }
1098
+ function toUpdateStatus(latestVersion, checkedAt) {
1099
+ const current = currentVersion();
1100
+ return {
1101
+ currentVersion: current,
1102
+ latestVersion,
1103
+ updateAvailable: latestVersion ? semverLT(current, latestVersion) : false,
1104
+ installCommand: UPDATE_INSTALL_COMMAND,
1105
+ checkedAt
1106
+ };
1107
+ }
1108
+ function getUpdateStatus() {
1109
+ const cache = readCache();
1110
+ if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
1111
+ return toUpdateStatus(cache.latest, cache.checkedAt);
1112
+ }
1113
+ const latest = fetchLatestVersion();
1114
+ if (latest) {
1115
+ const checkedAt = Date.now();
1116
+ writeCache({ latest, checkedAt });
1117
+ return toUpdateStatus(latest, checkedAt);
1118
+ }
1119
+ if (cache) {
1120
+ return toUpdateStatus(cache.latest, cache.checkedAt);
1121
+ }
1122
+ return toUpdateStatus(null, null);
1123
+ }
1124
+ function getAvailableUpdate() {
1125
+ const current = currentVersion();
1126
+ const cache = readCache();
1127
+ if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
1128
+ return semverLT(current, cache.latest) ? cache.latest : null;
1129
+ }
1130
+ const latest = fetchLatestVersion();
1131
+ if (latest) {
1132
+ writeCache({ latest, checkedAt: Date.now() });
1133
+ return semverLT(current, latest) ? latest : null;
1134
+ }
1135
+ return null;
1136
+ }
1137
+ function getUpdateNoticeLines(latest) {
1138
+ const yellow2 = esc("33");
1139
+ const bold2 = esc("1");
1140
+ const dim2 = esc("2");
1141
+ return [
1142
+ ` ${yellow2("Update available")} ${dim2(currentVersion())} \u2192 ${bold2(latest)}`,
1143
+ ` Run ${bold2(UPDATE_INSTALL_COMMAND)} to update`
1144
+ ];
1145
+ }
1146
+ function printUpdateNotice(latest) {
1147
+ process.stderr.write(`
1148
+ ${getUpdateNoticeLines(latest).join("\n")}
1149
+
1150
+ `);
1151
+ }
1152
+
1050
1153
  export {
1051
1154
  noColor,
1052
1155
  identity,
@@ -1063,6 +1166,7 @@ export {
1063
1166
  debug,
1064
1167
  formatCommanderError,
1065
1168
  ErrorCode,
1169
+ EXIT_CODES,
1066
1170
  CLIError,
1067
1171
  errAuthRequired,
1068
1172
  errAccessKeyRequired,
@@ -1109,5 +1213,9 @@ export {
1109
1213
  timeAgo,
1110
1214
  etherscanTxURL,
1111
1215
  brandedHelp,
1112
- isInteractiveAllowed
1216
+ isInteractiveAllowed,
1217
+ getUpdateStatus,
1218
+ getAvailableUpdate,
1219
+ getUpdateNoticeLines,
1220
+ printUpdateNotice
1113
1221
  };
@@ -40,7 +40,7 @@ import {
40
40
  toMap,
41
41
  withSpinner,
42
42
  yellow
43
- } from "./chunk-QKXQW4OF.js";
43
+ } from "./chunk-SDUCDSCZ.js";
44
44
 
45
45
  // src/lib/client-utils.ts
46
46
  function isLocalhost(hostname) {
package/dist/index.js CHANGED
@@ -14,14 +14,16 @@ import {
14
14
  splitCommaList,
15
15
  validateAddress,
16
16
  validateTxHash
17
- } from "./chunk-PH4BPYSY.js";
17
+ } from "./chunk-XP5KF4W2.js";
18
18
  import {
19
19
  getRPCNetworks,
20
20
  getSetupStatus,
21
21
  isSetupComplete,
22
22
  shouldRunOnboarding
23
- } from "./chunk-F7KTEZFZ.js";
23
+ } from "./chunk-HBRTTBCY.js";
24
24
  import {
25
+ EXIT_CODES,
26
+ ErrorCode,
25
27
  bold,
26
28
  brand,
27
29
  brandedHelp,
@@ -40,6 +42,8 @@ import {
40
42
  exitWithError,
41
43
  failBadge,
42
44
  formatCommanderError,
45
+ getAvailableUpdate,
46
+ getUpdateStatus,
43
47
  green,
44
48
  identity,
45
49
  isInteractiveAllowed,
@@ -52,6 +56,7 @@ import {
52
56
  printKeyValueBox,
53
57
  printSyntaxJSON,
54
58
  printTable,
59
+ printUpdateNotice,
55
60
  promptSelect,
56
61
  quiet,
57
62
  setFlags,
@@ -60,7 +65,7 @@ import {
60
65
  verbose,
61
66
  weiToEth,
62
67
  withSpinner
63
- } from "./chunk-QKXQW4OF.js";
68
+ } from "./chunk-SDUCDSCZ.js";
64
69
 
65
70
  // src/index.ts
66
71
  import { Command, Help } from "commander";
@@ -1609,6 +1614,218 @@ function registerSolana(program2) {
1609
1614
  });
1610
1615
  }
1611
1616
 
1617
+ // src/commands/agent-prompt.ts
1618
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([
1619
+ ErrorCode.RATE_LIMITED,
1620
+ ErrorCode.NETWORK_ERROR
1621
+ ]);
1622
+ var ERROR_RECOVERY = {
1623
+ AUTH_REQUIRED: "Set ALCHEMY_API_KEY env var or run: alchemy config set api-key <key>",
1624
+ INVALID_API_KEY: "Check your API key and set a valid one: alchemy config set api-key <key>",
1625
+ NETWORK_NOT_ENABLED: "Enable the target network for your app at dashboard.alchemy.com",
1626
+ INVALID_ACCESS_KEY: "Check your access key: https://dashboard.alchemy.com/",
1627
+ ACCESS_KEY_REQUIRED: "Set ALCHEMY_ACCESS_KEY env var or run: alchemy config set access-key <key>",
1628
+ APP_REQUIRED: "Select an app: alchemy config set app <app-id>",
1629
+ ADMIN_API_ERROR: "Check the error message for details; verify access key permissions",
1630
+ NETWORK_ERROR: "Check internet connection and retry",
1631
+ RPC_ERROR: "Check RPC method, params, and network; verify API key has access",
1632
+ INVALID_ARGS: "Check command usage via: alchemy --json help <command>",
1633
+ NOT_FOUND: "Verify the resource identifier (address, hash, id) is correct",
1634
+ RATE_LIMITED: "Wait and retry; consider upgrading your Alchemy plan",
1635
+ PAYMENT_REQUIRED: "Fund your x402 wallet or switch to API key auth",
1636
+ SETUP_REQUIRED: "Run preflight: alchemy --json setup status, then follow nextCommands",
1637
+ INTERNAL_ERROR: "Unexpected error; retry or report a bug"
1638
+ };
1639
+ function buildCommandSchema(cmd) {
1640
+ const schema = {
1641
+ name: cmd.name(),
1642
+ description: cmd.description()
1643
+ };
1644
+ const aliases = cmd.aliases();
1645
+ if (aliases.length > 0) {
1646
+ schema.aliases = aliases;
1647
+ }
1648
+ const args = cmd.registeredArguments;
1649
+ if (args.length > 0) {
1650
+ schema.arguments = args.map((a) => ({
1651
+ name: a.name(),
1652
+ description: a.description,
1653
+ required: a.required
1654
+ }));
1655
+ }
1656
+ const opts = cmd.options;
1657
+ if (opts.length > 0) {
1658
+ schema.options = opts.map((o) => ({
1659
+ flags: o.flags,
1660
+ description: o.description
1661
+ }));
1662
+ }
1663
+ const subs = cmd.commands;
1664
+ if (subs.length > 0) {
1665
+ schema.subcommands = subs.map(buildCommandSchema);
1666
+ }
1667
+ return schema;
1668
+ }
1669
+ function buildAgentPrompt(program2) {
1670
+ const errors = {};
1671
+ for (const [code, exitCode] of Object.entries(EXIT_CODES)) {
1672
+ errors[code] = {
1673
+ exitCode,
1674
+ retryable: RETRYABLE_CODES.has(code),
1675
+ recovery: ERROR_RECOVERY[code] ?? "Check error message"
1676
+ };
1677
+ }
1678
+ const commands = program2.commands.filter((cmd) => cmd.name() !== "agent-prompt").map(buildCommandSchema);
1679
+ return {
1680
+ executionPolicy: [
1681
+ "Always pass --json --no-interactive",
1682
+ "Parse stdout as JSON on exit code 0",
1683
+ "Parse stderr as JSON on nonzero exit code",
1684
+ "Never run bare 'alchemy' without --json --no-interactive",
1685
+ "Run alchemy --json --no-interactive update-check when you need to detect available CLI upgrades"
1686
+ ],
1687
+ preflight: {
1688
+ command: "alchemy --json setup status",
1689
+ description: "Check auth readiness before first command. If complete is false, follow nextCommands in the response to configure auth."
1690
+ },
1691
+ auth: [
1692
+ {
1693
+ method: "API key",
1694
+ envVar: "ALCHEMY_API_KEY",
1695
+ flag: "--api-key <key>",
1696
+ configKey: "api-key",
1697
+ commandFamilies: [
1698
+ "balance",
1699
+ "tx",
1700
+ "block",
1701
+ "rpc",
1702
+ "trace",
1703
+ "debug",
1704
+ "tokens",
1705
+ "nfts",
1706
+ "transfers",
1707
+ "prices",
1708
+ "portfolio",
1709
+ "simulate",
1710
+ "solana"
1711
+ ]
1712
+ },
1713
+ {
1714
+ method: "Access key",
1715
+ envVar: "ALCHEMY_ACCESS_KEY",
1716
+ flag: "--access-key <key>",
1717
+ configKey: "access-key",
1718
+ commandFamilies: ["apps", "chains", "network list --configured"]
1719
+ },
1720
+ {
1721
+ method: "Webhook API key",
1722
+ envVar: "ALCHEMY_WEBHOOK_API_KEY",
1723
+ flag: "--webhook-api-key <key>",
1724
+ configKey: "webhook-api-key",
1725
+ commandFamilies: ["webhooks"]
1726
+ },
1727
+ {
1728
+ method: "x402 wallet",
1729
+ envVar: "ALCHEMY_WALLET_KEY",
1730
+ flag: "--x402 --wallet-key-file <path>",
1731
+ configKey: "x402",
1732
+ commandFamilies: [
1733
+ "balance",
1734
+ "tx",
1735
+ "block",
1736
+ "rpc",
1737
+ "trace",
1738
+ "debug",
1739
+ "tokens",
1740
+ "nfts",
1741
+ "transfers"
1742
+ ]
1743
+ }
1744
+ ],
1745
+ commands,
1746
+ errors,
1747
+ examples: [
1748
+ "alchemy --json --no-interactive setup status",
1749
+ "alchemy --json --no-interactive update-check",
1750
+ "alchemy --json --no-interactive balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --api-key $ALCHEMY_API_KEY",
1751
+ "alchemy --json --no-interactive apps list --access-key $ALCHEMY_ACCESS_KEY",
1752
+ "alchemy --json --no-interactive rpc eth_blockNumber --api-key $ALCHEMY_API_KEY",
1753
+ "alchemy --json --no-interactive network list"
1754
+ ],
1755
+ docs: "https://www.alchemy.com/docs"
1756
+ };
1757
+ }
1758
+ function formatAsSystemPrompt(payload) {
1759
+ const lines = [];
1760
+ lines.push("Alchemy CLI agent instructions");
1761
+ lines.push("==============================");
1762
+ lines.push("");
1763
+ lines.push("Execution policy:");
1764
+ for (const rule of payload.executionPolicy) {
1765
+ lines.push(` - ${rule}`);
1766
+ }
1767
+ lines.push("");
1768
+ lines.push("Preflight:");
1769
+ lines.push(` Command: ${payload.preflight.command}`);
1770
+ lines.push(` ${payload.preflight.description}`);
1771
+ lines.push("");
1772
+ lines.push("Auth methods:");
1773
+ for (const auth of payload.auth) {
1774
+ lines.push(` ${auth.method}:`);
1775
+ lines.push(` env: ${auth.envVar}`);
1776
+ lines.push(` flag: ${auth.flag}`);
1777
+ lines.push(` config: alchemy config set ${auth.configKey} <value>`);
1778
+ lines.push(` commands: ${auth.commandFamilies.join(", ")}`);
1779
+ }
1780
+ lines.push("");
1781
+ lines.push("Error codes:");
1782
+ for (const [code, entry] of Object.entries(payload.errors)) {
1783
+ const retry = entry.retryable ? " [retryable]" : "";
1784
+ lines.push(` ${code} (exit ${entry.exitCode})${retry}: ${entry.recovery}`);
1785
+ }
1786
+ lines.push("");
1787
+ lines.push("Examples:");
1788
+ for (const example of payload.examples) {
1789
+ lines.push(` ${example}`);
1790
+ }
1791
+ lines.push("");
1792
+ lines.push(`Docs: ${payload.docs}`);
1793
+ lines.push(" For RPC method signatures, parameters, and supported networks.");
1794
+ lines.push("");
1795
+ lines.push(
1796
+ "For full command tree, run: alchemy --json agent-prompt"
1797
+ );
1798
+ lines.push("");
1799
+ return lines.join("\n");
1800
+ }
1801
+ function registerAgentPrompt(program2) {
1802
+ program2.command("agent-prompt").description("Emit complete agent/automation usage instructions").action(() => {
1803
+ const payload = buildAgentPrompt(program2);
1804
+ printHuman(formatAsSystemPrompt(payload), payload);
1805
+ });
1806
+ }
1807
+
1808
+ // src/commands/update-check.ts
1809
+ function formatCheckedAt(checkedAt) {
1810
+ return checkedAt ? new Date(checkedAt).toISOString() : dim("(unknown)");
1811
+ }
1812
+ function registerUpdateCheck(program2) {
1813
+ program2.command("update-check").description("Check whether a newer CLI version is available").action(() => {
1814
+ const status = getUpdateStatus();
1815
+ if (isJSONMode()) {
1816
+ printJSON(status);
1817
+ return;
1818
+ }
1819
+ printKeyValueBox([
1820
+ ["Current version", status.currentVersion],
1821
+ ["Latest version", status.latestVersion ?? dim("(unknown)")],
1822
+ ["Update available", status.updateAvailable ? "yes" : "no"],
1823
+ ["Checked at", formatCheckedAt(status.checkedAt)],
1824
+ ["Install", status.installCommand]
1825
+ ]);
1826
+ });
1827
+ }
1828
+
1612
1829
  // src/index.ts
1613
1830
  var hBrand = noColor ? identity : (s) => `\x1B[38;2;54;63;249m${s}\x1B[39m`;
1614
1831
  var hBold = esc("1");
@@ -1646,7 +1863,7 @@ var ROOT_COMMAND_PILLARS = [
1646
1863
  },
1647
1864
  {
1648
1865
  label: "Admin",
1649
- commands: ["apps", "config", "setup", "version", "help"]
1866
+ commands: ["apps", "config", "setup", "agent-prompt", "update-check", "version", "help"]
1650
1867
  }
1651
1868
  ];
1652
1869
  function formatCommandSignature(sub) {
@@ -1681,12 +1898,29 @@ var findCommandByPath = (root, path) => {
1681
1898
  }
1682
1899
  return current;
1683
1900
  };
1901
+ var cachedAvailableUpdate;
1902
+ var updateShownDuringInteractiveStartup = false;
1903
+ function getAvailableUpdateOnce() {
1904
+ if (cachedAvailableUpdate === void 0) {
1905
+ cachedAvailableUpdate = getAvailableUpdate();
1906
+ }
1907
+ return cachedAvailableUpdate;
1908
+ }
1909
+ function resetUpdateNoticeState() {
1910
+ cachedAvailableUpdate = void 0;
1911
+ updateShownDuringInteractiveStartup = false;
1912
+ }
1684
1913
  program.name("alchemy").description(
1685
1914
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
1686
- ).version("0.1.5").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option("--access-key <key>", "Alchemy access key (env: ALCHEMY_ACCESS_KEY)").option(
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(
1687
1916
  "-n, --network <network>",
1688
1917
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
1689
- ).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("-v, --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).configureOutput({
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) => {
1919
+ if (err.code === "commander.help" || err.code === "commander.helpDisplayed" || err.code === "commander.version") {
1920
+ process.exit(0);
1921
+ }
1922
+ process.exit(EXIT_CODES.INVALID_ARGS);
1923
+ }).configureOutput({
1690
1924
  outputError(str, write) {
1691
1925
  write(formatCommanderError(str));
1692
1926
  }
@@ -1827,27 +2061,39 @@ ${styledLine}`;
1827
2061
  }).hook("postAction", () => {
1828
2062
  if (!isJSONMode() && !quiet) {
1829
2063
  console.log("");
2064
+ if (!updateShownDuringInteractiveStartup) {
2065
+ const latest = getAvailableUpdateOnce();
2066
+ if (latest) printUpdateNotice(latest);
2067
+ }
1830
2068
  }
2069
+ resetUpdateNoticeState();
1831
2070
  }).action(async () => {
1832
2071
  const cfg = load();
1833
2072
  if (!isSetupComplete(cfg) && !isInteractiveAllowed(program)) {
1834
2073
  throw errSetupRequired(getSetupStatus(cfg));
1835
2074
  }
1836
2075
  if (isInteractiveAllowed(program)) {
2076
+ let latestForInteractiveStartup = null;
1837
2077
  if (shouldRunOnboarding(program, cfg)) {
1838
- const { runOnboarding } = await import("./onboarding-3J4EXZMG.js");
1839
- const completed = await runOnboarding(program);
2078
+ const { runOnboarding } = await import("./onboarding-YA32OJOT.js");
2079
+ const latest = getAvailableUpdateOnce();
2080
+ const completed = await runOnboarding(program, latest);
2081
+ updateShownDuringInteractiveStartup = Boolean(latest);
2082
+ latestForInteractiveStartup = null;
1840
2083
  if (!completed) {
1841
2084
  return;
1842
2085
  }
2086
+ } else {
2087
+ latestForInteractiveStartup = getAvailableUpdateOnce();
2088
+ updateShownDuringInteractiveStartup = Boolean(latestForInteractiveStartup);
1843
2089
  }
1844
- const { startREPL } = await import("./interactive-2ITFWH3B.js");
2090
+ const { startREPL } = await import("./interactive-L7N4HI7K.js");
1845
2091
  program.exitOverride();
1846
2092
  program.configureOutput({
1847
2093
  writeErr: () => {
1848
2094
  }
1849
2095
  });
1850
- await startREPL(program);
2096
+ await startREPL(program, latestForInteractiveStartup);
1851
2097
  return;
1852
2098
  }
1853
2099
  program.help();
@@ -1874,6 +2120,8 @@ registerApps(program);
1874
2120
  registerSetup(program);
1875
2121
  registerConfig(program);
1876
2122
  registerSolana(program);
2123
+ registerAgentPrompt(program);
2124
+ registerUpdateCheck(program);
1877
2125
  registerVersion(program);
1878
2126
  program.command("help [command...]").description("display help for command").action((commandPath) => {
1879
2127
  if (!commandPath || commandPath.length === 0) {
@@ -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-F7KTEZFZ.js";
6
+ } from "./chunk-HBRTTBCY.js";
7
7
  import {
8
8
  bgRgb,
9
9
  bold,
@@ -11,6 +11,7 @@ import {
11
11
  brandedHelp,
12
12
  configDir,
13
13
  dim,
14
+ getUpdateNoticeLines,
14
15
  green,
15
16
  isJSONMode,
16
17
  load,
@@ -18,7 +19,7 @@ import {
18
19
  rgb,
19
20
  setBrandedHelpSuppressed,
20
21
  setReplMode
21
- } from "./chunk-QKXQW4OF.js";
22
+ } from "./chunk-SDUCDSCZ.js";
22
23
 
23
24
  // src/commands/interactive.ts
24
25
  import * as readline from "readline";
@@ -88,6 +89,7 @@ var COMMAND_NAMES = [
88
89
  "config get",
89
90
  "config list",
90
91
  "help",
92
+ "update-check",
91
93
  "network",
92
94
  "network list",
93
95
  "nfts",
@@ -100,6 +102,7 @@ var COMMAND_NAMES = [
100
102
  "tokens metadata",
101
103
  "tokens allowance",
102
104
  "tx",
105
+ "agent-prompt",
103
106
  "version",
104
107
  "wallet",
105
108
  "wallet generate",
@@ -135,7 +138,7 @@ function saveReplHistory(lines) {
135
138
  mkdirSync(dirname(historyFilePath), { recursive: true, mode: 493 });
136
139
  writeFileSync(historyFilePath, normalized.join("\n") + "\n", { mode: 384 });
137
140
  }
138
- async function startREPL(program) {
141
+ async function startREPL(program, latestUpdate = null) {
139
142
  if (!stdin.isTTY) return;
140
143
  setReplMode(true);
141
144
  setBrandedHelpSuppressed(true);
@@ -216,6 +219,12 @@ async function startREPL(program) {
216
219
  console.log(` ${green("\u2713")} ${dim(`Configured auth: ${formatSetupMethodLabel()}`)}`);
217
220
  console.log(` ${dim("Run commands directly (no 'alchemy' prefix).")}`);
218
221
  console.log("");
222
+ if (latestUpdate) {
223
+ for (const line of getUpdateNoticeLines(latestUpdate)) {
224
+ console.log(line);
225
+ }
226
+ console.log("");
227
+ }
219
228
  console.log(` ${brand("\u25C6")} ${bold("Quick commands")}`);
220
229
  console.log(` ${dim("- rpc eth_chainId")}`);
221
230
  console.log(` ${dim("- config list")}`);
@@ -5,12 +5,13 @@ import {
5
5
  generateAndPersistWallet,
6
6
  importAndPersistWallet,
7
7
  selectOrCreateApp
8
- } from "./chunk-PH4BPYSY.js";
8
+ } from "./chunk-XP5KF4W2.js";
9
9
  import {
10
10
  bold,
11
11
  brand,
12
12
  brandedHelp,
13
13
  dim,
14
+ getUpdateNoticeLines,
14
15
  green,
15
16
  load,
16
17
  maskIf,
@@ -18,7 +19,7 @@ import {
18
19
  promptSelect,
19
20
  promptText,
20
21
  save
21
- } from "./chunk-QKXQW4OF.js";
22
+ } from "./chunk-SDUCDSCZ.js";
22
23
 
23
24
  // src/commands/onboarding.ts
24
25
  function printNextSteps(method) {
@@ -106,7 +107,7 @@ async function runX402Onboarding() {
106
107
  save({ ...cfg, x402: true });
107
108
  console.log(` ${green("\u2713")} x402 enabled with wallet ${wallet.address}`);
108
109
  }
109
- async function runOnboarding(_program) {
110
+ async function runOnboarding(_program, latestUpdate = null) {
110
111
  process.stdout.write(brandedHelp({ force: true }));
111
112
  console.log("");
112
113
  console.log(` ${brand("\u25C6")} ${bold("Welcome to Alchemy CLI")}`);
@@ -115,6 +116,12 @@ async function runOnboarding(_program) {
115
116
  console.log(` ${dim(" Choose one auth path to continue.")}`);
116
117
  console.log(` ${dim(" Tip: select 'exit' to skip setup for now.")}`);
117
118
  console.log("");
119
+ if (latestUpdate) {
120
+ for (const line of getUpdateNoticeLines(latestUpdate)) {
121
+ console.log(line);
122
+ }
123
+ console.log("");
124
+ }
118
125
  const method = await promptSelect({
119
126
  message: "Choose an auth setup path",
120
127
  options: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.1.5",
3
+ "version": "0.2.1",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {