@alchemy/cli 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -74
- package/dist/{auth-7E33EMAI.js → auth-QB3BA7AN.js} +7 -3
- package/dist/{auth-E26YCAJV.js → auth-S4DTOWW3.js} +7 -5
- package/dist/{chunk-Z7J64GJJ.js → chunk-3W4ICF67.js} +2 -2
- package/dist/chunk-ATX65U7J.js +737 -0
- package/dist/chunk-BAAQ7ELR.js +143 -0
- package/dist/{chunk-IGD4NIK7.js → chunk-FFMNT74F.js} +54 -36
- package/dist/chunk-JQRGILIS.js +53 -0
- package/dist/chunk-KDMIWPZH.js +27 -0
- package/dist/chunk-NBDWF4ZQ.js +554 -0
- package/dist/{chunk-5X6YRTPU.js → chunk-T5Z2GJUX.js} +7 -5
- package/dist/{chunk-LYUW7O6X.js → chunk-UMKDYHMO.js} +113 -37
- package/dist/credential-storage-T6FFW7DG.js +14 -0
- package/dist/index.js +726 -44
- package/dist/{interactive-G4ON47AR.js → interactive-OM476LBG.js} +11 -6
- package/dist/onboarding-S3GAP4OV.js +61 -0
- package/dist/resolve-HXKHDOJZ.js +31 -0
- package/package.json +2 -1
- package/dist/chunk-44OGGLN4.js +0 -681
- package/dist/chunk-T2XSNZE3.js +0 -1398
- package/dist/onboarding-CWCVWSUG.js +0 -227
|
@@ -2,25 +2,39 @@
|
|
|
2
2
|
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
3
|
import {
|
|
4
4
|
AUTH_PORT,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
exchangeCodeForToken,
|
|
6
|
+
openBrowser,
|
|
7
|
+
prepareBrowserLogin,
|
|
8
|
+
revokeToken,
|
|
9
|
+
waitForCallback
|
|
10
|
+
} from "./chunk-FFMNT74F.js";
|
|
11
|
+
import {
|
|
12
|
+
isInteractiveAllowed
|
|
13
|
+
} from "./chunk-KDMIWPZH.js";
|
|
9
14
|
import {
|
|
10
15
|
AdminClient,
|
|
16
|
+
resolveAuthToken
|
|
17
|
+
} from "./chunk-ATX65U7J.js";
|
|
18
|
+
import {
|
|
19
|
+
deleteCredentials,
|
|
20
|
+
getCredentials,
|
|
21
|
+
getStorageBackend,
|
|
22
|
+
saveCredentials
|
|
23
|
+
} from "./chunk-JQRGILIS.js";
|
|
24
|
+
import {
|
|
11
25
|
bold,
|
|
12
26
|
brand,
|
|
13
|
-
configPath,
|
|
14
27
|
dim,
|
|
15
28
|
green,
|
|
16
|
-
|
|
29
|
+
promptAutocomplete,
|
|
30
|
+
promptText,
|
|
31
|
+
withSpinner
|
|
32
|
+
} from "./chunk-NBDWF4ZQ.js";
|
|
33
|
+
import {
|
|
17
34
|
load,
|
|
18
35
|
maskIf,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
save,
|
|
22
|
-
withSpinner
|
|
23
|
-
} from "./chunk-T2XSNZE3.js";
|
|
36
|
+
save
|
|
37
|
+
} from "./chunk-BAAQ7ELR.js";
|
|
24
38
|
import {
|
|
25
39
|
CLIError,
|
|
26
40
|
ErrorCode,
|
|
@@ -32,11 +46,12 @@ import {
|
|
|
32
46
|
|
|
33
47
|
// src/commands/auth.ts
|
|
34
48
|
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) => {
|
|
49
|
+
const cmd = program.command("auth").description("Authenticate with your Alchemy account").option("-y, --yes", "Skip confirmation prompt and open browser immediately");
|
|
50
|
+
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) => {
|
|
51
|
+
const yes = opts.yes || cmd.opts().yes;
|
|
37
52
|
try {
|
|
38
53
|
if (!opts.force) {
|
|
39
|
-
const existing = resolveAuthToken();
|
|
54
|
+
const existing = await resolveAuthToken();
|
|
40
55
|
if (existing) {
|
|
41
56
|
printHuman(
|
|
42
57
|
` ${green("\u2713")} Already authenticated
|
|
@@ -49,39 +64,89 @@ function registerAuth(program) {
|
|
|
49
64
|
}
|
|
50
65
|
}
|
|
51
66
|
if (opts.force) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
const existingCreds = await getCredentials();
|
|
68
|
+
const tokenToRevoke = existingCreds?.auth_token;
|
|
69
|
+
if (!tokenToRevoke) {
|
|
70
|
+
const cfg2 = load();
|
|
71
|
+
if (cfg2.auth_token) {
|
|
72
|
+
await revokeToken(cfg2.auth_token);
|
|
73
|
+
save({ ...cfg2, auth_token: void 0, auth_token_expires_at: void 0 });
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
await revokeToken(tokenToRevoke);
|
|
56
77
|
}
|
|
78
|
+
await deleteCredentials();
|
|
57
79
|
}
|
|
80
|
+
const prepared = prepareBrowserLogin();
|
|
81
|
+
const callbackPromise = waitForCallback(AUTH_PORT);
|
|
58
82
|
if (!isJSONMode()) {
|
|
59
83
|
console.log("");
|
|
60
84
|
console.log(` ${brand("\u25C6")} ${bold("Alchemy Authentication")}`);
|
|
61
85
|
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
86
|
console.log("");
|
|
63
|
-
console.log(`
|
|
64
|
-
console.log(` ${dim(getLoginUrl(AUTH_PORT))}`);
|
|
87
|
+
console.log(` ${dim(prepared.authorizeUrl)}`);
|
|
65
88
|
console.log("");
|
|
66
|
-
console.log(` ${dim("Waiting for authentication...")}`);
|
|
67
89
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
let browserOpened = false;
|
|
91
|
+
if (!yes && !isJSONMode() && isInteractiveAllowed(program)) {
|
|
92
|
+
const promptResult = await Promise.race([
|
|
93
|
+
promptText({
|
|
94
|
+
message: "Press Enter to open browser, or paste the URL above to log in manually",
|
|
95
|
+
cancelMessage: "Login cancelled."
|
|
96
|
+
}),
|
|
97
|
+
callbackPromise.then(() => "callback_received")
|
|
98
|
+
]);
|
|
99
|
+
if (promptResult === null) return;
|
|
100
|
+
if (promptResult !== "callback_received") {
|
|
101
|
+
if (!isJSONMode()) {
|
|
102
|
+
console.log(` Opening browser to log in...`);
|
|
103
|
+
console.log(` ${dim("Waiting for authentication...")}`);
|
|
104
|
+
}
|
|
105
|
+
openBrowser(prepared.authorizeUrl);
|
|
106
|
+
browserOpened = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!browserOpened && yes) {
|
|
110
|
+
if (!isJSONMode()) {
|
|
111
|
+
console.log(` Opening browser to log in...`);
|
|
112
|
+
console.log(` ${dim("Waiting for authentication...")}`);
|
|
113
|
+
}
|
|
114
|
+
openBrowser(prepared.authorizeUrl);
|
|
115
|
+
}
|
|
116
|
+
const callback = await callbackPromise;
|
|
117
|
+
if (callback.state !== prepared.state) {
|
|
118
|
+
callback.sendError("State mismatch \u2014 possible CSRF attack.");
|
|
119
|
+
throw new Error("OAuth state mismatch. Authentication aborted.");
|
|
120
|
+
}
|
|
121
|
+
let result;
|
|
122
|
+
try {
|
|
123
|
+
result = await exchangeCodeForToken(callback.code, AUTH_PORT, {
|
|
124
|
+
codeVerifier: prepared.codeVerifier
|
|
125
|
+
});
|
|
126
|
+
callback.sendSuccess();
|
|
127
|
+
} catch (err) {
|
|
128
|
+
callback.sendError("Failed to complete authentication. Please try again.");
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
await saveCredentials({
|
|
72
132
|
auth_token: result.token,
|
|
73
133
|
auth_token_expires_at: result.expiresAt
|
|
74
134
|
});
|
|
135
|
+
const cfg = load();
|
|
136
|
+
if (cfg.auth_token) {
|
|
137
|
+
save({ ...cfg, auth_token: void 0, auth_token_expires_at: void 0 });
|
|
138
|
+
}
|
|
75
139
|
const expiresAt = result.expiresAt;
|
|
140
|
+
const backend = await getStorageBackend();
|
|
76
141
|
printHuman(
|
|
77
142
|
` ${green("\u2713")} Logged in successfully
|
|
78
|
-
${dim("
|
|
143
|
+
${dim("Credentials stored in")} ${backend}
|
|
79
144
|
${dim("Expires:")} ${expiresAt}
|
|
80
145
|
`,
|
|
81
146
|
{
|
|
82
147
|
status: "authenticated",
|
|
83
148
|
expiresAt,
|
|
84
|
-
|
|
149
|
+
storageBackend: backend
|
|
85
150
|
}
|
|
86
151
|
);
|
|
87
152
|
if (isInteractiveAllowed(program)) {
|
|
@@ -94,11 +159,13 @@ function registerAuth(program) {
|
|
|
94
159
|
);
|
|
95
160
|
}
|
|
96
161
|
});
|
|
97
|
-
cmd.command("status").description("Show current authentication status").action(() => {
|
|
162
|
+
cmd.command("status").description("Show current authentication status").action(async () => {
|
|
98
163
|
try {
|
|
164
|
+
const creds = await getCredentials();
|
|
99
165
|
const cfg = load();
|
|
100
|
-
const validToken = resolveAuthToken(cfg);
|
|
101
|
-
|
|
166
|
+
const validToken = await resolveAuthToken(cfg);
|
|
167
|
+
const hasToken = creds?.auth_token || cfg.auth_token;
|
|
168
|
+
if (!hasToken) {
|
|
102
169
|
printHuman(
|
|
103
170
|
` ${dim("Not authenticated. Run")} alchemy auth ${dim("to log in.")}
|
|
104
171
|
`,
|
|
@@ -114,15 +181,20 @@ function registerAuth(program) {
|
|
|
114
181
|
);
|
|
115
182
|
return;
|
|
116
183
|
}
|
|
184
|
+
const expiresAt = creds?.auth_token_expires_at || cfg.auth_token_expires_at || "unknown";
|
|
185
|
+
const backend = await getStorageBackend();
|
|
186
|
+
const storedIn = creds?.auth_token ? backend : "config file (legacy)";
|
|
117
187
|
printHuman(
|
|
118
188
|
` ${green("\u2713")} Authenticated
|
|
119
189
|
${dim("Token:")} ${maskIf(validToken)}
|
|
120
|
-
${dim("
|
|
190
|
+
${dim("Storage:")} ${storedIn}
|
|
191
|
+
${dim("Expires:")} ${expiresAt}
|
|
121
192
|
`,
|
|
122
193
|
{
|
|
123
194
|
authenticated: true,
|
|
124
195
|
expired: false,
|
|
125
|
-
expiresAt
|
|
196
|
+
expiresAt,
|
|
197
|
+
storageBackend: storedIn
|
|
126
198
|
}
|
|
127
199
|
);
|
|
128
200
|
} catch (err) {
|
|
@@ -131,14 +203,17 @@ function registerAuth(program) {
|
|
|
131
203
|
});
|
|
132
204
|
cmd.command("logout").description("Clear saved authentication token").action(async () => {
|
|
133
205
|
try {
|
|
206
|
+
const creds = await getCredentials();
|
|
134
207
|
const cfg = load();
|
|
208
|
+
const activeToken = creds?.auth_token || cfg.auth_token;
|
|
135
209
|
let revokeResult;
|
|
136
|
-
if (
|
|
137
|
-
revokeResult = await revokeToken(
|
|
210
|
+
if (activeToken) {
|
|
211
|
+
revokeResult = await revokeToken(activeToken);
|
|
138
212
|
}
|
|
139
|
-
|
|
213
|
+
await deleteCredentials();
|
|
214
|
+
const { auth_token: _, auth_token_expires_at: __, app: ___, ...rest } = cfg;
|
|
140
215
|
save(rest);
|
|
141
|
-
if (!
|
|
216
|
+
if (!activeToken) {
|
|
142
217
|
printHuman(
|
|
143
218
|
` ${dim("No active session.")}
|
|
144
219
|
`,
|
|
@@ -196,8 +271,9 @@ async function selectAppAfterAuth(authToken) {
|
|
|
196
271
|
console.log(` ${green("\u2713")} Auto-selected app: ${bold(selectedApp.name)}`);
|
|
197
272
|
} else {
|
|
198
273
|
console.log("");
|
|
199
|
-
const appId = await
|
|
274
|
+
const appId = await promptAutocomplete({
|
|
200
275
|
message: "Select an app",
|
|
276
|
+
placeholder: "Type to search by name",
|
|
201
277
|
options: apps.map((app) => ({
|
|
202
278
|
value: app.id,
|
|
203
279
|
label: app.name,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
|
|
3
|
+
import {
|
|
4
|
+
deleteCredentials,
|
|
5
|
+
getCredentials,
|
|
6
|
+
getStorageBackend,
|
|
7
|
+
saveCredentials
|
|
8
|
+
} from "./chunk-JQRGILIS.js";
|
|
9
|
+
export {
|
|
10
|
+
deleteCredentials,
|
|
11
|
+
getCredentials,
|
|
12
|
+
getStorageBackend,
|
|
13
|
+
saveCredentials
|
|
14
|
+
};
|