@alchemy/cli 0.7.0 → 0.7.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
@@ -82,7 +82,7 @@ Have your agent run `agent-prompt` as its first step to get a complete, machine-
82
82
  alchemy --json --no-interactive agent-prompt
83
83
  ```
84
84
 
85
- 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.
85
+ This returns a single JSON document with execution policy, runtime discovery instructions, 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.
86
86
 
87
87
  Agents can also call `alchemy --json --no-interactive update-check` to retrieve the current CLI version, latest known version, and install command for upgrades.
88
88
 
@@ -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-I3X4G2UY.js";
6
+ } from "./chunk-SYP6KKP6.js";
7
7
  import "./chunk-HSKKIATB.js";
8
8
  import "./chunk-HYCRHNPX.js";
9
9
  import "./chunk-TOEVZMIP.js";
10
- import "./chunk-A6L3WCJN.js";
10
+ import "./chunk-5IFXLC2S.js";
11
11
  import "./chunk-7WD3YLRK.js";
12
12
  import "./chunk-QEDAULQ2.js";
13
13
  export {
@@ -46,6 +46,53 @@ function suspendStdinKeypressListeners() {
46
46
  }
47
47
  };
48
48
  }
49
+ function resetStdinKeypressDecoder() {
50
+ const input = stdin;
51
+ for (const symbol of Object.getOwnPropertySymbols(input)) {
52
+ const name = String(symbol);
53
+ if (name === "Symbol(keypress-decoder)" || name === "Symbol(escape-decoder)") {
54
+ delete input[symbol];
55
+ }
56
+ }
57
+ }
58
+ function isReadlineKeypressDataListener(listener) {
59
+ return typeof listener === "function" && listener.name === "onData";
60
+ }
61
+ function prepareStdinKeypressEvents() {
62
+ const dataListeners = stdin.listeners("data");
63
+ const keypressListeners = stdin.listeners("keypress");
64
+ for (const listener of dataListeners) {
65
+ stdin.removeListener("data", listener);
66
+ }
67
+ for (const listener of keypressListeners) {
68
+ stdin.removeListener("keypress", listener);
69
+ }
70
+ resetStdinKeypressDecoder();
71
+ readline.emitKeypressEvents(stdin);
72
+ return () => {
73
+ for (const listener of stdin.listeners("data")) {
74
+ stdin.removeListener("data", listener);
75
+ }
76
+ resetStdinKeypressDecoder();
77
+ for (const listener of dataListeners) {
78
+ if (!isReadlineKeypressDataListener(listener)) {
79
+ stdin.on("data", listener);
80
+ }
81
+ }
82
+ for (const listener of keypressListeners) {
83
+ stdin.on("keypress", listener);
84
+ }
85
+ if (keypressListeners.length > 0) {
86
+ readline.emitKeypressEvents(stdin);
87
+ }
88
+ };
89
+ }
90
+ function requestPromptInterrupt() {
91
+ const handled = process.emit("SIGINT", "SIGINT");
92
+ if (!handled) {
93
+ process.exit(130);
94
+ }
95
+ }
49
96
  async function runListPrompt(opts) {
50
97
  if (!stdin.isTTY || !stdout.isTTY) {
51
98
  if (opts.allowMultiple) {
@@ -54,8 +101,7 @@ async function runListPrompt(opts) {
54
101
  const initial = opts.initialValue ?? opts.options.find((o) => !o.disabled)?.value ?? null;
55
102
  return { value: initial, cancelled: false };
56
103
  }
57
- readline.emitKeypressEvents(stdin);
58
- const restoreKeypressListeners = suspendStdinKeypressListeners();
104
+ const restoreKeypressEvents = prepareStdinKeypressEvents();
59
105
  const previousRawMode = stdin.isRaw;
60
106
  stdin.resume();
61
107
  stdin.setRawMode(true);
@@ -131,7 +177,7 @@ async function runListPrompt(opts) {
131
177
  if (renderedLines > 0) clearRenderedLines(renderedLines);
132
178
  stdin.setRawMode(previousRawMode);
133
179
  stdin.removeListener("keypress", onKeypress);
134
- restoreKeypressListeners();
180
+ restoreKeypressEvents();
135
181
  if (!previousRawMode) {
136
182
  stdin.pause();
137
183
  }
@@ -144,7 +190,13 @@ async function runListPrompt(opts) {
144
190
  const onKeypress = (str, key) => {
145
191
  const filtered = getFiltered();
146
192
  const current = filtered[cursor];
147
- if (key.name === "escape" || key.ctrl && key.name === "c") {
193
+ if (key.ctrl && key.name === "c") {
194
+ cleanup();
195
+ requestPromptInterrupt();
196
+ resolver({ value: null, cancelled: true });
197
+ return;
198
+ }
199
+ if (key.name === "escape") {
148
200
  cleanup();
149
201
  resolver({ value: null, cancelled: true });
150
202
  return;
@@ -222,8 +274,10 @@ async function promptText(opts) {
222
274
  const previousRawMode = stdin.isRaw;
223
275
  if (previousRawMode) stdin.setRawMode(false);
224
276
  const ABORTED = /* @__PURE__ */ Symbol("aborted");
277
+ const INTERRUPTED = /* @__PURE__ */ Symbol("interrupted");
278
+ let removeAbortListener;
225
279
  const value = await new Promise((resolve) => {
226
- rl.on("SIGINT", () => resolve(null));
280
+ rl.on("SIGINT", () => resolve(INTERRUPTED));
227
281
  rl.question(question, (answer) => resolve(answer));
228
282
  if (opts.abortSignal) {
229
283
  const onAbort = () => resolve(ABORTED);
@@ -231,18 +285,24 @@ async function promptText(opts) {
231
285
  onAbort();
232
286
  } else {
233
287
  opts.abortSignal.addEventListener("abort", onAbort, { once: true });
288
+ removeAbortListener = () => opts.abortSignal?.removeEventListener("abort", onAbort);
234
289
  }
235
290
  }
236
291
  });
237
292
  rl.close();
293
+ removeAbortListener?.();
238
294
  restoreKeypressListeners();
239
- if (previousRawMode) stdin.setRawMode(true);
295
+ if (previousRawMode) {
296
+ stdin.setRawMode(true);
297
+ } else {
298
+ stdin.pause();
299
+ }
240
300
  if (opts.clearAfterSubmit || value === ABORTED) {
241
301
  clearRenderedLines(2);
242
302
  }
243
303
  if (value === ABORTED) return "";
244
- if (value === null) {
245
- printCancel(opts.cancelMessage);
304
+ if (value === INTERRUPTED) {
305
+ requestPromptInterrupt();
246
306
  return null;
247
307
  }
248
308
  if (!value.trim() && opts.defaultValue !== void 0) return opts.defaultValue;
@@ -53,7 +53,7 @@ function semverLT(a, b) {
53
53
  return false;
54
54
  }
55
55
  function currentVersion() {
56
- return true ? "0.7.0" : "0.0.0";
56
+ return true ? "0.7.1" : "0.0.0";
57
57
  }
58
58
  function toUpdateStatus(latestVersion, checkedAt) {
59
59
  const current = currentVersion();
@@ -20,7 +20,7 @@ import {
20
20
  promptAutocomplete,
21
21
  promptText,
22
22
  withSpinner
23
- } from "./chunk-A6L3WCJN.js";
23
+ } from "./chunk-5IFXLC2S.js";
24
24
  import {
25
25
  configPath,
26
26
  load,
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
4
  registerAuth
5
- } from "./chunk-I3X4G2UY.js";
5
+ } from "./chunk-SYP6KKP6.js";
6
6
  import {
7
7
  openBrowser
8
8
  } from "./chunk-HSKKIATB.js";
@@ -46,7 +46,7 @@ import {
46
46
  getAvailableUpdate,
47
47
  getUpdateStatus,
48
48
  printUpdateNotice
49
- } from "./chunk-WWWVMK3J.js";
49
+ } from "./chunk-RS3DSL3X.js";
50
50
  import {
51
51
  bold,
52
52
  brand,
@@ -70,7 +70,7 @@ import {
70
70
  weiToEth,
71
71
  withSpinner,
72
72
  yellow
73
- } from "./chunk-A6L3WCJN.js";
73
+ } from "./chunk-5IFXLC2S.js";
74
74
  import {
75
75
  KEY_MAP,
76
76
  configDir,
@@ -283,7 +283,6 @@ function validateTxHash(hash) {
283
283
 
284
284
  // src/commands/config.ts
285
285
  var RESET_KEY_MAP = { ...KEY_MAP, app: "app" };
286
- var APP_SEARCH_THRESHOLD = 15;
287
286
  async function saveAppWithPrompt(app) {
288
287
  const cfg = load();
289
288
  const updated = {
@@ -331,17 +330,12 @@ async function selectOrCreateApp(admin) {
331
330
  })),
332
331
  { label: "Create a new app", value: CREATE_NEW }
333
332
  ];
334
- const selected = apps.length > APP_SEARCH_THRESHOLD ? await promptAutocomplete({
333
+ const selected = await promptAutocomplete({
335
334
  message: "Select default app",
336
335
  placeholder: "Type app name or id",
337
336
  options,
338
337
  cancelMessage: "Cancelled app selection.",
339
338
  commitLabel: null
340
- }) : await promptSelect({
341
- message: "Select default app",
342
- options,
343
- cancelMessage: "Cancelled app selection.",
344
- commitLabel: null
345
339
  });
346
340
  if (selected === null) {
347
341
  return;
@@ -1185,8 +1179,9 @@ function registerApps(program2) {
1185
1179
  printJSON({ apps: result.apps.map(maskAppSecrets) });
1186
1180
  return;
1187
1181
  }
1188
- const appId = await promptSelect({
1182
+ const appId = await promptAutocomplete({
1189
1183
  message: "Select an app",
1184
+ placeholder: "Type to search by name or id",
1190
1185
  options: result.apps.map((app) => ({
1191
1186
  value: app.id,
1192
1187
  label: app.name,
@@ -4694,12 +4689,39 @@ function buildAgentPrompt(program2) {
4694
4689
  "Parse stdout as JSON on exit code 0",
4695
4690
  "Parse stderr as JSON on nonzero exit code",
4696
4691
  "Never run bare 'alchemy' without --json --no-interactive",
4692
+ "Before answering Alchemy CLI capability or usage questions, inspect the live command contract from agent-prompt and trust it over model memory",
4693
+ "For general capability and usage questions, prefer npx -y @alchemy/cli@latest --json --no-interactive agent-prompt so stale local installs or PATH shims do not hide new commands",
4694
+ "Use the user's installed alchemy binary only when executing commands against their local config or diagnosing their installed version",
4695
+ "If installed alchemy help contradicts latest npm help, compare alchemy --json --no-interactive version with npx -y @alchemy/cli@latest --json --no-interactive version and check command -v alchemy",
4697
4696
  "Run alchemy --json --no-interactive update-check when you need to detect available CLI upgrades"
4698
4697
  ],
4699
4698
  preflight: {
4700
4699
  command: "alchemy --json --no-interactive config status",
4701
4700
  description: "Check auth readiness before first command. If complete is false, follow nextCommands in the response to configure auth."
4702
4701
  },
4702
+ runtimeDiscovery: {
4703
+ installed: {
4704
+ command: "alchemy --json --no-interactive agent-prompt",
4705
+ description: "Use for the user's currently installed CLI only when executing against local config or diagnosing local install behavior."
4706
+ },
4707
+ latest: {
4708
+ command: "npx -y @alchemy/cli@latest --json --no-interactive agent-prompt",
4709
+ description: "Default source of truth for general capability and usage questions; avoids stale local installs and PATH shims."
4710
+ },
4711
+ commandHelp: {
4712
+ commandTemplate: "npx -y @alchemy/cli@latest --no-interactive help <command...>",
4713
+ description: "Use for command-specific help from the latest published CLI when constructing an invocation."
4714
+ },
4715
+ versionCheck: {
4716
+ installedCommand: "alchemy --json --no-interactive version",
4717
+ latestCommand: "npx -y @alchemy/cli@latest --json --no-interactive version",
4718
+ description: "Compare when local help or agent-prompt output appears stale after an install or update."
4719
+ },
4720
+ pathCheck: {
4721
+ command: "command -v alchemy && alchemy version",
4722
+ description: "Use when installed CLI behavior contradicts the latest contract or an update appears not to take effect."
4723
+ }
4724
+ },
4703
4725
  auth: [
4704
4726
  {
4705
4727
  method: "API key",
@@ -4809,6 +4831,19 @@ function formatAsSystemPrompt(payload) {
4809
4831
  lines.push(` Command: ${payload.preflight.command}`);
4810
4832
  lines.push(` ${payload.preflight.description}`);
4811
4833
  lines.push("");
4834
+ lines.push("Runtime discovery:");
4835
+ lines.push(` Installed CLI: ${payload.runtimeDiscovery.installed.command}`);
4836
+ lines.push(` ${payload.runtimeDiscovery.installed.description}`);
4837
+ lines.push(` Latest npm CLI: ${payload.runtimeDiscovery.latest.command}`);
4838
+ lines.push(` ${payload.runtimeDiscovery.latest.description}`);
4839
+ lines.push(` Command help: ${payload.runtimeDiscovery.commandHelp.commandTemplate}`);
4840
+ lines.push(` ${payload.runtimeDiscovery.commandHelp.description}`);
4841
+ lines.push(` Installed version: ${payload.runtimeDiscovery.versionCheck.installedCommand}`);
4842
+ lines.push(` Latest npm version: ${payload.runtimeDiscovery.versionCheck.latestCommand}`);
4843
+ lines.push(` ${payload.runtimeDiscovery.versionCheck.description}`);
4844
+ lines.push(` Path check: ${payload.runtimeDiscovery.pathCheck.command}`);
4845
+ lines.push(` ${payload.runtimeDiscovery.pathCheck.description}`);
4846
+ lines.push("");
4812
4847
  lines.push("Auth methods:");
4813
4848
  for (const auth of payload.auth) {
4814
4849
  lines.push(` ${auth.method}:`);
@@ -7953,7 +7988,7 @@ function resetUpdateNoticeState() {
7953
7988
  }
7954
7989
  program.name("alchemy").description(
7955
7990
  "The Alchemy CLI lets you query blockchain data, call JSON-RPC methods, and manage your Alchemy configuration."
7956
- ).version("0.7.0", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
7991
+ ).version("0.7.1", "-v, --version", "display CLI version").option("--api-key <key>", "Alchemy API key (env: ALCHEMY_API_KEY)").option(
7957
7992
  "-n, --network <network>",
7958
7993
  "Target network (default: eth-mainnet) (env: ALCHEMY_NETWORK)"
7959
7994
  ).option("--x402", "Use x402 wallet-based gateway auth").option(
@@ -8144,7 +8179,7 @@ ${styledLine}`;
8144
8179
  const authToken = resolveAuthToken2(cfg);
8145
8180
  const hasApiKey = Boolean(cfg.api_key?.trim() || cfg.app?.apiKey);
8146
8181
  if (authToken && !hasApiKey) {
8147
- const { selectAppAfterAuth } = await import("./auth-4HAUNG54.js");
8182
+ const { selectAppAfterAuth } = await import("./auth-BNT5Z5GZ.js");
8148
8183
  console.log("");
8149
8184
  console.log(` No app selected. Please select an app to continue.`);
8150
8185
  await selectAppAfterAuth(authToken);
@@ -8175,7 +8210,7 @@ ${styledLine}`;
8175
8210
  if (isInteractiveAllowed(program)) {
8176
8211
  let latestForInteractiveStartup = null;
8177
8212
  if (shouldRunOnboarding(program, cfg)) {
8178
- const { runOnboarding } = await import("./onboarding-RBJF5E7U.js");
8213
+ const { runOnboarding } = await import("./onboarding-S6HKWOEA.js");
8179
8214
  const latest = getAvailableUpdateOnce();
8180
8215
  const completed = await runOnboarding(program, latest);
8181
8216
  updateShownDuringInteractiveStartup = Boolean(latest);
@@ -8189,7 +8224,7 @@ ${styledLine}`;
8189
8224
  latestForInteractiveStartup
8190
8225
  );
8191
8226
  }
8192
- const { startREPL } = await import("./interactive-F2AFLW7U.js");
8227
+ const { startREPL } = await import("./interactive-N33RCX33.js");
8193
8228
  program.exitOverride();
8194
8229
  program.configureOutput({
8195
8230
  writeErr: () => {
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-TOEVZMIP.js";
10
10
  import {
11
11
  getUpdateNoticeLines
12
- } from "./chunk-WWWVMK3J.js";
12
+ } from "./chunk-RS3DSL3X.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-A6L3WCJN.js";
20
+ } from "./chunk-5IFXLC2S.js";
21
21
  import {
22
22
  configDir,
23
23
  load
@@ -436,6 +436,9 @@ async function startREPL(program, latestUpdate = null) {
436
436
  };
437
437
  return new Promise((resolve) => {
438
438
  rl.on("line", (line) => void onLine(line));
439
+ rl.on("SIGINT", () => {
440
+ rl.close();
441
+ });
439
442
  rl.on("close", () => {
440
443
  if (Array.isArray(rlWithHistory.history)) {
441
444
  saveReplHistory([...rlWithHistory.history].reverse());
@@ -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-WWWVMK3J.js";
5
+ } from "./chunk-RS3DSL3X.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-A6L3WCJN.js";
13
+ } from "./chunk-5IFXLC2S.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-4HAUNG54.js");
54
+ const { selectAppAfterAuth } = await import("./auth-BNT5Z5GZ.js");
55
55
  await selectAppAfterAuth(result.token);
56
56
  return true;
57
57
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy/cli",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Alchemy CLI — interact with blockchain data",
5
5
  "type": "module",
6
6
  "bin": {