@alchemy/cli 0.6.2 → 0.7.0-alpha.11

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.
@@ -1,26 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  if(process.argv.includes("--no-color"))process.env.NO_COLOR="1";
3
3
  import {
4
- AUTH_PORT,
5
- exchangeCodeForToken,
6
- openBrowser,
7
- prepareBrowserLogin,
8
- revokeToken,
9
- waitForCallback
10
- } from "./chunk-FFMNT74F.js";
4
+ completeLogin,
5
+ prepareLogin,
6
+ revokeToken
7
+ } from "./chunk-EKBSQAEG.js";
11
8
  import {
12
9
  isInteractiveAllowed
13
- } from "./chunk-KDMIWPZH.js";
10
+ } from "./chunk-JMDBEC5L.js";
14
11
  import {
15
12
  AdminClient,
16
13
  resolveAuthToken
17
- } from "./chunk-ATX65U7J.js";
18
- import {
19
- deleteCredentials,
20
- getCredentials,
21
- getStorageBackend,
22
- saveCredentials
23
- } from "./chunk-JQRGILIS.js";
14
+ } from "./chunk-A6BWLRFV.js";
24
15
  import {
25
16
  bold,
26
17
  brand,
@@ -29,12 +20,13 @@ import {
29
20
  promptAutocomplete,
30
21
  promptText,
31
22
  withSpinner
32
- } from "./chunk-NBDWF4ZQ.js";
23
+ } from "./chunk-AFD4Y42F.js";
33
24
  import {
25
+ configPath,
34
26
  load,
35
27
  maskIf,
36
28
  save
37
- } from "./chunk-BAAQ7ELR.js";
29
+ } from "./chunk-HGZTGSXO.js";
38
30
  import {
39
31
  CLIError,
40
32
  ErrorCode,
@@ -42,7 +34,7 @@ import {
42
34
  exitWithError,
43
35
  isJSONMode,
44
36
  printHuman
45
- } from "./chunk-56ZVYB4G.js";
37
+ } from "./chunk-OLVYWGY6.js";
46
38
 
47
39
  // src/commands/auth.ts
48
40
  function registerAuth(program) {
@@ -51,7 +43,7 @@ function registerAuth(program) {
51
43
  const yes = opts.yes || cmd.opts().yes;
52
44
  try {
53
45
  if (!opts.force) {
54
- const existing = await resolveAuthToken();
46
+ const existing = resolveAuthToken();
55
47
  if (existing) {
56
48
  printHuman(
57
49
  ` ${green("\u2713")} Already authenticated
@@ -64,108 +56,79 @@ function registerAuth(program) {
64
56
  }
65
57
  }
66
58
  if (opts.force) {
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);
59
+ const cfg2 = load();
60
+ if (cfg2.auth_token) {
61
+ await revokeToken(cfg2.auth_token);
62
+ save({ ...cfg2, auth_token: void 0, auth_token_expires_at: void 0 });
77
63
  }
78
- await deleteCredentials();
79
64
  }
80
- const prepared = prepareBrowserLogin();
81
- const callbackPromise = waitForCallback(AUTH_PORT);
65
+ const prepared = prepareLogin();
82
66
  if (!isJSONMode()) {
83
67
  console.log("");
84
68
  console.log(` ${brand("\u25C6")} ${bold("Alchemy Authentication")}`);
85
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")}`);
86
70
  console.log("");
87
- console.log(` ${dim(prepared.authorizeUrl)}`);
71
+ console.log(` ${dim(prepared.loginUrl)}`);
88
72
  console.log("");
89
73
  }
90
- let browserOpened = false;
74
+ const promptAbort = new AbortController();
75
+ let earlyAuth = false;
76
+ prepared.callbackPromise.then(() => {
77
+ earlyAuth = true;
78
+ promptAbort.abort();
79
+ }).catch(() => {
80
+ });
91
81
  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...")}`);
82
+ const answer = await promptText({
83
+ message: "Press Enter to open browser, or paste the link above to authenticate",
84
+ cancelMessage: "Login cancelled.",
85
+ abortSignal: promptAbort.signal
86
+ });
87
+ if (answer === null) {
88
+ prepared.cancel();
89
+ return;
113
90
  }
114
- openBrowser(prepared.authorizeUrl);
115
91
  }
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;
92
+ if (!earlyAuth && !isJSONMode()) {
93
+ console.log(` Opening browser to log in...`);
94
+ console.log(` ${dim("Waiting for authentication...")}`);
130
95
  }
131
- await saveCredentials({
96
+ const result = await completeLogin(prepared, {
97
+ skipBrowserOpen: earlyAuth
98
+ });
99
+ const cfg = load();
100
+ const hasConfiguredApp = Boolean(cfg.app);
101
+ save({
102
+ ...cfg,
132
103
  auth_token: result.token,
133
104
  auth_token_expires_at: result.expiresAt
134
105
  });
135
- const cfg = load();
136
- if (cfg.auth_token) {
137
- save({ ...cfg, auth_token: void 0, auth_token_expires_at: void 0 });
138
- }
139
106
  const expiresAt = result.expiresAt;
140
- const backend = await getStorageBackend();
141
107
  printHuman(
142
108
  ` ${green("\u2713")} Logged in successfully
143
- ${dim("Credentials stored in")} ${backend}
109
+ ${dim("Token saved to")} ${configPath()}
144
110
  ${dim("Expires:")} ${expiresAt}
145
111
  `,
146
112
  {
147
113
  status: "authenticated",
148
114
  expiresAt,
149
- storageBackend: backend
115
+ configPath: configPath()
150
116
  }
151
117
  );
152
- if (isInteractiveAllowed(program)) {
118
+ if (isInteractiveAllowed(program) && !hasConfiguredApp) {
153
119
  await selectAppAfterAuth(result.token);
154
120
  }
155
- process.exit(0);
156
121
  } catch (err) {
157
122
  exitWithError(
158
123
  err instanceof CLIError ? err : new CLIError(ErrorCode.AUTH_REQUIRED, String(err.message))
159
124
  );
160
125
  }
161
126
  });
162
- cmd.command("status").description("Show current authentication status").action(async () => {
127
+ cmd.command("status").description("Show current authentication status").action(() => {
163
128
  try {
164
- const creds = await getCredentials();
165
129
  const cfg = load();
166
- const validToken = await resolveAuthToken(cfg);
167
- const hasToken = creds?.auth_token || cfg.auth_token;
168
- if (!hasToken) {
130
+ const validToken = resolveAuthToken(cfg);
131
+ if (!cfg.auth_token) {
169
132
  printHuman(
170
133
  ` ${dim("Not authenticated. Run")} alchemy auth ${dim("to log in.")}
171
134
  `,
@@ -181,20 +144,15 @@ function registerAuth(program) {
181
144
  );
182
145
  return;
183
146
  }
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)";
187
147
  printHuman(
188
148
  ` ${green("\u2713")} Authenticated
189
149
  ${dim("Token:")} ${maskIf(validToken)}
190
- ${dim("Storage:")} ${storedIn}
191
- ${dim("Expires:")} ${expiresAt}
150
+ ${dim("Expires:")} ${cfg.auth_token_expires_at || "unknown"}
192
151
  `,
193
152
  {
194
153
  authenticated: true,
195
154
  expired: false,
196
- expiresAt,
197
- storageBackend: storedIn
155
+ expiresAt: cfg.auth_token_expires_at
198
156
  }
199
157
  );
200
158
  } catch (err) {
@@ -203,17 +161,14 @@ function registerAuth(program) {
203
161
  });
204
162
  cmd.command("logout").description("Clear saved authentication token").action(async () => {
205
163
  try {
206
- const creds = await getCredentials();
207
164
  const cfg = load();
208
- const activeToken = creds?.auth_token || cfg.auth_token;
209
165
  let revokeResult;
210
- if (activeToken) {
211
- revokeResult = await revokeToken(activeToken);
166
+ if (cfg.auth_token) {
167
+ revokeResult = await revokeToken(cfg.auth_token);
212
168
  }
213
- await deleteCredentials();
214
- const { auth_token: _, auth_token_expires_at: __, app: ___, ...rest } = cfg;
169
+ const { auth_token: _, auth_token_expires_at: __, ...rest } = cfg;
215
170
  save(rest);
216
- if (!activeToken) {
171
+ if (!cfg.auth_token) {
217
172
  printHuman(
218
173
  ` ${dim("No active session.")}
219
174
  `,