ai-agent-config 2.6.6 → 2.7.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 +8 -8
- package/package.json +1 -1
- package/scripts/installer.js +3 -3
- package/scripts/mcp-installer.js +148 -62
- package/scripts/platforms.js +9 -0
- package/scripts/postinstall.js +22 -36
- package/scripts/secret-manager.js +5 -4
package/README.md
CHANGED
|
@@ -61,14 +61,14 @@ ai-agent update
|
|
|
61
61
|
|
|
62
62
|
## Supported Platforms
|
|
63
63
|
|
|
64
|
-
| Platform | Skills Path |
|
|
65
|
-
|
|
66
|
-
| Claude Code | `~/.claude/skills/` |
|
|
67
|
-
| Antigravity IDE | `~/.gemini/antigravity/skills/` |
|
|
68
|
-
| Cursor | `~/.cursor/skills/` |
|
|
69
|
-
| Windsurf | `~/.windsurf/skills/` |
|
|
70
|
-
| Codex CLI | `~/.codex/skills/` |
|
|
71
|
-
| GitHub Copilot | `~/.github/copilot-instructions.md` |
|
|
64
|
+
| Platform | Skills Path | MCP Support |
|
|
65
|
+
|----------|-------------|-------------|
|
|
66
|
+
| Claude Code | `~/.claude/skills/` | `claude_desktop_config.json` |
|
|
67
|
+
| Antigravity IDE | `~/.gemini/antigravity/skills/` | `mcp_config.json` |
|
|
68
|
+
| Cursor | `~/.cursor/skills/` | - |
|
|
69
|
+
| Windsurf | `~/.windsurf/skills/` | - |
|
|
70
|
+
| Codex CLI | `~/.codex/skills/` | - |
|
|
71
|
+
| GitHub Copilot | `~/.github/copilot-instructions.md` | - |
|
|
72
72
|
|
|
73
73
|
## Secret Management
|
|
74
74
|
|
package/package.json
CHANGED
package/scripts/installer.js
CHANGED
|
@@ -233,10 +233,10 @@ function installToPlatform(platform, options = {}) {
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
// Install MCP servers
|
|
237
|
-
if (platform.
|
|
236
|
+
// Install MCP servers to platforms with MCP support
|
|
237
|
+
if (platform.mcpConfigPath) {
|
|
238
238
|
try {
|
|
239
|
-
results.mcpServers = mcpInstaller.installMcpServers({ force });
|
|
239
|
+
results.mcpServers = mcpInstaller.installMcpServers({ force, platform });
|
|
240
240
|
} catch (error) {
|
|
241
241
|
console.warn(` ⚠️ MCP install failed: ${error.message}`);
|
|
242
242
|
}
|
package/scripts/mcp-installer.js
CHANGED
|
@@ -110,116 +110,150 @@ function collectBitwardenEnvs() {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* @param {
|
|
116
|
-
* @
|
|
113
|
+
* Write MCP servers to a platform's config file
|
|
114
|
+
* @param {string} configPath - Path to platform's MCP config file
|
|
115
|
+
* @param {Array} servers - MCP server configs to install
|
|
116
|
+
* @param {Object} options - { force, platformName }
|
|
117
|
+
* @returns {{ added: number, skipped: number }}
|
|
117
118
|
*/
|
|
118
|
-
function
|
|
119
|
-
const { force = false } = options;
|
|
120
|
-
const antigravity = platforms.getByName("antigravity");
|
|
121
|
-
|
|
122
|
-
if (!antigravity || !antigravity.mcpConfigPath) {
|
|
123
|
-
return { added: 0, skipped: 0, servers: [] };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const servers = getAvailableMcpServers().filter((s) => s.enabled !== false);
|
|
127
|
-
if (servers.length === 0) {
|
|
128
|
-
return { added: 0, skipped: 0, servers: [] };
|
|
129
|
-
}
|
|
119
|
+
function writeMcpToPlatformConfig(configPath, servers, options = {}) {
|
|
120
|
+
const { force = false, platformName = "" } = options;
|
|
130
121
|
|
|
131
|
-
// Read existing
|
|
132
|
-
let
|
|
133
|
-
if (fs.existsSync(
|
|
122
|
+
// Read existing config — preserve ALL existing keys (e.g. Claude's "preferences")
|
|
123
|
+
let config = { mcpServers: {} };
|
|
124
|
+
if (fs.existsSync(configPath)) {
|
|
134
125
|
try {
|
|
135
|
-
|
|
136
|
-
if (!
|
|
126
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
127
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
137
128
|
} catch {
|
|
138
|
-
|
|
129
|
+
config = { mcpServers: {} };
|
|
139
130
|
}
|
|
140
131
|
}
|
|
141
132
|
|
|
142
133
|
let added = 0;
|
|
143
134
|
let skipped = 0;
|
|
144
|
-
const serverNames = [];
|
|
145
135
|
|
|
146
136
|
for (const server of servers) {
|
|
147
|
-
const existing =
|
|
137
|
+
const existing = config.mcpServers[server.name];
|
|
148
138
|
|
|
149
139
|
if (existing && !force) {
|
|
150
140
|
skipped++;
|
|
151
|
-
serverNames.push(server.name);
|
|
152
141
|
continue;
|
|
153
142
|
}
|
|
154
143
|
|
|
155
|
-
// Build server entry for Antigravity format
|
|
156
144
|
const entry = {
|
|
157
145
|
command: server.command,
|
|
158
146
|
args: server.args,
|
|
159
147
|
};
|
|
160
148
|
|
|
161
|
-
// Preserve existing env
|
|
149
|
+
// Preserve existing env
|
|
162
150
|
if (existing && existing.env) {
|
|
163
151
|
entry.env = existing.env;
|
|
164
152
|
}
|
|
165
153
|
|
|
166
|
-
|
|
154
|
+
// disabledTools: only add for platforms that support it (not Claude)
|
|
155
|
+
if (platformName !== "claude" && server.disabledTools && server.disabledTools.length > 0) {
|
|
167
156
|
entry.disabledTools = server.disabledTools;
|
|
168
157
|
}
|
|
169
158
|
|
|
170
|
-
|
|
159
|
+
config.mcpServers[server.name] = entry;
|
|
171
160
|
added++;
|
|
172
|
-
serverNames.push(server.name);
|
|
173
161
|
}
|
|
174
162
|
|
|
175
|
-
// Write back
|
|
163
|
+
// Write back (create directory if needed)
|
|
176
164
|
if (added > 0) {
|
|
177
|
-
const configDir = path.dirname(
|
|
165
|
+
const configDir = path.dirname(configPath);
|
|
178
166
|
if (!fs.existsSync(configDir)) {
|
|
179
167
|
fs.mkdirSync(configDir, { recursive: true });
|
|
180
168
|
}
|
|
181
|
-
fs.writeFileSync(
|
|
169
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
170
|
+
// Set restrictive permissions (owner read/write only) to protect secrets
|
|
171
|
+
// Only on Unix-like systems (macOS, Linux) - Windows uses ACL instead
|
|
172
|
+
if (process.platform !== "win32") {
|
|
173
|
+
try {
|
|
174
|
+
fs.chmodSync(configPath, 0o600);
|
|
175
|
+
} catch (e) {
|
|
176
|
+
console.warn(`⚠️ Warning: Could not set file permissions on ${configPath}: ${e.message}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
return { added, skipped
|
|
181
|
+
return { added, skipped };
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
/**
|
|
188
|
-
* Install MCP servers
|
|
189
|
-
* This is the "
|
|
190
|
-
* @param {Object}
|
|
191
|
-
* @returns {{
|
|
185
|
+
* Install MCP servers to platforms' config files
|
|
186
|
+
* This is the "pull" flow - installs structure without resolved secrets
|
|
187
|
+
* @param {Object} options - { force: boolean, platform: Object|null }
|
|
188
|
+
* @returns {{ added: number, skipped: number, servers: string[] }}
|
|
192
189
|
*/
|
|
193
|
-
function
|
|
194
|
-
const
|
|
190
|
+
function installMcpServers(options = {}) {
|
|
191
|
+
const { force = false, platform = null } = options;
|
|
195
192
|
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
const servers = getAvailableMcpServers().filter((s) => s.enabled !== false);
|
|
194
|
+
if (servers.length === 0) {
|
|
195
|
+
return { added: 0, skipped: 0, servers: [] };
|
|
198
196
|
}
|
|
199
197
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
// Determine target platforms
|
|
199
|
+
const targetPlatforms = [];
|
|
200
|
+
if (platform) {
|
|
201
|
+
// Single platform specified
|
|
202
|
+
if (platform.mcpConfigPath) targetPlatforms.push(platform);
|
|
203
|
+
} else {
|
|
204
|
+
// All detected platforms with MCP support
|
|
205
|
+
for (const p of platforms.detectAll()) {
|
|
206
|
+
const full = platforms.getByName(p.name);
|
|
207
|
+
if (full && full.mcpConfigPath) targetPlatforms.push(full);
|
|
208
|
+
}
|
|
203
209
|
}
|
|
204
210
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
if (targetPlatforms.length === 0) {
|
|
212
|
+
return { added: 0, skipped: 0, servers: [] };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let totalAdded = 0;
|
|
216
|
+
let totalSkipped = 0;
|
|
217
|
+
const serverNames = [...new Set(servers.map((s) => s.name))];
|
|
218
|
+
|
|
219
|
+
for (const p of targetPlatforms) {
|
|
220
|
+
const result = writeMcpToPlatformConfig(p.mcpConfigPath, servers, {
|
|
221
|
+
force,
|
|
222
|
+
platformName: p.name,
|
|
223
|
+
});
|
|
224
|
+
totalAdded += result.added;
|
|
225
|
+
totalSkipped += result.skipped;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { added: totalAdded, skipped: totalSkipped, servers: serverNames };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Write MCP servers with resolved secrets to a platform's config file
|
|
233
|
+
* @param {string} configPath - Path to platform's MCP config file
|
|
234
|
+
* @param {Array} servers - MCP server configs
|
|
235
|
+
* @param {Object} resolvedSecrets - Map of bitwardenItem → resolvedValue
|
|
236
|
+
* @param {string} platformName - Platform name for disabledTools handling
|
|
237
|
+
* @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
|
|
238
|
+
*/
|
|
239
|
+
function writeMcpWithSecretsToPlatformConfig(configPath, servers, resolvedSecrets, platformName) {
|
|
240
|
+
// Read existing config — preserve ALL existing keys
|
|
241
|
+
let config = { mcpServers: {} };
|
|
242
|
+
if (fs.existsSync(configPath)) {
|
|
208
243
|
try {
|
|
209
|
-
|
|
210
|
-
if (!
|
|
244
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
245
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
211
246
|
} catch {
|
|
212
|
-
|
|
247
|
+
config = { mcpServers: {} };
|
|
213
248
|
}
|
|
214
249
|
}
|
|
215
250
|
|
|
216
251
|
let installed = 0;
|
|
217
252
|
const serverResults = [];
|
|
218
253
|
|
|
219
|
-
for (const server of
|
|
220
|
-
const existing =
|
|
254
|
+
for (const server of servers) {
|
|
255
|
+
const existing = config.mcpServers[server.name] || {};
|
|
221
256
|
|
|
222
|
-
// Build entry: keep existing custom fields (disabledTools user may have customized)
|
|
223
257
|
const entry = {
|
|
224
258
|
command: server.command,
|
|
225
259
|
args: server.args,
|
|
@@ -246,29 +280,80 @@ function installMcpServersWithSecrets(resolvedSecrets) {
|
|
|
246
280
|
serverResults.push({ name: server.name, secretsCount: 0 });
|
|
247
281
|
}
|
|
248
282
|
|
|
249
|
-
//
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
283
|
+
// disabledTools: only for platforms that support it (not Claude)
|
|
284
|
+
if (platformName !== "claude") {
|
|
285
|
+
if (existing.disabledTools) {
|
|
286
|
+
entry.disabledTools = existing.disabledTools;
|
|
287
|
+
} else if (server.disabledTools && server.disabledTools.length > 0) {
|
|
288
|
+
entry.disabledTools = server.disabledTools;
|
|
289
|
+
}
|
|
254
290
|
}
|
|
255
291
|
|
|
256
|
-
|
|
292
|
+
config.mcpServers[server.name] = entry;
|
|
257
293
|
installed++;
|
|
258
294
|
}
|
|
259
295
|
|
|
260
296
|
// Write back
|
|
261
297
|
if (installed > 0) {
|
|
262
|
-
const configDir = path.dirname(
|
|
298
|
+
const configDir = path.dirname(configPath);
|
|
263
299
|
if (!fs.existsSync(configDir)) {
|
|
264
300
|
fs.mkdirSync(configDir, { recursive: true });
|
|
265
301
|
}
|
|
266
|
-
fs.writeFileSync(
|
|
302
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
303
|
+
// Set restrictive permissions (owner read/write only) to protect secrets
|
|
304
|
+
// Only on Unix-like systems (macOS, Linux) - Windows uses ACL instead
|
|
305
|
+
if (process.platform !== "win32") {
|
|
306
|
+
try {
|
|
307
|
+
fs.chmodSync(configPath, 0o600);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
console.warn(`⚠️ Warning: Could not set file permissions on ${configPath}: ${e.message}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
267
312
|
}
|
|
268
313
|
|
|
269
314
|
return { installed, servers: serverResults };
|
|
270
315
|
}
|
|
271
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Install MCP servers with resolved secrets to all MCP-capable platforms
|
|
319
|
+
* This is the "secrets sync" flow - updates env with real values
|
|
320
|
+
* @param {Object} resolvedSecrets - Map of bitwardenItem → resolvedValue
|
|
321
|
+
* @returns {{ installed: number, servers: Array<{ name: string, secretsCount: number }> }}
|
|
322
|
+
*/
|
|
323
|
+
function installMcpServersWithSecrets(resolvedSecrets) {
|
|
324
|
+
const allServers = getAvailableMcpServers().filter((s) => s.enabled !== false);
|
|
325
|
+
if (allServers.length === 0) {
|
|
326
|
+
return { installed: 0, servers: [] };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// All detected platforms with MCP support
|
|
330
|
+
const targetPlatforms = [];
|
|
331
|
+
for (const p of platforms.detectAll()) {
|
|
332
|
+
const full = platforms.getByName(p.name);
|
|
333
|
+
if (full && full.mcpConfigPath) targetPlatforms.push(full);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (targetPlatforms.length === 0) {
|
|
337
|
+
return { installed: 0, servers: [] };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
let totalInstalled = 0;
|
|
341
|
+
let aggregatedServers = [];
|
|
342
|
+
|
|
343
|
+
for (const p of targetPlatforms) {
|
|
344
|
+
const result = writeMcpWithSecretsToPlatformConfig(
|
|
345
|
+
p.mcpConfigPath, allServers, resolvedSecrets, p.name
|
|
346
|
+
);
|
|
347
|
+
totalInstalled += result.installed;
|
|
348
|
+
// Use server results from last platform (they're the same servers)
|
|
349
|
+
if (result.servers.length > 0) {
|
|
350
|
+
aggregatedServers = result.servers;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return { installed: totalInstalled, servers: aggregatedServers };
|
|
355
|
+
}
|
|
356
|
+
|
|
272
357
|
module.exports = {
|
|
273
358
|
getMcpServersDir,
|
|
274
359
|
validateMcpConfig,
|
|
@@ -276,5 +361,6 @@ module.exports = {
|
|
|
276
361
|
collectBitwardenEnvs,
|
|
277
362
|
installMcpServers,
|
|
278
363
|
installMcpServersWithSecrets,
|
|
364
|
+
writeMcpToPlatformConfig,
|
|
279
365
|
SKIP_FOLDERS,
|
|
280
366
|
};
|
package/scripts/platforms.js
CHANGED
|
@@ -32,6 +32,15 @@ const SUPPORTED = [
|
|
|
32
32
|
get commandsPath() {
|
|
33
33
|
return path.join(HOME, this.configDir, this.commandsDir);
|
|
34
34
|
},
|
|
35
|
+
get mcpConfigPath() {
|
|
36
|
+
if (process.platform === "darwin") {
|
|
37
|
+
return path.join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
38
|
+
} else if (process.platform === "win32") {
|
|
39
|
+
return path.join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
40
|
+
} else {
|
|
41
|
+
return path.join(HOME, ".config", "Claude", "claude_desktop_config.json");
|
|
42
|
+
}
|
|
43
|
+
},
|
|
35
44
|
detect() {
|
|
36
45
|
return fs.existsSync(this.configPath);
|
|
37
46
|
},
|
package/scripts/postinstall.js
CHANGED
|
@@ -7,6 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
const platforms = require("./platforms");
|
|
9
9
|
|
|
10
|
+
// Bitwarden MCP: Tools to disable (org management, device approval, sends, etc.)
|
|
11
|
+
const BITWARDEN_DISABLED_TOOLS = [
|
|
12
|
+
"lock", "sync", "status", "confirm",
|
|
13
|
+
"create_org_collection", "edit_org_collection", "edit_item_collections", "move",
|
|
14
|
+
"device_approval_list", "device_approval_approve", "device_approval_approve_all",
|
|
15
|
+
"device_approval_deny", "device_approval_deny_all",
|
|
16
|
+
"create_text_send", "create_file_send", "list_send", "get_send",
|
|
17
|
+
"edit_send", "delete_send", "remove_send_password",
|
|
18
|
+
"create_attachment",
|
|
19
|
+
"list_org_collections", "get_org_collection", "update_org_collection", "delete_org_collection",
|
|
20
|
+
"list_org_members", "get_org_member", "get_org_member_groups",
|
|
21
|
+
"invite_org_member", "update_org_member", "update_org_member_groups",
|
|
22
|
+
"remove_org_member", "reinvite_org_member",
|
|
23
|
+
"list_org_groups", "get_org_group", "get_org_group_members",
|
|
24
|
+
"create_org_group", "update_org_group", "delete_org_group", "update_org_group_members",
|
|
25
|
+
"list_org_policies", "get_org_policy", "update_org_policy",
|
|
26
|
+
"get_org_events", "get_org_subscription", "update_org_subscription",
|
|
27
|
+
"import_org_users_and_groups"
|
|
28
|
+
];
|
|
29
|
+
|
|
10
30
|
function main() {
|
|
11
31
|
console.log("\n╔═══════════════════════════════════════════════════════════════╗");
|
|
12
32
|
console.log("║ AI Agent Config Installed! ║");
|
|
@@ -73,24 +93,7 @@ function main() {
|
|
|
73
93
|
BW_CLIENT_ID: "${BW_CLIENT_ID}",
|
|
74
94
|
BW_CLIENT_SECRET: "${BW_CLIENT_SECRET}",
|
|
75
95
|
},
|
|
76
|
-
disabledTools:
|
|
77
|
-
"lock", "sync", "status", "confirm",
|
|
78
|
-
"create_org_collection", "edit_org_collection", "edit_item_collections", "move",
|
|
79
|
-
"device_approval_list", "device_approval_approve", "device_approval_approve_all",
|
|
80
|
-
"device_approval_deny", "device_approval_deny_all",
|
|
81
|
-
"create_text_send", "create_file_send", "list_send", "get_send",
|
|
82
|
-
"edit_send", "delete_send", "remove_send_password",
|
|
83
|
-
"create_attachment",
|
|
84
|
-
"list_org_collections", "get_org_collection", "update_org_collection", "delete_org_collection",
|
|
85
|
-
"list_org_members", "get_org_member", "get_org_member_groups",
|
|
86
|
-
"invite_org_member", "update_org_member", "update_org_member_groups",
|
|
87
|
-
"remove_org_member", "reinvite_org_member",
|
|
88
|
-
"list_org_groups", "get_org_group", "get_org_group_members",
|
|
89
|
-
"create_org_group", "update_org_group", "delete_org_group", "update_org_group_members",
|
|
90
|
-
"list_org_policies", "get_org_policy", "update_org_policy",
|
|
91
|
-
"get_org_events", "get_org_subscription", "update_org_subscription",
|
|
92
|
-
"import_org_users_and_groups"
|
|
93
|
-
],
|
|
96
|
+
disabledTools: BITWARDEN_DISABLED_TOOLS,
|
|
94
97
|
};
|
|
95
98
|
changed = true;
|
|
96
99
|
console.log("🔐 Bitwarden MCP server added to Antigravity (✓ enabled)");
|
|
@@ -110,24 +113,7 @@ function main() {
|
|
|
110
113
|
|
|
111
114
|
// Phase 4: Add disabledTools if not present (don't override if user customized)
|
|
112
115
|
if (!bw.disabledTools) {
|
|
113
|
-
bw.disabledTools =
|
|
114
|
-
"lock", "sync", "status", "confirm",
|
|
115
|
-
"create_org_collection", "edit_org_collection", "edit_item_collections", "move",
|
|
116
|
-
"device_approval_list", "device_approval_approve", "device_approval_approve_all",
|
|
117
|
-
"device_approval_deny", "device_approval_deny_all",
|
|
118
|
-
"create_text_send", "create_file_send", "list_send", "get_send",
|
|
119
|
-
"edit_send", "delete_send", "remove_send_password",
|
|
120
|
-
"create_attachment",
|
|
121
|
-
"list_org_collections", "get_org_collection", "update_org_collection", "delete_org_collection",
|
|
122
|
-
"list_org_members", "get_org_member", "get_org_member_groups",
|
|
123
|
-
"invite_org_member", "update_org_member", "update_org_member_groups",
|
|
124
|
-
"remove_org_member", "reinvite_org_member",
|
|
125
|
-
"list_org_groups", "get_org_group", "get_org_group_members",
|
|
126
|
-
"create_org_group", "update_org_group", "delete_org_group", "update_org_group_members",
|
|
127
|
-
"list_org_policies", "get_org_policy", "update_org_policy",
|
|
128
|
-
"get_org_events", "get_org_subscription", "update_org_subscription",
|
|
129
|
-
"import_org_users_and_groups"
|
|
130
|
-
];
|
|
116
|
+
bw.disabledTools = BITWARDEN_DISABLED_TOOLS;
|
|
131
117
|
changed = true;
|
|
132
118
|
console.log("🎛️ Bitwarden MCP: Added tool filters (disabled org-management tools)");
|
|
133
119
|
}
|
|
@@ -124,13 +124,14 @@ async function promptPassword() {
|
|
|
124
124
|
/**
|
|
125
125
|
* Unlock Bitwarden vault with password
|
|
126
126
|
* Returns session key or null if failed
|
|
127
|
-
* Uses
|
|
127
|
+
* Uses --passwordenv to pass password securely (not visible in process list)
|
|
128
128
|
*/
|
|
129
129
|
function unlockBitwarden(password) {
|
|
130
130
|
try {
|
|
131
|
-
// Use
|
|
132
|
-
//
|
|
133
|
-
const result = spawnSync("bw", ["unlock",
|
|
131
|
+
// Use --passwordenv to avoid leaking password in /proc/<pid>/cmdline
|
|
132
|
+
// Set password in env only for this child process
|
|
133
|
+
const result = spawnSync("bw", ["unlock", "--passwordenv", "BW_UNLOCK_PASSWORD", "--raw"], {
|
|
134
|
+
env: { ...process.env, BW_UNLOCK_PASSWORD: password },
|
|
134
135
|
encoding: "utf-8",
|
|
135
136
|
});
|
|
136
137
|
|