@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 +67 -117
- package/dist/{chunk-F7KTEZFZ.js → chunk-HBRTTBCY.js} +1 -1
- package/dist/{chunk-QKXQW4OF.js → chunk-SDUCDSCZ.js} +109 -1
- package/dist/{chunk-PH4BPYSY.js → chunk-XP5KF4W2.js} +1 -1
- package/dist/index.js +258 -10
- package/dist/{interactive-2ITFWH3B.js → interactive-L7N4HI7K.js} +12 -3
- package/dist/{onboarding-3J4EXZMG.js → onboarding-YA32OJOT.js} +10 -3
- package/package.json +1 -1
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
|
-
|
|
|
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-
|
|
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
|
};
|
package/dist/index.js
CHANGED
|
@@ -14,14 +14,16 @@ import {
|
|
|
14
14
|
splitCommaList,
|
|
15
15
|
validateAddress,
|
|
16
16
|
validateTxHash
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XP5KF4W2.js";
|
|
18
18
|
import {
|
|
19
19
|
getRPCNetworks,
|
|
20
20
|
getSetupStatus,
|
|
21
21
|
isSetupComplete,
|
|
22
22
|
shouldRunOnboarding
|
|
23
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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("
|
|
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-
|
|
1839
|
-
const
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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: [
|