@lizard-build/cli 0.3.51 → 0.3.53

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,7 +1,28 @@
1
1
  import { Command } from "commander";
2
- import { type Credentials } from "../lib/auth.js";
2
+ interface SessionResponse {
3
+ sessionId: string;
4
+ sessionSecret: string;
5
+ expiresIn: number;
6
+ }
7
+ export interface CheckResponse {
8
+ status: "pending" | "complete" | "expired";
9
+ accessToken?: string;
10
+ user?: {
11
+ id: string;
12
+ username: string;
13
+ email?: string;
14
+ avatarUrl?: string;
15
+ };
16
+ }
17
+ /** Create a CLI login session on the server */
18
+ export declare function createSession(): Promise<SessionResponse>;
19
+ /** Check once if the user has completed authentication (no polling loop) */
20
+ export declare function checkSession(sessionId: string, sessionSecret: string): Promise<CheckResponse>;
3
21
  /**
4
- * Perform the login flow. Used by the login command and by auto-login in requireAuth.
22
+ * Start the login flow: creates a session, saves it to disk, opens the
23
+ * browser, prints the URL, then exits. The user authenticates in the browser
24
+ * and re-runs their original command — requireAuth will pick up the session.
5
25
  */
6
- export declare function performLogin(): Promise<Credentials>;
26
+ export declare function performLogin(): Promise<never>;
7
27
  export declare function registerLogin(program: Command): void;
28
+ export {};
@@ -1,94 +1,45 @@
1
1
  import chalk from "chalk";
2
- import ora from "ora";
3
- import { saveCredentials, openURL, jwtExpiryMs, } from "../lib/auth.js";
2
+ import { saveCredentials, savePendingAuth, openURL, jwtExpiryMs, } from "../lib/auth.js";
4
3
  import { getBaseURL } from "../lib/api.js";
5
- import { success, info, isJSONMode, printJSON } from "../lib/format.js";
6
- const POLL_INTERVAL = 2000;
7
- const SESSION_TIMEOUT = 300_000; // 5 min
4
+ import { success, isJSONMode, printJSON } from "../lib/format.js";
8
5
  /** Create a CLI login session on the server */
9
- async function createSession() {
6
+ export async function createSession() {
10
7
  const res = await fetch(`${getBaseURL()}/api/auth/cli/session`, {
11
8
  method: "POST",
12
9
  headers: { "Content-Type": "application/json" },
13
10
  });
14
- if (!res.ok) {
11
+ if (!res.ok)
15
12
  throw new Error(`Failed to create login session: ${res.statusText}`);
16
- }
17
13
  return res.json();
18
14
  }
19
- /** Poll the server to check if the user completed login */
20
- async function pollSession(sessionId, sessionSecret) {
15
+ /** Check once if the user has completed authentication (no polling loop) */
16
+ export async function checkSession(sessionId, sessionSecret) {
21
17
  const res = await fetch(`${getBaseURL()}/api/auth/cli/poll`, {
22
18
  method: "POST",
23
19
  headers: { "Content-Type": "application/json" },
24
20
  body: JSON.stringify({ sessionId, sessionSecret }),
25
21
  });
26
- if (!res.ok) {
27
- throw new Error(`Poll failed: ${res.statusText}`);
28
- }
22
+ if (!res.ok)
23
+ throw new Error(`Auth check failed: ${res.statusText}`);
29
24
  return res.json();
30
25
  }
31
26
  /**
32
- * Perform the login flow. Used by the login command and by auto-login in requireAuth.
27
+ * Start the login flow: creates a session, saves it to disk, opens the
28
+ * browser, prints the URL, then exits. The user authenticates in the browser
29
+ * and re-runs their original command — requireAuth will pick up the session.
33
30
  */
34
31
  export async function performLogin() {
35
- // 1. Create session
36
32
  const session = await createSession();
37
- const loginURL = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
38
- // 2. Try to open browser
39
- const opened = await openURL(loginURL);
40
- if (opened) {
41
- info("Opening browser to log in...");
42
- }
43
- else {
44
- info(`Open this URL in your browser to log in:\n ${chalk.cyan(loginURL)}`);
45
- }
46
- // 3. Poll until complete
47
- const spinner = ora("Waiting for login...").start();
48
- const deadline = Date.now() + SESSION_TIMEOUT;
49
- while (Date.now() < deadline) {
50
- await sleep(POLL_INTERVAL);
51
- try {
52
- const result = await pollSession(session.sessionId, session.sessionSecret);
53
- if (result.status === "complete" && result.accessToken && result.user) {
54
- spinner.stop();
55
- const expMs = jwtExpiryMs(result.accessToken);
56
- const creds = {
57
- accessToken: result.accessToken,
58
- refreshToken: result.refreshToken,
59
- expiresAt: expMs ? new Date(expMs).toISOString() : undefined,
60
- userId: result.user.id,
61
- username: result.user.username,
62
- email: result.user.email,
63
- avatarUrl: result.user.avatarUrl,
64
- };
65
- saveCredentials(creds);
66
- if (isJSONMode()) {
67
- printJSON({ username: creds.username, email: creds.email });
68
- }
69
- else {
70
- success(`Logged in as ${chalk.bold(creds.username)}`);
71
- }
72
- return creds;
73
- }
74
- if (result.status === "expired") {
75
- spinner.stop();
76
- throw new Error("Login session expired. Please try again.");
77
- }
78
- }
79
- catch (err) {
80
- if (err.message?.includes("expired")) {
81
- spinner.stop();
82
- throw err;
83
- }
84
- // Network error — keep trying
85
- }
86
- }
87
- spinner.stop();
88
- throw new Error("Login timed out. Please try again.");
89
- }
90
- function sleep(ms) {
91
- return new Promise((r) => setTimeout(r, ms));
33
+ const authUrl = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
34
+ savePendingAuth({
35
+ sessionId: session.sessionId,
36
+ sessionSecret: session.sessionSecret,
37
+ authUrl,
38
+ createdAt: Date.now(),
39
+ });
40
+ await openURL(authUrl);
41
+ process.stderr.write(`\nAuthenticate with Lizard:\n ${chalk.cyan(authUrl)}\n\nOnce authenticated, run your command again.\n\n`);
42
+ process.exit(0);
92
43
  }
93
44
  export function registerLogin(program) {
94
45
  program
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,eAAe,EACf,eAAe,EAEf,OAAO,EACP,WAAW,GAEZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAmBlE,+CAA+C;AAC/C,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,EAAE,uBAAuB,EAAE;QAC9D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAChD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAClF,OAAO,GAAG,CAAC,IAAI,EAA8B,CAAC;AAChD,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,aAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,EAAE,oBAAoB,EAAE;QAC3D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;KACnD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IACrE,OAAO,GAAG,CAAC,IAAI,EAA4B,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,GAAG,UAAU,EAAE,qBAAqB,OAAO,CAAC,SAAS,EAAE,CAAC;IAExE,eAAe,CAAC;QACd,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kCAAkC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,qDAAqD,CAC3G,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,kBAAkB,CAAC;SAC/B,MAAM,CAAC,iBAAiB,EAAE,gCAAgC,CAAC;SAC3D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,KAAK,EAAE,CAAC;YACV,kCAAkC;YAClC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,EAAE,cAAc,EAAE;gBACrD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;aAC9C,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAQ,CAAC;YACvC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACjC,eAAe,CAAC;gBACd,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;gBAC5D,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC,CAAC;YACH,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,gBAAgB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,10 +1,13 @@
1
- import { type Credentials } from "./config.js";
1
+ import { type Credentials, type PendingAuth } from "./config.js";
2
2
  export type { Credentials } from "./config.js";
3
3
  /** Get the active token in priority order: env → file */
4
4
  export declare function getToken(): string | null;
5
5
  export declare function loadCredentials(): Credentials | null;
6
6
  export declare function saveCredentials(creds: Credentials): void;
7
7
  export declare function clearCredentials(): void;
8
+ export declare function loadPendingAuth(): PendingAuth | null;
9
+ export declare function savePendingAuth(pending: PendingAuth): void;
10
+ export declare function clearPendingAuth(): void;
8
11
  export declare function isLoggedIn(): boolean;
9
12
  /**
10
13
  * Expiry of a JWT in epoch-ms, decoded from the `exp` claim. Returns null
@@ -13,9 +16,16 @@ export declare function isLoggedIn(): boolean;
13
16
  */
14
17
  export declare function jwtExpiryMs(token: string): number | null;
15
18
  /**
16
- * Ensure the user is authenticated. If not logged in (or the saved token
17
- * has expired — there is no refresh endpoint) and TTY, auto-login.
18
- * Returns credentials or throws.
19
+ * Ensure the user is authenticated.
20
+ *
21
+ * On first call with no credentials: creates a CLI auth session, saves it to
22
+ * disk, opens the browser, prints the URL, and exits. The user authenticates
23
+ * in the browser, then re-runs their command.
24
+ *
25
+ * On subsequent calls while a session is pending: checks once (no loop) if
26
+ * the user has completed authentication. If yes, stores credentials and
27
+ * returns them. If still pending, prints the URL and exits. If the session
28
+ * expired, starts a fresh one.
19
29
  */
20
30
  export declare function requireAuth(): Promise<Credentials>;
21
31
  /** Open a URL in the default browser, or print it if headless. */
package/dist/lib/auth.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import open from "open";
2
+ import chalk from "chalk";
2
3
  import { loadConfig, saveConfig, } from "./config.js";
3
4
  /** Get the active token in priority order: env → file */
4
5
  export function getToken() {
@@ -19,6 +20,19 @@ export function clearCredentials() {
19
20
  delete config.credentials;
20
21
  saveConfig(config);
21
22
  }
23
+ export function loadPendingAuth() {
24
+ return loadConfig().pendingAuth ?? null;
25
+ }
26
+ export function savePendingAuth(pending) {
27
+ const config = loadConfig();
28
+ config.pendingAuth = pending;
29
+ saveConfig(config);
30
+ }
31
+ export function clearPendingAuth() {
32
+ const config = loadConfig();
33
+ delete config.pendingAuth;
34
+ saveConfig(config);
35
+ }
22
36
  export function isLoggedIn() {
23
37
  return getToken() !== null;
24
38
  }
@@ -50,17 +64,20 @@ function isExpired(creds) {
50
64
  return Date.now() > expMs - 60_000; // 60s margin
51
65
  }
52
66
  /**
53
- * Ensure the user is authenticated. If not logged in (or the saved token
54
- * has expired — there is no refresh endpoint) and TTY, auto-login.
55
- * Returns credentials or throws.
67
+ * Ensure the user is authenticated.
68
+ *
69
+ * On first call with no credentials: creates a CLI auth session, saves it to
70
+ * disk, opens the browser, prints the URL, and exits. The user authenticates
71
+ * in the browser, then re-runs their command.
72
+ *
73
+ * On subsequent calls while a session is pending: checks once (no loop) if
74
+ * the user has completed authentication. If yes, stores credentials and
75
+ * returns them. If still pending, prints the URL and exits. If the session
76
+ * expired, starts a fresh one.
56
77
  */
57
78
  export async function requireAuth() {
58
79
  if (process.env.LIZARD_TOKEN) {
59
- return {
60
- accessToken: process.env.LIZARD_TOKEN,
61
- userId: "",
62
- username: "",
63
- };
80
+ return { accessToken: process.env.LIZARD_TOKEN, userId: "", username: "" };
64
81
  }
65
82
  const creds = loadCredentials();
66
83
  if (creds && !isExpired(creds))
@@ -72,11 +89,58 @@ export async function requireAuth() {
72
89
  err.code = "NOT_AUTHENTICATED";
73
90
  throw err;
74
91
  }
75
- if (creds) {
76
- process.stderr.write("Session expired logging in again...\n");
92
+ // Check if we already have a pending auth session
93
+ const pending = loadPendingAuth();
94
+ if (pending) {
95
+ const { checkSession } = await import("../commands/login.js");
96
+ try {
97
+ const result = await checkSession(pending.sessionId, pending.sessionSecret);
98
+ if (result.status === "complete" && result.accessToken && result.user) {
99
+ const expMs = jwtExpiryMs(result.accessToken);
100
+ saveCredentials({
101
+ accessToken: result.accessToken,
102
+ expiresAt: expMs ? new Date(expMs).toISOString() : undefined,
103
+ userId: result.user.id,
104
+ username: result.user.username,
105
+ email: result.user.email,
106
+ avatarUrl: result.user.avatarUrl,
107
+ });
108
+ clearPendingAuth();
109
+ return loadCredentials();
110
+ }
111
+ if (result.status === "expired") {
112
+ clearPendingAuth();
113
+ // Fall through to start a fresh session below
114
+ }
115
+ else {
116
+ // Still pending — user hasn't authenticated yet
117
+ printAuthPrompt(pending.authUrl);
118
+ process.exit(0);
119
+ }
120
+ }
121
+ catch {
122
+ // Network error — assume still pending, show URL
123
+ printAuthPrompt(pending.authUrl);
124
+ process.exit(0);
125
+ }
77
126
  }
78
- const { performLogin } = await import("../commands/login.js");
79
- return performLogin();
127
+ // No credentials and no pending session — start a new auth flow
128
+ const { createSession } = await import("../commands/login.js");
129
+ const { getBaseURL } = await import("./api.js");
130
+ const session = await createSession();
131
+ const authUrl = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
132
+ savePendingAuth({
133
+ sessionId: session.sessionId,
134
+ sessionSecret: session.sessionSecret,
135
+ authUrl,
136
+ createdAt: Date.now(),
137
+ });
138
+ await openURL(authUrl);
139
+ printAuthPrompt(authUrl);
140
+ process.exit(0);
141
+ }
142
+ function printAuthPrompt(authUrl) {
143
+ process.stderr.write(`\nAuthenticate with Lizard:\n ${chalk.cyan(authUrl)}\n\nThen run your command again.\n\n`);
80
144
  }
81
145
  /** Open a URL in the default browser, or print it if headless. */
82
146
  export async function openURL(url) {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,UAAU,GAGX,MAAM,aAAa,CAAC;AAIrB,yDAAyD;AACzD,MAAM,UAAU,QAAQ;IACtB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC9D,OAAO,eAAe,EAAE,EAAE,WAAW,IAAI,IAAI,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,EAAE,CAAC,WAAW,IAAI,IAAI,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;IAC3B,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,WAAW,CAAC;IAC1B,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,EAAE,CAAC,WAAW,IAAI,IAAI,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAoB;IAClD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,WAAW,CAAC;IAC1B,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,QAAQ,EAAE,KAAK,IAAI,CAAC;AAC7B,CAAC;AAED,SAAS,KAAK;IACZ,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/E,OAAO,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAkB;IACnC,MAAM,KAAK,GACT,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;QAC9B,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC,aAAa;AACnD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7B,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC7E,CAAC;IAED,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7C,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,IAAI,KAAK,CACnB,KAAK;YACH,CAAC,CAAC,gEAAgE;YAClE,CAAC,CAAC,kEAAkE,CAC3C,CAAC;QAC9B,GAAG,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAC/B,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,kDAAkD;IAClD,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;YAC5E,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACtE,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBAC9C,eAAe,CAAC;oBACd,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;oBAC5D,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE;oBACtB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ;oBAC9B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK;oBACxB,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS;iBACjC,CAAC,CAAC;gBACH,gBAAgB,EAAE,CAAC;gBACnB,OAAO,eAAe,EAAG,CAAC;YAC5B,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,gBAAgB,EAAE,CAAC;gBACnB,8CAA8C;YAChD,CAAC;iBAAM,CAAC;gBACN,gDAAgD;gBAChD,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,iDAAiD;YACjD,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAC/D,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,GAAG,UAAU,EAAE,qBAAqB,OAAO,CAAC,SAAS,EAAE,CAAC;IACxE,eAAe,CAAC;QACd,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kCAAkC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,sCAAsC,CAC5F,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW;IACvC,MAAM,KAAK,GAAG,OAAO,CACnB,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAC5E,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrC,MAAM,SAAS,GACb,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC5B,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO;QACpB,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAE/B,IAAI,KAAK,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -22,8 +22,15 @@ export interface ProjectLink {
22
22
  /** @deprecated use serviceName */
23
23
  appName?: string;
24
24
  }
25
+ export interface PendingAuth {
26
+ sessionId: string;
27
+ sessionSecret: string;
28
+ authUrl: string;
29
+ createdAt: number;
30
+ }
25
31
  export interface Config {
26
32
  credentials?: Credentials;
33
+ pendingAuth?: PendingAuth;
27
34
  projects?: Record<string, ProjectLink>;
28
35
  }
29
36
  export declare function loadConfig(): Config;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export declare const CURRENT_VERSION = "0.3.51";
1
+ export declare const CURRENT_VERSION = "0.3.53";
2
2
  /**
3
3
  * True only when running as the Bun-compiled standalone binary. Under
4
4
  * npm/node, `process.execPath` is the *node* executable — self-update would
@@ -4,7 +4,7 @@ import { Readable } from "node:stream";
4
4
  import { join, dirname } from "node:path";
5
5
  import os from "node:os";
6
6
  import { spawn } from "node:child_process";
7
- export const CURRENT_VERSION = "0.3.51";
7
+ export const CURRENT_VERSION = "0.3.53";
8
8
  const RELEASES_API = "https://api.github.com/repos/lizard-build/lizard-cli/releases/latest";
9
9
  const RELEASE_BASE = "https://github.com/lizard-build/lizard-cli/releases/latest/download";
10
10
  /** Minimum gap between background update checks. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lizard-build/cli",
3
- "version": "0.3.51",
3
+ "version": "0.3.53",
4
4
  "description": "Lizard CLI — deploy and manage apps on Lizard",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,17 +1,15 @@
1
1
  import chalk from "chalk";
2
- import ora from "ora";
3
2
  import { Command } from "commander";
4
3
  import {
5
4
  saveCredentials,
5
+ savePendingAuth,
6
+ clearPendingAuth,
6
7
  openURL,
7
8
  jwtExpiryMs,
8
9
  type Credentials,
9
10
  } from "../lib/auth.js";
10
11
  import { getBaseURL } from "../lib/api.js";
11
- import { success, info, isJSONMode, printJSON } from "../lib/format.js";
12
-
13
- const POLL_INTERVAL = 2000;
14
- const SESSION_TIMEOUT = 300_000; // 5 min
12
+ import { success, isJSONMode, printJSON } from "../lib/format.js";
15
13
 
16
14
  interface SessionResponse {
17
15
  sessionId: string;
@@ -19,10 +17,9 @@ interface SessionResponse {
19
17
  expiresIn: number;
20
18
  }
21
19
 
22
- interface PollResponse {
20
+ export interface CheckResponse {
23
21
  status: "pending" | "complete" | "expired";
24
22
  accessToken?: string;
25
- refreshToken?: string;
26
23
  user?: {
27
24
  id: string;
28
25
  username: string;
@@ -32,100 +29,50 @@ interface PollResponse {
32
29
  }
33
30
 
34
31
  /** Create a CLI login session on the server */
35
- async function createSession(): Promise<SessionResponse> {
32
+ export async function createSession(): Promise<SessionResponse> {
36
33
  const res = await fetch(`${getBaseURL()}/api/auth/cli/session`, {
37
34
  method: "POST",
38
35
  headers: { "Content-Type": "application/json" },
39
36
  });
40
- if (!res.ok) {
41
- throw new Error(`Failed to create login session: ${res.statusText}`);
42
- }
37
+ if (!res.ok) throw new Error(`Failed to create login session: ${res.statusText}`);
43
38
  return res.json() as Promise<SessionResponse>;
44
39
  }
45
40
 
46
- /** Poll the server to check if the user completed login */
47
- async function pollSession(
41
+ /** Check once if the user has completed authentication (no polling loop) */
42
+ export async function checkSession(
48
43
  sessionId: string,
49
44
  sessionSecret: string,
50
- ): Promise<PollResponse> {
45
+ ): Promise<CheckResponse> {
51
46
  const res = await fetch(`${getBaseURL()}/api/auth/cli/poll`, {
52
47
  method: "POST",
53
48
  headers: { "Content-Type": "application/json" },
54
49
  body: JSON.stringify({ sessionId, sessionSecret }),
55
50
  });
56
- if (!res.ok) {
57
- throw new Error(`Poll failed: ${res.statusText}`);
58
- }
59
- return res.json() as Promise<PollResponse>;
51
+ if (!res.ok) throw new Error(`Auth check failed: ${res.statusText}`);
52
+ return res.json() as Promise<CheckResponse>;
60
53
  }
61
54
 
62
55
  /**
63
- * Perform the login flow. Used by the login command and by auto-login in requireAuth.
56
+ * Start the login flow: creates a session, saves it to disk, opens the
57
+ * browser, prints the URL, then exits. The user authenticates in the browser
58
+ * and re-runs their original command — requireAuth will pick up the session.
64
59
  */
65
- export async function performLogin(): Promise<Credentials> {
66
- // 1. Create session
60
+ export async function performLogin(): Promise<never> {
67
61
  const session = await createSession();
68
- const loginURL = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
69
-
70
- // 2. Try to open browser
71
- const opened = await openURL(loginURL);
72
- if (opened) {
73
- info("Opening browser to log in...");
74
- } else {
75
- info(`Open this URL in your browser to log in:\n ${chalk.cyan(loginURL)}`);
76
- }
77
-
78
- // 3. Poll until complete
79
- const spinner = ora("Waiting for login...").start();
80
- const deadline = Date.now() + SESSION_TIMEOUT;
81
-
82
- while (Date.now() < deadline) {
83
- await sleep(POLL_INTERVAL);
84
-
85
- try {
86
- const result = await pollSession(session.sessionId, session.sessionSecret);
62
+ const authUrl = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
87
63
 
88
- if (result.status === "complete" && result.accessToken && result.user) {
89
- spinner.stop();
90
- const expMs = jwtExpiryMs(result.accessToken);
91
- const creds: Credentials = {
92
- accessToken: result.accessToken,
93
- refreshToken: result.refreshToken,
94
- expiresAt: expMs ? new Date(expMs).toISOString() : undefined,
95
- userId: result.user.id,
96
- username: result.user.username,
97
- email: result.user.email,
98
- avatarUrl: result.user.avatarUrl,
99
- };
100
- saveCredentials(creds);
101
-
102
- if (isJSONMode()) {
103
- printJSON({ username: creds.username, email: creds.email });
104
- } else {
105
- success(`Logged in as ${chalk.bold(creds.username)}`);
106
- }
107
- return creds;
108
- }
109
-
110
- if (result.status === "expired") {
111
- spinner.stop();
112
- throw new Error("Login session expired. Please try again.");
113
- }
114
- } catch (err: any) {
115
- if (err.message?.includes("expired")) {
116
- spinner.stop();
117
- throw err;
118
- }
119
- // Network error — keep trying
120
- }
121
- }
122
-
123
- spinner.stop();
124
- throw new Error("Login timed out. Please try again.");
125
- }
64
+ savePendingAuth({
65
+ sessionId: session.sessionId,
66
+ sessionSecret: session.sessionSecret,
67
+ authUrl,
68
+ createdAt: Date.now(),
69
+ });
126
70
 
127
- function sleep(ms: number): Promise<void> {
128
- return new Promise((r) => setTimeout(r, ms));
71
+ await openURL(authUrl);
72
+ process.stderr.write(
73
+ `\nAuthenticate with Lizard:\n ${chalk.cyan(authUrl)}\n\nOnce authenticated, run your command again.\n\n`,
74
+ );
75
+ process.exit(0);
129
76
  }
130
77
 
131
78
  export function registerLogin(program: Command) {
package/src/lib/auth.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import open from "open";
2
+ import chalk from "chalk";
2
3
  import {
3
4
  loadConfig,
4
5
  saveConfig,
5
6
  type Credentials,
7
+ type PendingAuth,
6
8
  } from "./config.js";
7
9
 
8
10
  export type { Credentials } from "./config.js";
@@ -29,6 +31,22 @@ export function clearCredentials() {
29
31
  saveConfig(config);
30
32
  }
31
33
 
34
+ export function loadPendingAuth(): PendingAuth | null {
35
+ return loadConfig().pendingAuth ?? null;
36
+ }
37
+
38
+ export function savePendingAuth(pending: PendingAuth) {
39
+ const config = loadConfig();
40
+ config.pendingAuth = pending;
41
+ saveConfig(config);
42
+ }
43
+
44
+ export function clearPendingAuth() {
45
+ const config = loadConfig();
46
+ delete config.pendingAuth;
47
+ saveConfig(config);
48
+ }
49
+
32
50
  export function isLoggedIn(): boolean {
33
51
  return getToken() !== null;
34
52
  }
@@ -62,17 +80,20 @@ function isExpired(creds: Credentials): boolean {
62
80
  }
63
81
 
64
82
  /**
65
- * Ensure the user is authenticated. If not logged in (or the saved token
66
- * has expired — there is no refresh endpoint) and TTY, auto-login.
67
- * Returns credentials or throws.
83
+ * Ensure the user is authenticated.
84
+ *
85
+ * On first call with no credentials: creates a CLI auth session, saves it to
86
+ * disk, opens the browser, prints the URL, and exits. The user authenticates
87
+ * in the browser, then re-runs their command.
88
+ *
89
+ * On subsequent calls while a session is pending: checks once (no loop) if
90
+ * the user has completed authentication. If yes, stores credentials and
91
+ * returns them. If still pending, prints the URL and exits. If the session
92
+ * expired, starts a fresh one.
68
93
  */
69
94
  export async function requireAuth(): Promise<Credentials> {
70
95
  if (process.env.LIZARD_TOKEN) {
71
- return {
72
- accessToken: process.env.LIZARD_TOKEN,
73
- userId: "",
74
- username: "",
75
- };
96
+ return { accessToken: process.env.LIZARD_TOKEN, userId: "", username: "" };
76
97
  }
77
98
 
78
99
  const creds = loadCredentials();
@@ -88,11 +109,60 @@ export async function requireAuth(): Promise<Credentials> {
88
109
  throw err;
89
110
  }
90
111
 
91
- if (creds) {
92
- process.stderr.write("Session expired logging in again...\n");
112
+ // Check if we already have a pending auth session
113
+ const pending = loadPendingAuth();
114
+ if (pending) {
115
+ const { checkSession } = await import("../commands/login.js");
116
+ try {
117
+ const result = await checkSession(pending.sessionId, pending.sessionSecret);
118
+ if (result.status === "complete" && result.accessToken && result.user) {
119
+ const expMs = jwtExpiryMs(result.accessToken);
120
+ saveCredentials({
121
+ accessToken: result.accessToken,
122
+ expiresAt: expMs ? new Date(expMs).toISOString() : undefined,
123
+ userId: result.user.id,
124
+ username: result.user.username,
125
+ email: result.user.email,
126
+ avatarUrl: result.user.avatarUrl,
127
+ });
128
+ clearPendingAuth();
129
+ return loadCredentials()!;
130
+ }
131
+ if (result.status === "expired") {
132
+ clearPendingAuth();
133
+ // Fall through to start a fresh session below
134
+ } else {
135
+ // Still pending — user hasn't authenticated yet
136
+ printAuthPrompt(pending.authUrl);
137
+ process.exit(0);
138
+ }
139
+ } catch {
140
+ // Network error — assume still pending, show URL
141
+ printAuthPrompt(pending.authUrl);
142
+ process.exit(0);
143
+ }
93
144
  }
94
- const { performLogin } = await import("../commands/login.js");
95
- return performLogin();
145
+
146
+ // No credentials and no pending session — start a new auth flow
147
+ const { createSession } = await import("../commands/login.js");
148
+ const { getBaseURL } = await import("./api.js");
149
+ const session = await createSession();
150
+ const authUrl = `${getBaseURL()}/auth/cli?session=${session.sessionId}`;
151
+ savePendingAuth({
152
+ sessionId: session.sessionId,
153
+ sessionSecret: session.sessionSecret,
154
+ authUrl,
155
+ createdAt: Date.now(),
156
+ });
157
+ await openURL(authUrl);
158
+ printAuthPrompt(authUrl);
159
+ process.exit(0);
160
+ }
161
+
162
+ function printAuthPrompt(authUrl: string) {
163
+ process.stderr.write(
164
+ `\nAuthenticate with Lizard:\n ${chalk.cyan(authUrl)}\n\nThen run your command again.\n\n`,
165
+ );
96
166
  }
97
167
 
98
168
  /** Open a URL in the default browser, or print it if headless. */
package/src/lib/config.ts CHANGED
@@ -41,8 +41,16 @@ export interface ProjectLink {
41
41
  appName?: string;
42
42
  }
43
43
 
44
+ export interface PendingAuth {
45
+ sessionId: string;
46
+ sessionSecret: string;
47
+ authUrl: string;
48
+ createdAt: number;
49
+ }
50
+
44
51
  export interface Config {
45
52
  credentials?: Credentials;
53
+ pendingAuth?: PendingAuth;
46
54
  projects?: Record<string, ProjectLink>;
47
55
  }
48
56
 
@@ -5,7 +5,7 @@ import { join, dirname } from "node:path";
5
5
  import os from "node:os";
6
6
  import { spawn } from "node:child_process";
7
7
 
8
- export const CURRENT_VERSION = "0.3.51";
8
+ export const CURRENT_VERSION = "0.3.53";
9
9
  const RELEASES_API = "https://api.github.com/repos/lizard-build/lizard-cli/releases/latest";
10
10
  const RELEASE_BASE = "https://github.com/lizard-build/lizard-cli/releases/latest/download";
11
11