@lizard-build/cli 0.3.52 → 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.
- package/dist/commands/login.d.ts +24 -3
- package/dist/commands/login.js +21 -70
- package/dist/commands/login.js.map +1 -1
- package/dist/lib/auth.d.ts +14 -4
- package/dist/lib/auth.js +76 -12
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/updater.d.ts +1 -1
- package/dist/lib/updater.js +1 -1
- package/package.json +1 -1
- package/src/commands/login.ts +27 -80
- package/src/lib/auth.ts +82 -12
- package/src/lib/config.ts +8 -0
- package/src/lib/updater.ts +1 -1
package/dist/commands/login.d.ts
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
|
|
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
|
-
*
|
|
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<
|
|
26
|
+
export declare function performLogin(): Promise<never>;
|
|
7
27
|
export declare function registerLogin(program: Command): void;
|
|
28
|
+
export {};
|
package/dist/commands/login.js
CHANGED
|
@@ -1,94 +1,45 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
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,
|
|
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
|
-
/**
|
|
20
|
-
async function
|
|
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(`
|
|
28
|
-
}
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
throw new Error(`Auth check failed: ${res.statusText}`);
|
|
29
24
|
return res.json();
|
|
30
25
|
}
|
|
31
26
|
/**
|
|
32
|
-
*
|
|
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
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
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":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -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.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
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
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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) {
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EACL,UAAU,EACV,UAAU,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC;AAE1C,4EAA4E;AAC5E,8EAA8E;AAC9E,yEAAyE;AACzE,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW;QAC5B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC;QAC/C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AACzC,CAAC;AACD,SAAS,UAAU;IACjB,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/updater.d.ts
CHANGED
package/dist/lib/updater.js
CHANGED
|
@@ -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.
|
|
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
package/src/commands/login.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
/**
|
|
47
|
-
async function
|
|
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<
|
|
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
|
-
|
|
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
|
-
*
|
|
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<
|
|
66
|
-
// 1. Create session
|
|
60
|
+
export async function performLogin(): Promise<never> {
|
|
67
61
|
const session = await createSession();
|
|
68
|
-
const
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
128
|
-
|
|
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.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
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
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
|
package/src/lib/updater.ts
CHANGED
|
@@ -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.
|
|
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
|
|