@skillmarkdown/cli 0.2.0 → 0.2.1
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 +3 -0
- package/dist/commands/login.js +37 -11
- package/dist/lib/auth-config.js +3 -1
- package/dist/lib/auth-defaults.js +1 -0
- package/dist/lib/auth-session.js +6 -0
- package/dist/lib/firebase-auth.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,6 +74,7 @@ By default, `login` uses the project’s built-in development config. You can ov
|
|
|
74
74
|
|
|
75
75
|
- `SKILLMD_GITHUB_CLIENT_ID`
|
|
76
76
|
- `SKILLMD_FIREBASE_API_KEY`
|
|
77
|
+
- `SKILLMD_FIREBASE_PROJECT_ID`
|
|
77
78
|
|
|
78
79
|
See `.env.example` for the expected keys.
|
|
79
80
|
Maintainers: built-in defaults are defined in `src/lib/auth-defaults.ts`.
|
|
@@ -94,6 +95,8 @@ skillmd login --reauth
|
|
|
94
95
|
skillmd logout
|
|
95
96
|
```
|
|
96
97
|
|
|
98
|
+
`skillmd login --status` includes the authenticated Firebase project so you can confirm whether the active session is for `skillmarkdown` or `skillmarkdown-development`.
|
|
99
|
+
|
|
97
100
|
When a saved session exists, `skillmd login` verifies the stored refresh token. If it is invalid/expired, the CLI automatically starts a new login flow. If verification is inconclusive (for example network timeout), the command exits non-zero and keeps the current session.
|
|
98
101
|
|
|
99
102
|
## Development
|
package/dist/commands/login.js
CHANGED
|
@@ -26,16 +26,34 @@ function parseFlags(args) {
|
|
|
26
26
|
}
|
|
27
27
|
return { status, reauth, valid: true };
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function formatSessionProject(session, currentConfigProjectId) {
|
|
30
|
+
if (!session.projectId) {
|
|
31
|
+
if (currentConfigProjectId) {
|
|
32
|
+
return { label: `unknown (current config: ${currentConfigProjectId})`, mismatch: false };
|
|
33
|
+
}
|
|
34
|
+
return { label: "unknown", mismatch: false };
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
label: session.projectId,
|
|
38
|
+
mismatch: Boolean(currentConfigProjectId && session.projectId !== currentConfigProjectId),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function printSessionStatus(session, currentConfigProjectId) {
|
|
30
42
|
if (!session) {
|
|
31
43
|
console.log("Not logged in.");
|
|
32
44
|
return 1;
|
|
33
45
|
}
|
|
46
|
+
const project = formatSessionProject(session, currentConfigProjectId);
|
|
34
47
|
if (session.email) {
|
|
35
|
-
console.log(`Logged in with GitHub as ${session.email}.`);
|
|
36
|
-
|
|
48
|
+
console.log(`Logged in with GitHub as ${session.email} (project: ${project.label}).`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(`Logged in with GitHub (uid: ${session.uid}, project: ${project.label}).`);
|
|
52
|
+
}
|
|
53
|
+
if (project.mismatch && currentConfigProjectId) {
|
|
54
|
+
console.log(`Current CLI config targets project '${currentConfigProjectId}'. ` +
|
|
55
|
+
"Run 'skillmd login --reauth' to switch projects.");
|
|
37
56
|
}
|
|
38
|
-
console.log(`Logged in with GitHub (uid: ${session.uid}).`);
|
|
39
57
|
return 0;
|
|
40
58
|
}
|
|
41
59
|
function requireConfig(env) {
|
|
@@ -57,22 +75,29 @@ async function runLoginCommand(args, options = {}) {
|
|
|
57
75
|
const readSessionFn = options.readSession ?? auth_session_1.readAuthSession;
|
|
58
76
|
const writeSessionFn = options.writeSession ?? auth_session_1.writeAuthSession;
|
|
59
77
|
const clearSessionFn = options.clearSession ?? auth_session_1.clearAuthSession;
|
|
60
|
-
if (status) {
|
|
61
|
-
return printSessionStatus(readSessionFn());
|
|
62
|
-
}
|
|
63
78
|
try {
|
|
64
79
|
const config = requireConfig(options.env ?? process.env);
|
|
80
|
+
if (status) {
|
|
81
|
+
return printSessionStatus(readSessionFn(), config.firebaseProjectId);
|
|
82
|
+
}
|
|
65
83
|
const existingSession = readSessionFn();
|
|
66
84
|
if (existingSession && !reauth) {
|
|
67
85
|
const verifyRefreshTokenFn = options.verifyRefreshToken ?? firebase_auth_1.verifyFirebaseRefreshToken;
|
|
68
86
|
try {
|
|
69
87
|
const validation = await verifyRefreshTokenFn(config.firebaseApiKey, existingSession.refreshToken);
|
|
70
88
|
if (validation.valid) {
|
|
89
|
+
const project = formatSessionProject(existingSession, config.firebaseProjectId);
|
|
71
90
|
if (existingSession.email) {
|
|
72
|
-
console.log(`Already logged in as ${existingSession.email}
|
|
91
|
+
console.log(`Already logged in as ${existingSession.email} (project: ${project.label}). ` +
|
|
92
|
+
"Run 'skillmd logout' first.");
|
|
73
93
|
}
|
|
74
94
|
else {
|
|
75
|
-
console.log(
|
|
95
|
+
console.log(`Already logged in (uid: ${existingSession.uid}, project: ${project.label}). ` +
|
|
96
|
+
"Run 'skillmd logout' first.");
|
|
97
|
+
}
|
|
98
|
+
if (project.mismatch) {
|
|
99
|
+
console.log(`Current CLI config targets project '${config.firebaseProjectId}'. ` +
|
|
100
|
+
"Run 'skillmd login --reauth' to switch projects.");
|
|
76
101
|
}
|
|
77
102
|
return 0;
|
|
78
103
|
}
|
|
@@ -100,12 +125,13 @@ async function runLoginCommand(args, options = {}) {
|
|
|
100
125
|
uid: firebaseSession.localId,
|
|
101
126
|
email: firebaseSession.email,
|
|
102
127
|
refreshToken: firebaseSession.refreshToken,
|
|
128
|
+
projectId: config.firebaseProjectId,
|
|
103
129
|
});
|
|
104
130
|
if (firebaseSession.email) {
|
|
105
|
-
console.log(`Login successful. Signed in as ${firebaseSession.email}.`);
|
|
131
|
+
console.log(`Login successful. Signed in as ${firebaseSession.email} (project: ${config.firebaseProjectId}).`);
|
|
106
132
|
}
|
|
107
133
|
else {
|
|
108
|
-
console.log(
|
|
134
|
+
console.log(`Login successful (project: ${config.firebaseProjectId}).`);
|
|
109
135
|
}
|
|
110
136
|
return 0;
|
|
111
137
|
}
|
package/dist/lib/auth-config.js
CHANGED
|
@@ -54,12 +54,14 @@ function getLoginEnvConfig(env = process.env, options = {}) {
|
|
|
54
54
|
const dotEnv = loadDotEnv(getDefaultUserEnvPath(options));
|
|
55
55
|
const githubClientId = pickValue(env.SKILLMD_GITHUB_CLIENT_ID, dotEnv.SKILLMD_GITHUB_CLIENT_ID, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.githubClientId);
|
|
56
56
|
const firebaseApiKey = pickValue(env.SKILLMD_FIREBASE_API_KEY, dotEnv.SKILLMD_FIREBASE_API_KEY, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseApiKey);
|
|
57
|
-
|
|
57
|
+
const firebaseProjectId = pickValue(env.SKILLMD_FIREBASE_PROJECT_ID, dotEnv.SKILLMD_FIREBASE_PROJECT_ID, auth_defaults_1.DEFAULT_LOGIN_AUTH_CONFIG.firebaseProjectId);
|
|
58
|
+
if (!githubClientId || !firebaseApiKey || !firebaseProjectId) {
|
|
58
59
|
throw new Error("missing login configuration");
|
|
59
60
|
}
|
|
60
61
|
return {
|
|
61
62
|
githubClientId,
|
|
62
63
|
firebaseApiKey,
|
|
64
|
+
firebaseProjectId,
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
function getDefaultUserEnvPath(options = {}) {
|
package/dist/lib/auth-session.js
CHANGED
|
@@ -28,6 +28,12 @@ function readAuthSession(sessionPath = SESSION_PATH) {
|
|
|
28
28
|
if (parsed.email !== undefined && typeof parsed.email !== "string") {
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
31
|
+
if (parsed.projectId !== undefined && typeof parsed.projectId !== "string") {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (typeof parsed.projectId === "string" && parsed.projectId.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
31
37
|
return parsed;
|
|
32
38
|
}
|
|
33
39
|
catch {
|
|
@@ -60,7 +60,9 @@ async function verifyFirebaseRefreshToken(apiKey, refreshToken) {
|
|
|
60
60
|
}
|
|
61
61
|
const errorMessage = payload.error?.message ?? "";
|
|
62
62
|
if (response.status === 400 &&
|
|
63
|
-
(errorMessage === "INVALID_REFRESH_TOKEN" ||
|
|
63
|
+
(errorMessage === "INVALID_REFRESH_TOKEN" ||
|
|
64
|
+
errorMessage === "TOKEN_EXPIRED" ||
|
|
65
|
+
errorMessage === "PROJECT_NUMBER_MISMATCH")) {
|
|
64
66
|
return { valid: false };
|
|
65
67
|
}
|
|
66
68
|
throw new Error(`Firebase token verification failed (${response.status}): ${errorMessage || "unknown error"}`);
|