|
1 | 1 | import { select } from "@inquirer/prompts"; |
2 | 2 | import type { Command, CommandContext } from "@/commands/types"; |
3 | | -import { getEnvironmentConfig, type Environment } from "@/environment"; |
4 | | -import { clearToken, loadToken, saveToken } from "@/secureStore"; |
5 | | -import { requestAppPairing } from "./appPairingRequest"; |
| 3 | +import type { Environment } from "@/environment"; |
| 4 | +import { saveToken } from "@/secureStore"; |
| 5 | +import { requestAppPairing } from "@/commands/auth/appPairingRequest"; |
6 | 6 | import { |
7 | 7 | decryptAppPairingToken, |
8 | 8 | generateAppPairingKeyPair, |
9 | 9 | } from "@/utils/appPairingCrypto"; |
10 | 10 | import { emojiHash } from "@/utils/emojiHash"; |
11 | 11 | import { openBrowser } from "@/utils/browser"; |
12 | 12 | import { renderQrCode } from "@/utils/qrCode"; |
| 13 | +import { fetchDeveloperMe } from "@/commands/auth/developerMe"; |
13 | 14 |
|
14 | 15 | type LoginOptions = { |
15 | 16 | token?: string; |
16 | 17 | tokenStdin: boolean; |
17 | 18 | }; |
18 | 19 |
|
19 | | -type DevUser = { |
20 | | - id: number; |
21 | | - first_name: string; |
22 | | - last_name: string | null; |
23 | | -}; |
24 | | - |
25 | 20 | type DeviceAuthMethod = "browser" | "qr"; |
26 | 21 |
|
27 | 22 | const USAGE = [ |
28 | | - "bee auth login", |
29 | | - "bee auth login --token <token>", |
30 | | - "bee auth login --token-stdin", |
31 | | - "bee auth status", |
32 | | - "bee auth logout", |
| 23 | + "bee login", |
| 24 | + "bee login --token <token>", |
| 25 | + "bee login --token-stdin", |
33 | 26 | ].join("\n"); |
34 | 27 |
|
35 | | -const DESCRIPTION = |
36 | | - "Manage developer API authentication (app tokens with embedded secrets)."; |
37 | | - |
38 | | -export const authCommand: Command = { |
39 | | - name: "auth", |
40 | | - description: DESCRIPTION, |
| 28 | +export const loginCommand: Command = { |
| 29 | + name: "login", |
| 30 | + description: "Authenticate the CLI with your Bee account.", |
41 | 31 | usage: USAGE, |
42 | 32 | run: async (args, context) => { |
43 | | - if (args.length === 0) { |
44 | | - throw new Error("Missing subcommand. Use login, status, or logout."); |
45 | | - } |
46 | | - |
47 | | - const [subcommand, ...rest] = args; |
48 | | - switch (subcommand) { |
49 | | - case "login": |
50 | | - await handleLogin(rest, context); |
51 | | - return; |
52 | | - case "status": |
53 | | - await handleStatus(rest, context); |
54 | | - return; |
55 | | - case "logout": |
56 | | - await handleLogout(rest, context); |
57 | | - return; |
58 | | - default: |
59 | | - throw new Error(`Unknown auth subcommand: ${subcommand}`); |
60 | | - } |
| 33 | + await handleLogin(args, context); |
61 | 34 | }, |
62 | 35 | }; |
63 | 36 |
|
@@ -103,41 +76,6 @@ async function handleLogin( |
103 | 76 | console.log("Token stored."); |
104 | 77 | } |
105 | 78 |
|
106 | | -async function handleStatus( |
107 | | - _args: readonly string[], |
108 | | - context: CommandContext |
109 | | -): Promise<void> { |
110 | | - if (_args.length > 0) { |
111 | | - throw new Error("status does not accept arguments."); |
112 | | - } |
113 | | - const token = await loadToken(context.env); |
114 | | - const config = getEnvironmentConfig(context.env); |
115 | | - |
116 | | - if (!token) { |
117 | | - console.log("Not logged in."); |
118 | | - console.log(`API: ${config.label} (${config.apiUrl})`); |
119 | | - return; |
120 | | - } |
121 | | - |
122 | | - console.log(`API: ${config.label} (${config.apiUrl})`); |
123 | | - console.log(`Token: ${maskToken(token)}`); |
124 | | - |
125 | | - const user = await fetchDeveloperMe(context, token); |
126 | | - const name = [user.first_name, user.last_name].filter(Boolean).join(" "); |
127 | | - console.log(`Verified as ${name} (id ${user.id}).`); |
128 | | -} |
129 | | - |
130 | | -async function handleLogout( |
131 | | - args: readonly string[], |
132 | | - context: CommandContext |
133 | | -): Promise<void> { |
134 | | - if (args.length > 0) { |
135 | | - throw new Error("logout does not accept arguments."); |
136 | | - } |
137 | | - await clearToken(context.env); |
138 | | - console.log("Logged out."); |
139 | | -} |
140 | | - |
141 | 79 | function parseLoginArgs(args: readonly string[]): LoginOptions { |
142 | 80 | let token: string | undefined; |
143 | 81 | let tokenStdin = false; |
@@ -207,14 +145,6 @@ async function readTokenFromStdin(): Promise<string> { |
207 | 145 | }); |
208 | 146 | } |
209 | 147 |
|
210 | | -function maskToken(token: string): string { |
211 | | - const trimmed = token.trim(); |
212 | | - if (trimmed.length <= 8) { |
213 | | - return "********"; |
214 | | - } |
215 | | - return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`; |
216 | | -} |
217 | | - |
218 | 148 | async function loginWithAppPairing(context: CommandContext): Promise<string> { |
219 | 149 | if (!process.stdin.isTTY) { |
220 | 150 | throw new Error( |
@@ -356,51 +286,3 @@ async function sleep(durationMs: number): Promise<void> { |
356 | 286 | setTimeout(resolve, durationMs); |
357 | 287 | }); |
358 | 288 | } |
359 | | - |
360 | | -async function fetchDeveloperMe( |
361 | | - context: CommandContext, |
362 | | - token: string |
363 | | -): Promise<DevUser> { |
364 | | - const response = await context.client.fetch("/v1/me", { |
365 | | - method: "GET", |
366 | | - headers: { |
367 | | - Authorization: `Bearer ${token}`, |
368 | | - }, |
369 | | - }); |
370 | | - |
371 | | - if (!response.ok) { |
372 | | - const errorPayload = await safeJson(response); |
373 | | - const message = |
374 | | - typeof errorPayload?.["error"] === "string" |
375 | | - ? errorPayload["error"] |
376 | | - : `Request failed with status ${response.status}`; |
377 | | - throw new Error(message); |
378 | | - } |
379 | | - |
380 | | - const data = await safeJson(response); |
381 | | - const id = data?.["id"]; |
382 | | - const firstName = data?.["first_name"]; |
383 | | - if (typeof id !== "number" || typeof firstName !== "string") { |
384 | | - throw new Error("Invalid response from developer API."); |
385 | | - } |
386 | | - |
387 | | - return { |
388 | | - id, |
389 | | - first_name: firstName, |
390 | | - last_name: typeof data?.["last_name"] === "string" ? data["last_name"] : null, |
391 | | - }; |
392 | | -} |
393 | | - |
394 | | -async function safeJson( |
395 | | - response: Response |
396 | | -): Promise<Record<string, unknown> | null> { |
397 | | - try { |
398 | | - const parsed = (await response.json()) as Record<string, unknown>; |
399 | | - if (!parsed || typeof parsed !== "object") { |
400 | | - return null; |
401 | | - } |
402 | | - return parsed; |
403 | | - } catch { |
404 | | - return null; |
405 | | - } |
406 | | -} |
0 commit comments