@alchemy/cli 0.5.1 → 0.5.2
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 +22 -71
- package/dist/{auth-E26YCAJV.js → auth-76PDHQ3U.js} +1 -1
- package/dist/{auth-7E33EMAI.js → auth-PYH5WEC3.js} +6 -3
- package/dist/{chunk-IGD4NIK7.js → chunk-5ZAK2VSS.js} +2 -2
- package/dist/chunk-BAAQ7ELR.js +143 -0
- package/dist/{chunk-LYUW7O6X.js → chunk-DBTRDS35.js} +30 -12
- package/dist/{chunk-5X6YRTPU.js → chunk-FM7GQX6U.js} +4 -2
- package/dist/chunk-KDMIWPZH.js +27 -0
- package/dist/chunk-NBDWF4ZQ.js +554 -0
- package/dist/chunk-NM25MEJZ.js +724 -0
- package/dist/{chunk-Z7J64GJJ.js → chunk-NSG4ZKZI.js} +2 -2
- package/dist/index.js +699 -30
- package/dist/{interactive-G4ON47AR.js → interactive-CGEVIPC2.js} +9 -5
- package/dist/onboarding-DNEXVUUH.js +64 -0
- package/dist/resolve-CLDYJ27A.js +30 -0
- package/package.json +1 -1
- package/dist/chunk-44OGGLN4.js +0 -681
- package/dist/chunk-T2XSNZE3.js +0 -1398
- package/dist/onboarding-CWCVWSUG.js +0 -227
package/README.md
CHANGED
|
@@ -39,26 +39,20 @@ alchemy completions fish > ~/.config/fish/completions/alchemy.fish
|
|
|
39
39
|
|
|
40
40
|
### Authentication Quick Start
|
|
41
41
|
|
|
42
|
-
Authentication is required before making requests.
|
|
43
|
-
|
|
44
|
-
If you are using the CLI as a human in an interactive terminal, the easiest path is:
|
|
42
|
+
Authentication is required before making requests. The recommended path is browser login:
|
|
45
43
|
|
|
46
44
|
```bash
|
|
47
|
-
alchemy
|
|
45
|
+
alchemy auth
|
|
48
46
|
```
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
This opens a browser to link your Alchemy account, then prompts you to select an app. The selected app's API key is saved to your config automatically. Pass `-y` to skip the confirmation prompt.
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
If you run `alchemy` with no command and no auth configured, the CLI will guide you through browser login automatically.
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
- **Access key** - Admin/API app management; app setup/selection can also provide API key auth for blockchain queries
|
|
56
|
-
- **x402 wallet auth** - wallet-authenticated, pay-per-request model for supported blockchain queries
|
|
52
|
+
If you have an auth token but haven't selected an app yet, the CLI will prompt you to pick one before running any command that requires an API key. Teams with many apps can type to search by name.
|
|
57
53
|
|
|
58
54
|
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`.
|
|
59
55
|
|
|
60
|
-
For setup commands, env vars, and resolution order, see [Authentication Reference](#authentication-reference).
|
|
61
|
-
|
|
62
56
|
### Usage By Workflow
|
|
63
57
|
|
|
64
58
|
After auth is configured, use the CLI differently depending on who is driving it:
|
|
@@ -165,6 +159,9 @@ Use `alchemy help` or `alchemy help <command>` for generated command help.
|
|
|
165
159
|
| Command | What it does | Example |
|
|
166
160
|
|---|---|---|
|
|
167
161
|
| `(no command)` | Starts interactive REPL mode (TTY only) | `alchemy` |
|
|
162
|
+
| `auth` (`auth login`) | Log in via browser (PKCE) | `alchemy auth` |
|
|
163
|
+
| `auth status` | Show current authentication status | `alchemy auth status` |
|
|
164
|
+
| `auth logout` | Clear saved authentication token | `alchemy auth logout` |
|
|
168
165
|
| `apps list` | Lists apps (supports pagination/filtering) | `alchemy apps list --all` |
|
|
169
166
|
| `apps chains` | Lists Admin API chain identifiers (e.g. `ETH_MAINNET`) | `alchemy apps chains` |
|
|
170
167
|
| `apps get <id>` | Gets app details | `alchemy apps get <app-id>` |
|
|
@@ -231,6 +228,7 @@ Additional env vars:
|
|
|
231
228
|
|
|
232
229
|
| Command | Flags |
|
|
233
230
|
|---|---|
|
|
231
|
+
| `auth login` | `--force`, `-y, --yes` |
|
|
234
232
|
| `nfts` | `--limit <n>`, `--page-key <key>` |
|
|
235
233
|
| `nfts metadata` | `--contract <address>` (required), `--token-id <id>` (required) |
|
|
236
234
|
| `tokens` | `--page-key <key>` |
|
|
@@ -258,74 +256,27 @@ Additional env vars:
|
|
|
258
256
|
|
|
259
257
|
## Authentication Reference
|
|
260
258
|
|
|
261
|
-
The CLI supports three auth inputs:
|
|
262
|
-
|
|
263
|
-
- API key for blockchain queries (`balance`, `tx`, `block`, `nfts`, `tokens`, `rpc`)
|
|
264
|
-
- 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
|
|
265
|
-
- x402 wallet key for wallet-authenticated blockchain queries in a pay-per-request model
|
|
266
|
-
|
|
267
|
-
Notify/webhook commands use a webhook API key with resolution order:
|
|
268
|
-
`--webhook-api-key` -> `ALCHEMY_WEBHOOK_API_KEY` -> `ALCHEMY_NOTIFY_AUTH_TOKEN` -> config `webhook-api-key` -> configured app webhook key.
|
|
269
|
-
|
|
270
|
-
Get API/access keys at [alchemy.com](https://dashboard.alchemy.com/).
|
|
271
|
-
|
|
272
|
-
#### API key
|
|
273
|
-
|
|
274
259
|
```bash
|
|
275
|
-
#
|
|
276
|
-
alchemy
|
|
260
|
+
# Interactive login — opens browser to link your Alchemy account
|
|
261
|
+
alchemy auth
|
|
277
262
|
|
|
278
|
-
#
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
# Per-command override
|
|
282
|
-
alchemy balance 0x... --api-key <your-key>
|
|
283
|
-
```
|
|
263
|
+
# Skip the confirmation prompt
|
|
264
|
+
alchemy auth -y
|
|
284
265
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
#### Access key
|
|
288
|
-
|
|
289
|
-
```bash
|
|
290
|
-
# Config (in TTY, this may trigger app setup flow)
|
|
291
|
-
alchemy config set access-key <your-key>
|
|
266
|
+
# Force re-authentication
|
|
267
|
+
alchemy auth login --force
|
|
292
268
|
|
|
293
|
-
#
|
|
294
|
-
|
|
269
|
+
# Check auth status
|
|
270
|
+
alchemy auth status
|
|
295
271
|
|
|
296
|
-
#
|
|
297
|
-
alchemy
|
|
272
|
+
# Log out
|
|
273
|
+
alchemy auth logout
|
|
298
274
|
```
|
|
299
275
|
|
|
300
|
-
|
|
276
|
+
After login, the CLI prompts you to select an app. The app's API key is saved to config and used for all subsequent commands. If you skip app selection during login, the CLI will prompt you to pick one before running any command that needs an API key.
|
|
301
277
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
x402 is a wallet-authenticated, pay-per-request usage model for supported blockchain queries.
|
|
305
|
-
The CLI can generate or import the wallet key used for these requests.
|
|
306
|
-
|
|
307
|
-
```bash
|
|
308
|
-
# Generate/import a wallet managed by CLI
|
|
309
|
-
alchemy wallet generate
|
|
310
|
-
# or
|
|
311
|
-
alchemy wallet import ./private-key.txt
|
|
312
|
-
|
|
313
|
-
# Use x402 per command
|
|
314
|
-
alchemy balance 0x... --x402
|
|
315
|
-
|
|
316
|
-
# Or enable by default
|
|
317
|
-
alchemy config set x402 true
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
Generated/imported wallets are stored as unique key files under `~/.config/alchemy/wallet-keys/` so creating another wallet does not overwrite prior private keys.
|
|
321
|
-
|
|
322
|
-
You can also provide wallet key directly:
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
export ALCHEMY_WALLET_KEY=0x...
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
Wallet key resolution order: `--wallet-key-file` -> `ALCHEMY_WALLET_KEY` -> `wallet-key-file` in config.
|
|
278
|
+
Notify/webhook commands use a webhook API key with resolution order:
|
|
279
|
+
`--webhook-api-key` -> `ALCHEMY_WEBHOOK_API_KEY` -> `ALCHEMY_NOTIFY_AUTH_TOKEN` -> config `webhook-api-key` -> configured app webhook key.
|
|
329
280
|
|
|
330
281
|
## REPL Mode
|
|
331
282
|
|
|
@@ -3,9 +3,12 @@ if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
|
3
3
|
import {
|
|
4
4
|
registerAuth,
|
|
5
5
|
selectAppAfterAuth
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-DBTRDS35.js";
|
|
7
|
+
import "./chunk-5ZAK2VSS.js";
|
|
8
|
+
import "./chunk-KDMIWPZH.js";
|
|
9
|
+
import "./chunk-NM25MEJZ.js";
|
|
10
|
+
import "./chunk-NBDWF4ZQ.js";
|
|
11
|
+
import "./chunk-BAAQ7ELR.js";
|
|
9
12
|
import "./chunk-56ZVYB4G.js";
|
|
10
13
|
export {
|
|
11
14
|
registerAuth,
|
|
@@ -18,7 +18,7 @@ var SHARED_STYLE = `
|
|
|
18
18
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
19
19
|
display: flex; justify-content: center; align-items: center;
|
|
20
20
|
min-height: 100vh;
|
|
21
|
-
background: #
|
|
21
|
+
background: linear-gradient(180deg, #4F46E5 0%, #06B6D4 100%);
|
|
22
22
|
color: #fff;
|
|
23
23
|
overflow: hidden;
|
|
24
24
|
}
|
|
@@ -35,7 +35,7 @@ var SHARED_STYLE = `
|
|
|
35
35
|
letter-spacing: -0.01em;
|
|
36
36
|
}
|
|
37
37
|
p {
|
|
38
|
-
color: #
|
|
38
|
+
color: #fff;
|
|
39
39
|
font-size: 0.875rem;
|
|
40
40
|
}
|
|
41
41
|
`;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
isRevealMode
|
|
5
|
+
} from "./chunk-56ZVYB4G.js";
|
|
6
|
+
|
|
7
|
+
// src/lib/secrets.ts
|
|
8
|
+
function maskSecret(value) {
|
|
9
|
+
if (value.length <= 8) return "\u2022".repeat(value.length);
|
|
10
|
+
return value.slice(0, 4) + "\u2022".repeat(value.length - 8) + value.slice(-4);
|
|
11
|
+
}
|
|
12
|
+
function maskIf(value) {
|
|
13
|
+
return isRevealMode() ? value : maskSecret(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/lib/config.ts
|
|
17
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
import { join, dirname } from "path";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
var KEY_MAP = {
|
|
22
|
+
"api-key": "api_key",
|
|
23
|
+
api_key: "api_key",
|
|
24
|
+
"access-key": "access_key",
|
|
25
|
+
access_key: "access_key",
|
|
26
|
+
"webhook-api-key": "webhook_api_key",
|
|
27
|
+
webhook_api_key: "webhook_api_key",
|
|
28
|
+
network: "network",
|
|
29
|
+
verbose: "verbose",
|
|
30
|
+
"wallet-key-file": "wallet_key_file",
|
|
31
|
+
wallet_key_file: "wallet_key_file",
|
|
32
|
+
"wallet-address": "wallet_address",
|
|
33
|
+
wallet_address: "wallet_address",
|
|
34
|
+
x402: "x402",
|
|
35
|
+
"auth-token": "auth_token",
|
|
36
|
+
auth_token: "auth_token",
|
|
37
|
+
"auth-token-expires-at": "auth_token_expires_at",
|
|
38
|
+
auth_token_expires_at: "auth_token_expires_at"
|
|
39
|
+
};
|
|
40
|
+
var SAFE_ID_RE = /^[A-Za-z0-9:_-]{1,128}$/;
|
|
41
|
+
var SAFE_NETWORK_RE = /^[A-Za-z0-9:_-]{1,128}$/;
|
|
42
|
+
var MAX_SECRET_LEN = 512;
|
|
43
|
+
var MAX_APP_NAME_LEN = 128;
|
|
44
|
+
var CONTROL_CHAR_RE = /[\u0000-\u001f\u007f]/;
|
|
45
|
+
var safeTextSchema = (maxLen) => z.string().min(1).max(maxLen).refine((value) => !CONTROL_CHAR_RE.test(value));
|
|
46
|
+
var appConfigSchema = z.object({
|
|
47
|
+
id: z.string().regex(SAFE_ID_RE),
|
|
48
|
+
name: safeTextSchema(MAX_APP_NAME_LEN),
|
|
49
|
+
apiKey: safeTextSchema(MAX_SECRET_LEN),
|
|
50
|
+
webhookApiKey: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0)
|
|
51
|
+
}).strip();
|
|
52
|
+
var MAX_PATH_LEN = 4096;
|
|
53
|
+
var configSchema = z.object({
|
|
54
|
+
api_key: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
55
|
+
access_key: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
56
|
+
webhook_api_key: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
57
|
+
app: appConfigSchema.optional().catch(void 0),
|
|
58
|
+
network: z.string().regex(SAFE_NETWORK_RE).optional().catch(void 0),
|
|
59
|
+
verbose: z.boolean().optional().catch(void 0),
|
|
60
|
+
wallet_key_file: safeTextSchema(MAX_PATH_LEN).optional().catch(void 0),
|
|
61
|
+
wallet_address: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
62
|
+
x402: z.boolean().optional().catch(void 0),
|
|
63
|
+
auth_token: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
64
|
+
auth_token_expires_at: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0),
|
|
65
|
+
siwe_token: safeTextSchema(MAX_PATH_LEN).optional().catch(void 0),
|
|
66
|
+
siwe_token_expires_at: safeTextSchema(MAX_SECRET_LEN).optional().catch(void 0)
|
|
67
|
+
}).strip();
|
|
68
|
+
function sanitizeConfig(input) {
|
|
69
|
+
const parsed = configSchema.safeParse(input);
|
|
70
|
+
if (!parsed.success) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
return parsed.data;
|
|
74
|
+
}
|
|
75
|
+
function getHome() {
|
|
76
|
+
return process.env.HOME || homedir();
|
|
77
|
+
}
|
|
78
|
+
function configPath() {
|
|
79
|
+
if (process.env.ALCHEMY_CONFIG) return process.env.ALCHEMY_CONFIG;
|
|
80
|
+
const configHome = process.env.XDG_CONFIG_HOME || join(getHome(), ".config");
|
|
81
|
+
return join(configHome, "alchemy", "config.json");
|
|
82
|
+
}
|
|
83
|
+
function configDir() {
|
|
84
|
+
return dirname(configPath());
|
|
85
|
+
}
|
|
86
|
+
function load() {
|
|
87
|
+
const p = configPath();
|
|
88
|
+
if (!existsSync(p)) return {};
|
|
89
|
+
try {
|
|
90
|
+
const data = readFileSync(p, "utf-8");
|
|
91
|
+
return sanitizeConfig(JSON.parse(data));
|
|
92
|
+
} catch {
|
|
93
|
+
console.error(`warning: could not parse config file at ${p} \u2014 using defaults`);
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function save(cfg) {
|
|
98
|
+
const p = configPath();
|
|
99
|
+
const sanitized = sanitizeConfig(cfg);
|
|
100
|
+
mkdirSync(dirname(p), { recursive: true, mode: 493 });
|
|
101
|
+
writeFileSync(p, JSON.stringify(sanitized, null, 2) + "\n", {
|
|
102
|
+
mode: 384
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function get(cfg, key) {
|
|
106
|
+
if (key === "app") {
|
|
107
|
+
if (!cfg.app) return void 0;
|
|
108
|
+
return `${cfg.app.name} (${cfg.app.id})`;
|
|
109
|
+
}
|
|
110
|
+
const mapped = KEY_MAP[key];
|
|
111
|
+
if (!mapped) return void 0;
|
|
112
|
+
const value = cfg[mapped];
|
|
113
|
+
if (value === void 0) return void 0;
|
|
114
|
+
if (typeof value === "boolean") return String(value);
|
|
115
|
+
if (typeof value === "string") return value;
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
function toMap(cfg) {
|
|
119
|
+
const m = {};
|
|
120
|
+
if (cfg.api_key) m["api-key"] = maskIf(cfg.api_key);
|
|
121
|
+
if (cfg.access_key) m["access-key"] = maskIf(cfg.access_key);
|
|
122
|
+
if (cfg.webhook_api_key) m["webhook-api-key"] = maskIf(cfg.webhook_api_key);
|
|
123
|
+
if (cfg.app) m["app"] = `${cfg.app.name} (${cfg.app.id})`;
|
|
124
|
+
if (cfg.network) m["network"] = cfg.network;
|
|
125
|
+
if (cfg.verbose !== void 0) m["verbose"] = String(cfg.verbose);
|
|
126
|
+
if (cfg.wallet_key_file) m["wallet-key-file"] = cfg.wallet_key_file;
|
|
127
|
+
if (cfg.wallet_address) m["wallet-address"] = cfg.wallet_address;
|
|
128
|
+
if (cfg.x402 !== void 0) m["x402"] = String(cfg.x402);
|
|
129
|
+
if (cfg.auth_token) m["auth-token"] = maskIf(cfg.auth_token);
|
|
130
|
+
if (cfg.auth_token_expires_at) m["auth-token-expires-at"] = cfg.auth_token_expires_at;
|
|
131
|
+
return m;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export {
|
|
135
|
+
maskIf,
|
|
136
|
+
KEY_MAP,
|
|
137
|
+
configPath,
|
|
138
|
+
configDir,
|
|
139
|
+
load,
|
|
140
|
+
save,
|
|
141
|
+
get,
|
|
142
|
+
toMap
|
|
143
|
+
};
|
|
@@ -5,22 +5,29 @@ import {
|
|
|
5
5
|
getLoginUrl,
|
|
6
6
|
performBrowserLogin,
|
|
7
7
|
revokeToken
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5ZAK2VSS.js";
|
|
9
|
+
import {
|
|
10
|
+
isInteractiveAllowed
|
|
11
|
+
} from "./chunk-KDMIWPZH.js";
|
|
9
12
|
import {
|
|
10
13
|
AdminClient,
|
|
14
|
+
resolveAuthToken
|
|
15
|
+
} from "./chunk-NM25MEJZ.js";
|
|
16
|
+
import {
|
|
11
17
|
bold,
|
|
12
18
|
brand,
|
|
13
|
-
configPath,
|
|
14
19
|
dim,
|
|
15
20
|
green,
|
|
16
|
-
|
|
21
|
+
promptAutocomplete,
|
|
22
|
+
promptText,
|
|
23
|
+
withSpinner
|
|
24
|
+
} from "./chunk-NBDWF4ZQ.js";
|
|
25
|
+
import {
|
|
26
|
+
configPath,
|
|
17
27
|
load,
|
|
18
28
|
maskIf,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
save,
|
|
22
|
-
withSpinner
|
|
23
|
-
} from "./chunk-T2XSNZE3.js";
|
|
29
|
+
save
|
|
30
|
+
} from "./chunk-BAAQ7ELR.js";
|
|
24
31
|
import {
|
|
25
32
|
CLIError,
|
|
26
33
|
ErrorCode,
|
|
@@ -32,8 +39,9 @@ import {
|
|
|
32
39
|
|
|
33
40
|
// src/commands/auth.ts
|
|
34
41
|
function registerAuth(program) {
|
|
35
|
-
const cmd = program.command("auth").description("Authenticate with your Alchemy account");
|
|
36
|
-
cmd.command("login", { isDefault: true }).description("Log in via browser").option("--force", "Force re-authentication even if a valid token exists").action(async (opts) => {
|
|
42
|
+
const cmd = program.command("auth").description("Authenticate with your Alchemy account").option("-y, --yes", "Skip confirmation prompt and open browser immediately");
|
|
43
|
+
cmd.command("login", { isDefault: true }).description("Log in via browser").option("--force", "Force re-authentication even if a valid token exists").option("-y, --yes", "Skip confirmation prompt and open browser immediately").action(async (opts) => {
|
|
44
|
+
const yes = opts.yes || cmd.opts().yes;
|
|
37
45
|
try {
|
|
38
46
|
if (!opts.force) {
|
|
39
47
|
const existing = resolveAuthToken();
|
|
@@ -60,9 +68,18 @@ function registerAuth(program) {
|
|
|
60
68
|
console.log(` ${brand("\u25C6")} ${bold("Alchemy Authentication")}`);
|
|
61
69
|
console.log(` ${dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
|
|
62
70
|
console.log("");
|
|
63
|
-
console.log(` Opening browser to log in...`);
|
|
64
71
|
console.log(` ${dim(getLoginUrl(AUTH_PORT))}`);
|
|
65
72
|
console.log("");
|
|
73
|
+
}
|
|
74
|
+
if (!yes && !isJSONMode() && isInteractiveAllowed(program)) {
|
|
75
|
+
const answer = await promptText({
|
|
76
|
+
message: "Press Enter to open browser and link your Alchemy account",
|
|
77
|
+
cancelMessage: "Login cancelled."
|
|
78
|
+
});
|
|
79
|
+
if (answer === null) return;
|
|
80
|
+
}
|
|
81
|
+
if (!isJSONMode()) {
|
|
82
|
+
console.log(` Opening browser to log in...`);
|
|
66
83
|
console.log(` ${dim("Waiting for authentication...")}`);
|
|
67
84
|
}
|
|
68
85
|
const result = await performBrowserLogin();
|
|
@@ -196,8 +213,9 @@ async function selectAppAfterAuth(authToken) {
|
|
|
196
213
|
console.log(` ${green("\u2713")} Auto-selected app: ${bold(selectedApp.name)}`);
|
|
197
214
|
} else {
|
|
198
215
|
console.log("");
|
|
199
|
-
const appId = await
|
|
216
|
+
const appId = await promptAutocomplete({
|
|
200
217
|
message: "Select an app",
|
|
218
|
+
placeholder: "Type to search by name",
|
|
201
219
|
options: apps.map((app) => ({
|
|
202
220
|
value: app.id,
|
|
203
221
|
label: app.name,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
3
|
import {
|
|
4
|
-
isInteractiveAllowed
|
|
4
|
+
isInteractiveAllowed
|
|
5
|
+
} from "./chunk-KDMIWPZH.js";
|
|
6
|
+
import {
|
|
5
7
|
resolveAuthToken
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NM25MEJZ.js";
|
|
7
9
|
import {
|
|
8
10
|
getBaseDomain
|
|
9
11
|
} from "./chunk-56ZVYB4G.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
isJSONMode
|
|
5
|
+
} from "./chunk-56ZVYB4G.js";
|
|
6
|
+
|
|
7
|
+
// src/lib/interaction.ts
|
|
8
|
+
import { stdin, stdout } from "process";
|
|
9
|
+
function isTruthy(value) {
|
|
10
|
+
if (!value) return false;
|
|
11
|
+
const normalized = value.trim().toLowerCase();
|
|
12
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
13
|
+
}
|
|
14
|
+
function isNonInteractiveEnv() {
|
|
15
|
+
return isTruthy(process.env.ALCHEMY_NON_INTERACTIVE);
|
|
16
|
+
}
|
|
17
|
+
function isInteractiveAllowed(program) {
|
|
18
|
+
if (!stdin.isTTY || !stdout.isTTY) return false;
|
|
19
|
+
if (isJSONMode()) return false;
|
|
20
|
+
if (isNonInteractiveEnv()) return false;
|
|
21
|
+
if (program && program.opts().interactive === false) return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
isInteractiveAllowed
|
|
27
|
+
};
|