@kylewadegrove/cutline-mcp-cli 0.4.0 → 0.4.2
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.js +16 -34
- package/dist/commands/status.js +27 -39
- package/dist/utils/config-store.js +3 -0
- package/package.json +1 -1
- package/src/commands/login.ts +18 -40
- package/src/commands/status.ts +29 -44
- package/src/utils/config-store.ts +3 -0
package/dist/commands/login.js
CHANGED
|
@@ -7,37 +7,25 @@ exports.loginCommand = loginCommand;
|
|
|
7
7
|
const open_1 = __importDefault(require("open"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const ora_1 = __importDefault(require("ora"));
|
|
10
|
-
const firebase_admin_1 = __importDefault(require("firebase-admin"));
|
|
11
10
|
const callback_js_1 = require("../auth/callback.js");
|
|
12
11
|
const keychain_js_1 = require("../auth/keychain.js");
|
|
13
12
|
const config_store_js_1 = require("../utils/config-store.js");
|
|
14
13
|
const config_js_1 = require("../utils/config.js");
|
|
15
|
-
async function getSubscriptionStatus(
|
|
14
|
+
async function getSubscriptionStatus(idToken, isStaging) {
|
|
16
15
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
const status = subscription.status || 'unknown';
|
|
32
|
-
// Get plan name from price ID or default
|
|
33
|
-
let planName = 'Premium';
|
|
34
|
-
if (subscription.priceId?.includes('yearly') || subscription.interval === 'year') {
|
|
35
|
-
planName = 'Premium (Yearly)';
|
|
36
|
-
}
|
|
37
|
-
else if (subscription.priceId?.includes('monthly') || subscription.interval === 'month') {
|
|
38
|
-
planName = 'Premium (Monthly)';
|
|
39
|
-
}
|
|
40
|
-
return { status, planName };
|
|
16
|
+
const baseUrl = isStaging
|
|
17
|
+
? 'https://us-central1-cutline-staging.cloudfunctions.net'
|
|
18
|
+
: 'https://us-central1-cutline-prod.cloudfunctions.net';
|
|
19
|
+
const response = await fetch(`${baseUrl}/mcpSubscriptionStatus`, {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
'Authorization': `Bearer ${idToken}`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
return { status: 'unknown' };
|
|
27
|
+
}
|
|
28
|
+
return await response.json();
|
|
41
29
|
}
|
|
42
30
|
catch (error) {
|
|
43
31
|
// Silently fail - subscription check is optional during login
|
|
@@ -135,7 +123,7 @@ async function loginCommand(options) {
|
|
|
135
123
|
catch (error) {
|
|
136
124
|
console.warn(chalk_1.default.yellow(' ⚠️ Could not save to Keychain (skipping)'));
|
|
137
125
|
}
|
|
138
|
-
// Save to file config (cross-platform)
|
|
126
|
+
// Save to file config (cross-platform)
|
|
139
127
|
try {
|
|
140
128
|
(0, config_store_js_1.saveConfig)({
|
|
141
129
|
refreshToken,
|
|
@@ -155,14 +143,8 @@ async function loginCommand(options) {
|
|
|
155
143
|
// Check subscription status
|
|
156
144
|
try {
|
|
157
145
|
spinner.start('Checking subscription...');
|
|
158
|
-
const projectId = options.staging ? 'cutline-staging' : 'cutline-prod';
|
|
159
146
|
const idToken = await exchangeRefreshForIdToken(refreshToken, firebaseApiKey);
|
|
160
|
-
|
|
161
|
-
if (firebase_admin_1.default.apps.length === 0) {
|
|
162
|
-
firebase_admin_1.default.initializeApp({ projectId });
|
|
163
|
-
}
|
|
164
|
-
const decoded = await firebase_admin_1.default.auth().verifyIdToken(idToken);
|
|
165
|
-
const subscription = await getSubscriptionStatus(decoded.uid, projectId);
|
|
147
|
+
const subscription = await getSubscriptionStatus(idToken, !!options.staging);
|
|
166
148
|
spinner.stop();
|
|
167
149
|
if (subscription.status === 'active' || subscription.status === 'trialing') {
|
|
168
150
|
const statusLabel = subscription.status === 'trialing' ? ' (trial)' : '';
|
package/dist/commands/status.js
CHANGED
|
@@ -6,34 +6,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.statusCommand = statusCommand;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const ora_1 = __importDefault(require("ora"));
|
|
9
|
-
const firebase_admin_1 = __importDefault(require("firebase-admin"));
|
|
10
9
|
const keychain_js_1 = require("../auth/keychain.js");
|
|
11
10
|
const config_js_1 = require("../utils/config.js");
|
|
12
|
-
async function getSubscriptionStatus(
|
|
11
|
+
async function getSubscriptionStatus(idToken, isStaging) {
|
|
13
12
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
: undefined;
|
|
28
|
-
// Get plan name from price ID or default
|
|
29
|
-
let planName = 'Premium';
|
|
30
|
-
if (subscription.priceId?.includes('yearly') || subscription.interval === 'year') {
|
|
31
|
-
planName = 'Premium (Yearly)';
|
|
32
|
-
}
|
|
33
|
-
else if (subscription.priceId?.includes('monthly') || subscription.interval === 'month') {
|
|
34
|
-
planName = 'Premium (Monthly)';
|
|
35
|
-
}
|
|
36
|
-
return { status, planName, periodEnd };
|
|
13
|
+
const baseUrl = isStaging
|
|
14
|
+
? 'https://us-central1-cutline-staging.cloudfunctions.net'
|
|
15
|
+
: 'https://us-central1-cutline-prod.cloudfunctions.net';
|
|
16
|
+
const response = await fetch(`${baseUrl}/mcpSubscriptionStatus`, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'Authorization': `Bearer ${idToken}`,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
return { status: 'unknown' };
|
|
24
|
+
}
|
|
25
|
+
return await response.json();
|
|
37
26
|
}
|
|
38
27
|
catch (error) {
|
|
39
28
|
console.error('Error fetching subscription:', error);
|
|
@@ -89,16 +78,12 @@ async function statusCommand(options) {
|
|
|
89
78
|
// Exchange refresh token for ID token
|
|
90
79
|
spinner.text = 'Verifying credentials...';
|
|
91
80
|
const idToken = await exchangeRefreshToken(refreshToken, firebaseApiKey);
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
firebase_admin_1.default.initializeApp({ projectId });
|
|
96
|
-
}
|
|
97
|
-
// Verify the ID token
|
|
98
|
-
const decoded = await firebase_admin_1.default.auth().verifyIdToken(idToken);
|
|
81
|
+
// Decode JWT payload (base64) to get user info - no verification needed, just display
|
|
82
|
+
const payloadBase64 = idToken.split('.')[1];
|
|
83
|
+
const decoded = JSON.parse(Buffer.from(payloadBase64, 'base64').toString());
|
|
99
84
|
spinner.succeed(chalk_1.default.green('Authenticated'));
|
|
100
|
-
console.log(chalk_1.default.gray(' User:'), chalk_1.default.white(decoded.email || decoded.
|
|
101
|
-
console.log(chalk_1.default.gray(' UID:'), chalk_1.default.dim(decoded.
|
|
85
|
+
console.log(chalk_1.default.gray(' User:'), chalk_1.default.white(decoded.email || decoded.user_id || decoded.sub));
|
|
86
|
+
console.log(chalk_1.default.gray(' UID:'), chalk_1.default.dim(decoded.user_id || decoded.sub));
|
|
102
87
|
// Calculate token expiry
|
|
103
88
|
const expiresIn = Math.floor((decoded.exp * 1000 - Date.now()) / 1000 / 60);
|
|
104
89
|
console.log(chalk_1.default.gray(' Token expires in:'), chalk_1.default.white(`${expiresIn} minutes`));
|
|
@@ -109,14 +94,17 @@ async function statusCommand(options) {
|
|
|
109
94
|
if (decoded.deviceId) {
|
|
110
95
|
console.log(chalk_1.default.gray(' Device ID:'), chalk_1.default.dim(decoded.deviceId));
|
|
111
96
|
}
|
|
112
|
-
// Check subscription status
|
|
113
|
-
|
|
97
|
+
// Check subscription status via Cloud Function
|
|
98
|
+
spinner.start('Checking subscription...');
|
|
99
|
+
const subscription = await getSubscriptionStatus(idToken, !!options.staging);
|
|
100
|
+
spinner.stop();
|
|
114
101
|
if (subscription.status === 'active' || subscription.status === 'trialing') {
|
|
115
102
|
const statusLabel = subscription.status === 'trialing' ? ' (trial)' : '';
|
|
116
103
|
console.log(chalk_1.default.gray(' Plan:'), chalk_1.default.green(`✓ ${subscription.planName || 'Premium'}${statusLabel}`));
|
|
117
104
|
if (subscription.periodEnd) {
|
|
118
|
-
const
|
|
119
|
-
|
|
105
|
+
const periodEndDate = new Date(subscription.periodEnd);
|
|
106
|
+
const daysLeft = Math.ceil((periodEndDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
107
|
+
console.log(chalk_1.default.gray(' Renews:'), chalk_1.default.white(`${periodEndDate.toLocaleDateString()} (${daysLeft} days)`));
|
|
120
108
|
}
|
|
121
109
|
}
|
|
122
110
|
else if (subscription.status === 'past_due') {
|
|
@@ -125,7 +113,7 @@ async function statusCommand(options) {
|
|
|
125
113
|
else if (subscription.status === 'canceled') {
|
|
126
114
|
console.log(chalk_1.default.gray(' Plan:'), chalk_1.default.yellow('Premium (canceled)'));
|
|
127
115
|
if (subscription.periodEnd) {
|
|
128
|
-
console.log(chalk_1.default.gray(' Access until:'), chalk_1.default.white(subscription.periodEnd.toLocaleDateString()));
|
|
116
|
+
console.log(chalk_1.default.gray(' Access until:'), chalk_1.default.white(new Date(subscription.periodEnd).toLocaleDateString()));
|
|
129
117
|
}
|
|
130
118
|
}
|
|
131
119
|
else {
|
|
@@ -20,6 +20,9 @@ function saveConfig(config) {
|
|
|
20
20
|
ensureConfigDir();
|
|
21
21
|
const current = loadConfig();
|
|
22
22
|
const newConfig = { ...current, ...config };
|
|
23
|
+
// Remove legacy fields that are no longer stored (e.g. firebaseApiKey)
|
|
24
|
+
// to prevent stale values from causing cross-project auth mismatches
|
|
25
|
+
delete newConfig.firebaseApiKey;
|
|
23
26
|
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
|
|
24
27
|
}
|
|
25
28
|
function loadConfig() {
|
package/package.json
CHANGED
package/src/commands/login.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import open from 'open';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import admin from 'firebase-admin';
|
|
5
4
|
import { startCallbackServer } from '../auth/callback.js';
|
|
6
5
|
import { storeRefreshToken } from '../auth/keychain.js';
|
|
7
6
|
import { saveConfig } from '../utils/config-store.js';
|
|
@@ -10,40 +9,27 @@ import { getConfig, fetchFirebaseApiKey } from '../utils/config.js';
|
|
|
10
9
|
interface SubscriptionInfo {
|
|
11
10
|
status: 'free' | 'active' | 'trialing' | 'past_due' | 'canceled' | 'unknown';
|
|
12
11
|
planName?: string;
|
|
12
|
+
periodEnd?: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
async function getSubscriptionStatus(
|
|
15
|
+
async function getSubscriptionStatus(idToken: string, isStaging: boolean): Promise<SubscriptionInfo> {
|
|
16
16
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const db = admin.firestore();
|
|
23
|
-
const userDoc = await db.collection('users').doc(uid).get();
|
|
17
|
+
const baseUrl = isStaging
|
|
18
|
+
? 'https://us-central1-cutline-staging.cloudfunctions.net'
|
|
19
|
+
: 'https://us-central1-cutline-prod.cloudfunctions.net';
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const response = await fetch(`${baseUrl}/mcpSubscriptionStatus`, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
headers: {
|
|
24
|
+
'Authorization': `Bearer ${idToken}`,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
return { status: 'unknown' };
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const subscription = userData?.subscription;
|
|
31
|
-
|
|
32
|
-
if (!subscription) {
|
|
33
|
-
return { status: 'free' };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const status = subscription.status || 'unknown';
|
|
37
|
-
|
|
38
|
-
// Get plan name from price ID or default
|
|
39
|
-
let planName = 'Premium';
|
|
40
|
-
if (subscription.priceId?.includes('yearly') || subscription.interval === 'year') {
|
|
41
|
-
planName = 'Premium (Yearly)';
|
|
42
|
-
} else if (subscription.priceId?.includes('monthly') || subscription.interval === 'month') {
|
|
43
|
-
planName = 'Premium (Monthly)';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return { status, planName };
|
|
31
|
+
|
|
32
|
+
return await response.json();
|
|
47
33
|
} catch (error) {
|
|
48
34
|
// Silently fail - subscription check is optional during login
|
|
49
35
|
return { status: 'unknown' };
|
|
@@ -162,7 +148,7 @@ export async function loginCommand(options: { staging?: boolean; signup?: boolea
|
|
|
162
148
|
console.warn(chalk.yellow(' ⚠️ Could not save to Keychain (skipping)'));
|
|
163
149
|
}
|
|
164
150
|
|
|
165
|
-
// Save to file config (cross-platform)
|
|
151
|
+
// Save to file config (cross-platform)
|
|
166
152
|
try {
|
|
167
153
|
saveConfig({
|
|
168
154
|
refreshToken,
|
|
@@ -185,16 +171,8 @@ export async function loginCommand(options: { staging?: boolean; signup?: boolea
|
|
|
185
171
|
// Check subscription status
|
|
186
172
|
try {
|
|
187
173
|
spinner.start('Checking subscription...');
|
|
188
|
-
const projectId = options.staging ? 'cutline-staging' : 'cutline-prod';
|
|
189
174
|
const idToken = await exchangeRefreshForIdToken(refreshToken, firebaseApiKey);
|
|
190
|
-
|
|
191
|
-
// Initialize Firebase Admin if needed
|
|
192
|
-
if (admin.apps.length === 0) {
|
|
193
|
-
admin.initializeApp({ projectId });
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const decoded = await admin.auth().verifyIdToken(idToken);
|
|
197
|
-
const subscription = await getSubscriptionStatus(decoded.uid, projectId);
|
|
175
|
+
const subscription = await getSubscriptionStatus(idToken, !!options.staging);
|
|
198
176
|
spinner.stop();
|
|
199
177
|
|
|
200
178
|
if (subscription.status === 'active' || subscription.status === 'trialing') {
|
package/src/commands/status.ts
CHANGED
|
@@ -1,45 +1,32 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import admin from 'firebase-admin';
|
|
4
3
|
import { getRefreshToken } from '../auth/keychain.js';
|
|
5
4
|
import { fetchFirebaseApiKey } from '../utils/config.js';
|
|
6
5
|
|
|
7
6
|
interface SubscriptionInfo {
|
|
8
7
|
status: 'free' | 'active' | 'trialing' | 'past_due' | 'canceled' | 'unknown';
|
|
9
8
|
planName?: string;
|
|
10
|
-
periodEnd?:
|
|
9
|
+
periodEnd?: string;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
async function getSubscriptionStatus(
|
|
12
|
+
async function getSubscriptionStatus(idToken: string, isStaging: boolean): Promise<SubscriptionInfo> {
|
|
14
13
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
14
|
+
const baseUrl = isStaging
|
|
15
|
+
? 'https://us-central1-cutline-staging.cloudfunctions.net'
|
|
16
|
+
: 'https://us-central1-cutline-prod.cloudfunctions.net';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const response = await fetch(`${baseUrl}/mcpSubscriptionStatus`, {
|
|
19
|
+
method: 'GET',
|
|
20
|
+
headers: {
|
|
21
|
+
'Authorization': `Bearer ${idToken}`,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
return { status: 'unknown' };
|
|
20
27
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const subscription = userData?.subscription;
|
|
24
|
-
|
|
25
|
-
if (!subscription) {
|
|
26
|
-
return { status: 'free' };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const status = subscription.status || 'unknown';
|
|
30
|
-
const periodEnd = subscription.periodEndsAt
|
|
31
|
-
? new Date(subscription.periodEndsAt)
|
|
32
|
-
: undefined;
|
|
33
|
-
|
|
34
|
-
// Get plan name from price ID or default
|
|
35
|
-
let planName = 'Premium';
|
|
36
|
-
if (subscription.priceId?.includes('yearly') || subscription.interval === 'year') {
|
|
37
|
-
planName = 'Premium (Yearly)';
|
|
38
|
-
} else if (subscription.priceId?.includes('monthly') || subscription.interval === 'month') {
|
|
39
|
-
planName = 'Premium (Monthly)';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return { status, planName, periodEnd };
|
|
28
|
+
|
|
29
|
+
return await response.json();
|
|
43
30
|
} catch (error) {
|
|
44
31
|
console.error('Error fetching subscription:', error);
|
|
45
32
|
return { status: 'unknown' };
|
|
@@ -105,19 +92,14 @@ export async function statusCommand(options: { staging?: boolean }) {
|
|
|
105
92
|
spinner.text = 'Verifying credentials...';
|
|
106
93
|
const idToken = await exchangeRefreshToken(refreshToken, firebaseApiKey);
|
|
107
94
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
admin.initializeApp({ projectId });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Verify the ID token
|
|
115
|
-
const decoded = await admin.auth().verifyIdToken(idToken);
|
|
95
|
+
// Decode JWT payload (base64) to get user info - no verification needed, just display
|
|
96
|
+
const payloadBase64 = idToken.split('.')[1];
|
|
97
|
+
const decoded = JSON.parse(Buffer.from(payloadBase64, 'base64').toString());
|
|
116
98
|
|
|
117
99
|
spinner.succeed(chalk.green('Authenticated'));
|
|
118
100
|
|
|
119
|
-
console.log(chalk.gray(' User:'), chalk.white(decoded.email || decoded.
|
|
120
|
-
console.log(chalk.gray(' UID:'), chalk.dim(decoded.
|
|
101
|
+
console.log(chalk.gray(' User:'), chalk.white(decoded.email || decoded.user_id || decoded.sub));
|
|
102
|
+
console.log(chalk.gray(' UID:'), chalk.dim(decoded.user_id || decoded.sub));
|
|
121
103
|
|
|
122
104
|
// Calculate token expiry
|
|
123
105
|
const expiresIn = Math.floor((decoded.exp * 1000 - Date.now()) / 1000 / 60);
|
|
@@ -131,22 +113,25 @@ export async function statusCommand(options: { staging?: boolean }) {
|
|
|
131
113
|
console.log(chalk.gray(' Device ID:'), chalk.dim(decoded.deviceId));
|
|
132
114
|
}
|
|
133
115
|
|
|
134
|
-
// Check subscription status
|
|
135
|
-
|
|
116
|
+
// Check subscription status via Cloud Function
|
|
117
|
+
spinner.start('Checking subscription...');
|
|
118
|
+
const subscription = await getSubscriptionStatus(idToken, !!options.staging);
|
|
119
|
+
spinner.stop();
|
|
136
120
|
|
|
137
121
|
if (subscription.status === 'active' || subscription.status === 'trialing') {
|
|
138
122
|
const statusLabel = subscription.status === 'trialing' ? ' (trial)' : '';
|
|
139
123
|
console.log(chalk.gray(' Plan:'), chalk.green(`✓ ${subscription.planName || 'Premium'}${statusLabel}`));
|
|
140
124
|
if (subscription.periodEnd) {
|
|
141
|
-
const
|
|
142
|
-
|
|
125
|
+
const periodEndDate = new Date(subscription.periodEnd);
|
|
126
|
+
const daysLeft = Math.ceil((periodEndDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
127
|
+
console.log(chalk.gray(' Renews:'), chalk.white(`${periodEndDate.toLocaleDateString()} (${daysLeft} days)`));
|
|
143
128
|
}
|
|
144
129
|
} else if (subscription.status === 'past_due') {
|
|
145
130
|
console.log(chalk.gray(' Plan:'), chalk.yellow('⚠ Premium (payment past due)'));
|
|
146
131
|
} else if (subscription.status === 'canceled') {
|
|
147
132
|
console.log(chalk.gray(' Plan:'), chalk.yellow('Premium (canceled)'));
|
|
148
133
|
if (subscription.periodEnd) {
|
|
149
|
-
console.log(chalk.gray(' Access until:'), chalk.white(subscription.periodEnd.toLocaleDateString()));
|
|
134
|
+
console.log(chalk.gray(' Access until:'), chalk.white(new Date(subscription.periodEnd).toLocaleDateString()));
|
|
150
135
|
}
|
|
151
136
|
} else {
|
|
152
137
|
console.log(chalk.gray(' Plan:'), chalk.white('Free'));
|
|
@@ -22,6 +22,9 @@ export function saveConfig(config: McpConfig) {
|
|
|
22
22
|
ensureConfigDir();
|
|
23
23
|
const current = loadConfig();
|
|
24
24
|
const newConfig = { ...current, ...config };
|
|
25
|
+
// Remove legacy fields that are no longer stored (e.g. firebaseApiKey)
|
|
26
|
+
// to prevent stale values from causing cross-project auth mismatches
|
|
27
|
+
delete (newConfig as any).firebaseApiKey;
|
|
25
28
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
|
|
26
29
|
}
|
|
27
30
|
|