ai-agent-config 2.6.0 → 2.6.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/package.json +1 -1
- package/scripts/secret-manager.js +111 -44
package/package.json
CHANGED
|
@@ -9,7 +9,6 @@ const { execSync, spawnSync } = require("child_process");
|
|
|
9
9
|
const os = require("os");
|
|
10
10
|
|
|
11
11
|
const HOME = os.homedir();
|
|
12
|
-
const BITWARDEN_FOLDER = "MCP Secrets";
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Validate that Bitwarden CLI is installed
|
|
@@ -152,6 +151,49 @@ function unlockBitwarden(password) {
|
|
|
152
151
|
}
|
|
153
152
|
}
|
|
154
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Try to reuse BW_SESSION from Antigravity MCP config
|
|
156
|
+
* This prevents creating a new session that would invalidate the MCP server's session
|
|
157
|
+
* @returns {{ success: boolean, sessionKey?: string, reason?: string }}
|
|
158
|
+
*/
|
|
159
|
+
function tryReuseAntigravitySession() {
|
|
160
|
+
const platforms = require("./platforms");
|
|
161
|
+
const antigravity = platforms.getByName("antigravity");
|
|
162
|
+
|
|
163
|
+
if (!antigravity || !antigravity.mcpConfigPath) {
|
|
164
|
+
return { success: false, reason: "Antigravity not configured" };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!fs.existsSync(antigravity.mcpConfigPath)) {
|
|
168
|
+
return { success: false, reason: "mcp_config.json not found" };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const mcpConfig = JSON.parse(fs.readFileSync(antigravity.mcpConfigPath, "utf-8"));
|
|
173
|
+
const bitwardenServer = mcpConfig.mcpServers?.bitwarden;
|
|
174
|
+
|
|
175
|
+
if (!bitwardenServer || !bitwardenServer.env || !bitwardenServer.env.BW_SESSION) {
|
|
176
|
+
return { success: false, reason: "No BW_SESSION in bitwarden MCP config" };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const sessionKey = bitwardenServer.env.BW_SESSION;
|
|
180
|
+
|
|
181
|
+
// Validate session by trying to list folders
|
|
182
|
+
const testResult = spawnSync("bw", ["list", "folders", "--session", sessionKey], {
|
|
183
|
+
encoding: "utf-8",
|
|
184
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (testResult.status === 0) {
|
|
188
|
+
return { success: true, sessionKey };
|
|
189
|
+
} else {
|
|
190
|
+
return { success: false, reason: "Session expired or invalid" };
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
return { success: false, reason: `Failed to read config: ${error.message}` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
155
197
|
/**
|
|
156
198
|
* Discover required secrets from MCP server configs in the repo
|
|
157
199
|
* Scans .agent/mcp-servers/{name}/config.json for bitwardenEnv fields
|
|
@@ -176,7 +218,7 @@ function discoverRequiredSecrets() {
|
|
|
176
218
|
|
|
177
219
|
/**
|
|
178
220
|
* Fetch secrets from Bitwarden vault
|
|
179
|
-
*
|
|
221
|
+
* Searches entire vault (all folders) for matching items
|
|
180
222
|
*/
|
|
181
223
|
function fetchSecretsFromBitwarden(sessionKey, secretNames) {
|
|
182
224
|
const results = {
|
|
@@ -185,38 +227,47 @@ function fetchSecretsFromBitwarden(sessionKey, secretNames) {
|
|
|
185
227
|
};
|
|
186
228
|
|
|
187
229
|
try {
|
|
188
|
-
//
|
|
189
|
-
const
|
|
190
|
-
encoding: "utf-8",
|
|
191
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
192
|
-
});
|
|
193
|
-
const folders = JSON.parse(foldersJson);
|
|
194
|
-
const mcpFolder = folders.find((f) => f.name === BITWARDEN_FOLDER);
|
|
195
|
-
|
|
196
|
-
if (!mcpFolder) {
|
|
197
|
-
console.warn(`\n⚠️ Folder "${BITWARDEN_FOLDER}" not found in Bitwarden vault`);
|
|
198
|
-
console.warn(` Create folder "${BITWARDEN_FOLDER}" and add your secrets there\n`);
|
|
199
|
-
// All secrets are missing since folder doesn't exist
|
|
200
|
-
secretNames.forEach((name) => results.missing.push(name));
|
|
201
|
-
return results;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Step 2: List all items in "MCP Secrets" folder
|
|
205
|
-
const itemsJson = execSync(`bw list items --folderid ${mcpFolder.id} --session ${sessionKey}`, {
|
|
230
|
+
// List all items in the vault (across all folders)
|
|
231
|
+
const itemsJson = execSync(`bw list items --session ${sessionKey}`, {
|
|
206
232
|
encoding: "utf-8",
|
|
207
233
|
stdio: ["pipe", "pipe", "pipe"],
|
|
208
234
|
});
|
|
209
235
|
const items = JSON.parse(itemsJson);
|
|
210
236
|
|
|
211
|
-
//
|
|
237
|
+
// Match secrets by name
|
|
212
238
|
for (const secretName of secretNames) {
|
|
213
239
|
const item = items.find((i) => i.name === secretName);
|
|
214
240
|
|
|
215
|
-
if (item
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
241
|
+
if (item) {
|
|
242
|
+
// Try multiple sources for the secret value
|
|
243
|
+
let secretValue = null;
|
|
244
|
+
|
|
245
|
+
// Priority 1: Login password (most common for API keys)
|
|
246
|
+
if (item.login && item.login.password) {
|
|
247
|
+
secretValue = item.login.password;
|
|
248
|
+
}
|
|
249
|
+
// Priority 2: Secure note content
|
|
250
|
+
else if (item.notes && item.notes.trim()) {
|
|
251
|
+
secretValue = item.notes.trim();
|
|
252
|
+
}
|
|
253
|
+
// Priority 3: Custom field named "value" or "secret"
|
|
254
|
+
else if (item.fields && item.fields.length > 0) {
|
|
255
|
+
const valueField = item.fields.find(
|
|
256
|
+
(f) => f.name.toLowerCase() === "value" || f.name.toLowerCase() === "secret"
|
|
257
|
+
);
|
|
258
|
+
if (valueField && valueField.value) {
|
|
259
|
+
secretValue = valueField.value;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (secretValue) {
|
|
264
|
+
results.found.push({
|
|
265
|
+
name: secretName,
|
|
266
|
+
value: secretValue,
|
|
267
|
+
});
|
|
268
|
+
} else {
|
|
269
|
+
results.missing.push(secretName);
|
|
270
|
+
}
|
|
220
271
|
} else {
|
|
221
272
|
results.missing.push(secretName);
|
|
222
273
|
}
|
|
@@ -291,6 +342,7 @@ function writeToShellProfile(secrets) {
|
|
|
291
342
|
*/
|
|
292
343
|
async function syncSecrets() {
|
|
293
344
|
let sessionKey = null;
|
|
345
|
+
let sessionSource = null; // Track where session came from: "reused" or "new"
|
|
294
346
|
|
|
295
347
|
try {
|
|
296
348
|
console.log("\n🔐 Bitwarden Secret Sync\n");
|
|
@@ -309,22 +361,36 @@ async function syncSecrets() {
|
|
|
309
361
|
process.exit(1);
|
|
310
362
|
}
|
|
311
363
|
|
|
312
|
-
// 3.
|
|
313
|
-
|
|
364
|
+
// 3. Try to reuse existing session from Antigravity MCP
|
|
365
|
+
console.log("🔄 Checking for existing Bitwarden session...");
|
|
366
|
+
const reuseResult = tryReuseAntigravitySession();
|
|
314
367
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
368
|
+
if (reuseResult.success) {
|
|
369
|
+
console.log("✓ Reusing session from Antigravity MCP config\n");
|
|
370
|
+
sessionKey = reuseResult.sessionKey;
|
|
371
|
+
sessionSource = "reused";
|
|
372
|
+
} else {
|
|
373
|
+
console.log(` ⊗ ${reuseResult.reason}`);
|
|
374
|
+
console.log(" → Creating new session\n");
|
|
318
375
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
376
|
+
// 4. Fallback: Prompt for password
|
|
377
|
+
const password = await promptPassword();
|
|
378
|
+
|
|
379
|
+
// 5. Unlock vault
|
|
380
|
+
console.log("\n🔓 Unlocking vault...");
|
|
381
|
+
const unlockResult = unlockBitwarden(password);
|
|
323
382
|
|
|
324
|
-
|
|
325
|
-
|
|
383
|
+
if (!unlockResult.success) {
|
|
384
|
+
console.error(`❌ ${unlockResult.message}\n`);
|
|
385
|
+
process.exit(1);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log("✓ Vault unlocked\n");
|
|
389
|
+
sessionKey = unlockResult.sessionKey;
|
|
390
|
+
sessionSource = "new";
|
|
391
|
+
}
|
|
326
392
|
|
|
327
|
-
//
|
|
393
|
+
// 6. Discover required secrets from repo's bitwardenEnv
|
|
328
394
|
console.log("🔍 Scanning MCP server configs for required secrets...");
|
|
329
395
|
const discovery = discoverRequiredSecrets();
|
|
330
396
|
|
|
@@ -344,8 +410,8 @@ async function syncSecrets() {
|
|
|
344
410
|
console.log(` • ${name}`);
|
|
345
411
|
});
|
|
346
412
|
|
|
347
|
-
//
|
|
348
|
-
console.log(`\n🔐 Fetching from Bitwarden
|
|
413
|
+
// 7. Fetch secrets from Bitwarden
|
|
414
|
+
console.log(`\n🔐 Fetching from Bitwarden vault...`);
|
|
349
415
|
const fetchResults = fetchSecretsFromBitwarden(sessionKey, discovery.secrets);
|
|
350
416
|
|
|
351
417
|
fetchResults.found.forEach((secret) => {
|
|
@@ -383,18 +449,19 @@ async function syncSecrets() {
|
|
|
383
449
|
|
|
384
450
|
if (fetchResults.missing.length > 0) {
|
|
385
451
|
console.log(`⚠️ Missing secrets: ${fetchResults.missing.join(", ")}`);
|
|
386
|
-
console.log(` Add them to Bitwarden vault
|
|
452
|
+
console.log(` Add them to your Bitwarden vault\n`);
|
|
387
453
|
}
|
|
388
454
|
} finally {
|
|
389
|
-
// Cleanup:
|
|
390
|
-
if
|
|
455
|
+
// Cleanup: Only lock if we created a new session
|
|
456
|
+
// Don't lock if we reused the session - let MCP keep using it
|
|
457
|
+
if (sessionKey && sessionSource === "new") {
|
|
391
458
|
try {
|
|
392
459
|
execSync("bw lock", { stdio: "pipe" });
|
|
393
460
|
} catch (e) {
|
|
394
461
|
// Silent fail - vault may already be locked
|
|
395
462
|
}
|
|
396
|
-
sessionKey = null;
|
|
397
463
|
}
|
|
464
|
+
sessionKey = null;
|
|
398
465
|
}
|
|
399
466
|
}
|
|
400
467
|
|